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
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// Qt-Security score:significant reason:default
4
10#include "qwindowstheme.h"
12
13#include <QtCore/qt_windows.h>
14
15#include <QtCore/qsettings.h>
16#include <QtGui/qpixmap.h>
17#include <QtGui/qguiapplication.h>
18#include <qpa/qwindowsysteminterface.h>
19#include <QtCore/private/qsystemerror_p.h>
20#include <QtCore/private/qsystemlibrary_p.h>
21#include <QtGui/private/qedidparser_p.h>
22#include <private/qwindowsfontdatabasebase_p.h>
23#include <private/qpixmap_win_p.h>
24#include <private/quniquehandle_p.h>
25
26#include <QtGui/qscreen.h>
27
28#include <QtCore/qdebug.h>
29
30#include <memory>
31#include <type_traits>
32
33#include <cfgmgr32.h>
34#include <setupapi.h>
35#include <shellscalingapi.h>
36#include <icm.h>
37
38#if QT_CONFIG(cpp_winrt)
39# include <QtCore/qoperatingsystemversion.h>
40# include <QtCore/private/qt_winrtbase_p.h>
41# include <winrt/Windows.Foundation.h>
42# include <winrt/Windows.Graphics.Display.h>
43# include <windows.graphics.display.interop.h>
44#endif
45
46#ifndef WCS_ICCONLY
47#define WCS_ICCONLY 0x00010000L
48#endif
49
50QT_BEGIN_NAMESPACE
51
52using namespace Qt::StringLiterals;
53
55{
57 {
58 HMODULE lib = QSystemLibrary::load(L"Mscms");
59 if (!lib)
60 return;
61
62 colorProfileGetDisplayDefault = reinterpret_cast<DisplayDefaultSignature>(
63 reinterpret_cast<QFunctionPointer>(
64 ::GetProcAddress(lib, "ColorProfileGetDisplayDefault")));
65 colorProfileGetDisplayUserScope = reinterpret_cast<DisplayUserScopeSignature>(
66 reinterpret_cast<QFunctionPointer>(
67 ::GetProcAddress(lib, "ColorProfileGetDisplayUserScope")));
68 }
69
70 HRESULT getDisplayDefault(WCS_PROFILE_MANAGEMENT_SCOPE scope, LUID targetAdapterID,
71 UINT32 sourceID, COLORPROFILETYPE profileType,
72 COLORPROFILESUBTYPE profileSubType, LPWSTR *profileName)
73 {
74 if (!colorProfileGetDisplayDefault)
75 return E_NOTIMPL;
76 return colorProfileGetDisplayDefault(scope, targetAdapterID, sourceID,
77 profileType, profileSubType, profileName);
78 }
79
80 HRESULT getDisplayUserScope(LUID targetAdapterID, UINT32 sourceID,
81 WCS_PROFILE_MANAGEMENT_SCOPE *scope)
82 {
83 if (!colorProfileGetDisplayUserScope)
84 return E_NOTIMPL;
85 return colorProfileGetDisplayUserScope(targetAdapterID, sourceID, scope);
86 }
87
88private:
93
94 DisplayDefaultSignature colorProfileGetDisplayDefault = nullptr;
95 DisplayUserScopeSignature colorProfileGetDisplayUserScope = nullptr;
96};
97Q_GLOBAL_STATIC(WindowsColorSpaceFunctions, windowsColorSpaceFunctions)
98
99static inline QDpi deviceDPI(HDC hdc)
100{
101 return QDpi(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY));
102}
103
104static inline QDpi monitorDPI(HMONITOR hMonitor)
105{
106 UINT dpiX;
107 UINT dpiY;
108 if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY)))
109 return QDpi(dpiX, dpiY);
110 return {0, 0};
111}
112
113static std::vector<DISPLAYCONFIG_PATH_INFO> getPathInfo(const MONITORINFOEX &viewInfo)
114{
115 // We might want to consider storing adapterId/id from DISPLAYCONFIG_PATH_TARGET_INFO.
116 std::vector<DISPLAYCONFIG_PATH_INFO> pathInfos;
117 std::vector<DISPLAYCONFIG_MODE_INFO> modeInfos;
118
119 // Fetch paths
120 LONG result;
121 UINT32 numPathArrayElements;
122 UINT32 numModeInfoArrayElements;
123 do {
124 // QueryDisplayConfig documentation doesn't say the number of needed elements is updated
125 // when the call fails with ERROR_INSUFFICIENT_BUFFER, so we need a separate call to
126 // look up the needed buffer sizes.
127 if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &numPathArrayElements,
128 &numModeInfoArrayElements) != ERROR_SUCCESS) {
129 return {};
130 }
131 pathInfos.resize(numPathArrayElements);
132 modeInfos.resize(numModeInfoArrayElements);
133 result = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &numPathArrayElements, pathInfos.data(),
134 &numModeInfoArrayElements, modeInfos.data(), nullptr);
135 } while (result == ERROR_INSUFFICIENT_BUFFER);
136
137 if (result != ERROR_SUCCESS)
138 return {};
139
140 // Find paths matching monitor name
141 auto discardThese =
142 std::remove_if(pathInfos.begin(), pathInfos.end(), [&](const auto &path) -> bool {
143 DISPLAYCONFIG_SOURCE_DEVICE_NAME deviceName;
144 deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
145 deviceName.header.size = sizeof(DISPLAYCONFIG_SOURCE_DEVICE_NAME);
146 deviceName.header.adapterId = path.sourceInfo.adapterId;
147 deviceName.header.id = path.sourceInfo.id;
148 if (DisplayConfigGetDeviceInfo(&deviceName.header) == ERROR_SUCCESS) {
149 return wcscmp(viewInfo.szDevice, deviceName.viewGdiDeviceName) != 0;
150 }
151 return true;
152 });
153
154 pathInfos.erase(discardThese, pathInfos.end());
155
156 return pathInfos;
157}
158
159#if 0
160// Needed later for HDR support
161static float getMonitorSDRWhiteLevel(DISPLAYCONFIG_PATH_TARGET_INFO *targetInfo)
162{
163 const float defaultSdrWhiteLevel = 200.0;
164 if (!targetInfo)
165 return defaultSdrWhiteLevel;
166
167 DISPLAYCONFIG_SDR_WHITE_LEVEL whiteLevel = {};
168 whiteLevel.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL;
169 whiteLevel.header.size = sizeof(DISPLAYCONFIG_SDR_WHITE_LEVEL);
170 whiteLevel.header.adapterId = targetInfo->adapterId;
171 whiteLevel.header.id = targetInfo->id;
172 if (DisplayConfigGetDeviceInfo(&whiteLevel.header) != ERROR_SUCCESS)
173 return defaultSdrWhiteLevel;
174 return whiteLevel.SDRWhiteLevel * 80.0 / 1000.0;
175}
176#endif
177
178using WindowsScreenDataList = QList<QWindowsScreenData>;
179
180namespace {
181
182struct DiRegKeyHandleTraits
183{
184 using Type = HKEY;
185 static Type invalidValue() noexcept
186 {
187 // The setupapi.h functions return INVALID_HANDLE_VALUE when failing to open a registry key
188 return reinterpret_cast<HKEY>(INVALID_HANDLE_VALUE);
189 }
190 static bool close(Type handle) noexcept { return RegCloseKey(handle) == ERROR_SUCCESS; }
191};
192
193using DiRegKeyHandle = QUniqueHandle<DiRegKeyHandleTraits>;
194
195struct DevInfoHandleTraits
196{
197 using Type = HDEVINFO;
198 static Type invalidValue() noexcept
199 {
200 return reinterpret_cast<HDEVINFO>(INVALID_HANDLE_VALUE);
201 }
202 static bool close(Type handle) noexcept { return SetupDiDestroyDeviceInfoList(handle) == TRUE; }
203};
204
205using DevInfoHandle = QUniqueHandle<DevInfoHandleTraits>;
206
207}
208
209static void setMonitorDataFromSetupApi(QWindowsScreenData &data,
210 const std::vector<DISPLAYCONFIG_PATH_INFO> &pathGroup)
211{
212 if (pathGroup.empty()) {
213 return;
214 }
215
216 // The only property shared among monitors in a clone group is deviceName
217 {
218 DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName = {};
219 deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
220 deviceName.header.size = sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME);
221 // The first element in the clone group is the main monitor.
222 deviceName.header.adapterId = pathGroup[0].targetInfo.adapterId;
223 deviceName.header.id = pathGroup[0].targetInfo.id;
224 const LONG result = DisplayConfigGetDeviceInfo(&deviceName.header);
225 if (result == ERROR_SUCCESS) {
226 data.devicePath = QString::fromWCharArray(deviceName.monitorDevicePath);
227 } else {
228 // This can fail for virtual screens or disconnected displays - not an error
229 qCDebug(lcQpaScreen)
230 << u"Unable to get device information for %1:"_s.arg(pathGroup[0].targetInfo.id)
231 << QSystemError::windowsString(result);
232 }
233 }
234
235 // The rest must be concatenated into the resulting property
236 QStringList names;
237 QStringList manufacturers;
238 QStringList models;
239 QStringList serialNumbers;
240
241 for (const auto &path : pathGroup) {
242 DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName = {};
243 deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
244 deviceName.header.size = sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME);
245 deviceName.header.adapterId = path.targetInfo.adapterId;
246 deviceName.header.id = path.targetInfo.id;
247 const LONG result = DisplayConfigGetDeviceInfo(&deviceName.header);
248 if (result != ERROR_SUCCESS) {
249 // This can fail for virtual screens (WinDisc) or disconnected displays - not an error
250 qCDebug(lcQpaScreen)
251 << u"Unable to get device information for %1:"_s.arg(path.targetInfo.id)
252 << QSystemError::windowsString(result);
253 continue;
254 }
255
256 // https://learn.microsoft.com/en-us/windows-hardware/drivers/install/guid-devinterface-monitor
257 constexpr GUID GUID_DEVINTERFACE_MONITOR = {
258 0xe6f07b5f, 0xee97, 0x4a90, { 0xb0, 0x76, 0x33, 0xf5, 0x7b, 0xf4, 0xea, 0xa7 }
259 };
260 const DevInfoHandle devInfo{ SetupDiGetClassDevs(
261 &GUID_DEVINTERFACE_MONITOR, nullptr, nullptr, DIGCF_DEVICEINTERFACE) };
262
263 if (!devInfo.isValid())
264 continue;
265
266 SP_DEVICE_INTERFACE_DATA deviceInterfaceData{};
267 deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
268
269 if (!SetupDiOpenDeviceInterfaceW(devInfo.get(), deviceName.monitorDevicePath, DIODI_NO_ADD,
270 &deviceInterfaceData)) {
271 // This can fail for virtual screens with no physical target - not an error
272 qCDebug(lcQpaScreen)
273 << u"Unable to open monitor interface to %1:"_s.arg(data.deviceName)
274 << QSystemError::windowsString();
275 continue;
276 }
277
278 DWORD requiredSize{ 0 };
279 if (SetupDiGetDeviceInterfaceDetailW(devInfo.get(), &deviceInterfaceData, nullptr, 0,
280 &requiredSize, nullptr)
281 || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
282 continue;
283 }
284
285 const std::unique_ptr<std::byte[]> storage(new std::byte[requiredSize]);
286 auto *devicePath = reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA_W *>(storage.get());
287 devicePath->cbSize = sizeof(std::remove_pointer_t<decltype(devicePath)>);
288 SP_DEVINFO_DATA deviceInfoData{};
289 deviceInfoData.cbSize = sizeof(deviceInfoData);
290 if (!SetupDiGetDeviceInterfaceDetailW(devInfo.get(), &deviceInterfaceData, devicePath,
291 requiredSize, nullptr, &deviceInfoData)) {
292 qCDebug(lcQpaScreen) << u"Unable to get monitor metadata for %1:"_s.arg(data.deviceName)
293 << QSystemError::windowsString();
294 continue;
295 }
296
297 const DiRegKeyHandle edidRegistryKey{ SetupDiOpenDevRegKey(
298 devInfo.get(), &deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ) };
299
300 if (!edidRegistryKey.isValid())
301 continue;
302
303 DWORD edidDataSize{ 0 };
304 if (RegQueryValueExW(edidRegistryKey.get(), L"EDID", nullptr, nullptr, nullptr,
305 &edidDataSize)
306 != ERROR_SUCCESS) {
307 continue;
308 }
309
310 QByteArray edidData;
311 edidData.resize(edidDataSize);
312
313 if (RegQueryValueExW(edidRegistryKey.get(), L"EDID", nullptr, nullptr,
314 reinterpret_cast<unsigned char *>(edidData.data()), &edidDataSize)
315 != ERROR_SUCCESS) {
316 qCDebug(lcQpaScreen) << u"Unable to get EDID from the Registry for %1:"_s.arg(
317 data.deviceName)
318 << QSystemError::windowsString();
319 continue;
320 }
321
322 QEdidParser edid;
323
324 if (!edid.parse(edidData)) {
325 qCDebug(lcQpaScreen) << "Invalid EDID blob for" << data.deviceName;
326 continue;
327 }
328
329 // We skip edid.identifier because it is unreliable, and a better option
330 // is already available through DisplayConfigGetDeviceInfo (see below).
331 names << QString::fromWCharArray(deviceName.monitorFriendlyDeviceName);
332 manufacturers << edid.manufacturer;
333 models << edid.model;
334 serialNumbers << edid.serialNumber;
335 }
336
337 data.name = names.join(u"|"_s);
338 data.manufacturer = manufacturers.join(u"|"_s);
339 data.model = models.join(u"|"_s);
340 data.serialNumber = serialNumbers.join(u"|"_s);
341}
342
343static QColorSpace resolveColorSpace(HMONITOR hMonitor,
344 const MONITORINFOEX &info,
345 const std::vector<DISPLAYCONFIG_PATH_INFO> &pathGroup)
346{
347 qCDebug(lcQpaScreen) << "Resolving color space for" << QString::fromWCharArray(info.szDevice);
348
349 LPWSTR profileName = [&]() -> LPWSTR {
350 if (!pathGroup.empty()) {
351 const auto &sourceInfo = pathGroup[0].sourceInfo;
352 WCS_PROFILE_MANAGEMENT_SCOPE scope = WCS_PROFILE_MANAGEMENT_SCOPE_SYSTEM_WIDE;
353 if (SUCCEEDED(windowsColorSpaceFunctions->getDisplayUserScope(
354 sourceInfo.adapterId, sourceInfo.id, &scope))) {
355 LPWSTR profileName = nullptr;
356 if (SUCCEEDED(windowsColorSpaceFunctions->getDisplayDefault(
357 scope, sourceInfo.adapterId, sourceInfo.id, CPT_ICC,
358 CPST_RGB_WORKING_SPACE, &profileName))) {
359 return profileName;
360 } else {
361 return nullptr;
362 }
363 }
364 }
365
366 if (const HDC hdc = CreateDC(info.szDevice, nullptr, nullptr, nullptr)) {
367 const auto freeHdc = qScopeGuard([&]{ DeleteDC(hdc); });
368 DWORD colorProfilePathLength = MAX_PATH;
369 LPWSTR profileName = reinterpret_cast<LPWSTR>(LocalAlloc(LPTR, MAX_PATH * sizeof(WCHAR)));
370 if (GetICMProfile(hdc, &colorProfilePathLength, profileName))
371 return profileName;
372 }
373
374 return nullptr;
375 }();
376
377 if (profileName) {
378 qCDebug(lcQpaScreen) << "Found color profile" << QString::fromWCharArray(profileName);
379 const auto freeProfile = qScopeGuard([&]{ LocalFree(profileName); });
380
381 PROFILE profile;
382 profile.dwType = PROFILE_FILENAME;
383 profile.pProfileData = profileName;
384 profile.cbDataSize = DWORD(wcslen(profileName) * sizeof(wchar_t));
385 if (HPROFILE hProfile = OpenColorProfile(&profile, PROFILE_READ, FILE_SHARE_READ, OPEN_EXISTING)) {
386 const auto closeProfile = qScopeGuard([&]{ CloseColorProfile(hProfile); });
387
388 // Qt can only read ICC profiles, so convert from WCS profile if needed
389 if (HPROFILE wcsProfile = WcsCreateIccProfile(hProfile, WCS_ICCONLY)) {
390 CloseColorProfile(hProfile);
391 hProfile = wcsProfile;
392 }
393
394 DWORD iccDataSize = 0;
395 GetColorProfileFromHandle(hProfile, nullptr, &iccDataSize);
396 QByteArray iccData(iccDataSize, Qt::Uninitialized);
397 if (GetColorProfileFromHandle(hProfile,
398 reinterpret_cast<BYTE*>(iccData.data()), &iccDataSize))
399 return QColorSpace::fromIccProfile(iccData);
400 }
401 } else {
402 // No profile is associated with the screen, or Advanced Color is active,
403 // in which case any calls to the color profile management APIs to get the
404 // profile for a display will return "no profile", regardless of what
405 // profiles are actually installed.
406
407#if QT_CONFIG(cpp_winrt)
408 // Try to to resolve what color space the Windows compositor (DWM) is
409 // working in, and reflect that as the screen's preferred color space.
410
411 // https://learn.microsoft.com/nb-no/windows/win32/api/windows.graphics.display.interop
412 if (static bool haveInterop = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11_22H2; haveInterop) {
413 using namespace winrt::Windows::Foundation;
414 using namespace winrt::Windows::Graphics::Display;
415
416 try {
417 DisplayInformation displayInfo = nullptr;
418 auto factory = winrt::get_activation_factory<DisplayInformation, IDisplayInformationStaticsInterop>();
419 if (SUCCEEDED(factory->GetForMonitor(hMonitor, winrt::guid_of<DisplayInformation>(), winrt::put_abi(displayInfo)))) {
420 qCDebug(lcQpaScreen) << "Checking Advanced Color preferences";
421 AdvancedColorInfo advancedColorInfo = displayInfo.GetAdvancedColorInfo();
422 switch (advancedColorInfo.CurrentAdvancedColorKind()) {
423 case AdvancedColorKind::StandardDynamicRange:
424 // The display only supports standard dynamic range. In this case, it is safe to assume
425 // that OS composition is being done using an RGB:8 surface encoded as sRGB gamma.
426 return QColorSpace::SRgb;
427 case AdvancedColorKind::WideColorGamut:
428 // The display supports Wide Color Gamut. In this case, it is safe to assume that OS
429 // composition is being done using an RGB:FP16 surface encoded as scRGB gamma.
430 return QColorSpace::SRgbLinear;
431 case AdvancedColorKind::HighDynamicRange:
432 // The display supports high dynamic range. In this case, it is safe to assume that OS
433 // composition is being done using an RGB:FP16 surface encoded as scRGB gamma.
434 return QColorSpace::SRgbLinear;
435 }
436 }
437 } catch (const std::exception &ex) {
438 qCWarning(lcQpaScreen) << "Failed to query for advanced color info" << ex.what();
439 }
440 }
441#else
442 Q_UNUSED(hMonitor);
443#endif
444
445 // If we can't figure out the Advanced Color color-space above, we fall back to sRGB
446 qCDebug(lcQpaScreen) << "No color profile or advanced color preference. Falling back to sRGB";
447 return QColorSpace::SRgb;
448 }
449
450 // We hit an error condition that didn't result in falling back to sRGB,
451 // so we conservatively report that we don't know the color space.
452 return QColorSpace();
453}
454
455static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data)
456{
457 MONITORINFOEX info;
458 memset(&info, 0, sizeof(MONITORINFOEX));
459 info.cbSize = sizeof(MONITORINFOEX);
460 if (GetMonitorInfo(hMonitor, &info) == FALSE)
461 return false;
462
463 data->hMonitor = hMonitor;
464 data->geometry = QRect(QPoint(info.rcMonitor.left, info.rcMonitor.top), QPoint(info.rcMonitor.right - 1, info.rcMonitor.bottom - 1));
465 data->availableGeometry = QRect(QPoint(info.rcWork.left, info.rcWork.top), QPoint(info.rcWork.right - 1, info.rcWork.bottom - 1));
466 data->deviceName = QString::fromWCharArray(info.szDevice);
467 const auto pathGroup = getPathInfo(info);
468 if (!pathGroup.empty()) {
469 setMonitorDataFromSetupApi(*data, pathGroup);
470 }
471 if (data->name.isEmpty())
472 data->name = data->deviceName;
473 if (data->deviceName == u"WinDisc") {
474 data->flags |= QWindowsScreenData::LockScreen;
475 } else {
476 if (const HDC hdc = CreateDC(info.szDevice, nullptr, nullptr, nullptr)) {
477 const QDpi dpi = monitorDPI(hMonitor);
478 data->dpi = dpi.first > 0 ? dpi : deviceDPI(hdc);
479 data->depth = GetDeviceCaps(hdc, BITSPIXEL);
480 data->format = data->depth == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32;
481 data->physicalSizeMM = QSizeF(GetDeviceCaps(hdc, HORZSIZE), GetDeviceCaps(hdc, VERTSIZE));
482 const int refreshRate = GetDeviceCaps(hdc, VREFRESH);
483 if (refreshRate > 1) // 0,1 means hardware default.
484 data->refreshRateHz = refreshRate;
485 DeleteDC(hdc);
486 } else {
487 qWarning("%s: Unable to obtain handle for monitor '%s', defaulting to %g DPI.",
488 __FUNCTION__, qPrintable(data->deviceName),
489 data->dpi.first);
490 } // CreateDC() failed
491 } // not lock screen
492
493 // ### We might want to consider storing adapterId/id from DISPLAYCONFIG_PATH_TARGET_INFO,
494 // if we are going to use DISPLAYCONFIG lookups more.
495 if (!pathGroup.empty()) {
496 // The first element in the clone group is the main monitor.
497 const auto &pathInfo = pathGroup[0];
498 switch (pathInfo.targetInfo.rotation) {
499 case DISPLAYCONFIG_ROTATION_IDENTITY:
500 data->orientation = Qt::LandscapeOrientation;
501 break;
502 case DISPLAYCONFIG_ROTATION_ROTATE90:
503 data->orientation = Qt::PortraitOrientation;
504 break;
505 case DISPLAYCONFIG_ROTATION_ROTATE180:
506 data->orientation = Qt::InvertedLandscapeOrientation;
507 break;
508 case DISPLAYCONFIG_ROTATION_ROTATE270:
509 data->orientation = Qt::InvertedPortraitOrientation;
510 break;
511 case DISPLAYCONFIG_ROTATION_FORCE_UINT32:
512 Q_UNREACHABLE();
513 break;
514 }
515 if (pathInfo.targetInfo.refreshRate.Numerator && pathInfo.targetInfo.refreshRate.Denominator)
516 data->refreshRateHz = static_cast<qreal>(pathInfo.targetInfo.refreshRate.Numerator)
517 / pathInfo.targetInfo.refreshRate.Denominator;
518 } else {
519 data->orientation = data->geometry.height() > data->geometry.width()
520 ? Qt::PortraitOrientation
521 : Qt::LandscapeOrientation;
522 }
523
524 data->colorSpace = resolveColorSpace(hMonitor, info, pathGroup);
525 qCDebug(lcQpaScreen) << "Resolved" << data->colorSpace;
526
527 // EnumDisplayMonitors (as opposed to EnumDisplayDevices) enumerates only
528 // virtual desktop screens.
529 data->flags |= QWindowsScreenData::VirtualDesktop;
530 if (info.dwFlags & MONITORINFOF_PRIMARY)
531 data->flags |= QWindowsScreenData::PrimaryScreen;
532 return true;
533}
534
535// from monitorData, taking WindowsScreenDataList as LPARAM
536BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM p)
537{
538 QWindowsScreenData data;
539 if (monitorData(hMonitor, &data)) {
540 auto *result = reinterpret_cast<WindowsScreenDataList *>(p);
541 auto it = std::find_if(result->rbegin(), result->rend(),
542 [&data](QWindowsScreenData i){ return i.name == data.name; });
543 if (it != result->rend()) {
544 int previousIndex = 1;
545 if (it->deviceIndex.has_value())
546 previousIndex = it->deviceIndex.value();
547 else
548 (*it).deviceIndex = 1;
549 data.deviceIndex = previousIndex + 1;
550 }
551 // QWindowSystemInterface::handleScreenAdded() documentation specifies that first
552 // added screen will be the primary screen, so order accordingly.
553 // Note that the side effect of this policy is that there is no way to change primary
554 // screen reported by Qt, unless we want to delete all existing screens and add them
555 // again whenever primary screen changes.
556 if (data.flags & QWindowsScreenData::PrimaryScreen)
557 result->prepend(data);
558 else
559 result->append(data);
560 }
561 return TRUE;
562}
563
565{
566 WindowsScreenDataList result;
567 EnumDisplayMonitors(nullptr, nullptr, monitorEnumCallback, reinterpret_cast<LPARAM>(&result));
568 return result;
569}
570
571#ifndef QT_NO_DEBUG_STREAM
572static QDebug operator<<(QDebug dbg, const QWindowsScreenData &d)
573{
574 QDebugStateSaver saver(dbg);
575 dbg.nospace();
576 dbg.noquote();
577 dbg << "Screen \"" << d.name << "\" " << d.geometry.width() << 'x' << d.geometry.height() << '+'
578 << d.geometry.x() << '+' << d.geometry.y() << " avail: " << d.availableGeometry.width()
579 << 'x' << d.availableGeometry.height() << '+' << d.availableGeometry.x() << '+'
580 << d.availableGeometry.y() << " physical: " << d.physicalSizeMM.width() << 'x'
581 << d.physicalSizeMM.height() << " DPI: " << d.dpi.first << 'x' << d.dpi.second
582 << " Depth: " << d.depth << " Format: " << d.format << " hMonitor: " << d.hMonitor
583 << " device name: " << d.deviceName << " manufacturer: " << d.manufacturer
584 << " model: " << d.model << " serial number: " << d.serialNumber
585 << " color space: " << d.colorSpace;
586 if (d.flags & QWindowsScreenData::PrimaryScreen)
587 dbg << " primary";
588 if (d.flags & QWindowsScreenData::VirtualDesktop)
589 dbg << " virtual desktop";
590 if (d.flags & QWindowsScreenData::LockScreen)
591 dbg << " lock screen";
592 return dbg;
593}
594#endif // !QT_NO_DEBUG_STREAM
595
596/*!
597 \class QWindowsScreen
598 \brief Windows screen.
599 \sa QWindowsScreenManager
600 \internal
601*/
602
603QWindowsScreen::QWindowsScreen(const QWindowsScreenData &data) :
604 m_data(data)
605#ifndef QT_NO_CURSOR
606 , m_cursor(new QWindowsCursor(this))
607#endif
608{
609}
610
612{
613 return m_data.deviceIndex.has_value()
614 ? (u"%1 (%2)"_s).arg(m_data.name, QString::number(m_data.deviceIndex.value()))
615 : m_data.name;
616}
617
618QPixmap QWindowsScreen::grabWindow(WId window, int xIn, int yIn, int width, int height) const
619{
620 QSize windowSize;
621 int x = xIn;
622 int y = yIn;
623 HWND hwnd = reinterpret_cast<HWND>(window);
624 if (hwnd) {
625 RECT r;
626 GetClientRect(hwnd, &r);
627 windowSize = QSize(r.right - r.left, r.bottom - r.top);
628 } else {
629 // Grab current screen. The client rectangle of GetDesktopWindow() is the
630 // primary screen, but it is possible to grab other screens from it.
631 hwnd = GetDesktopWindow();
632 const QRect screenGeometry = geometry();
633 windowSize = screenGeometry.size();
634 // When dpi awareness is not set to PerMonitor, windows reports primary display or dummy
635 // DPI for all displays, so xIn and yIn and windowSize are calculated with a wrong DPI,
636 // so we need to recalculate them using the actual screen size we get from
637 // EnumDisplaySettings api.
639 if (dpiAwareness != QtWindows::DpiAwareness::PerMonitor &&
640 dpiAwareness != QtWindows::DpiAwareness::PerMonitorVersion2) {
641 MONITORINFOEX info = {};
642 info.cbSize = sizeof(MONITORINFOEX);
643 if (GetMonitorInfo(handle(), &info)) {
644 DEVMODE dm = {};
645 dm.dmSize = sizeof(dm);
646 if (EnumDisplaySettings(info.szDevice, ENUM_CURRENT_SETTINGS, &dm)) {
647 qreal scale = static_cast<qreal>(dm.dmPelsWidth) / windowSize.width();
648 x = static_cast<int>(static_cast<qreal>(x) * scale);
649 y = static_cast<int>(static_cast<qreal>(y) * scale);
650 windowSize = QSize(dm.dmPelsWidth, dm.dmPelsHeight);
651 }
652 }
653 }
654 x += screenGeometry.x();
655 y += screenGeometry.y();
656 }
657
658 if (width < 0)
659 width = windowSize.width() - xIn;
660 if (height < 0)
661 height = windowSize.height() - yIn;
662
663 // Create and setup bitmap
664 HDC display_dc = GetDC(nullptr);
665 HDC bitmap_dc = CreateCompatibleDC(display_dc);
666 HBITMAP bitmap = CreateCompatibleBitmap(display_dc, width, height);
667 HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap);
668
669 // copy data
670 HDC window_dc = GetDC(hwnd);
671 BitBlt(bitmap_dc, 0, 0, width, height, window_dc, x, y, SRCCOPY | CAPTUREBLT);
672
673 // clean up all but bitmap
674 ReleaseDC(hwnd, window_dc);
675 SelectObject(bitmap_dc, null_bitmap);
676 DeleteDC(bitmap_dc);
677
678 const QPixmap pixmap = qt_pixmapFromWinHBITMAP(bitmap);
679
680 DeleteObject(bitmap);
681 ReleaseDC(nullptr, display_dc);
682
683 return pixmap;
684}
685
686/*!
687 \brief Find a top level window taking the flags of ChildWindowFromPointEx.
688*/
689
690QWindow *QWindowsScreen::topLevelAt(const QPoint &point) const
691{
692 QWindow *result = nullptr;
693 if (QWindow *child = QWindowsScreen::windowAt(point, CWP_SKIPINVISIBLE))
694 result = QWindowsWindow::topLevelOf(child);
696 qCDebug(lcQpaScreen) <<__FUNCTION__ << point << result;
697 return result;
698}
699
700QWindow *QWindowsScreen::windowAt(const QPoint &screenPoint, unsigned flags)
701{
702 QWindow* result = nullptr;
703 if (QPlatformWindow *bw = QWindowsContext::instance()->
704 findPlatformWindowAt(GetDesktopWindow(), screenPoint, flags))
705 result = bw->window();
707 qCDebug(lcQpaScreen) <<__FUNCTION__ << screenPoint << " returns " << result;
708 return result;
709}
710
711/*!
712 \brief Determine siblings in a virtual desktop system.
713
714 Self is by definition a sibling, else collect all screens
715 within virtual desktop.
716*/
717
719{
720 QList<QPlatformScreen *> result;
721 if (m_data.flags & QWindowsScreenData::VirtualDesktop) {
722 const QWindowsScreenManager::WindowsScreenList screens
724 for (QWindowsScreen *screen : screens) {
725 if (screen->data().flags & QWindowsScreenData::VirtualDesktop)
726 result.push_back(screen);
727 }
728 } else {
729 result.push_back(const_cast<QWindowsScreen *>(this));
730 }
731 return result;
732}
733
734/*!
735 \brief Notify QWindowSystemInterface about changes of a screen and synchronize data.
736*/
737
738void QWindowsScreen::handleChanges(const QWindowsScreenData &newData)
739{
740 m_data.physicalSizeMM = newData.physicalSizeMM;
741
742 if (m_data.hMonitor != newData.hMonitor) {
743 qCDebug(lcQpaScreen) << "Monitor" << m_data.name
744 << "has had its hMonitor handle changed from"
745 << m_data.hMonitor << "to" << newData.hMonitor;
746 m_data.hMonitor = newData.hMonitor;
747 }
748
749 // QGuiApplicationPrivate::processScreenGeometryChange() checks and emits
750 // DPI and orientation as well, so, assign new values and emit DPI first.
751 const bool geometryChanged = m_data.geometry != newData.geometry
752 || m_data.availableGeometry != newData.availableGeometry;
753 const bool dpiChanged = !qFuzzyCompare(m_data.dpi.first, newData.dpi.first)
754 || !qFuzzyCompare(m_data.dpi.second, newData.dpi.second);
755 const bool orientationChanged = m_data.orientation != newData.orientation;
756 const bool primaryChanged = (newData.flags & QWindowsScreenData::PrimaryScreen)
757 && !(m_data.flags & QWindowsScreenData::PrimaryScreen);
758 const bool refreshRateChanged = m_data.refreshRateHz != newData.refreshRateHz;
759
760 m_data.dpi = newData.dpi;
761 m_data.orientation = newData.orientation;
762 m_data.geometry = newData.geometry;
763 m_data.availableGeometry = newData.availableGeometry;
764 m_data.flags = (m_data.flags & ~QWindowsScreenData::PrimaryScreen)
765 | (newData.flags & QWindowsScreenData::PrimaryScreen);
766 m_data.refreshRateHz = newData.refreshRateHz;
767 m_data.colorSpace = newData.colorSpace;
768
769 if (dpiChanged) {
770 QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(),
771 newData.dpi.first,
772 newData.dpi.second);
773 }
774 if (orientationChanged)
775 QWindowSystemInterface::handleScreenOrientationChange(screen(), newData.orientation);
776 if (geometryChanged) {
777 QWindowSystemInterface::handleScreenGeometryChange(screen(),
778 newData.geometry, newData.availableGeometry);
779 }
780 if (primaryChanged)
781 QWindowSystemInterface::handlePrimaryScreenChanged(this);
782
783 if (refreshRateChanged)
784 QWindowSystemInterface::handleScreenRefreshRateChange(screen(), newData.refreshRateHz);
785}
786
788{
789 return m_data.hMonitor;
790}
791
792QRect QWindowsScreen::virtualGeometry(const QPlatformScreen *screen) // cf QScreen::virtualGeometry()
793{
794 QRect result;
795 const auto siblings = screen->virtualSiblings();
796 for (const QPlatformScreen *sibling : siblings)
797 result |= sibling->geometry();
798 return result;
799}
800
801bool QWindowsScreen::setOrientationPreference(Qt::ScreenOrientation o)
802{
803 bool result = false;
804 ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE;
805 switch (o) {
806 case Qt::PrimaryOrientation:
807 break;
808 case Qt::PortraitOrientation:
809 orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT;
810 break;
811 case Qt::LandscapeOrientation:
812 orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE;
813 break;
814 case Qt::InvertedPortraitOrientation:
815 orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED;
816 break;
817 case Qt::InvertedLandscapeOrientation:
818 orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED;
819 break;
820 }
821 result = SetDisplayAutoRotationPreferences(orientationPreference);
822 return result;
823}
824
825Qt::ScreenOrientation QWindowsScreen::orientationPreference()
826{
827 Qt::ScreenOrientation result = Qt::PrimaryOrientation;
828 ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE;
829 if (GetDisplayAutoRotationPreferences(&orientationPreference)) {
830 switch (orientationPreference) {
831 case ORIENTATION_PREFERENCE_NONE:
832 break;
833 case ORIENTATION_PREFERENCE_LANDSCAPE:
834 result = Qt::LandscapeOrientation;
835 break;
836 case ORIENTATION_PREFERENCE_PORTRAIT:
837 result = Qt::PortraitOrientation;
838 break;
839 case ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED:
840 result = Qt::InvertedLandscapeOrientation;
841 break;
842 case ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED:
843 result = Qt::InvertedPortraitOrientation;
844 break;
845 }
846 }
847 return result;
848}
849
850/*!
851 \brief Queries ClearType settings to check the pixel layout
852*/
854{
855 QPlatformScreen::SubpixelAntialiasingType type = QPlatformScreen::subpixelAntialiasingTypeHint();
856 if (type == QPlatformScreen::Subpixel_None) {
857 QSettings settings(R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Avalon.Graphics\DISPLAY1)"_L1,
858 QSettings::NativeFormat);
859 int registryValue = settings.value("PixelStructure"_L1, -1).toInt();
860 switch (registryValue) {
861 case 0:
862 type = QPlatformScreen::Subpixel_None;
863 break;
864 case 1:
865 type = QPlatformScreen::Subpixel_RGB;
866 break;
867 case 2:
868 type = QPlatformScreen::Subpixel_BGR;
869 break;
870 default:
871 type = QPlatformScreen::Subpixel_None;
872 break;
873 }
874 }
875 return type;
876}
877
878/*!
879 \class QWindowsScreenManager
880 \brief Manages a list of QWindowsScreen.
881
882 Listens for changes and notifies QWindowSystemInterface about changed/
883 added/deleted screens.
884
885 \sa QWindowsScreen
886 \internal
887*/
888
889LRESULT QT_WIN_CALLBACK qDisplayChangeObserverWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
890{
891 if (message == WM_DISPLAYCHANGE) {
892 qCDebug(lcQpaScreen) << "Handling WM_DISPLAYCHANGE";
893 if (QWindowsTheme *t = QWindowsTheme::instance())
894 t->displayChanged();
895 QWindowsWindow::displayChanged();
896 if (auto *context = QWindowsContext::instance())
897 context->screenManager().handleScreenChanges();
898 }
899
900 return DefWindowProc(hwnd, message, wParam, lParam);
901}
902
904
906{
907 qCDebug(lcQpaScreen) << "Initializing screen manager";
908
909 auto className = QWindowsWindowClassRegistry::instance()->registerWindowClass(
910 "ScreenChangeObserverWindow"_L1,
911 qDisplayChangeObserverWndProc);
912
913 // HWND_MESSAGE windows do not get WM_DISPLAYCHANGE, so we need to create
914 // a real top level window that we never show.
915 m_displayChangeObserver = CreateWindowEx(0, reinterpret_cast<LPCWSTR>(className.utf16()),
916 nullptr, WS_TILED, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
917 nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
918 Q_ASSERT(m_displayChangeObserver);
919
920 qCDebug(lcQpaScreen) << "Created display change observer" << m_displayChangeObserver;
921
922 // https://learn.microsoft.com/en-us/windows/win32/wcs/wcs-registry-keys
923 m_perUserColorProfileAssociationNotifier.reset(new QWinRegistryNotifier(HKEY_CURRENT_USER,
924 LR"(Software\Microsoft\Windows NT\CurrentVersion\ICM\ProfileAssociations\Display\{4d36e96e-e325-11ce-bfc1-08002be10318})"));
925 QObject::connect(m_perUserColorProfileAssociationNotifier.get(),
926 &QWinRegistryNotifier::valueChanged, [this] { handleScreenChanges(); });
927 m_systemWideColorProfileAssociationNotifier.reset(new QWinRegistryNotifier(HKEY_LOCAL_MACHINE,
928 LR"(SYSTEM\CurrentControlSet\Control\Class\{4d36e96e-e325-11ce-bfc1-08002be10318})"));
929 QObject::connect(m_systemWideColorProfileAssociationNotifier.get(),
930 &QWinRegistryNotifier::valueChanged, [this] { handleScreenChanges(); });
931
933}
934
936{
937 qCDebug(lcQpaScreen) << "Destroying display change observer" << m_displayChangeObserver;
938 DestroyWindow(m_displayChangeObserver);
939 m_displayChangeObserver = nullptr;
940}
941
943
948
949static inline int indexOfMonitor(const QWindowsScreenManager::WindowsScreenList &screens,
950 const QString &deviceName)
951{
952 for (int i= 0; i < screens.size(); ++i)
953 if (screens.at(i)->data().deviceName == deviceName)
954 return i;
955 return -1;
956}
957
958static inline int indexOfMonitor(const WindowsScreenDataList &screenData,
959 const QString &deviceName)
960{
961 for (int i = 0; i < screenData.size(); ++i)
962 if (screenData.at(i).deviceName == deviceName)
963 return i;
964 return -1;
965}
966
967// Move a window to a new virtual screen, accounting for varying sizes.
968static void moveToVirtualScreen(QWindow *w, const QScreen *newScreen)
969{
970 QRect geometry = w->geometry();
971 const QRect oldScreenGeometry = w->screen()->geometry();
972 const QRect newScreenGeometry = newScreen->geometry();
973 QPoint relativePosition = geometry.topLeft() - oldScreenGeometry.topLeft();
974 if (oldScreenGeometry.size() != newScreenGeometry.size()) {
975 const qreal factor =
976 qreal(QPoint(newScreenGeometry.width(), newScreenGeometry.height()).manhattanLength()) /
977 qreal(QPoint(oldScreenGeometry.width(), oldScreenGeometry.height()).manhattanLength());
978 relativePosition = (QPointF(relativePosition) * factor).toPoint();
979 }
980 geometry.moveTopLeft(relativePosition);
981 w->setGeometry(geometry);
982}
983
984void QWindowsScreenManager::addScreen(const QWindowsScreenData &screenData)
985{
986 auto *newScreen = new QWindowsScreen(screenData);
987 m_screens.push_back(newScreen);
988 QWindowSystemInterface::handleScreenAdded(newScreen,
989 screenData.flags & QWindowsScreenData::PrimaryScreen);
990 qCDebug(lcQpaScreen) << "New Monitor: " << screenData;
991
992 // When a new screen is attached Window might move windows to the new screen
993 // automatically, in which case they will get a WM_DPICHANGED event. But at
994 // that point we have not received WM_DISPLAYCHANGE yet, so we fail to reflect
995 // the new screen's DPI. To account for this we explicitly check for screen
996 // change here, now that we are processing the WM_DISPLAYCHANGE.
997 const auto allWindows = QGuiApplication::allWindows();
998 for (QWindow *w : allWindows) {
999 if (w->isVisible() && w->handle()) {
1000 if (QWindowsWindow *window = QWindowsWindow::windowsWindowOf(w))
1001 window->checkForScreenChanged(QWindowsWindow::ScreenChangeMode::FromScreenAdded);
1002 }
1003 }
1004}
1005
1006void QWindowsScreenManager::removeScreen(int index)
1007{
1008 qCDebug(lcQpaScreen) << "Removing Monitor:" << m_screens.at(index)->data();
1009 QPlatformScreen *platformScreen = m_screens.takeAt(index);
1010 QScreen *screen = platformScreen->screen();
1011 QScreen *primaryScreen = QGuiApplication::primaryScreen();
1012 // QTBUG-38650: When a screen is disconnected, Windows will automatically
1013 // move the Window to another screen. This will trigger a geometry change
1014 // event, but unfortunately after the screen destruction signal. To prevent
1015 // QtGui from automatically hiding the QWindow, pretend all Windows move to
1016 // the primary screen first (which is likely the correct, final screen).
1017 // QTBUG-39320: Windows does not automatically move WS_EX_TOOLWINDOW (dock) windows;
1018 // move those manually.
1019 if (screen != primaryScreen) {
1020 unsigned movedWindowCount = 0;
1021 const QWindowList tlws = QGuiApplication::topLevelWindows();
1022 for (QWindow *w : tlws) {
1023 if (w->screen() == screen && w->handle()) {
1024 if (w->isVisible() && w->windowState() != Qt::WindowMinimized
1025 && (QWindowsWindow::baseWindowOf(w)->exStyle() & WS_EX_TOOLWINDOW)) {
1026 moveToVirtualScreen(w, primaryScreen);
1027 } else {
1028 QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(w, primaryScreen);
1029 }
1030 ++movedWindowCount;
1031 }
1032 }
1033 if (movedWindowCount)
1034 QWindowSystemInterface::flushWindowSystemEvents();
1035 }
1036 QWindowSystemInterface::handleScreenRemoved(platformScreen);
1037}
1038
1039/*!
1040 \brief Synchronizes the screen list, adds new screens, removes deleted
1041 ones and propagates resolution changes to QWindowSystemInterface.
1042*/
1043
1045{
1046 // Look for changed monitors, add new ones
1047 const WindowsScreenDataList newDataList = monitorData();
1048 const bool lockScreen = newDataList.size() == 1 && (newDataList.front().flags & QWindowsScreenData::LockScreen);
1049 bool primaryScreenChanged = false;
1050 for (const QWindowsScreenData &newData : newDataList) {
1051 const int existingIndex = indexOfMonitor(m_screens, newData.deviceName);
1052 if (existingIndex != -1) {
1053 m_screens.at(existingIndex)->handleChanges(newData);
1054 if (existingIndex == 0)
1055 primaryScreenChanged = true;
1056 } else {
1057 addScreen(newData);
1058 } // exists
1059 } // for new screens.
1060 // Remove deleted ones but keep main monitors if we get only the
1061 // temporary lock screen to avoid window recreation (QTBUG-33062).
1062 if (!lockScreen) {
1063 for (int i = m_screens.size() - 1; i >= 0; --i) {
1064 if (indexOfMonitor(newDataList, m_screens.at(i)->data().deviceName) == -1)
1065 removeScreen(i);
1066 } // for existing screens
1067 } // not lock screen
1068 if (primaryScreenChanged) {
1069 if (auto theme = QWindowsTheme::instance()) // QTBUG-85734/Wine
1070 theme->refreshFonts();
1071 }
1072 return true;
1073}
1074
1076{
1077 // Delete screens in reverse order to avoid crash in case of multiple screens
1078 while (!m_screens.isEmpty())
1079 QWindowSystemInterface::handleScreenRemoved(m_screens.takeLast());
1080}
1081
1083{
1084 for (QWindowsScreen *scr : m_screens) {
1085 if (scr->geometry().contains(p))
1086 return scr;
1087 }
1088 return nullptr;
1089}
1090
1091const QWindowsScreen *QWindowsScreenManager::screenForMonitor(HMONITOR hMonitor) const
1092{
1093 if (hMonitor == nullptr)
1094 return nullptr;
1095 const auto it =
1096 std::find_if(m_screens.cbegin(), m_screens.cend(),
1097 [hMonitor](const QWindowsScreen *s)
1098 {
1099 return s->data().hMonitor == hMonitor
1100 && (s->data().flags & QWindowsScreenData::VirtualDesktop) != 0;
1101 });
1102 return it != m_screens.cend() ? *it : nullptr;
1103}
1104
1106{
1107 HMONITOR hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
1108 return screenForMonitor(hMonitor);
1109}
1110
1112{
1113 if (rect == nullptr)
1114 return nullptr;
1115 HMONITOR hMonitor = MonitorFromRect(rect, MONITOR_DEFAULTTONULL);
1116 return screenForMonitor(hMonitor);
1117}
1118
1119QT_END_NAMESPACE
\inmodule QtCore\reentrant
Definition qpoint.h:30
Singleton container for all relevant information.
QWindowsScreenManager & screenManager()
static QtWindows::DpiAwareness processDpiAwareness()
static QWindowsContext * instance()
Manages a list of QWindowsScreen.
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 QWindowsScreen * screenForRect(const RECT *rect) const
Windows screen.
QList< QPlatformScreen * > virtualSiblings() const override
Determine siblings in a virtual desktop system.
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.
QPlatformScreen::SubpixelAntialiasingType subpixelAntialiasingTypeHint() const override
Queries ClearType settings to check the pixel layout.
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 QWindowsTheme * instance()
static QWindowsWindowClassRegistry * instance()
static QColorSpace resolveColorSpace(HMONITOR hMonitor, const MONITORINFOEX &info, const std::vector< DISPLAYCONFIG_PATH_INFO > &pathGroup)
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)
static void setMonitorDataFromSetupApi(QWindowsScreenData &data, const std::vector< DISPLAYCONFIG_PATH_INFO > &pathGroup)
static int indexOfMonitor(const QWindowsScreenManager::WindowsScreenList &screens, const QString &deviceName)
static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data)
static QDpi monitorDPI(HMONITOR hMonitor)
#define WCS_ICCONLY
HRESULT getDisplayUserScope(LUID targetAdapterID, UINT32 sourceID, WCS_PROFILE_MANAGEMENT_SCOPE *scope)
HRESULT getDisplayDefault(WCS_PROFILE_MANAGEMENT_SCOPE scope, LUID targetAdapterID, UINT32 sourceID, COLORPROFILETYPE profileType, COLORPROFILESUBTYPE profileSubType, LPWSTR *profileName)