diff options
author | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2022-06-16 12:01:36 +0200 |
---|---|---|
committer | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2022-06-18 21:08:02 +0200 |
commit | fa0b2ef81c0d22f4038235871fbc1abda55887d1 (patch) | |
tree | a8e67e11a00b6319569b7ffe8916e60351db7932 | |
parent | e457aeec3802e79d33347768cb9c50eb87deed92 (diff) |
Windows: Decouple screen change monitoring from top level QWindows
The WM_DISPLAYCHANGE message it sent when displays are added, removed,
or update their properties such as the scale/DPI.
We were processing this message as part of QWindowsContext::windowsProc(),
which meant that we would only react to display changes if there was a
QWindow on screen. Just creating a QGuiApplication was insufficient to
pick up changes to screens after startup.
In addition, despite being documented to post messages to child windows,
WM_DISPLAYCHANGE only ends up in top level windows. Presumably it's the
top level window's responsibility to post the message to child windows.
As a result, if a QWindow was a native child window of a foreign window,
such as in audio plugins being hosted in a DAW, we would again fail to
pick up display changes.
We solve both these cases by decoupling the WM_DISPLAYCHANGE handling
from QWindowsContext::windowsProc(), by creating a dedicated window
for listening to WM_DISPLAYCHANGE. This is similar to how we already
handle tray icons, power notifications, clipboard, etc -- the only
difference being that since purely HWND_MESSAGE windows do not
receive WM_DISPLAYCHANGE it's an actual invisible WS_TILED window.
This also lets us remove the workaround for QTBUG-79248, which was
doing screen updates in response to WM_DPICHANGED when detecting
that there were no QWindows.
Task-number: QTBUG-103383
Task-number: QTBUG-79248
Fixes: QTBUG-102343
Pick-to: 6.4
Change-Id: I905d8253069ec339b193edf05c052d21361ca3e9
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
7 files changed, 44 insertions, 14 deletions
diff --git a/src/plugins/platforms/windows/qtwindowsglobal.h b/src/plugins/platforms/windows/qtwindowsglobal.h index 71a4269a27..9a65603e24 100644 --- a/src/plugins/platforms/windows/qtwindowsglobal.h +++ b/src/plugins/platforms/windows/qtwindowsglobal.h @@ -140,8 +140,7 @@ enum WindowsEventType // Simplify event types InputMethodRequest = InputMethodEventFlag + 6, ThemeChanged = ThemingEventFlag + 1, CompositionSettingsChanged = ThemingEventFlag + 2, - DisplayChangedEvent = 437, - SettingChangedEvent = DisplayChangedEvent + 1, + SettingChangedEvent = 438, ScrollEvent = GenericEventFlag + 1, ContextMenu = 123, GestureEvent = 124, @@ -258,8 +257,6 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI // http://msdn.microsoft.com/en-us/library/ms695534(v=vs.85).aspx case WM_SETTINGCHANGE: return QtWindows::SettingChangedEvent; - case WM_DISPLAYCHANGE: - return QtWindows::DisplayChangedEvent; case WM_THEMECHANGED: case WM_SYSCOLORCHANGE: // Handle color change as theme change (QTBUG-34170). case WM_DWMCOLORIZATIONCOLORCHANGED: diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 68d2e5d98d..9397c0213e 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -71,6 +71,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") Q_LOGGING_CATEGORY(lcQpaAccessibility, "qt.qpa.accessibility") Q_LOGGING_CATEGORY(lcQpaUiAutomation, "qt.qpa.uiautomation") Q_LOGGING_CATEGORY(lcQpaTrayIcon, "qt.qpa.trayicon") +Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen") int QWindowsContext::verbose = 0; @@ -1091,12 +1092,6 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, #else return false; #endif - case QtWindows::DisplayChangedEvent: - if (QWindowsTheme *t = QWindowsTheme::instance()) - t->displayChanged(); - QWindowsWindow::displayChanged(); - d->m_screenManager.handleScreenChanges(); - return false; case QtWindows::SettingChangedEvent: { QWindowsWindow::settingsChanged(); // Only refresh the window theme if the user changes the personalize settings. diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h index 4cc47a3ba4..af92690b3b 100644 --- a/src/plugins/platforms/windows/qwindowscontext.h +++ b/src/plugins/platforms/windows/qwindowscontext.h @@ -31,6 +31,7 @@ Q_DECLARE_LOGGING_CATEGORY(lcQpaTablet) Q_DECLARE_LOGGING_CATEGORY(lcQpaAccessibility) Q_DECLARE_LOGGING_CATEGORY(lcQpaUiAutomation) Q_DECLARE_LOGGING_CATEGORY(lcQpaTrayIcon) +Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen) class QWindow; class QPlatformScreen; diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index 8e94722565..f1874b37bb 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -264,7 +264,7 @@ QWindowsIntegration::QWindowsIntegration(const QStringList ¶mList) : #if QT_CONFIG(clipboard) d->m_clipboard.registerViewer(); #endif - d->m_context.screenManager().handleScreenChanges(); + d->m_context.screenManager().initialize(); d->m_context.setDetectAltGrModifier((d->m_options & DetectAltGrModifier) != 0); } diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index 6b60083759..177bff8a12 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -513,8 +513,45 @@ QPlatformScreen::SubpixelAntialiasingType QWindowsScreen::subpixelAntialiasingTy \internal */ +extern "C" LRESULT QT_WIN_CALLBACK qDisplayChangeObserverWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + if (message == WM_DISPLAYCHANGE) { + qCDebug(lcQpaScreen) << "Handling WM_DISPLAYCHANGE"; + if (QWindowsTheme *t = QWindowsTheme::instance()) + t->displayChanged(); + QWindowsWindow::displayChanged(); + QWindowsContext::instance()->screenManager().handleScreenChanges(); + } + + return DefWindowProc(hwnd, message, wParam, lParam); +} + QWindowsScreenManager::QWindowsScreenManager() = default; +void QWindowsScreenManager::initialize() +{ + qCDebug(lcQpaScreen) << "Initializing screen manager"; + + auto className = QWindowsContext::instance()->registerWindowClass( + QWindowsContext::classNamePrefix() + QLatin1String("ScreenChangeObserverWindow"), + qDisplayChangeObserverWndProc); + + // HWND_MESSAGE windows do not get WM_DISPLAYCHANGE, so we need to create + // a real top level window that we never show. + m_displayChangeObserver = CreateWindowEx(0, reinterpret_cast<LPCWSTR>(className.utf16()), + nullptr, WS_TILED, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + nullptr, nullptr, GetModuleHandle(nullptr), nullptr); + Q_ASSERT(m_displayChangeObserver); + + qCDebug(lcQpaScreen) << "Created display change observer" << m_displayChangeObserver; + + handleScreenChanges(); +} + +QWindowsScreenManager::~QWindowsScreenManager() +{ + DestroyWindow(m_displayChangeObserver); +} bool QWindowsScreenManager::isSingleScreen() { diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h index babb9de758..6c638842fe 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.h +++ b/src/plugins/platforms/windows/qwindowsscreen.h @@ -96,6 +96,8 @@ public: using WindowsScreenList = QList<QWindowsScreen *>; QWindowsScreenManager(); + void initialize(); + ~QWindowsScreenManager(); void clearScreens(); @@ -110,6 +112,7 @@ public: private: void removeScreen(int index); + HWND m_displayChangeObserver = nullptr; WindowsScreenList m_screens; }; diff --git a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp index 25d29a2302..cf150b5772 100644 --- a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp +++ b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp @@ -94,9 +94,6 @@ static int indexOfHwnd(HWND hwnd) extern "C" LRESULT QT_WIN_CALLBACK qWindowsTrayIconWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { - // QTBUG-79248: Trigger screen update if there are no other windows. - if (message == WM_DPICHANGED && QGuiApplication::topLevelWindows().isEmpty()) - QWindowsContext::instance()->screenManager().handleScreenChanges(); if (message == MYWM_TASKBARCREATED || message == MYWM_NOTIFYICON || message == WM_INITMENU || message == WM_INITMENUPOPUP || message == WM_CLOSE || message == WM_COMMAND) { |