1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/ozone/platform/dri/dri_cursor.h"
7 #include "base/thread_task_runner_handle.h"
8 #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
9 #include "ui/gfx/geometry/point.h"
10 #include "ui/gfx/geometry/point_conversions.h"
11 #include "ui/gfx/geometry/point_f.h"
12 #include "ui/ozone/common/gpu/ozone_gpu_messages.h"
13 #include "ui/ozone/platform/dri/dri_gpu_platform_support_host.h"
14 #include "ui/ozone/platform/dri/dri_window.h"
15 #include "ui/ozone/platform/dri/dri_window_manager.h"
17 #if defined(OS_CHROMEOS)
18 #include "ui/events/ozone/chromeos/cursor_controller.h"
23 DriCursor::DriCursor(DriWindowManager
* window_manager
,
24 DriGpuPlatformSupportHost
* gpu_platform_support_host
)
25 : window_manager_(window_manager
),
26 gpu_platform_support_host_(gpu_platform_support_host
) {
29 DriCursor::~DriCursor() {
30 gpu_platform_support_host_
->UnregisterHandler(this);
33 void DriCursor::Init() {
34 gpu_platform_support_host_
->RegisterHandler(this);
37 void DriCursor::SetCursor(gfx::AcceleratedWidget window
,
38 PlatformCursor platform_cursor
) {
39 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
40 DCHECK_NE(window
, gfx::kNullAcceleratedWidget
);
42 scoped_refptr
<BitmapCursorOzone
> bitmap
=
43 BitmapCursorFactoryOzone::GetBitmapCursor(platform_cursor
);
45 base::AutoLock
lock(state_
.lock
);
46 if (state_
.window
!= window
|| state_
.bitmap
== bitmap
)
49 state_
.bitmap
= bitmap
;
51 SendCursorShowLocked();
54 void DriCursor::OnWindowAdded(gfx::AcceleratedWidget window
,
55 const gfx::Rect
& bounds_in_screen
,
56 const gfx::Rect
& cursor_confined_bounds
) {
59 ui_task_runner_
= base::ThreadTaskRunnerHandle::Get();
61 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
62 base::AutoLock
lock(state_
.lock
);
64 if (state_
.window
== gfx::kNullAcceleratedWidget
) {
65 // First window added & cursor is not placed. Place it.
66 state_
.window
= window
;
67 state_
.display_bounds_in_screen
= bounds_in_screen
;
68 state_
.confined_bounds
= cursor_confined_bounds
;
69 SetCursorLocationLocked(cursor_confined_bounds
.CenterPoint());
73 void DriCursor::OnWindowRemoved(gfx::AcceleratedWidget window
) {
74 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
75 base::AutoLock
lock(state_
.lock
);
77 if (state_
.window
== window
) {
78 // Try to find a new location for the cursor.
79 DriWindow
* dest_window
= window_manager_
->GetPrimaryWindow();
82 state_
.window
= dest_window
->GetAcceleratedWidget();
83 state_
.display_bounds_in_screen
= dest_window
->GetBounds();
84 state_
.confined_bounds
= dest_window
->GetCursorConfinedBounds();
85 SetCursorLocationLocked(state_
.confined_bounds
.CenterPoint());
86 SendCursorShowLocked();
88 state_
.window
= gfx::kNullAcceleratedWidget
;
89 state_
.display_bounds_in_screen
= gfx::Rect();
90 state_
.confined_bounds
= gfx::Rect();
91 state_
.location
= gfx::Point();
96 void DriCursor::PrepareForBoundsChange(gfx::AcceleratedWidget window
) {
97 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
98 base::AutoLock
lock(state_
.lock
);
100 // Bounds changes can reparent the window to a different display, so
101 // we hide prior to the change so that we avoid leaving a cursor
102 // behind on the old display.
103 // TODO(spang): The GPU-side code should handle this.
104 if (state_
.window
== window
)
105 SendCursorHideLocked();
107 // The cursor will be shown and moved in CommitBoundsChange().
110 void DriCursor::CommitBoundsChange(
111 gfx::AcceleratedWidget window
,
112 const gfx::Rect
& new_display_bounds_in_screen
,
113 const gfx::Rect
& new_confined_bounds
) {
114 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
115 base::AutoLock
lock(state_
.lock
);
116 if (state_
.window
== window
) {
117 state_
.display_bounds_in_screen
= new_display_bounds_in_screen
;
118 state_
.confined_bounds
= new_confined_bounds
;
119 SetCursorLocationLocked(state_
.location
);
120 SendCursorShowLocked();
124 void DriCursor::ConfineCursorToBounds(gfx::AcceleratedWidget window
,
125 const gfx::Rect
& bounds
) {
126 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
127 base::AutoLock
lock(state_
.lock
);
128 if (state_
.window
== window
) {
129 state_
.confined_bounds
= bounds
;
130 SetCursorLocationLocked(state_
.location
);
131 SendCursorShowLocked();
135 void DriCursor::MoveCursorTo(gfx::AcceleratedWidget window
,
136 const gfx::PointF
& location
) {
137 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
138 base::AutoLock
lock(state_
.lock
);
139 gfx::AcceleratedWidget old_window
= state_
.window
;
141 if (window
!= old_window
) {
142 // When moving between displays, hide the cursor on the old display
143 // prior to showing it on the new display.
144 if (old_window
!= gfx::kNullAcceleratedWidget
)
145 SendCursorHideLocked();
147 DriWindow
* dri_window
= window_manager_
->GetWindow(window
);
148 state_
.display_bounds_in_screen
= dri_window
->GetBounds();
149 state_
.confined_bounds
= dri_window
->GetCursorConfinedBounds();
150 state_
.window
= window
;
153 SetCursorLocationLocked(location
);
155 if (window
!= old_window
)
156 SendCursorShowLocked();
158 SendCursorMoveLocked();
161 void DriCursor::MoveCursorTo(const gfx::PointF
& screen_location
) {
162 base::AutoLock
lock(state_
.lock
);
164 // TODO(spang): Moving between windows doesn't work here, but
165 // is not needed for current uses.
167 SetCursorLocationLocked(screen_location
-
168 state_
.display_bounds_in_screen
.OffsetFromOrigin());
170 SendCursorMoveLocked();
173 void DriCursor::MoveCursor(const gfx::Vector2dF
& delta
) {
174 base::AutoLock
lock(state_
.lock
);
175 if (state_
.window
== gfx::kNullAcceleratedWidget
)
179 #if defined(OS_CHROMEOS)
180 gfx::Vector2dF transformed_delta
= delta
;
181 ui::CursorController::GetInstance()->ApplyCursorConfigForWindow(
182 state_
.window
, &transformed_delta
);
183 SetCursorLocationLocked(state_
.location
+ transformed_delta
);
185 SetCursorLocationLocked(state_
.location
+ delta
);
188 SendCursorMoveLocked();
191 bool DriCursor::IsCursorVisible() {
192 base::AutoLock
lock(state_
.lock
);
193 return state_
.bitmap
;
196 gfx::PointF
DriCursor::GetLocation() {
197 base::AutoLock
lock(state_
.lock
);
198 return state_
.location
+ state_
.display_bounds_in_screen
.OffsetFromOrigin();
201 gfx::Rect
DriCursor::GetCursorConfinedBounds() {
202 base::AutoLock
lock(state_
.lock
);
203 return state_
.confined_bounds
+
204 state_
.display_bounds_in_screen
.OffsetFromOrigin();
207 void DriCursor::OnChannelEstablished(
209 scoped_refptr
<base::SingleThreadTaskRunner
> send_runner
,
210 const base::Callback
<void(IPC::Message
*)>& send_callback
) {
212 if (!ui_task_runner_
)
213 ui_task_runner_
= base::ThreadTaskRunnerHandle::Get();
215 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
216 base::AutoLock
lock(state_
.lock
);
217 state_
.host_id
= host_id
;
218 state_
.send_runner
= send_runner
;
219 state_
.send_callback
= send_callback
;
220 // Initial set for this GPU process will happen after the window
221 // initializes, in CommitBoundsChange().
224 void DriCursor::OnChannelDestroyed(int host_id
) {
225 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
226 base::AutoLock
lock(state_
.lock
);
227 if (state_
.host_id
== host_id
) {
229 state_
.send_runner
= NULL
;
230 state_
.send_callback
.Reset();
234 bool DriCursor::OnMessageReceived(const IPC::Message
& message
) {
238 void DriCursor::SetCursorLocationLocked(const gfx::PointF
& location
) {
239 state_
.lock
.AssertAcquired();
241 gfx::PointF clamped_location
= location
;
242 clamped_location
.SetToMax(state_
.confined_bounds
.origin());
243 // Right and bottom edges are exclusive.
244 clamped_location
.SetToMin(gfx::PointF(state_
.confined_bounds
.right() - 1,
245 state_
.confined_bounds
.bottom() - 1));
247 state_
.location
= clamped_location
;
250 gfx::Point
DriCursor::GetBitmapLocationLocked() {
251 return gfx::ToFlooredPoint(state_
.location
) -
252 state_
.bitmap
->hotspot().OffsetFromOrigin();
255 bool DriCursor::IsConnectedLocked() {
256 return !state_
.send_callback
.is_null();
259 void DriCursor::SendCursorShowLocked() {
260 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
261 if (!state_
.bitmap
) {
262 SendCursorHideLocked();
265 SendLocked(new OzoneGpuMsg_CursorSet(state_
.window
, state_
.bitmap
->bitmaps(),
266 GetBitmapLocationLocked(),
267 state_
.bitmap
->frame_delay_ms()));
270 void DriCursor::SendCursorHideLocked() {
271 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
272 SendLocked(new OzoneGpuMsg_CursorSet(state_
.window
, std::vector
<SkBitmap
>(),
276 void DriCursor::SendCursorMoveLocked() {
280 new OzoneGpuMsg_CursorMove(state_
.window
, GetBitmapLocationLocked()));
283 void DriCursor::SendLocked(IPC::Message
* message
) {
284 state_
.lock
.AssertAcquired();
286 if (IsConnectedLocked() &&
287 state_
.send_runner
->PostTask(FROM_HERE
,
288 base::Bind(state_
.send_callback
, message
)))
291 // Drop disconnected updates. DriWindow will call CommitBoundsChange() when
292 // we connect to initialize the cursor location.
296 DriCursor::CursorState::CursorState()
297 : window(gfx::kNullAcceleratedWidget
), host_id(-1) {
300 DriCursor::CursorState::~CursorState() {