From 0e9c2e428fb61953fd8f152125897646fe6fd337 Mon Sep 17 00:00:00 2001
From: Yuhang Zhao <zhaoyuhang@rankyee.com>
Date: 摹曛, 21 12月 2023 16:44:30 +0800
Subject: [PATCH] add mica mica alt acrylic

---
 src/core/contexts/win32windowcontext.cpp |  236 +++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 205 insertions(+), 31 deletions(-)

diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp
index 7a8ad24..76510ee 100644
--- a/src/core/contexts/win32windowcontext.cpp
+++ b/src/core/contexts/win32windowcontext.cpp
@@ -62,6 +62,82 @@
         _DWMWA_MICA_EFFECT = 1029
     };
 
+    // Types used with DWMWA_SYSTEMBACKDROP_TYPE
+    enum _DWM_SYSTEMBACKDROP_TYPE {
+        _DWMSBT_AUTO,             // [Default] Let DWM automatically decide the system-drawn backdrop for this window.
+        _DWMSBT_NONE,             // [Disable] Do not draw any system backdrop.
+        _DWMSBT_MAINWINDOW,       // [Mica] Draw the backdrop material effect corresponding to a long-lived window.
+        _DWMSBT_TRANSIENTWINDOW,  // [Acrylic] Draw the backdrop material effect corresponding to a transient window.
+        _DWMSBT_TABBEDWINDOW,     // [Mica Alt] Draw the backdrop material effect corresponding to a window with a tabbed title bar.
+    };
+
+    enum WINDOWCOMPOSITIONATTRIB {
+        WCA_UNDEFINED = 0,
+        WCA_NCRENDERING_ENABLED = 1,
+        WCA_NCRENDERING_POLICY = 2,
+        WCA_TRANSITIONS_FORCEDISABLED = 3,
+        WCA_ALLOW_NCPAINT = 4,
+        WCA_CAPTION_BUTTON_BOUNDS = 5,
+        WCA_NONCLIENT_RTL_LAYOUT = 6,
+        WCA_FORCE_ICONIC_REPRESENTATION = 7,
+        WCA_EXTENDED_FRAME_BOUNDS = 8,
+        WCA_HAS_ICONIC_BITMAP = 9,
+        WCA_THEME_ATTRIBUTES = 10,
+        WCA_NCRENDERING_EXILED = 11,
+        WCA_NCADORNMENTINFO = 12,
+        WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
+        WCA_VIDEO_OVERLAY_ACTIVE = 14,
+        WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
+        WCA_DISALLOW_PEEK = 16,
+        WCA_CLOAK = 17,
+        WCA_CLOAKED = 18,
+        WCA_ACCENT_POLICY = 19,
+        WCA_FREEZE_REPRESENTATION = 20,
+        WCA_EVER_UNCLOAKED = 21,
+        WCA_VISUAL_OWNER = 22,
+        WCA_HOLOGRAPHIC = 23,
+        WCA_EXCLUDED_FROM_DDA = 24,
+        WCA_PASSIVEUPDATEMODE = 25,
+        WCA_USEDARKMODECOLORS = 26,
+        WCA_CORNER_STYLE = 27,
+        WCA_PART_COLOR = 28,
+        WCA_DISABLE_MOVESIZE_FEEDBACK = 29,
+        WCA_LAST = 30
+    };
+
+    enum ACCENT_STATE {
+        ACCENT_DISABLED = 0,
+        ACCENT_ENABLE_GRADIENT = 1,
+        ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
+        ACCENT_ENABLE_BLURBEHIND = 3, // Traditional DWM blur
+        ACCENT_ENABLE_ACRYLICBLURBEHIND = 4, // RS4 1803
+        ACCENT_ENABLE_HOST_BACKDROP = 5, // RS5 1809
+        ACCENT_INVALID_STATE = 6 // Using this value will remove the window background
+    };
+
+    enum ACCENT_FLAG {
+        ACCENT_NONE = 0,
+        ACCENT_ENABLE_ACRYLIC = 1,
+        ACCENT_ENABLE_ACRYLIC_WITH_LUMINOSITY = 482
+    };
+
+    struct ACCENT_POLICY {
+        DWORD dwAccentState;
+        DWORD dwAccentFlags;
+        DWORD dwGradientColor; // #AABBGGRR
+        DWORD dwAnimationId;
+    };
+    using PACCENT_POLICY = ACCENT_POLICY *;
+
+    struct WINDOWCOMPOSITIONATTRIBDATA {
+        WINDOWCOMPOSITIONATTRIB Attrib;
+        PVOID pvData;
+        SIZE_T cbData;
+    };
+    using PWINDOWCOMPOSITIONATTRIBDATA = WINDOWCOMPOSITIONATTRIBDATA *;
+
+    using SetWindowCompositionAttributePtr = BOOL(WINAPI *)(HWND, PWINDOWCOMPOSITIONATTRIBDATA);
+
     // The thickness of an auto-hide taskbar in pixels.
     static constexpr const quint8 kAutoHideTaskBarThickness = 2;
 
@@ -105,6 +181,7 @@
         DYNAMIC_API_DECLARE(DwmGetCompositionTimingInfo);
         DYNAMIC_API_DECLARE(DwmGetWindowAttribute);
         DYNAMIC_API_DECLARE(DwmSetWindowAttribute);
+        DYNAMIC_API_DECLARE(DwmExtendFrameIntoClientArea);
         DYNAMIC_API_DECLARE(GetDpiForWindow);
         DYNAMIC_API_DECLARE(GetSystemMetricsForDpi);
         DYNAMIC_API_DECLARE(GetDpiForMonitor);
@@ -114,6 +191,8 @@
 
 #undef DYNAMIC_API_DECLARE
 
+        SetWindowCompositionAttributePtr pSetWindowCompositionAttribute = nullptr;
+
     private:
         DynamicApis() {
 #define DYNAMIC_API_RESOLVE(DLL, NAME)                                                             \
@@ -122,6 +201,7 @@
             QSystemLibrary user32(QStringLiteral("user32"));
             DYNAMIC_API_RESOLVE(user32, GetDpiForWindow);
             DYNAMIC_API_RESOLVE(user32, GetSystemMetricsForDpi);
+            DYNAMIC_API_RESOLVE(user32, SetWindowCompositionAttribute);
 
             QSystemLibrary shcore(QStringLiteral("shcore"));
             DYNAMIC_API_RESOLVE(shcore, GetDpiForMonitor);
@@ -132,6 +212,7 @@
             DYNAMIC_API_RESOLVE(dwmapi, DwmGetCompositionTimingInfo);
             DYNAMIC_API_RESOLVE(dwmapi, DwmGetWindowAttribute);
             DYNAMIC_API_RESOLVE(dwmapi, DwmSetWindowAttribute);
+            DYNAMIC_API_RESOLVE(dwmapi, DwmExtendFrameIntoClientArea);
 
             QSystemLibrary winmm(QStringLiteral("winmm"));
             DYNAMIC_API_RESOLVE(winmm, timeGetDevCaps);
@@ -243,6 +324,11 @@
 
     static inline bool isWin11OrGreater() {
         static const bool result = IsWindows11OrGreater_Real();
+        return result;
+    }
+
+    static inline bool isWin1122H2OrGreater() {
+        static const bool result = IsWindows1122H2OrGreater_Real();
         return result;
     }
 
@@ -379,16 +465,7 @@
                getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi);
     }
 
-    static void updateInternalWindowFrameMargins(HWND hwnd, QWindow *window) {
-        const auto margins = [hwnd]() -> QMargins {
-            const auto titleBarHeight = int(getTitleBarHeight(hwnd));
-            if (isWin10OrGreater()) {
-                return {0, -titleBarHeight, 0, 0};
-            } else {
-                const auto frameSize = int(getResizeBorderThickness(hwnd));
-                return {-frameSize, -titleBarHeight, -frameSize, -frameSize};
-            }
-        }();
+    static void setInternalWindowFrameMargins(QWindow *window, const QMargins &margins) {
         const QVariant marginsVar = QVariant::fromValue(margins);
         window->setProperty("_q_windowsCustomMargins", marginsVar);
 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
@@ -829,7 +906,20 @@
         return ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam);
     }
 
-    static inline void addManagedWindow(HWND hWnd, Win32WindowContext *ctx) {
+    static inline void addManagedWindow(QWindow *window, HWND hWnd, Win32WindowContext *ctx) {
+        const auto margins = [hWnd]() -> QMargins {
+            const auto titleBarHeight = int(getTitleBarHeight(hWnd));
+            if (isWin10OrGreater()) {
+                return {0, -titleBarHeight, 0, 0};
+            } else {
+                const auto frameSize = int(getResizeBorderThickness(hWnd));
+                return {-frameSize, -titleBarHeight, -frameSize, -frameSize};
+            }
+        }();
+
+        // Inform Qt we want and have set custom margins
+        setInternalWindowFrameMargins(window, margins);
+
         // Store original window proc
         if (!g_qtWindowProc) {
             g_qtWindowProc = reinterpret_cast<WNDPROC>(::GetWindowLongPtrW(hWnd, GWLP_WNDPROC));
@@ -845,7 +935,6 @@
         g_wndProcHash->insert(hWnd, ctx);
     }
 
-    template <bool Destroyed = true>
     static inline void removeManagedWindow(HWND hWnd) {
         // Remove window handle mapping
         if (!g_wndProcHash->remove(hWnd))
@@ -854,11 +943,6 @@
         // Remove event filter if the all windows has been destroyed
         if (g_wndProcHash->empty()) {
             WindowsNativeEventFilter::uninstall();
-        }
-
-        // Restore window proc
-        if constexpr (!Destroyed) {
-            ::SetWindowLongPtrW(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(g_qtWindowProc));
         }
     }
 
@@ -878,18 +962,24 @@
     void Win32WindowContext::virtual_hook(int id, void *data) {
         switch (id) {
             case CentralizeHook: {
+                if (!windowId)
+                    return;
                 const auto hwnd = reinterpret_cast<HWND>(windowId);
                 moveWindowToDesktopCenter(hwnd);
                 return;
             }
 
             case RaiseWindowHook: {
+                if (!windowId)
+                    return;
                 const auto hwnd = reinterpret_cast<HWND>(windowId);
                 bringWindowToFront(hwnd);
                 return;
             }
 
             case ShowSystemMenuHook: {
+                if (!windowId)
+                    return;
                 const auto &pos = *static_cast<const QPoint *>(data);
                 auto hWnd = reinterpret_cast<HWND>(windowId);
 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
@@ -909,14 +999,104 @@
                 const auto &newVar = *static_cast<const QVariant *>(args[1]);
                 const auto &oldVar = *static_cast<const QVariant *>(args[2]);
 
-                if (key == QStringLiteral("no-frame-shadow")) {
+                if (!windowId)
+                    return;
+                const auto hwnd = reinterpret_cast<HWND>(windowId);
+
+                const DynamicApis &apis = DynamicApis::instance();
+
+                if (key == QStringLiteral("frame-shadow")) {
                     if (newVar.toBool()) {
                         // TODO: set off
                     } else {
                         // TODO: set on
                     }
+                } else if (key == QStringLiteral("mica")) {
+                    if (!isWin11OrGreater()) {
+                        return;
+                    }
+                    if (newVar.toBool()) {
+                        // We need to extend the window frame into the whole client area to be able
+                        // to see the blurred window background.
+                        static constexpr const MARGINS margins = {-1, -1, -1, -1};
+                        apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
+                        if (isWin1122H2OrGreater()) {
+                            // Use official DWM API to enable Mica, available since Windows 11 22H2 (10.0.22621).
+                            const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_MAINWINDOW;
+                            apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType, sizeof(backdropType));
+                        } else {
+                            // Use undocumented DWM API to enable Mica, available since Windows 11 (10.0.22000).
+                            const BOOL enable = TRUE;
+                            apis.pDwmSetWindowAttribute(hwnd, _DWMWA_MICA_EFFECT, &enable, sizeof(enable));
+                        }
+                    } else {
+                        if (isWin1122H2OrGreater()) {
+                            const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_AUTO;
+                            apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType, sizeof(backdropType));
+                        } else {
+                            const BOOL enable = FALSE;
+                            apis.pDwmSetWindowAttribute(hwnd, _DWMWA_MICA_EFFECT, &enable, sizeof(enable));
+                        }
+                        static constexpr const MARGINS margins = {0, 0, 0, 0};
+                        apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
+                    }
+                } else if (key == QStringLiteral("mica-alt")) {
+                    if (!isWin1122H2OrGreater()) {
+                        return;
+                    }
+                    if (newVar.toBool()) {
+                        // We need to extend the window frame into the whole client area to be able
+                        // to see the blurred window background.
+                        static constexpr const MARGINS margins = {-1, -1, -1, -1};
+                        apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
+                        // Use official DWM API to enable Mica Alt, available since Windows 11 22H2 (10.0.22621).
+                        const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_TABBEDWINDOW;
+                        apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType, sizeof(backdropType));
+                    } else {
+                        const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_AUTO;
+                        apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType, sizeof(backdropType));
+                        static constexpr const MARGINS margins = {0, 0, 0, 0};
+                        apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
+                    }
+                } else if (key == QStringLiteral("acrylic-material")) {
+                    if (newVar.toBool()) {
+                        // We need to extend the window frame into the whole client area to be able
+                        // to see the blurred window background.
+                        static constexpr const MARGINS margins = {-1, -1, -1, -1};
+                        apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
+                        if (isWin11OrGreater()) {
+                            const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_TRANSIENTWINDOW;
+                            apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType, sizeof(backdropType));
+                        } else {
+                            ACCENT_POLICY policy{};
+                            policy.dwAccentState = ACCENT_ENABLE_ACRYLICBLURBEHIND;
+                            policy.dwAccentFlags = ACCENT_ENABLE_ACRYLIC_WITH_LUMINOSITY;
+                            // This API expects the #AABBGGRR format.
+                            //policy.dwGradientColor = DWORD(qRgba(gradientColor.blue(), gradientColor.green(), gradientColor.red(), gradientColor.alpha()));
+                            WINDOWCOMPOSITIONATTRIBDATA wcad{};
+                            wcad.Attrib = WCA_ACCENT_POLICY;
+                            wcad.pvData = &policy;
+                            wcad.cbData = sizeof(policy);
+                            apis.pSetWindowCompositionAttribute(hwnd, &wcad);
+                        }
+                    } else {
+                        if (isWin11OrGreater()) {
+                            const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_AUTO;
+                            apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType, sizeof(backdropType));
+                        } else {
+                            ACCENT_POLICY policy{};
+                            policy.dwAccentState = ACCENT_DISABLED;
+                            policy.dwAccentFlags = ACCENT_NONE;
+                            WINDOWCOMPOSITIONATTRIBDATA wcad{};
+                            wcad.Attrib = WCA_ACCENT_POLICY;
+                            wcad.pvData = &policy;
+                            wcad.cbData = sizeof(policy);
+                            apis.pSetWindowCompositionAttribute(hwnd, &wcad);
+                        }
+                        static constexpr const MARGINS margins = {0, 0, 0, 0};
+                        apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
+                    }
                 }
-
                 return;
             }
 
@@ -931,6 +1111,9 @@
             }
 
             case DrawWindows10BorderHook: {
+                if (!windowId)
+                    return;
+
                 auto args = static_cast<void **>(data);
                 auto &painter = *static_cast<QPainter *>(args[0]);
                 const auto &rect = *static_cast<const QRect *>(args[1]);
@@ -984,16 +1167,10 @@
         return getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId));
     }
 
-    void Win32WindowContext::winIdChanged(QWindow *oldWindow, bool isDestroyed) {
-        Q_UNUSED(isDestroyed)
-
+    void Win32WindowContext::winIdChanged() {
         // If the original window id is valid, remove all resources related
         if (windowId) {
-            if (isDestroyed) {
-                removeManagedWindow(reinterpret_cast<HWND>(windowId));
-            } else {
-                removeManagedWindow<false>(reinterpret_cast<HWND>(windowId));
-            }
+            removeManagedWindow(reinterpret_cast<HWND>(windowId));
             windowId = 0;
         }
 
@@ -1015,11 +1192,8 @@
         }
 #endif
 
-        // Inform Qt we want and have set custom margins
-        updateInternalWindowFrameMargins(hWnd, m_windowHandle); // TODO: Restore?
-
         // Add managed window
-        addManagedWindow(hWnd, this);
+        addManagedWindow(m_windowHandle, hWnd, this);
 
         // Cache win id
         windowId = winId;

--
Gitblit v1.9.1