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