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>;
class_nameRootWindow54 static LPCWSTR class_name() {return L"Scratch";}
change_classRootWindow55 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
set_titleRootWindow64 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 }
get_titleRootWindow70 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
set_titleRootWindow82 void set_title(const std::wstring& t) {
83 title = t;
84 }
get_titleRootWindow85 const std::wstring& get_title() {
86 return title;
87 }
88
89 // lifetime
90
91 // called during WM_NCDESTROY
~RootWindowRootWindow92 ~RootWindow() {
93 PostQuitMessage(0);
94 }
95
96 // called during WM_NCCREATE
RootWindowRootWindow97 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
PaintContentRootWindow115 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
OnKeyDownRootWindow125 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
OnMovesWhileLButtonDownRootWindow138 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
OnPaintRootWindow159 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
OnPrintClientRootWindow171 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
wWinMain(HINSTANCE hinst,HINSTANCE,LPWSTR,int nShowCmd)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