13#include <QtCore/qt_windows.h>
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>
26#include <QtGui/qscreen.h>
28#include <QtCore/qdebug.h>
35#include <shellscalingapi.h>
38#if QT_CONFIG(cpp_winrt) && __has_include(<windows.graphics.display.interop.h>)
39# define QT_USE_WINRT_DISPLAY_INTEROP
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>
51#define WCS_ICCONLY 0x00010000L
56using namespace Qt::StringLiterals;
62 HMODULE lib = QSystemLibrary::load(L"Mscms");
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")));
75 UINT32 sourceID, COLORPROFILETYPE profileType,
76 COLORPROFILESUBTYPE profileSubType, LPWSTR *profileName)
78 if (!colorProfileGetDisplayDefault)
80 return colorProfileGetDisplayDefault(scope, targetAdapterID, sourceID,
81 profileType, profileSubType, profileName);
85 WCS_PROFILE_MANAGEMENT_SCOPE *scope)
87 if (!colorProfileGetDisplayUserScope)
89 return colorProfileGetDisplayUserScope(targetAdapterID, sourceID, scope);
98 DisplayDefaultSignature colorProfileGetDisplayDefault =
nullptr;
99 DisplayUserScopeSignature colorProfileGetDisplayUserScope =
nullptr;
105 return QDpi(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY));
112 if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY)))
113 return QDpi(dpiX, dpiY);
120 std::vector<DISPLAYCONFIG_PATH_INFO> pathInfos;
121 std::vector<DISPLAYCONFIG_MODE_INFO> modeInfos;
125 UINT32 numPathArrayElements;
126 UINT32 numModeInfoArrayElements;
131 if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &numPathArrayElements,
132 &numModeInfoArrayElements) != ERROR_SUCCESS) {
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);
141 if (result != ERROR_SUCCESS)
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;
158 pathInfos.erase(discardThese, pathInfos.end());
165static float getMonitorSDRWhiteLevel(DISPLAYCONFIG_PATH_TARGET_INFO *targetInfo)
167 const float defaultSdrWhiteLevel = 200.0;
169 return defaultSdrWhiteLevel;
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;
182using WindowsScreenDataList = QList<QWindowsScreenData>;
186struct DiRegKeyHandleTraits
189 static Type invalidValue()
noexcept
192 return reinterpret_cast<HKEY>(INVALID_HANDLE_VALUE);
194 static bool close(Type handle)
noexcept {
return RegCloseKey(handle) == ERROR_SUCCESS; }
197using DiRegKeyHandle = QUniqueHandle<DiRegKeyHandleTraits>;
199struct DevInfoHandleTraits
201 using Type = HDEVINFO;
202 static Type invalidValue()
noexcept
204 return reinterpret_cast<HDEVINFO>(INVALID_HANDLE_VALUE);
206 static bool close(Type handle)
noexcept {
return SetupDiDestroyDeviceInfoList(handle) == TRUE; }
209using DevInfoHandle = QUniqueHandle<DevInfoHandleTraits>;
214 const std::vector<DISPLAYCONFIG_PATH_INFO> &pathGroup)
216 if (pathGroup.empty()) {
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);
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);
234 << u"Unable to get device information for %1:"_s.arg(pathGroup[0].targetInfo.id)
235 << QSystemError::windowsString(result);
241 QStringList manufacturers;
243 QStringList serialNumbers;
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) {
255 << u"Unable to get device information for %1:"_s.arg(path.targetInfo.id)
256 << QSystemError::windowsString(result);
261 constexpr GUID GUID_DEVINTERFACE_MONITOR = {
262 0xe6f07b5f, 0xee97, 0x4a90, { 0xb0, 0x76, 0x33, 0xf5, 0x7b, 0xf4, 0xea, 0xa7 }
264 const DevInfoHandle devInfo{ SetupDiGetClassDevs(
265 &GUID_DEVINTERFACE_MONITOR,
nullptr,
nullptr, DIGCF_DEVICEINTERFACE) };
267 if (!devInfo.isValid())
270 SP_DEVICE_INTERFACE_DATA deviceInterfaceData{};
271 deviceInterfaceData.cbSize =
sizeof(deviceInterfaceData);
273 if (!SetupDiOpenDeviceInterfaceW(devInfo.get(), deviceName.monitorDevicePath, DIODI_NO_ADD,
274 &deviceInterfaceData)) {
277 << u"Unable to open monitor interface to %1:"_s.arg(data.deviceName)
278 << QSystemError::windowsString();
282 DWORD requiredSize{ 0 };
283 if (SetupDiGetDeviceInterfaceDetailW(devInfo.get(), &deviceInterfaceData,
nullptr, 0,
284 &requiredSize,
nullptr)
285 || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
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();
301 const DiRegKeyHandle edidRegistryKey{ SetupDiOpenDevRegKey(
302 devInfo.get(), &deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ) };
304 if (!edidRegistryKey.isValid())
307 DWORD edidDataSize{ 0 };
308 if (RegQueryValueExW(edidRegistryKey.get(), L"EDID",
nullptr,
nullptr,
nullptr,
315 edidData.resize(edidDataSize);
317 if (RegQueryValueExW(edidRegistryKey.get(), L"EDID",
nullptr,
nullptr,
318 reinterpret_cast<
unsigned char *>(edidData.data()), &edidDataSize)
320 qCDebug(lcQpaScreen) << u"Unable to get EDID from the Registry for %1:"_s.arg(
322 << QSystemError::windowsString();
328 if (!edid.parse(edidData)) {
329 qCDebug(lcQpaScreen) <<
"Invalid EDID blob for" << data.deviceName;
335 names << QString::fromWCharArray(deviceName.monitorFriendlyDeviceName);
336 manufacturers << edid.manufacturer;
337 models << edid.model;
338 serialNumbers << edid.serialNumber;
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);
348 const MONITORINFOEX &info,
349 const std::vector<DISPLAYCONFIG_PATH_INFO> &pathGroup)
351 qCDebug(lcQpaScreen) <<
"Resolving color space for" << QString::fromWCharArray(info.szDevice);
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))) {
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))
382 qCDebug(lcQpaScreen) <<
"Found color profile" << QString::fromWCharArray(profileName);
383 const auto freeProfile = qScopeGuard([&]{ LocalFree(profileName); });
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); });
393 if (HPROFILE wcsProfile = WcsCreateIccProfile(hProfile,
WCS_ICCONLY)) {
394 CloseColorProfile(hProfile);
395 hProfile = wcsProfile;
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);
411#ifdef QT_USE_WINRT_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;
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:
430 return QColorSpace::SRgb;
431 case AdvancedColorKind::WideColorGamut:
434 return QColorSpace::SRgbLinear;
435 case AdvancedColorKind::HighDynamicRange:
438 return QColorSpace::SRgbLinear;
441 }
catch (
const std::exception &ex) {
442 qCWarning(lcQpaScreen) <<
"Failed to query for advanced color info" << ex.what();
450 qCDebug(lcQpaScreen) <<
"No color profile or advanced color preference. Falling back to sRGB";
451 return QColorSpace::SRgb;
456 return QColorSpace();
459static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data)
462 memset(&info, 0,
sizeof(MONITORINFOEX));
463 info.cbSize =
sizeof(MONITORINFOEX);
464 if (GetMonitorInfo(hMonitor, &info) == FALSE)
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);
475 if (data->name.isEmpty())
476 data->name = data->deviceName;
477 if (data->deviceName == u"WinDisc") {
478 data->flags |= QWindowsScreenData::LockScreen;
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);
488 data->refreshRateHz = refreshRate;
491 qWarning(
"%s: Unable to obtain handle for monitor '%s', defaulting to %g DPI.",
492 __FUNCTION__, qPrintable(data->deviceName),
499 if (!pathGroup.empty()) {
501 const auto &pathInfo = pathGroup[0];
502 switch (pathInfo.targetInfo.rotation) {
503 case DISPLAYCONFIG_ROTATION_IDENTITY:
504 data->orientation = Qt::LandscapeOrientation;
506 case DISPLAYCONFIG_ROTATION_ROTATE90:
507 data->orientation = Qt::PortraitOrientation;
509 case DISPLAYCONFIG_ROTATION_ROTATE180:
510 data->orientation = Qt::InvertedLandscapeOrientation;
512 case DISPLAYCONFIG_ROTATION_ROTATE270:
513 data->orientation = Qt::InvertedPortraitOrientation;
515 case DISPLAYCONFIG_ROTATION_FORCE_UINT32:
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;
523 data->orientation = data->geometry.height() > data->geometry.width()
524 ? Qt::PortraitOrientation
525 : Qt::LandscapeOrientation;
528 data->colorSpace = resolveColorSpace(hMonitor, info, pathGroup);
529 qCDebug(lcQpaScreen) <<
"Resolved" << data->colorSpace;
533 data->flags |= QWindowsScreenData::VirtualDesktop;
534 if (info.dwFlags & MONITORINFOF_PRIMARY)
535 data->flags |= QWindowsScreenData::PrimaryScreen;
540BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM p)
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();
552 (*it).deviceIndex = 1;
553 data.deviceIndex = previousIndex + 1;
560 if (data.flags & QWindowsScreenData::PrimaryScreen)
561 result->prepend(data);
563 result->append(data);
570 WindowsScreenDataList result;
571 EnumDisplayMonitors(
nullptr,
nullptr, monitorEnumCallback,
reinterpret_cast<LPARAM>(&result));
575#ifndef QT_NO_DEBUG_STREAM
576static QDebug operator<<(QDebug dbg,
const QWindowsScreenData &d)
578 QDebugStateSaver saver(dbg);
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)
592 if (d.flags & QWindowsScreenData::VirtualDesktop)
593 dbg <<
" virtual desktop";
594 if (d.flags & QWindowsScreenData::LockScreen)
595 dbg <<
" lock screen";
601
602
603
604
605
610 , m_cursor(
new QWindowsCursor(
this))
617 return m_data.deviceIndex.has_value()
618 ? (u"%1 (%2)"_s).arg(m_data.name, QString::number(m_data.deviceIndex.value()))
627 HWND hwnd =
reinterpret_cast<HWND>(window);
630 GetClientRect(hwnd, &r);
631 windowSize = QSize(r.right - r.left, r.bottom - r.top);
635 hwnd = GetDesktopWindow();
636 const QRect screenGeometry = geometry();
637 windowSize = screenGeometry.size();
645 MONITORINFOEX info = {};
646 info.cbSize =
sizeof(MONITORINFOEX);
647 if (GetMonitorInfo(handle(), &info)) {
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);
658 x += screenGeometry.x();
659 y += screenGeometry.y();
663 width = windowSize.width() - xIn;
665 height = windowSize.height() - yIn;
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);
674 HDC window_dc = GetDC(hwnd);
675 BitBlt(bitmap_dc, 0, 0, width, height, window_dc, x, y, SRCCOPY | CAPTUREBLT);
678 ReleaseDC(hwnd, window_dc);
679 SelectObject(bitmap_dc, null_bitmap);
682 const QPixmap pixmap = qt_pixmapFromWinHBITMAP(bitmap);
684 DeleteObject(bitmap);
685 ReleaseDC(
nullptr, display_dc);
691
692
696 QWindow *result =
nullptr;
697 if (QWindow *child = QWindowsScreen::windowAt(point, CWP_SKIPINVISIBLE))
698 result = QWindowsWindow::topLevelOf(child);
700 qCDebug(lcQpaScreen) <<
__FUNCTION__ << point << result;
706 QWindow* result =
nullptr;
708 findPlatformWindowAt(GetDesktopWindow(), screenPoint, flags))
709 result = bw->window();
711 qCDebug(lcQpaScreen) <<
__FUNCTION__ << screenPoint <<
" returns " << result;
716
717
718
719
720
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);
739
740
744 m_data.physicalSizeMM = newData.physicalSizeMM;
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;
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;
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;
774 QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(),
778 if (orientationChanged)
779 QWindowSystemInterface::handleScreenOrientationChange(screen(), newData.orientation);
780 if (geometryChanged) {
781 QWindowSystemInterface::handleScreenGeometryChange(screen(),
782 newData.geometry, newData.availableGeometry);
785 QWindowSystemInterface::handlePrimaryScreenChanged(
this);
787 if (refreshRateChanged)
788 QWindowSystemInterface::handleScreenRefreshRateChange(screen(), newData.refreshRateHz);
793 return m_data.hMonitor;
799 const auto siblings = screen->virtualSiblings();
800 for (
const QPlatformScreen *sibling : siblings)
801 result |= sibling->geometry();
808 ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE;
810 case Qt::PrimaryOrientation:
812 case Qt::PortraitOrientation:
813 orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT;
815 case Qt::LandscapeOrientation:
816 orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE;
818 case Qt::InvertedPortraitOrientation:
819 orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED;
821 case Qt::InvertedLandscapeOrientation:
822 orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED;
825 result = SetDisplayAutoRotationPreferences(orientationPreference);
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:
837 case ORIENTATION_PREFERENCE_LANDSCAPE:
838 result = Qt::LandscapeOrientation;
840 case ORIENTATION_PREFERENCE_PORTRAIT:
841 result = Qt::PortraitOrientation;
843 case ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED:
844 result = Qt::InvertedLandscapeOrientation;
846 case ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED:
847 result = Qt::InvertedPortraitOrientation;
855
856
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) {
866 type = QPlatformScreen::Subpixel_None;
869 type = QPlatformScreen::Subpixel_RGB;
872 type = QPlatformScreen::Subpixel_BGR;
875 type = QPlatformScreen::Subpixel_None;
883
884
885
886
887
888
889
890
891
893LRESULT QT_WIN_CALLBACK qDisplayChangeObserverWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
895 if (message == WM_DISPLAYCHANGE) {
896 qCDebug(lcQpaScreen) <<
"Handling WM_DISPLAYCHANGE";
897 if (QWindowsTheme *t = QWindowsTheme::instance())
899 QWindowsWindow::displayChanged();
900 if (
auto *context = QWindowsContext::instance())
901 context->screenManager().handleScreenChanges();
904 return DefWindowProc(hwnd, message, wParam, lParam);
911 qCDebug(lcQpaScreen) <<
"Initializing screen manager";
914 "ScreenChangeObserverWindow"_L1,
915 qDisplayChangeObserverWndProc);
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);
924 qCDebug(lcQpaScreen) <<
"Created display change observer" << m_displayChangeObserver;
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(); });
941 qCDebug(lcQpaScreen) <<
"Destroying display change observer" << m_displayChangeObserver;
942 DestroyWindow(m_displayChangeObserver);
943 m_displayChangeObserver =
nullptr;
953static inline int indexOfMonitor(
const QWindowsScreenManager::WindowsScreenList &screens,
954 const QString &deviceName)
956 for (
int i= 0; i < screens.size(); ++i)
957 if (screens.at(i)->data().deviceName == deviceName)
963 const QString &deviceName)
965 for (
int i = 0; i < screenData.size(); ++i)
966 if (screenData.at(i).deviceName == deviceName)
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()) {
980 qreal(
QPoint(newScreenGeometry.width(), newScreenGeometry.height()).manhattanLength()) /
981 qreal(
QPoint(oldScreenGeometry.width(), oldScreenGeometry.height()).manhattanLength());
982 relativePosition = (QPointF(relativePosition) * factor).toPoint();
984 geometry.moveTopLeft(relativePosition);
985 w->setGeometry(geometry);
991 m_screens.push_back(newScreen);
992 QWindowSystemInterface::handleScreenAdded(newScreen,
993 screenData.flags & QWindowsScreenData::PrimaryScreen);
994 qCDebug(lcQpaScreen) <<
"New Monitor: " << screenData;
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);
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();
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);
1032 QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(w, primaryScreen);
1037 if (movedWindowCount)
1038 QWindowSystemInterface::flushWindowSystemEvents();
1040 QWindowSystemInterface::handleScreenRemoved(platformScreen);
1044
1045
1046
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;
1067 for (
int i = m_screens.size() - 1; i >= 0; --i) {
1068 if (indexOfMonitor(newDataList, m_screens.at(i)->data().deviceName) == -1)
1072 if (primaryScreenChanged) {
1082 while (!m_screens.isEmpty())
1083 QWindowSystemInterface::handleScreenRemoved(m_screens.takeLast());
1088 for (QWindowsScreen *scr : m_screens) {
1089 if (scr->geometry().contains(p))
1097 if (hMonitor ==
nullptr)
1100 std::find_if(m_screens.cbegin(), m_screens.cend(),
1101 [hMonitor](
const QWindowsScreen *s)
1103 return s->data().hMonitor == hMonitor
1104 && (s->data().flags & QWindowsScreenData::VirtualDesktop) != 0;
1106 return it != m_screens.cend() ? *it :
nullptr;
1111 HMONITOR hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
1112 return screenForMonitor(hMonitor);
1117 if (rect ==
nullptr)
1119 HMONITOR hMonitor = MonitorFromRect(rect, MONITOR_DEFAULTTONULL);
1120 return screenForMonitor(hMonitor);
\inmodule QtCore\reentrant
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
static bool isSingleScreen()
const QWindowsScreen * screenForRect(const RECT *rect) const
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)
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)
WindowsColorSpaceFunctions()