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