1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch>
4 ** Copyright (C) 2016 The Qt Company Ltd.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the plugins of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #include "qwindowscontext.h"
42 #include "qwindowsintegration.h"
43 #include "qwindowswindow.h"
44 #include "qwindowskeymapper.h"
45 #include "qwindowsnativeinterface.h"
46 #include "qwindowsmousehandler.h"
47 #include "qwindowspointerhandler.h"
48 #include "qtwindowsglobal.h"
49 #include "qwindowsmenu.h"
50 #include "qwindowsmime.h"
51 #include "qwindowsinputcontext.h"
52 #if QT_CONFIG(tabletevent)
53 #  include "qwindowstabletsupport.h"
54 #endif
55 #include "qwindowstheme.h"
56 #include <private/qguiapplication_p.h>
57 #if QT_CONFIG(accessibility)
58 #  include "uiautomation/qwindowsuiaaccessibility.h"
59 #endif
60 #if QT_CONFIG(sessionmanager)
61 # include <private/qsessionmanager_p.h>
62 # include "qwindowssessionmanager.h"
63 #endif
64 #include "qwindowsscreen.h"
65 #include "qwindowstheme.h"
66 
67 #include <QtGui/qwindow.h>
68 #include <qpa/qwindowsysteminterface.h>
69 #include <qpa/qwindowsysteminterface_p.h>
70 #include <qpa/qplatformnativeinterface.h>
71 #include <QtGui/qguiapplication.h>
72 #include <QtGui/qopenglcontext.h>
73 
74 #include <QtCore/qset.h>
75 #include <QtCore/qhash.h>
76 #include <QtCore/qlibraryinfo.h>
77 #include <QtCore/qstringlist.h>
78 #include <QtCore/qdebug.h>
79 #include <QtCore/qoperatingsystemversion.h>
80 #include <QtCore/qsysinfo.h>
81 #include <QtCore/qscopedpointer.h>
82 #include <QtCore/quuid.h>
83 #include <QtCore/private/qsystemlibrary_p.h>
84 #include <QtCore/private/qwinregistry_p.h>
85 
86 #include <QtEventDispatcherSupport/private/qwindowsguieventdispatcher_p.h>
87 
88 #include <stdlib.h>
89 #include <stdio.h>
90 #include <windowsx.h>
91 #include <comdef.h>
92 #include <dbt.h>
93 #include <wtsapi32.h>
94 
95 QT_BEGIN_NAMESPACE
96 
97 Q_LOGGING_CATEGORY(lcQpaWindows, "qt.qpa.windows")
98 Q_LOGGING_CATEGORY(lcQpaEvents, "qt.qpa.events")
99 Q_LOGGING_CATEGORY(lcQpaGl, "qt.qpa.gl")
100 Q_LOGGING_CATEGORY(lcQpaMime, "qt.qpa.mime")
101 Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods")
102 Q_LOGGING_CATEGORY(lcQpaDialogs, "qt.qpa.dialogs")
103 Q_LOGGING_CATEGORY(lcQpaMenus, "qt.qpa.menus")
104 Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
105 Q_LOGGING_CATEGORY(lcQpaAccessibility, "qt.qpa.accessibility")
106 Q_LOGGING_CATEGORY(lcQpaUiAutomation, "qt.qpa.uiautomation")
107 Q_LOGGING_CATEGORY(lcQpaTrayIcon, "qt.qpa.trayicon")
108 
109 int QWindowsContext::verbose = 0;
110 
111 #if !defined(LANG_SYRIAC)
112 #    define LANG_SYRIAC 0x5a
113 #endif
114 
useRTL_Extensions()115 static inline bool useRTL_Extensions()
116 {
117     // Since the IsValidLanguageGroup/IsValidLocale functions always return true on
118     // Vista, check the Keyboard Layouts for enabling RTL.
119     if (const int nLayouts = GetKeyboardLayoutList(0, nullptr)) {
120         QScopedArrayPointer<HKL> lpList(new HKL[nLayouts]);
121         GetKeyboardLayoutList(nLayouts, lpList.data());
122         for (int i = 0; i < nLayouts; ++i) {
123             switch (PRIMARYLANGID((quintptr)lpList[i])) {
124             case LANG_ARABIC:
125             case LANG_HEBREW:
126             case LANG_FARSI:
127             case LANG_SYRIAC:
128                 return true;
129             default:
130                 break;
131             }
132         }
133     }
134     return false;
135 }
136 
137 #if QT_CONFIG(sessionmanager)
platformSessionManager()138 static inline QWindowsSessionManager *platformSessionManager()
139 {
140     auto *guiPrivate = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(qApp));
141     auto *managerPrivate = static_cast<QSessionManagerPrivate*>(QObjectPrivate::get(guiPrivate->session_manager));
142     return static_cast<QWindowsSessionManager *>(managerPrivate->platformSessionManager);
143 }
144 
sessionManagerInteractionBlocked()145 static inline bool sessionManagerInteractionBlocked()
146 {
147     return platformSessionManager()->isInteractionBlocked();
148 }
149 #else // QT_CONFIG(sessionmanager)
sessionManagerInteractionBlocked()150 static inline bool sessionManagerInteractionBlocked() { return false; }
151 #endif
152 
windowDpiAwareness(HWND hwnd)153 static inline int windowDpiAwareness(HWND hwnd)
154 {
155     return QWindowsContext::user32dll.getWindowDpiAwarenessContext && QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext
156         ? QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext(QWindowsContext::user32dll.getWindowDpiAwarenessContext(hwnd))
157         : -1;
158 }
159 
160 // Note: This only works within WM_NCCREATE
enableNonClientDpiScaling(HWND hwnd)161 static bool enableNonClientDpiScaling(HWND hwnd)
162 {
163     bool result = false;
164     if (QWindowsContext::user32dll.enableNonClientDpiScaling && windowDpiAwareness(hwnd) == 2) {
165         result = QWindowsContext::user32dll.enableNonClientDpiScaling(hwnd) != FALSE;
166         if (!result) {
167             const DWORD errorCode = GetLastError();
168             qErrnoWarning(int(errorCode), "EnableNonClientDpiScaling() failed for HWND %p (%lu)",
169                           hwnd, errorCode);
170         }
171     }
172     return result;
173 }
174 
175 /*!
176     \class QWindowsUser32DLL
177     \brief Struct that contains dynamically resolved symbols of User32.dll.
178 
179     The stub libraries shipped with the MinGW compiler miss some of the
180     functions. They need to be retrieved dynamically.
181 
182     In addition, touch-related functions are available only from Windows onwards.
183     These need to resolved dynamically for Q_CC_MSVC as well.
184 
185     \sa QWindowsShell32DLL
186 
187     \internal
188 */
189 
init()190 void QWindowsUser32DLL::init()
191 {
192     QSystemLibrary library(QStringLiteral("user32"));
193     setProcessDPIAware = (SetProcessDPIAware)library.resolve("SetProcessDPIAware");
194 
195     addClipboardFormatListener = (AddClipboardFormatListener)library.resolve("AddClipboardFormatListener");
196     removeClipboardFormatListener = (RemoveClipboardFormatListener)library.resolve("RemoveClipboardFormatListener");
197 
198     getDisplayAutoRotationPreferences = (GetDisplayAutoRotationPreferences)library.resolve("GetDisplayAutoRotationPreferences");
199     setDisplayAutoRotationPreferences = (SetDisplayAutoRotationPreferences)library.resolve("SetDisplayAutoRotationPreferences");
200 
201     if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8) {
202         enableMouseInPointer = (EnableMouseInPointer)library.resolve("EnableMouseInPointer");
203         getPointerType = (GetPointerType)library.resolve("GetPointerType");
204         getPointerInfo = (GetPointerInfo)library.resolve("GetPointerInfo");
205         getPointerDeviceRects = (GetPointerDeviceRects)library.resolve("GetPointerDeviceRects");
206         getPointerTouchInfo = (GetPointerTouchInfo)library.resolve("GetPointerTouchInfo");
207         getPointerFrameTouchInfo = (GetPointerFrameTouchInfo)library.resolve("GetPointerFrameTouchInfo");
208         getPointerFrameTouchInfoHistory = (GetPointerFrameTouchInfoHistory)library.resolve("GetPointerFrameTouchInfoHistory");
209         getPointerPenInfo = (GetPointerPenInfo)library.resolve("GetPointerPenInfo");
210         getPointerPenInfoHistory = (GetPointerPenInfoHistory)library.resolve("GetPointerPenInfoHistory");
211         skipPointerFrameMessages = (SkipPointerFrameMessages)library.resolve("SkipPointerFrameMessages");
212     }
213 
214     if (QOperatingSystemVersion::current()
215         >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 14393)) {
216         adjustWindowRectExForDpi = (AdjustWindowRectExForDpi)library.resolve("AdjustWindowRectExForDpi");
217         enableNonClientDpiScaling = (EnableNonClientDpiScaling)library.resolve("EnableNonClientDpiScaling");
218         getWindowDpiAwarenessContext = (GetWindowDpiAwarenessContext)library.resolve("GetWindowDpiAwarenessContext");
219         getAwarenessFromDpiAwarenessContext = (GetAwarenessFromDpiAwarenessContext)library.resolve("GetAwarenessFromDpiAwarenessContext");
220         systemParametersInfoForDpi = (SystemParametersInfoForDpi)library.resolve("SystemParametersInfoForDpi");
221     }
222 }
223 
supportsPointerApi()224 bool QWindowsUser32DLL::supportsPointerApi()
225 {
226     return enableMouseInPointer && getPointerType && getPointerInfo && getPointerDeviceRects
227             && getPointerTouchInfo && getPointerFrameTouchInfo && getPointerFrameTouchInfoHistory
228             && getPointerPenInfo && getPointerPenInfoHistory && skipPointerFrameMessages;
229 }
230 
init()231 void QWindowsShcoreDLL::init()
232 {
233     if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8_1)
234         return;
235     QSystemLibrary library(QStringLiteral("SHCore"));
236     getProcessDpiAwareness = (GetProcessDpiAwareness)library.resolve("GetProcessDpiAwareness");
237     setProcessDpiAwareness = (SetProcessDpiAwareness)library.resolve("SetProcessDpiAwareness");
238     getDpiForMonitor = (GetDpiForMonitor)library.resolve("GetDpiForMonitor");
239 }
240 
241 QWindowsUser32DLL QWindowsContext::user32dll;
242 QWindowsShcoreDLL QWindowsContext::shcoredll;
243 
244 QWindowsContext *QWindowsContext::m_instance = nullptr;
245 
246 /*!
247     \class QWindowsContext
248     \brief Singleton container for all relevant information.
249 
250     Holds state information formerly stored in \c qapplication_win.cpp.
251 
252     \internal
253 */
254 
255 typedef QHash<HWND, QWindowsWindow *> HandleBaseWindowHash;
256 
257 struct QWindowsContextPrivate {
258     QWindowsContextPrivate();
259 
260     unsigned m_systemInfo = 0;
261     QSet<QString> m_registeredWindowClassNames;
262     HandleBaseWindowHash m_windows;
263     HDC m_displayContext = nullptr;
264     int m_defaultDPI = 96;
265     QWindowsKeyMapper m_keyMapper;
266     QWindowsMouseHandler m_mouseHandler;
267     QWindowsPointerHandler m_pointerHandler;
268     QWindowsMimeConverter m_mimeConverter;
269     QWindowsScreenManager m_screenManager;
270     QSharedPointer<QWindowCreationContext> m_creationContext;
271 #if QT_CONFIG(tabletevent)
272     QScopedPointer<QWindowsTabletSupport> m_tabletSupport;
273 #endif
274     const HRESULT m_oleInitializeResult;
275     QWindow *m_lastActiveWindow = nullptr;
276     bool m_asyncExpose = false;
277     HPOWERNOTIFY m_powerNotification = nullptr;
278     HWND m_powerDummyWindow = nullptr;
279     static bool m_darkMode;
280 };
281 
282 bool QWindowsContextPrivate::m_darkMode = false;
283 
QWindowsContextPrivate()284 QWindowsContextPrivate::QWindowsContextPrivate()
285     : m_oleInitializeResult(OleInitialize(nullptr))
286 {
287     QWindowsContext::user32dll.init();
288     QWindowsContext::shcoredll.init();
289 
290     if (m_pointerHandler.touchDevice() || m_mouseHandler.touchDevice())
291         m_systemInfo |= QWindowsContext::SI_SupportsTouch;
292     m_displayContext = GetDC(nullptr);
293     m_defaultDPI = GetDeviceCaps(m_displayContext, LOGPIXELSY);
294     if (useRTL_Extensions()) {
295         m_systemInfo |= QWindowsContext::SI_RTL_Extensions;
296         m_keyMapper.setUseRTLExtensions(true);
297     }
298     m_darkMode = QWindowsTheme::queryDarkMode();
299     if (FAILED(m_oleInitializeResult)) {
300        qWarning() << "QWindowsContext: OleInitialize() failed: "
301            << QWindowsContext::comErrorString(m_oleInitializeResult);
302     }
303 }
304 
QWindowsContext()305 QWindowsContext::QWindowsContext() :
306     d(new QWindowsContextPrivate)
307 {
308 #ifdef Q_CC_MSVC
309 #    pragma warning( disable : 4996 )
310 #endif
311     m_instance = this;
312     // ### FIXME: Remove this once the logging system has other options of configurations.
313     const QByteArray bv = qgetenv("QT_QPA_VERBOSE");
314     if (!bv.isEmpty())
315         QLoggingCategory::setFilterRules(QString::fromLocal8Bit(bv));
316 }
317 
~QWindowsContext()318 QWindowsContext::~QWindowsContext()
319 {
320 #if QT_CONFIG(tabletevent)
321     d->m_tabletSupport.reset(); // Destroy internal window before unregistering classes.
322 #endif
323 
324     if (d->m_powerNotification)
325         UnregisterPowerSettingNotification(d->m_powerNotification);
326 
327     if (d->m_powerDummyWindow)
328         DestroyWindow(d->m_powerDummyWindow);
329 
330     unregisterWindowClasses();
331     if (d->m_oleInitializeResult == S_OK || d->m_oleInitializeResult == S_FALSE)
332         OleUninitialize();
333 
334     d->m_screenManager.clearScreens(); // Order: Potentially calls back to the windows.
335     if (d->m_displayContext)
336         ReleaseDC(nullptr, d->m_displayContext);
337     m_instance = nullptr;
338 }
339 
initTouch()340 bool QWindowsContext::initTouch()
341 {
342     return initTouch(QWindowsIntegration::instance()->options());
343 }
344 
initTouch(unsigned integrationOptions)345 bool QWindowsContext::initTouch(unsigned integrationOptions)
346 {
347     if (d->m_systemInfo & QWindowsContext::SI_SupportsTouch)
348         return true;
349 
350     QTouchDevice *touchDevice = (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) ?
351                 d->m_pointerHandler.ensureTouchDevice() : d->m_mouseHandler.ensureTouchDevice();
352     if (!touchDevice)
353         return false;
354 
355     if (!(integrationOptions & QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch))
356         touchDevice->setCapabilities(touchDevice->capabilities() | QTouchDevice::MouseEmulation);
357 
358     QWindowSystemInterface::registerTouchDevice(touchDevice);
359 
360     d->m_systemInfo |= QWindowsContext::SI_SupportsTouch;
361 
362     // A touch device was plugged while the app is running. Register all windows for touch.
363     if (QGuiApplicationPrivate::is_app_running) {
364         for (QWindowsWindow *w : qAsConst(d->m_windows))
365             w->registerTouchWindow();
366     }
367 
368     return true;
369 }
370 
initTablet(unsigned integrationOptions)371 bool QWindowsContext::initTablet(unsigned integrationOptions)
372 {
373     Q_UNUSED(integrationOptions);
374 #if QT_CONFIG(tabletevent)
375     d->m_tabletSupport.reset(QWindowsTabletSupport::create());
376     return true;
377 #else
378     return false;
379 #endif
380 }
381 
initPointer(unsigned integrationOptions)382 bool QWindowsContext::initPointer(unsigned integrationOptions)
383 {
384     if (integrationOptions & QWindowsIntegration::DontUseWMPointer)
385         return false;
386 
387     if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8)
388         return false;
389 
390     if (!QWindowsContext::user32dll.supportsPointerApi())
391         return false;
392 
393     d->m_systemInfo |= QWindowsContext::SI_SupportsPointer;
394     return true;
395 }
396 
qWindowsPowerWindowProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)397 extern "C" LRESULT QT_WIN_CALLBACK qWindowsPowerWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
398 {
399     if (message != WM_POWERBROADCAST || wParam != PBT_POWERSETTINGCHANGE)
400         return DefWindowProc(hwnd, message, wParam, lParam);
401 
402     static bool initialized = false; // ignore the initial change
403     if (!initialized) {
404         initialized = true;
405         return DefWindowProc(hwnd, message, wParam, lParam);
406     }
407 
408     auto setting = reinterpret_cast<const POWERBROADCAST_SETTING *>(lParam);
409     if (setting) {
410         auto data = reinterpret_cast<const DWORD *>(&setting->Data);
411         if (*data == 1) {
412             // Repaint the windows when returning from sleeping display mode.
413             const auto tlw = QGuiApplication::topLevelWindows();
414             for (auto w : tlw) {
415                 if (w->isVisible() && w->windowState() != Qt::WindowMinimized) {
416                     if (auto tw = QWindowsWindow::windowsWindowOf(w)) {
417                         if (HWND hwnd = tw->handle()) {
418                             InvalidateRect(hwnd, nullptr, false);
419                         }
420                     }
421                 }
422             }
423         }
424     }
425     return DefWindowProc(hwnd, message, wParam, lParam);
426 }
427 
initPowerNotificationHandler()428 bool QWindowsContext::initPowerNotificationHandler()
429 {
430     if (d->m_powerNotification)
431         return false;
432 
433     d->m_powerDummyWindow = createDummyWindow(QStringLiteral("PowerDummyWindow"), L"QtPowerDummyWindow", qWindowsPowerWindowProc);
434     if (!d->m_powerDummyWindow)
435         return false;
436 
437     d->m_powerNotification = RegisterPowerSettingNotification(d->m_powerDummyWindow, &GUID_MONITOR_POWER_ON, DEVICE_NOTIFY_WINDOW_HANDLE);
438     if (!d->m_powerNotification) {
439         DestroyWindow(d->m_powerDummyWindow);
440         d->m_powerDummyWindow = nullptr;
441         return false;
442     }
443     return true;
444 }
445 
setTabletAbsoluteRange(int a)446 void QWindowsContext::setTabletAbsoluteRange(int a)
447 {
448 #if QT_CONFIG(tabletevent)
449     if (!d->m_tabletSupport.isNull())
450         d->m_tabletSupport->setAbsoluteRange(a);
451 #else
452     Q_UNUSED(a)
453 #endif
454 }
455 
setDetectAltGrModifier(bool a)456 void QWindowsContext::setDetectAltGrModifier(bool a)
457 {
458     d->m_keyMapper.setDetectAltGrModifier(a);
459 }
460 
processDpiAwareness()461 int QWindowsContext::processDpiAwareness()
462 {
463     int result;
464     if (QWindowsContext::shcoredll.getProcessDpiAwareness
465         && SUCCEEDED(QWindowsContext::shcoredll.getProcessDpiAwareness(nullptr, &result))) {
466         return result;
467     }
468     return -1;
469 }
470 
setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiAwareness)471 void QWindowsContext::setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiAwareness)
472 {
473     qCDebug(lcQpaWindows) << __FUNCTION__ << dpiAwareness;
474     if (QWindowsContext::shcoredll.isValid()) {
475         const HRESULT hr = QWindowsContext::shcoredll.setProcessDpiAwareness(dpiAwareness);
476         // E_ACCESSDENIED means set externally (MSVC manifest or external app loading Qt plugin).
477         // Silence warning in that case unless debug is enabled.
478         if (FAILED(hr) && (hr != E_ACCESSDENIED || lcQpaWindows().isDebugEnabled())) {
479             qWarning().noquote().nospace() << "SetProcessDpiAwareness("
480                 << dpiAwareness << ") failed: " << QWindowsContext::comErrorString(hr)
481                 << ", using " << QWindowsContext::processDpiAwareness();
482         }
483     } else {
484         if (dpiAwareness != QtWindows::ProcessDpiUnaware && QWindowsContext::user32dll.setProcessDPIAware) {
485             if (!QWindowsContext::user32dll.setProcessDPIAware())
486                 qErrnoWarning("SetProcessDPIAware() failed");
487         }
488     }
489 }
490 
isDarkMode()491 bool QWindowsContext::isDarkMode()
492 {
493     return QWindowsContextPrivate::m_darkMode;
494 }
495 
instance()496 QWindowsContext *QWindowsContext::instance()
497 {
498     return m_instance;
499 }
500 
systemInfo() const501 unsigned QWindowsContext::systemInfo() const
502 {
503     return d->m_systemInfo;
504 }
505 
useRTLExtensions() const506 bool QWindowsContext::useRTLExtensions() const
507 {
508     return d->m_keyMapper.useRTLExtensions();
509 }
510 
possibleKeys(const QKeyEvent * e) const511 QList<int> QWindowsContext::possibleKeys(const QKeyEvent *e) const
512 {
513     return d->m_keyMapper.possibleKeys(e);
514 }
515 
setWindowCreationContext(const QSharedPointer<QWindowCreationContext> & ctx)516 QSharedPointer<QWindowCreationContext> QWindowsContext::setWindowCreationContext(const QSharedPointer<QWindowCreationContext> &ctx)
517 {
518     const QSharedPointer<QWindowCreationContext> old = d->m_creationContext;
519     d->m_creationContext = ctx;
520     return old;
521 }
522 
windowCreationContext() const523 QSharedPointer<QWindowCreationContext> QWindowsContext::windowCreationContext() const
524 {
525     return d->m_creationContext;
526 }
527 
defaultDPI() const528 int QWindowsContext::defaultDPI() const
529 {
530     return d->m_defaultDPI;
531 }
532 
displayContext() const533 HDC QWindowsContext::displayContext() const
534 {
535     return d->m_displayContext;
536 }
537 
keyGrabber() const538 QWindow *QWindowsContext::keyGrabber() const
539 {
540     return d->m_keyMapper.keyGrabber();
541 }
542 
setKeyGrabber(QWindow * w)543 void QWindowsContext::setKeyGrabber(QWindow *w)
544 {
545     d->m_keyMapper.setKeyGrabber(w);
546 }
547 
classNamePrefix()548 QString QWindowsContext::classNamePrefix()
549 {
550     static QString result;
551     if (result.isEmpty()) {
552         QTextStream str(&result);
553         str << "Qt" << QT_VERSION_MAJOR << QT_VERSION_MINOR << QT_VERSION_PATCH;
554         if (QLibraryInfo::isDebugBuild())
555             str << 'd';
556 #ifdef QT_NAMESPACE
557 #  define xstr(s) str(s)
558 #  define str(s) #s
559         str << xstr(QT_NAMESPACE);
560 #endif
561     }
562     return result;
563 }
564 
565 // Window class registering code (from qapplication_win.cpp)
566 
registerWindowClass(const QWindow * w)567 QString QWindowsContext::registerWindowClass(const QWindow *w)
568 {
569     Q_ASSERT(w);
570     const Qt::WindowFlags flags = w->flags();
571     const Qt::WindowFlags type = flags & Qt::WindowType_Mask;
572     // Determine style and icon.
573     uint style = CS_DBLCLKS;
574     bool icon = true;
575     // The following will not set CS_OWNDC for any widget window, even if it contains a
576     // QOpenGLWidget or QQuickWidget later on. That cannot be detected at this stage.
577     if (w->surfaceType() == QSurface::OpenGLSurface || (flags & Qt::MSWindowsOwnDC))
578         style |= CS_OWNDC;
579     if (!(flags & Qt::NoDropShadowWindowHint)
580         && (type == Qt::Popup || w->property("_q_windowsDropShadow").toBool())) {
581         style |= CS_DROPSHADOW;
582     }
583     switch (type) {
584     case Qt::Tool:
585     case Qt::ToolTip:
586     case Qt::Popup:
587         style |= CS_SAVEBITS; // Save/restore background
588         icon = false;
589         break;
590     case Qt::Dialog:
591         if (!(flags & Qt::WindowSystemMenuHint))
592             icon = false; // QTBUG-2027, dialogs without system menu.
593         break;
594     }
595     // Create a unique name for the flag combination
596     QString cname = classNamePrefix();
597     cname += QLatin1String("QWindow");
598     switch (type) {
599     case Qt::Tool:
600         cname += QLatin1String("Tool");
601         break;
602     case Qt::ToolTip:
603         cname += QLatin1String("ToolTip");
604         break;
605     case Qt::Popup:
606         cname += QLatin1String("Popup");
607         break;
608     default:
609         break;
610     }
611     if (style & CS_DROPSHADOW)
612         cname += QLatin1String("DropShadow");
613     if (style & CS_SAVEBITS)
614         cname += QLatin1String("SaveBits");
615     if (style & CS_OWNDC)
616         cname += QLatin1String("OwnDC");
617     if (icon)
618         cname += QLatin1String("Icon");
619 
620     return registerWindowClass(cname, qWindowsWndProc, style, GetSysColorBrush(COLOR_WINDOW), icon);
621 }
622 
registerWindowClass(QString cname,WNDPROC proc,unsigned style,HBRUSH brush,bool icon)623 QString QWindowsContext::registerWindowClass(QString cname,
624                                              WNDPROC proc,
625                                              unsigned style,
626                                              HBRUSH brush,
627                                              bool icon)
628 {
629     // since multiple Qt versions can be used in one process
630     // each one has to have window class names with a unique name
631     // The first instance gets the unmodified name; if the class
632     // has already been registered by another instance of Qt then
633     // add a UUID. The check needs to be performed for each name
634     // in case new message windows are added (QTBUG-81347).
635     // Note: GetClassInfo() returns != 0 when a class exists.
636     const auto appInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr));
637     WNDCLASS wcinfo;
638     const bool classExists = GetClassInfo(appInstance, reinterpret_cast<LPCWSTR>(cname.utf16()), &wcinfo) != FALSE
639         && wcinfo.lpfnWndProc != proc;
640 
641     if (classExists)
642         cname += QUuid::createUuid().toString();
643 
644     if (d->m_registeredWindowClassNames.contains(cname))        // already registered in our list
645         return cname;
646 
647     WNDCLASSEX wc;
648     wc.cbSize       = sizeof(WNDCLASSEX);
649     wc.style        = style;
650     wc.lpfnWndProc  = proc;
651     wc.cbClsExtra   = 0;
652     wc.cbWndExtra   = 0;
653     wc.hInstance    = appInstance;
654     wc.hCursor      = nullptr;
655     wc.hbrBackground = brush;
656     if (icon) {
657         wc.hIcon = static_cast<HICON>(LoadImage(appInstance, L"IDI_ICON1", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE));
658         if (wc.hIcon) {
659             int sw = GetSystemMetrics(SM_CXSMICON);
660             int sh = GetSystemMetrics(SM_CYSMICON);
661             wc.hIconSm = static_cast<HICON>(LoadImage(appInstance, L"IDI_ICON1", IMAGE_ICON, sw, sh, 0));
662         } else {
663             wc.hIcon = static_cast<HICON>(LoadImage(nullptr, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED));
664             wc.hIconSm = nullptr;
665         }
666     } else {
667         wc.hIcon    = nullptr;
668         wc.hIconSm  = nullptr;
669     }
670 
671     wc.lpszMenuName  = nullptr;
672     wc.lpszClassName = reinterpret_cast<LPCWSTR>(cname.utf16());
673     ATOM atom = RegisterClassEx(&wc);
674     if (!atom)
675         qErrnoWarning("QApplication::regClass: Registering window class '%s' failed.",
676                       qPrintable(cname));
677 
678     d->m_registeredWindowClassNames.insert(cname);
679     qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << ' ' << cname
680         << " style=0x" << Qt::hex << style << Qt::dec
681         << " brush=" << brush << " icon=" << icon << " atom=" << atom;
682     return cname;
683 }
684 
unregisterWindowClasses()685 void QWindowsContext::unregisterWindowClasses()
686 {
687     const auto appInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr));
688 
689     for (const QString &name : qAsConst(d->m_registeredWindowClassNames)) {
690         if (!UnregisterClass(reinterpret_cast<LPCWSTR>(name.utf16()), appInstance) && QWindowsContext::verbose)
691             qErrnoWarning("UnregisterClass failed for '%s'", qPrintable(name));
692     }
693     d->m_registeredWindowClassNames.clear();
694 }
695 
screenDepth() const696 int QWindowsContext::screenDepth() const
697 {
698     return GetDeviceCaps(d->m_displayContext, BITSPIXEL);
699 }
700 
windowsErrorMessage(unsigned long errorCode)701 QString QWindowsContext::windowsErrorMessage(unsigned long errorCode)
702 {
703     QString rc = QString::fromLatin1("#%1: ").arg(errorCode);
704     ushort *lpMsgBuf;
705 
706     const DWORD len = FormatMessage(
707             FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
708             nullptr, errorCode, 0, reinterpret_cast<LPTSTR>(&lpMsgBuf), 0, nullptr);
709     if (len) {
710         rc = QString::fromUtf16(lpMsgBuf, int(len));
711         LocalFree(lpMsgBuf);
712     } else {
713         rc += QString::fromLatin1("<unknown error>");
714     }
715     return rc;
716 }
717 
addWindow(HWND hwnd,QWindowsWindow * w)718 void QWindowsContext::addWindow(HWND hwnd, QWindowsWindow *w)
719 {
720     d->m_windows.insert(hwnd, w);
721 }
722 
removeWindow(HWND hwnd)723 void QWindowsContext::removeWindow(HWND hwnd)
724 {
725     const HandleBaseWindowHash::iterator it = d->m_windows.find(hwnd);
726     if (it != d->m_windows.end()) {
727         if (d->m_keyMapper.keyGrabber() == it.value()->window())
728             d->m_keyMapper.setKeyGrabber(nullptr);
729         d->m_windows.erase(it);
730     }
731 }
732 
findPlatformWindow(const QWindowsMenuBar * mb) const733 QWindowsWindow *QWindowsContext::findPlatformWindow(const QWindowsMenuBar *mb) const
734 {
735     for (auto it = d->m_windows.cbegin(), end = d->m_windows.cend(); it != end; ++it) {
736         if ((*it)->menuBar() == mb)
737             return *it;
738     }
739     return nullptr;
740 }
741 
findPlatformWindow(HWND hwnd) const742 QWindowsWindow *QWindowsContext::findPlatformWindow(HWND hwnd) const
743 {
744     return d->m_windows.value(hwnd);
745 }
746 
findClosestPlatformWindow(HWND hwnd) const747 QWindowsWindow *QWindowsContext::findClosestPlatformWindow(HWND hwnd) const
748 {
749     QWindowsWindow *window = d->m_windows.value(hwnd);
750 
751     // Requested hwnd may also be a child of a platform window in case of embedded native windows.
752     // Find the closest parent that has a platform window.
753     if (!window) {
754         for (HWND w = hwnd; w; w = GetParent(w)) {
755             window = d->m_windows.value(w);
756             if (window)
757                 break;
758         }
759     }
760 
761     return window;
762 }
763 
findWindow(HWND hwnd) const764 QWindow *QWindowsContext::findWindow(HWND hwnd) const
765 {
766     if (const QWindowsWindow *bw = findPlatformWindow(hwnd))
767             return bw->window();
768     return nullptr;
769 }
770 
windowUnderMouse() const771 QWindow *QWindowsContext::windowUnderMouse() const
772 {
773     return (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) ?
774         d->m_pointerHandler.windowUnderMouse() : d->m_mouseHandler.windowUnderMouse();
775 }
776 
clearWindowUnderMouse()777 void QWindowsContext::clearWindowUnderMouse()
778 {
779     if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer)
780         d->m_pointerHandler.clearWindowUnderMouse();
781     else
782         d->m_mouseHandler.clearWindowUnderMouse();
783 }
784 
785 /*!
786     \brief Find a child window at a screen point.
787 
788     Deep search for a QWindow at global point, skipping non-owned
789     windows (accessibility?). Implemented using ChildWindowFromPointEx()
790     instead of (historically used) WindowFromPoint() to get a well-defined
791     behaviour for hidden/transparent windows.
792 
793     \a cwex_flags are flags of ChildWindowFromPointEx().
794     \a parent is the parent window, pass GetDesktopWindow() for top levels.
795 */
796 
findPlatformWindowHelper(const POINT & screenPoint,unsigned cwexFlags,const QWindowsContext * context,HWND * hwnd,QWindowsWindow ** result)797 static inline bool findPlatformWindowHelper(const POINT &screenPoint, unsigned cwexFlags,
798                                             const QWindowsContext *context,
799                                             HWND *hwnd, QWindowsWindow **result)
800 {
801     POINT point = screenPoint;
802     screenToClient(*hwnd, &point);
803     // Returns parent if inside & none matched.
804     const HWND child = ChildWindowFromPointEx(*hwnd, point, cwexFlags);
805     if (!child || child == *hwnd)
806         return false;
807     if (QWindowsWindow *window = context->findPlatformWindow(child)) {
808         *result = window;
809         *hwnd = child;
810         return true;
811     }
812     // QTBUG-40555: despite CWP_SKIPINVISIBLE, it is possible to hit on invisible
813     // full screen windows of other applications that have WS_EX_TRANSPARENT set
814     // (for example created by  screen sharing applications). In that case, try to
815     // find a Qt window by searching again with CWP_SKIPTRANSPARENT.
816     // Note that Qt 5 uses WS_EX_TRANSPARENT for Qt::WindowTransparentForInput
817     // as well.
818     if (!(cwexFlags & CWP_SKIPTRANSPARENT)
819         && (GetWindowLongPtr(child, GWL_EXSTYLE) & WS_EX_TRANSPARENT)) {
820         const HWND nonTransparentChild = ChildWindowFromPointEx(*hwnd, point, cwexFlags | CWP_SKIPTRANSPARENT);
821         if (QWindowsWindow *nonTransparentWindow = context->findPlatformWindow(nonTransparentChild)) {
822             *result = nonTransparentWindow;
823             *hwnd = nonTransparentChild;
824             return true;
825         }
826     }
827     *hwnd = child;
828     return true;
829 }
830 
findPlatformWindowAt(HWND parent,const QPoint & screenPointIn,unsigned cwex_flags) const831 QWindowsWindow *QWindowsContext::findPlatformWindowAt(HWND parent,
832                                                           const QPoint &screenPointIn,
833                                                           unsigned cwex_flags) const
834 {
835     QWindowsWindow *result = nullptr;
836     const POINT screenPoint = { screenPointIn.x(), screenPointIn.y() };
837     while (findPlatformWindowHelper(screenPoint, cwex_flags, this, &parent, &result)) {}
838     // QTBUG-40815: ChildWindowFromPointEx() can hit on special windows from
839     // screen recorder applications like ScreenToGif. Fall back to WindowFromPoint().
840     if (result == nullptr) {
841         if (const HWND window = WindowFromPoint(screenPoint))
842             result = findPlatformWindow(window);
843     }
844     return result;
845 }
846 
isSessionLocked()847 bool QWindowsContext::isSessionLocked()
848 {
849     bool result = false;
850     const DWORD sessionId = WTSGetActiveConsoleSessionId();
851     if (sessionId != 0xFFFFFFFF) {
852         LPTSTR buffer = nullptr;
853         DWORD size = 0;
854 #if !defined(Q_CC_MINGW)
855         if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, sessionId,
856                                        WTSSessionInfoEx, &buffer, &size) == TRUE
857             && size > 0) {
858             const WTSINFOEXW *info = reinterpret_cast<WTSINFOEXW *>(buffer);
859             result = info->Level == 1 && info->Data.WTSInfoExLevel1.SessionFlags == WTS_SESSIONSTATE_LOCK;
860             WTSFreeMemory(buffer);
861         }
862 #else   // MinGW as of 7.3 does not have WTSINFOEXW in wtsapi32.h
863         // Retrieve the flags which are at offset 16 due to padding for 32/64bit alike.
864         if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, sessionId,
865                                        WTS_INFO_CLASS(25), &buffer, &size) == TRUE
866             && size >= 20) {
867             const DWORD *p = reinterpret_cast<DWORD *>(buffer);
868             const DWORD level = *p;
869             const DWORD sessionFlags = *(p + 4);
870             result = level == 1 && sessionFlags == 1;
871             WTSFreeMemory(buffer);
872         }
873 #endif // Q_CC_MINGW
874     }
875     return result;
876 }
877 
mimeConverter() const878 QWindowsMimeConverter &QWindowsContext::mimeConverter() const
879 {
880     return d->m_mimeConverter;
881 }
882 
screenManager()883 QWindowsScreenManager &QWindowsContext::screenManager()
884 {
885     return d->m_screenManager;
886 }
887 
tabletSupport() const888 QWindowsTabletSupport *QWindowsContext::tabletSupport() const
889 {
890 #if QT_CONFIG(tabletevent)
891     return d->m_tabletSupport.data();
892 #else
893     return 0;
894 #endif
895 }
896 
897 /*!
898     \brief Convenience to create a non-visible, message-only dummy
899     window for example used as clipboard watcher or for GL.
900 */
901 
createDummyWindow(const QString & classNameIn,const wchar_t * windowName,WNDPROC wndProc,DWORD style)902 HWND QWindowsContext::createDummyWindow(const QString &classNameIn,
903                                         const wchar_t *windowName,
904                                         WNDPROC wndProc, DWORD style)
905 {
906     if (!wndProc)
907         wndProc = DefWindowProc;
908     QString className = registerWindowClass(classNamePrefix() + classNameIn, wndProc);
909     return CreateWindowEx(0, reinterpret_cast<LPCWSTR>(className.utf16()),
910                           windowName, style,
911                           CW_USEDEFAULT, CW_USEDEFAULT,
912                           CW_USEDEFAULT, CW_USEDEFAULT,
913                           HWND_MESSAGE, nullptr, static_cast<HINSTANCE>(GetModuleHandle(nullptr)), nullptr);
914 }
915 
916 // Re-engineered from the inline function _com_error::ErrorMessage().
917 // We cannot use it directly since it uses swprintf_s(), which is not
918 // present in the MSVCRT.DLL found on Windows XP (QTBUG-35617).
errorMessageFromComError(const _com_error & comError)919 static inline QString errorMessageFromComError(const _com_error &comError)
920 {
921      TCHAR *message = nullptr;
922      FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
923                    nullptr, DWORD(comError.Error()), MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
924                    message, 0, nullptr);
925      if (message) {
926          const QString result = QString::fromWCharArray(message).trimmed();
927          LocalFree(static_cast<HLOCAL>(message));
928          return result;
929      }
930      if (const WORD wCode = comError.WCode())
931          return QString::asprintf("IDispatch error #%u", uint(wCode));
932      return QString::asprintf("Unknown error 0x0%x", uint(comError.Error()));
933 }
934 
935 /*!
936     \brief Common COM error strings.
937 */
938 
comErrorString(HRESULT hr)939 QByteArray QWindowsContext::comErrorString(HRESULT hr)
940 {
941     QByteArray result = QByteArrayLiteral("COM error 0x")
942         + QByteArray::number(quintptr(hr), 16) + ' ';
943     switch (hr) {
944     case S_OK:
945         result += QByteArrayLiteral("S_OK");
946         break;
947     case S_FALSE:
948         result += QByteArrayLiteral("S_FALSE");
949         break;
950     case E_UNEXPECTED:
951         result += QByteArrayLiteral("E_UNEXPECTED");
952         break;
953     case E_ACCESSDENIED:
954         result += QByteArrayLiteral("E_ACCESSDENIED");
955         break;
956     case CO_E_ALREADYINITIALIZED:
957         result += QByteArrayLiteral("CO_E_ALREADYINITIALIZED");
958         break;
959     case CO_E_NOTINITIALIZED:
960         result += QByteArrayLiteral("CO_E_NOTINITIALIZED");
961         break;
962     case RPC_E_CHANGED_MODE:
963         result += QByteArrayLiteral("RPC_E_CHANGED_MODE");
964         break;
965     case OLE_E_WRONGCOMPOBJ:
966         result += QByteArrayLiteral("OLE_E_WRONGCOMPOBJ");
967         break;
968     case CO_E_NOT_SUPPORTED:
969         result += QByteArrayLiteral("CO_E_NOT_SUPPORTED");
970         break;
971     case E_NOTIMPL:
972         result += QByteArrayLiteral("E_NOTIMPL");
973         break;
974     case E_INVALIDARG:
975         result += QByteArrayLiteral("E_INVALIDARG");
976         break;
977     case E_NOINTERFACE:
978         result += QByteArrayLiteral("E_NOINTERFACE");
979         break;
980     case E_POINTER:
981         result += QByteArrayLiteral("E_POINTER");
982         break;
983     case E_HANDLE:
984         result += QByteArrayLiteral("E_HANDLE");
985         break;
986     case E_ABORT:
987         result += QByteArrayLiteral("E_ABORT");
988         break;
989     case E_FAIL:
990         result += QByteArrayLiteral("E_FAIL");
991         break;
992     case RPC_E_WRONG_THREAD:
993         result += QByteArrayLiteral("RPC_E_WRONG_THREAD");
994         break;
995     case RPC_E_THREAD_NOT_INIT:
996         result += QByteArrayLiteral("RPC_E_THREAD_NOT_INIT");
997         break;
998     default:
999         break;
1000     }
1001     _com_error error(hr);
1002     result += QByteArrayLiteral(" (");
1003     result += errorMessageFromComError(error);
1004     result += ')';
1005     return result;
1006 }
1007 
forceNcCalcSize(HWND hwnd)1008 void QWindowsContext::forceNcCalcSize(HWND hwnd)
1009 {
1010     // Force WM_NCCALCSIZE to adjust margin
1011     SetWindowPos(hwnd, nullptr, 0, 0, 0, 0,
1012                  SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
1013 }
1014 
systemParametersInfo(unsigned action,unsigned param,void * out,unsigned dpi)1015 bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void *out,
1016                                            unsigned dpi)
1017 {
1018     const BOOL result = QWindowsContext::user32dll.systemParametersInfoForDpi != nullptr && dpi != 0
1019         ? QWindowsContext::user32dll.systemParametersInfoForDpi(action, param, out, 0, dpi)
1020         : SystemParametersInfo(action, param, out, 0);
1021     return result == TRUE;
1022 }
1023 
systemParametersInfoForScreen(unsigned action,unsigned param,void * out,const QPlatformScreen * screen)1024 bool QWindowsContext::systemParametersInfoForScreen(unsigned action, unsigned param, void *out,
1025                                                     const QPlatformScreen *screen)
1026 {
1027     return systemParametersInfo(action, param, out, screen ? unsigned(screen->logicalDpi().first) : 0u);
1028 }
1029 
systemParametersInfoForWindow(unsigned action,unsigned param,void * out,const QPlatformWindow * win)1030 bool QWindowsContext::systemParametersInfoForWindow(unsigned action, unsigned param, void *out,
1031                                                     const QPlatformWindow *win)
1032 {
1033     return systemParametersInfoForScreen(action, param, out, win ? win->screen() : nullptr);
1034 }
1035 
nonClientMetrics(NONCLIENTMETRICS * ncm,unsigned dpi)1036 bool QWindowsContext::nonClientMetrics(NONCLIENTMETRICS *ncm, unsigned dpi)
1037 {
1038     memset(ncm, 0, sizeof(NONCLIENTMETRICS));
1039     ncm->cbSize = sizeof(NONCLIENTMETRICS);
1040     return systemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm->cbSize, ncm, dpi);
1041 }
1042 
nonClientMetricsForScreen(NONCLIENTMETRICS * ncm,const QPlatformScreen * screen)1043 bool QWindowsContext::nonClientMetricsForScreen(NONCLIENTMETRICS *ncm,
1044                                                 const QPlatformScreen *screen)
1045 {
1046     const int dpi = screen ? qRound(screen->logicalDpi().first) : 0;
1047     return nonClientMetrics(ncm, unsigned(dpi));
1048 }
1049 
nonClientMetricsForWindow(NONCLIENTMETRICS * ncm,const QPlatformWindow * win)1050 bool QWindowsContext::nonClientMetricsForWindow(NONCLIENTMETRICS *ncm, const QPlatformWindow *win)
1051 {
1052     return nonClientMetricsForScreen(ncm, win ? win->screen() : nullptr);
1053 }
1054 
windowsInputContext()1055 static inline QWindowsInputContext *windowsInputContext()
1056 {
1057     return qobject_cast<QWindowsInputContext *>(QWindowsIntegration::instance()->inputContext());
1058 }
1059 
1060 
1061 // Child windows, fixed-size windows or pop-ups and similar should not be resized
resizeOnDpiChanged(const QWindow * w)1062 static inline bool resizeOnDpiChanged(const QWindow *w)
1063 {
1064     bool result = false;
1065     if (w->isTopLevel()) {
1066         switch (w->type()) {
1067         case Qt::Window:
1068         case Qt::Dialog:
1069         case Qt::Sheet:
1070         case Qt::Drawer:
1071         case Qt::Tool:
1072             result = !w->flags().testFlag(Qt::MSWindowsFixedSizeDialogHint);
1073             break;
1074         default:
1075             break;
1076         }
1077     }
1078     return result;
1079 }
1080 
shouldHaveNonClientDpiScaling(const QWindow * window)1081 bool QWindowsContext::shouldHaveNonClientDpiScaling(const QWindow *window)
1082 {
1083     return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10
1084         && window->isTopLevel()
1085         && !window->property(QWindowsWindow::embeddedNativeParentHandleProperty).isValid()
1086 #if QT_CONFIG(opengl) // /QTBUG-62901, EnableNonClientDpiScaling has problems with GL
1087         && (window->surfaceType() != QSurface::OpenGLSurface
1088             || QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL)
1089 #endif
1090        ;
1091 }
1092 
isInputMessage(UINT m)1093 static inline bool isInputMessage(UINT m)
1094 {
1095     switch (m) {
1096     case WM_IME_STARTCOMPOSITION:
1097     case WM_IME_ENDCOMPOSITION:
1098     case WM_IME_COMPOSITION:
1099     case WM_INPUT:
1100     case WM_TOUCH:
1101     case WM_MOUSEHOVER:
1102     case WM_MOUSELEAVE:
1103     case WM_NCMOUSEHOVER:
1104     case WM_NCMOUSELEAVE:
1105     case WM_SIZING:
1106     case WM_MOVING:
1107     case WM_SYSCOMMAND:
1108     case WM_COMMAND:
1109     case WM_DWMNCRENDERINGCHANGED:
1110     case WM_PAINT:
1111         return true;
1112     default:
1113         break;
1114     }
1115     return (m >= WM_MOUSEFIRST && m <= WM_MOUSELAST)
1116         || (m >= WM_NCMOUSEMOVE && m <= WM_NCXBUTTONDBLCLK)
1117         || (m >= WM_KEYFIRST && m <= WM_KEYLAST);
1118 }
1119 
1120 /*!
1121      \brief Main windows procedure registered for windows.
1122 
1123      \sa QWindowsGuiEventDispatcher
1124 */
1125 
windowsProc(HWND hwnd,UINT message,QtWindows::WindowsEventType et,WPARAM wParam,LPARAM lParam,LRESULT * result,QWindowsWindow ** platformWindowPtr)1126 bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
1127                                   QtWindows::WindowsEventType et,
1128                                   WPARAM wParam, LPARAM lParam,
1129                                   LRESULT *result,
1130                                   QWindowsWindow **platformWindowPtr)
1131 {
1132     *result = 0;
1133 
1134     MSG msg;
1135     msg.hwnd = hwnd;         // re-create MSG structure
1136     msg.message = message;   // time and pt fields ignored
1137     msg.wParam = wParam;
1138     msg.lParam = lParam;
1139     msg.pt.x = msg.pt.y = 0;
1140     if (et != QtWindows::CursorEvent && (et & (QtWindows::MouseEventFlag | QtWindows::NonClientEventFlag))) {
1141         msg.pt.x = GET_X_LPARAM(lParam);
1142         msg.pt.y = GET_Y_LPARAM(lParam);
1143         // For non-client-area messages, these are screen coordinates (as expected
1144         // in the MSG structure), otherwise they are client coordinates.
1145         if (!(et & QtWindows::NonClientEventFlag)) {
1146             clientToScreen(msg.hwnd, &msg.pt);
1147         }
1148     } else {
1149         GetCursorPos(&msg.pt);
1150     }
1151 
1152     QWindowsWindow *platformWindow = findPlatformWindow(hwnd);
1153     *platformWindowPtr = platformWindow;
1154 
1155     // Run the native event filters. QTBUG-67095: Exclude input messages which are sent
1156     // by QEventDispatcherWin32::processEvents()
1157     if (!isInputMessage(msg.message) && filterNativeEvent(&msg, result))
1158         return true;
1159 
1160     if (platformWindow && filterNativeEvent(platformWindow->window(), &msg, result))
1161         return true;
1162 
1163     if (et & QtWindows::InputMethodEventFlag) {
1164         QWindowsInputContext *windowsInputContext = ::windowsInputContext();
1165         // Disable IME assuming this is a special implementation hooking into keyboard input.
1166         // "Real" IME implementations should use a native event filter intercepting IME events.
1167         if (!windowsInputContext) {
1168             QWindowsInputContext::setWindowsImeEnabled(platformWindow, false);
1169             return false;
1170         }
1171         switch (et) {
1172         case QtWindows::InputMethodStartCompositionEvent:
1173             return windowsInputContext->startComposition(hwnd);
1174         case QtWindows::InputMethodCompositionEvent:
1175             return windowsInputContext->composition(hwnd, lParam);
1176         case QtWindows::InputMethodEndCompositionEvent:
1177             return windowsInputContext->endComposition(hwnd);
1178         case QtWindows::InputMethodRequest:
1179             return windowsInputContext->handleIME_Request(wParam, lParam, result);
1180         default:
1181             break;
1182         }
1183     } // InputMethodEventFlag
1184 
1185     switch (et) {
1186     case QtWindows::GestureEvent:
1187         if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer))
1188             return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateGestureEvent(platformWindow->window(), hwnd, et, msg, result);
1189         break;
1190     case QtWindows::InputMethodOpenCandidateWindowEvent:
1191     case QtWindows::InputMethodCloseCandidateWindowEvent:
1192         // TODO: Release/regrab mouse if a popup has mouse grab.
1193         return false;
1194     case QtWindows::DestroyEvent:
1195         if (platformWindow && !platformWindow->testFlag(QWindowsWindow::WithinDestroy)) {
1196             qWarning() << "External WM_DESTROY received for " << platformWindow->window()
1197                        << ", parent: " << platformWindow->window()->parent()
1198                        << ", transient parent: " << platformWindow->window()->transientParent();
1199             }
1200         return false;
1201     case QtWindows::ClipboardEvent:
1202         return false;
1203     case QtWindows::CursorEvent: // Sent to windows that do not have capture (see QTBUG-58590).
1204         if (QWindowsCursor::hasOverrideCursor()) {
1205             QWindowsCursor::enforceOverrideCursor();
1206             return true;
1207         }
1208         break;
1209     case QtWindows::UnknownEvent:
1210         return false;
1211     case QtWindows::AccessibleObjectFromWindowRequest:
1212 #if QT_CONFIG(accessibility)
1213         return QWindowsUiaAccessibility::handleWmGetObject(hwnd, wParam, lParam, result);
1214 #else
1215         return false;
1216 #endif
1217     case QtWindows::DisplayChangedEvent:
1218         if (QWindowsTheme *t = QWindowsTheme::instance())
1219             t->displayChanged();
1220         QWindowsWindow::displayChanged();
1221         return d->m_screenManager.handleDisplayChange(wParam, lParam);
1222     case QtWindows::SettingChangedEvent: {
1223         QWindowsWindow::settingsChanged();
1224         const bool darkMode = QWindowsTheme::queryDarkMode();
1225         if (darkMode != QWindowsContextPrivate::m_darkMode) {
1226             QWindowsContextPrivate::m_darkMode = darkMode;
1227             auto nativeInterface =
1228                 static_cast<QWindowsNativeInterface *>(QWindowsIntegration::instance()->nativeInterface());
1229             emit nativeInterface->darkModeChanged(darkMode);
1230             const auto options = QWindowsIntegration::instance()->options();
1231             if ((options & QWindowsIntegration::DarkModeWindowFrames) != 0) {
1232                 for (QWindowsWindow *w : d->m_windows)
1233                     w->setDarkBorder(QWindowsContextPrivate::m_darkMode);
1234             }
1235             if ((options & QWindowsIntegration::DarkModeStyle) != 0) {
1236                 QWindowsTheme::instance()->refresh();
1237                 for (QWindowsWindow *w : d->m_windows)
1238                     QWindowSystemInterface::handleThemeChange(w->window());
1239             }
1240         }
1241         return d->m_screenManager.handleScreenChanges();
1242     }
1243     default:
1244         break;
1245     }
1246 
1247     // Before CreateWindowEx() returns, some events are sent,
1248     // for example WM_GETMINMAXINFO asking for size constraints for top levels.
1249     // Pass on to current creation context
1250     if (!platformWindow && !d->m_creationContext.isNull()) {
1251         switch (et) {
1252         case QtWindows::QuerySizeHints:
1253             d->m_creationContext->applyToMinMaxInfo(reinterpret_cast<MINMAXINFO *>(lParam));
1254             return true;
1255         case QtWindows::ResizeEvent:
1256             d->m_creationContext->obtainedSize = QSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
1257             return true;
1258         case QtWindows::MoveEvent:
1259             d->m_creationContext->obtainedPos = QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
1260             return true;
1261         case QtWindows::NonClientCreate:
1262             if (shouldHaveNonClientDpiScaling(d->m_creationContext->window))
1263                 enableNonClientDpiScaling(msg.hwnd);
1264             return false;
1265         case QtWindows::CalculateSize:
1266             return QWindowsGeometryHint::handleCalculateSize(d->m_creationContext->customMargins, msg, result);
1267         case QtWindows::GeometryChangingEvent:
1268             return QWindowsWindow::handleGeometryChangingMessage(&msg, d->m_creationContext->window,
1269                                                                  d->m_creationContext->margins + d->m_creationContext->customMargins);
1270         default:
1271             break;
1272         }
1273     }
1274     if (platformWindow) {
1275         // Suppress events sent during DestroyWindow() for native children.
1276         if (platformWindow->testFlag(QWindowsWindow::WithinDestroy))
1277             return false;
1278         if (QWindowsContext::verbose > 1)
1279             qCDebug(lcQpaEvents) << "Event window: " << platformWindow->window();
1280     } else {
1281         qWarning("%s: No Qt Window found for event 0x%x (%s), hwnd=0x%p.",
1282                  __FUNCTION__, message,
1283                  QWindowsGuiEventDispatcher::windowsMessageName(message), hwnd);
1284         return false;
1285     }
1286 
1287     switch (et) {
1288     case QtWindows::DeviceChangeEvent:
1289         if (d->m_systemInfo & QWindowsContext::SI_SupportsTouch)
1290             break;
1291         // See if there are any touch devices added
1292         if (wParam == DBT_DEVNODES_CHANGED)
1293             initTouch();
1294         break;
1295     case QtWindows::KeyboardLayoutChangeEvent:
1296         if (QWindowsInputContext *wic = windowsInputContext())
1297             wic->handleInputLanguageChanged(wParam, lParam);
1298         Q_FALLTHROUGH();
1299     case QtWindows::KeyDownEvent:
1300     case QtWindows::KeyEvent:
1301     case QtWindows::InputMethodKeyEvent:
1302     case QtWindows::InputMethodKeyDownEvent:
1303     case QtWindows::AppCommandEvent:
1304         return sessionManagerInteractionBlocked() ||  d->m_keyMapper.translateKeyEvent(platformWindow->window(), hwnd, msg, result);
1305     case QtWindows::MenuAboutToShowEvent:
1306         if (sessionManagerInteractionBlocked())
1307             return true;
1308         if (QWindowsPopupMenu::notifyAboutToShow(reinterpret_cast<HMENU>(wParam)))
1309             return true;
1310         if (platformWindow == nullptr || platformWindow->menuBar() == nullptr)
1311             return false;
1312         return platformWindow->menuBar()->notifyAboutToShow(reinterpret_cast<HMENU>(wParam));
1313     case QtWindows::MenuCommandEvent:
1314         if (sessionManagerInteractionBlocked())
1315             return true;
1316         if (QWindowsPopupMenu::notifyTriggered(LOWORD(wParam)))
1317             return true;
1318         if (platformWindow == nullptr || platformWindow->menuBar() == nullptr)
1319             return false;
1320         return platformWindow->menuBar()->notifyTriggered(LOWORD(wParam));
1321     case QtWindows::MoveEvent:
1322         platformWindow->handleMoved();
1323         return true;
1324     case QtWindows::ResizeEvent:
1325         platformWindow->handleResized(static_cast<int>(wParam));
1326         return true;
1327     case QtWindows::QuerySizeHints:
1328         platformWindow->getSizeHints(reinterpret_cast<MINMAXINFO *>(lParam));
1329         return true;// maybe available on some SDKs revisit WM_NCCALCSIZE
1330     case QtWindows::CalculateSize:
1331         return QWindowsGeometryHint::handleCalculateSize(platformWindow->customMargins(), msg, result);
1332     case QtWindows::NonClientHitTest:
1333         return platformWindow->handleNonClientHitTest(QPoint(msg.pt.x, msg.pt.y), result);
1334     case QtWindows::GeometryChangingEvent:
1335         return platformWindow->QWindowsWindow::handleGeometryChanging(&msg);
1336     case QtWindows::ExposeEvent:
1337         return platformWindow->handleWmPaint(hwnd, message, wParam, lParam);
1338     case QtWindows::NonClientMouseEvent:
1339         if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer) && platformWindow->frameStrutEventsEnabled())
1340             return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result);
1341         else
1342             return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result);
1343     case QtWindows::NonClientPointerEvent:
1344         if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer) && platformWindow->frameStrutEventsEnabled())
1345             return sessionManagerInteractionBlocked() || d->m_pointerHandler.translatePointerEvent(platformWindow->window(), hwnd, et, msg, result);
1346         break;
1347     case QtWindows::EnterSizeMoveEvent:
1348         platformWindow->setFlag(QWindowsWindow::ResizeMoveActive);
1349         return true;
1350     case QtWindows::ExitSizeMoveEvent:
1351         platformWindow->clearFlag(QWindowsWindow::ResizeMoveActive);
1352         platformWindow->checkForScreenChanged();
1353         handleExitSizeMove(platformWindow->window());
1354         return true;
1355     case QtWindows::ScrollEvent:
1356         if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer))
1357             return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateScrollEvent(platformWindow->window(), hwnd, msg, result);
1358         break;
1359     case QtWindows::MouseWheelEvent:
1360     case QtWindows::MouseEvent:
1361     case QtWindows::LeaveEvent:
1362         {
1363             QWindow *window = platformWindow->window();
1364             while (window && (window->flags() & Qt::WindowTransparentForInput))
1365                 window = window->parent();
1366             if (!window)
1367                 return false;
1368             if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer)
1369                 return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(window, hwnd, et, msg, result);
1370             else
1371                 return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateMouseEvent(window, hwnd, et, msg, result);
1372         }
1373         break;
1374     case QtWindows::TouchEvent:
1375         if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer))
1376             return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateTouchEvent(platformWindow->window(), hwnd, et, msg, result);
1377         break;
1378     case QtWindows::PointerEvent:
1379         if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer)
1380             return sessionManagerInteractionBlocked() || d->m_pointerHandler.translatePointerEvent(platformWindow->window(), hwnd, et, msg, result);
1381         break;
1382     case QtWindows::FocusInEvent: // see QWindowsWindow::requestActivateWindow().
1383     case QtWindows::FocusOutEvent:
1384         handleFocusEvent(et, platformWindow);
1385         return true;
1386     case QtWindows::ShowEventOnParentRestoring: // QTBUG-40696, prevent Windows from re-showing hidden transient children (dialogs).
1387         if (!platformWindow->window()->isVisible()) {
1388             *result = 0;
1389             return true;
1390         }
1391         break;
1392     case QtWindows::HideEvent:
1393         platformWindow->handleHidden();
1394         return false;// Indicate transient children should be hidden by windows (SW_PARENTCLOSING)
1395     case QtWindows::CloseEvent:
1396         QWindowSystemInterface::handleCloseEvent(platformWindow->window());
1397         return true;
1398     case QtWindows::ThemeChanged: {
1399         // Switch from Aero to Classic changes margins.
1400         if (QWindowsTheme *theme = QWindowsTheme::instance())
1401             theme->windowsThemeChanged(platformWindow->window());
1402         return true;
1403     }
1404     case QtWindows::CompositionSettingsChanged:
1405         platformWindow->handleCompositionSettingsChanged();
1406         return true;
1407     case QtWindows::ActivateWindowEvent:
1408         if (platformWindow->window()->flags() & Qt::WindowDoesNotAcceptFocus) {
1409             *result = LRESULT(MA_NOACTIVATE);
1410             return true;
1411         }
1412 #if QT_CONFIG(tabletevent)
1413         if (!d->m_tabletSupport.isNull())
1414             d->m_tabletSupport->notifyActivate();
1415 #endif // QT_CONFIG(tabletevent)
1416         if (platformWindow->testFlag(QWindowsWindow::BlockedByModal))
1417             if (const QWindow *modalWindow = QGuiApplication::modalWindow()) {
1418                 QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(modalWindow);
1419                 Q_ASSERT(platformWindow);
1420                 platformWindow->alertWindow();
1421             }
1422         break;
1423     case QtWindows::MouseActivateWindowEvent:
1424     case QtWindows::PointerActivateWindowEvent:
1425         if (platformWindow->window()->flags() & Qt::WindowDoesNotAcceptFocus) {
1426             *result = LRESULT(MA_NOACTIVATE);
1427             return true;
1428         }
1429         break;
1430 #ifndef QT_NO_CONTEXTMENU
1431     case QtWindows::ContextMenu:
1432         return handleContextMenuEvent(platformWindow->window(), msg);
1433 #endif
1434     case QtWindows::WhatsThisEvent: {
1435 #ifndef QT_NO_WHATSTHIS
1436         QWindowSystemInterface::handleEnterWhatsThisEvent();
1437         return true;
1438 #endif
1439     }   break;
1440     case QtWindows::DpiChangedEvent: {
1441         // Try to apply the suggested size first and then notify ScreenChanged
1442         // so that the resize event sent from QGuiApplication incorporates it
1443         // WM_DPICHANGED is sent with a size that avoids resize loops (by
1444         // snapping back to the previous screen, see QTBUG-65580).
1445         const bool doResize = resizeOnDpiChanged(platformWindow->window());
1446         if (doResize) {
1447             platformWindow->setFlag(QWindowsWindow::WithinDpiChanged);
1448             platformWindow->updateFullFrameMargins();
1449             const auto prcNewWindow = reinterpret_cast<RECT *>(lParam);
1450             qCDebug(lcQpaWindows) << __FUNCTION__ << "WM_DPICHANGED"
1451                 << platformWindow->window() << *prcNewWindow;
1452             SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top,
1453                          prcNewWindow->right - prcNewWindow->left,
1454                          prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE);
1455             platformWindow->clearFlag(QWindowsWindow::WithinDpiChanged);
1456         }
1457         platformWindow->checkForScreenChanged(QWindowsWindow::FromDpiChange);
1458         return doResize;
1459     }
1460 #if QT_CONFIG(sessionmanager)
1461     case QtWindows::QueryEndSessionApplicationEvent: {
1462         QWindowsSessionManager *sessionManager = platformSessionManager();
1463         if (sessionManager->isActive()) { // bogus message from windows
1464             *result = sessionManager->wasCanceled() ? 0 : 1;
1465             return true;
1466         }
1467 
1468         sessionManager->setActive(true);
1469         sessionManager->blocksInteraction();
1470         sessionManager->clearCancellation();
1471 
1472         auto *qGuiAppPriv = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(qApp));
1473         qGuiAppPriv->commitData();
1474 
1475         if (lParam & ENDSESSION_LOGOFF)
1476             fflush(nullptr);
1477 
1478         *result = sessionManager->wasCanceled() ? 0 : 1;
1479         return true;
1480     }
1481     case QtWindows::EndSessionApplicationEvent: {
1482         QWindowsSessionManager *sessionManager = platformSessionManager();
1483 
1484         sessionManager->setActive(false);
1485         sessionManager->allowsInteraction();
1486         const bool endsession = wParam != 0;
1487 
1488         // we receive the message for each toplevel window included internal hidden ones,
1489         // but the aboutToQuit signal should be emitted only once.
1490         auto *qGuiAppPriv = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(qApp));
1491         if (endsession && !qGuiAppPriv->aboutToQuitEmitted) {
1492             qGuiAppPriv->aboutToQuitEmitted = true;
1493             int index = QGuiApplication::staticMetaObject.indexOfSignal("aboutToQuit()");
1494             qApp->qt_metacall(QMetaObject::InvokeMetaMethod, index, nullptr);
1495             // since the process will be killed immediately quit() has no real effect
1496             QGuiApplication::quit();
1497         }
1498         return true;
1499     }
1500 #endif // !defined(QT_NO_SESSIONMANAGER)
1501     default:
1502         break;
1503     }
1504     return false;
1505 }
1506 
1507 /* Compress activation events. If the next focus window is already known
1508  * at the time the current one receives focus-out, pass that to
1509  * QWindowSystemInterface instead of sending 0 and ignore its consecutive
1510  * focus-in event.
1511  * This helps applications that do handling in focus-out events. */
handleFocusEvent(QtWindows::WindowsEventType et,QWindowsWindow * platformWindow)1512 void QWindowsContext::handleFocusEvent(QtWindows::WindowsEventType et,
1513                                        QWindowsWindow *platformWindow)
1514 {
1515     QWindow *nextActiveWindow = nullptr;
1516     if (et == QtWindows::FocusInEvent) {
1517         QWindow *topWindow = QWindowsWindow::topLevelOf(platformWindow->window());
1518         QWindow *modalWindow = nullptr;
1519         if (QGuiApplicationPrivate::instance()->isWindowBlocked(topWindow, &modalWindow) && topWindow != modalWindow) {
1520             modalWindow->requestActivate();
1521             return;
1522         }
1523         // QTBUG-32867: Invoking WinAPI SetParent() can cause focus-in for the
1524         // window which is not desired for native child widgets.
1525         if (platformWindow->testFlag(QWindowsWindow::WithinSetParent)) {
1526             QWindow *currentFocusWindow = QGuiApplication::focusWindow();
1527             if (currentFocusWindow && currentFocusWindow != platformWindow->window()) {
1528                 currentFocusWindow->requestActivate();
1529                 return;
1530             }
1531         }
1532         nextActiveWindow = platformWindow->window();
1533     } else {
1534         // Focus out: Is the next window known and different
1535         // from the receiving the focus out.
1536         if (const HWND nextActiveHwnd = GetFocus())
1537             if (QWindowsWindow *nextActivePlatformWindow = findClosestPlatformWindow(nextActiveHwnd))
1538                 if (nextActivePlatformWindow != platformWindow)
1539                     nextActiveWindow = nextActivePlatformWindow->window();
1540     }
1541     if (nextActiveWindow != d->m_lastActiveWindow) {
1542          d->m_lastActiveWindow = nextActiveWindow;
1543          QWindowSystemInterface::handleWindowActivated(nextActiveWindow);
1544     }
1545 }
1546 
1547 #ifndef QT_NO_CONTEXTMENU
handleContextMenuEvent(QWindow * window,const MSG & msg)1548 bool QWindowsContext::handleContextMenuEvent(QWindow *window, const MSG &msg)
1549 {
1550     bool mouseTriggered = false;
1551     QPoint globalPos;
1552     QPoint pos;
1553     if (msg.lParam != int(0xffffffff)) {
1554         mouseTriggered = true;
1555         globalPos.setX(msg.pt.x);
1556         globalPos.setY(msg.pt.y);
1557         pos = QWindowsGeometryHint::mapFromGlobal(msg.hwnd, globalPos);
1558 
1559         RECT clientRect;
1560         if (GetClientRect(msg.hwnd, &clientRect)) {
1561             if (pos.x() < clientRect.left || pos.x() >= clientRect.right ||
1562                 pos.y() < clientRect.top || pos.y() >= clientRect.bottom)
1563             {
1564                 // This is the case that user has right clicked in the window's caption,
1565                 // We should call DefWindowProc() to display a default shortcut menu
1566                 // instead of sending a Qt window system event.
1567                 return false;
1568             }
1569         }
1570     }
1571 
1572     QWindowSystemInterface::handleContextMenuEvent(window, mouseTriggered, pos, globalPos,
1573                                                    QWindowsKeyMapper::queryKeyboardModifiers());
1574     return true;
1575 }
1576 #endif
1577 
handleExitSizeMove(QWindow * window)1578 void QWindowsContext::handleExitSizeMove(QWindow *window)
1579 {
1580     // Windows can be moved/resized by:
1581     // 1) User moving a window by dragging the title bar: Causes a sequence
1582     //    of WM_NCLBUTTONDOWN, WM_NCMOUSEMOVE but no WM_NCLBUTTONUP,
1583     //    leaving the left mouse button 'pressed'
1584     // 2) User choosing Resize/Move from System menu and using mouse/cursor keys:
1585     //    No mouse events are received
1586     // 3) Programmatically via QSizeGrip calling QPlatformWindow::startSystemResize/Move():
1587     //    Mouse is left in pressed state after press on size grip (inside window),
1588     //    no further mouse events are received
1589     // For cases 1,3, intercept WM_EXITSIZEMOVE to sync the buttons.
1590     const Qt::MouseButtons currentButtons = QWindowsMouseHandler::queryMouseButtons();
1591     const Qt::MouseButtons appButtons = QGuiApplication::mouseButtons();
1592     if (currentButtons == appButtons)
1593         return;
1594     const Qt::KeyboardModifiers keyboardModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
1595     const QPoint globalPos = QWindowsCursor::mousePosition();
1596     const QPlatformWindow *platWin = window->handle();
1597     const QPoint localPos = platWin->mapFromGlobal(globalPos);
1598     const QEvent::Type type = platWin->geometry().contains(globalPos)
1599         ? QEvent::MouseButtonRelease : QEvent::NonClientAreaMouseButtonRelease;
1600     for (Qt::MouseButton button : {Qt::LeftButton, Qt::RightButton, Qt::MiddleButton}) {
1601         if (appButtons.testFlag(button) && !currentButtons.testFlag(button)) {
1602             QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos,
1603                                                      currentButtons, button, type,
1604                                                      keyboardModifiers);
1605         }
1606     }
1607     if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer)
1608         d->m_pointerHandler.clearEvents();
1609     else
1610         d->m_mouseHandler.clearEvents();
1611 }
1612 
asyncExpose() const1613 bool QWindowsContext::asyncExpose() const
1614 {
1615     return d->m_asyncExpose;
1616 }
1617 
setAsyncExpose(bool value)1618 void QWindowsContext::setAsyncExpose(bool value)
1619 {
1620     d->m_asyncExpose = value;
1621 }
1622 
touchDevice() const1623 QTouchDevice *QWindowsContext::touchDevice() const
1624 {
1625     return (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) ?
1626         d->m_pointerHandler.touchDevice() : d->m_mouseHandler.touchDevice();
1627 }
1628 
readAdvancedExplorerSettings(const wchar_t * subKey,DWORD defaultValue)1629 DWORD QWindowsContext::readAdvancedExplorerSettings(const wchar_t *subKey, DWORD defaultValue)
1630 {
1631     const auto value =
1632         QWinRegistryKey(HKEY_CURRENT_USER,
1633                         LR"(Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced)")
1634                        .dwordValue(subKey);
1635     return value.second ? value.first : defaultValue;
1636 }
1637 
isEmptyRect(const RECT & rect)1638 static inline bool isEmptyRect(const RECT &rect)
1639 {
1640     return rect.right - rect.left == 0 && rect.bottom - rect.top == 0;
1641 }
1642 
marginsFromRects(const RECT & frame,const RECT & client)1643 static inline QMargins marginsFromRects(const RECT &frame, const RECT &client)
1644 {
1645     return QMargins(client.left - frame.left, client.top - frame.top,
1646                     frame.right - client.right, frame.bottom - client.bottom);
1647 }
1648 
rectFromNcCalcSize(UINT message,WPARAM wParam,LPARAM lParam,int n)1649 static RECT rectFromNcCalcSize(UINT message, WPARAM wParam, LPARAM lParam, int n)
1650 {
1651     RECT result = {0, 0, 0, 0};
1652     if (message == WM_NCCALCSIZE && wParam)
1653         result = reinterpret_cast<const NCCALCSIZE_PARAMS *>(lParam)->rgrc[n];
1654     return result;
1655 }
1656 
isMinimized(HWND hwnd)1657 static inline bool isMinimized(HWND hwnd)
1658 {
1659     WINDOWPLACEMENT windowPlacement;
1660     windowPlacement.length = sizeof(WINDOWPLACEMENT);
1661     return GetWindowPlacement(hwnd, &windowPlacement) && windowPlacement.showCmd == SW_SHOWMINIMIZED;
1662 }
1663 
isTopLevel(HWND hwnd)1664 static inline bool isTopLevel(HWND hwnd)
1665 {
1666     return (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CHILD) == 0;
1667 }
1668 
1669 /*!
1670     \brief Windows functions for actual windows.
1671 
1672     There is another one for timers, sockets, etc in
1673     QEventDispatcherWin32.
1674 
1675 */
1676 
qWindowsWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)1677 extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1678 {
1679     LRESULT result;
1680     const QtWindows::WindowsEventType et = windowsEventType(message, wParam, lParam);
1681     QWindowsWindow *platformWindow = nullptr;
1682     const RECT ncCalcSizeFrame = rectFromNcCalcSize(message, wParam, lParam, 0);
1683     const bool handled = QWindowsContext::instance()->windowsProc(hwnd, message, et, wParam, lParam, &result, &platformWindow);
1684     if (QWindowsContext::verbose > 1 && lcQpaEvents().isDebugEnabled()) {
1685         if (const char *eventName = QWindowsGuiEventDispatcher::windowsMessageName(message)) {
1686             qCDebug(lcQpaEvents).nospace() << "EVENT: hwd=" << hwnd << ' ' << eventName
1687                 << " msg=0x" << Qt::hex << message << " et=0x" << et << Qt::dec << " wp="
1688                 << int(wParam) << " at " << GET_X_LPARAM(lParam) << ','
1689                 << GET_Y_LPARAM(lParam) << " handled=" << handled;
1690         }
1691     }
1692     if (!handled)
1693         result = DefWindowProc(hwnd, message, wParam, lParam);
1694 
1695     // Capture WM_NCCALCSIZE on top level windows and obtain the window margins by
1696     // subtracting the rectangles before and after processing. This will correctly
1697     // capture client code overriding the message and allow for per-monitor margins
1698     // for High DPI (QTBUG-53255, QTBUG-40578).
1699     if (message == WM_NCCALCSIZE && !isEmptyRect(ncCalcSizeFrame) && isTopLevel(hwnd) && !isMinimized(hwnd)) {
1700         const QMargins margins =
1701             marginsFromRects(ncCalcSizeFrame, rectFromNcCalcSize(message, wParam, lParam, 0));
1702         if (margins.left() >= 0) {
1703             if (platformWindow) {
1704                 qCDebug(lcQpaWindows) << __FUNCTION__ << "WM_NCCALCSIZE for" << hwnd << margins;
1705                 platformWindow->setFullFrameMargins(margins);
1706             } else {
1707                 const QSharedPointer<QWindowCreationContext> ctx = QWindowsContext::instance()->windowCreationContext();
1708                 if (!ctx.isNull())
1709                     ctx->margins = margins;
1710             }
1711         }
1712     }
1713     return result;
1714 }
1715 
1716 
nativeEventType()1717 static inline QByteArray nativeEventType() { return QByteArrayLiteral("windows_generic_MSG"); }
1718 
1719 // Send to QAbstractEventDispatcher
filterNativeEvent(MSG * msg,LRESULT * result)1720 bool QWindowsContext::filterNativeEvent(MSG *msg, LRESULT *result)
1721 {
1722     QAbstractEventDispatcher *dispatcher = QAbstractEventDispatcher::instance();
1723 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1724     qintptr filterResult = 0;
1725 #else
1726     long filterResult = 0;
1727 #endif
1728     if (dispatcher && dispatcher->filterNativeEvent(nativeEventType(), msg, &filterResult)) {
1729         *result = LRESULT(filterResult);
1730         return true;
1731     }
1732     return false;
1733 }
1734 
1735 // Send to QWindowSystemInterface
filterNativeEvent(QWindow * window,MSG * msg,LRESULT * result)1736 bool QWindowsContext::filterNativeEvent(QWindow *window, MSG *msg, LRESULT *result)
1737 {
1738 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1739     qintptr filterResult = 0;
1740 #else
1741     long filterResult = 0;
1742 #endif
1743     if (QWindowSystemInterface::handleNativeEvent(window, nativeEventType(), msg, &filterResult)) {
1744         *result = LRESULT(filterResult);
1745         return true;
1746     }
1747     return false;
1748 }
1749 
1750 QT_END_NAMESPACE
1751