@@ -15,14 +15,109 @@ static bool bCancelPressed = false;
15
15
static bool bOkPressed = false ;
16
16
static bool bOtherPressed = false ;
17
17
static int iOtherCode = 0 ;
18
- static HWND hwndSplash = NULL ;
18
+ static WNDCLASS splashWindowClass{};
19
+ static HWND splashWindow{};
19
20
static HWND hwndProgressDialog = NULL ;
20
21
static unsigned long ulProgressStartTime = 0 ;
21
22
static HWND hwndCrashedDialog = NULL ;
22
23
static HWND hwndGraphicsDllDialog = NULL ;
23
24
static HWND hwndOptimusDialog = NULL ;
24
25
static HWND hwndNoAvDialog = NULL ;
25
26
27
+ /* *
28
+ * @brief Automatically destroys a window on scope-exit.
29
+ */
30
+ struct WindowScope
31
+ {
32
+ WindowScope (HWND handle_) noexcept : handle(handle_) {}
33
+
34
+ ~WindowScope () noexcept { DestroyWindow (handle); }
35
+
36
+ [[nodiscard]] HWND Release () noexcept { return std::exchange (handle, nullptr ); }
37
+
38
+ HWND handle{};
39
+ };
40
+
41
+ /* *
42
+ * @brief Provides a compatible memory-only device context for a bitmap handle and
43
+ * automatically destroys the device context and bitmap on scope-exit.
44
+ */
45
+ struct BitmapScope
46
+ {
47
+ BitmapScope (HDC deviceContext_, HBITMAP bitmap_) noexcept : bitmap(bitmap_)
48
+ {
49
+ if (bitmap != nullptr )
50
+ {
51
+ if (deviceContext = CreateCompatibleDC (deviceContext_))
52
+ {
53
+ previousObject = SelectObject (deviceContext, bitmap_);
54
+ }
55
+ }
56
+ }
57
+
58
+ ~BitmapScope () noexcept
59
+ {
60
+ if (previousObject && previousObject != HGDI_ERROR)
61
+ SelectObject (deviceContext, previousObject);
62
+
63
+ if (deviceContext)
64
+ DeleteDC (deviceContext);
65
+
66
+ if (bitmap)
67
+ DeleteObject (bitmap);
68
+ }
69
+
70
+ HDC deviceContext{};
71
+ HGDIOBJ previousObject{};
72
+ HBITMAP bitmap{};
73
+ };
74
+
75
+ /* *
76
+ * @brief Returns the dots per inch (dpi) value for the specified window.
77
+ */
78
+ UINT GetWindowDpi (HWND window)
79
+ {
80
+ // Minimum version: Windows 10, version 1607
81
+ using GetDpiForWindow_t = UINT (WINAPI*)(HWND);
82
+
83
+ static GetDpiForWindow_t Win32GetDpiForWindow = ([] {
84
+ HMODULE user32 = LoadLibrary (" user32" );
85
+ return user32 ? reinterpret_cast <GetDpiForWindow_t>(static_cast <void *>(GetProcAddress (user32, " GetDpiForWindow" ))) : nullptr ;
86
+ })();
87
+
88
+ if (Win32GetDpiForWindow)
89
+ return Win32GetDpiForWindow (window);
90
+
91
+ HDC screenDC = GetDC (NULL );
92
+ auto x = static_cast <UINT>(GetDeviceCaps (screenDC, LOGPIXELSX));
93
+ ReleaseDC (NULL , screenDC);
94
+ return x;
95
+ }
96
+
97
+ /* *
98
+ * @brief Returns the width and height of the primary monitor.
99
+ */
100
+ SIZE GetPrimaryMonitorSize ()
101
+ {
102
+ POINT zero{};
103
+ HMONITOR primaryMonitor = MonitorFromPoint (zero, MONITOR_DEFAULTTOPRIMARY);
104
+
105
+ MONITORINFO monitorInfo{};
106
+ monitorInfo.cbSize = sizeof (monitorInfo);
107
+ GetMonitorInfo (primaryMonitor, &monitorInfo);
108
+
109
+ const RECT& work = monitorInfo.rcWork ;
110
+ return {work.right - work.left , work.bottom - work.top };
111
+ }
112
+
113
+ /* *
114
+ * @brief Scales the value from the default screen dpi (96) to the provided dpi.
115
+ */
116
+ LONG ScaleToDpi (LONG value, UINT dpi)
117
+ {
118
+ return static_cast <LONG>(ceil (value * static_cast <float >(dpi) / USER_DEFAULT_SCREEN_DPI));
119
+ }
120
+
26
121
// /////////////////////////////////////////////////////////////////////////
27
122
//
28
123
// Dialog strings
@@ -188,23 +283,63 @@ int CALLBACK DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
188
283
void ShowSplash (HINSTANCE hInstance)
189
284
{
190
285
#ifndef MTA_DEBUG
191
- if (!hwndSplash)
286
+ if (splashWindowClass.hInstance != hInstance)
287
+ {
288
+ splashWindowClass.lpfnWndProc = DefWindowProc;
289
+ splashWindowClass.hInstance = hInstance;
290
+ splashWindowClass.hCursor = LoadCursor (NULL , IDC_ARROW);
291
+ splashWindowClass.lpszClassName = TEXT (" SplashWindow" );
292
+ RegisterClass (&splashWindowClass);
293
+ }
294
+
295
+ if (splashWindow)
296
+ {
297
+ ShowWindow (splashWindow, SW_SHOW);
298
+ }
299
+ else
192
300
{
193
- hwndSplash = CreateDialog (hInstance, MAKEINTRESOURCE (IDD_DIALOG1), 0 , DialogProc);
301
+ WindowScope window (CreateWindowEx (WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW, splashWindowClass.lpszClassName , NULL , WS_POPUP | WS_VISIBLE,
302
+ 0 , 0 , 0 , 0 , NULL , NULL , hInstance, NULL ));
303
+
304
+ if (!window.handle )
305
+ return ;
306
+
307
+ UINT dpi = GetWindowDpi (window.handle );
308
+ SIZE monitorSize = GetPrimaryMonitorSize ();
309
+
310
+ POINT origin{};
311
+ SIZE windowSize{ScaleToDpi (500 , dpi), ScaleToDpi (245 , dpi)};
312
+ origin.x = (monitorSize.cx - windowSize.cx ) / 2 ;
313
+ origin.y = (monitorSize.cy - windowSize.cy ) / 2 ;
314
+
315
+ HDC windowHDC = GetWindowDC (window.handle );
316
+ {
317
+ BitmapScope unscaled (windowHDC, LoadBitmap (hInstance, MAKEINTRESOURCE (IDB_BITMAP1)));
194
318
195
- HWND hBitmap = GetDlgItem (hwndSplash, IDC_SPLASHBITMAP);
196
- RECT splashRect;
197
- GetWindowRect (hBitmap, &splashRect);
198
- int iScreenWidth = GetSystemMetrics (SM_CXSCREEN);
199
- int iScreenHeight = GetSystemMetrics (SM_CYSCREEN);
200
- int iWindowWidth = splashRect.right - splashRect.left ;
201
- int iWindowHeight = splashRect.bottom - splashRect.top ;
319
+ if (!unscaled.bitmap )
320
+ return ;
321
+
322
+ BitmapScope scaled (windowHDC, CreateCompatibleBitmap (windowHDC, windowSize.cx , windowSize.cy ));
323
+
324
+ if (!scaled.bitmap )
325
+ return ;
326
+
327
+ BITMAP bitmap{};
328
+ GetObject (unscaled.bitmap , sizeof (bitmap), &bitmap);
329
+
330
+ // Draw the unscaled bitmap to the window-scaled bitmap.
331
+ SetStretchBltMode (scaled.deviceContext , HALFTONE);
332
+ StretchBlt (scaled.deviceContext , 0 , 0 , windowSize.cx , windowSize.cy , unscaled.deviceContext , 0 , 0 , bitmap.bmWidth , bitmap.bmHeight , SRCCOPY);
333
+
334
+ // Update the splash window and draw the scaled bitmap.
335
+ POINT zero{};
336
+ BLENDFUNCTION blend{AC_SRC_OVER, 0 , 255 , AC_SRC_ALPHA};
337
+ UpdateLayeredWindow (window.handle , windowHDC, &origin, &windowSize, scaled.deviceContext , &zero, RGB (0 , 0 , 0 ), &blend, 0 );
338
+ }
339
+ ReleaseDC (window.handle , windowHDC);
202
340
203
- // Adjust and center the window (to be DPI-aware)
204
- SetWindowPos (hwndSplash, NULL , (iScreenWidth - iWindowWidth) / 2 , (iScreenHeight - iWindowHeight) / 2 , iWindowWidth, iWindowHeight, 0 );
341
+ splashWindow = window.Release ();
205
342
}
206
- SetForegroundWindow (hwndSplash);
207
- SetWindowPos (hwndSplash, HWND_TOP, 0 , 0 , 0 , 0 , SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
208
343
209
344
// Drain messages to allow for repaint in case picture bits were lost during previous operations
210
345
MSG msg;
@@ -224,10 +359,10 @@ void ShowSplash(HINSTANCE hInstance)
224
359
//
225
360
void HideSplash ()
226
361
{
227
- if (hwndSplash )
362
+ if (splashWindow )
228
363
{
229
- DestroyWindow (hwndSplash );
230
- hwndSplash = NULL ;
364
+ DestroyWindow (splashWindow );
365
+ splashWindow = {} ;
231
366
}
232
367
}
233
368
@@ -236,9 +371,9 @@ void HideSplash()
236
371
//
237
372
void SuspendSplash ()
238
373
{
239
- if (hwndSplash )
374
+ if (splashWindow )
240
375
{
241
- ShowWindow (hwndSplash , SW_HIDE);
376
+ ShowWindow (splashWindow , SW_HIDE);
242
377
}
243
378
}
244
379
@@ -247,7 +382,7 @@ void SuspendSplash()
247
382
//
248
383
void ResumeSplash ()
249
384
{
250
- if (hwndSplash )
385
+ if (splashWindow )
251
386
{
252
387
HideSplash ();
253
388
ShowSplash (g_hInstance);
0 commit comments