aboutsummaryrefslogtreecommitdiff
path: root/src/tools/thmviewer/display.cpp
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-07-14 15:19:53 -0700
committerRob Mensching <rob@firegiant.com>2022-07-14 16:02:24 -0700
commit229242cf7c328b89b5aa65ed7a04e33c8b93b393 (patch)
treede0a9547e73e46490b0946d6850228d5b30258b8 /src/tools/thmviewer/display.cpp
parentf46ca6a9dce91607ffc9855270dd6998216e1a8b (diff)
downloadwix-229242cf7c328b89b5aa65ed7a04e33c8b93b393.tar.gz
wix-229242cf7c328b89b5aa65ed7a04e33c8b93b393.tar.bz2
wix-229242cf7c328b89b5aa65ed7a04e33c8b93b393.zip
Rename "samples" segment to "tools"
This segment is a bit of a "miscellaneous section" in the WiX repo. As such it has been difficult to name. I originally eschewed the name "tools" because what is in the "wix" segment was once called "tools". However, now that wix.exe is firmly established as the entry point for WiX operations, I've become comfortable with its segment being named "wix". That meant "tools" was again available and "tools" better describes the content of this section.
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}