aboutsummaryrefslogtreecommitdiff
path: root/src/thmviewer/display.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/thmviewer/display.cpp')
-rw-r--r--src/thmviewer/display.cpp353
1 files changed, 353 insertions, 0 deletions
diff --git a/src/thmviewer/display.cpp b/src/thmviewer/display.cpp
new file mode 100644
index 00000000..cfd1c6b7
--- /dev/null
+++ b/src/thmviewer/display.cpp
@@ -0,0 +1,353 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5static const LPCWSTR THMVWR_WINDOW_CLASS_DISPLAY = L"ThmViewerDisplay";
6
7struct DISPLAY_THREAD_CONTEXT
8{
9 HWND hWnd;
10 HINSTANCE hInstance;
11
12 HANDLE hInit;
13};
14
15static DWORD WINAPI DisplayThreadProc(
16 __in LPVOID pvContext
17 );
18static LRESULT CALLBACK DisplayWndProc(
19 __in HWND hWnd,
20 __in UINT uMsg,
21 __in WPARAM wParam,
22 __in LPARAM lParam
23 );
24static BOOL DisplayOnCreate(
25 __in THEME* pTheme,
26 __in HWND hWnd
27 );
28
29
30extern "C" HRESULT DisplayStart(
31 __in HINSTANCE hInstance,
32 __in HWND hWnd,
33 __out HANDLE *phThread,
34 __out DWORD* pdwThreadId
35 )
36{
37 HRESULT hr = S_OK;
38 HANDLE rgHandles[2] = { };
39 DISPLAY_THREAD_CONTEXT context = { };
40
41 rgHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL);
42 ExitOnNullWithLastError(rgHandles[0], hr, "Failed to create load init event.");
43
44 context.hWnd = hWnd;
45 context.hInstance = hInstance;
46 context.hInit = rgHandles[0];
47
48 rgHandles[1] = ::CreateThread(NULL, 0, DisplayThreadProc, reinterpret_cast<LPVOID>(&context), 0, pdwThreadId);
49 ExitOnNullWithLastError(rgHandles[1], hr, "Failed to create display thread.");
50
51 ::WaitForMultipleObjects(countof(rgHandles), rgHandles, FALSE, INFINITE);
52
53 *phThread = rgHandles[1];
54 rgHandles[1] = NULL;
55
56LExit:
57 ReleaseHandle(rgHandles[1]);
58 ReleaseHandle(rgHandles[0]);
59 return hr;
60}
61
62static DWORD WINAPI DisplayThreadProc(
63 __in LPVOID pvContext
64 )
65{
66 HRESULT hr = S_OK;
67
68 DISPLAY_THREAD_CONTEXT* pContext = static_cast<DISPLAY_THREAD_CONTEXT*>(pvContext);
69 HINSTANCE hInstance = pContext->hInstance;
70 HWND hwndParent = pContext->hWnd;
71
72 // We can signal the initialization event as soon as we have copied the context
73 // values into local variables.
74 ::SetEvent(pContext->hInit);
75
76 BOOL fComInitialized = FALSE;
77
78 HANDLE_THEME* pCurrentHandle = NULL;
79 ATOM atomWc = 0;
80 WNDCLASSW wc = { }; // the following are constant for the display window class.
81 wc.lpfnWndProc = DisplayWndProc;
82 wc.hInstance = hInstance;
83 wc.lpszClassName = THMVWR_WINDOW_CLASS_DISPLAY;
84
85 HWND hWnd = NULL;
86 RECT rc = { };
87 int x = CW_USEDEFAULT;
88 int y = CW_USEDEFAULT;
89
90 BOOL fRedoMsg = FALSE;
91 BOOL fRet = FALSE;
92 MSG msg = { };
93
94 BOOL fCreateIfNecessary = FALSE;
95
96 hr = ::CoInitialize(NULL);
97 ExitOnFailure(hr, "Failed to initialize COM on display thread.");
98 fComInitialized = TRUE;
99
100 // As long as the parent window is alive and kicking, keep this thread going (with or without a theme to display ).
101 while (::IsWindow(hwndParent))
102 {
103 if (pCurrentHandle && fCreateIfNecessary)
104 {
105 THEME* pTheme = pCurrentHandle->pTheme;
106
107 if (CW_USEDEFAULT == x && CW_USEDEFAULT == y && ::GetWindowRect(hwndParent, &rc))
108 {
109 x = rc.left;
110 y = rc.bottom + 20;
111 }
112
113 hWnd = ::CreateWindowExW(0, wc.lpszClassName, pTheme->sczCaption, pTheme->dwStyle, x, y, pTheme->nWidth, pTheme->nHeight, hwndParent, NULL, hInstance, pCurrentHandle);
114 ExitOnNullWithLastError(hWnd, hr, "Failed to create display window.");
115
116 fCreateIfNecessary = FALSE;
117 }
118
119 // message pump
120 while (fRedoMsg || 0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
121 {
122 if (fRedoMsg)
123 {
124 fRedoMsg = FALSE;
125 }
126
127 if (-1 == fRet)
128 {
129 hr = E_UNEXPECTED;
130 ExitOnFailure(hr, "Unexpected return value from display message pump.");
131 }
132 else if (NULL == msg.hwnd) // Thread message.
133 {
134 if (WM_THMVWR_NEW_THEME == msg.message)
135 {
136 // If there is already a handle, release it.
137 if (pCurrentHandle)
138 {
139 DecrementHandleTheme(pCurrentHandle);
140 pCurrentHandle = NULL;
141 }
142
143 // If the window was created, remember its window location before we destroy
144 // it so so we can open the new window in the same place.
145 if (::IsWindow(hWnd))
146 {
147 ::GetWindowRect(hWnd, &rc);
148 x = rc.left;
149 y = rc.top;
150
151 ::DestroyWindow(hWnd);
152 }
153
154 // If the display window class was registered, unregister it so we can
155 // reuse the same window class name for the new theme.
156 if (atomWc)
157 {
158 if (!::UnregisterClassW(reinterpret_cast<LPCWSTR>(atomWc), hInstance))
159 {
160 DWORD er = ::GetLastError();
161 er = er;
162 }
163
164 atomWc = 0;
165 }
166
167 // If we were provided a new theme handle, create a new window class to
168 // support it.
169 pCurrentHandle = reinterpret_cast<HANDLE_THEME*>(msg.lParam);
170 if (pCurrentHandle)
171 {
172 wc.hIcon = reinterpret_cast<HICON>(pCurrentHandle->pTheme->hIcon);
173 wc.hCursor = ::LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
174 if (0 < pCurrentHandle->pTheme->cFonts)
175 {
176 wc.hbrBackground = pCurrentHandle->pTheme->rgFonts[pCurrentHandle->pTheme->dwFontId].hBackground;
177 }
178 atomWc = ::RegisterClassW(&wc);
179 if (!atomWc)
180 {
181 ExitWithLastError(hr, "Failed to register display window class.");
182 }
183 }
184 }
185 else if (WM_THMVWR_SHOWPAGE == msg.message)
186 {
187 if (pCurrentHandle && ::IsWindow(hWnd) && pCurrentHandle->pTheme->hwndParent == hWnd)
188 {
189 DWORD dwPageId = static_cast<DWORD>(msg.lParam);
190 int nCmdShow = static_cast<int>(msg.wParam);
191
192 // First show/hide the controls not associated with a page.
193 for (DWORD i = 0; i < pCurrentHandle->pTheme->cControls; ++i)
194 {
195 THEME_CONTROL* pControl = pCurrentHandle->pTheme->rgControls + i;
196 if (!pControl->wPageId)
197 {
198 ThemeShowControl(pCurrentHandle->pTheme, pControl->wId, nCmdShow);
199 }
200 }
201
202 // If a page id was provided also, show/hide those controls
203 if (dwPageId)
204 {
205 // Ignore error since we aren't using variables and it can only fail when using variables.
206 ThemeShowPage(pCurrentHandle->pTheme, dwPageId, nCmdShow);
207 }
208 }
209 else // display window isn't visible or it doesn't match the current handle.
210 {
211 // Keep the current message around to try again after we break out of this loop
212 // and create the window.
213 fRedoMsg = TRUE;
214 fCreateIfNecessary = TRUE;
215 break;
216 }
217 }
218 }
219 else if (!ThemeHandleKeyboardMessage(pCurrentHandle->pTheme, hwndParent, &msg)) // Window message.
220 {
221 ::TranslateMessage(&msg);
222 ::DispatchMessageW(&msg);
223 }
224 }
225 }
226
227LExit:
228 if (::IsWindow(hWnd))
229 {
230 ::DestroyWindow(hWnd);
231 }
232
233 if (atomWc)
234 {
235 if (!::UnregisterClassW(THMVWR_WINDOW_CLASS_DISPLAY, hInstance))
236 {
237 DWORD er = ::GetLastError();
238 er = er;
239 }
240 }
241
242 DecrementHandleTheme(pCurrentHandle);
243
244 if (fComInitialized)
245 {
246 ::CoUninitialize();
247 }
248
249 return hr;
250}
251
252static LRESULT CALLBACK DisplayWndProc(
253 __in HWND hWnd,
254 __in UINT uMsg,
255 __in WPARAM wParam,
256 __in LPARAM lParam
257 )
258{
259 static DWORD dwProgress = 0;
260 HANDLE_THEME* pHandleTheme = reinterpret_cast<HANDLE_THEME*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
261
262 switch (uMsg)
263 {
264 case WM_NCCREATE:
265 {
266 LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
267 IncrementHandleTheme(reinterpret_cast<HANDLE_THEME*>(lpcs->lpCreateParams));
268 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(lpcs->lpCreateParams));
269 }
270 break;
271
272 case WM_CREATE:
273 if (!DisplayOnCreate(pHandleTheme->pTheme, hWnd))
274 {
275 return -1;
276 }
277 break;
278
279 case WM_TIMER:
280 if (!lParam && SUCCEEDED(ThemeSetProgressControl(pHandleTheme->pTheme, wParam, dwProgress)))
281 {
282 dwProgress += rand() % 10 + 1;
283 if (dwProgress > 100)
284 {
285 dwProgress = 0;
286 }
287
288 return 0;
289 }
290 break;
291
292 case WM_COMMAND:
293 {
294 WCHAR wzText[1024];
295 ::StringCchPrintfW(wzText, countof(wzText), L"Command %u\r\n", LOWORD(wParam));
296 OutputDebugStringW(wzText);
297 //::MessageBoxW(hWnd, wzText, L"Command fired", MB_OK);
298 }
299 break;
300
301 case WM_SYSCOMMAND:
302 {
303 WCHAR wzText[1024];
304 ::StringCchPrintfW(wzText, countof(wzText), L"SysCommand %u\r\n", LOWORD(wParam));
305 OutputDebugStringW(wzText);
306 //::MessageBoxW(hWnd, wzText, L"Command fired", MB_OK);
307 }
308 break;
309
310 case WM_DESTROY:
311 ThemeUnloadControls(pHandleTheme->pTheme);
312 ::PostQuitMessage(0);
313 break;
314
315 case WM_NCDESTROY:
316 DecrementHandleTheme(pHandleTheme);
317 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
318 break;
319 }
320
321 return ThemeDefWindowProc(pHandleTheme ? pHandleTheme->pTheme : NULL, hWnd, uMsg, wParam, lParam);
322}
323
324static BOOL DisplayOnCreate(
325 __in THEME* pTheme,
326 __in HWND hWnd
327 )
328{
329 HRESULT hr = S_OK;
330
331 hr = ThemeLoadControls(pTheme, hWnd, NULL, 0);
332 ExitOnFailure(hr, "Failed to load theme controls");
333
334 // Pre-populate some control types with data.
335 for (DWORD i = 0; i < pTheme->cControls; ++i)
336 {
337 THEME_CONTROL* pControl = pTheme->rgControls + i;
338 if (THEME_CONTROL_TYPE_RICHEDIT == pControl->type)
339 {
340 hr = ThemeLoadRichEditFromResource(pTheme, pControl->wId, MAKEINTRESOURCEA(THMVWR_RES_RICHEDIT_FILE), ::GetModuleHandleW(NULL));
341 ExitOnFailure(hr, "Failed to load richedit text.");
342 }
343 else if (THEME_CONTROL_TYPE_PROGRESSBAR == pControl->type)
344 {
345 DWORD dwId = ::SetTimer(hWnd, pControl->wId, 500, NULL);
346 dwId = dwId; // prevents warning in "ship" build.
347 Assert(dwId == pControl->wId);
348 }
349 }
350
351LExit:
352 return SUCCEEDED(hr);
353}