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