1 /*
2 * Copyright (c) 2013 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 <string.h>
12
13 #include <memory>
14
15 #include "modules/desktop_capture/desktop_capture_types.h"
16 #include "modules/desktop_capture/desktop_frame.h"
17 #include "modules/desktop_capture/desktop_geometry.h"
18 #include "modules/desktop_capture/mouse_cursor.h"
19 #include "modules/desktop_capture/mouse_cursor_monitor.h"
20 #include "modules/desktop_capture/win/cursor.h"
21 #include "modules/desktop_capture/win/screen_capture_utils.h"
22 #include "modules/desktop_capture/win/window_capture_utils.h"
23 #include "rtc_base/logging.h"
24
25 namespace webrtc {
26
27 namespace {
28
IsSameCursorShape(const CURSORINFO & left,const CURSORINFO & right)29 bool IsSameCursorShape(const CURSORINFO& left, const CURSORINFO& right) {
30 // If the cursors are not showing, we do not care the hCursor handle.
31 return left.flags == right.flags &&
32 (left.flags != CURSOR_SHOWING || left.hCursor == right.hCursor);
33 }
34
35 } // namespace
36
37 class MouseCursorMonitorWin : public MouseCursorMonitor {
38 public:
39 explicit MouseCursorMonitorWin(HWND window);
40 explicit MouseCursorMonitorWin(ScreenId screen);
41 ~MouseCursorMonitorWin() override;
42
43 void Init(Callback* callback, Mode mode) override;
44 void Capture() override;
45
46 private:
47 // Get the rect of the currently selected screen, relative to the primary
48 // display's top-left. If the screen is disabled or disconnected, or any error
49 // happens, an empty rect is returned.
50 DesktopRect GetScreenRect();
51
52 HWND window_;
53 ScreenId screen_;
54
55 Callback* callback_;
56 Mode mode_;
57
58 HDC desktop_dc_;
59
60 // The last CURSORINFO (converted to MouseCursor) we have sent to the client.
61 CURSORINFO last_cursor_;
62 };
63
MouseCursorMonitorWin(HWND window)64 MouseCursorMonitorWin::MouseCursorMonitorWin(HWND window)
65 : window_(window),
66 screen_(kInvalidScreenId),
67 callback_(NULL),
68 mode_(SHAPE_AND_POSITION),
69 desktop_dc_(NULL) {
70 memset(&last_cursor_, 0, sizeof(CURSORINFO));
71 }
72
MouseCursorMonitorWin(ScreenId screen)73 MouseCursorMonitorWin::MouseCursorMonitorWin(ScreenId screen)
74 : window_(NULL),
75 screen_(screen),
76 callback_(NULL),
77 mode_(SHAPE_AND_POSITION),
78 desktop_dc_(NULL) {
79 RTC_DCHECK_GE(screen, kFullDesktopScreenId);
80 memset(&last_cursor_, 0, sizeof(CURSORINFO));
81 }
82
~MouseCursorMonitorWin()83 MouseCursorMonitorWin::~MouseCursorMonitorWin() {
84 if (desktop_dc_)
85 ReleaseDC(NULL, desktop_dc_);
86 }
87
Init(Callback * callback,Mode mode)88 void MouseCursorMonitorWin::Init(Callback* callback, Mode mode) {
89 RTC_DCHECK(!callback_);
90 RTC_DCHECK(callback);
91
92 callback_ = callback;
93 mode_ = mode;
94
95 desktop_dc_ = GetDC(NULL);
96 }
97
Capture()98 void MouseCursorMonitorWin::Capture() {
99 RTC_DCHECK(callback_);
100
101 CURSORINFO cursor_info;
102 cursor_info.cbSize = sizeof(CURSORINFO);
103 if (!GetCursorInfo(&cursor_info)) {
104 RTC_LOG_F(LS_ERROR) << "Unable to get cursor info. Error = "
105 << GetLastError();
106 return;
107 }
108
109 if (!IsSameCursorShape(cursor_info, last_cursor_)) {
110 if (cursor_info.flags == CURSOR_SUPPRESSED) {
111 // The cursor is intentionally hidden now, send an empty bitmap.
112 last_cursor_ = cursor_info;
113 callback_->OnMouseCursor(new MouseCursor(
114 new BasicDesktopFrame(DesktopSize()), DesktopVector()));
115 } else {
116 // According to MSDN https://goo.gl/u6gyuC, HCURSOR instances returned by
117 // functions other than CreateCursor do not need to be actively destroyed.
118 // And CloseHandle function (https://goo.gl/ja5ycW) does not close a
119 // cursor, so assume a HCURSOR does not need to be closed.
120 if (cursor_info.flags == 0) {
121 // Host machine does not have a hardware mouse attached, we will send a
122 // default one instead.
123 // Note, Windows automatically caches cursor resource, so we do not need
124 // to cache the result of LoadCursor.
125 cursor_info.hCursor = LoadCursor(nullptr, IDC_ARROW);
126 }
127 std::unique_ptr<MouseCursor> cursor(
128 CreateMouseCursorFromHCursor(desktop_dc_, cursor_info.hCursor));
129 if (cursor) {
130 last_cursor_ = cursor_info;
131 callback_->OnMouseCursor(cursor.release());
132 }
133 }
134 }
135
136 if (mode_ != SHAPE_AND_POSITION)
137 return;
138
139 // CURSORINFO::ptScreenPos is in full desktop coordinate.
140 DesktopVector position(cursor_info.ptScreenPos.x, cursor_info.ptScreenPos.y);
141 bool inside = cursor_info.flags == CURSOR_SHOWING;
142
143 if (window_) {
144 DesktopRect original_rect;
145 DesktopRect cropped_rect;
146 if (!GetCroppedWindowRect(window_, /*avoid_cropping_border*/ false,
147 &cropped_rect, &original_rect)) {
148 position.set(0, 0);
149 inside = false;
150 } else {
151 if (inside) {
152 HWND windowUnderCursor = WindowFromPoint(cursor_info.ptScreenPos);
153 inside = windowUnderCursor
154 ? (window_ == GetAncestor(windowUnderCursor, GA_ROOT))
155 : false;
156 }
157 position = position.subtract(cropped_rect.top_left());
158 }
159 } else {
160 RTC_DCHECK_NE(screen_, kInvalidScreenId);
161 DesktopRect rect = GetScreenRect();
162 if (inside)
163 inside = rect.Contains(position);
164 position = position.subtract(rect.top_left());
165 }
166
167 callback_->OnMouseCursorPosition(position);
168 }
169
GetScreenRect()170 DesktopRect MouseCursorMonitorWin::GetScreenRect() {
171 RTC_DCHECK_NE(screen_, kInvalidScreenId);
172 if (screen_ == kFullDesktopScreenId) {
173 return DesktopRect::MakeXYWH(GetSystemMetrics(SM_XVIRTUALSCREEN),
174 GetSystemMetrics(SM_YVIRTUALSCREEN),
175 GetSystemMetrics(SM_CXVIRTUALSCREEN),
176 GetSystemMetrics(SM_CYVIRTUALSCREEN));
177 }
178 DISPLAY_DEVICE device;
179 device.cb = sizeof(device);
180 BOOL result = EnumDisplayDevices(NULL, screen_, &device, 0);
181 if (!result)
182 return DesktopRect();
183
184 DEVMODE device_mode;
185 device_mode.dmSize = sizeof(device_mode);
186 device_mode.dmDriverExtra = 0;
187 result = EnumDisplaySettingsEx(device.DeviceName, ENUM_CURRENT_SETTINGS,
188 &device_mode, 0);
189 if (!result)
190 return DesktopRect();
191
192 return DesktopRect::MakeXYWH(
193 device_mode.dmPosition.x, device_mode.dmPosition.y,
194 device_mode.dmPelsWidth, device_mode.dmPelsHeight);
195 }
196
CreateForWindow(const DesktopCaptureOptions & options,WindowId window)197 MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
198 const DesktopCaptureOptions& options,
199 WindowId window) {
200 return new MouseCursorMonitorWin(reinterpret_cast<HWND>(window));
201 }
202
CreateForScreen(const DesktopCaptureOptions & options,ScreenId screen)203 MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
204 const DesktopCaptureOptions& options,
205 ScreenId screen) {
206 return new MouseCursorMonitorWin(screen);
207 }
208
Create(const DesktopCaptureOptions & options)209 std::unique_ptr<MouseCursorMonitor> MouseCursorMonitor::Create(
210 const DesktopCaptureOptions& options) {
211 return std::unique_ptr<MouseCursorMonitor>(
212 CreateForScreen(options, kFullDesktopScreenId));
213 }
214
215 } // namespace webrtc
216