From d94f8221d3572e5aada429c33b89b6171828e16d Mon Sep 17 00:00:00 2001 From: Yuhang Zhao <2546789017@qq.com> Date: Wed, 23 Mar 2022 10:48:22 +0800 Subject: [PATCH] wip Signed-off-by: Yuhang Zhao <2546789017@qq.com> --- examples/mainwindow/main.cpp | 7 ++ examples/quick/main.cpp | 5 ++ examples/widget/main.cpp | 7 ++ .../Core/framelesshelper_windows.h | 23 +++-- .../Core/framelesshelpercore_global.h | 9 ++ include/FramelessHelper/Core/utils.h | 16 +++- src/core/framelesshelper_win.cpp | 2 +- src/core/framelesswindowsmanager.cpp | 3 - src/core/utils.cpp | 26 +++--- src/core/utils_win.cpp | 88 +++++++++++++++++-- src/widgets/framelesswidgetshelper.cpp | 16 +++- 11 files changed, 169 insertions(+), 33 deletions(-) diff --git a/examples/mainwindow/main.cpp b/examples/mainwindow/main.cpp index b12eca8..efd75ac 100644 --- a/examples/mainwindow/main.cpp +++ b/examples/mainwindow/main.cpp @@ -23,10 +23,17 @@ */ #include +#include #include "mainwindow.h" +FRAMELESSHELPER_USE_NAMESPACE + int main(int argc, char *argv[]) { +#ifdef Q_OS_WINDOWS + Utils::tryToEnableHighestDpiAwarenessLevel(); +#endif + #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); diff --git a/examples/quick/main.cpp b/examples/quick/main.cpp index 9e31478..9afb11c 100644 --- a/examples/quick/main.cpp +++ b/examples/quick/main.cpp @@ -27,11 +27,16 @@ #include #include #include +#include FRAMELESSHELPER_USE_NAMESPACE int main(int argc, char *argv[]) { +#ifdef Q_OS_WINDOWS + Utils::tryToEnableHighestDpiAwarenessLevel(); +#endif + #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); diff --git a/examples/widget/main.cpp b/examples/widget/main.cpp index c51ccf0..fb24c0b 100644 --- a/examples/widget/main.cpp +++ b/examples/widget/main.cpp @@ -23,10 +23,17 @@ */ #include +#include #include "widget.h" +FRAMELESSHELPER_USE_NAMESPACE + int main(int argc, char *argv[]) { +#ifdef Q_OS_WINDOWS + Utils::tryToEnableHighestDpiAwarenessLevel(); +#endif + #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); diff --git a/include/FramelessHelper/Core/framelesshelper_windows.h b/include/FramelessHelper/Core/framelesshelper_windows.h index 5fb4ced..b114f71 100644 --- a/include/FramelessHelper/Core/framelesshelper_windows.h +++ b/include/FramelessHelper/Core/framelesshelper_windows.h @@ -141,6 +141,14 @@ using PTIMECAPS = TIMECAPS *; using NPTIMECAPS = TIMECAPS NEAR *; using LPTIMECAPS = TIMECAPS FAR *; +using PROCESS_DPI_AWARENESS = enum PROCESS_DPI_AWARENESS +{ + PROCESS_DPI_UNAWARE = 0, + PROCESS_SYSTEM_DPI_AWARE = 1, + PROCESS_PER_MONITOR_DPI_AWARE = 2, + PROCESS_PER_MONITOR_DPI_AWARE_V2 = 3 +}; + using MONITOR_DPI_TYPE = enum MONITOR_DPI_TYPE { MDT_EFFECTIVE_DPI = 0, @@ -165,6 +173,11 @@ timeEndPeriod( _In_ UINT uPeriod ); +EXTERN_C HRESULT WINAPI +SetProcessDpiAwareness( + _In_ PROCESS_DPI_AWARENESS value +); + EXTERN_C HRESULT WINAPI GetDpiForMonitor( _In_ HMONITOR hMonitor, @@ -173,14 +186,12 @@ GetDpiForMonitor( _Out_ UINT *dpiY ); -#include - [[maybe_unused]] static constexpr const int kAutoHideTaskBarThickness = 2; // The thickness of an auto-hide taskbar in pixels. -[[maybe_unused]] static const QString kDwmRegistryKey = QStringLiteral(R"(Software\Microsoft\Windows\DWM)"); -[[maybe_unused]] static const QString kPersonalizeRegistryKey = QStringLiteral(R"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)"); -[[maybe_unused]] static const QString kThemeSettingChangeEventName = QStringLiteral("ImmersiveColorSet"); -[[maybe_unused]] static const QString kDwmColorKeyName = QStringLiteral("ColorPrevalence"); +[[maybe_unused]] static constexpr const char kDwmRegistryKey[] = R"(Software\Microsoft\Windows\DWM)"; +[[maybe_unused]] static constexpr const char kPersonalizeRegistryKey[] = R"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)"; +[[maybe_unused]] static constexpr const char kThemeSettingChangeEventName[] = "ImmersiveColorSet"; +[[maybe_unused]] static constexpr const char kDwmColorKeyName[] = "ColorPrevalence"; [[maybe_unused]] static constexpr const DWORD _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19; [[maybe_unused]] static constexpr const DWORD _DWMWA_USE_IMMERSIVE_DARK_MODE = 20; diff --git a/include/FramelessHelper/Core/framelesshelpercore_global.h b/include/FramelessHelper/Core/framelesshelpercore_global.h index c9965c3..cc96491 100644 --- a/include/FramelessHelper/Core/framelesshelpercore_global.h +++ b/include/FramelessHelper/Core/framelesshelpercore_global.h @@ -73,6 +73,14 @@ # define Q_NODISCARD #endif +#ifndef QUtf8String +# define QUtf8String(str) QString::fromUtf8(str) +#endif + +#ifndef QU8Str +# define QU8Str(str) QUtf8String(str) +#endif + #ifndef FRAMELESSHELPER_NAMESPACE # define FRAMELESSHELPER_NAMESPACE __flh_ns #endif @@ -112,6 +120,7 @@ Q_NAMESPACE_EXPORT(FRAMELESSHELPER_CORE_API) [[maybe_unused]] static constexpr const char kUsePureQtImplFlag[] = "FRAMELESSHELPER_PURE_QT_IMPLEMENTATION"; [[maybe_unused]] static constexpr const char kForceHideFrameBorderFlag[] = "FRAMELESSHELPER_FORCE_HIDE_FRAME_BORDER"; [[maybe_unused]] static constexpr const char kForceShowFrameBorderFlag[] = "FRAMELESSHELPER_FORCE_SHOW_FRAME_BORDER"; +[[maybe_unused]] static constexpr const char kSystemMenuOffsetFlag[] = "FRAMELESSHELPER_SYSTEM_MENU_OFFSET"; [[maybe_unused]] static const QString kConfigFileName = QStringLiteral(".framelesshelper.ini"); [[maybe_unused]] static const QString kUsePureQtImplKeyPath = QStringLiteral("Options/UsePureQtImplementation"); diff --git a/include/FramelessHelper/Core/utils.h b/include/FramelessHelper/Core/utils.h index 4c51c83..d8ef6d4 100644 --- a/include/FramelessHelper/Core/utils.h +++ b/include/FramelessHelper/Core/utils.h @@ -28,6 +28,10 @@ #include #include +QT_BEGIN_NAMESPACE +class QScreen; +QT_END_NAMESPACE + FRAMELESSHELPER_BEGIN_NAMESPACE enum class SystemTheme : int @@ -69,7 +73,12 @@ Q_ENUM_NS(DwmColorizationArea) #endif using GetWindowFlagsCallback = std::function; -using SetWindowFlagsCallback = std::function; +using SetWindowFlagsCallback = std::function; + +using GetWindowSizeCallback = std::function; +using MoveWindowCallback = std::function; + +using GetWindowScreenCallback = std::function; namespace Utils { @@ -83,7 +92,8 @@ FRAMELESSHELPER_CORE_API void startSystemResize(QWindow *window, const Qt::Edges (const SystemButtonType button, const SystemTheme theme, const ResourceType type); FRAMELESSHELPER_CORE_API void sendMouseReleaseEvent(); [[nodiscard]] FRAMELESSHELPER_CORE_API QWindow *findWindow(const WId winId); -FRAMELESSHELPER_CORE_API void moveWindowToDesktopCenter(QWindow *window, const bool considerTaskBar); +FRAMELESSHELPER_CORE_API void moveWindowToDesktopCenter(const GetWindowScreenCallback &getWindowScreen, + const GetWindowSizeCallback &getWindowSize, const MoveWindowCallback &moveWindow, const bool considerTaskBar); #ifdef Q_OS_WINDOWS [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin8OrGreater(); @@ -120,6 +130,8 @@ FRAMELESSHELPER_CORE_API void installSystemMenuHook(const QWindow *window); FRAMELESSHELPER_CORE_API void uninstallSystemMenuHook(const WId winId); FRAMELESSHELPER_CORE_API void tryToBeCompatibleWithQtFramelessWindowHint(const WId winId, const GetWindowFlagsCallback &getWindowFlags, const SetWindowFlagsCallback &setWindowFlags, const bool enable); +FRAMELESSHELPER_CORE_API void disableAeroSnapping(const WId winId); +FRAMELESSHELPER_CORE_API void tryToEnableHighestDpiAwarenessLevel(); #endif // Q_OS_WINDOWS } // namespace Utils diff --git a/src/core/framelesshelper_win.cpp b/src/core/framelesshelper_win.cpp index fb2a5dc..58fc0db 100644 --- a/src/core/framelesshelper_win.cpp +++ b/src/core/framelesshelper_win.cpp @@ -679,7 +679,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me if (Utils::isWin10OrGreater()) { if (msg->message == WM_SETTINGCHANGE) { if ((msg->wParam == 0) && (QString::fromWCharArray(reinterpret_cast(msg->lParam)) - .compare(kThemeSettingChangeEventName, Qt::CaseInsensitive) == 0)) { + .compare(QU8Str(kThemeSettingChangeEventName), Qt::CaseInsensitive) == 0)) { return true; } } diff --git a/src/core/framelesswindowsmanager.cpp b/src/core/framelesswindowsmanager.cpp index 7cdba27..38fc9ae 100644 --- a/src/core/framelesswindowsmanager.cpp +++ b/src/core/framelesswindowsmanager.cpp @@ -209,9 +209,6 @@ void FramelessWindowsManager::addWindow(QWindow *window) Utils::installSystemMenuHook(window); } #endif - if (!(options & Option::DontMoveWindowToDesktopCenter)) { - Utils::moveWindowToDesktopCenter(window, true); - } } void FramelessWindowsManager::removeWindow(QWindow *window) diff --git a/src/core/utils.cpp b/src/core/utils.cpp index ccce6de..8b708ff 100644 --- a/src/core/utils.cpp +++ b/src/core/utils.cpp @@ -25,8 +25,8 @@ #include "utils.h" #include #include -#include #include +#include // The "Q_INIT_RESOURCE()" macro can't be used within a namespace, // so we wrap it into a separate function outside of the namespace and @@ -172,20 +172,25 @@ QWindow *Utils::findWindow(const WId winId) return nullptr; } -void Utils::moveWindowToDesktopCenter(QWindow *window, const bool considerTaskBar) +void Utils::moveWindowToDesktopCenter(const GetWindowScreenCallback &getWindowScreen, + const GetWindowSizeCallback &getWindowSize, + const MoveWindowCallback &moveWindow, + const bool considerTaskBar) { - Q_ASSERT(window); - if (!window) { + Q_ASSERT(getWindowScreen); + Q_ASSERT(getWindowSize); + Q_ASSERT(moveWindow); + if (!getWindowScreen || !getWindowSize || !moveWindow) { return; } - const QSize windowSize = window->size(); + const QSize windowSize = getWindowSize(); if (windowSize.isEmpty() || (windowSize == kInvalidWindowSize)) { return; } - const QScreen * const screen = [window]() -> const QScreen * { - const QScreen * const s = window->screen(); - return (s ? s : QGuiApplication::primaryScreen()); - }(); + const QScreen *screen = getWindowScreen(); + if (!screen) { + screen = QGuiApplication::primaryScreen(); + } Q_ASSERT(screen); if (!screen) { return; @@ -194,8 +199,7 @@ void Utils::moveWindowToDesktopCenter(QWindow *window, const bool considerTaskBa const QPoint offset = (considerTaskBar ? screen->availableGeometry().topLeft() : QPoint(0, 0)); const auto newX = static_cast(qRound(qreal(screenSize.width() - windowSize.width()) / 2.0)); const auto newY = static_cast(qRound(qreal(screenSize.height() - windowSize.height()) / 2.0)); - window->setX(newX + offset.x()); - window->setY(newY + offset.y()); + moveWindow(newX + offset.x(), newY + offset.y()); } FRAMELESSHELPER_END_NAMESPACE diff --git a/src/core/utils_win.cpp b/src/core/utils_win.cpp index 3c9f5a7..05c1e6e 100644 --- a/src/core/utils_win.cpp +++ b/src/core/utils_win.cpp @@ -313,7 +313,7 @@ bool Utils::isDwmCompositionEnabled() return true; } const auto resultFromRegistry = []() -> bool { - const QWinRegistryKey registry(HKEY_CURRENT_USER, kDwmRegistryKey); + const QWinRegistryKey registry(HKEY_CURRENT_USER, QU8Str(kDwmRegistryKey)); const auto result = registry.dwordValue(QStringLiteral("Composition")); return (result.second && (result.first != 0)); }; @@ -434,7 +434,7 @@ QString Utils::getSystemErrorMessage(const QString &function) QColor Utils::getDwmColorizationColor() { const auto resultFromRegistry = []() -> QColor { - const QWinRegistryKey registry(HKEY_CURRENT_USER, kDwmRegistryKey); + const QWinRegistryKey registry(HKEY_CURRENT_USER, QU8Str(kDwmRegistryKey)); const auto result = registry.dwordValue(QStringLiteral("ColorizationColor")); return (result.second ? QColor::fromRgba(result.first) : Qt::darkGray); }; @@ -461,7 +461,7 @@ bool Utils::shouldAppsUseDarkMode() return false; } const auto resultFromRegistry = []() -> bool { - const QWinRegistryKey registry(HKEY_CURRENT_USER, kPersonalizeRegistryKey); + const QWinRegistryKey registry(HKEY_CURRENT_USER, QU8Str(kPersonalizeRegistryKey)); const auto result = registry.dwordValue(QStringLiteral("AppsUseLightTheme")); return (result.second && (result.first == 0)); }; @@ -478,10 +478,10 @@ DwmColorizationArea Utils::getDwmColorizationArea() if (!isWin10OrGreater()) { return DwmColorizationArea::None; } - const QWinRegistryKey themeRegistry(HKEY_CURRENT_USER, kPersonalizeRegistryKey); - const auto themeValue = themeRegistry.dwordValue(kDwmColorKeyName); - const QWinRegistryKey dwmRegistry(HKEY_CURRENT_USER, kDwmRegistryKey); - const auto dwmValue = dwmRegistry.dwordValue(kDwmColorKeyName); + const QWinRegistryKey themeRegistry(HKEY_CURRENT_USER, QU8Str(kPersonalizeRegistryKey)); + const auto themeValue = themeRegistry.dwordValue(QU8Str(kDwmColorKeyName)); + const QWinRegistryKey dwmRegistry(HKEY_CURRENT_USER, QU8Str(kDwmRegistryKey)); + const auto dwmValue = dwmRegistry.dwordValue(QU8Str(kDwmColorKeyName)); const bool theme = themeValue.second && (themeValue.first != 0); const bool dwm = dwmValue.second && (dwmValue.first != 0); if (theme && dwm) { @@ -506,7 +506,6 @@ void Utils::showSystemMenu(const QWindow *window, const QPoint &pos) qWarning() << getSystemErrorMessage(QStringLiteral("GetSystemMenu")); return; } - // Update the options based on window state. MENUITEMINFOW mii; SecureZeroMemory(&mii, sizeof(mii)); mii.cbSize = sizeof(mii); @@ -545,8 +544,11 @@ void Utils::showSystemMenu(const QWindow *window, const QPoint &pos) qWarning() << getSystemErrorMessage(QStringLiteral("SetMenuDefaultItem")); return; } + const QPoint offset = (maxOrFull ? QPoint(0, 0) : window->property(kSystemMenuOffsetFlag).toPoint()); + const int xPos = (pos.x() + offset.x()); + const int yPos = (pos.y() + offset.y()); const int ret = TrackPopupMenu(menu, (TPM_RETURNCMD | (QGuiApplication::isRightToLeft() - ? TPM_RIGHTALIGN : TPM_LEFTALIGN)), pos.x(), pos.y(), 0, hWnd, nullptr); + ? TPM_RIGHTALIGN : TPM_LEFTALIGN)), xPos, yPos, 0, hWnd, nullptr); if (ret != 0) { if (PostMessageW(hWnd, WM_SYSCOMMAND, ret, 0) == FALSE) { qWarning() << getSystemErrorMessage(QStringLiteral("PostMessageW")); @@ -1108,4 +1110,72 @@ void Utils::tryToBeCompatibleWithQtFramelessWindowHint(const WId winId, triggerFrameChange(winId); } +void Utils::disableAeroSnapping(const WId winId) +{ + Q_ASSERT(winId); + if (!winId) { + return; + } + const auto hwnd = reinterpret_cast(winId); + SetLastError(ERROR_SUCCESS); + const auto oldWindowStyle = static_cast(GetWindowLongPtrW(hwnd, GWL_STYLE)); + if (oldWindowStyle == 0) { + qWarning() << getSystemErrorMessage(QStringLiteral("GetWindowLongPtrW")); + return; + } + const DWORD newWindowStyle = ((oldWindowStyle & ~WS_THICKFRAME) | WS_POPUP); + SetLastError(ERROR_SUCCESS); + if (SetWindowLongPtrW(hwnd, GWL_STYLE, static_cast(newWindowStyle)) == 0) { + qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); + return; + } + triggerFrameChange(winId); +} + +void Utils::tryToEnableHighestDpiAwarenessLevel() +{ + static const auto pSetProcessDpiAwarenessContext = + reinterpret_cast( + QSystemLibrary::resolve(QStringLiteral("user32"), "SetProcessDpiAwarenessContext")); + if (pSetProcessDpiAwarenessContext) { + if (pSetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) != FALSE) { + return; + } + const DWORD dwError = GetLastError(); + // "ERROR_ACCESS_DENIED" means set externally (mostly due to manifest file). + if (dwError == ERROR_ACCESS_DENIED) { + return; + } + if (pSetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE) != FALSE) { + return; + } + if (pSetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE) != FALSE) { + return; + } + if (pSetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED) != FALSE) { + return; + } + } + static const auto pSetProcessDpiAwareness = + reinterpret_cast( + QSystemLibrary::resolve(QStringLiteral("shcore"), "SetProcessDpiAwareness")); + if (pSetProcessDpiAwareness) { + if (SUCCEEDED(pSetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE_V2))) { + return; + } + const HRESULT hr = pSetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); + if (SUCCEEDED(hr)) { + return; + } + // "E_ACCESSDENIED" means set externally (mostly due to manifest file). + if (hr == E_ACCESSDENIED) { + return; + } + if (SUCCEEDED(pSetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE))) { + return; + } + } + SetProcessDPIAware(); +} + FRAMELESSHELPER_END_NAMESPACE diff --git a/src/widgets/framelesswidgetshelper.cpp b/src/widgets/framelesswidgetshelper.cpp index 80e5a04..7799a21 100644 --- a/src/widgets/framelesswidgetshelper.cpp +++ b/src/widgets/framelesswidgetshelper.cpp @@ -311,7 +311,7 @@ void FramelessWidgetsHelper::initialize() if (m_options & Option::BeCompatibleWithQtFramelessWindowHint) { Utils::tryToBeCompatibleWithQtFramelessWindowHint(q->winId(), [this]() -> Qt::WindowFlags { return q->windowFlags(); }, - [this](Qt::WindowFlags flags) -> void { q->setWindowFlags(flags); }, + [this](const Qt::WindowFlags flags) -> void { q->setWindowFlags(flags); }, true); } FramelessWindowsManager *manager = FramelessWindowsManager::instance(); @@ -325,6 +325,20 @@ void FramelessWidgetsHelper::initialize() QMetaObject::invokeMethod(q, "systemThemeChanged"); }); setupInitialUi(); + if (!(m_options & Option::DontMoveWindowToDesktopCenter)) { + Utils::moveWindowToDesktopCenter( + [this, window]() -> QScreen * { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + Q_UNUSED(window); + return q->screen(); +#else + return window->screen(); +#endif + }, + [this]() -> QSize { return q->size(); }, + [this](const int x, const int y) -> void { q->move(x, y); }, + true); + } } void FramelessWidgetsHelper::createSystemTitleBar()