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 "ash/display/cursor_window_controller.h"
7 #include "ash/display/display_manager.h"
8 #include "ash/display/mirror_window_controller.h"
9 #include "ash/display/window_tree_host_manager.h"
10 #include "ash/root_window_controller.h"
11 #include "ash/shell.h"
12 #include "ash/shell_window_ids.h"
13 #include "ui/aura/env.h"
14 #include "ui/aura/window_delegate.h"
15 #include "ui/aura/window_event_dispatcher.h"
16 #include "ui/base/cursor/cursors_aura.h"
17 #include "ui/base/hit_test.h"
18 #include "ui/base/resource/resource_bundle.h"
19 #include "ui/compositor/dip_util.h"
20 #include "ui/compositor/paint_recorder.h"
21 #include "ui/gfx/canvas.h"
22 #include "ui/gfx/display.h"
23 #include "ui/gfx/geometry/dip_util.h"
24 #include "ui/gfx/image/image_skia.h"
25 #include "ui/gfx/image/image_skia_operations.h"
29 class CursorWindowDelegate
: public aura::WindowDelegate
{
31 CursorWindowDelegate() {}
32 ~CursorWindowDelegate() override
{}
34 // aura::WindowDelegate overrides:
35 gfx::Size
GetMinimumSize() const override
{ return size_
; }
36 gfx::Size
GetMaximumSize() const override
{ return size_
; }
37 void OnBoundsChanged(const gfx::Rect
& old_bounds
,
38 const gfx::Rect
& new_bounds
) override
{}
39 gfx::NativeCursor
GetCursor(const gfx::Point
& point
) override
{
40 return gfx::kNullCursor
;
42 int GetNonClientComponent(const gfx::Point
& point
) const override
{
45 bool ShouldDescendIntoChildForEventHandling(
47 const gfx::Point
& location
) override
{
50 bool CanFocus() override
{ return false; }
51 void OnCaptureLost() override
{}
52 void OnPaint(const ui::PaintContext
& context
) override
{
53 // No need to cache the output here, the CursorWindow is not invalidated.
54 ui::PaintRecorder
recorder(context
, size_
);
55 recorder
.canvas()->DrawImageInt(cursor_image_
, 0, 0);
57 void OnDeviceScaleFactorChanged(float device_scale_factor
) override
{}
58 void OnWindowDestroying(aura::Window
* window
) override
{}
59 void OnWindowDestroyed(aura::Window
* window
) override
{}
60 void OnWindowTargetVisibilityChanged(bool visible
) override
{}
61 bool HasHitTestMask() const override
{ return false; }
62 void GetHitTestMask(gfx::Path
* mask
) const override
{}
64 // Sets the cursor image for the |display|'s scale factor.
65 void SetCursorImage(const gfx::Size
& size
, const gfx::ImageSkia
& image
) {
67 cursor_image_
= image
;
70 const gfx::Size
& size() const { return size_
; }
71 const gfx::ImageSkia
& cursor_image() const { return cursor_image_
; }
74 gfx::ImageSkia cursor_image_
;
77 DISALLOW_COPY_AND_ASSIGN(CursorWindowDelegate
);
80 CursorWindowController::CursorWindowController()
81 : is_cursor_compositing_enabled_(false),
83 cursor_type_(ui::kCursorNone
),
85 cursor_set_(ui::CURSOR_SET_NORMAL
),
86 delegate_(new CursorWindowDelegate()) {
89 CursorWindowController::~CursorWindowController() {
93 void CursorWindowController::SetCursorCompositingEnabled(bool enabled
) {
94 if (is_cursor_compositing_enabled_
!= enabled
) {
95 is_cursor_compositing_enabled_
= enabled
;
96 if (display_
.is_valid())
102 void CursorWindowController::UpdateContainer() {
103 if (is_cursor_compositing_enabled_
) {
104 gfx::Screen
* screen
= Shell::GetScreen();
105 gfx::Display display
= screen
->GetDisplayNearestPoint(
106 screen
->GetCursorScreenPoint());
107 DCHECK(display
.is_valid());
108 if (display
.is_valid())
111 aura::Window
* mirror_window
= Shell::GetInstance()
112 ->window_tree_host_manager()
113 ->mirror_window_controller()
116 display_
= Shell::GetScreen()->GetPrimaryDisplay();
117 SetContainer(mirror_window
);
119 // Updates the hot point based on the current display.
123 void CursorWindowController::SetDisplay(const gfx::Display
& display
) {
124 if (!is_cursor_compositing_enabled_
)
127 // TODO(oshima): Do not updatethe composition cursor when crossing
128 // display in unified desktop mode for now. crbug.com/517222.
129 if (Shell::GetInstance()->display_manager()->IsInUnifiedMode() &&
130 display
.id() != DisplayManager::kUnifiedDisplayId
) {
135 aura::Window
* root_window
= Shell::GetInstance()
136 ->window_tree_host_manager()
137 ->GetRootWindowForDisplayId(display
.id());
141 SetContainer(GetRootWindowController(root_window
)->GetContainer(
142 kShellWindowId_MouseCursorContainer
));
143 SetBoundsInScreen(display
.bounds());
144 // Updates the hot point based on the current display.
148 void CursorWindowController::UpdateLocation() {
151 gfx::Point point
= aura::Env::GetInstance()->last_mouse_location();
152 if (!is_cursor_compositing_enabled_
) {
153 Shell::GetPrimaryRootWindow()->GetHost()->ConvertPointToHost(&point
);
155 point
.Offset(-bounds_in_screen_
.x(), -bounds_in_screen_
.y());
157 point
.Offset(-hot_point_
.x(), -hot_point_
.y());
158 gfx::Rect bounds
= cursor_window_
->bounds();
159 bounds
.set_origin(point
);
160 cursor_window_
->SetBounds(bounds
);
163 void CursorWindowController::SetCursor(gfx::NativeCursor cursor
) {
164 if (cursor_type_
== cursor
.native_type())
166 cursor_type_
= cursor
.native_type();
168 UpdateCursorVisibility();
171 void CursorWindowController::SetCursorSet(ui::CursorSetType cursor_set
) {
172 cursor_set_
= cursor_set
;
176 void CursorWindowController::SetVisibility(bool visible
) {
178 UpdateCursorVisibility();
181 void CursorWindowController::SetContainer(aura::Window
* container
) {
182 if (container_
== container
)
184 container_
= container
;
186 cursor_window_
.reset();
190 // Reusing the window does not work when the display is disconnected.
191 // Just creates a new one instead. crbug.com/384218.
192 cursor_window_
.reset(new aura::Window(delegate_
.get()));
193 cursor_window_
->SetTransparent(true);
194 cursor_window_
->Init(ui::LAYER_TEXTURED
);
195 cursor_window_
->set_ignore_events(true);
196 cursor_window_
->set_owned_by_parent(false);
197 // Call UpdateCursorImage() to figure out |cursor_window_|'s desired size.
200 container
->AddChild(cursor_window_
.get());
201 UpdateCursorVisibility();
202 SetBoundsInScreen(container
->bounds());
205 void CursorWindowController::SetBoundsInScreen(const gfx::Rect
& bounds
) {
206 bounds_in_screen_
= bounds
;
210 void CursorWindowController::UpdateCursorImage() {
212 if (!is_cursor_compositing_enabled_
) {
213 cursor_scale
= display_
.device_scale_factor();
215 // Use the original device scale factor instead of the display's, which
216 // might have been adjusted for the UI scale.
217 const float original_scale
= Shell::GetInstance()
219 ->GetDisplayInfo(display_
.id())
220 .device_scale_factor();
221 // And use the nearest resource scale factor.
223 ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactor(original_scale
));
226 // TODO(hshi): support custom cursor set.
227 if (!ui::GetCursorDataFor(cursor_set_
, cursor_type_
, cursor_scale
,
228 &resource_id
, &hot_point_
)) {
231 const gfx::ImageSkia
* image
=
232 ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id
);
233 if (!is_cursor_compositing_enabled_
) {
234 gfx::ImageSkia rotated
= *image
;
235 switch (display_
.rotation()) {
236 case gfx::Display::ROTATE_0
:
238 case gfx::Display::ROTATE_90
:
239 rotated
= gfx::ImageSkiaOperations::CreateRotatedImage(
240 *image
, SkBitmapOperations::ROTATION_90_CW
);
242 rotated
.width() - hot_point_
.y(),
245 case gfx::Display::ROTATE_180
:
246 rotated
= gfx::ImageSkiaOperations::CreateRotatedImage(
247 *image
, SkBitmapOperations::ROTATION_180_CW
);
249 rotated
.height() - hot_point_
.x(),
250 rotated
.width() - hot_point_
.y());
252 case gfx::Display::ROTATE_270
:
253 rotated
= gfx::ImageSkiaOperations::CreateRotatedImage(
254 *image
, SkBitmapOperations::ROTATION_270_CW
);
257 rotated
.height() - hot_point_
.x());
260 // Note that mirror window's scale factor is always 1.0f, therefore we
261 // need to take 2x's image and paint as if it's 1x image.
262 const gfx::ImageSkiaRep
& image_rep
=
263 rotated
.GetRepresentation(cursor_scale
);
264 delegate_
->SetCursorImage(
265 image_rep
.pixel_size(),
266 gfx::ImageSkia::CreateFrom1xBitmap(image_rep
.sk_bitmap()));
268 const gfx::ImageSkiaRep
& image_rep
= image
->GetRepresentation(cursor_scale
);
269 delegate_
->SetCursorImage(
271 gfx::ImageSkia(gfx::ImageSkiaRep(image_rep
.sk_bitmap(), cursor_scale
)));
272 hot_point_
= gfx::ConvertPointToDIP(cursor_scale
, hot_point_
);
274 if (cursor_window_
) {
275 cursor_window_
->SetBounds(gfx::Rect(delegate_
->size()));
276 cursor_window_
->SchedulePaintInRect(
277 gfx::Rect(cursor_window_
->bounds().size()));
282 void CursorWindowController::UpdateCursorVisibility() {
285 bool visible
= (visible_
&& cursor_type_
!= ui::kCursorNone
);
287 cursor_window_
->Show();
289 cursor_window_
->Hide();
292 const gfx::ImageSkia
& CursorWindowController::GetCursorImageForTest() const {
293 return delegate_
->cursor_image();