@@ -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
716726Q_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+
718736struct 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