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