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