/**************************************************************************** ** ** Copyright (C) 2013 Samuel Gaist ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qwindowscontext.h" #include "qwindowsintegration.h" #include "qwindowswindow.h" #include "qwindowskeymapper.h" #include "qwindowsnativeinterface.h" #include "qwindowsmousehandler.h" #include "qwindowspointerhandler.h" #include "qtwindowsglobal.h" #include "qwindowsmenu.h" #include "qwindowsmime.h" #include "qwindowsinputcontext.h" #if QT_CONFIG(tabletevent) # include "qwindowstabletsupport.h" #endif #include "qwindowstheme.h" #include #if QT_CONFIG(accessibility) # include "uiautomation/qwindowsuiaaccessibility.h" #endif #if QT_CONFIG(sessionmanager) # include # include "qwindowssessionmanager.h" #endif #include "qwindowsscreen.h" #include "qwindowstheme.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaWindows, "qt.qpa.windows") Q_LOGGING_CATEGORY(lcQpaEvents, "qt.qpa.events") Q_LOGGING_CATEGORY(lcQpaGl, "qt.qpa.gl") Q_LOGGING_CATEGORY(lcQpaMime, "qt.qpa.mime") Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods") Q_LOGGING_CATEGORY(lcQpaDialogs, "qt.qpa.dialogs") Q_LOGGING_CATEGORY(lcQpaMenus, "qt.qpa.menus") Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") Q_LOGGING_CATEGORY(lcQpaAccessibility, "qt.qpa.accessibility") Q_LOGGING_CATEGORY(lcQpaUiAutomation, "qt.qpa.uiautomation") Q_LOGGING_CATEGORY(lcQpaTrayIcon, "qt.qpa.trayicon") int QWindowsContext::verbose = 0; #if !defined(LANG_SYRIAC) # define LANG_SYRIAC 0x5a #endif static inline bool useRTL_Extensions() { // Since the IsValidLanguageGroup/IsValidLocale functions always return true on // Vista, check the Keyboard Layouts for enabling RTL. if (const int nLayouts = GetKeyboardLayoutList(0, nullptr)) { QScopedArrayPointer lpList(new HKL[nLayouts]); GetKeyboardLayoutList(nLayouts, lpList.data()); for (int i = 0; i < nLayouts; ++i) { switch (PRIMARYLANGID((quintptr)lpList[i])) { case LANG_ARABIC: case LANG_HEBREW: case LANG_FARSI: case LANG_SYRIAC: return true; default: break; } } } return false; } #if QT_CONFIG(sessionmanager) static inline QWindowsSessionManager *platformSessionManager() { auto *guiPrivate = static_cast(QObjectPrivate::get(qApp)); auto *managerPrivate = static_cast(QObjectPrivate::get(guiPrivate->session_manager)); return static_cast(managerPrivate->platformSessionManager); } static inline bool sessionManagerInteractionBlocked() { return platformSessionManager()->isInteractionBlocked(); } #else // QT_CONFIG(sessionmanager) static inline bool sessionManagerInteractionBlocked() { return false; } #endif static inline int windowDpiAwareness(HWND hwnd) { return static_cast(GetAwarenessFromDpiAwarenessContext(GetWindowDpiAwarenessContext(hwnd))); } // Note: This only works within WM_NCCREATE static bool enableNonClientDpiScaling(HWND hwnd) { bool result = false; if (windowDpiAwareness(hwnd) == 2) { result = EnableNonClientDpiScaling(hwnd) != FALSE; if (!result) { const DWORD errorCode = GetLastError(); qErrnoWarning(int(errorCode), "EnableNonClientDpiScaling() failed for HWND %p (%lu)", hwnd, errorCode); } } return result; } QWindowsContext *QWindowsContext::m_instance = nullptr; /*! \class QWindowsContext \brief Singleton container for all relevant information. Holds state information formerly stored in \c qapplication_win.cpp. \internal */ struct QWindowsContextPrivate { QWindowsContextPrivate(); unsigned m_systemInfo = 0; QSet m_registeredWindowClassNames; QWindowsContext::HandleBaseWindowHash m_windows; HDC m_displayContext = nullptr; int m_defaultDPI = 96; QWindowsKeyMapper m_keyMapper; QWindowsMouseHandler m_mouseHandler; QWindowsPointerHandler m_pointerHandler; QWindowsMimeConverter m_mimeConverter; QWindowsScreenManager m_screenManager; QSharedPointer m_creationContext; #if QT_CONFIG(tabletevent) QScopedPointer m_tabletSupport; #endif const HRESULT m_oleInitializeResult; QWindow *m_lastActiveWindow = nullptr; bool m_asyncExpose = false; HPOWERNOTIFY m_powerNotification = nullptr; HWND m_powerDummyWindow = nullptr; static bool m_darkMode; static bool m_v2DpiAware; }; bool QWindowsContextPrivate::m_darkMode = false; bool QWindowsContextPrivate::m_v2DpiAware = false; QWindowsContextPrivate::QWindowsContextPrivate() : m_oleInitializeResult(OleInitialize(nullptr)) { if (m_pointerHandler.touchDevice() || m_mouseHandler.touchDevice()) m_systemInfo |= QWindowsContext::SI_SupportsTouch; m_displayContext = GetDC(nullptr); m_defaultDPI = GetDeviceCaps(m_displayContext, LOGPIXELSY); if (useRTL_Extensions()) { m_systemInfo |= QWindowsContext::SI_RTL_Extensions; m_keyMapper.setUseRTLExtensions(true); } m_darkMode = QWindowsTheme::queryDarkMode(); if (FAILED(m_oleInitializeResult)) { qWarning() << "QWindowsContext: OleInitialize() failed: " << QWindowsContext::comErrorString(m_oleInitializeResult); } } QWindowsContext::QWindowsContext() : d(new QWindowsContextPrivate) { #ifdef Q_CC_MSVC # pragma warning( disable : 4996 ) #endif m_instance = this; // ### FIXME: Remove this once the logging system has other options of configurations. const QByteArray bv = qgetenv("QT_QPA_VERBOSE"); if (!bv.isEmpty()) QLoggingCategory::setFilterRules(QString::fromLocal8Bit(bv)); } QWindowsContext::~QWindowsContext() { #if QT_CONFIG(tabletevent) d->m_tabletSupport.reset(); // Destroy internal window before unregistering classes. #endif if (d->m_powerNotification) UnregisterPowerSettingNotification(d->m_powerNotification); if (d->m_powerDummyWindow) DestroyWindow(d->m_powerDummyWindow); unregisterWindowClasses(); if (d->m_oleInitializeResult == S_OK || d->m_oleInitializeResult == S_FALSE) OleUninitialize(); d->m_screenManager.clearScreens(); // Order: Potentially calls back to the windows. if (d->m_displayContext) ReleaseDC(nullptr, d->m_displayContext); m_instance = nullptr; } bool QWindowsContext::initTouch() { return initTouch(QWindowsIntegration::instance()->options()); } bool QWindowsContext::initTouch(unsigned integrationOptions) { if (d->m_systemInfo & QWindowsContext::SI_SupportsTouch) return true; const bool usePointerHandler = (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) != 0; auto touchDevice = usePointerHandler ? d->m_pointerHandler.touchDevice() : d->m_mouseHandler.touchDevice(); if (touchDevice.isNull()) { const bool mouseEmulation = (integrationOptions & QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch) == 0; touchDevice = QWindowsPointerHandler::createTouchDevice(mouseEmulation); } if (touchDevice.isNull()) return false; d->m_pointerHandler.setTouchDevice(touchDevice); d->m_mouseHandler.setTouchDevice(touchDevice); QWindowSystemInterface::registerInputDevice(touchDevice.data()); d->m_systemInfo |= QWindowsContext::SI_SupportsTouch; // A touch device was plugged while the app is running. Register all windows for touch. registerTouchWindows(); return true; } void QWindowsContext::registerTouchWindows() { if (QGuiApplicationPrivate::is_app_running && (d->m_systemInfo & QWindowsContext::SI_SupportsTouch) != 0) { for (QWindowsWindow *w : qAsConst(d->m_windows)) w->registerTouchWindow(); } } bool QWindowsContext::initTablet() { #if QT_CONFIG(tabletevent) d->m_tabletSupport.reset(QWindowsTabletSupport::create()); return true; #else return false; #endif } bool QWindowsContext::disposeTablet() { #if QT_CONFIG(tabletevent) d->m_tabletSupport.reset(); return true; #else return false; #endif } bool QWindowsContext::initPointer(unsigned integrationOptions) { if (integrationOptions & QWindowsIntegration::DontUseWMPointer) return false; d->m_systemInfo |= QWindowsContext::SI_SupportsPointer; return true; } extern "C" LRESULT QT_WIN_CALLBACK qWindowsPowerWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { if (message != WM_POWERBROADCAST || wParam != PBT_POWERSETTINGCHANGE) return DefWindowProc(hwnd, message, wParam, lParam); static bool initialized = false; // ignore the initial change if (!initialized) { initialized = true; return DefWindowProc(hwnd, message, wParam, lParam); } auto setting = reinterpret_cast(lParam); if (setting) { auto data = reinterpret_cast(&setting->Data); if (*data == 1) { // Repaint the windows when returning from sleeping display mode. const auto tlw = QGuiApplication::topLevelWindows(); for (auto w : tlw) { if (w->isVisible() && w->windowState() != Qt::WindowMinimized) { if (auto tw = QWindowsWindow::windowsWindowOf(w)) { if (HWND hwnd = tw->handle()) { InvalidateRect(hwnd, nullptr, false); } } } } } } return DefWindowProc(hwnd, message, wParam, lParam); } bool QWindowsContext::initPowerNotificationHandler() { if (d->m_powerNotification) return false; d->m_powerDummyWindow = createDummyWindow(QStringLiteral("PowerDummyWindow"), L"QtPowerDummyWindow", qWindowsPowerWindowProc); if (!d->m_powerDummyWindow) return false; d->m_powerNotification = RegisterPowerSettingNotification(d->m_powerDummyWindow, &GUID_MONITOR_POWER_ON, DEVICE_NOTIFY_WINDOW_HANDLE); if (!d->m_powerNotification) { DestroyWindow(d->m_powerDummyWindow); d->m_powerDummyWindow = nullptr; return false; } return true; } void QWindowsContext::setTabletAbsoluteRange(int a) { #if QT_CONFIG(tabletevent) QWindowsTabletSupport::setAbsoluteRange(a); #else Q_UNUSED(a); #endif } void QWindowsContext::setDetectAltGrModifier(bool a) { d->m_keyMapper.setDetectAltGrModifier(a); } int QWindowsContext::processDpiAwareness() { PROCESS_DPI_AWARENESS result; if (SUCCEEDED(GetProcessDpiAwareness(nullptr, &result))) { return static_cast(result); } return -1; } void QWindowsContext::setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiAwareness) { qCDebug(lcQpaWindows) << __FUNCTION__ << dpiAwareness; const HRESULT hr = SetProcessDpiAwareness(static_cast(dpiAwareness)); // E_ACCESSDENIED means set externally (MSVC manifest or external app loading Qt plugin). // Silence warning in that case unless debug is enabled. if (FAILED(hr) && (hr != E_ACCESSDENIED || lcQpaWindows().isDebugEnabled())) { qWarning().noquote().nospace() << "SetProcessDpiAwareness(" << dpiAwareness << ") failed: " << QWindowsContext::comErrorString(hr) << ", using " << QWindowsContext::processDpiAwareness(); } } void QWindowsContext::setProcessDpiV2Awareness() { qCDebug(lcQpaWindows) << __FUNCTION__; const BOOL ok = SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); if (ok) { QWindowsContextPrivate::m_v2DpiAware = true; } else { const HRESULT errorCode = GetLastError(); // ERROR_ACCESS_DENIED means set externally (MSVC manifest or external app loading Qt plugin). // Silence warning in that case unless debug is enabled. if (errorCode != ERROR_ACCESS_DENIED || lcQpaWindows().isDebugEnabled()) { qWarning().noquote().nospace() << "setProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) failed: " << QWindowsContext::comErrorString(errorCode); } } } bool QWindowsContext::isDarkMode() { return QWindowsContextPrivate::m_darkMode; } QWindowsContext *QWindowsContext::instance() { return m_instance; } unsigned QWindowsContext::systemInfo() const { return d->m_systemInfo; } bool QWindowsContext::useRTLExtensions() const { return d->m_keyMapper.useRTLExtensions(); } QList QWindowsContext::possibleKeys(const QKeyEvent *e) const { return d->m_keyMapper.possibleKeys(e); } QWindowsContext::HandleBaseWindowHash &QWindowsContext::windows() { return d->m_windows; } QSharedPointer QWindowsContext::setWindowCreationContext(const QSharedPointer &ctx) { const QSharedPointer old = d->m_creationContext; d->m_creationContext = ctx; return old; } QSharedPointer QWindowsContext::windowCreationContext() const { return d->m_creationContext; } int QWindowsContext::defaultDPI() const { return d->m_defaultDPI; } HDC QWindowsContext::displayContext() const { return d->m_displayContext; } QWindow *QWindowsContext::keyGrabber() const { return d->m_keyMapper.keyGrabber(); } void QWindowsContext::setKeyGrabber(QWindow *w) { d->m_keyMapper.setKeyGrabber(w); } QString QWindowsContext::classNamePrefix() { static QString result; if (result.isEmpty()) { QTextStream str(&result); str << "Qt" << QT_VERSION_MAJOR << QT_VERSION_MINOR << QT_VERSION_PATCH; if (QLibraryInfo::isDebugBuild()) str << 'd'; #ifdef QT_NAMESPACE # define xstr(s) str(s) # define str(s) #s str << xstr(QT_NAMESPACE); #endif } return result; } // Window class registering code (from qapplication_win.cpp) QString QWindowsContext::registerWindowClass(const QWindow *w) { Q_ASSERT(w); const Qt::WindowFlags flags = w->flags(); const Qt::WindowFlags type = flags & Qt::WindowType_Mask; // Determine style and icon. uint style = CS_DBLCLKS; bool icon = true; // The following will not set CS_OWNDC for any widget window, even if it contains a // QOpenGLWidget or QQuickWidget later on. That cannot be detected at this stage. if (w->surfaceType() == QSurface::OpenGLSurface || (flags & Qt::MSWindowsOwnDC)) style |= CS_OWNDC; if (!(flags & Qt::NoDropShadowWindowHint) && (type == Qt::Popup || w->property("_q_windowsDropShadow").toBool())) { style |= CS_DROPSHADOW; } switch (type) { case Qt::Tool: case Qt::ToolTip: case Qt::Popup: style |= CS_SAVEBITS; // Save/restore background icon = false; break; case Qt::Dialog: if (!(flags & Qt::WindowSystemMenuHint)) icon = false; // QTBUG-2027, dialogs without system menu. break; } // Create a unique name for the flag combination QString cname = classNamePrefix(); cname += QLatin1String("QWindow"); switch (type) { case Qt::Tool: cname += QLatin1String("Tool"); break; case Qt::ToolTip: cname += QLatin1String("ToolTip"); break; case Qt::Popup: cname += QLatin1String("Popup"); break; default: break; } if (style & CS_DROPSHADOW) cname += QLatin1String("DropShadow"); if (style & CS_SAVEBITS) cname += QLatin1String("SaveBits"); if (style & CS_OWNDC) cname += QLatin1String("OwnDC"); if (icon) cname += QLatin1String("Icon"); return registerWindowClass(cname, qWindowsWndProc, style, GetSysColorBrush(COLOR_WINDOW), icon); } QString QWindowsContext::registerWindowClass(QString cname, WNDPROC proc, unsigned style, HBRUSH brush, bool icon) { // since multiple Qt versions can be used in one process // each one has to have window class names with a unique name // The first instance gets the unmodified name; if the class // has already been registered by another instance of Qt then // add a UUID. The check needs to be performed for each name // in case new message windows are added (QTBUG-81347). // Note: GetClassInfo() returns != 0 when a class exists. const auto appInstance = static_cast(GetModuleHandle(nullptr)); WNDCLASS wcinfo; const bool classExists = GetClassInfo(appInstance, reinterpret_cast(cname.utf16()), &wcinfo) != FALSE && wcinfo.lpfnWndProc != proc; if (classExists) cname += QUuid::createUuid().toString(); if (d->m_registeredWindowClassNames.contains(cname)) // already registered in our list return cname; WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = style; wc.lpfnWndProc = proc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = appInstance; wc.hCursor = nullptr; wc.hbrBackground = brush; if (icon) { wc.hIcon = static_cast(LoadImage(appInstance, L"IDI_ICON1", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE)); if (wc.hIcon) { int sw = GetSystemMetrics(SM_CXSMICON); int sh = GetSystemMetrics(SM_CYSMICON); wc.hIconSm = static_cast(LoadImage(appInstance, L"IDI_ICON1", IMAGE_ICON, sw, sh, 0)); } else { wc.hIcon = static_cast(LoadImage(nullptr, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED)); wc.hIconSm = nullptr; } } else { wc.hIcon = nullptr; wc.hIconSm = nullptr; } wc.lpszMenuName = nullptr; wc.lpszClassName = reinterpret_cast(cname.utf16()); ATOM atom = RegisterClassEx(&wc); if (!atom) qErrnoWarning("QApplication::regClass: Registering window class '%s' failed.", qPrintable(cname)); d->m_registeredWindowClassNames.insert(cname); qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << ' ' << cname << " style=0x" << Qt::hex << style << Qt::dec << " brush=" << brush << " icon=" << icon << " atom=" << atom; return cname; } void QWindowsContext::unregisterWindowClasses() { const auto appInstance = static_cast(GetModuleHandle(nullptr)); for (const QString &name : qAsConst(d->m_registeredWindowClassNames)) { if (!UnregisterClass(reinterpret_cast(name.utf16()), appInstance) && QWindowsContext::verbose) qErrnoWarning("UnregisterClass failed for '%s'", qPrintable(name)); } d->m_registeredWindowClassNames.clear(); } int QWindowsContext::screenDepth() const { return GetDeviceCaps(d->m_displayContext, BITSPIXEL); } QString QWindowsContext::windowsErrorMessage(unsigned long errorCode) { QString rc = QString::fromLatin1("#%1: ").arg(errorCode); char16_t *lpMsgBuf; const DWORD len = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, errorCode, 0, reinterpret_cast(&lpMsgBuf), 0, nullptr); if (len) { rc = QString::fromUtf16(lpMsgBuf, int(len)); LocalFree(lpMsgBuf); } else { rc += QString::fromLatin1(""); } return rc; } void QWindowsContext::addWindow(HWND hwnd, QWindowsWindow *w) { d->m_windows.insert(hwnd, w); } void QWindowsContext::removeWindow(HWND hwnd) { const HandleBaseWindowHash::iterator it = d->m_windows.find(hwnd); if (it != d->m_windows.end()) { if (d->m_keyMapper.keyGrabber() == it.value()->window()) d->m_keyMapper.setKeyGrabber(nullptr); d->m_windows.erase(it); } } QWindowsWindow *QWindowsContext::findPlatformWindow(const QWindowsMenuBar *mb) const { for (auto it = d->m_windows.cbegin(), end = d->m_windows.cend(); it != end; ++it) { if ((*it)->menuBar() == mb) return *it; } return nullptr; } QWindowsWindow *QWindowsContext::findPlatformWindow(HWND hwnd) const { return d->m_windows.value(hwnd); } QWindowsWindow *QWindowsContext::findClosestPlatformWindow(HWND hwnd) const { QWindowsWindow *window = d->m_windows.value(hwnd); // Requested hwnd may also be a child of a platform window in case of embedded native windows. // Find the closest parent that has a platform window. if (!window) { for (HWND w = hwnd; w; w = GetParent(w)) { window = d->m_windows.value(w); if (window) break; } } return window; } QWindow *QWindowsContext::findWindow(HWND hwnd) const { if (const QWindowsWindow *bw = findPlatformWindow(hwnd)) return bw->window(); return nullptr; } QWindow *QWindowsContext::windowUnderMouse() const { return (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) ? d->m_pointerHandler.windowUnderMouse() : d->m_mouseHandler.windowUnderMouse(); } void QWindowsContext::clearWindowUnderMouse() { if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) d->m_pointerHandler.clearWindowUnderMouse(); else d->m_mouseHandler.clearWindowUnderMouse(); } /*! \brief Find a child window at a screen point. Deep search for a QWindow at global point, skipping non-owned windows (accessibility?). Implemented using ChildWindowFromPointEx() instead of (historically used) WindowFromPoint() to get a well-defined behaviour for hidden/transparent windows. \a cwex_flags are flags of ChildWindowFromPointEx(). \a parent is the parent window, pass GetDesktopWindow() for top levels. */ static inline bool findPlatformWindowHelper(const POINT &screenPoint, unsigned cwexFlags, const QWindowsContext *context, HWND *hwnd, QWindowsWindow **result) { POINT point = screenPoint; screenToClient(*hwnd, &point); // Returns parent if inside & none matched. const HWND child = ChildWindowFromPointEx(*hwnd, point, cwexFlags); if (!child || child == *hwnd) return false; if (QWindowsWindow *window = context->findPlatformWindow(child)) { *result = window; *hwnd = child; return true; } // QTBUG-40555: despite CWP_SKIPINVISIBLE, it is possible to hit on invisible // full screen windows of other applications that have WS_EX_TRANSPARENT set // (for example created by screen sharing applications). In that case, try to // find a Qt window by searching again with CWP_SKIPTRANSPARENT. // Note that Qt 5 uses WS_EX_TRANSPARENT for Qt::WindowTransparentForInput // as well. if (!(cwexFlags & CWP_SKIPTRANSPARENT) && (GetWindowLongPtr(child, GWL_EXSTYLE) & WS_EX_TRANSPARENT)) { const HWND nonTransparentChild = ChildWindowFromPointEx(*hwnd, point, cwexFlags | CWP_SKIPTRANSPARENT); if (QWindowsWindow *nonTransparentWindow = context->findPlatformWindow(nonTransparentChild)) { *result = nonTransparentWindow; *hwnd = nonTransparentChild; return true; } } *hwnd = child; return true; } QWindowsWindow *QWindowsContext::findPlatformWindowAt(HWND parent, const QPoint &screenPointIn, unsigned cwex_flags) const { QWindowsWindow *result = nullptr; const POINT screenPoint = { screenPointIn.x(), screenPointIn.y() }; while (findPlatformWindowHelper(screenPoint, cwex_flags, this, &parent, &result)) {} // QTBUG-40815: ChildWindowFromPointEx() can hit on special windows from // screen recorder applications like ScreenToGif. Fall back to WindowFromPoint(). if (result == nullptr) { if (const HWND window = WindowFromPoint(screenPoint)) result = findPlatformWindow(window); } return result; } bool QWindowsContext::isSessionLocked() { bool result = false; const DWORD sessionId = WTSGetActiveConsoleSessionId(); if (sessionId != 0xFFFFFFFF) { LPTSTR buffer = nullptr; DWORD size = 0; #if !defined(Q_CC_MINGW) if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, sessionId, WTSSessionInfoEx, &buffer, &size) == TRUE && size > 0) { const WTSINFOEXW *info = reinterpret_cast(buffer); result = info->Level == 1 && info->Data.WTSInfoExLevel1.SessionFlags == WTS_SESSIONSTATE_LOCK; WTSFreeMemory(buffer); } #else // MinGW as of 7.3 does not have WTSINFOEXW in wtsapi32.h // Retrieve the flags which are at offset 16 due to padding for 32/64bit alike. if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, sessionId, WTS_INFO_CLASS(25), &buffer, &size) == TRUE && size >= 20) { const DWORD *p = reinterpret_cast(buffer); const DWORD level = *p; const DWORD sessionFlags = *(p + 4); result = level == 1 && sessionFlags == 1; WTSFreeMemory(buffer); } #endif // Q_CC_MINGW } return result; } QWindowsMimeConverter &QWindowsContext::mimeConverter() const { return d->m_mimeConverter; } QWindowsScreenManager &QWindowsContext::screenManager() { return d->m_screenManager; } QWindowsTabletSupport *QWindowsContext::tabletSupport() const { #if QT_CONFIG(tabletevent) return d->m_tabletSupport.data(); #else return 0; #endif } /*! \brief Convenience to create a non-visible, message-only dummy window for example used as clipboard watcher or for GL. */ HWND QWindowsContext::createDummyWindow(const QString &classNameIn, const wchar_t *windowName, WNDPROC wndProc, DWORD style) { if (!wndProc) wndProc = DefWindowProc; QString className = registerWindowClass(classNamePrefix() + classNameIn, wndProc); return CreateWindowEx(0, reinterpret_cast(className.utf16()), windowName, style, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_MESSAGE, nullptr, static_cast(GetModuleHandle(nullptr)), nullptr); } // Re-engineered from the inline function _com_error::ErrorMessage(). // We cannot use it directly since it uses swprintf_s(), which is not // present in the MSVCRT.DLL found on Windows XP (QTBUG-35617). static inline QString errorMessageFromComError(const _com_error &comError) { TCHAR *message = nullptr; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, DWORD(comError.Error()), MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), message, 0, nullptr); if (message) { const QString result = QString::fromWCharArray(message).trimmed(); LocalFree(static_cast(message)); return result; } if (const WORD wCode = comError.WCode()) return QString::asprintf("IDispatch error #%u", uint(wCode)); return QString::asprintf("Unknown error 0x0%x", uint(comError.Error())); } /*! \brief Common COM error strings. */ QByteArray QWindowsContext::comErrorString(HRESULT hr) { QByteArray result = QByteArrayLiteral("COM error 0x") + QByteArray::number(quintptr(hr), 16) + ' '; switch (hr) { case S_OK: result += QByteArrayLiteral("S_OK"); break; case S_FALSE: result += QByteArrayLiteral("S_FALSE"); break; case E_UNEXPECTED: result += QByteArrayLiteral("E_UNEXPECTED"); break; case E_ACCESSDENIED: result += QByteArrayLiteral("E_ACCESSDENIED"); break; case CO_E_ALREADYINITIALIZED: result += QByteArrayLiteral("CO_E_ALREADYINITIALIZED"); break; case CO_E_NOTINITIALIZED: result += QByteArrayLiteral("CO_E_NOTINITIALIZED"); break; case RPC_E_CHANGED_MODE: result += QByteArrayLiteral("RPC_E_CHANGED_MODE"); break; case OLE_E_WRONGCOMPOBJ: result += QByteArrayLiteral("OLE_E_WRONGCOMPOBJ"); break; case CO_E_NOT_SUPPORTED: result += QByteArrayLiteral("CO_E_NOT_SUPPORTED"); break; case E_NOTIMPL: result += QByteArrayLiteral("E_NOTIMPL"); break; case E_INVALIDARG: result += QByteArrayLiteral("E_INVALIDARG"); break; case E_NOINTERFACE: result += QByteArrayLiteral("E_NOINTERFACE"); break; case E_POINTER: result += QByteArrayLiteral("E_POINTER"); break; case E_HANDLE: result += QByteArrayLiteral("E_HANDLE"); break; case E_ABORT: result += QByteArrayLiteral("E_ABORT"); break; case E_FAIL: result += QByteArrayLiteral("E_FAIL"); break; case RPC_E_WRONG_THREAD: result += QByteArrayLiteral("RPC_E_WRONG_THREAD"); break; case RPC_E_THREAD_NOT_INIT: result += QByteArrayLiteral("RPC_E_THREAD_NOT_INIT"); break; default: break; } _com_error error(hr); result += QByteArrayLiteral(" ("); result += errorMessageFromComError(error).toUtf8(); result += ')'; return result; } void QWindowsContext::forceNcCalcSize(HWND hwnd) { // Force WM_NCCALCSIZE to adjust margin SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER); } bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void *out, unsigned dpi) { const BOOL result = dpi != 0 ? SystemParametersInfoForDpi(action, param, out, 0, dpi) : SystemParametersInfo(action, param, out, 0); return result == TRUE; } bool QWindowsContext::systemParametersInfoForScreen(unsigned action, unsigned param, void *out, const QPlatformScreen *screen) { return systemParametersInfo(action, param, out, screen ? unsigned(screen->logicalDpi().first) : 0u); } bool QWindowsContext::systemParametersInfoForWindow(unsigned action, unsigned param, void *out, const QPlatformWindow *win) { return systemParametersInfoForScreen(action, param, out, win ? win->screen() : nullptr); } bool QWindowsContext::nonClientMetrics(NONCLIENTMETRICS *ncm, unsigned dpi) { memset(ncm, 0, sizeof(NONCLIENTMETRICS)); ncm->cbSize = sizeof(NONCLIENTMETRICS); return systemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm->cbSize, ncm, dpi); } bool QWindowsContext::nonClientMetricsForScreen(NONCLIENTMETRICS *ncm, const QPlatformScreen *screen) { const int dpi = screen ? qRound(screen->logicalDpi().first) : 0; return nonClientMetrics(ncm, unsigned(dpi)); } bool QWindowsContext::nonClientMetricsForWindow(NONCLIENTMETRICS *ncm, const QPlatformWindow *win) { return nonClientMetricsForScreen(ncm, win ? win->screen() : nullptr); } static inline QWindowsInputContext *windowsInputContext() { return qobject_cast(QWindowsIntegration::instance()->inputContext()); } bool QWindowsContext::shouldHaveNonClientDpiScaling(const QWindow *window) { // DPI aware V2 processes always have NonClientDpiScaling enabled. if (QWindowsContextPrivate::m_v2DpiAware) return true; return window->isTopLevel() && !window->property(QWindowsWindow::embeddedNativeParentHandleProperty).isValid() #if QT_CONFIG(opengl) // /QTBUG-62901, EnableNonClientDpiScaling has problems with GL && (window->surfaceType() != QSurface::OpenGLSurface || QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL) #endif ; } static inline bool isInputMessage(UINT m) { switch (m) { case WM_IME_STARTCOMPOSITION: case WM_IME_ENDCOMPOSITION: case WM_IME_COMPOSITION: case WM_INPUT: case WM_TOUCH: case WM_MOUSEHOVER: case WM_MOUSELEAVE: case WM_NCMOUSEHOVER: case WM_NCMOUSELEAVE: case WM_SIZING: case WM_MOVING: case WM_SYSCOMMAND: case WM_COMMAND: case WM_DWMNCRENDERINGCHANGED: case WM_PAINT: return true; default: break; } return (m >= WM_MOUSEFIRST && m <= WM_MOUSELAST) || (m >= WM_NCMOUSEMOVE && m <= WM_NCXBUTTONDBLCLK) || (m >= WM_KEYFIRST && m <= WM_KEYLAST); } /*! \brief Main windows procedure registered for windows. \sa QWindowsGuiEventDispatcher */ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, QtWindows::WindowsEventType et, WPARAM wParam, LPARAM lParam, LRESULT *result, QWindowsWindow **platformWindowPtr) { *result = 0; MSG msg; msg.hwnd = hwnd; // re-create MSG structure msg.message = message; // time and pt fields ignored msg.wParam = wParam; msg.lParam = lParam; msg.pt.x = msg.pt.y = 0; if (et != QtWindows::CursorEvent && (et & (QtWindows::MouseEventFlag | QtWindows::NonClientEventFlag))) { msg.pt.x = GET_X_LPARAM(lParam); msg.pt.y = GET_Y_LPARAM(lParam); // For non-client-area messages, these are screen coordinates (as expected // in the MSG structure), otherwise they are client coordinates. if (!(et & QtWindows::NonClientEventFlag)) { clientToScreen(msg.hwnd, &msg.pt); } } else { GetCursorPos(&msg.pt); } QWindowsWindow *platformWindow = findPlatformWindow(hwnd); *platformWindowPtr = platformWindow; // Run the native event filters. QTBUG-67095: Exclude input messages which are sent // by QEventDispatcherWin32::processEvents() if (!isInputMessage(msg.message) && filterNativeEvent(&msg, result)) return true; if (platformWindow && filterNativeEvent(platformWindow->window(), &msg, result)) return true; if (et & QtWindows::InputMethodEventFlag) { QWindowsInputContext *windowsInputContext = ::windowsInputContext(); // Disable IME assuming this is a special implementation hooking into keyboard input. // "Real" IME implementations should use a native event filter intercepting IME events. if (!windowsInputContext) { QWindowsInputContext::setWindowsImeEnabled(platformWindow, false); return false; } switch (et) { case QtWindows::InputMethodStartCompositionEvent: return windowsInputContext->startComposition(hwnd); case QtWindows::InputMethodCompositionEvent: return windowsInputContext->composition(hwnd, lParam); case QtWindows::InputMethodEndCompositionEvent: return windowsInputContext->endComposition(hwnd); case QtWindows::InputMethodRequest: return windowsInputContext->handleIME_Request(wParam, lParam, result); default: break; } } // InputMethodEventFlag switch (et) { case QtWindows::GestureEvent: if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer)) return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateGestureEvent(platformWindow->window(), hwnd, et, msg, result); break; case QtWindows::InputMethodOpenCandidateWindowEvent: case QtWindows::InputMethodCloseCandidateWindowEvent: // TODO: Release/regrab mouse if a popup has mouse grab. return false; case QtWindows::DestroyEvent: if (platformWindow && !platformWindow->testFlag(QWindowsWindow::WithinDestroy)) { qWarning() << "External WM_DESTROY received for " << platformWindow->window() << ", parent: " << platformWindow->window()->parent() << ", transient parent: " << platformWindow->window()->transientParent(); } return false; case QtWindows::ClipboardEvent: return false; case QtWindows::CursorEvent: // Sent to windows that do not have capture (see QTBUG-58590). if (QWindowsCursor::hasOverrideCursor()) { QWindowsCursor::enforceOverrideCursor(); return true; } break; case QtWindows::UnknownEvent: return false; case QtWindows::AccessibleObjectFromWindowRequest: #if QT_CONFIG(accessibility) return QWindowsUiaAccessibility::handleWmGetObject(hwnd, wParam, lParam, result); #else return false; #endif case QtWindows::DisplayChangedEvent: if (QWindowsTheme *t = QWindowsTheme::instance()) t->displayChanged(); QWindowsWindow::displayChanged(); d->m_screenManager.handleScreenChanges(); return false; case QtWindows::SettingChangedEvent: { QWindowsWindow::settingsChanged(); const bool darkMode = QWindowsTheme::queryDarkMode(); if (darkMode != QWindowsContextPrivate::m_darkMode) { QWindowsContextPrivate::m_darkMode = darkMode; auto integration = QWindowsIntegration::instance(); if (integration->darkModeHandling().testFlag(QWindowsApplication::DarkModeWindowFrames)) { for (QWindowsWindow *w : d->m_windows) w->setDarkBorder(QWindowsContextPrivate::m_darkMode); } if (integration->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle)) { QWindowsTheme::instance()->refresh(); for (QWindowsWindow *w : d->m_windows) QWindowSystemInterface::handleThemeChange(w->window()); } } return d->m_screenManager.handleScreenChanges(); } default: break; } // Before CreateWindowEx() returns, some events are sent, // for example WM_GETMINMAXINFO asking for size constraints for top levels. // Pass on to current creation context if (!platformWindow && !d->m_creationContext.isNull()) { switch (et) { case QtWindows::QuerySizeHints: d->m_creationContext->applyToMinMaxInfo(reinterpret_cast(lParam)); return true; case QtWindows::ResizeEvent: d->m_creationContext->obtainedSize = QSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); return true; case QtWindows::MoveEvent: d->m_creationContext->obtainedPos = QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); return true; case QtWindows::NonClientCreate: if (shouldHaveNonClientDpiScaling(d->m_creationContext->window) && // DPI aware V2 processes always have NonClientDpiScaling enabled // and there is no need to make an API call to manually enable. !QWindowsContextPrivate::m_v2DpiAware) { enableNonClientDpiScaling(msg.hwnd); } return false; case QtWindows::CalculateSize: return QWindowsGeometryHint::handleCalculateSize(d->m_creationContext->customMargins, msg, result); case QtWindows::GeometryChangingEvent: return QWindowsWindow::handleGeometryChangingMessage(&msg, d->m_creationContext->window, d->m_creationContext->margins + d->m_creationContext->customMargins); default: break; } } if (platformWindow) { // Suppress events sent during DestroyWindow() for native children. if (platformWindow->testFlag(QWindowsWindow::WithinDestroy)) return false; if (QWindowsContext::verbose > 1) qCDebug(lcQpaEvents) << "Event window: " << platformWindow->window(); } else { qWarning("%s: No Qt Window found for event 0x%x (%s), hwnd=0x%p.", __FUNCTION__, message, QWindowsGuiEventDispatcher::windowsMessageName(message), hwnd); return false; } switch (et) { case QtWindows::DeviceChangeEvent: if (d->m_systemInfo & QWindowsContext::SI_SupportsTouch) break; // See if there are any touch devices added if (wParam == DBT_DEVNODES_CHANGED) initTouch(); break; case QtWindows::KeyboardLayoutChangeEvent: if (QWindowsInputContext *wic = windowsInputContext()) wic->handleInputLanguageChanged(wParam, lParam); Q_FALLTHROUGH(); case QtWindows::KeyDownEvent: case QtWindows::KeyEvent: case QtWindows::InputMethodKeyEvent: case QtWindows::InputMethodKeyDownEvent: case QtWindows::AppCommandEvent: return sessionManagerInteractionBlocked() || d->m_keyMapper.translateKeyEvent(platformWindow->window(), hwnd, msg, result); case QtWindows::MenuAboutToShowEvent: if (sessionManagerInteractionBlocked()) return true; if (QWindowsPopupMenu::notifyAboutToShow(reinterpret_cast(wParam))) return true; if (platformWindow == nullptr || platformWindow->menuBar() == nullptr) return false; return platformWindow->menuBar()->notifyAboutToShow(reinterpret_cast(wParam)); case QtWindows::MenuCommandEvent: if (sessionManagerInteractionBlocked()) return true; if (QWindowsPopupMenu::notifyTriggered(LOWORD(wParam))) return true; if (platformWindow == nullptr || platformWindow->menuBar() == nullptr) return false; return platformWindow->menuBar()->notifyTriggered(LOWORD(wParam)); case QtWindows::MoveEvent: platformWindow->handleMoved(); return true; case QtWindows::ResizeEvent: platformWindow->handleResized(static_cast(wParam)); return true; case QtWindows::QuerySizeHints: platformWindow->getSizeHints(reinterpret_cast(lParam)); return true;// maybe available on some SDKs revisit WM_NCCALCSIZE case QtWindows::CalculateSize: return QWindowsGeometryHint::handleCalculateSize(platformWindow->customMargins(), msg, result); case QtWindows::NonClientHitTest: return platformWindow->handleNonClientHitTest(QPoint(msg.pt.x, msg.pt.y), result); case QtWindows::GeometryChangingEvent: return platformWindow->handleGeometryChanging(&msg); case QtWindows::ExposeEvent: return platformWindow->handleWmPaint(hwnd, message, wParam, lParam, result); case QtWindows::NonClientMouseEvent: if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer) && platformWindow->frameStrutEventsEnabled()) return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result); else return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result); case QtWindows::NonClientPointerEvent: if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer) && platformWindow->frameStrutEventsEnabled()) return sessionManagerInteractionBlocked() || d->m_pointerHandler.translatePointerEvent(platformWindow->window(), hwnd, et, msg, result); break; case QtWindows::EnterSizeMoveEvent: platformWindow->setFlag(QWindowsWindow::ResizeMoveActive); return true; case QtWindows::ExitSizeMoveEvent: platformWindow->clearFlag(QWindowsWindow::ResizeMoveActive); platformWindow->checkForScreenChanged(); handleExitSizeMove(platformWindow->window()); return true; case QtWindows::ScrollEvent: if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer)) return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateScrollEvent(platformWindow->window(), hwnd, msg, result); break; case QtWindows::MouseWheelEvent: case QtWindows::MouseEvent: case QtWindows::LeaveEvent: { QWindow *window = platformWindow->window(); while (window && (window->flags() & Qt::WindowTransparentForInput)) window = window->parent(); if (!window) return false; if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(window, hwnd, et, msg, result); else return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateMouseEvent(window, hwnd, et, msg, result); } break; case QtWindows::TouchEvent: if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer)) return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateTouchEvent(platformWindow->window(), hwnd, et, msg, result); break; case QtWindows::PointerEvent: if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) return sessionManagerInteractionBlocked() || d->m_pointerHandler.translatePointerEvent(platformWindow->window(), hwnd, et, msg, result); break; case QtWindows::FocusInEvent: // see QWindowsWindow::requestActivateWindow(). case QtWindows::FocusOutEvent: handleFocusEvent(et, platformWindow); return true; case QtWindows::ShowEventOnParentRestoring: // QTBUG-40696, prevent Windows from re-showing hidden transient children (dialogs). if (!platformWindow->window()->isVisible()) { *result = 0; return true; } break; case QtWindows::HideEvent: platformWindow->handleHidden(); return false;// Indicate transient children should be hidden by windows (SW_PARENTCLOSING) case QtWindows::CloseEvent: QWindowSystemInterface::handleCloseEvent(platformWindow->window()); return true; case QtWindows::ThemeChanged: { // Switch from Aero to Classic changes margins. if (QWindowsTheme *theme = QWindowsTheme::instance()) theme->windowsThemeChanged(platformWindow->window()); return true; } case QtWindows::CompositionSettingsChanged: platformWindow->handleCompositionSettingsChanged(); return true; case QtWindows::ActivateWindowEvent: if (platformWindow->window()->flags() & Qt::WindowDoesNotAcceptFocus) { *result = LRESULT(MA_NOACTIVATE); return true; } #if QT_CONFIG(tabletevent) if (!d->m_tabletSupport.isNull()) d->m_tabletSupport->notifyActivate(); #endif // QT_CONFIG(tabletevent) if (platformWindow->testFlag(QWindowsWindow::BlockedByModal)) if (const QWindow *modalWindow = QGuiApplication::modalWindow()) { QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(modalWindow); Q_ASSERT(platformWindow); platformWindow->alertWindow(); } break; case QtWindows::MouseActivateWindowEvent: case QtWindows::PointerActivateWindowEvent: if (platformWindow->window()->flags() & Qt::WindowDoesNotAcceptFocus) { *result = LRESULT(MA_NOACTIVATE); return true; } break; #ifndef QT_NO_CONTEXTMENU case QtWindows::ContextMenu: return handleContextMenuEvent(platformWindow->window(), msg); #endif case QtWindows::WhatsThisEvent: { #ifndef QT_NO_WHATSTHIS QWindowSystemInterface::handleEnterWhatsThisEvent(); return true; #endif } break; case QtWindows::DpiScaledSizeEvent: platformWindow->handleDpiScaledSize(wParam, lParam, result); return true; case QtWindows::DpiChangedEvent: platformWindow->handleDpiChanged(hwnd, wParam, lParam); return true; #if QT_CONFIG(sessionmanager) case QtWindows::QueryEndSessionApplicationEvent: { QWindowsSessionManager *sessionManager = platformSessionManager(); if (sessionManager->isActive()) { // bogus message from windows *result = sessionManager->wasCanceled() ? 0 : 1; return true; } sessionManager->setActive(true); sessionManager->blocksInteraction(); sessionManager->clearCancellation(); auto *qGuiAppPriv = static_cast(QObjectPrivate::get(qApp)); qGuiAppPriv->commitData(); if (lParam & ENDSESSION_LOGOFF) fflush(nullptr); *result = sessionManager->wasCanceled() ? 0 : 1; return true; } case QtWindows::EndSessionApplicationEvent: { QWindowsSessionManager *sessionManager = platformSessionManager(); sessionManager->setActive(false); sessionManager->allowsInteraction(); const bool endsession = wParam != 0; // we receive the message for each toplevel window included internal hidden ones, // but the aboutToQuit signal should be emitted only once. auto *qGuiAppPriv = static_cast(QObjectPrivate::get(qApp)); if (endsession && !qGuiAppPriv->aboutToQuitEmitted) { qGuiAppPriv->aboutToQuitEmitted = true; int index = QGuiApplication::staticMetaObject.indexOfSignal("aboutToQuit()"); qApp->qt_metacall(QMetaObject::InvokeMetaMethod, index, nullptr); // since the process will be killed immediately quit() has no real effect QGuiApplication::quit(); } return true; } #endif // !defined(QT_NO_SESSIONMANAGER) default: break; } return false; } /* Compress activation events. If the next focus window is already known * at the time the current one receives focus-out, pass that to * QWindowSystemInterface instead of sending 0 and ignore its consecutive * focus-in event. * This helps applications that do handling in focus-out events. */ void QWindowsContext::handleFocusEvent(QtWindows::WindowsEventType et, QWindowsWindow *platformWindow) { QWindow *nextActiveWindow = nullptr; if (et == QtWindows::FocusInEvent) { QWindow *topWindow = QWindowsWindow::topLevelOf(platformWindow->window()); QWindow *modalWindow = nullptr; if (QGuiApplicationPrivate::instance()->isWindowBlocked(topWindow, &modalWindow) && topWindow != modalWindow) { modalWindow->requestActivate(); return; } // QTBUG-32867: Invoking WinAPI SetParent() can cause focus-in for the // window which is not desired for native child widgets. if (platformWindow->testFlag(QWindowsWindow::WithinSetParent)) { QWindow *currentFocusWindow = QGuiApplication::focusWindow(); if (currentFocusWindow && currentFocusWindow != platformWindow->window()) { currentFocusWindow->requestActivate(); return; } } nextActiveWindow = platformWindow->window(); } else { // Focus out: Is the next window known and different // from the receiving the focus out. if (const HWND nextActiveHwnd = GetFocus()) if (QWindowsWindow *nextActivePlatformWindow = findClosestPlatformWindow(nextActiveHwnd)) if (nextActivePlatformWindow != platformWindow) nextActiveWindow = nextActivePlatformWindow->window(); } if (nextActiveWindow != d->m_lastActiveWindow) { d->m_lastActiveWindow = nextActiveWindow; QWindowSystemInterface::handleWindowActivated(nextActiveWindow, Qt::ActiveWindowFocusReason); } } #ifndef QT_NO_CONTEXTMENU bool QWindowsContext::handleContextMenuEvent(QWindow *window, const MSG &msg) { bool mouseTriggered = false; QPoint globalPos; QPoint pos; if (msg.lParam != int(0xffffffff)) { mouseTriggered = true; globalPos.setX(msg.pt.x); globalPos.setY(msg.pt.y); pos = QWindowsGeometryHint::mapFromGlobal(msg.hwnd, globalPos); RECT clientRect; if (GetClientRect(msg.hwnd, &clientRect)) { if (pos.x() < clientRect.left || pos.x() >= clientRect.right || pos.y() < clientRect.top || pos.y() >= clientRect.bottom) { // This is the case that user has right clicked in the window's caption, // We should call DefWindowProc() to display a default shortcut menu // instead of sending a Qt window system event. return false; } } } QWindowSystemInterface::handleContextMenuEvent(window, mouseTriggered, pos, globalPos, QWindowsKeyMapper::queryKeyboardModifiers()); return true; } #endif void QWindowsContext::handleExitSizeMove(QWindow *window) { // Windows can be moved/resized by: // 1) User moving a window by dragging the title bar: Causes a sequence // of WM_NCLBUTTONDOWN, WM_NCMOUSEMOVE but no WM_NCLBUTTONUP, // leaving the left mouse button 'pressed' // 2) User choosing Resize/Move from System menu and using mouse/cursor keys: // No mouse events are received // 3) Programmatically via QSizeGrip calling QPlatformWindow::startSystemResize/Move(): // Mouse is left in pressed state after press on size grip (inside window), // no further mouse events are received // For cases 1,3, intercept WM_EXITSIZEMOVE to sync the buttons. const Qt::MouseButtons currentButtons = QWindowsMouseHandler::queryMouseButtons(); const Qt::MouseButtons appButtons = QGuiApplication::mouseButtons(); if (currentButtons == appButtons) return; const Qt::KeyboardModifiers keyboardModifiers = QWindowsKeyMapper::queryKeyboardModifiers(); const QPoint globalPos = QWindowsCursor::mousePosition(); const QPlatformWindow *platWin = window->handle(); const QPoint localPos = platWin->mapFromGlobal(globalPos); const QEvent::Type type = platWin->geometry().contains(globalPos) ? QEvent::MouseButtonRelease : QEvent::NonClientAreaMouseButtonRelease; for (Qt::MouseButton button : {Qt::LeftButton, Qt::RightButton, Qt::MiddleButton}) { if (appButtons.testFlag(button) && !currentButtons.testFlag(button)) { QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos, currentButtons, button, type, keyboardModifiers); } } if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) d->m_pointerHandler.clearEvents(); else d->m_mouseHandler.clearEvents(); } bool QWindowsContext::asyncExpose() const { return d->m_asyncExpose; } void QWindowsContext::setAsyncExpose(bool value) { d->m_asyncExpose = value; } DWORD QWindowsContext::readAdvancedExplorerSettings(const wchar_t *subKey, DWORD defaultValue) { const auto value = QWinRegistryKey(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced)") .dwordValue(subKey); return value.second ? value.first : defaultValue; } static inline bool isEmptyRect(const RECT &rect) { return rect.right - rect.left == 0 && rect.bottom - rect.top == 0; } static inline QMargins marginsFromRects(const RECT &frame, const RECT &client) { return QMargins(client.left - frame.left, client.top - frame.top, frame.right - client.right, frame.bottom - client.bottom); } static RECT rectFromNcCalcSize(UINT message, WPARAM wParam, LPARAM lParam, int n) { RECT result = {0, 0, 0, 0}; if (message == WM_NCCALCSIZE && wParam) result = reinterpret_cast(lParam)->rgrc[n]; return result; } static inline bool isMinimized(HWND hwnd) { WINDOWPLACEMENT windowPlacement; windowPlacement.length = sizeof(WINDOWPLACEMENT); return GetWindowPlacement(hwnd, &windowPlacement) && windowPlacement.showCmd == SW_SHOWMINIMIZED; } static inline bool isTopLevel(HWND hwnd) { return (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CHILD) == 0; } /*! \brief Windows functions for actual windows. There is another one for timers, sockets, etc in QEventDispatcherWin32. */ extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { LRESULT result; const QtWindows::WindowsEventType et = windowsEventType(message, wParam, lParam); QWindowsWindow *platformWindow = nullptr; const RECT ncCalcSizeFrame = rectFromNcCalcSize(message, wParam, lParam, 0); const bool handled = QWindowsContext::instance()->windowsProc(hwnd, message, et, wParam, lParam, &result, &platformWindow); if (QWindowsContext::verbose > 1 && lcQpaEvents().isDebugEnabled()) { if (const char *eventName = QWindowsGuiEventDispatcher::windowsMessageName(message)) { qCDebug(lcQpaEvents).nospace() << "EVENT: hwd=" << hwnd << ' ' << eventName << " msg=0x" << Qt::hex << message << " et=0x" << et << Qt::dec << " wp=" << int(wParam) << " at " << GET_X_LPARAM(lParam) << ',' << GET_Y_LPARAM(lParam) << " handled=" << handled; } } if (!handled) result = DefWindowProc(hwnd, message, wParam, lParam); // Capture WM_NCCALCSIZE on top level windows and obtain the window margins by // subtracting the rectangles before and after processing. This will correctly // capture client code overriding the message and allow for per-monitor margins // for High DPI (QTBUG-53255, QTBUG-40578). if (message == WM_NCCALCSIZE && !isEmptyRect(ncCalcSizeFrame) && isTopLevel(hwnd) && !isMinimized(hwnd)) { const QMargins margins = marginsFromRects(ncCalcSizeFrame, rectFromNcCalcSize(message, wParam, lParam, 0)); if (margins.left() >= 0) { if (platformWindow) { qCDebug(lcQpaWindows) << __FUNCTION__ << "WM_NCCALCSIZE for" << hwnd << margins; platformWindow->setFullFrameMargins(margins); } else { const QSharedPointer ctx = QWindowsContext::instance()->windowCreationContext(); if (!ctx.isNull()) ctx->margins = margins; } } } return result; } static inline QByteArray nativeEventType() { return QByteArrayLiteral("windows_generic_MSG"); } // Send to QAbstractEventDispatcher bool QWindowsContext::filterNativeEvent(MSG *msg, LRESULT *result) { QAbstractEventDispatcher *dispatcher = QAbstractEventDispatcher::instance(); qintptr filterResult = 0; if (dispatcher && dispatcher->filterNativeEvent(nativeEventType(), msg, &filterResult)) { *result = LRESULT(filterResult); return true; } return false; } // Send to QWindowSystemInterface bool QWindowsContext::filterNativeEvent(QWindow *window, MSG *msg, LRESULT *result) { qintptr filterResult = 0; if (QWindowSystemInterface::handleNativeEvent(window, nativeEventType(), msg, &filterResult)) { *result = LRESULT(filterResult); return true; } return false; } QT_END_NAMESPACE