ozone: dri: Fix missing cursor update when moving to screen location
[chromium-blink-merge.git] / ui / ozone / platform / dri / dri_cursor.cc
blobe415cf50d4bcb374462bb49d899489f4070f369f
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"
19 #endif
21 namespace ui {
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)
47 return;
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) {
57 #if DCHECK_IS_ON()
58 if (!ui_task_runner_)
59 ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
60 #endif
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();
81 if (dest_window) {
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();
87 } else {
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();
157 else
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)
176 return;
178 gfx::Point location;
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);
184 #else
185 SetCursorLocationLocked(state_.location + delta);
186 #endif
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(
208 int host_id,
209 scoped_refptr<base::SingleThreadTaskRunner> send_runner,
210 const base::Callback<void(IPC::Message*)>& send_callback) {
211 #if DCHECK_IS_ON()
212 if (!ui_task_runner_)
213 ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
214 #endif
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) {
228 state_.host_id = -1;
229 state_.send_runner = NULL;
230 state_.send_callback.Reset();
234 bool DriCursor::OnMessageReceived(const IPC::Message& message) {
235 return false;
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();
263 return;
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>(),
273 gfx::Point(), 0));
276 void DriCursor::SendCursorMoveLocked() {
277 if (!state_.bitmap)
278 return;
279 SendLocked(
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)))
289 return;
291 // Drop disconnected updates. DriWindow will call CommitBoundsChange() when
292 // we connect to initialize the cursor location.
293 delete message;
296 DriCursor::CursorState::CursorState()
297 : window(gfx::kNullAcceleratedWidget), host_id(-1) {
300 DriCursor::CursorState::~CursorState() {
303 } // namespace ui