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