screen_capture_utils.cc (6293B)
1 /* 2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "modules/desktop_capture/win/screen_capture_utils.h" 12 13 #include <shellscalingapi.h> 14 #include <windows.h> 15 16 #include <cstddef> 17 #include <optional> 18 #include <string> 19 #include <vector> 20 21 #include "modules/desktop_capture/desktop_capture_types.h" 22 #include "modules/desktop_capture/desktop_capturer.h" 23 #include "modules/desktop_capture/desktop_geometry.h" 24 #include "rtc_base/checks.h" 25 #include "rtc_base/logging.h" 26 #include "rtc_base/string_utils.h" 27 #include "rtc_base/win32.h" 28 29 namespace webrtc { 30 31 bool HasActiveDisplay() { 32 DesktopCapturer::SourceList screens; 33 34 return GetScreenList(&screens) && !screens.empty(); 35 } 36 37 bool GetScreenList(DesktopCapturer::SourceList* screens, 38 std::vector<std::string>* device_names /* = nullptr */) { 39 RTC_DCHECK(screens->empty()); 40 RTC_DCHECK(!device_names || device_names->empty()); 41 42 BOOL enum_result = TRUE; 43 for (int device_index = 0;; ++device_index) { 44 DISPLAY_DEVICEW device; 45 device.cb = sizeof(device); 46 enum_result = EnumDisplayDevicesW(NULL, device_index, &device, 0); 47 48 // `enum_result` is 0 if we have enumerated all devices. 49 if (!enum_result) { 50 break; 51 } 52 53 // We only care about active displays. 54 if (!(device.StateFlags & DISPLAY_DEVICE_ACTIVE)) { 55 continue; 56 } 57 58 screens->push_back({device_index, 0, std::string()}); 59 if (device_names) { 60 device_names->push_back(ToUtf8(device.DeviceName)); 61 } 62 } 63 return true; 64 } 65 66 bool GetHmonitorFromDeviceIndex(const DesktopCapturer::SourceId device_index, 67 HMONITOR* hmonitor) { 68 // A device index of `kFullDesktopScreenId` or -1 represents all screens, an 69 // HMONITOR of 0 indicates the same. 70 if (device_index == kFullDesktopScreenId) { 71 *hmonitor = 0; 72 return true; 73 } 74 75 DesktopRect screen_rect = GetScreenRect(device_index, std::nullopt); 76 if (screen_rect.is_empty()) { 77 return false; 78 } 79 80 RECT rect = {screen_rect.left(), screen_rect.top(), screen_rect.right(), 81 screen_rect.bottom()}; 82 83 HMONITOR monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL); 84 if (monitor == NULL) { 85 RTC_LOG(LS_WARNING) << "No HMONITOR found for supplied device index."; 86 return false; 87 } 88 89 *hmonitor = monitor; 90 return true; 91 } 92 93 bool IsMonitorValid(const HMONITOR monitor) { 94 // An HMONITOR of 0 refers to a virtual monitor that spans all physical 95 // monitors. 96 if (monitor == 0) { 97 // There is a bug in a Windows OS API that causes a crash when capturing if 98 // there are no active displays. We must ensure there is an active display 99 // before returning true. 100 if (!HasActiveDisplay()) 101 return false; 102 103 return true; 104 } 105 106 MONITORINFO monitor_info; 107 monitor_info.cbSize = sizeof(MONITORINFO); 108 return GetMonitorInfoA(monitor, &monitor_info); 109 } 110 111 DesktopRect GetMonitorRect(const HMONITOR monitor) { 112 MONITORINFO monitor_info; 113 monitor_info.cbSize = sizeof(MONITORINFO); 114 if (!GetMonitorInfoA(monitor, &monitor_info)) { 115 return DesktopRect(); 116 } 117 118 return DesktopRect::MakeLTRB( 119 monitor_info.rcMonitor.left, monitor_info.rcMonitor.top, 120 monitor_info.rcMonitor.right, monitor_info.rcMonitor.bottom); 121 } 122 123 bool IsScreenValid(const DesktopCapturer::SourceId screen, 124 std::wstring* device_key) { 125 if (screen == kFullDesktopScreenId) { 126 *device_key = L""; 127 return true; 128 } 129 130 DISPLAY_DEVICEW device; 131 device.cb = sizeof(device); 132 BOOL enum_result = EnumDisplayDevicesW(NULL, screen, &device, 0); 133 if (enum_result) { 134 *device_key = device.DeviceKey; 135 } 136 137 return !!enum_result; 138 } 139 140 DesktopRect GetFullscreenRect() { 141 return DesktopRect::MakeXYWH(GetSystemMetrics(SM_XVIRTUALSCREEN), 142 GetSystemMetrics(SM_YVIRTUALSCREEN), 143 GetSystemMetrics(SM_CXVIRTUALSCREEN), 144 GetSystemMetrics(SM_CYVIRTUALSCREEN)); 145 } 146 147 DesktopVector GetDpiForMonitor(HMONITOR monitor) { 148 UINT dpi_x, dpi_y; 149 // MDT_EFFECTIVE_DPI includes the scale factor as well as the system DPI. 150 HRESULT hr = ::GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpi_x, &dpi_y); 151 if (SUCCEEDED(hr)) { 152 return {static_cast<INT>(dpi_x), static_cast<INT>(dpi_y)}; 153 } 154 RTC_LOG_GLE_EX(LS_WARNING, hr) << "GetDpiForMonitor() failed"; 155 156 // If we can't get the per-monitor DPI, then return the system DPI. 157 HDC hdc = GetDC(nullptr); 158 if (hdc) { 159 DesktopVector dpi{GetDeviceCaps(hdc, LOGPIXELSX), 160 GetDeviceCaps(hdc, LOGPIXELSY)}; 161 ReleaseDC(nullptr, hdc); 162 return dpi; 163 } 164 165 // If everything fails, then return the default DPI for Windows. 166 return {96, 96}; 167 } 168 169 DesktopRect GetScreenRect(const DesktopCapturer::SourceId screen, 170 const std::optional<std::wstring>& device_key) { 171 if (screen == kFullDesktopScreenId) { 172 return GetFullscreenRect(); 173 } 174 175 DISPLAY_DEVICEW device; 176 device.cb = sizeof(device); 177 BOOL result = EnumDisplayDevicesW(NULL, screen, &device, 0); 178 if (!result) { 179 return DesktopRect(); 180 } 181 182 // Verifies the device index still maps to the same display device, to make 183 // sure we are capturing the same device when devices are added or removed. 184 // DeviceKey is documented as reserved, but it actually contains the registry 185 // key for the device and is unique for each monitor, while DeviceID is not. 186 if (device_key.has_value() && *device_key != device.DeviceKey) { 187 return DesktopRect(); 188 } 189 190 DEVMODEW device_mode; 191 device_mode.dmSize = sizeof(device_mode); 192 device_mode.dmDriverExtra = 0; 193 result = EnumDisplaySettingsExW(device.DeviceName, ENUM_CURRENT_SETTINGS, 194 &device_mode, 0); 195 if (!result) { 196 return DesktopRect(); 197 } 198 199 return DesktopRect::MakeXYWH( 200 device_mode.dmPosition.x, device_mode.dmPosition.y, 201 device_mode.dmPelsWidth, device_mode.dmPelsHeight); 202 } 203 204 } // namespace webrtc