xref: /aosp_15_r20/external/webrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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