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