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