1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
content / browser / media / capture / mouse_cursor_overlay_controller.h [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_MOUSE_CURSOR_OVERLAY_CONTROLLER_H_
#define CONTENT_BROWSER_MEDIA_CAPTURE_MOUSE_CURSOR_OVERLAY_CONTROLLER_H_
#include <atomic>
#include <memory>
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "base/timer/timer.h"
#include "content/common/content_export.h"
#include "services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/cursor/cursor.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/native_widget_types.h"
namespace base {
class TickClock;
} // namespace base
namespace content {
// MouseCursorOverlayController is used by FrameSinkVideoCaptureDevice to manage
// the mouse cursor overlay in the viz::FrameSinkVideoCapturer session based on
// the behavior of the mouse cursor reported by the windowing system.
//
// All parts of this class are meant to run on the UI BrowserThread, except for
// IsUserInteractingWithView(), which may be called from any thread. It is up to
// the client code to ensure the controller's lifetime while in use across
// multiple threads.
class CONTENT_EXPORT MouseCursorOverlayController {
public:
using Overlay = viz::mojom::FrameSinkVideoCaptureOverlay;
MouseCursorOverlayController();
MouseCursorOverlayController(const MouseCursorOverlayController&) = delete;
MouseCursorOverlayController& operator=(const MouseCursorOverlayController&) =
delete;
~MouseCursorOverlayController();
// Sets a new target view to monitor for mouse cursor updates.
void SetTargetView(gfx::NativeView view);
// If the target view is not a gfx::NativeView (which is the case when
// capturing a NSWindow on macOS), this function may be used to set the size
// of the target. This function will only have an effect if SetTargetView has
// not been called (it doesn't make sense to call both functions). The units
// for |target_size| are different on different platforms (DIPs on macOS,
// pixels on all other platforms).
void SetTargetSize(const gfx::Size& target_size) {
target_size_ = target_size;
}
// Takes ownership of and starts controlling the given |overlay|, invoking its
// methods (and destruction) via the given |task_runner|.
void Start(std::unique_ptr<Overlay> overlay,
scoped_refptr<base::SequencedTaskRunner> task_runner);
// Stops controlling the Overlay (passed to Start()) and schedules its
// destruction.
void Stop();
// Returns true if the user has recently interacted with the target (by
// moving or clicking the mouse).
bool IsUserInteractingWithView() const;
// Called from platform-specific code to report on mouse events within the
// captured view. The units for |location| depend on the platform (DIPs on
// macOS and pixels on other platforms).
void OnMouseMoved(const gfx::PointF& location);
void OnMouseClicked(const gfx::PointF& location);
// Returns a weak pointer.
base::WeakPtr<MouseCursorOverlayController> GetWeakPtr();
bool ShouldSendMouseEvents() const { return should_send_mouse_events_; }
private:
friend class MouseCursorOverlayControllerBrowserTest;
// Observes mouse events from the windowing system and reports them via
// OnMouseMoved(), OnMouseClicked(), and OnMouseHasGoneIdle().
class Observer;
enum MouseMoveBehavior {
kNotMoving, // Mouse has not moved recently.
kStartingToMove, // Mouse has moved, but not significantly.
kRecentlyMovedOrClicked, // Sufficient mouse activity present.
};
// Called by the |mouse_activity_ended_timer_| once no mouse events have
// occurred for kIdleTimeout. Also, called by platform-specific code when
// changing the target view.
void OnMouseHasGoneIdle();
// Accessors for |mouse_move_behavior_atomic_|. See comments below.
MouseMoveBehavior mouse_move_behavior() const {
return mouse_move_behavior_atomic_.load(std::memory_order_relaxed);
}
void set_mouse_move_behavior(MouseMoveBehavior behavior) {
mouse_move_behavior_atomic_.store(behavior, std::memory_order_relaxed);
}
// Send the latest mouse event reported by the captured view to the associated
// mouse cursor overlay.
void SendMouseEvent();
// Examines the current mouse movement behavior, view properties, and cursor
// changes to determine whether to show or hide the overlay. |location| is the
// current mouse cursor location.
void UpdateOverlay(const gfx::PointF& location);
// Returns the current mouse cursor. The default "arrow pointer" cursor will
// be returned in lieu of a null cursor.
gfx::NativeCursor GetCurrentCursorOrDefault() const;
// Computes where the overlay should be shown, in terms of relative
// coordinates. This takes the view size, coordinate systems of the view and
// cursor bitmap, and cursor hotspot offset; all into account.
gfx::RectF ComputeRelativeBoundsForOverlay(const gfx::NativeCursor& cursor,
const gfx::PointF& location) const;
// Called after SetTargetView() to ignore mouse events from the
// platform/toolkit and set a default mouse cursor. This is used by the
// browser tests to prevent actual mouse movement from interfering with the
// testing of the control logic.
void DisconnectFromToolkitForTesting();
// Returns the image of the mouse cursor.
static SkBitmap GetCursorImage(const gfx::NativeCursor&);
// Called from platform-specific code to report on mouse events within the
// captured view.
void OnMouseCoordinatesUpdated(const gfx::Point& coordinates);
// Overrides the tick clock used by |this| for testing.
void SetTickClockForTesting(const base::TickClock* tick_clock);
// Platform-specific mouse event observer. Updated by SetTargetView().
std::unique_ptr<Observer> observer_;
// Updated in the mouse event handlers and used to decide whether the user is
// interacting with the view and whether to update the overlay.
gfx::PointF mouse_move_start_location_;
base::RetainingOneShotTimer mouse_activity_ended_timer_;
// Updated in the mouse event handlers and read by IsUserInteractingWithView()
// (on any thread). This is not protected by a mutex since strict memory
// ordering semantics are not necessary, just atomicity between threads. All
// code should use the accessors to read or set this value.
std::atomic<MouseMoveBehavior> mouse_move_behavior_atomic_;
// The overlay being controlled, and the task runner to use to invoke its
// methods and destruction.
std::unique_ptr<Overlay> overlay_;
scoped_refptr<base::SequencedTaskRunner> overlay_task_runner_;
// The last-shown mouse cursor. UpdateOverlay() uses this to determine whether
// to update the cursor image, or just the overlay position.
gfx::NativeCursor last_cursor_ = gfx::NativeCursor();
// This is empty if the overlay should be hidden. Otherwise, it represents a
// shown overlay with a relative position within the view in terms of the
// range [0.0,1.0). It can sometimes be a little bit outside of that range,
// depending on the cursor's hotspot.
gfx::RectF bounds_;
// The target's size, if set explicitly by SetTargetSize.
gfx::Size target_size_;
// Whether to transmit mouse events to the renderer process for dispatching
// it via the Captured Mouse Events API.
// See https://screen-share.github.io/captured-mouse-events/
bool should_send_mouse_events_ = false;
// Latest coordinates reported to OnMouseCoordinatesUpdated() and timer to
// postpone forwarding them to SendMouseEvent(), if necessary. Note that the
// initial value of last_observed_coordinates_ does not matter here, since
// it is always set in OnMouseCoordinatesUpdated() before being read in
// SendMouseEvent().
gfx::Point last_observed_coordinates_;
base::OneShotTimer last_observed_coordinates_timer_;
// Latest coordinates sent to the the overlay and time when it happened.
std::optional<gfx::Point> last_emitted_coordinates_;
base::TimeTicks last_emitted_coordinates_time_;
// Everything except the constructor and IsUserInteractingWithView() must be
// called on the UI BrowserThread.
SEQUENCE_CHECKER(ui_sequence_checker_);
// Tick clock used for controlling event delays.
raw_ptr<const base::TickClock> tick_clock_ = nullptr;
base::WeakPtrFactory<MouseCursorOverlayController> weak_factory_{this};
// Minium movement before the cursor has been considered intentionally moved
// by the user.
static constexpr int kMinMovementPixels = 15;
// Amount of time to elapse with no mouse activity before the cursor should
// stop showing.
static constexpr base::TimeDelta kIdleTimeout = base::Seconds(2);
// Special value from the Captured Mouse Events specification to indicate that
// the mouse is outside the captured view.
// See
// https://screen-share.github.io/captured-mouse-events/#captured-mouse-change-event
static constexpr gfx::Point kOutsideSurface = {-1, -1};
// The specification contains some hints to limit the frequency with which
// events are fired. This is implemented using a minimal time to wait between
// two reports of mouse coordinates (corresponding to a max frequency of 30
// dispatched events per seconds).
// See
// https://screen-share.github.io/captured-mouse-events/#captured-mouse-change-event
static constexpr base::TimeDelta kMinWaitInterval =
base::Milliseconds(1000 / (30 - 1));
};
} // namespace content
#endif // CONTENT_BROWSER_MEDIA_CAPTURE_MOUSE_CURSOR_OVERLAY_CONTROLLER_H_