/ src / common / Display / DisplayUtils.cpp
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  }