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