Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwindowscontext.cpp
Go to the documentation of this file.
1// Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch>
2// Copyright (C) 2016 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
12#include "qwindowsmenu.h"
15#if QT_CONFIG(tabletevent)
16# include "qwindowstabletsupport.h"
17#endif
18#include "qwindowstheme.h"
19#include <private/qguiapplication_p.h>
20#if QT_CONFIG(accessibility)
21# include "uiautomation/qwindowsuiaaccessibility.h"
22#endif
23#if QT_CONFIG(sessionmanager)
24# include <private/qsessionmanager_p.h>
25# include "qwindowssessionmanager.h"
26#endif
27#include "qwindowsscreen.h"
28#include "qwindowstheme.h"
29
30#include <QtGui/qwindow.h>
31#include <qpa/qwindowsysteminterface.h>
32#include <qpa/qwindowsysteminterface_p.h>
33#include <qpa/qplatformnativeinterface.h>
34#include <QtGui/qguiapplication.h>
35#include <QtGui/qopenglcontext.h>
36#include <QtGui/qpointingdevice.h>
37
38#include <QtCore/qset.h>
39#include <QtCore/qhash.h>
40#include <QtCore/qlibraryinfo.h>
41#include <QtCore/qstringlist.h>
42#include <QtCore/qdebug.h>
43#include <QtCore/qsysinfo.h>
44#include <QtCore/qscopedpointer.h>
45#include <QtCore/quuid.h>
46#include <QtCore/qscopeguard.h>
47#include <QtCore/private/qwinregistry_p.h>
48#if QT_CONFIG(cpp_winrt)
49# include <QtCore/private/qfactorycacheregistration_p.h>
50#endif
51#include <QtCore/private/qsystemerror_p.h>
52
53#include <QtGui/private/qwindowsguieventdispatcher_p.h>
54#include <QtGui/private/qwindowsthemecache_p.h>
55
56#include <stdlib.h>
57#include <stdio.h>
58#include <windowsx.h>
59#include <dbt.h>
60#include <wtsapi32.h>
61#include <shellscalingapi.h>
62
63QT_BEGIN_NAMESPACE
64
65using namespace Qt::StringLiterals;
66
67Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window")
68Q_LOGGING_CATEGORY(lcQpaEvents, "qt.qpa.events")
69Q_LOGGING_CATEGORY(lcQpaGl, "qt.qpa.gl")
70Q_LOGGING_CATEGORY(lcQpaMime, "qt.qpa.mime")
71Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods")
72Q_LOGGING_CATEGORY(lcQpaDialogs, "qt.qpa.dialogs")
73Q_LOGGING_CATEGORY(lcQpaMenus, "qt.qpa.menus")
74Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
75Q_LOGGING_CATEGORY(lcQpaAccessibility, "qt.qpa.accessibility")
76Q_LOGGING_CATEGORY(lcQpaUiAutomation, "qt.qpa.uiautomation")
77Q_LOGGING_CATEGORY(lcQpaTrayIcon, "qt.qpa.trayicon")
78Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen")
79Q_LOGGING_CATEGORY(lcQpaTheme, "qt.qpa.theme")
80
81int QWindowsContext::verbose = 0;
82
83#if !defined(LANG_SYRIAC)
84# define LANG_SYRIAC 0x5a
85#endif
86
87static inline bool useRTL_Extensions()
88{
89 // Since the IsValidLanguageGroup/IsValidLocale functions always return true on
90 // Vista, check the Keyboard Layouts for enabling RTL.
91 if (const int nLayouts = GetKeyboardLayoutList(0, nullptr)) {
92 QScopedArrayPointer<HKL> lpList(new HKL[nLayouts]);
93 GetKeyboardLayoutList(nLayouts, lpList.data());
94 for (int i = 0; i < nLayouts; ++i) {
95 switch (PRIMARYLANGID((quintptr)lpList[i])) {
96 case LANG_ARABIC:
97 case LANG_HEBREW:
98 case LANG_FARSI:
99 case LANG_SYRIAC:
100 return true;
101 default:
102 break;
103 }
104 }
105 }
106 return false;
107}
108
109#if QT_CONFIG(sessionmanager)
110static inline QWindowsSessionManager *platformSessionManager()
111{
112 auto *guiPrivate = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(qApp));
113 auto *managerPrivate = static_cast<QSessionManagerPrivate*>(QObjectPrivate::get(guiPrivate->session_manager));
114 return static_cast<QWindowsSessionManager *>(managerPrivate->platformSessionManager);
115}
116
117static inline bool sessionManagerInteractionBlocked()
118{
119 return platformSessionManager()->isInteractionBlocked();
120}
121#else // QT_CONFIG(sessionmanager)
122static inline bool sessionManagerInteractionBlocked() { return false; }
123#endif
124
125QWindowsContext *QWindowsContext::m_instance = nullptr;
126
127/*!
128 \class QWindowsContext
129 \brief Singleton container for all relevant information.
130
131 Holds state information formerly stored in \c qapplication_win.cpp.
132
133 \internal
134*/
135
159
161
164{
165 if (m_pointerHandler.touchDevice())
167 m_displayContext = GetDC(nullptr);
168 m_defaultDPI = GetDeviceCaps(m_displayContext, LOGPIXELSY);
171 m_keyMapper.setUseRTLExtensions(true);
172 }
173 if (FAILED(m_oleInitializeResult)) {
174 qWarning() << "QWindowsContext: OleInitialize() failed: "
175 << QSystemError::windowsComString(m_oleInitializeResult);
176 }
177}
178
181{
182#ifdef Q_CC_MSVC
183# pragma warning( disable : 4996 )
184#endif
185 m_instance = this;
186}
187
189{
190#if QT_CONFIG(tabletevent)
191 d->m_tabletSupport.reset(); // Destroy internal window before unregistering classes.
192#endif
193
194 if (d->m_powerNotification)
195 UnregisterPowerSettingNotification(d->m_powerNotification);
196
197 if (d->m_powerDummyWindow)
198 DestroyWindow(d->m_powerDummyWindow);
199
201 theme->destroyThemeChangeWindow();
202
203 d->m_screenManager.destroyWindow();
204
205 unregisterWindowClasses();
206 if (d->m_oleInitializeResult == S_OK || d->m_oleInitializeResult == S_FALSE) {
207#ifdef QT_USE_FACTORY_CACHE_REGISTRATION
208 detail::QWinRTFactoryCacheRegistration::clearAllCaches();
209#endif
210 OleUninitialize();
211 }
212
213 d->m_screenManager.clearScreens(); // Order: Potentially calls back to the windows.
214 if (d->m_displayContext)
215 ReleaseDC(nullptr, d->m_displayContext);
216 m_instance = nullptr;
217}
218
223
224bool QWindowsContext::initTouch(unsigned integrationOptions)
225{
226 if (d->m_systemInfo & QWindowsContext::SI_SupportsTouch)
227 return true;
228 auto touchDevice = d->m_pointerHandler.touchDevice();
229 if (touchDevice.isNull()) {
230 const bool mouseEmulation =
231 (integrationOptions & QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch) == 0;
232 touchDevice = QWindowsPointerHandler::createTouchDevice(mouseEmulation);
233 }
234 if (touchDevice.isNull())
235 return false;
236 d->m_pointerHandler.setTouchDevice(touchDevice);
237 QWindowSystemInterface::registerInputDevice(touchDevice.data());
238
239 d->m_systemInfo |= QWindowsContext::SI_SupportsTouch;
240
241 // A touch device was plugged while the app is running. Register all windows for touch.
243
244 return true;
245}
246
248{
249 if (QGuiApplicationPrivate::is_app_running
250 && (d->m_systemInfo & QWindowsContext::SI_SupportsTouch) != 0) {
251 for (QWindowsWindow *w : std::as_const(d->m_windows))
252 w->registerTouchWindow();
253 }
254}
255
257{
258#if QT_CONFIG(tabletevent)
259 d->m_tabletSupport.reset(QWindowsTabletSupport::create());
260 return true;
261#else
262 return false;
263#endif
264}
265
267{
268#if QT_CONFIG(tabletevent)
269 d->m_tabletSupport.reset();
270 return true;
271#else
272 return false;
273#endif
274}
275
276LRESULT QT_WIN_CALLBACK qWindowsPowerWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
277{
278 if (message != WM_POWERBROADCAST || wParam != PBT_POWERSETTINGCHANGE)
279 return DefWindowProc(hwnd, message, wParam, lParam);
280
281 static bool initialized = false; // ignore the initial change
282 if (!initialized) {
283 initialized = true;
284 return DefWindowProc(hwnd, message, wParam, lParam);
285 }
286
287 auto setting = reinterpret_cast<const POWERBROADCAST_SETTING *>(lParam);
288 if (setting) {
289 auto data = reinterpret_cast<const DWORD *>(&setting->Data);
290 if (*data == 1) {
291 // Repaint the windows when returning from sleeping display mode.
292 const auto tlw = QGuiApplication::topLevelWindows();
293 for (auto w : tlw) {
294 if (w->isVisible() && w->windowState() != Qt::WindowMinimized) {
295 if (auto tw = QWindowsWindow::windowsWindowOf(w)) {
296 if (HWND hwnd = tw->handle()) {
297 InvalidateRect(hwnd, nullptr, false);
298 }
299 }
300 }
301 }
302 }
303 }
304 return DefWindowProc(hwnd, message, wParam, lParam);
305}
306
308{
309 if (d->m_powerNotification)
310 return false;
311
312 d->m_powerDummyWindow = createDummyWindow(QStringLiteral("PowerDummyWindow"), L"QtPowerDummyWindow", qWindowsPowerWindowProc);
313 if (!d->m_powerDummyWindow)
314 return false;
315
316 d->m_powerNotification = RegisterPowerSettingNotification(d->m_powerDummyWindow, &GUID_MONITOR_POWER_ON, DEVICE_NOTIFY_WINDOW_HANDLE);
317 if (!d->m_powerNotification) {
318 DestroyWindow(d->m_powerDummyWindow);
319 d->m_powerDummyWindow = nullptr;
320 return false;
321 }
322 return true;
323}
324
326{
327#if QT_CONFIG(tabletevent)
328 QWindowsTabletSupport::setAbsoluteRange(a);
329#else
330 Q_UNUSED(a);
331#endif
332}
333
335{
336 d->m_keyMapper.setDetectAltGrModifier(a);
337}
338
339[[nodiscard]] static inline QtWindows::DpiAwareness
340 dpiAwarenessContextToQtDpiAwareness(DPI_AWARENESS_CONTEXT context)
341{
342 // IsValidDpiAwarenessContext() will handle the NULL pointer case.
343 if (!IsValidDpiAwarenessContext(context))
344 return QtWindows::DpiAwareness::Invalid;
345 if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED))
346 return QtWindows::DpiAwareness::Unaware_GdiScaled;
347 if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2))
349 if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE))
350 return QtWindows::DpiAwareness::PerMonitor;
351 if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_SYSTEM_AWARE))
352 return QtWindows::DpiAwareness::System;
353 if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_UNAWARE))
354 return QtWindows::DpiAwareness::Unaware;
355 return QtWindows::DpiAwareness::Invalid;
356}
357
358QtWindows::DpiAwareness QWindowsContext::windowDpiAwareness(HWND hwnd)
359{
360 if (!hwnd)
361 return QtWindows::DpiAwareness::Invalid;
362 const auto context = GetWindowDpiAwarenessContext(hwnd);
363 return dpiAwarenessContextToQtDpiAwareness(context);
364}
365
367{
368 // Although we have GetDpiAwarenessContextForProcess(), however,
369 // it's only available on Win10 1903+, which is a little higher
370 // than Qt's minimum supported version (1809), so we can't use it.
371 // Luckily, MS docs said GetThreadDpiAwarenessContext() will also
372 // return the default DPI_AWARENESS_CONTEXT for the process if
373 // SetThreadDpiAwarenessContext() was never called. So we can use
374 // it as an equivalent.
375 const auto context = GetThreadDpiAwarenessContext();
376 return dpiAwarenessContextToQtDpiAwareness(context);
377}
378
379[[nodiscard]] static inline DPI_AWARENESS_CONTEXT
381{
382 switch (dpiAwareness) {
383 case QtWindows::DpiAwareness::Invalid:
384 return nullptr;
385 case QtWindows::DpiAwareness::Unaware:
387 case QtWindows::DpiAwareness::System:
389 case QtWindows::DpiAwareness::PerMonitor:
391 case QtWindows::DpiAwareness::PerMonitorVersion2:
393 case QtWindows::DpiAwareness::Unaware_GdiScaled:
395 }
396 return nullptr;
397}
398
399#ifndef QT_NO_DEBUG_STREAM
400QDebug operator<<(QDebug d, QtWindows::DpiAwareness dpiAwareness)
401{
402 const QDebugStateSaver saver(d);
403 QString message = u"QtWindows::DpiAwareness::"_s;
404 switch (dpiAwareness) {
405 case QtWindows::DpiAwareness::Invalid:
406 message += u"Invalid"_s;
407 break;
408 case QtWindows::DpiAwareness::Unaware:
409 message += u"Unaware"_s;
410 break;
411 case QtWindows::DpiAwareness::System:
412 message += u"System"_s;
413 break;
414 case QtWindows::DpiAwareness::PerMonitor:
415 message += u"PerMonitor"_s;
416 break;
417 case QtWindows::DpiAwareness::PerMonitorVersion2:
418 message += u"PerMonitorVersion2"_s;
419 break;
420 case QtWindows::DpiAwareness::Unaware_GdiScaled:
421 message += u"Unaware_GdiScaled"_s;
422 break;
423 }
424 d.nospace().noquote() << message;
425 return d;
426}
427#endif
428
430{
431 qCDebug(lcQpaWindow) << __FUNCTION__ << dpiAwareness;
432 [[maybe_unused]] const auto updatePMv2Status = qScopeGuard([](){
435 });
436 if (processDpiAwareness() == dpiAwareness)
437 return true;
438 const auto context = qtDpiAwarenessToDpiAwarenessContext(dpiAwareness);
439 if (!IsValidDpiAwarenessContext(context)) {
440 qCWarning(lcQpaWindow) << dpiAwareness << "is not supported by current system.";
441 return false;
442 }
443 if (!SetProcessDpiAwarenessContext(context)) {
444 qCWarning(lcQpaWindow).noquote().nospace()
445 << "SetProcessDpiAwarenessContext() failed: "
446 << QSystemError::windowsString()
447 << "\nQt's default DPI awareness context is "
448 << "DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2. If you know what you "
449 << "are doing, you can overwrite this default using qt.conf "
450 << "(https://doc.qt.io/qt-6/highdpi.html#configuring-windows).";
451 return false;
452 }
453 return true;
454}
455
457{
458 return m_instance;
459}
460
462{
463 return d->m_systemInfo;
464}
465
467{
468 return d->m_keyMapper.useRTLExtensions();
469}
470
471QPlatformKeyMapper *QWindowsContext::keyMapper() const
472{
473 return &d->m_keyMapper;
474}
475
477{
478 return d->m_windows;
479}
480
481QSharedPointer<QWindowCreationContext> QWindowsContext::setWindowCreationContext(const QSharedPointer<QWindowCreationContext> &ctx)
482{
483 const QSharedPointer<QWindowCreationContext> old = d->m_creationContext;
484 d->m_creationContext = ctx;
485 return old;
486}
487
489{
490 return d->m_creationContext;
491}
492
494{
495 return d->m_defaultDPI;
496}
497
499{
500 return d->m_displayContext;
501}
502
504{
505 return d->m_keyMapper.keyGrabber();
506}
507
508void QWindowsContext::setKeyGrabber(QWindow *w)
509{
510 d->m_keyMapper.setKeyGrabber(w);
511}
512
513QString QWindowsContext::classNamePrefix()
514{
515 static QString result;
516 if (result.isEmpty()) {
517 QTextStream str(&result);
518 str << "Qt" << QT_VERSION_MAJOR << QT_VERSION_MINOR << QT_VERSION_PATCH;
519 if (QLibraryInfo::isDebugBuild())
520 str << 'd';
521#ifdef QT_NAMESPACE
522# define xstr(s) str(s)
523# define str(s) #s
524 str << xstr(QT_NAMESPACE);
525# undef str
526# undef xstr
527#endif
528 }
529 return result;
530}
531
532// Window class registering code (from qapplication_win.cpp)
533
534QString QWindowsContext::registerWindowClass(const QWindow *w)
535{
536 Q_ASSERT(w);
537 const Qt::WindowFlags flags = w->flags();
538 const Qt::WindowFlags type = flags & Qt::WindowType_Mask;
539 // Determine style and icon.
540 uint style = CS_DBLCLKS;
541 bool icon = true;
542 // The following will not set CS_OWNDC for any widget window, even if it contains a
543 // QOpenGLWidget or QQuickWidget later on. That cannot be detected at this stage.
544 if (w->surfaceType() == QSurface::OpenGLSurface || (flags & Qt::MSWindowsOwnDC))
545 style |= CS_OWNDC;
546 if (!(flags & Qt::NoDropShadowWindowHint)
547 && (type == Qt::Popup || w->property("_q_windowsDropShadow").toBool())) {
548 style |= CS_DROPSHADOW;
549 }
550 switch (type) {
551 case Qt::Tool:
552 case Qt::ToolTip:
553 case Qt::Popup:
554 style |= CS_SAVEBITS; // Save/restore background
555 icon = false;
556 break;
557 case Qt::Dialog:
558 if (!(flags & Qt::WindowSystemMenuHint))
559 icon = false; // QTBUG-2027, dialogs without system menu.
560 break;
561 }
562 // Create a unique name for the flag combination
563 QString cname = classNamePrefix();
564 cname += "QWindow"_L1;
565 switch (type) {
566 case Qt::Tool:
567 cname += "Tool"_L1;
568 break;
569 case Qt::ToolTip:
570 cname += "ToolTip"_L1;
571 break;
572 case Qt::Popup:
573 cname += "Popup"_L1;
574 break;
575 default:
576 break;
577 }
578 if (style & CS_DROPSHADOW)
579 cname += "DropShadow"_L1;
580 if (style & CS_SAVEBITS)
581 cname += "SaveBits"_L1;
582 if (style & CS_OWNDC)
583 cname += "OwnDC"_L1;
584 if (icon)
585 cname += "Icon"_L1;
586
587 return registerWindowClass(cname, qWindowsWndProc, style, nullptr, icon);
588}
589
591 WNDPROC proc,
592 unsigned style,
593 HBRUSH brush,
594 bool icon)
595{
596 // since multiple Qt versions can be used in one process
597 // each one has to have window class names with a unique name
598 // The first instance gets the unmodified name; if the class
599 // has already been registered by another instance of Qt then
600 // add a UUID. The check needs to be performed for each name
601 // in case new message windows are added (QTBUG-81347).
602 // Note: GetClassInfo() returns != 0 when a class exists.
603 const auto appInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr));
604 WNDCLASS wcinfo;
605 const bool classExists = GetClassInfo(appInstance, reinterpret_cast<LPCWSTR>(cname.utf16()), &wcinfo) != FALSE
606 && wcinfo.lpfnWndProc != proc;
607
608 if (classExists)
609 cname += QUuid::createUuid().toString();
610
611 if (d->m_registeredWindowClassNames.contains(cname)) // already registered in our list
612 return cname;
613
614 WNDCLASSEX wc;
615 wc.cbSize = sizeof(WNDCLASSEX);
616 wc.style = style;
617 wc.lpfnWndProc = proc;
618 wc.cbClsExtra = 0;
619 wc.cbWndExtra = 0;
620 wc.hInstance = appInstance;
621 wc.hCursor = nullptr;
622 wc.hbrBackground = brush;
623 if (icon) {
624 wc.hIcon = static_cast<HICON>(LoadImage(appInstance, L"IDI_ICON1", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE));
625 if (wc.hIcon) {
626 int sw = GetSystemMetrics(SM_CXSMICON);
627 int sh = GetSystemMetrics(SM_CYSMICON);
628 wc.hIconSm = static_cast<HICON>(LoadImage(appInstance, L"IDI_ICON1", IMAGE_ICON, sw, sh, 0));
629 } else {
630 wc.hIcon = static_cast<HICON>(LoadImage(nullptr, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED));
631 wc.hIconSm = nullptr;
632 }
633 } else {
634 wc.hIcon = nullptr;
635 wc.hIconSm = nullptr;
636 }
637
638 wc.lpszMenuName = nullptr;
639 wc.lpszClassName = reinterpret_cast<LPCWSTR>(cname.utf16());
640 ATOM atom = RegisterClassEx(&wc);
641 if (!atom)
642 qErrnoWarning("QApplication::regClass: Registering window class '%s' failed.",
643 qPrintable(cname));
644
645 d->m_registeredWindowClassNames.insert(cname);
646 qCDebug(lcQpaWindow).nospace() << __FUNCTION__ << ' ' << cname
647 << " style=0x" << Qt::hex << style << Qt::dec
648 << " brush=" << brush << " icon=" << icon << " atom=" << atom;
649 return cname;
650}
651
652void QWindowsContext::unregisterWindowClasses()
653{
654 const auto appInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr));
655
656 for (const QString &name : std::as_const(d->m_registeredWindowClassNames)) {
657 if (!UnregisterClass(reinterpret_cast<LPCWSTR>(name.utf16()), appInstance) && QWindowsContext::verbose)
658 qErrnoWarning("UnregisterClass failed for '%s'", qPrintable(name));
659 }
660 d->m_registeredWindowClassNames.clear();
661}
662
664{
665 return GetDeviceCaps(d->m_displayContext, BITSPIXEL);
666}
667
669{
670 d->m_windows.insert(hwnd, w);
671}
672
674{
675 const HandleBaseWindowHash::iterator it = d->m_windows.find(hwnd);
676 if (it != d->m_windows.end()) {
677 if (d->m_keyMapper.keyGrabber() == it.value()->window())
678 d->m_keyMapper.setKeyGrabber(nullptr);
679 d->m_windows.erase(it);
680 }
681}
682
684{
685 for (auto it = d->m_windows.cbegin(), end = d->m_windows.cend(); it != end; ++it) {
686 if ((*it)->menuBar() == mb)
687 return *it;
688 }
689 return nullptr;
690}
691
693{
694 return d->m_windows.value(hwnd);
695}
696
698{
699 QWindowsWindow *window = d->m_windows.value(hwnd);
700
701 // Requested hwnd may also be a child of a platform window in case of embedded native windows.
702 // Find the closest parent that has a platform window.
703 if (!window) {
704 for (HWND w = hwnd; w; w = GetParent(w)) {
705 window = d->m_windows.value(w);
706 if (window)
707 break;
708 }
709 }
710
711 return window;
712}
713
714QWindow *QWindowsContext::findWindow(HWND hwnd) const
715{
716 if (const QWindowsWindow *bw = findPlatformWindow(hwnd))
717 return bw->window();
718 return nullptr;
719}
720
722{
723 return d->m_pointerHandler.windowUnderMouse();
724}
725
727{
728 d->m_pointerHandler.clearWindowUnderMouse();
729}
730
731/*!
732 \brief Find a child window at a screen point.
733
734 Deep search for a QWindow at global point, skipping non-owned
735 windows (accessibility?). Implemented using ChildWindowFromPointEx()
736 instead of (historically used) WindowFromPoint() to get a well-defined
737 behaviour for hidden/transparent windows.
738
739 \a cwex_flags are flags of ChildWindowFromPointEx().
740 \a parent is the parent window, pass GetDesktopWindow() for top levels.
741*/
742
743static inline bool findPlatformWindowHelper(const POINT &screenPoint, unsigned cwexFlags,
744 const QWindowsContext *context,
745 HWND *hwnd, QWindowsWindow **result)
746{
747 POINT point = screenPoint;
748 screenToClient(*hwnd, &point);
749 // Returns parent if inside & none matched.
750 const HWND child = ChildWindowFromPointEx(*hwnd, point, cwexFlags);
751 if (!child || child == *hwnd)
752 return false;
753 if (QWindowsWindow *window = context->findPlatformWindow(child)) {
754 *result = window;
755 *hwnd = child;
756 return true;
757 }
758 // QTBUG-40555: despite CWP_SKIPINVISIBLE, it is possible to hit on invisible
759 // full screen windows of other applications that have WS_EX_TRANSPARENT set
760 // (for example created by screen sharing applications). In that case, try to
761 // find a Qt window by searching again with CWP_SKIPTRANSPARENT.
762 // Note that Qt 5 uses WS_EX_TRANSPARENT for Qt::WindowTransparentForInput
763 // as well.
764 if (!(cwexFlags & CWP_SKIPTRANSPARENT)
765 && (GetWindowLongPtr(child, GWL_EXSTYLE) & WS_EX_TRANSPARENT)) {
766 const HWND nonTransparentChild = ChildWindowFromPointEx(*hwnd, point, cwexFlags | CWP_SKIPTRANSPARENT);
767 if (!nonTransparentChild || nonTransparentChild == *hwnd)
768 return false;
769 if (QWindowsWindow *nonTransparentWindow = context->findPlatformWindow(nonTransparentChild)) {
770 *result = nonTransparentWindow;
771 *hwnd = nonTransparentChild;
772 return true;
773 }
774 }
775 *hwnd = child;
776 return true;
777}
778
780 const QPoint &screenPointIn,
781 unsigned cwex_flags) const
782{
783 QWindowsWindow *result = nullptr;
784 const POINT screenPoint = { screenPointIn.x(), screenPointIn.y() };
785 while (findPlatformWindowHelper(screenPoint, cwex_flags, this, &parent, &result)) {}
786 // QTBUG-40815: ChildWindowFromPointEx() can hit on special windows from
787 // screen recorder applications like ScreenToGif. Fall back to WindowFromPoint().
788 if (result == nullptr) {
789 if (const HWND window = WindowFromPoint(screenPoint))
790 result = findPlatformWindow(window);
791 }
792 return result;
793}
794
796{
797 bool result = false;
798 const DWORD sessionId = WTSGetActiveConsoleSessionId();
799 if (sessionId != 0xFFFFFFFF) {
800 LPTSTR buffer = nullptr;
801 DWORD size = 0;
802#if !defined(Q_CC_MINGW)
803 if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, sessionId,
804 WTSSessionInfoEx, &buffer, &size) == TRUE
805 && size > 0) {
806 const WTSINFOEXW *info = reinterpret_cast<WTSINFOEXW *>(buffer);
807 result = info->Level == 1 && info->Data.WTSInfoExLevel1.SessionFlags == WTS_SESSIONSTATE_LOCK;
808 WTSFreeMemory(buffer);
809 }
810#else // MinGW as of 7.3 does not have WTSINFOEXW in wtsapi32.h
811 // Retrieve the flags which are at offset 16 due to padding for 32/64bit alike.
812 if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, sessionId,
813 WTS_INFO_CLASS(25), &buffer, &size) == TRUE
814 && size >= 20) {
815 const DWORD *p = reinterpret_cast<DWORD *>(buffer);
816 const DWORD level = *p;
817 const DWORD sessionFlags = *(p + 4);
818 result = level == 1 && sessionFlags == 1;
819 WTSFreeMemory(buffer);
820 }
821#endif // Q_CC_MINGW
822 }
823 return result;
824}
825
827{
828 return d->m_mimeConverter;
829}
830
832{
833 return d->m_screenManager;
834}
835
837{
838#if QT_CONFIG(tabletevent)
839 return d->m_tabletSupport.data();
840#else
841 return 0;
842#endif
843}
844
845/*!
846 \brief Convenience to create a non-visible, message-only dummy
847 window for example used as clipboard watcher or for GL.
848*/
849
850HWND QWindowsContext::createDummyWindow(const QString &classNameIn,
851 const wchar_t *windowName,
852 WNDPROC wndProc, DWORD style)
853{
854 if (!wndProc)
855 wndProc = DefWindowProc;
856 QString className = registerWindowClass(classNamePrefix() + classNameIn, wndProc);
857 return CreateWindowEx(0, reinterpret_cast<LPCWSTR>(className.utf16()),
858 windowName, style,
859 CW_USEDEFAULT, CW_USEDEFAULT,
860 CW_USEDEFAULT, CW_USEDEFAULT,
861 HWND_MESSAGE, nullptr, static_cast<HINSTANCE>(GetModuleHandle(nullptr)), nullptr);
862}
863
864void QWindowsContext::forceNcCalcSize(HWND hwnd)
865{
866 // Force WM_NCCALCSIZE to adjust margin
867 SetWindowPos(hwnd, nullptr, 0, 0, 0, 0,
868 SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
869}
870
871bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void *out,
872 unsigned dpi)
873{
874 const BOOL result = dpi != 0
875 ? SystemParametersInfoForDpi(action, param, out, 0, dpi)
876 : SystemParametersInfo(action, param, out, 0);
877 return result == TRUE;
878}
879
880bool QWindowsContext::systemParametersInfoForScreen(unsigned action, unsigned param, void *out,
881 const QPlatformScreen *screen)
882{
883 return systemParametersInfo(action, param, out, screen ? unsigned(screen->logicalDpi().first) : 0u);
884}
885
886bool QWindowsContext::systemParametersInfoForWindow(unsigned action, unsigned param, void *out,
887 const QPlatformWindow *win)
888{
889 return systemParametersInfoForScreen(action, param, out, win ? win->screen() : nullptr);
890}
891
892bool QWindowsContext::nonClientMetrics(NONCLIENTMETRICS *ncm, unsigned dpi)
893{
894 memset(ncm, 0, sizeof(NONCLIENTMETRICS));
895 ncm->cbSize = sizeof(NONCLIENTMETRICS);
896 return systemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm->cbSize, ncm, dpi);
897}
898
899bool QWindowsContext::nonClientMetricsForScreen(NONCLIENTMETRICS *ncm,
900 const QPlatformScreen *screen)
901{
902 const int dpi = screen ? qRound(screen->logicalDpi().first) : 0;
903 return nonClientMetrics(ncm, unsigned(dpi));
904}
905
906bool QWindowsContext::nonClientMetricsForWindow(NONCLIENTMETRICS *ncm, const QPlatformWindow *win)
907{
908 return nonClientMetricsForScreen(ncm, win ? win->screen() : nullptr);
909}
910
912{
913 return qobject_cast<QWindowsInputContext *>(QWindowsIntegration::instance()->inputContext());
914}
915
916bool QWindowsContext::shouldHaveNonClientDpiScaling(const QWindow *window)
917{
918 // DPI aware V2 processes always have NonClientDpiScaling enabled.
920 return true;
921
922 return window->isTopLevel()
923 && !window->property(QWindowsWindow::embeddedNativeParentHandleProperty).isValid()
924#if QT_CONFIG(opengl) // /QTBUG-62901, EnableNonClientDpiScaling has problems with GL
925 && (window->surfaceType() != QSurface::OpenGLSurface
926 || QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL)
927#endif
928 ;
929}
930
931static inline bool isInputMessage(UINT m)
932{
933 switch (m) {
934 case WM_IME_STARTCOMPOSITION:
935 case WM_IME_ENDCOMPOSITION:
936 case WM_IME_COMPOSITION:
937 case WM_INPUT:
938 case WM_TOUCH:
939 case WM_MOUSEHOVER:
940 case WM_MOUSELEAVE:
941 case WM_NCMOUSEHOVER:
942 case WM_NCMOUSELEAVE:
943 case WM_SIZING:
944 case WM_MOVING:
945 case WM_SYSCOMMAND:
946 case WM_COMMAND:
947 case WM_DWMNCRENDERINGCHANGED:
948 case WM_PAINT:
949 return true;
950 default:
951 break;
952 }
953 return (m >= WM_MOUSEFIRST && m <= WM_MOUSELAST)
954 || (m >= WM_NCMOUSEMOVE && m <= WM_NCXBUTTONDBLCLK)
955 || (m >= WM_KEYFIRST && m <= WM_KEYLAST);
956}
957
958// Note: This only works within WM_NCCREATE
959static bool enableNonClientDpiScaling(HWND hwnd)
960{
961 bool result = false;
962 if (QWindowsContext::windowDpiAwareness(hwnd) == QtWindows::DpiAwareness::PerMonitor) {
963 result = EnableNonClientDpiScaling(hwnd) != FALSE;
964 if (!result) {
965 const DWORD errorCode = GetLastError();
966 qErrnoWarning(int(errorCode), "EnableNonClientDpiScaling() failed for HWND %p (%lu)",
967 hwnd, errorCode);
968 }
969 }
970 return result;
971}
972
973/*!
974 \brief Main windows procedure registered for windows.
975
976 \sa QWindowsGuiEventDispatcher
977*/
978
979bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
980 QtWindows::WindowsEventType et,
981 WPARAM wParam, LPARAM lParam,
982 LRESULT *result,
983 QWindowsWindow **platformWindowPtr)
984{
985 *result = 0;
986
987 MSG msg;
988 msg.hwnd = hwnd; // re-create MSG structure
989 msg.message = message;
990 msg.wParam = wParam;
991 msg.lParam = lParam;
992 msg.time = GetMessageTime();
993 msg.pt.x = msg.pt.y = 0;
994 if (et != QtWindows::CursorEvent && (et & (QtWindows::MouseEventFlag | QtWindows::NonClientEventFlag))) {
995 msg.pt.x = GET_X_LPARAM(lParam);
996 msg.pt.y = GET_Y_LPARAM(lParam);
997 // For non-client-area messages, these are screen coordinates (as expected
998 // in the MSG structure), otherwise they are client coordinates.
999 if (!(et & QtWindows::NonClientEventFlag)) {
1000 clientToScreen(msg.hwnd, &msg.pt);
1001 }
1002 } else {
1003 GetCursorPos(&msg.pt);
1004 }
1005
1006 QWindowsWindow *platformWindow = findPlatformWindow(hwnd);
1007 *platformWindowPtr = platformWindow;
1008
1009 // Run the native event filters. QTBUG-67095: Exclude input messages which are sent
1010 // by QEventDispatcherWin32::processEvents()
1011 if (!isInputMessage(msg.message) && filterNativeEvent(&msg, result))
1012 return true;
1013
1014 if (platformWindow && filterNativeEvent(platformWindow->window(), &msg, result))
1015 return true;
1016
1017 if (et & QtWindows::InputMethodEventFlag) {
1018 QWindowsInputContext *windowsInputContext = ::windowsInputContext();
1019 // Disable IME assuming this is a special implementation hooking into keyboard input.
1020 // "Real" IME implementations should use a native event filter intercepting IME events.
1021 if (!windowsInputContext) {
1023 return false;
1024 }
1025 switch (et) {
1027 return windowsInputContext->startComposition(hwnd);
1028 case QtWindows::InputMethodCompositionEvent:
1029 return windowsInputContext->composition(hwnd, lParam);
1031 return windowsInputContext->endComposition(hwnd);
1032 case QtWindows::InputMethodRequest:
1033 return windowsInputContext->handleIME_Request(wParam, lParam, result);
1034 default:
1035 break;
1036 }
1037 } // InputMethodEventFlag
1038
1039 switch (et) {
1040 case QtWindows::GestureEvent:
1041 // TODO???
1042 break;
1045 // TODO: Release/regrab mouse if a popup has mouse grab.
1046 return false;
1047 case QtWindows::DestroyEvent:
1048 if (platformWindow && !platformWindow->testFlag(QWindowsWindow::WithinDestroy)) {
1049 qWarning() << "External WM_DESTROY received for " << platformWindow->window()
1050 << ", parent: " << platformWindow->window()->parent()
1051 << ", transient parent: " << platformWindow->window()->transientParent();
1052 }
1053 return false;
1054 case QtWindows::ClipboardEvent:
1055 return false;
1056 case QtWindows::CursorEvent: // Sent to windows that do not have capture (see QTBUG-58590).
1059 return true;
1060 }
1061 break;
1062 case QtWindows::UnknownEvent:
1063 return false;
1065#if QT_CONFIG(accessibility)
1066 return QWindowsUiaAccessibility::handleWmGetObject(hwnd, wParam, lParam, result);
1067#else
1068 return false;
1069#endif
1070 case QtWindows::SettingChangedEvent: {
1072 return d->m_screenManager.handleScreenChanges();
1073 }
1074 default:
1075 break;
1076 }
1077
1078 // Before CreateWindowEx() returns, some events are sent,
1079 // for example WM_GETMINMAXINFO asking for size constraints for top levels.
1080 // Pass on to current creation context
1081 if (!platformWindow && !d->m_creationContext.isNull()) {
1082 switch (et) {
1083 case QtWindows::QuerySizeHints:
1084 d->m_creationContext->applyToMinMaxInfo(reinterpret_cast<MINMAXINFO *>(lParam));
1085 return true;
1086 case QtWindows::ResizeEvent:
1087 d->m_creationContext->obtainedSize = QSize(LOWORD(lParam), HIWORD(lParam));
1088 return true;
1089 case QtWindows::MoveEvent:
1090 d->m_creationContext->obtainedPos = QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
1091 return true;
1092 case QtWindows::NonClientCreate:
1093 if (shouldHaveNonClientDpiScaling(d->m_creationContext->window) &&
1094 // DPI aware V2 processes always have NonClientDpiScaling enabled
1095 // and there is no need to make an API call to manually enable.
1096 !QWindowsContextPrivate::m_v2DpiAware) {
1097 enableNonClientDpiScaling(msg.hwnd);
1098 }
1099 return false;
1100 case QtWindows::CalculateSize:
1101 return QWindowsGeometryHint::handleCalculateSize(d->m_creationContext->window, d->m_creationContext->customMargins, msg, result);
1102 case QtWindows::GeometryChangingEvent:
1103 return QWindowsWindow::handleGeometryChangingMessage(&msg, d->m_creationContext->window,
1104 d->m_creationContext->margins + d->m_creationContext->customMargins);
1105 default:
1106 break;
1107 }
1108 }
1109 if (platformWindow) {
1110 // Suppress events sent during DestroyWindow() for native children.
1112 return false;
1113 if (QWindowsContext::verbose > 1)
1114 qCDebug(lcQpaEvents) << "Event window: " << platformWindow->window();
1115 } else {
1116 qWarning("%s: No Qt Window found for event 0x%x (%s), hwnd=0x%p.",
1117 __FUNCTION__, message,
1118 QWindowsGuiEventDispatcher::windowsMessageName(message), hwnd);
1119 return false;
1120 }
1121
1122 switch (et) {
1123 case QtWindows::DeviceChangeEvent:
1124 if (d->m_systemInfo & QWindowsContext::SI_SupportsTouch)
1125 break;
1126 // See if there are any touch devices added
1127 if (wParam == DBT_DEVNODES_CHANGED)
1129 break;
1130 case QtWindows::InputLanguageChangeEvent:
1132 wic->handleInputLanguageChanged(wParam, lParam);
1133 Q_FALLTHROUGH();
1134 case QtWindows::KeyDownEvent:
1135 case QtWindows::KeyEvent:
1136 case QtWindows::InputMethodKeyEvent:
1137 case QtWindows::InputMethodKeyDownEvent:
1138 case QtWindows::AppCommandEvent:
1139 return sessionManagerInteractionBlocked() || d->m_keyMapper.translateKeyEvent(platformWindow->window(), hwnd, msg, result);
1140 case QtWindows::MenuAboutToShowEvent:
1142 return true;
1143 if (QWindowsPopupMenu::notifyAboutToShow(reinterpret_cast<HMENU>(wParam)))
1144 return true;
1145 if (platformWindow == nullptr || platformWindow->menuBar() == nullptr)
1146 return false;
1147 return platformWindow->menuBar()->notifyAboutToShow(reinterpret_cast<HMENU>(wParam));
1148 case QtWindows::MenuCommandEvent:
1150 return true;
1151 if (QWindowsPopupMenu::notifyTriggered(LOWORD(wParam)))
1152 return true;
1153 if (platformWindow == nullptr || platformWindow->menuBar() == nullptr)
1154 return false;
1155 return platformWindow->menuBar()->notifyTriggered(LOWORD(wParam));
1156 case QtWindows::MoveEvent:
1157 platformWindow->handleMoved();
1158 return true;
1159 case QtWindows::ResizeEvent: {
1160 QWindow *window = platformWindow->window();
1161 platformWindow->handleResized(static_cast<int>(wParam), lParam);
1162 if (window->flags().testFlags(Qt::ExpandedClientAreaHint))
1163 platformWindow->updateCustomTitlebar();
1164 return true;
1165 }
1166 case QtWindows::QuerySizeHints:
1167 platformWindow->getSizeHints(reinterpret_cast<MINMAXINFO *>(lParam));
1168 return true;// maybe available on some SDKs revisit WM_NCCALCSIZE
1169 case QtWindows::CalculateSize:
1170 return QWindowsGeometryHint::handleCalculateSize(platformWindow->window(), platformWindow->customMargins(), msg, result);
1171 case QtWindows::NonClientHitTest: {
1172 QWindow *window = platformWindow->window();
1173 if (window->flags().testFlags(Qt::ExpandedClientAreaHint))
1174 platformWindow->updateCustomTitlebar();
1175 return platformWindow->handleNonClientHitTest(QPoint(msg.pt.x, msg.pt.y), result);
1176 }
1177 case QtWindows::NonClientActivate:
1178 return platformWindow->handleNonClientActivate(result);
1179 case QtWindows::GeometryChangingEvent:
1180 return platformWindow->handleGeometryChanging(&msg);
1181 case QtWindows::ExposeEvent: {
1182 QWindow *window = platformWindow->window();
1183 if (window->flags().testFlags(Qt::ExpandedClientAreaHint))
1184 platformWindow->updateCustomTitlebar();
1185 return platformWindow->handleWmPaint(hwnd, message, wParam, lParam, result);
1186 }
1187 case QtWindows::NonClientMouseEvent:
1188 if (!platformWindow->frameStrutEventsEnabled())
1189 break;
1190 return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result);
1191 case QtWindows::NonClientPointerEvent:
1192 if (!platformWindow->frameStrutEventsEnabled())
1193 break;
1194 return sessionManagerInteractionBlocked() || d->m_pointerHandler.translatePointerEvent(platformWindow->window(), hwnd, et, msg, result);
1195 case QtWindows::EnterSizeMoveEvent:
1197 if (!IsZoomed(hwnd))
1198 platformWindow->updateRestoreGeometry();
1199 return true;
1200 case QtWindows::ExitSizeMoveEvent:
1202 platformWindow->checkForScreenChanged();
1203 handleExitSizeMove(platformWindow->window());
1204 if (!IsZoomed(hwnd))
1205 platformWindow->updateRestoreGeometry();
1206 return true;
1207 case QtWindows::ScrollEvent:
1208 // TODO???
1209 break;
1210 case QtWindows::MouseWheelEvent:
1211 case QtWindows::MouseEvent:
1212 case QtWindows::LeaveEvent:
1213 {
1214 QWindow *window = platformWindow->window();
1215 while (window && (window->flags() & Qt::WindowTransparentForInput))
1216 window = window->parent();
1217 if (!window)
1218 return false;
1219 return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(window, hwnd, et, msg, result);
1220 }
1221 break;
1222 case QtWindows::TouchEvent:
1223 // TODO???
1224 break;
1225 case QtWindows::PointerEvent:
1226 return sessionManagerInteractionBlocked() || d->m_pointerHandler.translatePointerEvent(platformWindow->window(), hwnd, et, msg, result);
1227 case QtWindows::FocusInEvent: // see QWindowsWindow::requestActivateWindow().
1228 if (platformWindow->window()->flags() & Qt::WindowDoesNotAcceptFocus)
1229 return false;
1230 [[fallthrough]];
1231 case QtWindows::FocusOutEvent:
1232 handleFocusEvent(et, platformWindow);
1233 return true;
1234 case QtWindows::ShowEventOnParentRestoring: // QTBUG-40696, prevent Windows from re-showing hidden transient children (dialogs).
1235 if (!platformWindow->window()->isVisible()) {
1236 *result = 0;
1237 return true;
1238 }
1239 break;
1240 case QtWindows::HideEvent:
1241 platformWindow->handleHidden();
1242 return false;// Indicate transient children should be hidden by windows (SW_PARENTCLOSING)
1243 case QtWindows::CloseEvent:
1244 QWindowSystemInterface::handleCloseEvent(platformWindow->window());
1245 return true;
1246 case QtWindows::ThemeChanged: {
1247 return true;
1248 }
1249 case QtWindows::CompositionSettingsChanged:
1251 return true;
1252 case QtWindows::ActivateWindowEvent:
1253 if (platformWindow->window()->flags() & Qt::WindowDoesNotAcceptFocus) {
1254 *result = LRESULT(MA_NOACTIVATE);
1255 return true;
1256 }
1257#if QT_CONFIG(tabletevent)
1258 if (!d->m_tabletSupport.isNull())
1259 d->m_tabletSupport->notifyActivate();
1260#endif // QT_CONFIG(tabletevent)
1262 if (const QWindow *modalWindow = QGuiApplication::modalWindow()) {
1263 QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(modalWindow);
1264 Q_ASSERT(platformWindow);
1265 platformWindow->alertWindow();
1266 }
1267 break;
1268 case QtWindows::MouseActivateWindowEvent:
1269 case QtWindows::PointerActivateWindowEvent:
1270 if (platformWindow->window()->flags() & Qt::WindowDoesNotAcceptFocus) {
1271 *result = LRESULT(MA_NOACTIVATE);
1272 return true;
1273 }
1274 break;
1275#ifndef QT_NO_CONTEXTMENU
1276 case QtWindows::ContextMenu:
1277 return handleContextMenuEvent(platformWindow->window(), msg);
1278#endif
1279 case QtWindows::WhatsThisEvent: {
1280#ifndef QT_NO_WHATSTHIS
1281 QWindowSystemInterface::handleEnterWhatsThisEvent();
1282 return true;
1283#endif
1284 } break;
1285 case QtWindows::DpiScaledSizeEvent:
1286 platformWindow->handleDpiScaledSize(wParam, lParam, result);
1287 return true;
1288 case QtWindows::DpiChangedEvent:
1289 platformWindow->handleDpiChanged(hwnd, wParam, lParam);
1290 return true;
1291 case QtWindows::DpiChangedAfterParentEvent:
1292 platformWindow->handleDpiChangedAfterParent(hwnd);
1293 return true;
1294#if QT_CONFIG(sessionmanager)
1295 case QtWindows::QueryEndSessionApplicationEvent: {
1296 QWindowsSessionManager *sessionManager = platformSessionManager();
1297 if (sessionManager->isActive()) { // bogus message from windows
1298 *result = sessionManager->wasCanceled() ? 0 : 1;
1299 return true;
1300 }
1301
1302 sessionManager->setActive(true);
1303 sessionManager->blocksInteraction();
1304 sessionManager->clearCancellation();
1305
1306 auto *qGuiAppPriv = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(qApp));
1307 qGuiAppPriv->commitData();
1308
1309 if (lParam & ENDSESSION_LOGOFF)
1310 fflush(nullptr);
1311
1312 *result = sessionManager->wasCanceled() ? 0 : 1;
1313 return true;
1314 }
1315 case QtWindows::EndSessionApplicationEvent: {
1316 QWindowsSessionManager *sessionManager = platformSessionManager();
1317
1318 sessionManager->setActive(false);
1319 sessionManager->allowsInteraction();
1320 const bool endsession = wParam != 0;
1321
1322 // we receive the message for each toplevel window included internal hidden ones,
1323 // but the aboutToQuit signal should be emitted only once.
1324 auto *qGuiAppPriv = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(qApp));
1325 if (endsession && !qGuiAppPriv->aboutToQuitEmitted) {
1326 qGuiAppPriv->aboutToQuitEmitted = true;
1327 int index = QGuiApplication::staticMetaObject.indexOfSignal("aboutToQuit()");
1328 qApp->qt_metacall(QMetaObject::InvokeMetaMethod, index, nullptr);
1329 // since the process will be killed immediately quit() has no real effect
1330 QGuiApplication::quit();
1331 }
1332 return true;
1333 }
1334#endif // !defined(QT_NO_SESSIONMANAGER)
1335 case QtWindows::TaskbarButtonCreated:
1336 // Apply application badge if this is the first time we have a taskbar
1337 // button, or after Explorer restart.
1339 break;
1340 default:
1341 break;
1342 }
1343 return false;
1344}
1345
1346/* Compress activation events. If the next focus window is already known
1347 * at the time the current one receives focus-out, pass that to
1348 * QWindowSystemInterface instead of sending 0 and ignore its consecutive
1349 * focus-in event.
1350 * This helps applications that do handling in focus-out events. */
1351void QWindowsContext::handleFocusEvent(QtWindows::WindowsEventType et,
1352 QWindowsWindow *platformWindow)
1353{
1354 QWindow *nextActiveWindow = nullptr;
1355 if (et == QtWindows::FocusInEvent) {
1356 QWindow *topWindow = QWindowsWindow::topLevelOf(platformWindow->window());
1357 QWindow *modalWindow = nullptr;
1358 if (QGuiApplicationPrivate::instance()->isWindowBlocked(topWindow, &modalWindow) && topWindow != modalWindow) {
1359 modalWindow->requestActivate();
1360 return;
1361 }
1362 // QTBUG-32867: Invoking WinAPI SetParent() can cause focus-in for the
1363 // window which is not desired for native child widgets.
1365 QWindow *currentFocusWindow = QGuiApplication::focusWindow();
1366 if (currentFocusWindow && currentFocusWindow != platformWindow->window()) {
1367 currentFocusWindow->requestActivate();
1368 return;
1369 }
1370 }
1371 nextActiveWindow = platformWindow->window();
1372 } else {
1373 // Focus out: Is the next window known and different
1374 // from the receiving the focus out.
1375 if (const HWND nextActiveHwnd = GetFocus())
1376 if (QWindowsWindow *nextActivePlatformWindow = findClosestPlatformWindow(nextActiveHwnd))
1377 if (nextActivePlatformWindow != platformWindow)
1378 nextActiveWindow = nextActivePlatformWindow->window();
1379 }
1380 if (nextActiveWindow != d->m_lastActiveWindow) {
1381 d->m_lastActiveWindow = nextActiveWindow;
1382 QWindowSystemInterface::handleFocusWindowChanged(nextActiveWindow, Qt::ActiveWindowFocusReason);
1383 }
1384}
1385
1386#ifndef QT_NO_CONTEXTMENU
1387bool QWindowsContext::handleContextMenuEvent(QWindow *window, const MSG &msg)
1388{
1389 bool mouseTriggered = false;
1390 QPoint globalPos;
1391 QPoint pos;
1392 if (msg.lParam != int(0xffffffff)) {
1393 mouseTriggered = true;
1394 globalPos.setX(msg.pt.x);
1395 globalPos.setY(msg.pt.y);
1396 pos = QWindowsGeometryHint::mapFromGlobal(msg.hwnd, globalPos);
1397
1398 RECT clientRect;
1399 if (GetClientRect(msg.hwnd, &clientRect)) {
1400 if (pos.x() < clientRect.left || pos.x() >= clientRect.right ||
1401 pos.y() < clientRect.top || pos.y() >= clientRect.bottom)
1402 {
1403 // This is the case that user has right clicked in the window's caption,
1404 // We should call DefWindowProc() to display a default shortcut menu
1405 // instead of sending a Qt window system event.
1406 return false;
1407 }
1408 }
1409 }
1410
1411 QWindowSystemInterface::handleContextMenuEvent(window, mouseTriggered, pos, globalPos,
1412 keyMapper()->queryKeyboardModifiers());
1413 return true;
1414}
1415#endif
1416
1417void QWindowsContext::handleExitSizeMove(QWindow *window)
1418{
1419 // Windows can be moved/resized by:
1420 // 1) User moving a window by dragging the title bar: Causes a sequence
1421 // of WM_NCLBUTTONDOWN, WM_NCMOUSEMOVE but no WM_NCLBUTTONUP,
1422 // leaving the left mouse button 'pressed'
1423 // 2) User choosing Resize/Move from System menu and using mouse/cursor keys:
1424 // No mouse events are received
1425 // 3) Programmatically via QSizeGrip calling QPlatformWindow::startSystemResize/Move():
1426 // Mouse is left in pressed state after press on size grip (inside window),
1427 // no further mouse events are received
1428 // For cases 1,3, intercept WM_EXITSIZEMOVE to sync the buttons.
1429 const Qt::MouseButtons currentButtons = QWindowsPointerHandler::queryMouseButtons();
1430 const Qt::MouseButtons appButtons = QGuiApplication::mouseButtons();
1431 if (currentButtons == appButtons)
1432 return;
1433 const Qt::KeyboardModifiers keyboardModifiers = keyMapper()->queryKeyboardModifiers();
1434 const QPoint globalPos = QWindowsCursor::mousePosition();
1435 const QPlatformWindow *platWin = window->handle();
1436 const QPoint localPos = platWin->mapFromGlobal(globalPos);
1437 const QEvent::Type type = platWin->geometry().contains(globalPos)
1438 ? QEvent::MouseButtonRelease : QEvent::NonClientAreaMouseButtonRelease;
1439 for (Qt::MouseButton button : {Qt::LeftButton, Qt::RightButton, Qt::MiddleButton}) {
1440 if (appButtons.testFlag(button) && !currentButtons.testFlag(button)) {
1441 QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos,
1442 currentButtons, button, type, keyboardModifiers);
1443 }
1444 }
1445 d->m_pointerHandler.clearEvents();
1446}
1447
1449{
1450 return d->m_asyncExpose;
1451}
1452
1454{
1455 d->m_asyncExpose = value;
1456}
1457
1458DWORD QWindowsContext::readAdvancedExplorerSettings(const wchar_t *subKey, DWORD defaultValue)
1459{
1460 const auto advancedSettings = QWinRegistryKey(
1461 HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced)");
1462 return advancedSettings.value<DWORD>(subKey).value_or(defaultValue);
1463}
1464
1465static inline bool isEmptyRect(const RECT &rect)
1466{
1467 return rect.right - rect.left == 0 && rect.bottom - rect.top == 0;
1468}
1469
1470static inline QMargins marginsFromRects(const RECT &frame, const RECT &client)
1471{
1472 return QMargins(client.left - frame.left, client.top - frame.top,
1473 frame.right - client.right, frame.bottom - client.bottom);
1474}
1475
1476static RECT rectFromNcCalcSize(UINT message, WPARAM wParam, LPARAM lParam, int n)
1477{
1478 RECT result = {0, 0, 0, 0};
1479 if (message == WM_NCCALCSIZE && wParam)
1480 result = reinterpret_cast<const NCCALCSIZE_PARAMS *>(lParam)->rgrc[n];
1481 return result;
1482}
1483
1484static inline bool isMinimized(HWND hwnd)
1485{
1486 WINDOWPLACEMENT windowPlacement;
1487 windowPlacement.length = sizeof(WINDOWPLACEMENT);
1488 return GetWindowPlacement(hwnd, &windowPlacement) && windowPlacement.showCmd == SW_SHOWMINIMIZED;
1489}
1490
1491static inline bool isTopLevel(HWND hwnd)
1492{
1493 return (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CHILD) == 0;
1494}
1495
1496/*!
1497 \brief Windows functions for actual windows.
1498
1499 There is another one for timers, sockets, etc in
1500 QEventDispatcherWin32.
1501
1502*/
1503
1504LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1505{
1506 LRESULT result;
1507 const QtWindows::WindowsEventType et = windowsEventType(message, wParam, lParam);
1508 QWindowsWindow *platformWindow = nullptr;
1509 const RECT ncCalcSizeFrame = rectFromNcCalcSize(message, wParam, lParam, 0);
1510 const bool handled = QWindowsContext::instance()->windowsProc(hwnd, message, et, wParam, lParam, &result, &platformWindow);
1511 if (QWindowsContext::verbose > 1 && lcQpaEvents().isDebugEnabled()) {
1512 if (const char *eventName = QWindowsGuiEventDispatcher::windowsMessageName(message)) {
1513 qCDebug(lcQpaEvents).nospace() << "EVENT: hwd=" << hwnd << ' ' << eventName
1514 << " msg=0x" << Qt::hex << message << " et=0x" << et << Qt::dec << " wp="
1515 << int(wParam) << " at " << GET_X_LPARAM(lParam) << ','
1516 << GET_Y_LPARAM(lParam) << " handled=" << handled;
1517 }
1518 }
1519 if (!handled)
1520 result = DefWindowProc(hwnd, message, wParam, lParam);
1521
1522 // Capture WM_NCCALCSIZE on top level windows and obtain the window margins by
1523 // subtracting the rectangles before and after processing. This will correctly
1524 // capture client code overriding the message and allow for per-monitor margins
1525 // for High DPI (QTBUG-53255, QTBUG-40578).
1526 if (message == WM_NCCALCSIZE && !isEmptyRect(ncCalcSizeFrame) && isTopLevel(hwnd) && !isMinimized(hwnd)) {
1527 const QMargins margins =
1528 marginsFromRects(ncCalcSizeFrame, rectFromNcCalcSize(message, wParam, lParam, 0));
1529 if (margins.left() >= 0) {
1530 if (platformWindow) {
1531 qCDebug(lcQpaWindow) << __FUNCTION__ << "WM_NCCALCSIZE for" << hwnd << margins;
1532 platformWindow->setFullFrameMargins(margins);
1533 } else {
1534 const QSharedPointer<QWindowCreationContext> ctx = QWindowsContext::instance()->windowCreationContext();
1535 if (!ctx.isNull())
1536 ctx->margins = margins;
1537 }
1538 }
1539 }
1540 return result;
1541}
1542
1543
1544static inline QByteArray nativeEventType() { return QByteArrayLiteral("windows_generic_MSG"); }
1545
1546// Send to QAbstractEventDispatcher
1547bool QWindowsContext::filterNativeEvent(MSG *msg, LRESULT *result)
1548{
1549 QAbstractEventDispatcher *dispatcher = QAbstractEventDispatcher::instance();
1550 qintptr filterResult = 0;
1551 if (dispatcher && dispatcher->filterNativeEvent(nativeEventType(), msg, &filterResult)) {
1552 *result = LRESULT(filterResult);
1553 return true;
1554 }
1555 return false;
1556}
1557
1558// Send to QWindowSystemInterface
1559bool QWindowsContext::filterNativeEvent(QWindow *window, MSG *msg, LRESULT *result)
1560{
1561 qintptr filterResult = 0;
1562 if (QWindowSystemInterface::handleNativeEvent(window, nativeEventType(), msg, &filterResult)) {
1563 *result = LRESULT(filterResult);
1564 return true;
1565 }
1566 return false;
1567}
1568
1569QT_END_NAMESPACE
\inmodule QtCore\reentrant
Definition qpoint.h:29
Singleton container for all relevant information.
QSharedPointer< QWindowCreationContext > windowCreationContext() const
QWindowsScreenManager & screenManager()
QWindowsWindow * findClosestPlatformWindow(HWND) const
QWindow * findWindow(HWND) const
bool asyncExpose() const
void addWindow(HWND, QWindowsWindow *w)
static bool systemParametersInfoForScreen(unsigned action, unsigned param, void *out, const QPlatformScreen *screen=nullptr)
static bool setProcessDpiAwareness(QtWindows::DpiAwareness dpiAwareness)
HDC displayContext() const
QWindowsTabletSupport * tabletSupport() const
static bool systemParametersInfoForWindow(unsigned action, unsigned param, void *out, const QPlatformWindow *win=nullptr)
static void setTabletAbsoluteRange(int a)
QWindowsWindow * findPlatformWindowAt(HWND parent, const QPoint &screenPoint, unsigned cwex_flags) const
bool initTouch(unsigned integrationOptions)
HWND createDummyWindow(const QString &classNameIn, const wchar_t *windowName, WNDPROC wndProc=nullptr, DWORD style=WS_OVERLAPPED)
Convenience to create a non-visible, message-only dummy window for example used as clipboard watcher ...
void setAsyncExpose(bool value)
QSharedPointer< QWindowCreationContext > setWindowCreationContext(const QSharedPointer< QWindowCreationContext > &ctx)
bool windowsProc(HWND hwnd, UINT message, QtWindows::WindowsEventType et, WPARAM wParam, LPARAM lParam, LRESULT *result, QWindowsWindow **platformWindowPtr)
Main windows procedure registered for windows.
unsigned systemInfo() const
static QtWindows::DpiAwareness processDpiAwareness()
QWindowsWindow * findPlatformWindow(HWND) const
QWindow * keyGrabber() const
QWindowsWindow * findPlatformWindow(const QWindowsMenuBar *mb) const
QWindow * windowUnderMouse() const
QPlatformKeyMapper * keyMapper() const
bool useRTLExtensions() const
static bool systemParametersInfo(unsigned action, unsigned param, void *out, unsigned dpi=0)
QWindowsMimeRegistry & mimeConverter() const
static bool isSessionLocked()
int screenDepth() const
bool initPowerNotificationHandler()
HandleBaseWindowHash & windows()
static QWindowsContext * instance()
QString registerWindowClass(QString cname, WNDPROC proc, unsigned style=0, HBRUSH brush=nullptr, bool icon=false)
void setDetectAltGrModifier(bool a)
Platform cursor implementation.
static bool hasOverrideCursor()
static void enforceOverrideCursor()
Windows Input context implementation.
static void setWindowsImeEnabled(QWindowsWindow *platformWindow, bool enabled)
static QWindowsIntegration * instance()
Translates Windows keys to QWindowSystemInterface events.
Windows native menu bar.
Manages the list of QWindowsMimeConverter instances.
Manages a list of QWindowsScreen.
Tablet support for Windows.
static QWindowsTheme * instance()
Raster or OpenGL Window.
void alertWindow(int durationMs=0)
void checkForScreenChanged(ScreenChangeMode mode=FromGeometryChange)
bool testFlag(unsigned f) const
void setFlag(unsigned f) const
void clearFlag(unsigned f) const
static void settingsChanged()
static const char * embeddedNativeParentHandleProperty
QWindowsMenuBar * menuBar() const
bool frameStrutEventsEnabled() const override
Reimplement this method to return whether frame strut events are enabled.
@ WithinSetParent
Automatic mouse capture on button press.
void handleCompositionSettingsChanged()
WindowsEventType
Enumerations for WM_XX events.
@ PointerActivateWindowEvent
@ InputMethodEndCompositionEvent
@ ShowEventOnParentRestoring
@ InputMethodCompositionEvent
@ InputMethodOpenCandidateWindowEvent
@ MouseActivateWindowEvent
@ DpiChangedAfterParentEvent
@ InputMethodStartCompositionEvent
@ CompositionSettingsChanged
@ InputMethodCloseCandidateWindowEvent
@ AccessibleObjectFromWindowRequest
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
#define WM_TOUCH
#define DPI_AWARENESS_CONTEXT_UNAWARE
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
#define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE
#define DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED
static bool isTopLevel(HWND hwnd)
#define LANG_SYRIAC
static RECT rectFromNcCalcSize(UINT message, WPARAM wParam, LPARAM lParam, int n)
static bool enableNonClientDpiScaling(HWND hwnd)
static bool isInputMessage(UINT m)
static DPI_AWARENESS_CONTEXT qtDpiAwarenessToDpiAwarenessContext(QtWindows::DpiAwareness dpiAwareness)
static bool isMinimized(HWND hwnd)
static QMargins marginsFromRects(const RECT &frame, const RECT &client)
static bool useRTL_Extensions()
static bool sessionManagerInteractionBlocked()
static QWindowsInputContext * windowsInputContext()
static QtWindows::DpiAwareness dpiAwarenessContextToQtDpiAwareness(DPI_AWARENESS_CONTEXT context)
static QByteArray nativeEventType()
static bool isEmptyRect(const RECT &rect)
static bool findPlatformWindowHelper(const POINT &screenPoint, unsigned cwexFlags, const QWindowsContext *context, HWND *hwnd, QWindowsWindow **result)
Find a child window at a screen point.
const HRESULT m_oleInitializeResult
QWindowsKeyMapper m_keyMapper
QWindowsPointerHandler m_pointerHandler
QSet< QString > m_registeredWindowClassNames
QWindowsScreenManager m_screenManager
QWindowsMimeRegistry m_mimeConverter
QWindowsContext::HandleBaseWindowHash m_windows
QSharedPointer< QWindowCreationContext > m_creationContext