Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qwindowsscreen.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qwindowsscreen.h"
5#include "qwindowscontext.h"
6#include "qwindowswindow.h"
8#include "qwindowscursor.h"
9#include "qwindowstheme.h"
10
11#include <QtCore/qt_windows.h>
12
13#include <QtCore/qsettings.h>
14#include <QtGui/qpixmap.h>
15#include <QtGui/qguiapplication.h>
16#include <qpa/qwindowsysteminterface.h>
17#include <QtCore/private/qsystemerror_p.h>
18#include <QtGui/private/qedidparser_p.h>
19#include <private/qhighdpiscaling_p.h>
20#include <private/qwindowsfontdatabasebase_p.h>
21#include <private/qpixmap_win_p.h>
22
23#include <QtGui/qscreen.h>
24
25#include <QtCore/qdebug.h>
26
27#include <memory>
28#include <type_traits>
29
30#include <cfgmgr32.h>
31#include <setupapi.h>
32#include <shellscalingapi.h>
33
35
36using namespace Qt::StringLiterals;
37
38static inline QDpi deviceDPI(HDC hdc)
39{
40 return QDpi(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY));
41}
42
43static inline QDpi monitorDPI(HMONITOR hMonitor)
44{
45 UINT dpiX;
46 UINT dpiY;
47 if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY)))
48 return QDpi(dpiX, dpiY);
49 return {0, 0};
50}
51
52static std::vector<DISPLAYCONFIG_PATH_INFO> getPathInfo(const MONITORINFOEX &viewInfo)
53{
54 // We might want to consider storing adapterId/id from DISPLAYCONFIG_PATH_TARGET_INFO.
55 std::vector<DISPLAYCONFIG_PATH_INFO> pathInfos;
56 std::vector<DISPLAYCONFIG_MODE_INFO> modeInfos;
57
58 // Fetch paths
59 LONG result;
60 UINT32 numPathArrayElements;
61 UINT32 numModeInfoArrayElements;
62 do {
63 // QueryDisplayConfig documentation doesn't say the number of needed elements is updated
64 // when the call fails with ERROR_INSUFFICIENT_BUFFER, so we need a separate call to
65 // look up the needed buffer sizes.
66 if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &numPathArrayElements,
67 &numModeInfoArrayElements) != ERROR_SUCCESS) {
68 return {};
69 }
70 pathInfos.resize(numPathArrayElements);
71 modeInfos.resize(numModeInfoArrayElements);
72 result = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &numPathArrayElements, pathInfos.data(),
73 &numModeInfoArrayElements, modeInfos.data(), nullptr);
74 } while (result == ERROR_INSUFFICIENT_BUFFER);
75
76 if (result != ERROR_SUCCESS)
77 return {};
78
79 // Find paths matching monitor name
80 auto discardThese =
81 std::remove_if(pathInfos.begin(), pathInfos.end(), [&](const auto &path) -> bool {
82 DISPLAYCONFIG_SOURCE_DEVICE_NAME deviceName;
83 deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
84 deviceName.header.size = sizeof(DISPLAYCONFIG_SOURCE_DEVICE_NAME);
85 deviceName.header.adapterId = path.sourceInfo.adapterId;
86 deviceName.header.id = path.sourceInfo.id;
87 if (DisplayConfigGetDeviceInfo(&deviceName.header) == ERROR_SUCCESS) {
88 return wcscmp(viewInfo.szDevice, deviceName.viewGdiDeviceName) != 0;
89 }
90 return true;
91 });
92
93 pathInfos.erase(discardThese, pathInfos.end());
94
95 return pathInfos;
96}
97
98#if 0
99// Needed later for HDR support
100static float getMonitorSDRWhiteLevel(DISPLAYCONFIG_PATH_TARGET_INFO *targetInfo)
101{
102 const float defaultSdrWhiteLevel = 200.0;
103 if (!targetInfo)
104 return defaultSdrWhiteLevel;
105
106 DISPLAYCONFIG_SDR_WHITE_LEVEL whiteLevel = {};
107 whiteLevel.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL;
108 whiteLevel.header.size = sizeof(DISPLAYCONFIG_SDR_WHITE_LEVEL);
109 whiteLevel.header.adapterId = targetInfo->adapterId;
110 whiteLevel.header.id = targetInfo->id;
111 if (DisplayConfigGetDeviceInfo(&whiteLevel.header) != ERROR_SUCCESS)
112 return defaultSdrWhiteLevel;
113 return whiteLevel.SDRWhiteLevel * 80.0 / 1000.0;
114}
115#endif
116
118
120{
121 void operator()(HKEY handle) const noexcept
122 {
123 if (handle != nullptr && handle != INVALID_HANDLE_VALUE)
124 RegCloseKey(handle);
125 }
126};
127
128using RegistryHandlePtr = std::unique_ptr<std::remove_pointer_t<HKEY>, RegistryHandleDeleter>;
129
131 const std::vector<DISPLAYCONFIG_PATH_INFO> &pathGroup)
132{
133 if (pathGroup.empty()) {
134 return;
135 }
136
137 // The only property shared among monitors in a clone group is deviceName
138 {
139 DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName = {};
140 deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
141 deviceName.header.size = sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME);
142 // The first element in the clone group is the main monitor.
143 deviceName.header.adapterId = pathGroup[0].targetInfo.adapterId;
144 deviceName.header.id = pathGroup[0].targetInfo.id;
145 if (DisplayConfigGetDeviceInfo(&deviceName.header) == ERROR_SUCCESS) {
146 data.devicePath = QString::fromWCharArray(deviceName.monitorDevicePath);
147 } else {
148 qCWarning(lcQpaScreen)
149 << u"Unable to get device information for %1:"_s.arg(pathGroup[0].targetInfo.id)
150 << QSystemError::windowsString();
151 }
152 }
153
154 // The rest must be concatenated into the resulting property
156 QStringList manufacturers;
157 QStringList models;
158 QStringList serialNumbers;
159
160 for (const auto &path : pathGroup) {
161 DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName = {};
162 deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
163 deviceName.header.size = sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME);
164 deviceName.header.adapterId = path.targetInfo.adapterId;
165 deviceName.header.id = path.targetInfo.id;
166 if (DisplayConfigGetDeviceInfo(&deviceName.header) != ERROR_SUCCESS) {
167 qCWarning(lcQpaScreen)
168 << u"Unable to get device information for %1:"_s.arg(path.targetInfo.id)
169 << QSystemError::windowsString();
170 continue;
171 }
172
173 // https://learn.microsoft.com/en-us/windows-hardware/drivers/install/guid-devinterface-monitor
174 constexpr GUID GUID_DEVINTERFACE_MONITOR = {
175 0xe6f07b5f, 0xee97, 0x4a90, { 0xb0, 0x76, 0x33, 0xf5, 0x7b, 0xf4, 0xea, 0xa7 }
176 };
177 const HDEVINFO devInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_MONITOR, nullptr, nullptr,
178 DIGCF_DEVICEINTERFACE);
179
180 SP_DEVICE_INTERFACE_DATA deviceInterfaceData{};
181 deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
182
183 if (!SetupDiOpenDeviceInterfaceW(devInfo, deviceName.monitorDevicePath, DIODI_NO_ADD,
184 &deviceInterfaceData)) {
185 qCWarning(lcQpaScreen)
186 << u"Unable to open monitor interface to %1:"_s.arg(data.deviceName)
187 << QSystemError::windowsString();
188 continue;
189 }
190
191 DWORD requiredSize{ 0 };
192 if (SetupDiGetDeviceInterfaceDetailW(devInfo, &deviceInterfaceData, nullptr, 0,
193 &requiredSize, nullptr)
194 || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
195 continue;
196 }
197
198 const std::unique_ptr<std::byte[]> storage(new std::byte[requiredSize]);
199 auto *devicePath = reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA_W *>(storage.get());
200 devicePath->cbSize = sizeof(std::remove_pointer_t<decltype(devicePath)>);
201 SP_DEVINFO_DATA deviceInfoData{};
202 deviceInfoData.cbSize = sizeof(deviceInfoData);
203 if (!SetupDiGetDeviceInterfaceDetailW(devInfo, &deviceInterfaceData, devicePath,
204 requiredSize, nullptr, &deviceInfoData)) {
205 qCDebug(lcQpaScreen) << u"Unable to get monitor metadata for %1:"_s.arg(data.deviceName)
206 << QSystemError::windowsString();
207 continue;
208 }
209
210 const RegistryHandlePtr edidRegistryKey{ SetupDiOpenDevRegKey(
211 devInfo, &deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ) };
212
213 if (!edidRegistryKey || edidRegistryKey.get() == INVALID_HANDLE_VALUE)
214 continue;
215
216 DWORD edidDataSize{ 0 };
217 if (RegQueryValueExW(edidRegistryKey.get(), L"EDID", nullptr, nullptr, nullptr,
218 &edidDataSize)
219 != ERROR_SUCCESS) {
220 continue;
221 }
222
223 QByteArray edidData;
224 edidData.resize(edidDataSize);
225
226 if (RegQueryValueExW(edidRegistryKey.get(), L"EDID", nullptr, nullptr,
227 reinterpret_cast<unsigned char *>(edidData.data()), &edidDataSize)
228 != ERROR_SUCCESS) {
229 qCDebug(lcQpaScreen) << u"Unable to get EDID from the Registry for %1:"_s.arg(
230 data.deviceName)
231 << QSystemError::windowsString();
232 continue;
233 }
234
235 QEdidParser edid;
236
237 if (!edid.parse(edidData)) {
238 qCDebug(lcQpaScreen) << "Invalid EDID blob for" << data.deviceName;
239 continue;
240 }
241
242 // We skip edid.identifier because it is unreliable, and a better option
243 // is already available through DisplayConfigGetDeviceInfo (see below).
244 names << QString::fromWCharArray(deviceName.monitorFriendlyDeviceName);
245 manufacturers << edid.manufacturer;
246 models << edid.model;
247 serialNumbers << edid.serialNumber;
248 }
249
250 data.name = names.join(u"|"_s);
251 data.manufacturer = manufacturers.join(u"|"_s);
252 data.model = models.join(u"|"_s);
253 data.serialNumber = serialNumbers.join(u"|"_s);
254}
255
256static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data)
257{
258 MONITORINFOEX info;
259 memset(&info, 0, sizeof(MONITORINFOEX));
260 info.cbSize = sizeof(MONITORINFOEX);
261 if (GetMonitorInfo(hMonitor, &info) == FALSE)
262 return false;
263
264 data->hMonitor = hMonitor;
265 data->geometry = QRect(QPoint(info.rcMonitor.left, info.rcMonitor.top), QPoint(info.rcMonitor.right - 1, info.rcMonitor.bottom - 1));
266 data->availableGeometry = QRect(QPoint(info.rcWork.left, info.rcWork.top), QPoint(info.rcWork.right - 1, info.rcWork.bottom - 1));
267 data->deviceName = QString::fromWCharArray(info.szDevice);
268 const auto pathGroup = getPathInfo(info);
269 if (!pathGroup.empty()) {
270 setMonitorDataFromSetupApi(*data, pathGroup);
271 }
272 if (data->name.isEmpty())
273 data->name = data->deviceName;
274 if (data->deviceName == u"WinDisc") {
276 } else {
277 if (const HDC hdc = CreateDC(info.szDevice, nullptr, nullptr, nullptr)) {
278 const QDpi dpi = monitorDPI(hMonitor);
279 data->dpi = dpi.first > 0 ? dpi : deviceDPI(hdc);
280 data->depth = GetDeviceCaps(hdc, BITSPIXEL);
281 data->format = data->depth == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32;
282 data->physicalSizeMM = QSizeF(GetDeviceCaps(hdc, HORZSIZE), GetDeviceCaps(hdc, VERTSIZE));
283 const int refreshRate = GetDeviceCaps(hdc, VREFRESH);
284 if (refreshRate > 1) // 0,1 means hardware default.
285 data->refreshRateHz = refreshRate;
286 DeleteDC(hdc);
287 } else {
288 qWarning("%s: Unable to obtain handle for monitor '%s', defaulting to %g DPI.",
289 __FUNCTION__, qPrintable(data->deviceName),
290 data->dpi.first);
291 } // CreateDC() failed
292 } // not lock screen
293
294 // ### We might want to consider storing adapterId/id from DISPLAYCONFIG_PATH_TARGET_INFO,
295 // if we are going to use DISPLAYCONFIG lookups more.
296 if (!pathGroup.empty()) {
297 // The first element in the clone group is the main monitor.
298 const auto &pathInfo = pathGroup[0];
299 switch (pathInfo.targetInfo.rotation) {
300 case DISPLAYCONFIG_ROTATION_IDENTITY:
301 data->orientation = Qt::LandscapeOrientation;
302 break;
303 case DISPLAYCONFIG_ROTATION_ROTATE90:
304 data->orientation = Qt::PortraitOrientation;
305 break;
306 case DISPLAYCONFIG_ROTATION_ROTATE180:
308 break;
309 case DISPLAYCONFIG_ROTATION_ROTATE270:
311 break;
312 case DISPLAYCONFIG_ROTATION_FORCE_UINT32:
313 Q_UNREACHABLE();
314 break;
315 }
316 if (pathInfo.targetInfo.refreshRate.Numerator && pathInfo.targetInfo.refreshRate.Denominator)
317 data->refreshRateHz = static_cast<qreal>(pathInfo.targetInfo.refreshRate.Numerator)
318 / pathInfo.targetInfo.refreshRate.Denominator;
319 } else {
320 data->orientation = data->geometry.height() > data->geometry.width()
323 }
324 // EnumDisplayMonitors (as opposed to EnumDisplayDevices) enumerates only
325 // virtual desktop screens.
327 if (info.dwFlags & MONITORINFOF_PRIMARY)
329 return true;
330}
331
332// from monitorData, taking WindowsScreenDataList as LPARAM
333BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM p)
334{
336 if (monitorData(hMonitor, &data)) {
337 auto *result = reinterpret_cast<WindowsScreenDataList *>(p);
338 auto it = std::find_if(result->rbegin(), result->rend(),
339 [&data](QWindowsScreenData i){ return i.name == data.name; });
340 if (it != result->rend()) {
341 int previousIndex = 1;
342 if (it->deviceIndex.has_value())
343 previousIndex = it->deviceIndex.value();
344 else
345 (*it).deviceIndex = 1;
346 data.deviceIndex = previousIndex + 1;
347 }
348 // QWindowSystemInterface::handleScreenAdded() documentation specifies that first
349 // added screen will be the primary screen, so order accordingly.
350 // Note that the side effect of this policy is that there is no way to change primary
351 // screen reported by Qt, unless we want to delete all existing screens and add them
352 // again whenever primary screen changes.
354 result->prepend(data);
355 else
356 result->append(data);
357 }
358 return TRUE;
359}
360
362{
364 EnumDisplayMonitors(nullptr, nullptr, monitorEnumCallback, reinterpret_cast<LPARAM>(&result));
365 return result;
366}
367
368#ifndef QT_NO_DEBUG_STREAM
370{
371 QDebugStateSaver saver(dbg);
372 dbg.nospace();
373 dbg.noquote();
374 dbg << "Screen \"" << d.name << "\" " << d.geometry.width() << 'x' << d.geometry.height() << '+'
375 << d.geometry.x() << '+' << d.geometry.y() << " avail: " << d.availableGeometry.width()
376 << 'x' << d.availableGeometry.height() << '+' << d.availableGeometry.x() << '+'
377 << d.availableGeometry.y() << " physical: " << d.physicalSizeMM.width() << 'x'
378 << d.physicalSizeMM.height() << " DPI: " << d.dpi.first << 'x' << d.dpi.second
379 << " Depth: " << d.depth << " Format: " << d.format << " hMonitor: " << d.hMonitor
380 << " device name: " << d.deviceName << " manufacturer: " << d.manufacturer
381 << " model: " << d.model << " serial number: " << d.serialNumber;
383 dbg << " primary";
385 dbg << " virtual desktop";
387 dbg << " lock screen";
388 return dbg;
389}
390#endif // !QT_NO_DEBUG_STREAM
391
400 m_data(data)
401#ifndef QT_NO_CURSOR
402 , m_cursor(new QWindowsCursor(this))
403#endif
404{
405}
406
408{
409 return m_data.deviceIndex.has_value()
410 ? (u"%1 (%2)"_s).arg(m_data.name, QString::number(m_data.deviceIndex.value()))
411 : m_data.name;
412}
413
414QPixmap QWindowsScreen::grabWindow(WId window, int xIn, int yIn, int width, int height) const
415{
416 QSize windowSize;
417 int x = xIn;
418 int y = yIn;
419 HWND hwnd = reinterpret_cast<HWND>(window);
420 if (hwnd) {
421 RECT r;
422 GetClientRect(hwnd, &r);
423 windowSize = QSize(r.right - r.left, r.bottom - r.top);
424 } else {
425 // Grab current screen. The client rectangle of GetDesktopWindow() is the
426 // primary screen, but it is possible to grab other screens from it.
427 hwnd = GetDesktopWindow();
428 const QRect screenGeometry = geometry();
429 windowSize = screenGeometry.size();
430 x += screenGeometry.x();
431 y += screenGeometry.y();
432 }
433
434 if (width < 0)
435 width = windowSize.width() - xIn;
436 if (height < 0)
437 height = windowSize.height() - yIn;
438
439 // Create and setup bitmap
440 HDC display_dc = GetDC(nullptr);
441 HDC bitmap_dc = CreateCompatibleDC(display_dc);
442 HBITMAP bitmap = CreateCompatibleBitmap(display_dc, width, height);
443 HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap);
444
445 // copy data
446 HDC window_dc = GetDC(hwnd);
447 BitBlt(bitmap_dc, 0, 0, width, height, window_dc, x, y, SRCCOPY | CAPTUREBLT);
448
449 // clean up all but bitmap
450 ReleaseDC(hwnd, window_dc);
451 SelectObject(bitmap_dc, null_bitmap);
452 DeleteDC(bitmap_dc);
453
455
456 DeleteObject(bitmap);
457 ReleaseDC(nullptr, display_dc);
458
459 return pixmap;
460}
461
467{
468 QWindow *result = nullptr;
469 if (QWindow *child = QWindowsScreen::windowAt(point, CWP_SKIPINVISIBLE))
472 qCDebug(lcQpaScreen) <<__FUNCTION__ << point << result;
473 return result;
474}
475
476QWindow *QWindowsScreen::windowAt(const QPoint &screenPoint, unsigned flags)
477{
478 QWindow* result = nullptr;
480 findPlatformWindowAt(GetDesktopWindow(), screenPoint, flags))
481 result = bw->window();
483 qCDebug(lcQpaScreen) <<__FUNCTION__ << screenPoint << " returns " << result;
484 return result;
485}
486
495{
500 for (QWindowsScreen *screen : screens) {
501 if (screen->data().flags & QWindowsScreenData::VirtualDesktop)
503 }
504 } else {
505 result.push_back(const_cast<QWindowsScreen *>(this));
506 }
507 return result;
508}
509
515{
516 m_data.physicalSizeMM = newData.physicalSizeMM;
517
518 if (m_data.hMonitor != newData.hMonitor) {
519 qCDebug(lcQpaScreen) << "Monitor" << m_data.name
520 << "has had its hMonitor handle changed from"
521 << m_data.hMonitor << "to" << newData.hMonitor;
522 m_data.hMonitor = newData.hMonitor;
523 }
524
525 // QGuiApplicationPrivate::processScreenGeometryChange() checks and emits
526 // DPI and orientation as well, so, assign new values and emit DPI first.
527 const bool geometryChanged = m_data.geometry != newData.geometry
528 || m_data.availableGeometry != newData.availableGeometry;
529 const bool dpiChanged = !qFuzzyCompare(m_data.dpi.first, newData.dpi.first)
530 || !qFuzzyCompare(m_data.dpi.second, newData.dpi.second);
531 const bool orientationChanged = m_data.orientation != newData.orientation;
532 m_data.dpi = newData.dpi;
533 m_data.orientation = newData.orientation;
534 m_data.geometry = newData.geometry;
535 m_data.availableGeometry = newData.availableGeometry;
536
537 if (dpiChanged) {
539 newData.dpi.first,
540 newData.dpi.second);
541 }
542 if (orientationChanged)
544 if (geometryChanged) {
546 newData.geometry, newData.availableGeometry);
547 }
548}
549
551{
552 return m_data.hMonitor;
553}
554
555QRect QWindowsScreen::virtualGeometry(const QPlatformScreen *screen) // cf QScreen::virtualGeometry()
556{
558 const auto siblings = screen->virtualSiblings();
559 for (const QPlatformScreen *sibling : siblings)
560 result |= sibling->geometry();
561 return result;
562}
563
565{
566 bool result = false;
567 ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE;
568 switch (o) {
570 break;
572 orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT;
573 break;
575 orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE;
576 break;
578 orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED;
579 break;
581 orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED;
582 break;
583 }
584 result = SetDisplayAutoRotationPreferences(orientationPreference);
585 return result;
586}
587
589{
591 ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE;
592 if (GetDisplayAutoRotationPreferences(&orientationPreference)) {
593 switch (orientationPreference) {
594 case ORIENTATION_PREFERENCE_NONE:
595 break;
596 case ORIENTATION_PREFERENCE_LANDSCAPE:
598 break;
599 case ORIENTATION_PREFERENCE_PORTRAIT:
601 break;
602 case ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED:
604 break;
605 case ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED:
607 break;
608 }
609 }
610 return result;
611}
612
617{
620 QSettings settings(R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Avalon.Graphics\DISPLAY1)"_L1,
622 int registryValue = settings.value("PixelStructure"_L1, -1).toInt();
623 switch (registryValue) {
624 case 0:
626 break;
627 case 1:
629 break;
630 case 2:
632 break;
633 default:
635 break;
636 }
637 }
638 return type;
639}
640
652extern "C" LRESULT QT_WIN_CALLBACK qDisplayChangeObserverWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
653{
654 if (message == WM_DISPLAYCHANGE) {
655 qCDebug(lcQpaScreen) << "Handling WM_DISPLAYCHANGE";
657 t->displayChanged();
660 }
661
662 return DefWindowProc(hwnd, message, wParam, lParam);
663}
664
666
668{
669 qCDebug(lcQpaScreen) << "Initializing screen manager";
670
672 QWindowsContext::classNamePrefix() + QLatin1String("ScreenChangeObserverWindow"),
674
675 // HWND_MESSAGE windows do not get WM_DISPLAYCHANGE, so we need to create
676 // a real top level window that we never show.
677 m_displayChangeObserver = CreateWindowEx(0, reinterpret_cast<LPCWSTR>(className.utf16()),
678 nullptr, WS_TILED, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
679 nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
680 Q_ASSERT(m_displayChangeObserver);
681
682 qCDebug(lcQpaScreen) << "Created display change observer" << m_displayChangeObserver;
683
685}
686
688{
689 DestroyWindow(m_displayChangeObserver);
690}
691
693{
695}
696
698 const QString &serialNumberIn)
699{
700 for (int i = 0; i < screens.size(); ++i)
701 if (screens.at(i)->data().serialNumber == serialNumberIn)
702 return i;
703 return -1;
704}
705
706static inline int indexOfMonitor(const WindowsScreenDataList &screenData,
707 const QString &serialNumberIn)
708{
709 for (int i = 0; i < screenData.size(); ++i)
710 if (screenData.at(i).serialNumber == serialNumberIn)
711 return i;
712 return -1;
713}
714
715// Move a window to a new virtual screen, accounting for varying sizes.
716static void moveToVirtualScreen(QWindow *w, const QScreen *newScreen)
717{
718 QRect geometry = w->geometry();
719 const QRect oldScreenGeometry = w->screen()->geometry();
720 const QRect newScreenGeometry = newScreen->geometry();
721 QPoint relativePosition = geometry.topLeft() - oldScreenGeometry.topLeft();
722 if (oldScreenGeometry.size() != newScreenGeometry.size()) {
723 const qreal factor =
724 qreal(QPoint(newScreenGeometry.width(), newScreenGeometry.height()).manhattanLength()) /
725 qreal(QPoint(oldScreenGeometry.width(), oldScreenGeometry.height()).manhattanLength());
726 relativePosition = (QPointF(relativePosition) * factor).toPoint();
727 }
728 geometry.moveTopLeft(relativePosition);
729 w->setGeometry(geometry);
730}
731
732void QWindowsScreenManager::removeScreen(int index)
733{
734 qCDebug(lcQpaScreen) << "Removing Monitor:" << m_screens.at(index)->data();
735 QScreen *screen = m_screens.at(index)->screen();
736 QScreen *primaryScreen = QGuiApplication::primaryScreen();
737 // QTBUG-38650: When a screen is disconnected, Windows will automatically
738 // move the Window to another screen. This will trigger a geometry change
739 // event, but unfortunately after the screen destruction signal. To prevent
740 // QtGui from automatically hiding the QWindow, pretend all Windows move to
741 // the primary screen first (which is likely the correct, final screen).
742 // QTBUG-39320: Windows does not automatically move WS_EX_TOOLWINDOW (dock) windows;
743 // move those manually.
744 if (screen != primaryScreen) {
745 unsigned movedWindowCount = 0;
747 for (QWindow *w : tlws) {
748 if (w->screen() == screen && w->handle() && w->type() != Qt::Desktop) {
749 if (w->isVisible() && w->windowState() != Qt::WindowMinimized
750 && (QWindowsWindow::baseWindowOf(w)->exStyle() & WS_EX_TOOLWINDOW)) {
751 moveToVirtualScreen(w, primaryScreen);
752 } else {
754 }
755 ++movedWindowCount;
756 }
757 }
758 if (movedWindowCount)
760 }
762}
763
770{
771 // Look for changed monitors, add new ones
772 const WindowsScreenDataList newDataList = monitorData();
773 const bool lockScreen = newDataList.size() == 1 && (newDataList.front().flags & QWindowsScreenData::LockScreen);
774 bool primaryScreenChanged = false;
775 for (const QWindowsScreenData &newData : newDataList) {
776 const int existingIndex = indexOfMonitor(m_screens, newData.serialNumber);
777 if (existingIndex != -1) {
778 m_screens.at(existingIndex)->handleChanges(newData);
779 if (existingIndex == 0)
780 primaryScreenChanged = true;
781 } else {
782 auto *newScreen = new QWindowsScreen(newData);
783 m_screens.push_back(newScreen);
785 newData.flags & QWindowsScreenData::PrimaryScreen);
786 qCDebug(lcQpaScreen) << "New Monitor: " << newData;
787 } // exists
788 } // for new screens.
789 // Remove deleted ones but keep main monitors if we get only the
790 // temporary lock screen to avoid window recreation (QTBUG-33062).
791 if (!lockScreen) {
792 for (int i = m_screens.size() - 1; i >= 0; --i) {
793 if (indexOfMonitor(newDataList, m_screens.at(i)->data().serialNumber) == -1)
794 removeScreen(i);
795 } // for existing screens
796 } // not lock screen
797 if (primaryScreenChanged) {
798 if (auto theme = QWindowsTheme::instance()) // QTBUG-85734/Wine
799 theme->refreshFonts();
800 }
801 return true;
802}
803
805{
806 // Delete screens in reverse order to avoid crash in case of multiple screens
807 while (!m_screens.isEmpty())
809}
810
812{
813 for (QWindowsScreen *scr : m_screens) {
814 if (scr->geometry().contains(p))
815 return scr;
816 }
817 return nullptr;
818}
819
821{
822 HMONITOR hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
823 if (hMonitor == nullptr)
824 return nullptr;
825 const auto it =
826 std::find_if(m_screens.cbegin(), m_screens.cend(),
827 [hMonitor](const QWindowsScreen *s)
828 {
829 return s->data().hMonitor == hMonitor
830 && (s->data().flags & QWindowsScreenData::VirtualDesktop) != 0;
831 });
832 return it != m_screens.cend() ? *it : nullptr;
833}
834
NSData * m_data
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
\inmodule QtCore
\inmodule QtCore
QString model
QString manufacturer
bool parse(const QByteArray &blob)
QString serialNumber
static QWindowList topLevelWindows()
Returns a list of the top-level windows in the application.
QScreen * primaryScreen
the primary (or default) screen of the application.
@ Format_RGB32
Definition qimage.h:46
@ Format_RGB16
Definition qimage.h:49
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
void push_back(parameter_type t)
Definition qlist.h:672
T takeAt(qsizetype i)
Definition qlist.h:592
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
value_type takeLast()
Definition qlist.h:550
reference front()
Definition qlist.h:684
const_iterator cend() const noexcept
Definition qlist.h:614
const_iterator cbegin() const noexcept
Definition qlist.h:613
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
The QPlatformScreen class provides an abstraction for visual displays.
QScreen * screen() const
virtual SubpixelAntialiasingType subpixelAntialiasingTypeHint() const
Returns a hint about this screen's subpixel layout structure.
The QPlatformWindow class provides an abstraction for top-level windows.
\inmodule QtCore\reentrant
Definition qpoint.h:214
\inmodule QtCore\reentrant
Definition qpoint.h:23
constexpr int manhattanLength() const
Returns the sum of the absolute values of x() and y(), traditionally known as the "Manhattan length" ...
Definition qpoint.h:147
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr void moveTopLeft(const QPoint &p) noexcept
Moves the rectangle, leaving the top-left corner at the given position.
Definition qrect.h:303
constexpr int height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:238
constexpr QPoint topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
Definition qrect.h:220
constexpr int x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:184
constexpr QSize size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:241
constexpr int width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:235
constexpr int y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:187
The QScreen class is used to query screen properties. \inmodule QtGui.
Definition qscreen.h:32
QList< QScreen * > virtualSiblings() const
Get the screen's virtual siblings.
Definition qscreen.cpp:357
QRect geometry
the screen's geometry in pixels
Definition qscreen.h:45
QPlatformScreen * handle() const
Get the platform screen handle.
Definition qscreen.cpp:83
\inmodule QtCore
Definition qsettings.h:30
@ NativeFormat
Definition qsettings.h:49
QVariant value(QAnyStringView key, const QVariant &defaultValue) const
Returns the value for setting key.
\inmodule QtCore
Definition qsize.h:207
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:132
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:129
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1164
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:7822
int toInt(bool *ok=nullptr) const
Returns the variant as an int if the variant has userType() \l QMetaType::Int, \l QMetaType::Bool,...
static bool flushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags=QEventLoop::AllEvents)
Make Qt Gui process all events on the event queue immediately.
static void handleWindowScreenChanged(QWindow *window, QScreen *newScreen)
static void handleScreenGeometryChange(QScreen *screen, const QRect &newGeometry, const QRect &newAvailableGeometry)
static void handleScreenAdded(QPlatformScreen *screen, bool isPrimary=false)
Should be called by the implementation whenever a new screen is added.
static void handleScreenRemoved(QPlatformScreen *screen)
Should be called by the implementation whenever a screen is removed.
static void handleScreenOrientationChange(QScreen *screen, Qt::ScreenOrientation newOrientation)
static void handleScreenLogicalDotsPerInchChange(QScreen *screen, qreal newDpiX, qreal newDpiY)
\inmodule QtGui
Definition qwindow.h:63
unsigned exStyle() const
static QWindowsBaseWindow * baseWindowOf(const QWindow *w)
QWindowsScreenManager & screenManager()
static QString classNamePrefix()
QString registerWindowClass(const QWindow *w)
static QWindowsContext * instance()
Platform cursor implementation.
bool handleScreenChanges()
Synchronizes the screen list, adds new screens, removes deleted ones and propagates resolution change...
const QWindowsScreen * screenForHwnd(HWND hwnd) const
const QWindowsScreen * screenAtDp(const QPoint &p) const
const WindowsScreenList & screens() const
Windows screen.
static QWindow * windowAt(const QPoint &point, unsigned flags)
QList< QPlatformScreen * > virtualSiblings() const override
Determine siblings in a virtual desktop system.
QWindowsScreen(const QWindowsScreenData &data)
QPixmap grabWindow(WId window, int qX, int qY, int qWidth, int qHeight) const override
This function is called when Qt needs to be able to grab the content of a window.
QRect geometry() const override
Reimplement in subclass to return the pixel geometry of the screen.
QPlatformScreen::SubpixelAntialiasingType subpixelAntialiasingTypeHint() const override
Queries ClearType settings to check the pixel layout.
void handleChanges(const QWindowsScreenData &newData)
Notify QWindowSystemInterface about changes of a screen and synchronize data.
static bool setOrientationPreference(Qt::ScreenOrientation o)
static QRect virtualGeometry(const QPlatformScreen *screen)
QString name() const override
QWindow * topLevelAt(const QPoint &point) const override
Find a top level window taking the flags of ChildWindowFromPointEx.
HMONITOR handle() const override
static Qt::ScreenOrientation orientationPreference()
static QWindowsTheme * instance()
static void displayChanged()
static QWindow * topLevelOf(QWindow *w)
#define this
Definition dialogs.cpp:9
QSet< QString >::iterator it
Combined button and popup list for selecting options.
@ WindowMinimized
Definition qnamespace.h:252
ScreenOrientation
Definition qnamespace.h:270
@ InvertedLandscapeOrientation
Definition qnamespace.h:275
@ InvertedPortraitOrientation
Definition qnamespace.h:274
@ LandscapeOrientation
Definition qnamespace.h:273
@ PortraitOrientation
Definition qnamespace.h:272
@ PrimaryOrientation
Definition qnamespace.h:271
@ Desktop
Definition qnamespace.h:214
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:287
QPair< qreal, qreal > QDpi
#define qWarning
Definition qlogging.h:162
#define qCWarning(category,...)
#define qCDebug(category,...)
GLuint64 GLenum void * handle
GLint GLint GLint GLint GLint x
[0]
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLuint index
[2]
GLboolean r
[2]
GLint GLsizei width
GLenum type
GLbitfield flags
GLuint GLsizei const GLchar * message
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint y
GLuint GLuint * names
GLsizei GLfixed GLfixed GLfixed GLfixed const GLubyte * bitmap
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
GLfloat GLfloat p
[1]
QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int(*) void arg)
#define qPrintable(string)
Definition qstring.h:1391
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
QScreen * screen
[1]
Definition main.cpp:29
double qreal
Definition qtypes.h:92
BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM p)
LRESULT QT_WIN_CALLBACK qDisplayChangeObserverWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
static QDebug operator<<(QDebug dbg, const QWindowsScreenData &d)
static QDpi deviceDPI(HDC hdc)
static WindowsScreenDataList monitorData()
static std::vector< DISPLAYCONFIG_PATH_INFO > getPathInfo(const MONITORINFOEX &viewInfo)
static void moveToVirtualScreen(QWindow *w, const QScreen *newScreen)
std::unique_ptr< std::remove_pointer_t< HKEY >, RegistryHandleDeleter > RegistryHandlePtr
static void setMonitorDataFromSetupApi(QWindowsScreenData &data, const std::vector< DISPLAYCONFIG_PATH_INFO > &pathGroup)
static int indexOfMonitor(const QWindowsScreenManager::WindowsScreenList &screens, const QString &serialNumberIn)
static QDpi monitorDPI(HMONITOR hMonitor)
const char className[16]
[1]
Definition qwizard.cpp:100
QFileInfo info(fileName)
[8]
QSettings settings("MySoft", "Star Runner")
[0]
QStorageInfo storage
[1]
QLayoutItem * child
[0]
widget render & pixmap
aWidget window() -> setWindowTitle("New Window Title")
[2]
Qt::ScreenOrientation orientation
std::optional< int > deviceIndex
void operator()(HKEY handle) const noexcept