Skip to content
This repository was archived by the owner on Dec 19, 2023. It is now read-only.

Commit ddb2dcc

Browse files
committed
win: fix some rare bugs, add more comments
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
1 parent 70a7182 commit ddb2dcc

File tree

3 files changed

+114
-47
lines changed

3 files changed

+114
-47
lines changed

include/FramelessHelper/Core/utils.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ FRAMELESSHELPER_CORE_API void enableNonClientAreaDpiScalingForWindow(const WId w
120120
Global::DpiAwareness getDpiAwarenessForCurrentProcess(bool *highest = nullptr);
121121
FRAMELESSHELPER_CORE_API void fixupChildWindowsDpiMessage(const WId windowId);
122122
FRAMELESSHELPER_CORE_API void fixupDialogsDpiScaling();
123-
FRAMELESSHELPER_CORE_API void setDarkModeEnabledForApp(const bool enable = true);
123+
FRAMELESSHELPER_CORE_API void setDarkModeAllowedForApp(const bool allow = true);
124124
#endif // Q_OS_WINDOWS
125125

126126
#ifdef Q_OS_LINUX

src/core/framelesshelpercore_global.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ void setApplicationOSThemeAware()
318318
set = true;
319319

320320
#ifdef Q_OS_WINDOWS
321-
Utils::setDarkModeEnabledForApp(true);
321+
Utils::setDarkModeAllowedForApp(true);
322322
# if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
323323
Utils::setQtDarkModeAwareEnabled(true);
324324
# endif

src/core/utils_win.cpp

Lines changed: 112 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -432,16 +432,23 @@ EnableChildWindowDpiMessage2(const HWND hWnd, const BOOL fEnable)
432432
using EnableChildWindowDpiMessagePtr = decltype(&::_EnableChildWindowDpiMessage);
433433
static const auto pEnableChildWindowDpiMessage = []() -> EnableChildWindowDpiMessagePtr {
434434
FRAMELESSHELPER_USE_NAMESPACE
435+
FRAMELESSHELPER_STRING_CONSTANT(win32u)
436+
FRAMELESSHELPER_STRING_CONSTANT(NtUserEnableChildWindowDpiMessage)
437+
// EnableChildWindowDpiMessage() was moved to "win32u.dll" and renamed to
438+
// "NtUserEnableChildWindowDpiMessage" since Win10 1607.
439+
if (const auto pFunc = reinterpret_cast<EnableChildWindowDpiMessagePtr>(
440+
SysApiLoader::resolve(kwin32u, kNtUserEnableChildWindowDpiMessage))) {
441+
return pFunc;
442+
}
435443
FRAMELESSHELPER_STRING_CONSTANT(user32)
436444
FRAMELESSHELPER_STRING_CONSTANT(EnableChildWindowDpiMessage)
437445
// EnableChildWindowDpiMessage() was once a public API, so we can load it by name,
438-
// but it got removed in some later SDK versions, so we can't link to it directly.
439-
// I haven't check the accurate time point of its removal.
446+
// but it got removed in Win10 1607, so we can't link to it directly.
440447
if (const auto pFunc = reinterpret_cast<EnableChildWindowDpiMessagePtr>(
441448
SysApiLoader::resolve(kuser32, kEnableChildWindowDpiMessage))) {
442449
return pFunc;
443450
}
444-
// EnableChildWindowDpiMessage() was a private API when first introduced.
451+
// EnableChildWindowDpiMessage() was made private since Win10 1607.
445452
if (const auto pFunc = reinterpret_cast<EnableChildWindowDpiMessagePtr>(
446453
SysApiLoader::resolve(kuser32, MAKEINTRESOURCEA(2704)))) {
447454
return pFunc;
@@ -464,13 +471,12 @@ EnablePerMonitorDialogScaling2(VOID)
464471
FRAMELESSHELPER_STRING_CONSTANT(user32)
465472
FRAMELESSHELPER_STRING_CONSTANT(EnablePerMonitorDialogScaling)
466473
// EnablePerMonitorDialogScaling() was once a public API, so we can load it by name,
467-
// but it got removed in some later SDK versions, so we can't link to it directly.
468-
// I haven't check the accurate time point of its removal.
474+
// but it got removed in Win10 1607, so we can't link to it directly.
469475
if (const auto pFunc = reinterpret_cast<EnablePerMonitorDialogScalingPtr>(
470476
SysApiLoader::resolve(kuser32, kEnablePerMonitorDialogScaling))) {
471477
return pFunc;
472478
}
473-
// EnablePerMonitorDialogScaling() was a private API when first introduced.
479+
// EnablePerMonitorDialogScaling() was made private since Win10 1607.
474480
if (const auto pFunc = reinterpret_cast<EnablePerMonitorDialogScalingPtr>(
475481
SysApiLoader::resolve(kuser32, MAKEINTRESOURCEA(2577)))) {
476482
return pFunc;
@@ -492,6 +498,7 @@ GetDpiForWindow2(const HWND hWnd)
492498
FRAMELESSHELPER_USE_NAMESPACE
493499
FRAMELESSHELPER_STRING_CONSTANT(user32)
494500
FRAMELESSHELPER_STRING_CONSTANT(GetDpiForWindow)
501+
// GetDpiForWindow() was made public since Win10 1607.
495502
if (const auto pFunc = reinterpret_cast<GetDpiForWindowPtr>(
496503
SysApiLoader::resolve(kuser32, kGetDpiForWindow))) {
497504
return pFunc;
@@ -502,7 +509,7 @@ GetDpiForWindow2(const HWND hWnd)
502509
SysApiLoader::resolve(kuser32, kGetWindowDPI))) {
503510
return pFunc;
504511
}
505-
// GetDpiForWindow() was a private API when first introduced.
512+
// GetWindowDPI() was made private since Win10 1607.
506513
if (const auto pFunc = reinterpret_cast<GetDpiForWindowPtr>(
507514
SysApiLoader::resolve(kuser32, MAKEINTRESOURCEA(2707)))) {
508515
return pFunc;
@@ -524,11 +531,13 @@ GetSystemMetricsForDpi2(const int nIndex, const UINT dpi)
524531
FRAMELESSHELPER_USE_NAMESPACE
525532
FRAMELESSHELPER_STRING_CONSTANT(user32)
526533
FRAMELESSHELPER_STRING_CONSTANT(GetSystemMetricsForDpi)
534+
// GetSystemMetricsForDpi() was made public since Win10 1607.
527535
if (const auto pFunc = reinterpret_cast<GetSystemMetricsForDpiPtr>(
528536
SysApiLoader::resolve(kuser32, kGetSystemMetricsForDpi))) {
529537
return pFunc;
530538
}
531-
// GetSystemMetricsForDpi() was named "GetDpiMetrics" before made public.
539+
// GetSystemMetricsForDpi() was named "GetDpiMetrics" before made public,
540+
// that is, when in Win10 1507 & 1511.
532541
FRAMELESSHELPER_STRING_CONSTANT(GetDpiMetrics)
533542
if (const auto pFunc = reinterpret_cast<GetSystemMetricsForDpiPtr>(
534543
SysApiLoader::resolve(kuser32, kGetDpiMetrics))) {
@@ -552,11 +561,12 @@ AdjustWindowRectExForDpi2(LPRECT lpRect, const DWORD dwStyle,
552561
FRAMELESSHELPER_USE_NAMESPACE
553562
FRAMELESSHELPER_STRING_CONSTANT(user32)
554563
FRAMELESSHELPER_STRING_CONSTANT(AdjustWindowRectExForDpi)
564+
// AdjustWindowRectExForDpi() was made public since Win10 1607.
555565
if (const auto pFunc = reinterpret_cast<AdjustWindowRectExForDpiPtr>(
556566
SysApiLoader::resolve(kuser32, kAdjustWindowRectExForDpi))) {
557567
return pFunc;
558568
}
559-
// AdjustWindowRectExForDpi() was a private API when first introduced.
569+
// AdjustWindowRectExForDpi() was made private in Win10 1507 & 1511.
560570
if (const auto pFunc = reinterpret_cast<AdjustWindowRectExForDpiPtr>(
561571
SysApiLoader::resolve(kuser32, MAKEINTRESOURCEA(2580)))) {
562572
return pFunc;
@@ -715,6 +725,14 @@ struct Win32UtilsHelper
715725

716726
Q_GLOBAL_STATIC(Win32UtilsHelper, g_utilsHelper)
717727

728+
struct MicaWindowData
729+
{
730+
QMutex mutex;
731+
QList<WId> windowIds = {};
732+
};
733+
734+
Q_GLOBAL_STATIC(MicaWindowData, g_micaData)
735+
718736
struct SYSTEM_METRIC
719737
{
720738
int DPI_96 = 0; // 100%. The scale factor for the device is 1x.
@@ -1058,12 +1076,22 @@ void Utils::updateWindowFrameMargins(const WId windowId, const bool reset)
10581076
if (!API_DWM_AVAILABLE(DwmExtendFrameIntoClientArea)) {
10591077
return;
10601078
}
1061-
const MARGINS margins = [reset]() -> MARGINS {
1079+
g_micaData()->mutex.lock();
1080+
const bool micaEnabled = g_micaData()->windowIds.contains(windowId);
1081+
g_micaData()->mutex.unlock();
1082+
const auto margins = [micaEnabled, reset]() -> MARGINS {
1083+
// To make Mica/Mica Alt work for normal Win32 windows, we have to
1084+
// let the window frame extend to the whole window (or disable the
1085+
// redirection surface, but this will break GDI's rendering, so we
1086+
// can't do this, unfortunately), so we can't change the window frame
1087+
// margins in this case, otherwise Mica/Mica Alt will be broken.
1088+
if (micaEnabled) {
1089+
return {-1, -1, -1, -1};
1090+
}
10621091
if (reset || isWindowFrameBorderVisible()) {
10631092
return {0, 0, 0, 0};
1064-
} else {
1065-
return {1, 1, 1, 1};
10661093
}
1094+
return {1, 1, 1, 1};
10671095
}();
10681096
const auto hwnd = reinterpret_cast<HWND>(windowId);
10691097
const HRESULT hr = API_CALL_FUNCTION(DwmExtendFrameIntoClientArea, hwnd, &margins);
@@ -2071,8 +2099,17 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
20712099
if (WindowsVersionHelper::isWin8OrGreater()) {
20722100
if (!(API_DWM_AVAILABLE(DwmSetWindowAttribute)
20732101
&& API_DWM_AVAILABLE(DwmExtendFrameIntoClientArea))) {
2102+
WARNING << "Blur behind window is not available on current platform.";
20742103
return false;
20752104
}
2105+
const auto restoreWindowFrameMargins = [windowId]() -> void {
2106+
g_micaData()->mutex.lock();
2107+
if (g_micaData()->windowIds.contains(windowId)) {
2108+
g_micaData()->windowIds.removeAll(windowId);
2109+
}
2110+
g_micaData()->mutex.unlock();
2111+
updateWindowFrameMargins(windowId, false);
2112+
};
20762113
const BlurMode blurMode = [mode]() -> BlurMode {
20772114
if ((mode == BlurMode::Disable) || (mode == BlurMode::Windows_Aero)) {
20782115
return mode;
@@ -2102,26 +2139,23 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
21022139
return mode;
21032140
}();
21042141
if (blurMode == BlurMode::Disable) {
2142+
bool result = true;
21052143
if (WindowsVersionHelper::isWin1122H2OrGreater()) {
21062144
const _DWM_SYSTEMBACKDROP_TYPE dwmsbt = _DWMSBT_NONE;
21072145
const HRESULT hr = API_CALL_FUNCTION(DwmSetWindowAttribute,
21082146
hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &dwmsbt, sizeof(dwmsbt));
21092147
if (FAILED(hr)) {
2148+
result = false;
21102149
WARNING << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
21112150
}
2112-
}
2113-
if (WindowsVersionHelper::isWin11OrGreater()) {
2151+
} else if (WindowsVersionHelper::isWin11OrGreater()) {
21142152
const BOOL enable = FALSE;
21152153
HRESULT hr = API_CALL_FUNCTION(DwmSetWindowAttribute,
21162154
hwnd, _DWMWA_MICA_EFFECT, &enable, sizeof(enable));
21172155
if (FAILED(hr)) {
2156+
result = false;
21182157
WARNING << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
21192158
}
2120-
const MARGINS margins = {0, 0, 0, 0};
2121-
hr = API_CALL_FUNCTION(DwmExtendFrameIntoClientArea, hwnd, &margins);
2122-
if (FAILED(hr)) {
2123-
WARNING << __getSystemErrorMessage(kDwmExtendFrameIntoClientArea, hr);
2124-
}
21252159
} else {
21262160
ACCENT_POLICY policy;
21272161
SecureZeroMemory(&policy, sizeof(policy));
@@ -2132,21 +2166,43 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
21322166
wcad.pvData = &policy;
21332167
wcad.cbData = sizeof(policy);
21342168
if (SetWindowCompositionAttribute(hwnd, &wcad) == FALSE) {
2169+
result = false;
21352170
WARNING << getSystemErrorMessage(kSetWindowCompositionAttribute);
21362171
}
21372172
}
2138-
return true;
2173+
if (WindowsVersionHelper::isWin11OrGreater()) {
2174+
restoreWindowFrameMargins();
2175+
}
2176+
return result;
21392177
} else {
21402178
if (blurMode == BlurMode::Windows_Mica) {
2179+
g_micaData()->mutex.lock();
2180+
if (!g_micaData()->windowIds.contains(windowId)) {
2181+
g_micaData()->windowIds.append(windowId);
2182+
}
2183+
g_micaData()->mutex.unlock();
21412184
// By giving a negative value, DWM will extend the window frame into the whole
21422185
// client area. We need this step because the Mica material can only be applied
21432186
// to the non-client area of a window. Without this step, you'll get a window
21442187
// with a pure black background.
2188+
// Actually disabling the redirection surface (by enabling WS_EX_NOREDIRECTIONBITMAP
2189+
// when you call CreateWindow(), it won't have any effect if you set it after the
2190+
// window has been created) can achieve the same effect with extending the window
2191+
// frame, however, it will completely break GDI's rendering, so sadly we can't choose
2192+
// this solution. But this can be used if you can make sure your application don't
2193+
// use GDI at all, for example, you only use Direct3D to draw your window (like
2194+
// UWP/WPF applications). And one additional note, it will also break OpenGL and Vulkan
2195+
// due to they also use the legacy swap chain model. In theory you can try this flag
2196+
// for Qt Quick applications when the rhi backend is Direct3D, however, some elements
2197+
// will still be broken because Qt Quick still use GDI to render some native controls
2198+
// such as the window menu.
21452199
const MARGINS margins = {-1, -1, -1, -1};
21462200
HRESULT hr = API_CALL_FUNCTION(DwmExtendFrameIntoClientArea, hwnd, &margins);
21472201
if (SUCCEEDED(hr)) {
21482202
if (WindowsVersionHelper::isWin1122H2OrGreater()) {
2149-
const _DWM_SYSTEMBACKDROP_TYPE dwmsbt = _DWMSBT_MAINWINDOW; // Mica
2203+
const auto dwmsbt = (
2204+
qEnvironmentVariableIntValue("FRAMELESSHELPER_USE_MICA_ALT")
2205+
? _DWMSBT_TABBEDWINDOW : _DWMSBT_MAINWINDOW);
21502206
hr = API_CALL_FUNCTION(DwmSetWindowAttribute, hwnd,
21512207
_DWMWA_SYSTEMBACKDROP_TYPE, &dwmsbt, sizeof(dwmsbt));
21522208
if (SUCCEEDED(hr)) {
@@ -2167,12 +2223,14 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
21672223
} else {
21682224
WARNING << __getSystemErrorMessage(kDwmExtendFrameIntoClientArea, hr);
21692225
}
2226+
restoreWindowFrameMargins();
21702227
} else {
21712228
ACCENT_POLICY policy;
21722229
SecureZeroMemory(&policy, sizeof(policy));
21732230
if (blurMode == BlurMode::Windows_Acrylic) {
21742231
policy.State = ACCENT_ENABLE_ACRYLICBLURBEHIND;
2175-
policy.Flags = 2; // Magic number, this member must be set to 2.
2232+
// Magic number, this member must be set to 2, otherwise will have no effect, don't know why.
2233+
policy.Flags = 2;
21762234
const QColor gradientColor = [&color]() -> QColor {
21772235
if (color.isValid()) {
21782236
return color;
@@ -2195,12 +2253,14 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
21952253
wcad.pvData = &policy;
21962254
wcad.cbData = sizeof(policy);
21972255
if (SetWindowCompositionAttribute(hwnd, &wcad) != FALSE) {
2198-
if (!WindowsVersionHelper::isWin11OrGreater()) {
2256+
if ((blurMode == BlurMode::Windows_Acrylic)
2257+
&& !WindowsVersionHelper::isWin11OrGreater()) {
21992258
DEBUG << "Enabling the Acrylic blur for Win32 windows on Windows 10 "
22002259
"is very buggy. The only recommended way by Microsoft is to "
22012260
"use the XAML Island technology or use pure UWP instead. If "
22022261
"you find your window becomes very laggy during moving and "
2203-
"resizing, please disable the Acrylic blur immediately.";
2262+
"resizing, please disable the Acrylic blur immediately (or "
2263+
"disable the transparent effect in your personalize settings).";
22042264
}
22052265
return true;
22062266
}
@@ -2210,25 +2270,27 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
22102270
} else {
22112271
// We prefer to use "DwmEnableBlurBehindWindow" on Windows 7 because it behaves
22122272
// better than the undocumented API.
2213-
if (API_DWM_AVAILABLE(DwmEnableBlurBehindWindow)) {
2214-
DWM_BLURBEHIND dwmbb;
2215-
SecureZeroMemory(&dwmbb, sizeof(dwmbb));
2216-
dwmbb.dwFlags = DWM_BB_ENABLE;
2217-
dwmbb.fEnable = [mode]() -> BOOL {
2218-
if (mode == BlurMode::Disable) {
2219-
return FALSE;
2220-
}
2221-
if ((mode != BlurMode::Default) && (mode != BlurMode::Windows_Aero)) {
2222-
WARNING << "The only supported blur mode on Windows 7 is the traditional DWM blur.";
2223-
}
2224-
return TRUE;
2225-
}();
2226-
const HRESULT hr = API_CALL_FUNCTION(DwmEnableBlurBehindWindow, hwnd, &dwmbb);
2227-
if (SUCCEEDED(hr)) {
2228-
return true;
2273+
if (!API_DWM_AVAILABLE(DwmEnableBlurBehindWindow)) {
2274+
WARNING << "Blur behind window is not available on current platform.";
2275+
return false;
2276+
}
2277+
DWM_BLURBEHIND dwmbb;
2278+
SecureZeroMemory(&dwmbb, sizeof(dwmbb));
2279+
dwmbb.dwFlags = DWM_BB_ENABLE;
2280+
dwmbb.fEnable = [mode]() -> BOOL {
2281+
if (mode == BlurMode::Disable) {
2282+
return FALSE;
2283+
}
2284+
if ((mode != BlurMode::Default) && (mode != BlurMode::Windows_Aero)) {
2285+
WARNING << "The only supported blur mode on Windows 7 is the traditional DWM blur.";
22292286
}
2230-
WARNING << __getSystemErrorMessage(kDwmEnableBlurBehindWindow, hr);
2287+
return TRUE;
2288+
}();
2289+
const HRESULT hr = API_CALL_FUNCTION(DwmEnableBlurBehindWindow, hwnd, &dwmbb);
2290+
if (SUCCEEDED(hr)) {
2291+
return true;
22312292
}
2293+
WARNING << __getSystemErrorMessage(kDwmEnableBlurBehindWindow, hr);
22322294
}
22332295
return false;
22342296
}
@@ -2618,14 +2680,19 @@ void Utils::fixupDialogsDpiScaling()
26182680
WARNING << getSystemErrorMessage(kEnablePerMonitorDialogScaling);
26192681
}
26202682

2621-
void Utils::setDarkModeEnabledForApp(const bool enable)
2683+
void Utils::setDarkModeAllowedForApp(const bool allow)
26222684
{
2685+
// This hack is only available since Win10 1809.
2686+
if (!WindowsVersionHelper::isWin10RS5OrGreater()) {
2687+
return;
2688+
}
2689+
// This hack is necessary to let AllowDarkModeForWindow() work ...
26232690
if (WindowsVersionHelper::isWin1019H1OrGreater()) {
2624-
if (SetPreferredAppMode(enable ? PAM_AUTO : PAM_DEFAULT) == PAM_MAX) {
2691+
if (SetPreferredAppMode(allow ? PAM_AUTO : PAM_DEFAULT) == PAM_MAX) {
26252692
WARNING << getSystemErrorMessage(kSetPreferredAppMode);
26262693
}
2627-
} else if (WindowsVersionHelper::isWin10RS5OrGreater()) {
2628-
if (AllowDarkModeForApp(enable ? TRUE : FALSE) == FALSE) {
2694+
} else {
2695+
if (AllowDarkModeForApp(allow ? TRUE : FALSE) == FALSE) {
26292696
WARNING << getSystemErrorMessage(kAllowDarkModeForApp);
26302697
}
26312698
}

0 commit comments

Comments
 (0)