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