From 245ddfa1f01b4c9fa10337246b8045755e5bcce5 Mon Sep 17 00:00:00 2001
From: Sine Striker <trueful@163.com>
Date: 周二, 19 12月 2023 21:03:58 +0800
Subject: [PATCH] Change back to qScopeGuard

---
 src/core/contexts/win32windowcontext.cpp |  186 ++++++++++++++++++++++++++++++++++++---------
 1 files changed, 147 insertions(+), 39 deletions(-)

diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp
index f116050..cd1ab62 100644
--- a/src/core/contexts/win32windowcontext.cpp
+++ b/src/core/contexts/win32windowcontext.cpp
@@ -414,16 +414,83 @@
         monitorInfo.cbSize = sizeof(monitorInfo);
         ::GetMonitorInfoW(monitor, &monitorInfo);
         return monitorInfo;
-    };
+    }
 
-    static inline void moveToDesktopCenter(HWND hwnd) {
-        const auto monitorInfo = getMonitorForWindow(hwnd);
+    static inline void moveWindowToDesktopCenter(HWND hwnd) {
+        MONITORINFOEXW monitorInfo = getMonitorForWindow(hwnd);
         RECT windowRect{};
         ::GetWindowRect(hwnd, &windowRect);
-        const auto newX = monitorInfo.rcMonitor.left + (RECT_WIDTH(monitorInfo.rcMonitor) - RECT_WIDTH(windowRect)) / 2;
-        const auto newY = monitorInfo.rcMonitor.top + (RECT_HEIGHT(monitorInfo.rcMonitor) - RECT_HEIGHT(windowRect)) / 2;
+        const auto newX = monitorInfo.rcMonitor.left +
+                          (RECT_WIDTH(monitorInfo.rcMonitor) - RECT_WIDTH(windowRect)) / 2;
+        const auto newY = monitorInfo.rcMonitor.top +
+                          (RECT_HEIGHT(monitorInfo.rcMonitor) - RECT_HEIGHT(windowRect)) / 2;
         ::SetWindowPos(hwnd, nullptr, newX, newY, 0, 0,
                        SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
+    }
+
+    static inline void moveWindowToMonitor(HWND hwnd, const MONITORINFOEXW &activeMonitor) {
+        RECT currentMonitorRect = getMonitorForWindow(hwnd).rcMonitor;
+        RECT activeMonitorRect = activeMonitor.rcMonitor;
+        // We are in the same monitor, nothing to adjust here.
+        if (currentMonitorRect == activeMonitorRect) {
+            return;
+        }
+        RECT currentWindowRect{};
+        ::GetWindowRect(hwnd, &currentWindowRect);
+        auto newWindowX =
+            activeMonitorRect.left + (currentWindowRect.left - currentMonitorRect.left);
+        auto newWindowY = activeMonitorRect.top + (currentWindowRect.top - currentMonitorRect.top);
+        ::SetWindowPos(hwnd, nullptr, newWindowX, newWindowY, RECT_WIDTH(currentWindowRect),
+                       RECT_HEIGHT(currentWindowRect),
+                       SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
+    }
+
+    static inline void bringWindowToFront(HWND hwnd) {
+        HWND oldForegroundWindow = ::GetForegroundWindow();
+        if (!oldForegroundWindow) {
+            // The foreground window can be NULL, it's not an API error.
+            return;
+        }
+        MONITORINFOEXW activeMonitor = getMonitorForWindow(oldForegroundWindow);
+        // We need to show the window first, otherwise we won't be able to bring it to front.
+        if (!::IsWindowVisible(hwnd)) {
+            ::ShowWindow(hwnd, SW_SHOW);
+        }
+        if (IsMinimized(hwnd)) {
+            // Restore the window if it is minimized.
+            ::ShowWindow(hwnd, SW_RESTORE);
+            // Once we've been restored, throw us on the active monitor.
+            moveWindowToMonitor(hwnd, activeMonitor);
+            // When the window is restored, it will always become the foreground window.
+            // So return early here, we don't need the following code to bring it to front.
+            return;
+        }
+        // OK, our window is not minimized, so now we will try to bring it to front manually.
+        // First try to send a message to the current foreground window to check whether
+        // it is currently hanging or not.
+        if (!::SendMessageTimeoutW(oldForegroundWindow, WM_NULL, 0, 0,
+                                   SMTO_BLOCK | SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG, 1000,
+                                   nullptr)) {
+            // The foreground window hangs, can't activate current window.
+            return;
+        }
+        DWORD windowThreadProcessId = ::GetWindowThreadProcessId(oldForegroundWindow, nullptr);
+        DWORD currentThreadId = ::GetCurrentThreadId();
+        // We won't be able to change a window's Z order if it's not our own window,
+        // so we use this small technique to pretend the foreground window is ours.
+        ::AttachThreadInput(windowThreadProcessId, currentThreadId, TRUE);
+
+        [[maybe_unused]] const auto &cleaner =
+            qScopeGuard([windowThreadProcessId, currentThreadId]() {
+                ::AttachThreadInput(windowThreadProcessId, currentThreadId, FALSE); //
+            });
+
+        ::BringWindowToTop(hwnd);
+        // Activate the window too. This will force us to the virtual desktop this
+        // window is on, if it's on another virtual desktop.
+        ::SetActiveWindow(hwnd);
+        // Throw us on the active monitor.
+        moveWindowToMonitor(hwnd, activeMonitor);
     }
 
     static inline bool isFullScreen(HWND hwnd) {
@@ -762,18 +829,39 @@
         return ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam);
     }
 
+    static inline void addManagedWindow(HWND hWnd, Win32WindowContext *ctx) {
+        // Store original window proc
+        if (!g_qtWindowProc) {
+            g_qtWindowProc = reinterpret_cast<WNDPROC>(::GetWindowLongPtrW(hWnd, GWLP_WNDPROC));
+        }
+
+        // Hook window proc
+        ::SetWindowLongPtrW(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(QWKHookedWndProc));
+
+        // Install global native event filter
+        WindowsNativeEventFilter::install();
+
+        // Save window handle mapping
+        g_wndProcHash->insert(hWnd, ctx);
+    }
+
+    static inline void removeManagedWindow(HWND hWnd) {
+        // Remove window handle mapping
+        if (!g_wndProcHash->remove(hWnd))
+            return;
+
+        // Remove event filter if the all windows has been destroyed
+        if (g_wndProcHash->empty()) {
+            WindowsNativeEventFilter::uninstall();
+        }
+    }
+
     Win32WindowContext::Win32WindowContext() : AbstractWindowContext() {
     }
 
     Win32WindowContext::~Win32WindowContext() {
-        // Remove window handle mapping
-        if (auto hWnd = reinterpret_cast<HWND>(windowId); hWnd) {
-            g_wndProcHash->remove(hWnd);
-
-            // Remove event filter if the all windows has been destroyed
-            if (g_wndProcHash->empty()) {
-                WindowsNativeEventFilter::uninstall();
-            }
+        if (windowId) {
+            removeManagedWindow(reinterpret_cast<HWND>(windowId));
         }
     }
 
@@ -784,22 +872,46 @@
     void Win32WindowContext::virtual_hook(int id, void *data) {
         switch (id) {
             case CentralizeHook: {
-                const auto hwnd = reinterpret_cast<HWND>(m_windowHandle->winId());
-                moveToDesktopCenter(hwnd);
+                const auto hwnd = reinterpret_cast<HWND>(windowId);
+                moveWindowToDesktopCenter(hwnd);
+                return;
+            }
+
+            case RaiseWindowHook: {
+                const auto hwnd = reinterpret_cast<HWND>(windowId);
+                bringWindowToFront(hwnd);
                 return;
             }
 
             case ShowSystemMenuHook: {
                 const auto &pos = *static_cast<const QPoint *>(data);
-                auto hWnd = reinterpret_cast<HWND>(m_windowHandle->winId());
+                auto hWnd = reinterpret_cast<HWND>(windowId);
 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
-                const QPoint nativeGlobalPos = QHighDpi::toNativeGlobalPosition(pos, m_windowHandle);
+                const QPoint nativeGlobalPos =
+                    QHighDpi::toNativeGlobalPosition(pos, m_windowHandle);
 #else
                 const QPoint nativeGlobalPos = QHighDpi::toNativePixels(pos, m_windowHandle);
 #endif
                 showSystemMenu2(hWnd, qpoint2point(nativeGlobalPos), false,
                                 m_delegate->isHostSizeFixed(m_host));
                 return;
+            }
+
+            case WindowAttributeChangedHook: {
+                auto args = static_cast<void **>(data);
+                const auto &key = *static_cast<const QString *>(args[0]);
+                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 (newVar.toBool()) {
+                        // TODO: set off
+                    } else {
+                        // TODO: set on
+                    }
+                }
+
+                break;
             }
 
             case DefaultColorsHook: {
@@ -817,7 +929,7 @@
                 auto &painter = *static_cast<QPainter *>(args[0]);
                 const auto &rect = *static_cast<const QRect *>(args[1]);
                 const auto &region = *static_cast<const QRegion *>(args[2]);
-                const auto hwnd = reinterpret_cast<HWND>(m_windowHandle->winId());
+                const auto hwnd = reinterpret_cast<HWND>(windowId);
 
                 QPen pen;
                 pen.setWidth(getWindowFrameBorderThickness(hwnd) * 2);
@@ -868,13 +980,21 @@
         return getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId));
     }
 
-    bool Win32WindowContext::setupHost() {
+    void Win32WindowContext::winIdChanged(QWindow *oldWindow) {
+        removeManagedWindow(reinterpret_cast<HWND>(windowId));
+        if (!m_windowHandle) {
+            return;
+        }
+
         // Install window hook
         auto winId = m_windowHandle->winId();
         auto hWnd = reinterpret_cast<HWND>(winId);
 
 #if QT_VERSION < QT_VERSION_CHECK(6, 5, 0)
-        for (const auto attr : { _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, _DWMWA_USE_IMMERSIVE_DARK_MODE }) {
+        for (const auto attr : {
+                 _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1,
+                 _DWMWA_USE_IMMERSIVE_DARK_MODE,
+             }) {
             const BOOL enable = TRUE;
             DynamicApis::instance().pDwmSetWindowAttribute(hWnd, attr, &enable, sizeof(enable));
         }
@@ -883,24 +1003,11 @@
         // Inform Qt we want and have set custom margins
         updateInternalWindowFrameMargins(hWnd, m_windowHandle);
 
-        // Store original window proc
-        if (!g_qtWindowProc) {
-            g_qtWindowProc = reinterpret_cast<WNDPROC>(::GetWindowLongPtrW(hWnd, GWLP_WNDPROC));
-        }
+        // Add managed window
+        addManagedWindow(hWnd, this);
 
-        // Hook window proc
-        ::SetWindowLongPtrW(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(QWKHookedWndProc));
-
-        // Install global native event filter
-        WindowsNativeEventFilter::install();
-
-        // Cache window ID
+        // Cache win id
         windowId = winId;
-
-        // Save window handle mapping
-        g_wndProcHash->insert(hWnd, this);
-
-        return true;
     }
 
     bool Win32WindowContext::windowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
@@ -1245,7 +1352,7 @@
                     // function.
                     if (wParam && !lParam) {
                         centered = true;
-                        moveToDesktopCenter(hWnd);
+                        moveWindowToDesktopCenter(hWnd);
                     }
                 }
                 break;
@@ -1332,7 +1439,6 @@
                 // Terminal does, however, later I found that if we choose a proper
                 // color, our homemade top border can almost have exactly the same
                 // appearance with the system's one.
-
                 [[maybe_unused]] const auto &hitTestRecorder = qScopeGuard([this, result]() {
                     lastHitTestResult = getHitWindowPart(int(*result)); //
                 });
@@ -1548,8 +1654,10 @@
                 // the client area as a whole will shift to the left, which looks very abnormal if
                 // we don't repaint it. This exception disappears if we add SWP_NOCOPYBITS flag.
                 // But I don't know what caused the problem, or why this would solve it.
+                static constexpr const auto kBadWindowPosFlag =
+                    SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED;
                 const auto windowPos = reinterpret_cast<LPWINDOWPOS>(lParam);
-                if ((windowPos->flags & SWP_FRAMECHANGED) && (windowPos->flags & SWP_NOSIZE)) {
+                if (windowPos->flags == kBadWindowPosFlag) {
                     windowPos->flags |= SWP_NOCOPYBITS;
                 }
                 break;

--
Gitblit v1.9.1