aboutsummaryrefslogtreecommitdiff
path: root/src/burn/engine/splashscreen.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/burn/engine/splashscreen.cpp')
-rw-r--r--src/burn/engine/splashscreen.cpp355
1 files changed, 355 insertions, 0 deletions
diff --git a/src/burn/engine/splashscreen.cpp b/src/burn/engine/splashscreen.cpp
new file mode 100644
index 00000000..90bd5203
--- /dev/null
+++ b/src/burn/engine/splashscreen.cpp
@@ -0,0 +1,355 @@
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
5#define BURN_SPLASHSCREEN_CLASS_WINDOW L"WixBurnSplashScreen"
6#define IDB_SPLASHSCREEN 1
7
8// struct
9
10struct SPLASHSCREEN_INFO
11{
12 HBITMAP hBitmap;
13 SIZE defaultDpiSize;
14 SIZE size;
15 UINT nDpi;
16 HWND hWnd;
17};
18
19struct SPLASHSCREEN_CONTEXT
20{
21 HANDLE hInitializedEvent;
22 HINSTANCE hInstance;
23 LPCWSTR wzCaption;
24
25 HWND* pHwnd;
26};
27
28// internal function definitions
29
30static DWORD WINAPI ThreadProc(
31 __in LPVOID pvContext
32 );
33static LRESULT CALLBACK WndProc(
34 __in HWND hWnd,
35 __in UINT uMsg,
36 __in WPARAM wParam,
37 __in LPARAM lParam
38 );
39static HRESULT LoadSplashScreen(
40 __in SPLASHSCREEN_CONTEXT* pContext,
41 __in SPLASHSCREEN_INFO* pSplashScreen
42 );
43static BOOL OnDpiChanged(
44 __in SPLASHSCREEN_INFO* pSplashScreen,
45 __in WPARAM wParam,
46 __in LPARAM lParam
47 );
48static void OnEraseBkgnd(
49 __in SPLASHSCREEN_INFO* pSplashScreen,
50 __in WPARAM wParam
51 );
52static void OnNcCreate(
53 __in HWND hWnd,
54 __in LPARAM lParam
55 );
56static void ScaleSplashScreen(
57 __in SPLASHSCREEN_INFO* pSplashScreen,
58 __in UINT nDpi,
59 __in int x,
60 __in int y
61 );
62
63
64// function definitions
65
66extern "C" void SplashScreenCreate(
67 __in HINSTANCE hInstance,
68 __in_z_opt LPCWSTR wzCaption,
69 __out HWND* pHwnd
70 )
71{
72 HRESULT hr = S_OK;
73 SPLASHSCREEN_CONTEXT context = { };
74 HANDLE rgSplashScreenEvents[2] = { };
75 DWORD dwSplashScreenThreadId = 0;
76
77 rgSplashScreenEvents[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL);
78 ExitOnNullWithLastError(rgSplashScreenEvents[0], hr, "Failed to create modal event.");
79
80 // create splash screen thread.
81 context.hInitializedEvent = rgSplashScreenEvents[0];
82 context.hInstance = hInstance;
83 context.wzCaption = wzCaption;
84 context.pHwnd = pHwnd;
85
86 rgSplashScreenEvents[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, &dwSplashScreenThreadId);
87 ExitOnNullWithLastError(rgSplashScreenEvents[1], hr, "Failed to create UI thread.");
88
89 // It doesn't really matter if the thread gets initialized (WAIT_OBJECT_0) or fails and exits
90 // prematurely (WAIT_OBJECT_0 + 1), we just want to wait long enough for one of those two
91 // events to happen.
92 ::WaitForMultipleObjects(countof(rgSplashScreenEvents), rgSplashScreenEvents, FALSE, INFINITE);
93
94LExit:
95 ReleaseHandle(rgSplashScreenEvents[1]);
96 ReleaseHandle(rgSplashScreenEvents[0]);
97}
98
99extern "C" HRESULT SplashScreenDisplayError(
100 __in BOOTSTRAPPER_DISPLAY display,
101 __in_z LPCWSTR wzBundleName,
102 __in HRESULT hrError
103 )
104{
105 HRESULT hr = S_OK;
106 LPWSTR sczDisplayString = NULL;
107
108 hr = StrAllocFromError(&sczDisplayString, hrError, NULL);
109 ExitOnFailure(hr, "Failed to allocate string to display error message");
110
111 Trace(REPORT_STANDARD, "Error message displayed because: %ls", sczDisplayString);
112
113 if (BOOTSTRAPPER_DISPLAY_NONE == display || BOOTSTRAPPER_DISPLAY_PASSIVE == display || BOOTSTRAPPER_DISPLAY_EMBEDDED == display)
114 {
115 // Don't display the error dialog in these modes
116 ExitFunction1(hr = S_OK);
117 }
118
119 ::MessageBoxW(NULL, sczDisplayString, wzBundleName, MB_OK | MB_ICONERROR | MB_SYSTEMMODAL);
120
121LExit:
122 ReleaseStr(sczDisplayString);
123
124 return hr;
125}
126
127
128static DWORD WINAPI ThreadProc(
129 __in LPVOID pvContext
130 )
131{
132 HRESULT hr = S_OK;
133 SPLASHSCREEN_CONTEXT* pContext = static_cast<SPLASHSCREEN_CONTEXT*>(pvContext);
134
135 SPLASHSCREEN_INFO splashScreenInfo = { };
136
137 WNDCLASSW wc = { };
138 BOOL fRegistered = TRUE;
139
140 BOOL fRet = FALSE;
141 MSG msg = { };
142
143 // Register the window class.
144 wc.lpfnWndProc = WndProc;
145 wc.hInstance = pContext->hInstance;
146 wc.hCursor = ::LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
147 wc.lpszClassName = BURN_SPLASHSCREEN_CLASS_WINDOW;
148 if (!::RegisterClassW(&wc))
149 {
150 ExitWithLastError(hr, "Failed to register window.");
151 }
152
153 fRegistered = TRUE;
154
155 hr = LoadSplashScreen(pContext, &splashScreenInfo);
156 ExitOnFailure(hr, "Failed to load splash screen.");
157
158 // Return the splash screen window and free the main thread waiting for us to be initialized.
159 *pContext->pHwnd = splashScreenInfo.hWnd;
160 ::SetEvent(pContext->hInitializedEvent);
161
162 // Pump messages until the bootstrapper application destroys the window.
163 while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
164 {
165 if (-1 == fRet)
166 {
167 hr = E_UNEXPECTED;
168 ExitOnFailure(hr, "Unexpected return value from message pump.");
169 }
170 else if (!::IsDialogMessageW(splashScreenInfo.hWnd, &msg))
171 {
172 ::TranslateMessage(&msg);
173 ::DispatchMessageW(&msg);
174 }
175 }
176
177LExit:
178 if (fRegistered)
179 {
180 ::UnregisterClassW(BURN_SPLASHSCREEN_CLASS_WINDOW, pContext->hInstance);
181 }
182
183 if (splashScreenInfo.hBitmap)
184 {
185 ::DeleteObject(splashScreenInfo.hBitmap);
186 }
187
188 return hr;
189}
190
191static LRESULT CALLBACK WndProc(
192 __in HWND hWnd,
193 __in UINT uMsg,
194 __in WPARAM wParam,
195 __in LPARAM lParam
196 )
197{
198 LRESULT lres = 0;
199 SPLASHSCREEN_INFO* pSplashScreen = reinterpret_cast<SPLASHSCREEN_INFO*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
200
201 switch (uMsg)
202 {
203 case WM_NCCREATE:
204 OnNcCreate(hWnd, lParam);
205 break;
206
207 case WM_NCDESTROY:
208 lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
209 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
210 ::PostQuitMessage(0);
211 return lres;
212
213 case WM_NCHITTEST:
214 return HTCAPTION; // allow window to be moved by grabbing any pixel.
215
216 case WM_DPICHANGED:
217 if (OnDpiChanged(pSplashScreen, wParam, lParam))
218 {
219 return 0;
220 }
221 break;
222
223 case WM_ERASEBKGND:
224 OnEraseBkgnd(pSplashScreen, wParam);
225 return 1;
226 }
227
228 return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
229}
230
231static HRESULT LoadSplashScreen(
232 __in SPLASHSCREEN_CONTEXT* pContext,
233 __in SPLASHSCREEN_INFO* pSplashScreen
234 )
235{
236 HRESULT hr = S_OK;
237 BITMAP bmp = { };
238 POINT pt = { };
239 int x = 0;
240 int y = 0;
241 DPIU_MONITOR_CONTEXT* pMonitorContext = NULL;
242 RECT* pMonitorRect = NULL;
243
244 pSplashScreen->nDpi = USER_DEFAULT_SCREEN_DPI;
245 pSplashScreen->hBitmap = ::LoadBitmapW(pContext->hInstance, MAKEINTRESOURCEW(IDB_SPLASHSCREEN));
246 ExitOnNullWithLastError(pSplashScreen->hBitmap, hr, "Failed to load splash screen bitmap.");
247
248 ::GetObject(pSplashScreen->hBitmap, sizeof(bmp), static_cast<void*>(&bmp));
249 pSplashScreen->defaultDpiSize.cx = pSplashScreen->size.cx = bmp.bmWidth;
250 pSplashScreen->defaultDpiSize.cy = pSplashScreen->size.cy = bmp.bmHeight;
251
252 // Try to default to the monitor with the mouse, otherwise default to the primary monitor.
253 if (!::GetCursorPos(&pt))
254 {
255 pt.x = 0;
256 pt.y = 0;
257 }
258
259 // Try to center the window on the chosen monitor.
260 hr = DpiuGetMonitorContextFromPoint(&pt, &pMonitorContext);
261 if (SUCCEEDED(hr))
262 {
263 pMonitorRect = &pMonitorContext->mi.rcWork;
264 if (pMonitorContext->nDpi != pSplashScreen->nDpi)
265 {
266 ScaleSplashScreen(pSplashScreen, pMonitorContext->nDpi, pMonitorRect->left, pMonitorRect->top);
267 }
268
269 x = pMonitorRect->left + (pMonitorRect->right - pMonitorRect->left - pSplashScreen->size.cx) / 2;
270 y = pMonitorRect->top + (pMonitorRect->bottom - pMonitorRect->top - pSplashScreen->size.cy) / 2;
271 }
272 else
273 {
274 hr = S_OK;
275 x = CW_USEDEFAULT;
276 y = CW_USEDEFAULT;
277 }
278
279 pSplashScreen->hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, BURN_SPLASHSCREEN_CLASS_WINDOW, pContext->wzCaption, WS_POPUP | WS_VISIBLE, x, y, pSplashScreen->size.cx, pSplashScreen->size.cy, HWND_DESKTOP, NULL, pContext->hInstance, pSplashScreen);
280 ExitOnNullWithLastError(pSplashScreen->hWnd, hr, "Failed to create window.");
281
282LExit:
283 MemFree(pMonitorContext);
284
285 return hr;
286}
287
288static BOOL OnDpiChanged(
289 __in SPLASHSCREEN_INFO* pSplashScreen,
290 __in WPARAM wParam,
291 __in LPARAM lParam
292 )
293{
294 UINT nDpi = HIWORD(wParam);
295 RECT* pRect = reinterpret_cast<RECT*>(lParam);
296 BOOL fDpiChanged = pSplashScreen->nDpi != nDpi;
297
298 if (fDpiChanged)
299 {
300 ScaleSplashScreen(pSplashScreen, nDpi, pRect->left, pRect->top);
301 }
302
303 return fDpiChanged;
304}
305
306static void OnEraseBkgnd(
307 __in SPLASHSCREEN_INFO* pSplashScreen,
308 __in WPARAM wParam
309 )
310{
311 HDC hdc = reinterpret_cast<HDC>(wParam);
312 HDC hdcMem = ::CreateCompatibleDC(hdc);
313 HBITMAP hDefaultBitmap = static_cast<HBITMAP>(::SelectObject(hdcMem, pSplashScreen->hBitmap));
314 ::StretchBlt(hdc, 0, 0, pSplashScreen->size.cx, pSplashScreen->size.cy, hdcMem, 0, 0, pSplashScreen->defaultDpiSize.cx, pSplashScreen->defaultDpiSize.cy, SRCCOPY);
315 ::SelectObject(hdcMem, hDefaultBitmap);
316 ::DeleteDC(hdcMem);
317}
318
319static void OnNcCreate(
320 __in HWND hWnd,
321 __in LPARAM lParam
322 )
323{
324 DPIU_WINDOW_CONTEXT windowContext = { };
325 CREATESTRUCTW* pCreateStruct = reinterpret_cast<CREATESTRUCTW*>(lParam);
326 SPLASHSCREEN_INFO* pSplashScreen = reinterpret_cast<SPLASHSCREEN_INFO*>(pCreateStruct->lpCreateParams);
327
328 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pSplashScreen));
329 pSplashScreen->hWnd = hWnd;
330
331 DpiuGetWindowContext(pSplashScreen->hWnd, &windowContext);
332
333 if (windowContext.nDpi != pSplashScreen->nDpi)
334 {
335 ScaleSplashScreen(pSplashScreen, windowContext.nDpi, pCreateStruct->x, pCreateStruct->y);
336 }
337}
338
339static void ScaleSplashScreen(
340 __in SPLASHSCREEN_INFO* pSplashScreen,
341 __in UINT nDpi,
342 __in int x,
343 __in int y
344 )
345{
346 pSplashScreen->nDpi = nDpi;
347
348 pSplashScreen->size.cx = DpiuScaleValue(pSplashScreen->defaultDpiSize.cx, pSplashScreen->nDpi);
349 pSplashScreen->size.cy = DpiuScaleValue(pSplashScreen->defaultDpiSize.cy, pSplashScreen->nDpi);
350
351 if (pSplashScreen->hWnd)
352 {
353 ::SetWindowPos(pSplashScreen->hWnd, NULL, x, y, pSplashScreen->size.cx, pSplashScreen->size.cy, SWP_NOACTIVATE | SWP_NOZORDER);
354 }
355}