From 4fc35bd506d1fb9d41324d7dd33047c491f2979f Mon Sep 17 00:00:00 2001 From: Sine Striker <trueful@163.com> Date: 周三, 08 5月 2024 11:00:38 +0800 Subject: [PATCH] Add mouse hook when exec menu --- src/core/contexts/win32windowcontext.cpp | 121 +++++++++++++++++++++++++++++----------- 1 files changed, 88 insertions(+), 33 deletions(-) diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index 287901a..9ad30da 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -960,9 +960,7 @@ } } - Win32WindowContext::Win32WindowContext() - : AbstractWindowContext(), lastHitTestResult(WindowPart::Outside), - lastHitTestResultRaw(HTNOWHERE), mouseLeaveBlocked(false), iconButtonClickTime(0) { + Win32WindowContext::Win32WindowContext() : AbstractWindowContext() { } Win32WindowContext::~Win32WindowContext() { @@ -1188,7 +1186,8 @@ return false; } - // qDebug().noquote() << QString::fromWCharArray(xmsgHash.value(message, L"")) << Qt::hex + // qDebug().noquote() << QDateTime::currentDateTime() + // << QString::fromStdWString(xmsgHash.value(message, L"")) << Qt::hex // << message; // Test snap layout @@ -1672,20 +1671,35 @@ // how to avoid doing this. // ### FIXME FIXME FIXME *result = ::DefWindowProcW(hWnd, WM_NCMOUSEMOVE, wParam, lParam); - } else if (lastHitTestResultRaw == HTSYSMENU) { - if (message == WM_NCLBUTTONDOWN) { - // A message of WM_SYSCOMMAND with SC_MOUSEMENU will be sent by Windows, - // and the current control flow will be blocked by the menu while - // Windows will create and execute a new event loop until the menu - // returns - iconButtonClickTime = ::GetTickCount64(); - *result = ::DefWindowProcW(hWnd, message, wParam, lParam); - iconButtonClickTime = 0; - } else if (message == WM_NCLBUTTONDBLCLK) { - // A message of WM_SYSCOMMAND with SC_CLOSE will be sent by Windows - *result = ::DefWindowProcW(hWnd, message, wParam, lParam); - } else { - *result = FALSE; + emulateClientAreaMessage(hWnd, message, wParam, lParam); + return true; + } + + if (lastHitTestResultRaw == HTSYSMENU) { + switch (message) { + case WM_NCLBUTTONDOWN: + if (iconButtonClickTime == 0) { + // A message of WM_SYSCOMMAND with SC_MOUSEMENU will be sent by + // Windows, and the current control flow will be blocked by the + // menu while Windows will create and execute a new event loop + // until the menu returns + iconButtonClickTime = ::GetTickCount64(); + *result = + ::DefWindowProcW(hWnd, WM_NCLBUTTONDOWN, wParam, lParam); + iconButtonClickTime = 0; + } else { + *result = FALSE; + emulateClientAreaMessage(hWnd, message, wParam, lParam); + } + break; + case WM_NCLBUTTONDBLCLK: + // A message of WM_SYSCOMMAND with SC_CLOSE will be sent by Windows + *result = ::DefWindowProcW(hWnd, message, wParam, lParam); + break; + default: + *result = FALSE; + emulateClientAreaMessage(hWnd, message, wParam, lParam); + break; } } else { // According to MSDN, we should return non-zero for X button messages to @@ -1695,8 +1709,8 @@ (((message >= WM_NCXBUTTONDOWN) && (message <= WM_NCXBUTTONDBLCLK)) ? TRUE : FALSE); + emulateClientAreaMessage(hWnd, message, wParam, lParam); } - emulateClientAreaMessage(hWnd, message, wParam, lParam); return true; } break; @@ -2429,24 +2443,65 @@ break; } if (shouldShowSystemMenu) { + static HHOOK mouseHook = nullptr; + static std::optional<POINT> mouseClickPos; + bool mouseHookedLocal = false; + if (iconButtonClickTime > 0) { + POINT menuPos{0, static_cast<LONG>(getTitleBarHeight(hWnd))}; + if (const auto tb = titleBar()) { + auto titleBarHeight = qreal(m_delegate->mapGeometryToScene(tb).height()); + titleBarHeight *= m_windowHandle->devicePixelRatio(); + menuPos.y = qRound(titleBarHeight); + } + ::ClientToScreen(hWnd, &menuPos); + nativeGlobalPos = menuPos; + + // Install mouse hook + if (!mouseHook) { + mouseHook = ::SetWindowsHookExW( + WH_MOUSE, + [](int nCode, WPARAM wParam, LPARAM lParam) { + if (nCode >= 0) { + if (wParam == WM_LBUTTONDOWN || wParam == WM_LBUTTONDBLCLK) { + auto pMouseStruct = reinterpret_cast<MOUSEHOOKSTRUCT *>(lParam); + if (pMouseStruct) { + mouseClickPos = pMouseStruct->pt; + } + } + } + return ::CallNextHookEx(nullptr, nCode, wParam, lParam); + }, + nullptr, ::GetCurrentThreadId()); + mouseHookedLocal = true; + } + } bool res = showSystemMenu_sys(hWnd, nativeGlobalPos, broughtByKeyboard, m_delegate->isHostSizeFixed(m_host)); - // Check time - static uint32_t doubleClickTime = ::GetDoubleClickTime(); - if (!res && iconButtonClickTime != 0 && - ::GetTickCount64() - iconButtonClickTime <= doubleClickTime) { - POINT nativeGlobalPos; - ::GetCursorPos(&nativeGlobalPos); - POINT nativeLocalPos = nativeGlobalPos; - ::ScreenToClient(hWnd, &nativeLocalPos); - QPoint qtScenePos = - QHighDpi::fromNativeLocalPosition(point2qpoint(nativeLocalPos), m_windowHandle); - WindowAgentBase::SystemButton sysButtonType = WindowAgentBase::Unknown; - if (isInSystemButtons(qtScenePos, &sysButtonType) && - sysButtonType == WindowAgentBase::WindowIcon) { - ::PostMessageW(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); + // Uninstall mouse hook and check if it's a double-click + if (mouseHookedLocal) { + ::UnhookWindowsHookEx(mouseHook); + + // Emulate the Windows icon button's behavior + static uint32_t doubleClickTime = ::GetDoubleClickTime(); + if (!res && mouseClickPos.has_value() && + ::GetTickCount64() - iconButtonClickTime <= doubleClickTime) { + POINT nativeGlobalPos; + ::GetCursorPos(&nativeGlobalPos); + POINT nativeLocalPos = mouseClickPos.value(); + // qDebug() << point2qpoint(nativeLocalPos) << point2qpoint(nativeGlobalPos); + ::ScreenToClient(hWnd, &nativeLocalPos); + QPoint qtScenePos = QHighDpi::fromNativeLocalPosition( + point2qpoint(nativeLocalPos), m_windowHandle); + WindowAgentBase::SystemButton sysButtonType = WindowAgentBase::Unknown; + if (isInSystemButtons(qtScenePos, &sysButtonType) && + sysButtonType == WindowAgentBase::WindowIcon) { + ::PostMessageW(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); + } } + + mouseHook = nullptr; + mouseClickPos.reset(); } // QPA's internal code will handle system menu events separately, and its -- Gitblit v1.9.1