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)
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>
46#define WCS_ICCONLY 0x00010000L
51using namespace Qt::StringLiterals;
57 HMODULE lib = QSystemLibrary::load(L"Mscms");
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")));
70 UINT32 sourceID, COLORPROFILETYPE profileType,
71 COLORPROFILESUBTYPE profileSubType, LPWSTR *profileName)
73 if (!colorProfileGetDisplayDefault)
75 return colorProfileGetDisplayDefault(scope, targetAdapterID, sourceID,
76 profileType, profileSubType, profileName);
80 WCS_PROFILE_MANAGEMENT_SCOPE *scope)
82 if (!colorProfileGetDisplayUserScope)
84 return colorProfileGetDisplayUserScope(targetAdapterID, sourceID, scope);
93 DisplayDefaultSignature colorProfileGetDisplayDefault =
nullptr;
94 DisplayUserScopeSignature colorProfileGetDisplayUserScope =
nullptr;
100 return QDpi(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY));
107 if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY)))
108 return QDpi(dpiX, dpiY);
115 std::vector<DISPLAYCONFIG_PATH_INFO> pathInfos;
116 std::vector<DISPLAYCONFIG_MODE_INFO> modeInfos;
120 UINT32 numPathArrayElements;
121 UINT32 numModeInfoArrayElements;
126 if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &numPathArrayElements,
127 &numModeInfoArrayElements) != ERROR_SUCCESS) {
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);
136 if (result != ERROR_SUCCESS)
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;
153 pathInfos.erase(discardThese, pathInfos.end());
160static float getMonitorSDRWhiteLevel(DISPLAYCONFIG_PATH_TARGET_INFO *targetInfo)
162 const float defaultSdrWhiteLevel = 200.0;
164 return defaultSdrWhiteLevel;
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;
177using WindowsScreenDataList = QList<QWindowsScreenData>;
181struct DiRegKeyHandleTraits
184 static Type invalidValue()
noexcept
187 return reinterpret_cast<HKEY>(INVALID_HANDLE_VALUE);
189 static bool close(Type handle)
noexcept {
return RegCloseKey(handle) == ERROR_SUCCESS; }
192using DiRegKeyHandle = QUniqueHandle<DiRegKeyHandleTraits>;
194struct DevInfoHandleTraits
196 using Type = HDEVINFO;
197 static Type invalidValue()
noexcept
199 return reinterpret_cast<HDEVINFO>(INVALID_HANDLE_VALUE);
201 static bool close(Type handle)
noexcept {
return SetupDiDestroyDeviceInfoList(handle) == TRUE; }
204using DevInfoHandle = QUniqueHandle<DevInfoHandleTraits>;
209 const std::vector<DISPLAYCONFIG_PATH_INFO> &pathGroup)
211 if (pathGroup.empty()) {
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);
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);
229 << u"Unable to get device information for %1:"_s.arg(pathGroup[0].targetInfo.id)
230 << QSystemError::windowsString(result);
236 QStringList manufacturers;
238 QStringList serialNumbers;
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) {
250 << u"Unable to get device information for %1:"_s.arg(path.targetInfo.id)
251 << QSystemError::windowsString(result);
256 constexpr GUID GUID_DEVINTERFACE_MONITOR = {
257 0xe6f07b5f, 0xee97, 0x4a90, { 0xb0, 0x76, 0x33, 0xf5, 0x7b, 0xf4, 0xea, 0xa7 }
259 const DevInfoHandle devInfo{ SetupDiGetClassDevs(
260 &GUID_DEVINTERFACE_MONITOR,
nullptr,
nullptr, DIGCF_DEVICEINTERFACE) };
262 if (!devInfo.isValid())
265 SP_DEVICE_INTERFACE_DATA deviceInterfaceData{};
266 deviceInterfaceData.cbSize =
sizeof(deviceInterfaceData);
268 if (!SetupDiOpenDeviceInterfaceW(devInfo.get(), deviceName.monitorDevicePath, DIODI_NO_ADD,
269 &deviceInterfaceData)) {
272 << u"Unable to open monitor interface to %1:"_s.arg(data.deviceName)
273 << QSystemError::windowsString();
277 DWORD requiredSize{ 0 };
278 if (SetupDiGetDeviceInterfaceDetailW(devInfo.get(), &deviceInterfaceData,
nullptr, 0,
279 &requiredSize,
nullptr)
280 || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
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();
296 const DiRegKeyHandle edidRegistryKey{ SetupDiOpenDevRegKey(
297 devInfo.get(), &deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ) };
299 if (!edidRegistryKey.isValid())
302 DWORD edidDataSize{ 0 };
303 if (RegQueryValueExW(edidRegistryKey.get(), L"EDID",
nullptr,
nullptr,
nullptr,
310 edidData.resize(edidDataSize);
312 if (RegQueryValueExW(edidRegistryKey.get(), L"EDID",
nullptr,
nullptr,
313 reinterpret_cast<
unsigned char *>(edidData.data()), &edidDataSize)
315 qCDebug(lcQpaScreen) << u"Unable to get EDID from the Registry for %1:"_s.arg(
317 << QSystemError::windowsString();
323 if (!edid.parse(edidData)) {
324 qCDebug(lcQpaScreen) <<
"Invalid EDID blob for" << data.deviceName;
330 names << QString::fromWCharArray(deviceName.monitorFriendlyDeviceName);
331 manufacturers << edid.manufacturer;
332 models << edid.model;
333 serialNumbers << edid.serialNumber;
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);
343 const MONITORINFOEX &info,
344 const std::vector<DISPLAYCONFIG_PATH_INFO> &pathGroup)
346 qCDebug(lcQpaScreen) <<
"Resolving color space for" << QString::fromWCharArray(info.szDevice);
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))) {
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))
377 qCDebug(lcQpaScreen) <<
"Found color profile" << QString::fromWCharArray(profileName);
378 const auto freeProfile = qScopeGuard([&]{ LocalFree(profileName); });
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); });
388 if (HPROFILE wcsProfile = WcsCreateIccProfile(hProfile,
WCS_ICCONLY)) {
389 CloseColorProfile(hProfile);
390 hProfile = wcsProfile;
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);
406#if QT_CONFIG(cpp_winrt)
411 if (
static bool haveInterop = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11_22H2; haveInterop) {
412 using namespace winrt::Windows::Foundation;
413 using namespace winrt::Windows::Graphics::Display;
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:
425 return QColorSpace::SRgb;
426 case AdvancedColorKind::WideColorGamut:
429 return QColorSpace::SRgbLinear;
430 case AdvancedColorKind::HighDynamicRange:
433 return QColorSpace::SRgbLinear;
436 }
catch (
const std::exception &ex) {
437 qCWarning(lcQpaScreen) <<
"Failed to query for advanced color info" << ex.what();
445 qCDebug(lcQpaScreen) <<
"No color profile or advanced color preference. Falling back to sRGB";
446 return QColorSpace::SRgb;
451 return QColorSpace();
454static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data)
457 memset(&info, 0,
sizeof(MONITORINFOEX));
458 info.cbSize =
sizeof(MONITORINFOEX);
459 if (GetMonitorInfo(hMonitor, &info) == FALSE)
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);
470 if (data->name.isEmpty())
471 data->name = data->deviceName;
472 if (data->deviceName == u"WinDisc") {
473 data->flags |= QWindowsScreenData::LockScreen;
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);
483 data->refreshRateHz = refreshRate;
486 qWarning(
"%s: Unable to obtain handle for monitor '%s', defaulting to %g DPI.",
487 __FUNCTION__, qPrintable(data->deviceName),
494 if (!pathGroup.empty()) {
496 const auto &pathInfo = pathGroup[0];
497 switch (pathInfo.targetInfo.rotation) {
498 case DISPLAYCONFIG_ROTATION_IDENTITY:
499 data->orientation = Qt::LandscapeOrientation;
501 case DISPLAYCONFIG_ROTATION_ROTATE90:
502 data->orientation = Qt::PortraitOrientation;
504 case DISPLAYCONFIG_ROTATION_ROTATE180:
505 data->orientation = Qt::InvertedLandscapeOrientation;
507 case DISPLAYCONFIG_ROTATION_ROTATE270:
508 data->orientation = Qt::InvertedPortraitOrientation;
510 case DISPLAYCONFIG_ROTATION_FORCE_UINT32:
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;
518 data->orientation = data->geometry.height() > data->geometry.width()
519 ? Qt::PortraitOrientation
520 : Qt::LandscapeOrientation;
523 data->colorSpace = resolveColorSpace(hMonitor, info, pathGroup);
524 qCDebug(lcQpaScreen) <<
"Resolved" << data->colorSpace;
528 data->flags |= QWindowsScreenData::VirtualDesktop;
529 if (info.dwFlags & MONITORINFOF_PRIMARY)
530 data->flags |= QWindowsScreenData::PrimaryScreen;
535BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM p)
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();
547 (*it).deviceIndex = 1;
548 data.deviceIndex = previousIndex + 1;
555 if (data.flags & QWindowsScreenData::PrimaryScreen)
556 result->prepend(data);
558 result->append(data);
565 WindowsScreenDataList result;
566 EnumDisplayMonitors(
nullptr,
nullptr, monitorEnumCallback,
reinterpret_cast<LPARAM>(&result));
570#ifndef QT_NO_DEBUG_STREAM
571static QDebug operator<<(QDebug dbg,
const QWindowsScreenData &d)
573 QDebugStateSaver saver(dbg);
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)
587 if (d.flags & QWindowsScreenData::VirtualDesktop)
588 dbg <<
" virtual desktop";
589 if (d.flags & QWindowsScreenData::LockScreen)
590 dbg <<
" lock screen";
596
597
598
599
600
605 , m_cursor(
new QWindowsCursor(
this))
612 return m_data.deviceIndex.has_value()
613 ? (u"%1 (%2)"_s).arg(m_data.name, QString::number(m_data.deviceIndex.value()))
622 HWND hwnd =
reinterpret_cast<HWND>(window);
625 GetClientRect(hwnd, &r);
626 windowSize = QSize(r.right - r.left, r.bottom - r.top);
630 hwnd = GetDesktopWindow();
631 const QRect screenGeometry = geometry();
632 windowSize = screenGeometry.size();
640 MONITORINFOEX info = {};
641 info.cbSize =
sizeof(MONITORINFOEX);
642 if (GetMonitorInfo(handle(), &info)) {
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);
653 x += screenGeometry.x();
654 y += screenGeometry.y();
658 width = windowSize.width() - xIn;
660 height = windowSize.height() - yIn;
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);
669 HDC window_dc = GetDC(hwnd);
670 BitBlt(bitmap_dc, 0, 0, width, height, window_dc, x, y, SRCCOPY | CAPTUREBLT);
673 ReleaseDC(hwnd, window_dc);
674 SelectObject(bitmap_dc, null_bitmap);
677 const QPixmap pixmap = qt_pixmapFromWinHBITMAP(bitmap);
679 DeleteObject(bitmap);
680 ReleaseDC(
nullptr, display_dc);
686
687
691 QWindow *result =
nullptr;
692 if (QWindow *child = QWindowsScreen::windowAt(point, CWP_SKIPINVISIBLE))
693 result = QWindowsWindow::topLevelOf(child);
695 qCDebug(lcQpaScreen) <<
__FUNCTION__ << point << result;
701 QWindow* result =
nullptr;
703 findPlatformWindowAt(GetDesktopWindow(), screenPoint, flags))
704 result = bw->window();
706 qCDebug(lcQpaScreen) <<
__FUNCTION__ << screenPoint <<
" returns " << result;
711
712
713
714
715
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);
734
735
739 m_data.physicalSizeMM = newData.physicalSizeMM;
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;
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;
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;
769 QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(),
773 if (orientationChanged)
774 QWindowSystemInterface::handleScreenOrientationChange(screen(), newData.orientation);
775 if (geometryChanged) {
776 QWindowSystemInterface::handleScreenGeometryChange(screen(),
777 newData.geometry, newData.availableGeometry);
780 QWindowSystemInterface::handlePrimaryScreenChanged(
this);
782 if (refreshRateChanged)
783 QWindowSystemInterface::handleScreenRefreshRateChange(screen(), newData.refreshRateHz);
788 return m_data.hMonitor;
794 const auto siblings = screen->virtualSiblings();
795 for (
const QPlatformScreen *sibling : siblings)
796 result |= sibling->geometry();
803 ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE;
805 case Qt::PrimaryOrientation:
807 case Qt::PortraitOrientation:
808 orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT;
810 case Qt::LandscapeOrientation:
811 orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE;
813 case Qt::InvertedPortraitOrientation:
814 orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED;
816 case Qt::InvertedLandscapeOrientation:
817 orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED;
820 result = SetDisplayAutoRotationPreferences(orientationPreference);
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:
832 case ORIENTATION_PREFERENCE_LANDSCAPE:
833 result = Qt::LandscapeOrientation;
835 case ORIENTATION_PREFERENCE_PORTRAIT:
836 result = Qt::PortraitOrientation;
838 case ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED:
839 result = Qt::InvertedLandscapeOrientation;
841 case ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED:
842 result = Qt::InvertedPortraitOrientation;
850
851
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) {
861 type = QPlatformScreen::Subpixel_None;
864 type = QPlatformScreen::Subpixel_RGB;
867 type = QPlatformScreen::Subpixel_BGR;
870 type = QPlatformScreen::Subpixel_None;
878
879
880
881
882
883
884
885
886
888LRESULT QT_WIN_CALLBACK qDisplayChangeObserverWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
890 if (message == WM_DISPLAYCHANGE) {
891 qCDebug(lcQpaScreen) <<
"Handling WM_DISPLAYCHANGE";
892 if (QWindowsTheme *t = QWindowsTheme::instance())
894 QWindowsWindow::displayChanged();
895 if (
auto *context = QWindowsContext::instance())
896 context->screenManager().handleScreenChanges();
899 return DefWindowProc(hwnd, message, wParam, lParam);
906 qCDebug(lcQpaScreen) <<
"Initializing screen manager";
909 "ScreenChangeObserverWindow"_L1,
910 qDisplayChangeObserverWndProc);
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);
919 qCDebug(lcQpaScreen) <<
"Created display change observer" << m_displayChangeObserver;
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(); });
936 qCDebug(lcQpaScreen) <<
"Destroying display change observer" << m_displayChangeObserver;
937 DestroyWindow(m_displayChangeObserver);
938 m_displayChangeObserver =
nullptr;
948static inline int indexOfMonitor(
const QWindowsScreenManager::WindowsScreenList &screens,
949 const QString &deviceName)
951 for (
int i= 0; i < screens.size(); ++i)
952 if (screens.at(i)->data().deviceName == deviceName)
958 const QString &deviceName)
960 for (
int i = 0; i < screenData.size(); ++i)
961 if (screenData.at(i).deviceName == deviceName)
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()) {
975 qreal(
QPoint(newScreenGeometry.width(), newScreenGeometry.height()).manhattanLength()) /
976 qreal(
QPoint(oldScreenGeometry.width(), oldScreenGeometry.height()).manhattanLength());
977 relativePosition = (QPointF(relativePosition) * factor).toPoint();
979 geometry.moveTopLeft(relativePosition);
980 w->setGeometry(geometry);
986 m_screens.push_back(newScreen);
987 QWindowSystemInterface::handleScreenAdded(newScreen,
988 screenData.flags & QWindowsScreenData::PrimaryScreen);
989 qCDebug(lcQpaScreen) <<
"New Monitor: " << screenData;
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);
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();
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);
1027 QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(w, primaryScreen);
1032 if (movedWindowCount)
1033 QWindowSystemInterface::flushWindowSystemEvents();
1035 QWindowSystemInterface::handleScreenRemoved(platformScreen);
1039
1040
1041
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;
1062 for (
int i = m_screens.size() - 1; i >= 0; --i) {
1063 if (indexOfMonitor(newDataList, m_screens.at(i)->data().deviceName) == -1)
1067 if (primaryScreenChanged) {
1077 while (!m_screens.isEmpty())
1078 QWindowSystemInterface::handleScreenRemoved(m_screens.takeLast());
1083 for (QWindowsScreen *scr : m_screens) {
1084 if (scr->geometry().contains(p))
1092 if (hMonitor ==
nullptr)
1095 std::find_if(m_screens.cbegin(), m_screens.cend(),
1096 [hMonitor](
const QWindowsScreen *s)
1098 return s->data().hMonitor == hMonitor
1099 && (s->data().flags & QWindowsScreenData::VirtualDesktop) != 0;
1101 return it != m_screens.cend() ? *it :
nullptr;
1106 HMONITOR hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
1107 return screenForMonitor(hMonitor);
1112 if (rect ==
nullptr)
1114 HMONITOR hMonitor = MonitorFromRect(rect, MONITOR_DEFAULTTONULL);
1115 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()