DisplayUtils.cpp
1 #include "DisplayUtils.h" 2 3 #include <algorithm> 4 #include <cwctype> 5 #include <iterator> 6 7 #include <dpi_aware.h> 8 #include <MonitorEnumerator.h> 9 10 #include <utils/OnThreadExecutor.h> 11 12 namespace DisplayUtils 13 { 14 std::wstring remove_non_digits(const std::wstring& input) 15 { 16 std::wstring result; 17 std::copy_if(input.begin(), input.end(), std::back_inserter(result), [](wchar_t ch) { return std::iswdigit(ch); }); 18 return result; 19 } 20 21 std::pair<std::wstring, std::wstring> SplitDisplayDeviceId(const std::wstring& str) noexcept 22 { 23 // format: \\?\DISPLAY#{device id}#{instance id}#{some other id} 24 // example: \\?\DISPLAY#GSM1388#4&125707d6&0&UID8388688#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7} 25 // output: { GSM1388, 4&125707d6&0&UID8388688 } 26 27 size_t nameStartPos = str.find_first_of('#'); 28 size_t uidStartPos = str.find('#', nameStartPos + 1); 29 size_t uidEndPos = str.find('#', uidStartPos + 1); 30 31 if (nameStartPos == std::string::npos || uidStartPos == std::string::npos || uidEndPos == std::string::npos) 32 { 33 return { str, L"" }; 34 } 35 36 return { str.substr(nameStartPos + 1, uidStartPos - nameStartPos - 1), str.substr(uidStartPos + 1, uidEndPos - uidStartPos - 1) }; 37 } 38 39 std::pair<bool, std::vector<DisplayUtils::DisplayData>> GetDisplays() 40 { 41 bool success = true; 42 std::vector<DisplayUtils::DisplayData> result{}; 43 auto allMonitors = MonitorEnumerator::Enumerate(); 44 45 OnThreadExecutor dpiUnawareThread; 46 dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] { 47 SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE); 48 SetThreadDpiHostingBehavior(DPI_HOSTING_BEHAVIOR_MIXED); 49 } }).wait(); 50 51 for (auto& monitorData : allMonitors) 52 { 53 MONITORINFOEX monitorInfo = monitorData.second; 54 MONITORINFOEX dpiUnawareMonitorInfo{}; 55 56 dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] { 57 dpiUnawareMonitorInfo.cbSize = sizeof(dpiUnawareMonitorInfo); 58 if (!GetMonitorInfo(monitorData.first, &dpiUnawareMonitorInfo)) 59 { 60 return; 61 } 62 } }).wait(); 63 64 UINT dpi = 0; 65 if (DPIAware::GetScreenDPIForMonitor(monitorData.first, dpi) != S_OK) 66 { 67 success = false; 68 break; 69 } 70 71 DisplayUtils::DisplayData data{ 72 .monitor = monitorData.first, 73 .dpi = dpi, 74 .monitorRectDpiAware = monitorInfo.rcMonitor, 75 .monitorRectDpiUnaware = dpiUnawareMonitorInfo.rcMonitor, 76 }; 77 78 bool foundActiveMonitor = false; 79 DISPLAY_DEVICE displayDevice{ .cb = sizeof(displayDevice) }; 80 DWORD displayDeviceIndex = 0; 81 while (EnumDisplayDevicesW(monitorInfo.szDevice, displayDeviceIndex, &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME)) 82 { 83 /* 84 * if (WI_IsFlagSet(displayDevice.StateFlags, DISPLAY_DEVICE_ACTIVE) && 85 WI_IsFlagClear(displayDevice.StateFlags, DISPLAY_DEVICE_MIRRORING_DRIVER)) 86 */ 87 if (((displayDevice.StateFlags & DISPLAY_DEVICE_ACTIVE) == DISPLAY_DEVICE_ACTIVE) && 88 (displayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) == 0) 89 { 90 // Find display devices associated with the display. 91 foundActiveMonitor = true; 92 break; 93 } 94 95 displayDeviceIndex++; 96 } 97 98 if (foundActiveMonitor) 99 { 100 auto deviceId = SplitDisplayDeviceId(displayDevice.DeviceID); 101 data.id = deviceId.first; 102 data.instanceId = deviceId.second; 103 try 104 { 105 std::wstring numberStr = displayDevice.DeviceName; // \\.\DISPLAY1\Monitor0 106 numberStr = numberStr.substr(0, numberStr.find_last_of('\\')); // \\.\DISPLAY1 107 numberStr = remove_non_digits(numberStr); 108 data.number = std::stoi(numberStr); 109 } 110 catch (...) 111 { 112 success = false; 113 break; 114 } 115 } 116 else 117 { 118 success = false; 119 120 // Use the display name as a fallback value when no proper device was found. 121 data.id = monitorInfo.szDevice; 122 data.instanceId = L""; 123 124 try 125 { 126 std::wstring numberStr = monitorInfo.szDevice; // \\.\DISPLAY1 127 numberStr = remove_non_digits(numberStr); 128 data.number = std::stoi(numberStr); 129 } 130 catch (...) 131 { 132 success = false; 133 break; 134 } 135 } 136 137 result.push_back(data); 138 } 139 140 return { success, result }; 141 } 142 143 }