From b0249aebbacf71eee27e77c766ccb3dc33693b11 Mon Sep 17 00:00:00 2001 From: Zhao Yuhang <2546789017@qq.com> Date: 周日, 10 12月 2023 13:54:14 +0800 Subject: [PATCH] a little simplify --- src/core/contexts/win32windowcontext.cpp | 368 ++++++++++++++++++++++++++++++++++++++++++--------- 1 files changed, 299 insertions(+), 69 deletions(-) diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index 2ff795e..0f2b23f 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -5,7 +5,6 @@ #include <QtCore/QHash> #include <QtCore/QAbstractNativeEventFilter> -#include <QtCore/QOperatingSystemVersion> #include <QtCore/QScopeGuard> #include <QtGui/QGuiApplication> @@ -24,19 +23,26 @@ #include <shellscalingapi.h> #include <dwmapi.h> #include <timeapi.h> +#include <versionhelpers.h> Q_DECLARE_METATYPE(QMargins) namespace QWK { - static constexpr const auto kAutoHideTaskBarThickness = - quint8{2}; // The thickness of an auto-hide taskbar in pixels. + // The thickness of an auto-hide taskbar in pixels. + static constexpr const auto kAutoHideTaskBarThickness = quint8{2}; - using WndProcHash = QHash<HWND, Win32WindowContext *>; // hWnd -> context + // hWnd -> context + using WndProcHash = QHash<HWND, Win32WindowContext *>; Q_GLOBAL_STATIC(WndProcHash, g_wndProcHash) - static WNDPROC g_qtWindowProc = nullptr; // Original Qt window proc function + // Original Qt window proc function + static WNDPROC g_qtWindowProc = nullptr; + // ### FIXME FIXME FIXME + // ### FIXME: Tell the user to call in the documentation, instead of automatically + // calling it directly. + // ### FIXME FIXME FIXME static struct QWK_Hook { QWK_Hook() { qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); @@ -44,37 +50,52 @@ } g_hook{}; struct DynamicApis { - decltype(&::DwmFlush) pDwmFlush = nullptr; - decltype(&::DwmIsCompositionEnabled) pDwmIsCompositionEnabled = nullptr; - decltype(&::DwmGetCompositionTimingInfo) pDwmGetCompositionTimingInfo = nullptr; - decltype(&::GetDpiForWindow) pGetDpiForWindow = nullptr; - decltype(&::GetSystemMetricsForDpi) pGetSystemMetricsForDpi = nullptr; - decltype(&::GetDpiForMonitor) pGetDpiForMonitor = nullptr; - decltype(&::timeGetDevCaps) ptimeGetDevCaps = nullptr; - decltype(&::timeBeginPeriod) ptimeBeginPeriod = nullptr; - decltype(&::timeEndPeriod) ptimeEndPeriod = nullptr; +// template <typename T> +// struct DefaultFunc; +// +// template <typename Return, typename... Args> +// struct DefaultFunc<Return(QT_WIN_CALLBACK *)(Args...)> { +// static Return STDAPICALLTYPE func(Args...) { +// return Return{}; +// } +// }; +// +// #define DYNAMIC_API_DECLARE(NAME) decltype(&::NAME) p##NAME = DefaultFunc<decltype(&::NAME)>::func +#define DYNAMIC_API_DECLARE(NAME) decltype(&::NAME) p##NAME = nullptr + + DYNAMIC_API_DECLARE(DwmFlush); + DYNAMIC_API_DECLARE(DwmIsCompositionEnabled); + DYNAMIC_API_DECLARE(DwmGetCompositionTimingInfo); + DYNAMIC_API_DECLARE(GetDpiForWindow); + DYNAMIC_API_DECLARE(GetSystemMetricsForDpi); + DYNAMIC_API_DECLARE(GetDpiForMonitor); + DYNAMIC_API_DECLARE(timeGetDevCaps); + DYNAMIC_API_DECLARE(timeBeginPeriod); + DYNAMIC_API_DECLARE(timeEndPeriod); + +#undef DYNAMIC_API_DECLARE DynamicApis() { - QSystemLibrary user32(QStringLiteral("user32.dll")); - pGetDpiForWindow = - reinterpret_cast<decltype(pGetDpiForWindow)>(user32.resolve("GetDpiForWindow")); - pGetSystemMetricsForDpi = reinterpret_cast<decltype(pGetSystemMetricsForDpi)>( - user32.resolve("GetSystemMetricsForDpi")); +#define DYNAMIC_API_RESOLVE(DLL, NAME) p##NAME = reinterpret_cast<decltype(p##NAME)>(DLL##.resolve(#NAME)) - QSystemLibrary shcore(QStringLiteral("shcore.dll")); - pGetDpiForMonitor = - reinterpret_cast<decltype(pGetDpiForMonitor)>(shcore.resolve("GetDpiForMonitor")); + QSystemLibrary user32(QStringLiteral("user32")); + DYNAMIC_API_RESOLVE(user32, GetDpiForWindow); + DYNAMIC_API_RESOLVE(user32, GetSystemMetricsForDpi); - QSystemLibrary dwmapi(QStringLiteral("dwmapi.dll")); - pDwmFlush = reinterpret_cast<decltype(pDwmFlush)>(dwmapi.resolve("DwmFlush")); - pDwmIsCompositionEnabled = reinterpret_cast<decltype(pDwmIsCompositionEnabled)>( - dwmapi.resolve("DwmIsCompositionEnabled")); - pDwmGetCompositionTimingInfo = reinterpret_cast<decltype(pDwmGetCompositionTimingInfo)>(dwmapi.resolve("DwmGetCompositionTimingInfo")); + QSystemLibrary shcore(QStringLiteral("shcore")); + DYNAMIC_API_RESOLVE(shcore, GetDpiForMonitor); - QSystemLibrary winmm(QStringLiteral("winmm.dll")); - ptimeGetDevCaps = reinterpret_cast<decltype(ptimeGetDevCaps)>(winmm.resolve("timeGetDevCaps")); - ptimeBeginPeriod = reinterpret_cast<decltype(ptimeBeginPeriod)>(winmm.resolve("timeBeginPeriod")); - ptimeEndPeriod = reinterpret_cast<decltype(ptimeEndPeriod)>(winmm.resolve("timeEndPeriod")); + QSystemLibrary dwmapi(QStringLiteral("dwmapi")); + DYNAMIC_API_RESOLVE(dwmapi, DwmFlush); + DYNAMIC_API_RESOLVE(dwmapi, DwmIsCompositionEnabled); + DYNAMIC_API_RESOLVE(dwmapi, DwmGetCompositionTimingInfo); + + QSystemLibrary winmm(QStringLiteral("winmm")); + DYNAMIC_API_RESOLVE(winmm, timeGetDevCaps); + DYNAMIC_API_RESOLVE(winmm, timeBeginPeriod); + DYNAMIC_API_RESOLVE(winmm, timeEndPeriod); + +#undef DYNAMIC_API_RESOLVE } ~DynamicApis() = default; @@ -169,20 +190,22 @@ } static inline bool isWin8OrGreater() { - static const bool result = - QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8; + static const bool result = ::IsWindows8OrGreater(); return result; } static inline bool isWin8Point1OrGreater() { - static const bool result = - QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8_1; + static const bool result = ::IsWindows8Point1OrGreater(); return result; } static inline bool isWin10OrGreater() { - static const bool result = - QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10; + static const bool result = ::IsWindows10OrGreater(); + return result; + } + + static inline bool isWin11OrGreater() { + static const bool result = ::IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN10), LOBYTE(_WIN32_WINNT_WIN10), 22000); return result; } @@ -210,8 +233,8 @@ return apis.pGetDpiForWindow(hwnd); } else if (apis.pGetDpiForMonitor) { // Win8.1 HMONITOR monitor = ::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); - UINT dpiX{USER_DEFAULT_SCREEN_DPI}; - UINT dpiY{USER_DEFAULT_SCREEN_DPI}; + UINT dpiX{0}; + UINT dpiY{0}; apis.pGetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); return dpiX; } else { // Win2K @@ -262,7 +285,7 @@ #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) if (QPlatformWindow *platformWindow = window->handle()) { if (const auto ni = QGuiApplication::platformNativeInterface()) { - ni->setWindowProperty(platformWindow, QStringLiteral("_q_windowsCustomMargins"), + ni->setWindowProperty(platformWindow, QStringLiteral("WindowsCustomMargins"), marginsVar); } } @@ -358,6 +381,117 @@ apis.ptimeEndPeriod(ms_granularity); } + static inline void showSystemMenu2(HWND hWnd, const POINT &pos, const bool selectFirstEntry) + { + const HMENU hMenu = ::GetSystemMenu(hWnd, FALSE); + if (!hMenu) { + // The corresponding window doesn't have a system menu, most likely due to the + // lack of the "WS_SYSMENU" window style. This situation should not be treated + // as an error so just ignore it and return early. + return; + } + + // Tweak the menu items according to the current window status and user settings. + const bool disableClose = /*data->callbacks->getProperty(kSysMenuDisableCloseVar, false).toBool()*/false; + const bool disableRestore = /*data->callbacks->getProperty(kSysMenuDisableRestoreVar, false).toBool()*/false; + const bool disableMinimize = /*data->callbacks->getProperty(kSysMenuDisableMinimizeVar, false).toBool()*/false; + const bool disableMaximize = /*data->callbacks->getProperty(kSysMenuDisableMaximizeVar, false).toBool()*/false; + const bool disableSize = /*data->callbacks->getProperty(kSysMenuDisableSizeVar, false).toBool()*/false; + const bool disableMove = /*data->callbacks->getProperty(kSysMenuDisableMoveVar, false).toBool()*/false; + const bool removeClose = /*data->callbacks->getProperty(kSysMenuRemoveCloseVar, false).toBool()*/false; + const bool removeSeparator = /*data->callbacks->getProperty(kSysMenuRemoveSeparatorVar, false).toBool()*/false; + const bool removeRestore = /*data->callbacks->getProperty(kSysMenuRemoveRestoreVar, false).toBool()*/false; + const bool removeMinimize = /*data->callbacks->getProperty(kSysMenuRemoveMinimizeVar, false).toBool()*/false; + const bool removeMaximize = /*data->callbacks->getProperty(kSysMenuRemoveMaximizeVar, false).toBool()*/false; + const bool removeSize = /*data->callbacks->getProperty(kSysMenuRemoveSizeVar, false).toBool()*/false; + const bool removeMove = /*data->callbacks->getProperty(kSysMenuRemoveMoveVar, false).toBool()*/false; + const bool maxOrFull = IsMaximized(hWnd) || isFullScreen(hWnd); + const bool fixedSize = /*data->callbacks->isWindowFixedSize()*/false; + if (removeClose) { + ::DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND); + } else { + ::EnableMenuItem(hMenu, SC_CLOSE, (MF_BYCOMMAND | (disableClose ? MFS_DISABLED : MFS_ENABLED))); + } + if (removeSeparator) { + // Looks like we must use 0 for the second parameter here, otherwise we can't remove the separator. + ::DeleteMenu(hMenu, 0, MFT_SEPARATOR); + } + if (removeMaximize) { + ::DeleteMenu(hMenu, SC_MAXIMIZE, MF_BYCOMMAND); + } else { + ::EnableMenuItem(hMenu, SC_MAXIMIZE, (MF_BYCOMMAND | ((maxOrFull || fixedSize || disableMaximize) ? MFS_DISABLED : MFS_ENABLED))); + } + if (removeRestore) { + ::DeleteMenu(hMenu, SC_RESTORE, MF_BYCOMMAND); + } else { + ::EnableMenuItem(hMenu, SC_RESTORE, (MF_BYCOMMAND | ((maxOrFull && !fixedSize && !disableRestore) ? MFS_ENABLED : MFS_DISABLED))); + // The first menu item should be selected by default if the menu is brought + // up by keyboard. I don't know how to pre-select a menu item but it seems + // highlight can do the job. However, there's an annoying issue if we do + // this manually: the highlighted menu item is really only highlighted, + // not selected, so even if the mouse cursor hovers on other menu items + // or the user navigates to other menu items through keyboard, the original + // highlight bar will not move accordingly, the OS will generate another + // highlight bar to indicate the current selected menu item, which will make + // the menu look kind of weird. Currently I don't know how to fix this issue. + ::HiliteMenuItem(hWnd, hMenu, SC_RESTORE, (MF_BYCOMMAND | (selectFirstEntry ? MFS_HILITE : MFS_UNHILITE))); + } + if (removeMinimize) { + ::DeleteMenu(hMenu, SC_MINIMIZE, MF_BYCOMMAND); + } else { + ::EnableMenuItem(hMenu, SC_MINIMIZE, (MF_BYCOMMAND | (disableMinimize ? MFS_DISABLED : MFS_ENABLED))); + } + if (removeSize) { + ::DeleteMenu(hMenu, SC_SIZE, MF_BYCOMMAND); + } else { + ::EnableMenuItem(hMenu, SC_SIZE, (MF_BYCOMMAND | ((maxOrFull || fixedSize || disableSize || disableMinimize || disableMaximize) ? MFS_DISABLED : MFS_ENABLED))); + } + if (removeMove) { + ::DeleteMenu(hMenu, SC_MOVE, MF_BYCOMMAND); + } else { + ::EnableMenuItem(hMenu, SC_MOVE, (MF_BYCOMMAND | ((maxOrFull || disableMove) ? MFS_DISABLED : MFS_ENABLED))); + } + + // The default menu item will appear in bold font. There can only be one default + // menu item per menu at most. Set the item ID to "UINT_MAX" (or simply "-1") + // can clear the default item for the given menu. + std::optional<UINT> defaultItemId = std::nullopt; + if (isWin11OrGreater()) { + if (maxOrFull) { + if (!removeRestore) { + defaultItemId = SC_RESTORE; + } + } else { + if (!removeMaximize) { + defaultItemId = SC_MAXIMIZE; + } + } + } + if (!(defaultItemId.has_value() || removeClose)) { + defaultItemId = SC_CLOSE; + } + ::SetMenuDefaultItem(hMenu, defaultItemId.value_or(UINT_MAX), FALSE); + + //::DrawMenuBar(hWnd); + + // Popup the system menu at the required position. + const auto result = ::TrackPopupMenu(hMenu, (TPM_RETURNCMD | (QGuiApplication::isRightToLeft() ? TPM_RIGHTALIGN : TPM_LEFTALIGN)), pos.x, pos.y, 0, hWnd, nullptr); + + if (!removeRestore) { + // Unhighlight the first menu item after the popup menu is closed, otherwise it will keep + // highlighting until we unhighlight it manually. + ::HiliteMenuItem(hWnd, hMenu, SC_RESTORE, (MF_BYCOMMAND | MFS_UNHILITE)); + } + + if (!result) { + // The user canceled the menu, no need to continue. + return; + } + + // Send the command that the user chooses to the corresponding window. + ::PostMessageW(hWnd, WM_SYSCOMMAND, result, 0); + } + static inline Win32WindowContext::WindowPart getHitWindowPart(int hitTestResult) { switch (hitTestResult) { case HTCLIENT: @@ -432,9 +566,6 @@ QT_NATIVE_EVENT_RESULT_TYPE *result) override { Q_UNUSED(eventType) - auto orgLastMessageContext = lastMessageContext; - lastMessageContext = nullptr; - // It has been observed that the pointer that Qt gives us is sometimes null on some // machines. We need to guard against it in such scenarios. if (!result) { @@ -443,11 +574,11 @@ // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/plugins/platforms/windows/qwindowscontext.cpp#L1546 // Qt needs to refer to the WM_NCCALCSIZE message data that hasn't been processed, so we - // have to process it after Qt acquired the initial data. + // have to process it after Qt acquires the initial data. auto msg = static_cast<const MSG *>(message); - if (msg->message == WM_NCCALCSIZE && orgLastMessageContext) { + if (msg->message == WM_NCCALCSIZE && lastMessageContext) { LRESULT res; - if (Win32WindowContext::nonClientCalcSizeHandler(msg->hwnd, msg->message, + if (lastMessageContext->nonClientCalcSizeHandler(msg->hwnd, msg->message, msg->wParam, msg->lParam, &res)) { *result = decltype(*result)(res); return true; @@ -460,11 +591,17 @@ static Win32WindowContext *lastMessageContext; static inline void install() { + if (instance) { + return; + } instance = new WindowsNativeEventFilter(); installNativeEventFilter(instance); } static inline void uninstall() { + if (!instance) { + return; + } removeNativeEventFilter(instance); delete instance; instance = nullptr; @@ -543,22 +680,18 @@ // forward it right away and process it in our native event filter. if (message == WM_NCCALCSIZE) { WindowsNativeEventFilter::lastMessageContext = ctx; - return ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam); + LRESULT result = ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam); + WindowsNativeEventFilter::lastMessageContext = nullptr; + return result; } // Try hooked procedure and save result LRESULT result; - bool handled = ctx->windowProc(hWnd, message, wParam, lParam, &result); - - // TODO: Determine whether to show system menu - // ... - - if (handled) { + if (ctx->windowProc(hWnd, message, wParam, lParam, &result)) { return result; } // Continue dispatching. - WindowsNativeEventFilter::lastMessageContext = ctx; return ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam); } @@ -594,9 +727,7 @@ ::SetWindowLongPtrW(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(QWKHookedWndProc)); // Install global native event filter - if (!WindowsNativeEventFilter::instance) { - WindowsNativeEventFilter::install(); - } + WindowsNativeEventFilter::install(); // Cache window ID windowId = winId; @@ -636,6 +767,10 @@ // Main implementation if (customWindowHandler(hWnd, message, wParam, lParam, result)) { + return true; + } + + if (systemMenuHandler(hWnd, message, wParam, lParam, result)) { return true; } @@ -798,8 +933,8 @@ DWORD dwScreenPos = ::GetMessagePos(); POINT screenPoint{GET_X_LPARAM(dwScreenPos), GET_Y_LPARAM(dwScreenPos)}; ::ScreenToClient(hWnd, &screenPoint); - QPoint qtScenePos = - QHighDpi::fromNativeLocalPosition(QPoint{screenPoint.x, screenPoint.y}, m_windowHandle); + QPoint qtScenePos = QHighDpi::fromNativeLocalPosition( + QPoint{screenPoint.x, screenPoint.y}, m_windowHandle); auto dummy = CoreWindowAgent::Unknown; if (isInSystemButtons(qtScenePos, &dummy)) { // We must record whether the last WM_MOUSELEAVE was filtered, because if @@ -1048,22 +1183,31 @@ // this is also the normal behavior of a native Win32 window (but only when the // window is not maximized/fullscreen/minimized, of course). if (isWindowNoState(hWnd)) { - static constexpr const int kBorderSize = 2; + static constexpr const auto kBorderSize = quint8{2}; bool isTop = (nativeLocalPos.y <= kBorderSize); + bool isLeft = nativeLocalPos.x <= kBorderSize; bool isRight = (nativeLocalPos.x >= (clientWidth - kBorderSize)); - if (isTop || isRight) { + if (isTop || isLeft || isRight) { if (dontOverrideCursor) { // The user doesn't want the window to be resized, so we tell // Windows we are in the client area so that the controls beneath // the mouse cursor can still be hovered or clicked. *result = (isTitleBar ? HTCAPTION : HTCLIENT); } else { - if (isTop && isRight) { - *result = HTTOPRIGHT; - } else if (isTop) { - *result = HTTOP; + if (isTop) { + if (isLeft) { + *result = HTTOPLEFT; + } else if (isRight) { + *result = HTTOPRIGHT; + } else { + *result = HTTOP; + } } else { - *result = HTRIGHT; + if (isLeft) { + *result = HTLEFT; + } else { + *result = HTRIGHT; + } } } } @@ -1163,9 +1307,9 @@ const bool isBottom = (nativeLocalPos.y >= (clientHeight - frameSize)); // Make the border a little wider to let the user easy to resize on corners. const auto scaleFactor = ((isTop || isBottom) ? qreal(2) : qreal(1)); - const int scaledFrameSizeX = std::round(qreal(frameSize) * scaleFactor); - const bool isLeft = (nativeLocalPos.x < scaledFrameSizeX); - const bool isRight = (nativeLocalPos.x >= (clientWidth - scaledFrameSizeX)); + const int scaledFrameSize = std::round(qreal(frameSize) * scaleFactor); + const bool isLeft = (nativeLocalPos.x < scaledFrameSize); + const bool isRight = (nativeLocalPos.x >= (clientWidth - scaledFrameSize)); if (dontOverrideCursor && (isTop || isBottom || isLeft || isRight)) { // Return HTCLIENT instead of HTBORDER here, because the mouse is // inside the window now, return HTCLIENT to let the controls @@ -1283,6 +1427,7 @@ bool Win32WindowContext::nonClientCalcSizeHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result) { Q_UNUSED(message) + Q_UNUSED(this) // Windows鏄牴鎹繖涓秷鎭殑杩斿洖鍊兼潵璁剧疆绐楀彛鐨勫鎴峰尯锛堢獥鍙d腑鐪熸鏄剧ず鐨勫唴瀹癸級 // 鍜岄潪瀹㈡埛鍖猴紙鏍囬鏍忋�佺獥鍙h竟妗嗐�佽彍鍗曟爮鍜岀姸鎬佹爮绛塛indows绯荤粺鑷鎻愪緵鐨勯儴鍒� @@ -1507,4 +1652,89 @@ return true; } + bool Win32WindowContext::systemMenuHandler(HWND hWnd, UINT message, WPARAM wParam, + LPARAM lParam, LRESULT *result) { + const auto getNativePosFromMouse = [lParam]() -> POINT { + return {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; + }; + const auto getNativeGlobalPosFromKeyboard = [hWnd]() -> POINT { + const bool maxOrFull = IsMaximized(hWnd) || isFullScreen(hWnd); + const quint32 frameSize = getResizeBorderThickness(hWnd); + const quint32 horizontalOffset = ((maxOrFull || !isWin10OrGreater()) ? 0 : frameSize); + const auto verticalOffset = [hWnd, maxOrFull, frameSize]() -> quint32 { + const quint32 titleBarHeight = getTitleBarHeight(hWnd); + if (!isWin10OrGreater()) { + return titleBarHeight; + } + if (isWin11OrGreater()) { + if (maxOrFull) { + return (titleBarHeight + frameSize); + } + return titleBarHeight; + } + if (maxOrFull) { + return titleBarHeight; + } + return titleBarHeight - frameSize; + }(); + RECT windowPos{}; + ::GetWindowRect(hWnd, &windowPos); + return {static_cast<LONG>(windowPos.left + horizontalOffset), static_cast<LONG>(windowPos.top + verticalOffset)}; + }; + bool shouldShowSystemMenu = false; + bool broughtByKeyboard = false; + POINT nativeGlobalPos{}; + switch (message) { + case WM_RBUTTONUP: { + const POINT nativeLocalPos = getNativePosFromMouse(); + const QPoint qtScenePos = QHighDpi::fromNativeLocalPosition(QPoint(nativeLocalPos.x, nativeLocalPos.y), m_windowHandle); + if (isInTitleBarDraggableArea(qtScenePos)) { + shouldShowSystemMenu = true; + nativeGlobalPos = nativeLocalPos; + ::ClientToScreen(hWnd, &nativeGlobalPos); + } + break; + } + case WM_NCRBUTTONUP: { + if (wParam == HTCAPTION) { + shouldShowSystemMenu = true; + nativeGlobalPos = getNativePosFromMouse(); + } + break; + } + case WM_SYSCOMMAND: { + const WPARAM filteredWParam = (wParam & 0xFFF0); + if ((filteredWParam == SC_KEYMENU) && (lParam == VK_SPACE)) { + shouldShowSystemMenu = true; + broughtByKeyboard = true; + nativeGlobalPos = getNativeGlobalPosFromKeyboard(); + } + break; + } + case WM_KEYDOWN: + case WM_SYSKEYDOWN: { + const bool altPressed = ((wParam == VK_MENU) || (::GetKeyState(VK_MENU) < 0)); + const bool spacePressed = ((wParam == VK_SPACE) || (::GetKeyState(VK_SPACE) < 0)); + if (altPressed && spacePressed) { + shouldShowSystemMenu = true; + broughtByKeyboard = true; + nativeGlobalPos = getNativeGlobalPosFromKeyboard(); + } + break; + } + default: + break; + } + if (shouldShowSystemMenu) { + showSystemMenu2(hWnd, nativeGlobalPos, broughtByKeyboard); + // QPA's internal code will handle system menu events separately, and its + // behavior is not what we would want to see because it doesn't know our + // window doesn't have any window frame now, so return early here to avoid + // entering Qt's own handling logic. + *result = FALSE; + return true; + } + return false; + } + } -- Gitblit v1.9.1