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/qoperatingsystemversion.h>
40# include <QtCore/private/qt_winrtbase_p.h>
41# include <winrt/Windows.Foundation.h>
42# include <winrt/Windows.Graphics.Display.h>
43# include <windows.graphics.display.interop.h>
47#define WCS_ICCONLY 0x00010000L
52using namespace Qt::StringLiterals;
58 HMODULE lib = QSystemLibrary::load(L"Mscms");
62 colorProfileGetDisplayDefault =
reinterpret_cast<DisplayDefaultSignature>(
63 reinterpret_cast<QFunctionPointer>(
64 ::GetProcAddress(lib,
"ColorProfileGetDisplayDefault")));
65 colorProfileGetDisplayUserScope =
reinterpret_cast<DisplayUserScopeSignature>(
66 reinterpret_cast<QFunctionPointer>(
67 ::GetProcAddress(lib,
"ColorProfileGetDisplayUserScope")));
71 UINT32 sourceID, COLORPROFILETYPE profileType,
72 COLORPROFILESUBTYPE profileSubType, LPWSTR *profileName)
74 if (!colorProfileGetDisplayDefault)
76 return colorProfileGetDisplayDefault(scope, targetAdapterID, sourceID,
77 profileType, profileSubType, profileName);
81 WCS_PROFILE_MANAGEMENT_SCOPE *scope)
83 if (!colorProfileGetDisplayUserScope)
85 return colorProfileGetDisplayUserScope(targetAdapterID, sourceID, scope);
94 DisplayDefaultSignature colorProfileGetDisplayDefault =
nullptr;
95 DisplayUserScopeSignature colorProfileGetDisplayUserScope =
nullptr;
101 return QDpi(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY));
108 if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY)))
109 return QDpi(dpiX, dpiY);
116 std::vector<DISPLAYCONFIG_PATH_INFO> pathInfos;
117 std::vector<DISPLAYCONFIG_MODE_INFO> modeInfos;
121 UINT32 numPathArrayElements;
122 UINT32 numModeInfoArrayElements;
127 if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &numPathArrayElements,
128 &numModeInfoArrayElements) != ERROR_SUCCESS) {
131 pathInfos.resize(numPathArrayElements);
132 modeInfos.resize(numModeInfoArrayElements);
133 result = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &numPathArrayElements, pathInfos.data(),
134 &numModeInfoArrayElements, modeInfos.data(),
nullptr);
135 }
while (result == ERROR_INSUFFICIENT_BUFFER);
137 if (result != ERROR_SUCCESS)
142 std::remove_if(pathInfos.begin(), pathInfos.end(), [&](
const auto &path) ->
bool {
143 DISPLAYCONFIG_SOURCE_DEVICE_NAME deviceName;
144 deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
145 deviceName.header.size =
sizeof(DISPLAYCONFIG_SOURCE_DEVICE_NAME);
146 deviceName.header.adapterId = path.sourceInfo.adapterId;
147 deviceName.header.id = path.sourceInfo.id;
148 if (DisplayConfigGetDeviceInfo(&deviceName.header) == ERROR_SUCCESS) {
149 return wcscmp(viewInfo.szDevice, deviceName.viewGdiDeviceName) != 0;
154 pathInfos.erase(discardThese, pathInfos.end());
161static float getMonitorSDRWhiteLevel(DISPLAYCONFIG_PATH_TARGET_INFO *targetInfo)
163 const float defaultSdrWhiteLevel = 200.0;
165 return defaultSdrWhiteLevel;
167 DISPLAYCONFIG_SDR_WHITE_LEVEL whiteLevel = {};
168 whiteLevel.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL;
169 whiteLevel.header.size =
sizeof(DISPLAYCONFIG_SDR_WHITE_LEVEL);
170 whiteLevel.header.adapterId = targetInfo->adapterId;
171 whiteLevel.header.id = targetInfo->id;
172 if (DisplayConfigGetDeviceInfo(&whiteLevel.header) != ERROR_SUCCESS)
173 return defaultSdrWhiteLevel;
174 return whiteLevel.SDRWhiteLevel * 80.0 / 1000.0;
178using WindowsScreenDataList = QList<QWindowsScreenData>;
182struct DiRegKeyHandleTraits
185 static Type invalidValue()
noexcept
188 return reinterpret_cast<HKEY>(INVALID_HANDLE_VALUE);
190 static bool close(Type handle)
noexcept {
return RegCloseKey(handle) == ERROR_SUCCESS; }
193using DiRegKeyHandle = QUniqueHandle<DiRegKeyHandleTraits>;
195struct DevInfoHandleTraits
197 using Type = HDEVINFO;
198 static Type invalidValue()
noexcept
200 return reinterpret_cast<HDEVINFO>(INVALID_HANDLE_VALUE);
202 static bool close(Type handle)
noexcept {
return SetupDiDestroyDeviceInfoList(handle) == TRUE; }
205using DevInfoHandle = QUniqueHandle<DevInfoHandleTraits>;
210 const std::vector<DISPLAYCONFIG_PATH_INFO> &pathGroup)
212 if (pathGroup.empty()) {
218 DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName = {};
219 deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
220 deviceName.header.size =
sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME);
222 deviceName.header.adapterId = pathGroup[0].targetInfo.adapterId;
223 deviceName.header.id = pathGroup[0].targetInfo.id;
224 const LONG result = DisplayConfigGetDeviceInfo(&deviceName.header);
225 if (result == ERROR_SUCCESS) {
226 data.devicePath = QString::fromWCharArray(deviceName.monitorDevicePath);
230 << u"Unable to get device information for %1:"_s.arg(pathGroup[0].targetInfo.id)
231 << QSystemError::windowsString(result);
237 QStringList manufacturers;
239 QStringList serialNumbers;
241 for (
const auto &path : pathGroup) {
242 DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName = {};
243 deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
244 deviceName.header.size =
sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME);
245 deviceName.header.adapterId = path.targetInfo.adapterId;
246 deviceName.header.id = path.targetInfo.id;
247 const LONG result = DisplayConfigGetDeviceInfo(&deviceName.header);
248 if (result != ERROR_SUCCESS) {
251 << u"Unable to get device information for %1:"_s.arg(path.targetInfo.id)
252 << QSystemError::windowsString(result);
257 constexpr GUID GUID_DEVINTERFACE_MONITOR = {
258 0xe6f07b5f, 0xee97, 0x4a90, { 0xb0, 0x76, 0x33, 0xf5, 0x7b, 0xf4, 0xea, 0xa7 }
260 const DevInfoHandle devInfo{ SetupDiGetClassDevs(
261 &GUID_DEVINTERFACE_MONITOR,
nullptr,
nullptr, DIGCF_DEVICEINTERFACE) };
263 if (!devInfo.isValid())
266 SP_DEVICE_INTERFACE_DATA deviceInterfaceData{};
267 deviceInterfaceData.cbSize =
sizeof(deviceInterfaceData);
269 if (!SetupDiOpenDeviceInterfaceW(devInfo.get(), deviceName.monitorDevicePath, DIODI_NO_ADD,
270 &deviceInterfaceData)) {
273 << u"Unable to open monitor interface to %1:"_s.arg(data.deviceName)
274 << QSystemError::windowsString();
278 DWORD requiredSize{ 0 };
279 if (SetupDiGetDeviceInterfaceDetailW(devInfo.get(), &deviceInterfaceData,
nullptr, 0,
280 &requiredSize,
nullptr)
281 || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
285 const std::unique_ptr<std::byte[]> storage(
new std::byte[requiredSize]);
286 auto *devicePath =
reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA_W *>(storage.get());
287 devicePath->cbSize =
sizeof(std::remove_pointer_t<
decltype(devicePath)>);
288 SP_DEVINFO_DATA deviceInfoData{};
289 deviceInfoData.cbSize =
sizeof(deviceInfoData);
290 if (!SetupDiGetDeviceInterfaceDetailW(devInfo.get(), &deviceInterfaceData, devicePath,
291 requiredSize,
nullptr, &deviceInfoData)) {
292 qCDebug(lcQpaScreen) << u"Unable to get monitor metadata for %1:"_s.arg(data.deviceName)
293 << QSystemError::windowsString();
297 const DiRegKeyHandle edidRegistryKey{ SetupDiOpenDevRegKey(
298 devInfo.get(), &deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ) };
300 if (!edidRegistryKey.isValid())
303 DWORD edidDataSize{ 0 };
304 if (RegQueryValueExW(edidRegistryKey.get(), L"EDID",
nullptr,
nullptr,
nullptr,
311 edidData.resize(edidDataSize);
313 if (RegQueryValueExW(edidRegistryKey.get(), L"EDID",
nullptr,
nullptr,
314 reinterpret_cast<
unsigned char *>(edidData.data()), &edidDataSize)
316 qCDebug(lcQpaScreen) << u"Unable to get EDID from the Registry for %1:"_s.arg(
318 << QSystemError::windowsString();
324 if (!edid.parse(edidData)) {
325 qCDebug(lcQpaScreen) <<
"Invalid EDID blob for" << data.deviceName;
331 names << QString::fromWCharArray(deviceName.monitorFriendlyDeviceName);
332 manufacturers << edid.manufacturer;
333 models << edid.model;
334 serialNumbers << edid.serialNumber;
337 data.name = names.join(u"|"_s);
338 data.manufacturer = manufacturers.join(u"|"_s);
339 data.model = models.join(u"|"_s);
340 data.serialNumber = serialNumbers.join(u"|"_s);
344 const MONITORINFOEX &info,
345 const std::vector<DISPLAYCONFIG_PATH_INFO> &pathGroup)
347 qCDebug(lcQpaScreen) <<
"Resolving color space for" << QString::fromWCharArray(info.szDevice);
349 LPWSTR profileName = [&]() -> LPWSTR {
350 if (!pathGroup.empty()) {
351 const auto &sourceInfo = pathGroup[0].sourceInfo;
352 WCS_PROFILE_MANAGEMENT_SCOPE scope = WCS_PROFILE_MANAGEMENT_SCOPE_SYSTEM_WIDE;
353 if (SUCCEEDED(windowsColorSpaceFunctions->getDisplayUserScope(
354 sourceInfo.adapterId, sourceInfo.id, &scope))) {
355 LPWSTR profileName =
nullptr;
356 if (SUCCEEDED(windowsColorSpaceFunctions->getDisplayDefault(
357 scope, sourceInfo.adapterId, sourceInfo.id, CPT_ICC,
358 CPST_RGB_WORKING_SPACE, &profileName))) {
366 if (
const HDC hdc = CreateDC(info.szDevice,
nullptr,
nullptr,
nullptr)) {
367 const auto freeHdc = qScopeGuard([&]{ DeleteDC(hdc); });
368 DWORD colorProfilePathLength = MAX_PATH;
369 LPWSTR profileName =
reinterpret_cast<LPWSTR>(LocalAlloc(LPTR, MAX_PATH *
sizeof(WCHAR)));
370 if (GetICMProfile(hdc, &colorProfilePathLength, profileName))
378 qCDebug(lcQpaScreen) <<
"Found color profile" << QString::fromWCharArray(profileName);
379 const auto freeProfile = qScopeGuard([&]{ LocalFree(profileName); });
382 profile.dwType = PROFILE_FILENAME;
383 profile.pProfileData = profileName;
384 profile.cbDataSize = DWORD(wcslen(profileName) *
sizeof(
wchar_t));
385 if (HPROFILE hProfile = OpenColorProfile(&profile, PROFILE_READ, FILE_SHARE_READ, OPEN_EXISTING)) {
386 const auto closeProfile = qScopeGuard([&]{ CloseColorProfile(hProfile); });
389 if (HPROFILE wcsProfile = WcsCreateIccProfile(hProfile,
WCS_ICCONLY)) {
390 CloseColorProfile(hProfile);
391 hProfile = wcsProfile;
394 DWORD iccDataSize = 0;
395 GetColorProfileFromHandle(hProfile,
nullptr, &iccDataSize);
396 QByteArray iccData(iccDataSize, Qt::Uninitialized);
397 if (GetColorProfileFromHandle(hProfile,
398 reinterpret_cast<BYTE*>(iccData.data()), &iccDataSize))
399 return QColorSpace::fromIccProfile(iccData);
407#if QT_CONFIG(cpp_winrt)
412 if (
static bool haveInterop = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11_22H2; haveInterop) {
413 using namespace winrt::Windows::Foundation;
414 using namespace winrt::Windows::Graphics::Display;
417 DisplayInformation displayInfo =
nullptr;
418 auto factory = winrt::get_activation_factory<DisplayInformation, IDisplayInformationStaticsInterop>();
419 if (SUCCEEDED(factory->GetForMonitor(hMonitor, winrt::guid_of<DisplayInformation>(), winrt::put_abi(displayInfo)))) {
420 qCDebug(lcQpaScreen) <<
"Checking Advanced Color preferences";
421 AdvancedColorInfo advancedColorInfo = displayInfo.GetAdvancedColorInfo();
422 switch (advancedColorInfo.CurrentAdvancedColorKind()) {
423 case AdvancedColorKind::StandardDynamicRange:
426 return QColorSpace::SRgb;
427 case AdvancedColorKind::WideColorGamut:
430 return QColorSpace::SRgbLinear;
431 case AdvancedColorKind::HighDynamicRange:
434 return QColorSpace::SRgbLinear;
437 }
catch (
const std::exception &ex) {
438 qCWarning(lcQpaScreen) <<
"Failed to query for advanced color info" << ex.what();
446 qCDebug(lcQpaScreen) <<
"No color profile or advanced color preference. Falling back to sRGB";
447 return QColorSpace::SRgb;
452 return QColorSpace();
455static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data)
458 memset(&info, 0,
sizeof(MONITORINFOEX));
459 info.cbSize =
sizeof(MONITORINFOEX);
460 if (GetMonitorInfo(hMonitor, &info) == FALSE)
463 data->hMonitor = hMonitor;
464 data->geometry = QRect(
QPoint(info.rcMonitor.left, info.rcMonitor.top),
QPoint(info.rcMonitor.right - 1, info.rcMonitor.bottom - 1));
465 data->availableGeometry = QRect(
QPoint(info.rcWork.left, info.rcWork.top),
QPoint(info.rcWork.right - 1, info.rcWork.bottom - 1));
466 data->deviceName = QString::fromWCharArray(info.szDevice);
467 const auto pathGroup = getPathInfo(info);
468 if (!pathGroup.empty()) {
469 setMonitorDataFromSetupApi(*data, pathGroup);
471 if (data->name.isEmpty())
472 data->name = data->deviceName;
473 if (data->deviceName == u"WinDisc") {
474 data->flags |= QWindowsScreenData::LockScreen;
476 if (
const HDC hdc = CreateDC(info.szDevice,
nullptr,
nullptr,
nullptr)) {
477 const QDpi dpi = monitorDPI(hMonitor);
478 data->dpi = dpi.first > 0 ? dpi : deviceDPI(hdc);
479 data->depth = GetDeviceCaps(hdc, BITSPIXEL);
480 data->format = data->depth == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32;
481 data->physicalSizeMM = QSizeF(GetDeviceCaps(hdc, HORZSIZE), GetDeviceCaps(hdc, VERTSIZE));
482 const int refreshRate = GetDeviceCaps(hdc, VREFRESH);
484 data->refreshRateHz = refreshRate;
487 qWarning(
"%s: Unable to obtain handle for monitor '%s', defaulting to %g DPI.",
488 __FUNCTION__, qPrintable(data->deviceName),
495 if (!pathGroup.empty()) {
497 const auto &pathInfo = pathGroup[0];
498 switch (pathInfo.targetInfo.rotation) {
499 case DISPLAYCONFIG_ROTATION_IDENTITY:
500 data->orientation = Qt::LandscapeOrientation;
502 case DISPLAYCONFIG_ROTATION_ROTATE90:
503 data->orientation = Qt::PortraitOrientation;
505 case DISPLAYCONFIG_ROTATION_ROTATE180:
506 data->orientation = Qt::InvertedLandscapeOrientation;
508 case DISPLAYCONFIG_ROTATION_ROTATE270:
509 data->orientation = Qt::InvertedPortraitOrientation;
511 case DISPLAYCONFIG_ROTATION_FORCE_UINT32:
515 if (pathInfo.targetInfo.refreshRate.Numerator && pathInfo.targetInfo.refreshRate.Denominator)
516 data->refreshRateHz =
static_cast<qreal>(pathInfo.targetInfo.refreshRate.Numerator)
517 / pathInfo.targetInfo.refreshRate.Denominator;
519 data->orientation = data->geometry.height() > data->geometry.width()
520 ? Qt::PortraitOrientation
521 : Qt::LandscapeOrientation;
524 data->colorSpace = resolveColorSpace(hMonitor, info, pathGroup);
525 qCDebug(lcQpaScreen) <<
"Resolved" << data->colorSpace;
529 data->flags |= QWindowsScreenData::VirtualDesktop;
530 if (info.dwFlags & MONITORINFOF_PRIMARY)
531 data->flags |= QWindowsScreenData::PrimaryScreen;
536BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM p)
538 QWindowsScreenData data;
539 if (monitorData(hMonitor, &data)) {
540 auto *result =
reinterpret_cast<WindowsScreenDataList *>(p);
541 auto it = std::find_if(result->rbegin(), result->rend(),
542 [&data](QWindowsScreenData i){
return i.name == data.name; });
543 if (it != result->rend()) {
544 int previousIndex = 1;
545 if (it->deviceIndex.has_value())
546 previousIndex = it->deviceIndex.value();
548 (*it).deviceIndex = 1;
549 data.deviceIndex = previousIndex + 1;
556 if (data.flags & QWindowsScreenData::PrimaryScreen)
557 result->prepend(data);
559 result->append(data);
566 WindowsScreenDataList result;
567 EnumDisplayMonitors(
nullptr,
nullptr, monitorEnumCallback,
reinterpret_cast<LPARAM>(&result));
571#ifndef QT_NO_DEBUG_STREAM
572static QDebug operator<<(QDebug dbg,
const QWindowsScreenData &d)
574 QDebugStateSaver saver(dbg);
577 dbg <<
"Screen \"" << d.name <<
"\" " << d.geometry.width() <<
'x' << d.geometry.height() <<
'+'
578 << d.geometry.x() <<
'+' << d.geometry.y() <<
" avail: " << d.availableGeometry.width()
579 <<
'x' << d.availableGeometry.height() <<
'+' << d.availableGeometry.x() <<
'+'
580 << d.availableGeometry.y() <<
" physical: " << d.physicalSizeMM.width() <<
'x'
581 << d.physicalSizeMM.height() <<
" DPI: " << d.dpi.first <<
'x' << d.dpi.second
582 <<
" Depth: " << d.depth <<
" Format: " << d.format <<
" hMonitor: " << d.hMonitor
583 <<
" device name: " << d.deviceName <<
" manufacturer: " << d.manufacturer
584 <<
" model: " << d.model <<
" serial number: " << d.serialNumber
585 <<
" color space: " << d.colorSpace;
586 if (d.flags & QWindowsScreenData::PrimaryScreen)
588 if (d.flags & QWindowsScreenData::VirtualDesktop)
589 dbg <<
" virtual desktop";
590 if (d.flags & QWindowsScreenData::LockScreen)
591 dbg <<
" lock screen";
597
598
599
600
601
606 , m_cursor(
new QWindowsCursor(
this))
613 return m_data.deviceIndex.has_value()
614 ? (u"%1 (%2)"_s).arg(m_data.name, QString::number(m_data.deviceIndex.value()))
623 HWND hwnd =
reinterpret_cast<HWND>(window);
626 GetClientRect(hwnd, &r);
627 windowSize = QSize(r.right - r.left, r.bottom - r.top);
631 hwnd = GetDesktopWindow();
632 const QRect screenGeometry = geometry();
633 windowSize = screenGeometry.size();
641 MONITORINFOEX info = {};
642 info.cbSize =
sizeof(MONITORINFOEX);
643 if (GetMonitorInfo(handle(), &info)) {
645 dm.dmSize =
sizeof(dm);
646 if (EnumDisplaySettings(info.szDevice, ENUM_CURRENT_SETTINGS, &dm)) {
647 qreal scale =
static_cast<qreal>(dm.dmPelsWidth) / windowSize.width();
648 x =
static_cast<
int>(
static_cast<qreal>(x) * scale);
649 y =
static_cast<
int>(
static_cast<qreal>(y) * scale);
650 windowSize = QSize(dm.dmPelsWidth, dm.dmPelsHeight);
654 x += screenGeometry.x();
655 y += screenGeometry.y();
659 width = windowSize.width() - xIn;
661 height = windowSize.height() - yIn;
664 HDC display_dc = GetDC(
nullptr);
665 HDC bitmap_dc = CreateCompatibleDC(display_dc);
666 HBITMAP bitmap = CreateCompatibleBitmap(display_dc, width, height);
667 HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap);
670 HDC window_dc = GetDC(hwnd);
671 BitBlt(bitmap_dc, 0, 0, width, height, window_dc, x, y, SRCCOPY | CAPTUREBLT);
674 ReleaseDC(hwnd, window_dc);
675 SelectObject(bitmap_dc, null_bitmap);
678 const QPixmap pixmap = qt_pixmapFromWinHBITMAP(bitmap);
680 DeleteObject(bitmap);
681 ReleaseDC(
nullptr, display_dc);
687
688
692 QWindow *result =
nullptr;
693 if (QWindow *child = QWindowsScreen::windowAt(point, CWP_SKIPINVISIBLE))
694 result = QWindowsWindow::topLevelOf(child);
696 qCDebug(lcQpaScreen) <<
__FUNCTION__ << point << result;
702 QWindow* result =
nullptr;
704 findPlatformWindowAt(GetDesktopWindow(), screenPoint, flags))
705 result = bw->window();
707 qCDebug(lcQpaScreen) <<
__FUNCTION__ << screenPoint <<
" returns " << result;
712
713
714
715
716
720 QList<QPlatformScreen *> result;
721 if (m_data.flags & QWindowsScreenData::VirtualDesktop) {
722 const QWindowsScreenManager::WindowsScreenList screens
724 for (QWindowsScreen *screen : screens) {
725 if (screen->data().flags & QWindowsScreenData::VirtualDesktop)
726 result.push_back(screen);
735
736
740 m_data.physicalSizeMM = newData.physicalSizeMM;
742 if (m_data.hMonitor != newData.hMonitor) {
743 qCDebug(lcQpaScreen) <<
"Monitor" << m_data.name
744 <<
"has had its hMonitor handle changed from"
745 << m_data.hMonitor <<
"to" << newData.hMonitor;
746 m_data.hMonitor = newData.hMonitor;
751 const bool geometryChanged = m_data.geometry != newData.geometry
752 || m_data.availableGeometry != newData.availableGeometry;
753 const bool dpiChanged = !qFuzzyCompare(m_data.dpi.first, newData.dpi.first)
754 || !qFuzzyCompare(m_data.dpi.second, newData.dpi.second);
755 const bool orientationChanged = m_data.orientation != newData.orientation;
756 const bool primaryChanged = (newData.flags & QWindowsScreenData::PrimaryScreen)
757 && !(m_data.flags & QWindowsScreenData::PrimaryScreen);
758 const bool refreshRateChanged = m_data.refreshRateHz != newData.refreshRateHz;
760 m_data.dpi = newData.dpi;
761 m_data.orientation = newData.orientation;
762 m_data.geometry = newData.geometry;
763 m_data.availableGeometry = newData.availableGeometry;
764 m_data.flags = (m_data.flags & ~QWindowsScreenData::PrimaryScreen)
765 | (newData.flags & QWindowsScreenData::PrimaryScreen);
766 m_data.refreshRateHz = newData.refreshRateHz;
767 m_data.colorSpace = newData.colorSpace;
770 QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(),
774 if (orientationChanged)
775 QWindowSystemInterface::handleScreenOrientationChange(screen(), newData.orientation);
776 if (geometryChanged) {
777 QWindowSystemInterface::handleScreenGeometryChange(screen(),
778 newData.geometry, newData.availableGeometry);
781 QWindowSystemInterface::handlePrimaryScreenChanged(
this);
783 if (refreshRateChanged)
784 QWindowSystemInterface::handleScreenRefreshRateChange(screen(), newData.refreshRateHz);
789 return m_data.hMonitor;
795 const auto siblings = screen->virtualSiblings();
796 for (
const QPlatformScreen *sibling : siblings)
797 result |= sibling->geometry();
804 ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE;
806 case Qt::PrimaryOrientation:
808 case Qt::PortraitOrientation:
809 orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT;
811 case Qt::LandscapeOrientation:
812 orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE;
814 case Qt::InvertedPortraitOrientation:
815 orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED;
817 case Qt::InvertedLandscapeOrientation:
818 orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED;
821 result = SetDisplayAutoRotationPreferences(orientationPreference);
827 Qt::ScreenOrientation result = Qt::PrimaryOrientation;
828 ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE;
829 if (GetDisplayAutoRotationPreferences(&orientationPreference)) {
830 switch (orientationPreference) {
831 case ORIENTATION_PREFERENCE_NONE:
833 case ORIENTATION_PREFERENCE_LANDSCAPE:
834 result = Qt::LandscapeOrientation;
836 case ORIENTATION_PREFERENCE_PORTRAIT:
837 result = Qt::PortraitOrientation;
839 case ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED:
840 result = Qt::InvertedLandscapeOrientation;
842 case ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED:
843 result = Qt::InvertedPortraitOrientation;
851
852
855 QPlatformScreen::SubpixelAntialiasingType type = QPlatformScreen::subpixelAntialiasingTypeHint();
856 if (type == QPlatformScreen::Subpixel_None) {
857 QSettings settings(R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Avalon.Graphics\DISPLAY1)"_L1,
858 QSettings::NativeFormat);
859 int registryValue = settings.value(
"PixelStructure"_L1, -1).toInt();
860 switch (registryValue) {
862 type = QPlatformScreen::Subpixel_None;
865 type = QPlatformScreen::Subpixel_RGB;
868 type = QPlatformScreen::Subpixel_BGR;
871 type = QPlatformScreen::Subpixel_None;
879
880
881
882
883
884
885
886
887
889LRESULT QT_WIN_CALLBACK qDisplayChangeObserverWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
891 if (message == WM_DISPLAYCHANGE) {
892 qCDebug(lcQpaScreen) <<
"Handling WM_DISPLAYCHANGE";
893 if (QWindowsTheme *t = QWindowsTheme::instance())
895 QWindowsWindow::displayChanged();
896 if (
auto *context = QWindowsContext::instance())
897 context->screenManager().handleScreenChanges();
900 return DefWindowProc(hwnd, message, wParam, lParam);
907 qCDebug(lcQpaScreen) <<
"Initializing screen manager";
910 "ScreenChangeObserverWindow"_L1,
911 qDisplayChangeObserverWndProc);
915 m_displayChangeObserver = CreateWindowEx(0,
reinterpret_cast<LPCWSTR>(className.utf16()),
916 nullptr, WS_TILED, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
917 nullptr,
nullptr, GetModuleHandle(
nullptr),
nullptr);
918 Q_ASSERT(m_displayChangeObserver);
920 qCDebug(lcQpaScreen) <<
"Created display change observer" << m_displayChangeObserver;
923 m_perUserColorProfileAssociationNotifier.reset(
new QWinRegistryNotifier(HKEY_CURRENT_USER,
924 LR"(Software\Microsoft\Windows NT\CurrentVersion\ICM\ProfileAssociations\Display\{4d36e96e-e325-11ce-bfc1-08002be10318})"));
925 QObject::connect(m_perUserColorProfileAssociationNotifier.get(),
926 &QWinRegistryNotifier::valueChanged, [
this] { handleScreenChanges(); });
927 m_systemWideColorProfileAssociationNotifier.reset(
new QWinRegistryNotifier(HKEY_LOCAL_MACHINE,
928 LR"(SYSTEM\CurrentControlSet\Control\Class\{4d36e96e-e325-11ce-bfc1-08002be10318})"));
929 QObject::connect(m_systemWideColorProfileAssociationNotifier.get(),
930 &QWinRegistryNotifier::valueChanged, [
this] { handleScreenChanges(); });
937 qCDebug(lcQpaScreen) <<
"Destroying display change observer" << m_displayChangeObserver;
938 DestroyWindow(m_displayChangeObserver);
939 m_displayChangeObserver =
nullptr;
949static inline int indexOfMonitor(
const QWindowsScreenManager::WindowsScreenList &screens,
950 const QString &deviceName)
952 for (
int i= 0; i < screens.size(); ++i)
953 if (screens.at(i)->data().deviceName == deviceName)
959 const QString &deviceName)
961 for (
int i = 0; i < screenData.size(); ++i)
962 if (screenData.at(i).deviceName == deviceName)
970 QRect geometry = w->geometry();
971 const QRect oldScreenGeometry = w->screen()->geometry();
972 const QRect newScreenGeometry = newScreen->geometry();
973 QPoint relativePosition = geometry.topLeft() - oldScreenGeometry.topLeft();
974 if (oldScreenGeometry.size() != newScreenGeometry.size()) {
976 qreal(
QPoint(newScreenGeometry.width(), newScreenGeometry.height()).manhattanLength()) /
977 qreal(
QPoint(oldScreenGeometry.width(), oldScreenGeometry.height()).manhattanLength());
978 relativePosition = (QPointF(relativePosition) * factor).toPoint();
980 geometry.moveTopLeft(relativePosition);
981 w->setGeometry(geometry);
987 m_screens.push_back(newScreen);
988 QWindowSystemInterface::handleScreenAdded(newScreen,
989 screenData.flags & QWindowsScreenData::PrimaryScreen);
990 qCDebug(lcQpaScreen) <<
"New Monitor: " << screenData;
997 const auto allWindows = QGuiApplication::allWindows();
998 for (QWindow *w : allWindows) {
999 if (w->isVisible() && w->handle()) {
1000 if (QWindowsWindow *window = QWindowsWindow::windowsWindowOf(w))
1001 window->checkForScreenChanged(QWindowsWindow::ScreenChangeMode::FromScreenAdded);
1008 qCDebug(lcQpaScreen) <<
"Removing Monitor:" << m_screens.at(index)->data();
1009 QPlatformScreen *platformScreen = m_screens.takeAt(index);
1010 QScreen *screen = platformScreen->screen();
1011 QScreen *primaryScreen = QGuiApplication::primaryScreen();
1019 if (screen != primaryScreen) {
1020 unsigned movedWindowCount = 0;
1021 const QWindowList tlws = QGuiApplication::topLevelWindows();
1022 for (QWindow *w : tlws) {
1023 if (w->screen() == screen && w->handle()) {
1024 if (w->isVisible() && w->windowState() != Qt::WindowMinimized
1025 && (QWindowsWindow::baseWindowOf(w)->exStyle() & WS_EX_TOOLWINDOW)) {
1026 moveToVirtualScreen(w, primaryScreen);
1028 QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(w, primaryScreen);
1033 if (movedWindowCount)
1034 QWindowSystemInterface::flushWindowSystemEvents();
1036 QWindowSystemInterface::handleScreenRemoved(platformScreen);
1040
1041
1042
1047 const WindowsScreenDataList newDataList = monitorData();
1048 const bool lockScreen = newDataList.size() == 1 && (newDataList.front().flags & QWindowsScreenData::LockScreen);
1049 bool primaryScreenChanged =
false;
1050 for (
const QWindowsScreenData &newData : newDataList) {
1051 const int existingIndex = indexOfMonitor(m_screens, newData.deviceName);
1052 if (existingIndex != -1) {
1053 m_screens.at(existingIndex)->handleChanges(newData);
1054 if (existingIndex == 0)
1055 primaryScreenChanged =
true;
1063 for (
int i = m_screens.size() - 1; i >= 0; --i) {
1064 if (indexOfMonitor(newDataList, m_screens.at(i)->data().deviceName) == -1)
1068 if (primaryScreenChanged) {
1078 while (!m_screens.isEmpty())
1079 QWindowSystemInterface::handleScreenRemoved(m_screens.takeLast());
1084 for (QWindowsScreen *scr : m_screens) {
1085 if (scr->geometry().contains(p))
1093 if (hMonitor ==
nullptr)
1096 std::find_if(m_screens.cbegin(), m_screens.cend(),
1097 [hMonitor](
const QWindowsScreen *s)
1099 return s->data().hMonitor == hMonitor
1100 && (s->data().flags & QWindowsScreenData::VirtualDesktop) != 0;
1102 return it != m_screens.cend() ? *it :
nullptr;
1107 HMONITOR hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
1108 return screenForMonitor(hMonitor);
1113 if (rect ==
nullptr)
1115 HMONITOR hMonitor = MonitorFromRect(rect, MONITOR_DEFAULTTONULL);
1116 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()