xref: /aosp_15_r20/external/webrtc/modules/desktop_capture/win/screen_capture_utils.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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 <windows.h>
14 
15 #include <string>
16 #include <vector>
17 
18 #include "modules/desktop_capture/desktop_capturer.h"
19 #include "modules/desktop_capture/desktop_geometry.h"
20 #include "rtc_base/checks.h"
21 #include "rtc_base/logging.h"
22 #include "rtc_base/string_utils.h"
23 #include "rtc_base/win32.h"
24 
25 namespace webrtc {
26 
HasActiveDisplay()27 bool HasActiveDisplay() {
28   DesktopCapturer::SourceList screens;
29 
30   return GetScreenList(&screens) && !screens.empty();
31 }
32 
GetScreenList(DesktopCapturer::SourceList * screens,std::vector<std::string> * device_names)33 bool GetScreenList(DesktopCapturer::SourceList* screens,
34                    std::vector<std::string>* device_names /* = nullptr */) {
35   RTC_DCHECK(screens->empty());
36   RTC_DCHECK(!device_names || device_names->empty());
37 
38   BOOL enum_result = TRUE;
39   for (int device_index = 0;; ++device_index) {
40     DISPLAY_DEVICEW device;
41     device.cb = sizeof(device);
42     enum_result = EnumDisplayDevicesW(NULL, device_index, &device, 0);
43 
44     // `enum_result` is 0 if we have enumerated all devices.
45     if (!enum_result) {
46       break;
47     }
48 
49     // We only care about active displays.
50     if (!(device.StateFlags & DISPLAY_DEVICE_ACTIVE)) {
51       continue;
52     }
53 
54     screens->push_back({device_index, std::string()});
55     if (device_names) {
56       device_names->push_back(rtc::ToUtf8(device.DeviceName));
57     }
58   }
59   return true;
60 }
61 
GetHmonitorFromDeviceIndex(const DesktopCapturer::SourceId device_index,HMONITOR * hmonitor)62 bool GetHmonitorFromDeviceIndex(const DesktopCapturer::SourceId device_index,
63                                 HMONITOR* hmonitor) {
64   // A device index of `kFullDesktopScreenId` or -1 represents all screens, an
65   // HMONITOR of 0 indicates the same.
66   if (device_index == kFullDesktopScreenId) {
67     *hmonitor = 0;
68     return true;
69   }
70 
71   std::wstring device_key;
72   if (!IsScreenValid(device_index, &device_key)) {
73     return false;
74   }
75 
76   DesktopRect screen_rect = GetScreenRect(device_index, device_key);
77   if (screen_rect.is_empty()) {
78     return false;
79   }
80 
81   RECT rect = {screen_rect.left(), screen_rect.top(), screen_rect.right(),
82                screen_rect.bottom()};
83 
84   HMONITOR monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL);
85   if (monitor == NULL) {
86     RTC_LOG(LS_WARNING) << "No HMONITOR found for supplied device index.";
87     return false;
88   }
89 
90   *hmonitor = monitor;
91   return true;
92 }
93 
IsMonitorValid(const HMONITOR monitor)94 bool IsMonitorValid(const HMONITOR monitor) {
95   // An HMONITOR of 0 refers to a virtual monitor that spans all physical
96   // monitors.
97   if (monitor == 0) {
98     // There is a bug in a Windows OS API that causes a crash when capturing if
99     // there are no active displays. We must ensure there is an active display
100     // before returning true.
101     if (!HasActiveDisplay())
102       return false;
103 
104     return true;
105   }
106 
107   MONITORINFO monitor_info;
108   monitor_info.cbSize = sizeof(MONITORINFO);
109   return GetMonitorInfoA(monitor, &monitor_info);
110 }
111 
GetMonitorRect(const HMONITOR monitor)112 DesktopRect GetMonitorRect(const HMONITOR monitor) {
113   MONITORINFO monitor_info;
114   monitor_info.cbSize = sizeof(MONITORINFO);
115   if (!GetMonitorInfoA(monitor, &monitor_info)) {
116     return DesktopRect();
117   }
118 
119   return DesktopRect::MakeLTRB(
120       monitor_info.rcMonitor.left, monitor_info.rcMonitor.top,
121       monitor_info.rcMonitor.right, monitor_info.rcMonitor.bottom);
122 }
123 
IsScreenValid(const DesktopCapturer::SourceId screen,std::wstring * device_key)124 bool IsScreenValid(const DesktopCapturer::SourceId screen,
125                    std::wstring* device_key) {
126   if (screen == kFullDesktopScreenId) {
127     *device_key = L"";
128     return true;
129   }
130 
131   DISPLAY_DEVICEW device;
132   device.cb = sizeof(device);
133   BOOL enum_result = EnumDisplayDevicesW(NULL, screen, &device, 0);
134   if (enum_result) {
135     *device_key = device.DeviceKey;
136   }
137 
138   return !!enum_result;
139 }
140 
GetFullscreenRect()141 DesktopRect GetFullscreenRect() {
142   return DesktopRect::MakeXYWH(GetSystemMetrics(SM_XVIRTUALSCREEN),
143                                GetSystemMetrics(SM_YVIRTUALSCREEN),
144                                GetSystemMetrics(SM_CXVIRTUALSCREEN),
145                                GetSystemMetrics(SM_CYVIRTUALSCREEN));
146 }
147 
GetScreenRect(const DesktopCapturer::SourceId screen,const std::wstring & device_key)148 DesktopRect GetScreenRect(const DesktopCapturer::SourceId screen,
149                           const std::wstring& device_key) {
150   if (screen == kFullDesktopScreenId) {
151     return GetFullscreenRect();
152   }
153 
154   DISPLAY_DEVICEW device;
155   device.cb = sizeof(device);
156   BOOL result = EnumDisplayDevicesW(NULL, screen, &device, 0);
157   if (!result) {
158     return DesktopRect();
159   }
160 
161   // Verifies the device index still maps to the same display device, to make
162   // sure we are capturing the same device when devices are added or removed.
163   // DeviceKey is documented as reserved, but it actually contains the registry
164   // key for the device and is unique for each monitor, while DeviceID is not.
165   if (device_key != device.DeviceKey) {
166     return DesktopRect();
167   }
168 
169   DEVMODEW device_mode;
170   device_mode.dmSize = sizeof(device_mode);
171   device_mode.dmDriverExtra = 0;
172   result = EnumDisplaySettingsExW(device.DeviceName, ENUM_CURRENT_SETTINGS,
173                                   &device_mode, 0);
174   if (!result) {
175     return DesktopRect();
176   }
177 
178   return DesktopRect::MakeXYWH(
179       device_mode.dmPosition.x, device_mode.dmPosition.y,
180       device_mode.dmPelsWidth, device_mode.dmPelsHeight);
181 }
182 
183 }  // namespace webrtc
184