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 "rtc_base/checks.h"
20 #include "rtc_base/string_utils.h"
21 #include "rtc_base/win32.h"
22 
23 namespace webrtc {
24 namespace {
25 
GetMonitorListHandler(HMONITOR monitor,HDC hdc,LPRECT rect,LPARAM data)26 BOOL CALLBACK GetMonitorListHandler(HMONITOR monitor,
27                                     HDC hdc,
28                                     LPRECT rect,
29                                     LPARAM data) {
30   auto monitor_list = reinterpret_cast<DesktopCapturer::SourceList*>(data);
31 
32   // Get the name of the monitor.
33   MONITORINFOEXA monitor_info;
34   monitor_info.cbSize = sizeof(MONITORINFOEXA);
35   if (!GetMonitorInfoA(monitor, &monitor_info)) {
36     // Continue the enumeration, but don't add this monitor to |monitor_list|.
37     return TRUE;
38   }
39 
40   DesktopCapturer::Source monitor_source;
41   monitor_source.id = reinterpret_cast<intptr_t>(monitor);
42   monitor_source.title = monitor_info.szDevice;
43   monitor_list->push_back(monitor_source);
44   return TRUE;
45 }
46 
47 }  // namespace
48 
49 // |monitors| is populated with HMONITOR values for each display monitor found.
50 // This is in contrast to |GetScreenList| which returns the display indices.
GetMonitorList(DesktopCapturer::SourceList * monitors)51 bool GetMonitorList(DesktopCapturer::SourceList* monitors) {
52   RTC_DCHECK_EQ(monitors->size(), 0U);
53   // |EnumDisplayMonitors| accepts a display context and a rectangle, which
54   // allows us to specify a certain region and return only the monitors that
55   // intersect that region. We, however, want all the monitors, so we pass in
56   // NULL parameters.
57   return EnumDisplayMonitors(/*hdc=*/NULL, /*clip_rect=*/NULL,
58                              GetMonitorListHandler,
59                              reinterpret_cast<LPARAM>(monitors));
60 }
61 
GetScreenList(DesktopCapturer::SourceList * screens,std::vector<std::string> * device_names)62 bool GetScreenList(DesktopCapturer::SourceList* screens,
63                    std::vector<std::string>* device_names /* = nullptr */) {
64   RTC_DCHECK_EQ(screens->size(), 0U);
65   if (device_names) {
66     RTC_DCHECK_EQ(device_names->size(), 0U);
67   }
68 
69   BOOL enum_result = TRUE;
70   for (int device_index = 0;; ++device_index) {
71     DISPLAY_DEVICEW device;
72     device.cb = sizeof(device);
73     enum_result = EnumDisplayDevicesW(NULL, device_index, &device, 0);
74 
75     // |enum_result| is 0 if we have enumerated all devices.
76     if (!enum_result)
77       break;
78 
79     // We only care about active displays.
80     if (!(device.StateFlags & DISPLAY_DEVICE_ACTIVE))
81       continue;
82 
83     screens->push_back({device_index, std::string()});
84     if (device_names) {
85       device_names->push_back(rtc::ToUtf8(device.DeviceName));
86     }
87   }
88   return true;
89 }
90 
IsMonitorValid(DesktopCapturer::SourceId monitor)91 bool IsMonitorValid(DesktopCapturer::SourceId monitor) {
92   MONITORINFO monitor_info;
93   monitor_info.cbSize = sizeof(MONITORINFO);
94   return GetMonitorInfoA(reinterpret_cast<HMONITOR>(monitor), &monitor_info);
95 }
96 
IsScreenValid(DesktopCapturer::SourceId screen,std::wstring * device_key)97 bool IsScreenValid(DesktopCapturer::SourceId screen, std::wstring* device_key) {
98   if (screen == kFullDesktopScreenId) {
99     *device_key = L"";
100     return true;
101   }
102 
103   DISPLAY_DEVICEW device;
104   device.cb = sizeof(device);
105   BOOL enum_result = EnumDisplayDevicesW(NULL, screen, &device, 0);
106   if (enum_result)
107     *device_key = device.DeviceKey;
108 
109   return !!enum_result;
110 }
111 
GetFullscreenRect()112 DesktopRect GetFullscreenRect() {
113   return DesktopRect::MakeXYWH(GetSystemMetrics(SM_XVIRTUALSCREEN),
114                                GetSystemMetrics(SM_YVIRTUALSCREEN),
115                                GetSystemMetrics(SM_CXVIRTUALSCREEN),
116                                GetSystemMetrics(SM_CYVIRTUALSCREEN));
117 }
118 
GetScreenRect(DesktopCapturer::SourceId screen,const std::wstring & device_key)119 DesktopRect GetScreenRect(DesktopCapturer::SourceId screen,
120                           const std::wstring& device_key) {
121   if (screen == kFullDesktopScreenId) {
122     return GetFullscreenRect();
123   }
124 
125   DISPLAY_DEVICEW device;
126   device.cb = sizeof(device);
127   BOOL result = EnumDisplayDevicesW(NULL, screen, &device, 0);
128   if (!result)
129     return DesktopRect();
130 
131   // Verifies the device index still maps to the same display device, to make
132   // sure we are capturing the same device when devices are added or removed.
133   // DeviceKey is documented as reserved, but it actually contains the registry
134   // key for the device and is unique for each monitor, while DeviceID is not.
135   if (device_key != device.DeviceKey)
136     return DesktopRect();
137 
138   DEVMODEW device_mode;
139   device_mode.dmSize = sizeof(device_mode);
140   device_mode.dmDriverExtra = 0;
141   result = EnumDisplaySettingsExW(device.DeviceName, ENUM_CURRENT_SETTINGS,
142                                   &device_mode, 0);
143   if (!result)
144     return DesktopRect();
145 
146   return DesktopRect::MakeXYWH(
147       device_mode.dmPosition.x, device_mode.dmPosition.y,
148       device_mode.dmPelsWidth, device_mode.dmPelsHeight);
149 }
150 
151 }  // namespace webrtc
152