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