diff --git a/include/FramelessHelper/Core/framelesshelper_windows.h b/include/FramelessHelper/Core/framelesshelper_windows.h index 4237c54..369b94c 100644 --- a/include/FramelessHelper/Core/framelesshelper_windows.h +++ b/include/FramelessHelper/Core/framelesshelper_windows.h @@ -146,11 +146,11 @@ #ifndef _DPI_AWARENESS_CONTEXTS_ # define _DPI_AWARENESS_CONTEXTS_ DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); -# define DPI_AWARENESS_CONTEXT_UNAWARE ((DPI_AWARENESS_CONTEXT)-1) -# define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE ((DPI_AWARENESS_CONTEXT)-2) -# define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((DPI_AWARENESS_CONTEXT)-3) -# define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4) -# define DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED ((DPI_AWARENESS_CONTEXT)-5) +# define DPI_AWARENESS_CONTEXT_UNAWARE (reinterpret_cast(-1)) +# define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE (reinterpret_cast(-2)) +# define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE (reinterpret_cast(-3)) +# define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 (reinterpret_cast(-4)) +# define DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED (reinterpret_cast(-5)) #endif #ifndef STATUS_SUCCESS @@ -160,8 +160,6 @@ using NTSTATUS = LONG; #ifndef WINMMAPI -# define WINMMAPI DECLSPEC_IMPORT - using MMRESULT = UINT; using TIMECAPS = struct TIMECAPS @@ -190,6 +188,27 @@ using MONITOR_DPI_TYPE = enum MONITOR_DPI_TYPE MDT_DEFAULT = MDT_EFFECTIVE_DPI }; +using DEVICE_SCALE_FACTOR = enum DEVICE_SCALE_FACTOR +{ + DEVICE_SCALE_FACTOR_INVALID = 0, + SCALE_100_PERCENT = 100, + SCALE_120_PERCENT = 120, + SCALE_125_PERCENT = 125, + SCALE_140_PERCENT = 140, + SCALE_150_PERCENT = 150, + SCALE_160_PERCENT = 160, + SCALE_175_PERCENT = 175, + SCALE_180_PERCENT = 180, + SCALE_200_PERCENT = 200, + SCALE_225_PERCENT = 225, + SCALE_250_PERCENT = 250, + SCALE_300_PERCENT = 300, + SCALE_350_PERCENT = 350, + SCALE_400_PERCENT = 400, + SCALE_450_PERCENT = 450, + SCALE_500_PERCENT = 500 +}; + using _DWMWINDOWATTRIBUTE = enum _DWMWINDOWATTRIBUTE { _DWMWA_USE_HOSTBACKDROPBRUSH = 17, // [set] BOOL, Allows the use of host backdrop brushes for the window. @@ -290,18 +309,18 @@ using SetWindowCompositionAttributePtr = BOOL(WINAPI *)(HWND, PWINDOWCOMPOSITION EXTERN_C_START -WINMMAPI MMRESULT WINAPI +DECLSPEC_IMPORT MMRESULT WINAPI timeGetDevCaps( _Out_writes_bytes_(cbtc) LPTIMECAPS ptc, _In_ UINT cbtc ); -WINMMAPI MMRESULT WINAPI +DECLSPEC_IMPORT MMRESULT WINAPI timeBeginPeriod( _In_ UINT uPeriod ); -WINMMAPI MMRESULT WINAPI +DECLSPEC_IMPORT MMRESULT WINAPI timeEndPeriod( _In_ UINT uPeriod ); @@ -319,37 +338,43 @@ GetDpiForMonitor( _Out_ UINT *dpiY ); -WINUSERAPI int WINAPI +DECLSPEC_IMPORT int WINAPI GetSystemMetricsForDpi( _In_ int nIndex, _In_ UINT dpi ); -WINUSERAPI UINT WINAPI +DECLSPEC_IMPORT UINT WINAPI GetDpiForWindow( _In_ HWND hwnd ); -WINUSERAPI UINT WINAPI +DECLSPEC_IMPORT UINT WINAPI GetDpiForSystem( VOID ); -WINUSERAPI UINT WINAPI +DECLSPEC_IMPORT UINT WINAPI GetSystemDpiForProcess( _In_ HANDLE hProcess ); -WINUSERAPI BOOL WINAPI +DECLSPEC_IMPORT BOOL WINAPI SetProcessDpiAwarenessContext( _In_ DPI_AWARENESS_CONTEXT value ); -WINUSERAPI BOOL WINAPI +DECLSPEC_IMPORT BOOL WINAPI SetProcessDPIAware( VOID ); +DECLSPEC_IMPORT HRESULT WINAPI +GetScaleFactorForMonitor( + _In_ HMONITOR hMon, + _Out_ DEVICE_SCALE_FACTOR *pScale +); + EXTERN_C_END [[maybe_unused]] static constexpr const int kAutoHideTaskBarThickness = 2; // The thickness of an auto-hide taskbar in pixels. diff --git a/include/FramelessHelper/Core/private/framelessconfig_p.h b/include/FramelessHelper/Core/private/framelessconfig_p.h index 8f0af1a..113406d 100644 --- a/include/FramelessHelper/Core/private/framelessconfig_p.h +++ b/include/FramelessHelper/Core/private/framelessconfig_p.h @@ -49,7 +49,7 @@ public: static void setLoadFromEnvironmentVariablesDisabled(const bool on = true); static void setLoadFromConfigurationFileDisabled(const bool on = true); - QVariant setInternal(const QString &key, const QVariant &value); + Q_NODISCARD QVariant setInternal(const QString &key, const QVariant &value); Q_NODISCARD QVariant getInternal(const QString &key) const; template Q_NODISCARD T getInternal(const QString &key) const diff --git a/include/FramelessHelper/Core/utils.h b/include/FramelessHelper/Core/utils.h index 79b304b..51f8a23 100644 --- a/include/FramelessHelper/Core/utils.h +++ b/include/FramelessHelper/Core/utils.h @@ -104,7 +104,7 @@ FRAMELESSHELPER_CORE_API void tryToEnableHighestDpiAwarenessLevel(); FRAMELESSHELPER_CORE_API void updateGlobalWin32ControlsTheme(const WId windowId, const bool dark); [[nodiscard]] FRAMELESSHELPER_CORE_API bool shouldAppsUseDarkMode_windows(); FRAMELESSHELPER_CORE_API void forceSquareCornersForWindow(const WId windowId, const bool force); -[[nodiscard]] FRAMELESSHELPER_CORE_API QColor getTitleBarAccentColor(); +[[nodiscard]] FRAMELESSHELPER_CORE_API QColor getDwmAccentColor(); #endif // Q_OS_WINDOWS #ifdef Q_OS_LINUX diff --git a/src/core/chromepalette.cpp b/src/core/chromepalette.cpp index ded7a4f..d192fde 100644 --- a/src/core/chromepalette.cpp +++ b/src/core/chromepalette.cpp @@ -70,7 +70,7 @@ void ChromePalettePrivate::refresh() titleBarActiveBackgroundColor_sys = [colorized, dark]() -> QColor { if (colorized) { #ifdef Q_OS_WINDOWS - return Utils::getTitleBarAccentColor(); + return Utils::getDwmAccentColor(); #elif defined(Q_OS_LINUX) return Utils::getWmThemeColor(); #elif defined(Q_OS_MACOS) diff --git a/src/core/utils_mac.mm b/src/core/utils_mac.mm index bd49656..81b12b5 100644 --- a/src/core/utils_mac.mm +++ b/src/core/utils_mac.mm @@ -26,6 +26,7 @@ #include "framelessmanager.h" #include #include +#include #include #include #include @@ -365,8 +366,13 @@ private: static inline sendEventPtr oldSendEvent = nil; }; -using NSWindowProxyHash = QHash; -Q_GLOBAL_STATIC(NSWindowProxyHash, g_nswindowOverrideHash); +struct MacUtilsData +{ + QMutex mutex; + QHash hash = {}; +}; + +Q_GLOBAL_STATIC(MacUtilsData, g_macUtilsData); [[nodiscard]] static inline NSWindow *mac_getNSWindow(const WId windowId) { @@ -388,7 +394,8 @@ Q_GLOBAL_STATIC(NSWindowProxyHash, g_nswindowOverrideHash); if (!windowId) { return nil; } - if (!g_nswindowOverrideHash()->contains(windowId)) { + QMutexLocker locker(&g_macUtilsData()->mutex); + if (!g_macUtilsData()->hash.contains(windowId)) { QWindow * const qwindow = Utils::findWindow(windowId); Q_ASSERT(qwindow); if (!qwindow) { @@ -400,9 +407,9 @@ Q_GLOBAL_STATIC(NSWindowProxyHash, g_nswindowOverrideHash); return nil; } const auto proxy = new NSWindowProxy(qwindow, nswindow); - g_nswindowOverrideHash()->insert(windowId, proxy); + g_macUtilsData()->hash.insert(windowId, proxy); } - return g_nswindowOverrideHash()->value(windowId); + return g_macUtilsData()->hash.value(windowId); } SystemTheme Utils::getSystemTheme() diff --git a/src/core/utils_win.cpp b/src/core/utils_win.cpp index 5b6a35b..43b7c4d 100644 --- a/src/core/utils_win.cpp +++ b/src/core/utils_win.cpp @@ -139,6 +139,34 @@ FRAMELESSHELPER_STRING_CONSTANT(GetModuleHandleW) FRAMELESSHELPER_STRING_CONSTANT(RegisterClassExW) FRAMELESSHELPER_STRING_CONSTANT(CreateWindowExW) FRAMELESSHELPER_STRING_CONSTANT(AccentColor) +FRAMELESSHELPER_STRING_CONSTANT(GetScaleFactorForMonitor) + +struct SYSTEM_METRIC +{ + int DPI_96 = 0; // 100%. The scale factor for the device is 1x. + int DPI_115 = 0; // 120%. The scale factor for the device is 1.2x. + int DPI_120 = 0; // 125%. The scale factor for the device is 1.25x. + int DPI_134 = 0; // 140%. The scale factor for the device is 1.4x. + int DPI_144 = 0; // 150%. The scale factor for the device is 1.5x. + int DPI_154 = 0; // 160%. The scale factor for the device is 1.6x. + int DPI_168 = 0; // 175%. The scale factor for the device is 1.75x. + int DPI_173 = 0; // 180%. The scale factor for the device is 1.8x. + int DPI_192 = 0; // 200%. The scale factor for the device is 2x. + int DPI_216 = 0; // 225%. The scale factor for the device is 2.25x. + int DPI_240 = 0; // 250%. The scale factor for the device is 2.5x. + int DPI_288 = 0; // 300%. The scale factor for the device is 3x. + int DPI_336 = 0; // 350%. The scale factor for the device is 3.5x. + int DPI_384 = 0; // 400%. The scale factor for the device is 4x. + int DPI_432 = 0; // 450%. The scale factor for the device is 4.5x. + int DPI_480 = 0; // 500%. The scale factor for the device is 5x. +}; + +static const QHash g_systemMetricsTable = { + {SM_CYCAPTION, {23, 27, 29, 32, 34, 36, 40, 41, 45, 51, 56, 67, 78, 89, 100, 111}}, + {SM_CXSIZEFRAME, { 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 7, 7, 8, 8}}, + {SM_CYSIZEFRAME, { 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 7, 7, 8, 8}}, + {SM_CXPADDEDBORDER, { 4, 5, 5, 6, 6, 6, 7, 7, 8, 9, 10, 12, 14, 16, 18, 20}} +}; template class HumbleComPtr @@ -325,15 +353,50 @@ private: if (!windowId) { return 0; } - const UINT windowDpi = Utils::getWindowDpi(windowId, horizontal); - if (API_USER_AVAILABLE(GetSystemMetricsForDpi)) { - const UINT dpi = (scaled ? windowDpi : USER_DEFAULT_SCREEN_DPI); - return API_CALL_FUNCTION(GetSystemMetricsForDpi, index, dpi); - } else { - // The returned value is already scaled, we need to divide the dpr to get the unscaled value. - const qreal dpr = (scaled ? 1.0 : (qreal(windowDpi) / qreal(USER_DEFAULT_SCREEN_DPI))); - return qRound(qreal(GetSystemMetrics(index)) / dpr); + + // NOTE: We deliberately don't use the "GetSystemMetrics(ForDpi)" function here, + // because in my testing process, I found in some abnormal cases it will always + // return the unscaled value (100% scale) regardless of what DPI we gave it. The + // exact reason is still unknown to me. To eliminate this uncertainty, I decided + // to return the hard-coded value directly. + + // When DWM composition is disabled (only possible on Windows 7 in some rare cases), + // the thickness of the padded border will become 0. + if ((index == SM_CXPADDEDBORDER) && !Utils::isDwmCompositionEnabled()) { + return 0; } + if (!g_systemMetricsTable.contains(index)) { + qWarning() << "FIXME: Add SYSTEM_METRIC value for index" << index; + return 0; + } + const SYSTEM_METRIC systemMetric = g_systemMetricsTable.value(index); + if (!scaled) { + return systemMetric.DPI_96; + } + const UINT dpi = Utils::getWindowDpi(windowId, horizontal); +#define DPI_CASE(x) case x: return systemMetric.DPI_##x; + switch (dpi) { + DPI_CASE(96) + DPI_CASE(115) + DPI_CASE(120) + DPI_CASE(134) + DPI_CASE(144) + DPI_CASE(154) + DPI_CASE(168) + DPI_CASE(173) + DPI_CASE(192) + DPI_CASE(216) + DPI_CASE(240) + DPI_CASE(288) + DPI_CASE(336) + DPI_CASE(384) + DPI_CASE(432) + DPI_CASE(480) + default: + qWarning() << "Unsupported DPI value:" << dpi; + return 0; + } +#undef DPI_CASE } [[maybe_unused]] [[nodiscard]] static inline @@ -842,15 +905,15 @@ bool Utils::isHighContrastModeEnabled() quint32 Utils::getPrimaryScreenDpi(const bool horizontal) { + const HMONITOR monitor = []() -> HMONITOR { + const HWND window = ensureDummyWindow(); + if (window) { + return MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY); + } + static constexpr const int kTaskBarSize = 100; + return MonitorFromPoint(POINT{kTaskBarSize, kTaskBarSize}, MONITOR_DEFAULTTOPRIMARY); + }(); if (API_SHCORE_AVAILABLE(GetDpiForMonitor)) { - const HMONITOR monitor = []() -> HMONITOR { - const HWND window = ensureDummyWindow(); - if (window) { - return MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY); - } - static constexpr const int kTaskBarSize = 100; - return MonitorFromPoint(POINT{kTaskBarSize, kTaskBarSize}, MONITOR_DEFAULTTOPRIMARY); - }(); if (monitor) { UINT dpiX = 0, dpiY = 0; const HRESULT hr = API_CALL_FUNCTION(GetDpiForMonitor, monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); @@ -863,6 +926,19 @@ quint32 Utils::getPrimaryScreenDpi(const bool horizontal) qWarning() << getSystemErrorMessage(kMonitorFromPoint); } } + if (API_SHCORE_AVAILABLE(GetScaleFactorForMonitor)) { + if (monitor) { + DEVICE_SCALE_FACTOR factor = DEVICE_SCALE_FACTOR_INVALID; + const HRESULT hr = API_CALL_FUNCTION(GetScaleFactorForMonitor, monitor, &factor); + if (SUCCEEDED(hr) && (factor != DEVICE_SCALE_FACTOR_INVALID)) { + return quint32(qRound(qreal(USER_DEFAULT_SCREEN_DPI) * qreal(factor) / qreal(100))); + } else { + qWarning() << __getSystemErrorMessage(kGetScaleFactorForMonitor, hr); + } + } else { + qWarning() << getSystemErrorMessage(kMonitorFromPoint); + } + } if (API_D2D_AVAILABLE(D2D1CreateFactory)) { using D2D1CreateFactoryPtr = HRESULT(WINAPI *)(D2D1_FACTORY_TYPE, REFIID, CONST D2D1_FACTORY_OPTIONS *, void **); @@ -958,6 +1034,20 @@ quint32 Utils::getWindowDpi(const WId windowId, const bool horizontal) qWarning() << getSystemErrorMessage(kMonitorFromWindow); } } + if (API_SHCORE_AVAILABLE(GetScaleFactorForMonitor)) { + const HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + if (monitor) { + DEVICE_SCALE_FACTOR factor = DEVICE_SCALE_FACTOR_INVALID; + const HRESULT hr = API_CALL_FUNCTION(GetScaleFactorForMonitor, monitor, &factor); + if (SUCCEEDED(hr) && (factor != DEVICE_SCALE_FACTOR_INVALID)) { + return quint32(qRound(qreal(USER_DEFAULT_SCREEN_DPI) * qreal(factor) / qreal(100))); + } else { + qWarning() << __getSystemErrorMessage(kGetScaleFactorForMonitor, hr); + } + } else { + qWarning() << getSystemErrorMessage(kMonitorFromWindow); + } + } return getPrimaryScreenDpi(horizontal); } @@ -1655,7 +1745,7 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode, return false; } -QColor Utils::getTitleBarAccentColor() +QColor Utils::getDwmAccentColor() { const QSettings registry(dwmRegistryKey(), QSettings::NativeFormat); bool ok = false;