Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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#include <private/quniquehandle_p.h>
23
24#include <QtGui/qscreen.h>
25
26#include <QtCore/qdebug.h>
27
28#include <memory>
29#include <type_traits>
30
31#include <cfgmgr32.h>
32#include <setupapi.h>
33#include <shellscalingapi.h>
34
36
37using namespace Qt::StringLiterals;
38
39static inline QDpi deviceDPI(HDC hdc)
40{
41 return QDpi(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY));
42}
43
44static inline QDpi monitorDPI(HMONITOR hMonitor)
45{
46 UINT dpiX;
47 UINT dpiY;
48 if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY)))
49 return QDpi(dpiX, dpiY);
50 return {0, 0};
51}
52
53static std::vector<DISPLAYCONFIG_PATH_INFO> getPathInfo(const MONITORINFOEX &viewInfo)
54{
55 // We might want to consider storing adapterId/id from DISPLAYCONFIG_PATH_TARGET_INFO.
56 std::vector<DISPLAYCONFIG_PATH_INFO> pathInfos;
57 std::vector<DISPLAYCONFIG_MODE_INFO> modeInfos;
58
59 // Fetch paths
60 LONG result;
61 UINT32 numPathArrayElements;
62 UINT32 numModeInfoArrayElements;
63 do {
64 // QueryDisplayConfig documentation doesn't say the number of needed elements is updated
65 // when the call fails with ERROR_INSUFFICIENT_BUFFER, so we need a separate call to
66 // look up the needed buffer sizes.
67 if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &numPathArrayElements,
68 &numModeInfoArrayElements) != ERROR_SUCCESS) {
69 return {};
70 }
71 pathInfos.resize(numPathArrayElements);
72 modeInfos.resize(numModeInfoArrayElements);
73 result = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &numPathArrayElements, pathInfos.data(),
74 &numModeInfoArrayElements, modeInfos.data(), nullptr);
75 } while (result == ERROR_INSUFFICIENT_BUFFER);
76
77 if (result != ERROR_SUCCESS)
78 return {};
79
80 // Find paths matching monitor name
81 auto discardThese =
82 std::remove_if(pathInfos.begin(), pathInfos.end(), [&](const auto &path) -> bool {
83 DISPLAYCONFIG_SOURCE_DEVICE_NAME deviceName;
84 deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
85 deviceName.header.size = sizeof(DISPLAYCONFIG_SOURCE_DEVICE_NAME);
86 deviceName.header.adapterId = path.sourceInfo.adapterId;
87 deviceName.header.id = path.sourceInfo.id;
88 if (DisplayConfigGetDeviceInfo(&deviceName.header) == ERROR_SUCCESS) {
89 return wcscmp(viewInfo.szDevice, deviceName.viewGdiDeviceName) != 0;
90 }
91 return true;
92 });
93
94 pathInfos.erase(discardThese, pathInfos.end());
95
96 return pathInfos;
97}
98
99#if 0
100// Needed later for HDR support
101static float getMonitorSDRWhiteLevel(DISPLAYCONFIG_PATH_TARGET_INFO *targetInfo)
102{
103 const float defaultSdrWhiteLevel = 200.0;
104 if (!targetInfo)
105 return defaultSdrWhiteLevel;
106
107 DISPLAYCONFIG_SDR_WHITE_LEVEL whiteLevel = {};
108 whiteLevel.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL;
109 whiteLevel.header.size = sizeof(DISPLAYCONFIG_SDR_WHITE_LEVEL);
110 whiteLevel.header.adapterId = targetInfo->adapterId;
111 whiteLevel.header.id = targetInfo->id;
112 if (DisplayConfigGetDeviceInfo(&whiteLevel.header) != ERROR_SUCCESS)
113 return defaultSdrWhiteLevel;
114 return whiteLevel.SDRWhiteLevel * 80.0 / 1000.0;
115}
116#endif
117
118using WindowsScreenDataList = QList<QWindowsScreenData>;
119
120namespace {
121
122struct DiRegKeyHandleTraits
123{
124 using Type = HKEY;
125 static Type invalidValue()
126 {
127 // The setupapi.h functions return INVALID_HANDLE_VALUE when failing to open a registry key
128 return reinterpret_cast<HKEY>(INVALID_HANDLE_VALUE);
129 }
130 static bool close(Type handle) { return RegCloseKey(handle) == ERROR_SUCCESS; }
131};
132
133using DiRegKeyHandle = QUniqueHandle<DiRegKeyHandleTraits>;
134
135}
136
138 const std::vector<DISPLAYCONFIG_PATH_INFO> &pathGroup)
139{
140 if (pathGroup.empty()) {
141 return;
142 }
143
144 // The only property shared among monitors in a clone group is deviceName
145 {
146 DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName = {};
147 deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
148 deviceName.header.size = sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME);
149 // The first element in the clone group is the main monitor.
150 deviceName.header.adapterId = pathGroup[0].targetInfo.adapterId;
151 deviceName.header.id = pathGroup[0].targetInfo.id;
152 if (DisplayConfigGetDeviceInfo(&deviceName.header) == ERROR_SUCCESS) {
153 data.devicePath = QString::fromWCharArray(deviceName.monitorDevicePath);
154 } else {
155 qCWarning(lcQpaScreen)
156 << u"Unable to get device information for %1:"_s.arg(pathGroup[0].targetInfo.id)
157 << QSystemError::windowsString();
158 }
159 }
160
161 // The rest must be concatenated into the resulting property
163 QStringList manufacturers;
164 QStringList models;
165 QStringList serialNumbers;
166
167 for (const auto &path : pathGroup) {
168 DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName = {};
169 deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
170 deviceName.header.size = sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME);
171 deviceName.header.adapterId = path.targetInfo.adapterId;
172 deviceName.header.id = path.targetInfo.id;
173 if (DisplayConfigGetDeviceInfo(&deviceName.header) != ERROR_SUCCESS) {
174 qCWarning(lcQpaScreen)
175 << u"Unable to get device information for %1:"_s.arg(path.targetInfo.id)
176 << QSystemError::windowsString();
177 continue;
178 }
179
180 // https://learn.microsoft.com/en-us/windows-hardware/drivers/install/guid-devinterface-monitor
181 constexpr GUID GUID_DEVINTERFACE_MONITOR = {
182 0xe6f07b5f, 0xee97, 0x4a90, { 0xb0, 0x76, 0x33, 0xf5, 0x7b, 0xf4, 0xea, 0xa7 }
183 };
184 const HDEVINFO devInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_MONITOR, nullptr, nullptr,
185 DIGCF_DEVICEINTERFACE);
186
187 SP_DEVICE_INTERFACE_DATA deviceInterfaceData{};
188 deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
189
190 if (!SetupDiOpenDeviceInterfaceW(devInfo, deviceName.monitorDevicePath, DIODI_NO_ADD,
191 &deviceInterfaceData)) {
192 qCWarning(lcQpaScreen)
193 << u"Unable to open monitor interface to %1:"_s.arg(data.deviceName)
194 << QSystemError::windowsString();
195 continue;
196 }
197
198 DWORD requiredSize{ 0 };
199 if (SetupDiGetDeviceInterfaceDetailW(devInfo, &deviceInterfaceData, nullptr, 0,
200 &requiredSize, nullptr)
201 || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
202 continue;
203 }
204
205 const std::unique_ptr<std::byte[]> storage(new std::byte[requiredSize]);
206 auto *devicePath = reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA_W *>(storage.get());
207 devicePath->cbSize = sizeof(std::remove_pointer_t<decltype(devicePath)>);
208 SP_DEVINFO_DATA deviceInfoData{};
209 deviceInfoData.cbSize = sizeof(deviceInfoData);
210 if (!SetupDiGetDeviceInterfaceDetailW(devInfo, &deviceInterfaceData, devicePath,
211 requiredSize, nullptr, &deviceInfoData)) {
212 qCDebug(lcQpaScreen) << u"Unable to get monitor metadata for %1:"_s.arg(data.deviceName)
213 << QSystemError::windowsString();
214 continue;
215 }
216
217 const DiRegKeyHandle edidRegistryKey{ SetupDiOpenDevRegKey(
218 devInfo, &deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ) };
219
220 if (!edidRegistryKey.isValid())
221 continue;
222
223 DWORD edidDataSize{ 0 };
224 if (RegQueryValueExW(edidRegistryKey.get(), L"EDID", nullptr, nullptr, nullptr,
225 &edidDataSize)
226 != ERROR_SUCCESS) {
227 continue;
228 }
229
230 QByteArray edidData;
231 edidData.resize(edidDataSize);
232
233 if (RegQueryValueExW(edidRegistryKey.get(), L"EDID", nullptr, nullptr,
234 reinterpret_cast<unsigned char *>(edidData.data()), &edidDataSize)
235 != ERROR_SUCCESS) {
236 qCDebug(lcQpaScreen) << u"Unable to get EDID from the Registry for %1:"_s.arg(
237 data.deviceName)
238 << QSystemError::windowsString();
239 continue;
240 }
241
242 QEdidParser edid;
243
244 if (!edid.parse(edidData)) {
245 qCDebug(lcQpaScreen) << "Invalid EDID blob for" << data.deviceName;
246 continue;
247 }
248
249 // We skip edid.identifier because it is unreliable, and a better option
250 // is already available through DisplayConfigGetDeviceInfo (see below).
251 names << QString::fromWCharArray(deviceName.monitorFriendlyDeviceName);
252 manufacturers << edid.manufacturer;
253 models << edid.model;
254 serialNumbers << edid.serialNumber;
255 }
256
257 data.name = names.join(u"|"_s);
258 data.manufacturer = manufacturers.join(u"|"_s);
259 data.model = models.join(u"|"_s);
260 data.serialNumber = serialNumbers.join(u"|"_s);
261}
262
263static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data)
264{
265 MONITORINFOEX info;
266 memset(&info, 0, sizeof(MONITORINFOEX));
267 info.cbSize = sizeof(MONITORINFOEX);
268 if (GetMonitorInfo(hMonitor, &info) == FALSE)
269 return false;
270
271 data->hMonitor = hMonitor;
272 data->geometry = QRect(QPoint(info.rcMonitor.left, info.rcMonitor.top), QPoint(info.rcMonitor.right - 1, info.rcMonitor.bottom - 1));
273 data->availableGeometry = QRect(QPoint(info.rcWork.left, info.rcWork.top), QPoint(info.rcWork.right - 1, info.rcWork.bottom - 1));
274 data->deviceName = QString::fromWCharArray(info.szDevice);
275 const auto pathGroup = getPathInfo(info);
276 if (!pathGroup.empty()) {
277 setMonitorDataFromSetupApi(*data, pathGroup);
278 }
279 if (data->name.isEmpty())
280 data->name = data->deviceName;
281 if (data->deviceName == u"WinDisc") {
283 } else {
284 if (const HDC hdc = CreateDC(info.szDevice, nullptr, nullptr, nullptr)) {
285 const QDpi dpi = monitorDPI(hMonitor);
286 data->dpi = dpi.first > 0 ? dpi : deviceDPI(hdc);
287 data->depth = GetDeviceCaps(hdc, BITSPIXEL);
288 data->format = data->depth == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32;
289 data->physicalSizeMM = QSizeF(GetDeviceCaps(hdc, HORZSIZE), GetDeviceCaps(hdc, VERTSIZE));
290 const int refreshRate = GetDeviceCaps(hdc, VREFRESH);
291 if (refreshRate > 1) // 0,1 means hardware default.
292 data->refreshRateHz = refreshRate;
293 DeleteDC(hdc);
294 } else {
295 qWarning("%s: Unable to obtain handle for monitor '%s', defaulting to %g DPI.",
296 __FUNCTION__, qPrintable(data->deviceName),
297 data->dpi.first);
298 } // CreateDC() failed
299 } // not lock screen
300
301 // ### We might want to consider storing adapterId/id from DISPLAYCONFIG_PATH_TARGET_INFO,
302 // if we are going to use DISPLAYCONFIG lookups more.
303 if (!pathGroup.empty()) {
304 // The first element in the clone group is the main monitor.
305 const auto &pathInfo = pathGroup[0];
306 switch (pathInfo.targetInfo.rotation) {
307 case DISPLAYCONFIG_ROTATION_IDENTITY:
308 data->orientation = Qt::LandscapeOrientation;
309 break;
310 case DISPLAYCONFIG_ROTATION_ROTATE90:
311 data->orientation = Qt::PortraitOrientation;
312 break;
313 case DISPLAYCONFIG_ROTATION_ROTATE180:
315 break;
316 case DISPLAYCONFIG_ROTATION_ROTATE270:
318 break;
319 case DISPLAYCONFIG_ROTATION_FORCE_UINT32:
320 Q_UNREACHABLE();
321 break;
322 }
323 if (pathInfo.targetInfo.refreshRate.Numerator && pathInfo.targetInfo.refreshRate.Denominator)
324 data->refreshRateHz = static_cast<qreal>(pathInfo.targetInfo.refreshRate.Numerator)
325 / pathInfo.targetInfo.refreshRate.Denominator;
326 } else {
327 data->orientation = data->geometry.height() > data->geometry.width()
330 }
331 // EnumDisplayMonitors (as opposed to EnumDisplayDevices) enumerates only
332 // virtual desktop screens.
334 if (info.dwFlags & MONITORINFOF_PRIMARY)
336 return true;
337}
338
339// from monitorData, taking WindowsScreenDataList as LPARAM
340BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM p)
341{
343 if (monitorData(hMonitor, &data)) {
344 auto *result = reinterpret_cast<WindowsScreenDataList *>(p);
345 auto it = std::find_if(result->rbegin(), result->rend(),
346 [&data](QWindowsScreenData i){ return i.name == data.name; });
347 if (it != result->rend()) {
348 int previousIndex = 1;
349 if (it->deviceIndex.has_value())
350 previousIndex = it->deviceIndex.value();
351 else
352 (*it).deviceIndex = 1;
353 data.deviceIndex = previousIndex + 1;
354 }
355 // QWindowSystemInterface::handleScreenAdded() documentation specifies that first
356 // added screen will be the primary screen, so order accordingly.
357 // Note that the side effect of this policy is that there is no way to change primary
358 // screen reported by Qt, unless we want to delete all existing screens and add them
359 // again whenever primary screen changes.
361 result->prepend(data);
362 else
363 result->append(data);
364 }
365 return TRUE;
366}
367
369{
371 EnumDisplayMonitors(nullptr, nullptr, monitorEnumCallback, reinterpret_cast<LPARAM>(&result));
372 return result;
373}
374
375#ifndef QT_NO_DEBUG_STREAM
377{
378 QDebugStateSaver saver(dbg);
379 dbg.nospace();
380 dbg.noquote();
381 dbg << "Screen \"" << d.name << "\" " << d.geometry.width() << 'x' << d.geometry.height() << '+'
382 << d.geometry.x() << '+' << d.geometry.y() << " avail: " << d.availableGeometry.width()
383 << 'x' << d.availableGeometry.height() << '+' << d.availableGeometry.x() << '+'
384 << d.availableGeometry.y() << " physical: " << d.physicalSizeMM.width() << 'x'
385 << d.physicalSizeMM.height() << " DPI: " << d.dpi.first << 'x' << d.dpi.second
386 << " Depth: " << d.depth << " Format: " << d.format << " hMonitor: " << d.hMonitor
387 << " device name: " << d.deviceName << " manufacturer: " << d.manufacturer
388 << " model: " << d.model << " serial number: " << d.serialNumber;
390 dbg << " primary";
392 dbg << " virtual desktop";
394 dbg << " lock screen";
395 return dbg;
396}
397#endif // !QT_NO_DEBUG_STREAM
398
407 m_data(data)
408#ifndef QT_NO_CURSOR
409 , m_cursor(new QWindowsCursor(this))
410#endif
411{
412}
413
415{
416 return m_data.deviceIndex.has_value()
417 ? (u"%1 (%2)"_s).arg(m_data.name, QString::number(m_data.deviceIndex.value()))
418 : m_data.name;
419}
420
421QPixmap QWindowsScreen::grabWindow(WId window, int xIn, int yIn, int width, int height) const
422{
423 QSize windowSize;
424 int x = xIn;
425 int y = yIn;
426 HWND hwnd = reinterpret_cast<HWND>(window);
427 if (hwnd) {
428 RECT r;
429 GetClientRect(hwnd, &r);
430 windowSize = QSize(r.right - r.left, r.bottom - r.top);
431 } else {
432 // Grab current screen. The client rectangle of GetDesktopWindow() is the
433 // primary screen, but it is possible to grab other screens from it.
434 hwnd = GetDesktopWindow();
435 const QRect screenGeometry = geometry();
436 windowSize = screenGeometry.size();
437 x += screenGeometry.x();
438 y += screenGeometry.y();
439 }
440
441 if (width < 0)
442 width = windowSize.width() - xIn;
443 if (height < 0)
444 height = windowSize.height() - yIn;
445
446 // Create and setup bitmap
447 HDC display_dc = GetDC(nullptr);
448 HDC bitmap_dc = CreateCompatibleDC(display_dc);
449 HBITMAP bitmap = CreateCompatibleBitmap(display_dc, width, height);
450 HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap);
451
452 // copy data
453 HDC window_dc = GetDC(hwnd);
454 BitBlt(bitmap_dc, 0, 0, width, height, window_dc, x, y, SRCCOPY | CAPTUREBLT);
455
456 // clean up all but bitmap
457 ReleaseDC(hwnd, window_dc);
458 SelectObject(bitmap_dc, null_bitmap);
459 DeleteDC(bitmap_dc);
460
462
463 DeleteObject(bitmap);
464 ReleaseDC(nullptr, display_dc);
465
466 return pixmap;
467}
468
474{
475 QWindow *result = nullptr;
476 if (QWindow *child = QWindowsScreen::windowAt(point, CWP_SKIPINVISIBLE))
479 qCDebug(lcQpaScreen) <<__FUNCTION__ << point << result;
480 return result;
481}
482
483QWindow *QWindowsScreen::windowAt(const QPoint &screenPoint, unsigned flags)
484{
485 QWindow* result = nullptr;
487 findPlatformWindowAt(GetDesktopWindow(), screenPoint, flags))
488 result = bw->window();
490 qCDebug(lcQpaScreen) <<__FUNCTION__ << screenPoint << " returns " << result;
491 return result;
492}
493
501QList<QPlatformScreen *> QWindowsScreen::virtualSiblings() const
502{
503 QList<QPlatformScreen *> result;
506 = QWindowsContext::instance()->screenManager().screens();
507 for (QWindowsScreen *screen : screens) {
508 if (screen->data().flags & QWindowsScreenData::VirtualDesktop)
509 result.push_back(screen);
510 }
511 } else {
512 result.push_back(const_cast<QWindowsScreen *>(this));
513 }
514 return result;
515}
516
522{
523 m_data.physicalSizeMM = newData.physicalSizeMM;
524
525 if (m_data.hMonitor != newData.hMonitor) {
526 qCDebug(lcQpaScreen) << "Monitor" << m_data.name
527 << "has had its hMonitor handle changed from"
528 << m_data.hMonitor << "to" << newData.hMonitor;
529 m_data.hMonitor = newData.hMonitor;
530 }
531
532 // QGuiApplicationPrivate::processScreenGeometryChange() checks and emits
533 // DPI and orientation as well, so, assign new values and emit DPI first.
534 const bool geometryChanged = m_data.geometry != newData.geometry
535 || m_data.availableGeometry != newData.availableGeometry;
536 const bool dpiChanged = !qFuzzyCompare(m_data.dpi.first, newData.dpi.first)
537 || !qFuzzyCompare(m_data.dpi.second, newData.dpi.second);
538 const bool orientationChanged = m_data.orientation != newData.orientation;
539 const bool primaryChanged = (newData.flags & QWindowsScreenData::PrimaryScreen)
541 m_data.dpi = newData.dpi;
542 m_data.orientation = newData.orientation;
543 m_data.geometry = newData.geometry;
544 m_data.availableGeometry = newData.availableGeometry;
545 m_data.flags = (m_data.flags & ~QWindowsScreenData::PrimaryScreen)
547
548 if (dpiChanged) {
550 newData.dpi.first,
551 newData.dpi.second);
552 }
553 if (orientationChanged)
555 if (geometryChanged) {
557 newData.geometry, newData.availableGeometry);
558 }
559 if (primaryChanged)
561}
562
564{
565 return m_data.hMonitor;
566}
567
568QRect QWindowsScreen::virtualGeometry(const QPlatformScreen *screen) // cf QScreen::virtualGeometry()
569{
571 const auto siblings = screen->virtualSiblings();
572 for (const QPlatformScreen *sibling : siblings)
573 result |= sibling->geometry();
574 return result;
575}
576
578{
579 bool result = false;
580 ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE;
581 switch (o) {
583 break;
585 orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT;
586 break;
588 orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE;
589 break;
591 orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED;
592 break;
594 orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED;
595 break;
596 }
597 result = SetDisplayAutoRotationPreferences(orientationPreference);
598 return result;
599}
600
602{
604 ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE;
605 if (GetDisplayAutoRotationPreferences(&orientationPreference)) {
606 switch (orientationPreference) {
607 case ORIENTATION_PREFERENCE_NONE:
608 break;
609 case ORIENTATION_PREFERENCE_LANDSCAPE:
611 break;
612 case ORIENTATION_PREFERENCE_PORTRAIT:
614 break;
615 case ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED:
617 break;
618 case ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED:
620 break;
621 }
622 }
623 return result;
624}
625
630{
633 QSettings settings(R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Avalon.Graphics\DISPLAY1)"_L1,
635 int registryValue = settings.value("PixelStructure"_L1, -1).toInt();
636 switch (registryValue) {
637 case 0:
639 break;
640 case 1:
642 break;
643 case 2:
645 break;
646 default:
648 break;
649 }
650 }
651 return type;
652}
653
665extern "C" LRESULT QT_WIN_CALLBACK qDisplayChangeObserverWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
666{
667 if (message == WM_DISPLAYCHANGE) {
668 qCDebug(lcQpaScreen) << "Handling WM_DISPLAYCHANGE";
670 t->displayChanged();
673 context->screenManager().handleScreenChanges();
674 }
675
676 return DefWindowProc(hwnd, message, wParam, lParam);
677}
678
680
682{
683 qCDebug(lcQpaScreen) << "Initializing screen manager";
684
685 auto className = QWindowsContext::instance()->registerWindowClass(
686 QWindowsContext::classNamePrefix() + QLatin1String("ScreenChangeObserverWindow"),
688
689 // HWND_MESSAGE windows do not get WM_DISPLAYCHANGE, so we need to create
690 // a real top level window that we never show.
691 m_displayChangeObserver = CreateWindowEx(0, reinterpret_cast<LPCWSTR>(className.utf16()),
692 nullptr, WS_TILED, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
693 nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
694 Q_ASSERT(m_displayChangeObserver);
695
696 qCDebug(lcQpaScreen) << "Created display change observer" << m_displayChangeObserver;
697
699}
700
702{
703 qCDebug(lcQpaScreen) << "Destroying display change observer" << m_displayChangeObserver;
704 DestroyWindow(m_displayChangeObserver);
705 m_displayChangeObserver = nullptr;
706}
707
709
711{
712 return QWindowsContext::instance()->screenManager().screens().size() < 2;
713}
714
716 const QString &deviceName)
717{
718 for (int i= 0; i < screens.size(); ++i)
719 if (screens.at(i)->data().deviceName == deviceName)
720 return i;
721 return -1;
722}
723
724static inline int indexOfMonitor(const WindowsScreenDataList &screenData,
725 const QString &deviceName)
726{
727 for (int i = 0; i < screenData.size(); ++i)
728 if (screenData.at(i).deviceName == deviceName)
729 return i;
730 return -1;
731}
732
733// Move a window to a new virtual screen, accounting for varying sizes.
734static void moveToVirtualScreen(QWindow *w, const QScreen *newScreen)
735{
736 QRect geometry = w->geometry();
737 const QRect oldScreenGeometry = w->screen()->geometry();
738 const QRect newScreenGeometry = newScreen->geometry();
739 QPoint relativePosition = geometry.topLeft() - oldScreenGeometry.topLeft();
740 if (oldScreenGeometry.size() != newScreenGeometry.size()) {
741 const qreal factor =
742 qreal(QPoint(newScreenGeometry.width(), newScreenGeometry.height()).manhattanLength()) /
743 qreal(QPoint(oldScreenGeometry.width(), oldScreenGeometry.height()).manhattanLength());
744 relativePosition = (QPointF(relativePosition) * factor).toPoint();
745 }
746 geometry.moveTopLeft(relativePosition);
747 w->setGeometry(geometry);
748}
749
750void QWindowsScreenManager::removeScreen(int index)
751{
752 qCDebug(lcQpaScreen) << "Removing Monitor:" << m_screens.at(index)->data();
753 QScreen *screen = m_screens.at(index)->screen();
754 QScreen *primaryScreen = QGuiApplication::primaryScreen();
755 // QTBUG-38650: When a screen is disconnected, Windows will automatically
756 // move the Window to another screen. This will trigger a geometry change
757 // event, but unfortunately after the screen destruction signal. To prevent
758 // QtGui from automatically hiding the QWindow, pretend all Windows move to
759 // the primary screen first (which is likely the correct, final screen).
760 // QTBUG-39320: Windows does not automatically move WS_EX_TOOLWINDOW (dock) windows;
761 // move those manually.
762 if (screen != primaryScreen) {
763 unsigned movedWindowCount = 0;
765 for (QWindow *w : tlws) {
766 if (w->screen() == screen && w->handle() && w->type() != Qt::Desktop) {
767 if (w->isVisible() && w->windowState() != Qt::WindowMinimized
768 && (QWindowsWindow::baseWindowOf(w)->exStyle() & WS_EX_TOOLWINDOW)) {
769 moveToVirtualScreen(w, primaryScreen);
770 } else {
772 }
773 ++movedWindowCount;
774 }
775 }
776 if (movedWindowCount)
778 }
780}
781
788{
789 // Look for changed monitors, add new ones
790 const WindowsScreenDataList newDataList = monitorData();
791 const bool lockScreen = newDataList.size() == 1 && (newDataList.front().flags & QWindowsScreenData::LockScreen);
792 bool primaryScreenChanged = false;
793 for (const QWindowsScreenData &newData : newDataList) {
794 const int existingIndex = indexOfMonitor(m_screens, newData.deviceName);
795 if (existingIndex != -1) {
796 m_screens.at(existingIndex)->handleChanges(newData);
797 if (existingIndex == 0)
798 primaryScreenChanged = true;
799 } else {
800 auto *newScreen = new QWindowsScreen(newData);
801 m_screens.push_back(newScreen);
803 newData.flags & QWindowsScreenData::PrimaryScreen);
804 qCDebug(lcQpaScreen) << "New Monitor: " << newData;
805 } // exists
806 } // for new screens.
807 // Remove deleted ones but keep main monitors if we get only the
808 // temporary lock screen to avoid window recreation (QTBUG-33062).
809 if (!lockScreen) {
810 for (int i = m_screens.size() - 1; i >= 0; --i) {
811 if (indexOfMonitor(newDataList, m_screens.at(i)->data().deviceName) == -1)
812 removeScreen(i);
813 } // for existing screens
814 } // not lock screen
815 if (primaryScreenChanged) {
816 if (auto theme = QWindowsTheme::instance()) // QTBUG-85734/Wine
817 theme->refreshFonts();
818 }
819 return true;
820}
821
823{
824 // Delete screens in reverse order to avoid crash in case of multiple screens
825 while (!m_screens.isEmpty())
827}
828
830{
831 for (QWindowsScreen *scr : m_screens) {
832 if (scr->geometry().contains(p))
833 return scr;
834 }
835 return nullptr;
836}
837
839{
840 HMONITOR hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
841 if (hMonitor == nullptr)
842 return nullptr;
843 const auto it =
844 std::find_if(m_screens.cbegin(), m_screens.cend(),
845 [hMonitor](const QWindowsScreen *s)
846 {
847 return s->data().hMonitor == hMonitor
848 && (s->data().flags & QWindowsScreenData::VirtualDesktop) != 0;
849 });
850 return it != m_screens.cend() ? *it : nullptr;
851}
852
NSData * m_data
\inmodule QtCore
Definition qbytearray.h:57
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
\inmodule QtCore
\inmodule QtCore
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
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
void push_back(parameter_type t)
Definition qlist.h:675
T takeAt(qsizetype i)
Definition qlist.h:609
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
value_type takeLast()
Definition qlist.h:567
const_iterator cend() const noexcept
Definition qlist.h:631
const_iterator cbegin() const noexcept
Definition qlist.h:630
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:217
\inmodule QtCore\reentrant
Definition qpoint.h:25
constexpr int manhattanLength() const
Returns the sum of the absolute values of x() and y(), traditionally known as the "Manhattan length" ...
Definition qpoint.h:150
\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:304
constexpr QPoint topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
Definition qrect.h:221
constexpr int x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:185
constexpr QSize size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:242
constexpr int y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:188
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
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:208
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1309
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:8084
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 handlePrimaryScreenChanged(QPlatformScreen *newPrimary)
Should be called whenever the primary screen changes.
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
static QWindowsBaseWindow * baseWindowOf(const QWindow *w)
static QString classNamePrefix()
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
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:253
ScreenOrientation
Definition qnamespace.h:271
@ InvertedLandscapeOrientation
Definition qnamespace.h:276
@ InvertedPortraitOrientation
Definition qnamespace.h:275
@ LandscapeOrientation
Definition qnamespace.h:274
@ PortraitOrientation
Definition qnamespace.h:273
@ PrimaryOrientation
Definition qnamespace.h:272
@ Desktop
Definition qnamespace.h:215
static void * context
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
QPair< qreal, qreal > QDpi
#define qWarning
Definition qlogging.h:166
#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 GLsizei GLenum GLenum GLsizei void * data
GLint GLsizei width
GLenum type
GLbitfield flags
GLuint GLsizei const GLchar * message
GLint y
GLdouble s
[6]
Definition qopenglext.h:235
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]
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:1531
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
QScreen * screen
[1]
Definition main.cpp:29
double qreal
Definition qtypes.h:187
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()
QList< QWindowsScreenData > WindowsScreenDataList
static std::vector< DISPLAYCONFIG_PATH_INFO > getPathInfo(const MONITORINFOEX &viewInfo)
static void moveToVirtualScreen(QWindow *w, const QScreen *newScreen)
static void setMonitorDataFromSetupApi(QWindowsScreenData &data, const std::vector< DISPLAYCONFIG_PATH_INFO > &pathGroup)
static int indexOfMonitor(const QWindowsScreenManager::WindowsScreenList &screens, const QString &deviceName)
static QDpi monitorDPI(HMONITOR hMonitor)
const char className[16]
[1]
Definition qwizard.cpp:100
QSettings settings("MySoft", "Star Runner")
[0]
QStorageInfo storage
[1]
QLayoutItem * child
[0]
widget render & pixmap
aWidget window() -> setWindowTitle("New Window Title")
[2]
QHostInfo info
[0]
Qt::ScreenOrientation orientation
std::optional< int > deviceIndex
Definition moc.h:23