1 2 // win_text.cpp : Defines the entry point for the application. 3 // 4 // 5 6 #define STRICT 7 #define WIN32_LEAN_AND_MEAN 8 #define NOMINMAX 9 10 #include <windows.h> 11 #include <windowsx.h> 12 #include <ole2.h> 13 #include <commctrl.h> 14 #include <shlwapi.h> 15 #include <shlobj.h> 16 #include <shellapi.h> 17 18 #pragma comment(lib, "user32.lib") 19 #pragma comment(lib, "gdi32.lib") 20 #pragma comment(lib, "Comctl32.lib") 21 #pragma comment(lib, "Ole32.lib") 22 23 #include <new> 24 #include <utility> 25 #include <memory> 26 #include <type_traits> 27 #include <tuple> 28 #include <list> 29 30 #include "rxcpp/rx.hpp" 31 // create alias' to simplify code 32 // these are owned by the user so that 33 // conflicts can be managed by the user. 34 namespace rx=rxcpp; 35 namespace rxsub=rxcpp::subjects; 36 namespace rxu=rxcpp::util; 37 38 // At this time, RxCpp will fail to compile if the contents 39 // of the std namespace are merged into the global namespace 40 // DO NOT USE: 'using namespace std;' 41 42 #include "unwinder.h" 43 44 #include "windows_user.h" 45 namespace wu = windows_user; 46 47 #include "rx_windows_user.h" 48 namespace rxwu = rxcpp::windows_user; 49 50 struct RootWindow : public rxwu::rx_messages, public rxwu::enable_send_call<RootWindow, WM_USER+1> 51 { 52 // window class 53 using window_class = wu::window_class<RootWindow>; 54 static LPCWSTR class_name() {return L"Scratch";} 55 static void change_class(WNDCLASSEX&) {} 56 57 // createstruct parameter type 58 using param_type = std::wstring; 59 60 // public methods 61 62 // static methods use a window message per call 63 64 static LRESULT set_title(HWND w, const std::wstring& t) { 65 return send_call(w, [&](RootWindow& r){ 66 r.set_title(t); 67 return 0; 68 }); 69 } 70 static std::wstring get_title(HWND w) { 71 std::wstring t; 72 send_call(w, [&](RootWindow& r){ 73 t = r.get_title(); 74 return 0; 75 }); 76 return t; 77 } 78 79 // instance methods are accessed using static send_call(hwnd, [](RootWindow& r){. . .}); 80 // send_call uses one window message, the lambda can call many instance methods. 81 82 void set_title(const std::wstring& t) { 83 title = t; 84 } 85 const std::wstring& get_title() { 86 return title; 87 } 88 89 // lifetime 90 91 // called during WM_NCDESTROY 92 ~RootWindow() { 93 PostQuitMessage(0); 94 } 95 96 // called during WM_NCCREATE 97 RootWindow(HWND w, LPCREATESTRUCT, param_type* title) 98 : window(w) 99 , title(title ? *title : L"RootWindow") 100 , position{40, 10} { 101 // listen for the following messages 102 OnPaint(); 103 OnPrintClient(); 104 OnKeyDown(); 105 OnMovesWhileLButtonDown(); 106 } 107 108 private: 109 // implementation 110 111 HWND window; 112 std::wstring title; 113 POINTS position; 114 115 void PaintContent(PAINTSTRUCT& ps) { 116 RECT rect; 117 GetClientRect (window, &rect) ; 118 SetTextColor(ps.hdc, 0x00000000); 119 SetBkMode(ps.hdc,TRANSPARENT); 120 rect.left=position.x; 121 rect.top=position.y; 122 DrawText( ps.hdc, title.c_str(), -1, &rect, DT_SINGLELINE | DT_NOCLIP ) ; 123 } 124 125 void OnKeyDown() { 126 messages<WM_KEYDOWN>(). 127 subscribe([this](auto m) { 128 m.handled(); // skip DefWindowProc 129 130 MessageBox(window, L"KeyDown", L"RootWindow", MB_OK); 131 // NOTE: MessageBox pumps messages, but this subscription only 132 // receives messages if it is suspended by 'for await', so any 133 // WM_KEYDOWN arriving while the message box is up is not delivered. 134 // the other subscriptions will receive messages. 135 }); 136 } 137 138 void OnMovesWhileLButtonDown() { 139 140 auto moves_while_lbutton_down = messages<WM_LBUTTONDOWN>(). 141 map( 142 [this](auto m) { 143 m.handled(); // skip DefWindowProc 144 145 return this->messages<WM_MOUSEMOVE>(). 146 take_until(this->messages<WM_LBUTTONUP>()); 147 }). 148 merge(); 149 150 moves_while_lbutton_down. 151 subscribe([this](auto m) { 152 m.handled(); // skip DefWindowProc 153 154 position = MAKEPOINTS(m.lParam); 155 InvalidateRect(window, nullptr, true); 156 }); 157 } 158 159 void OnPaint() { 160 messages<WM_PAINT>(). 161 subscribe([this](auto m) { 162 m.handled(); // skip DefWindowProc 163 164 PAINTSTRUCT ps; 165 BeginPaint(window, &ps); 166 PaintContent(ps); 167 EndPaint(window, &ps); 168 }); 169 } 170 171 void OnPrintClient() { 172 messages<WM_PRINTCLIENT, HDC>(). 173 subscribe([this](auto m) { 174 m.handled(); // skip DefWindowProc 175 176 PAINTSTRUCT ps; 177 ps.hdc = m.wParam; 178 GetClientRect(window, &ps.rcPaint); 179 PaintContent(ps); 180 }); 181 } 182 }; 183 184 int PASCAL 185 wWinMain(HINSTANCE hinst, HINSTANCE, LPWSTR, int nShowCmd) 186 { 187 HRESULT hr = S_OK; 188 189 hr = CoInitialize(NULL); 190 if (FAILED(hr)) 191 { 192 return FALSE; 193 } 194 ON_UNWIND_AUTO([&]{CoUninitialize();}); 195 196 InitCommonControls(); 197 198 RootWindow::window_class::Register(); 199 200 LONG winerror = ERROR_SUCCESS; 201 202 std::wstring title{L"Scratch App - RootWindow"}; 203 204 // normal create window call, just takes the class name and optional create parameters 205 HWND window = CreateWindow( 206 RootWindow::window_class::Name(), title.c_str(), 207 WS_OVERLAPPEDWINDOW, 208 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 209 NULL, NULL, 210 hinst, 211 &title); 212 if (!window) {winerror = GetLastError();} 213 214 if (!!winerror || !window) 215 { 216 return winerror; 217 } 218 219 ShowWindow(window, nShowCmd); 220 221 // interact with window safely on the UI thread from another thread 222 auto settitle = std::async([window](){ 223 224 // by static method (two SendMessage) 225 RootWindow::set_title(window, L"SET_TITLE! " + RootWindow::get_title(window)); 226 227 // or multiple instance methods (one SendMessage) 228 RootWindow::send_call(window, [](RootWindow& r){ 229 r.set_title(L"SEND_CALL! " + r.get_title()); 230 return 0; 231 }); 232 }); 233 234 MSG msg = {}; 235 while (GetMessage(&msg, NULL, 0, 0)) 236 { 237 TranslateMessage(&msg); 238 DispatchMessage(&msg); 239 } 240 241 settitle.get(); 242 243 return 0; 244 } 245