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 "webrtc/modules/desktop_capture/mouse_cursor_monitor.h"
12
13 #include <assert.h>
14
15 #include "webrtc/modules/desktop_capture/desktop_frame.h"
16 #include "webrtc/modules/desktop_capture/mouse_cursor.h"
17 #include "webrtc/modules/desktop_capture/win/cursor.h"
18 #include "webrtc/modules/desktop_capture/win/window_capture_utils.h"
19 #include "webrtc/system_wrappers/include/logging.h"
20
21 namespace webrtc {
22
23 class MouseCursorMonitorWin : public MouseCursorMonitor {
24 public:
25 explicit MouseCursorMonitorWin(HWND window);
26 explicit MouseCursorMonitorWin(ScreenId screen);
27 virtual ~MouseCursorMonitorWin();
28
29 void Init(Callback* callback, Mode mode) override;
30 void Capture() override;
31
32 private:
33 // Get the rect of the currently selected screen, relative to the primary
34 // display's top-left. If the screen is disabled or disconnected, or any error
35 // happens, an empty rect is returned.
36 DesktopRect GetScreenRect();
37
38 HWND window_;
39 ScreenId screen_;
40
41 Callback* callback_;
42 Mode mode_;
43
44 HDC desktop_dc_;
45
46 HCURSOR last_cursor_;
47 };
48
MouseCursorMonitorWin(HWND window)49 MouseCursorMonitorWin::MouseCursorMonitorWin(HWND window)
50 : window_(window),
51 screen_(kInvalidScreenId),
52 callback_(NULL),
53 mode_(SHAPE_AND_POSITION),
54 desktop_dc_(NULL),
55 last_cursor_(NULL) {
56 }
57
MouseCursorMonitorWin(ScreenId screen)58 MouseCursorMonitorWin::MouseCursorMonitorWin(ScreenId screen)
59 : window_(NULL),
60 screen_(screen),
61 callback_(NULL),
62 mode_(SHAPE_AND_POSITION),
63 desktop_dc_(NULL),
64 last_cursor_(NULL) {
65 assert(screen >= kFullDesktopScreenId);
66 }
67
~MouseCursorMonitorWin()68 MouseCursorMonitorWin::~MouseCursorMonitorWin() {
69 if (desktop_dc_)
70 ReleaseDC(NULL, desktop_dc_);
71 }
72
Init(Callback * callback,Mode mode)73 void MouseCursorMonitorWin::Init(Callback* callback, Mode mode) {
74 assert(!callback_);
75 assert(callback);
76
77 callback_ = callback;
78 mode_ = mode;
79
80 desktop_dc_ = GetDC(NULL);
81 }
82
Capture()83 void MouseCursorMonitorWin::Capture() {
84 assert(callback_);
85
86 CURSORINFO cursor_info;
87 cursor_info.cbSize = sizeof(CURSORINFO);
88 if (!GetCursorInfo(&cursor_info)) {
89 LOG_F(LS_ERROR) << "Unable to get cursor info. Error = " << GetLastError();
90 return;
91 }
92
93 if (last_cursor_ != cursor_info.hCursor) {
94 last_cursor_ = cursor_info.hCursor;
95 // Note that |cursor_info.hCursor| does not need to be freed.
96 rtc::scoped_ptr<MouseCursor> cursor(
97 CreateMouseCursorFromHCursor(desktop_dc_, cursor_info.hCursor));
98 if (cursor.get())
99 callback_->OnMouseCursor(cursor.release());
100 }
101
102 if (mode_ != SHAPE_AND_POSITION)
103 return;
104
105 DesktopVector position(cursor_info.ptScreenPos.x, cursor_info.ptScreenPos.y);
106 bool inside = cursor_info.flags == CURSOR_SHOWING;
107
108 if (window_) {
109 DesktopRect original_rect;
110 DesktopRect cropped_rect;
111 if (!GetCroppedWindowRect(window_, &cropped_rect, &original_rect)) {
112 position.set(0, 0);
113 inside = false;
114 } else {
115 if (inside) {
116 HWND windowUnderCursor = WindowFromPoint(cursor_info.ptScreenPos);
117 inside = windowUnderCursor ?
118 (window_ == GetAncestor(windowUnderCursor, GA_ROOT)) : false;
119 }
120 position = position.subtract(cropped_rect.top_left());
121 }
122 } else {
123 assert(screen_ != kInvalidScreenId);
124 DesktopRect rect = GetScreenRect();
125 if (inside)
126 inside = rect.Contains(position);
127 position = position.subtract(rect.top_left());
128 }
129
130 callback_->OnMouseCursorPosition(inside ? INSIDE : OUTSIDE, position);
131 }
132
GetScreenRect()133 DesktopRect MouseCursorMonitorWin::GetScreenRect() {
134 assert(screen_ != kInvalidScreenId);
135 if (screen_ == kFullDesktopScreenId) {
136 return DesktopRect::MakeXYWH(
137 GetSystemMetrics(SM_XVIRTUALSCREEN),
138 GetSystemMetrics(SM_YVIRTUALSCREEN),
139 GetSystemMetrics(SM_CXVIRTUALSCREEN),
140 GetSystemMetrics(SM_CYVIRTUALSCREEN));
141 }
142 DISPLAY_DEVICE device;
143 device.cb = sizeof(device);
144 BOOL result = EnumDisplayDevices(NULL, screen_, &device, 0);
145 if (!result)
146 return DesktopRect();
147
148 DEVMODE device_mode;
149 device_mode.dmSize = sizeof(device_mode);
150 device_mode.dmDriverExtra = 0;
151 result = EnumDisplaySettingsEx(
152 device.DeviceName, ENUM_CURRENT_SETTINGS, &device_mode, 0);
153 if (!result)
154 return DesktopRect();
155
156 return DesktopRect::MakeXYWH(device_mode.dmPosition.x,
157 device_mode.dmPosition.y,
158 device_mode.dmPelsWidth,
159 device_mode.dmPelsHeight);
160 }
161
CreateForWindow(const DesktopCaptureOptions & options,WindowId window)162 MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
163 const DesktopCaptureOptions& options, WindowId window) {
164 return new MouseCursorMonitorWin(reinterpret_cast<HWND>(window));
165 }
166
CreateForScreen(const DesktopCaptureOptions & options,ScreenId screen)167 MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
168 const DesktopCaptureOptions& options,
169 ScreenId screen) {
170 return new MouseCursorMonitorWin(screen);
171 }
172
173 } // namespace webrtc
174