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/interface/logging.h"
20
21namespace webrtc {
22
23class MouseCursorMonitorWin : public MouseCursorMonitor {
24 public:
25  explicit MouseCursorMonitorWin(HWND window);
26  explicit MouseCursorMonitorWin(ScreenId screen);
27  virtual ~MouseCursorMonitorWin();
28
29  virtual void Init(Callback* callback, Mode mode) OVERRIDE;
30  virtual 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
49MouseCursorMonitorWin::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
58MouseCursorMonitorWin::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
68MouseCursorMonitorWin::~MouseCursorMonitorWin() {
69  if (desktop_dc_)
70    ReleaseDC(NULL, desktop_dc_);
71}
72
73void 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
83void 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    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
133DesktopRect 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
162MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
163    const DesktopCaptureOptions& options, WindowId window) {
164  return new MouseCursorMonitorWin(reinterpret_cast<HWND>(window));
165}
166
167MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
168    const DesktopCaptureOptions& options,
169    ScreenId screen) {
170  return new MouseCursorMonitorWin(screen);
171}
172
173}  // namespace webrtc
174