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_controller.h"
8 #include "ash/display/mirror_window_controller.h"
9 #include "ash/root_window_controller.h"
10 #include "ash/shell.h"
11 #include "ash/shell_window_ids.h"
12 #include "ui/aura/env.h"
13 #include "ui/aura/window_delegate.h"
14 #include "ui/aura/window_event_dispatcher.h"
15 #include "ui/base/cursor/cursors_aura.h"
16 #include "ui/base/hit_test.h"
17 #include "ui/base/resource/resource_bundle.h"
18 #include "ui/compositor/dip_util.h"
19 #include "ui/gfx/canvas.h"
20 #include "ui/gfx/display.h"
21 #include "ui/gfx/image/image_skia.h"
22 #include "ui/gfx/image/image_skia_operations.h"
26 class CursorWindowDelegate
: public aura::WindowDelegate
{
28 CursorWindowDelegate() : is_cursor_compositing_enabled_(false) {}
29 ~CursorWindowDelegate() override
{}
31 // aura::WindowDelegate overrides:
32 gfx::Size
GetMinimumSize() const override
{ return size_
; }
33 gfx::Size
GetMaximumSize() const override
{ return size_
; }
34 void OnBoundsChanged(const gfx::Rect
& old_bounds
,
35 const gfx::Rect
& new_bounds
) override
{}
36 ui::TextInputClient
* GetFocusedTextInputClient() override
{ return nullptr; }
37 gfx::NativeCursor
GetCursor(const gfx::Point
& point
) override
{
38 return gfx::kNullCursor
;
40 int GetNonClientComponent(const gfx::Point
& point
) const override
{
43 bool ShouldDescendIntoChildForEventHandling(
45 const gfx::Point
& location
) override
{
48 bool CanFocus() override
{ return false; }
49 void OnCaptureLost() override
{}
50 void OnPaint(gfx::Canvas
* canvas
) override
{
51 canvas
->DrawImageInt(cursor_image_
, 0, 0);
53 void OnDeviceScaleFactorChanged(float device_scale_factor
) override
{}
54 void OnWindowDestroying(aura::Window
* window
) override
{}
55 void OnWindowDestroyed(aura::Window
* window
) override
{}
56 void OnWindowTargetVisibilityChanged(bool visible
) override
{}
57 bool HasHitTestMask() const override
{ return false; }
58 void GetHitTestMask(gfx::Path
* mask
) const override
{}
60 // Sets cursor compositing mode on/off.
61 void SetCursorCompositingEnabled(bool enabled
) {
62 is_cursor_compositing_enabled_
= enabled
;
65 // Sets the cursor image for the |display|'s scale factor.
66 void SetCursorImage(const gfx::ImageSkia
& image
,
67 const gfx::Display
& display
) {
68 float scale_factor
= display
.device_scale_factor();
69 const gfx::ImageSkiaRep
& image_rep
= image
.GetRepresentation(scale_factor
);
70 if (!is_cursor_compositing_enabled_
) {
71 // Note that mirror window's scale factor is always 1.0f, therefore we
72 // need to take 2x's image and paint as if it's 1x image.
73 size_
= image_rep
.pixel_size();
74 cursor_image_
= gfx::ImageSkia::CreateFrom1xBitmap(image_rep
.sk_bitmap());
77 cursor_image_
= gfx::ImageSkia(
78 gfx::ImageSkiaRep(image_rep
.sk_bitmap(), scale_factor
));
82 const gfx::Size
size() const { return size_
; }
85 bool is_cursor_compositing_enabled_
;
86 gfx::ImageSkia cursor_image_
;
89 DISALLOW_COPY_AND_ASSIGN(CursorWindowDelegate
);
92 CursorWindowController::CursorWindowController()
93 : is_cursor_compositing_enabled_(false),
95 cursor_type_(ui::kCursorNone
),
96 cursor_set_(ui::CURSOR_SET_NORMAL
),
97 cursor_rotation_(gfx::Display::ROTATE_0
),
98 delegate_(new CursorWindowDelegate()) {
101 CursorWindowController::~CursorWindowController() {
105 void CursorWindowController::SetCursorCompositingEnabled(bool enabled
) {
106 if (is_cursor_compositing_enabled_
!= enabled
) {
107 is_cursor_compositing_enabled_
= enabled
;
108 delegate_
->SetCursorCompositingEnabled(enabled
);
114 void CursorWindowController::UpdateContainer() {
115 if (is_cursor_compositing_enabled_
) {
116 gfx::Screen
* screen
= Shell::GetScreen();
117 gfx::Display display
= screen
->GetDisplayNearestPoint(
118 screen
->GetCursorScreenPoint());
119 DCHECK(display
.is_valid());
120 if (display
.is_valid())
123 aura::Window
* mirror_window
= Shell::GetInstance()->
124 display_controller()->
125 mirror_window_controller()->
128 display_
= Shell::GetScreen()->GetPrimaryDisplay();
129 SetContainer(mirror_window
);
131 // Updates the hot point based on the current display/container.
135 void CursorWindowController::SetDisplay(const gfx::Display
& display
) {
136 if (!is_cursor_compositing_enabled_
)
140 aura::Window
* root_window
= Shell::GetInstance()->display_controller()->
141 GetRootWindowForDisplayId(display
.id());
145 SetContainer(GetRootWindowController(root_window
)->GetContainer(
146 kShellWindowId_MouseCursorContainer
));
147 SetBoundsInScreen(display
.bounds());
150 void CursorWindowController::UpdateLocation() {
153 gfx::Point point
= aura::Env::GetInstance()->last_mouse_location();
154 if (!is_cursor_compositing_enabled_
) {
155 Shell::GetPrimaryRootWindow()->GetHost()->ConvertPointToHost(&point
);
157 point
.Offset(-bounds_in_screen_
.x(), -bounds_in_screen_
.y());
159 point
.Offset(-hot_point_
.x(), -hot_point_
.y());
160 gfx::Rect bounds
= cursor_window_
->bounds();
161 bounds
.set_origin(point
);
162 cursor_window_
->SetBounds(bounds
);
165 void CursorWindowController::SetCursor(gfx::NativeCursor cursor
) {
166 if (cursor_type_
== cursor
.native_type() &&
167 cursor_rotation_
== display_
.rotation())
169 cursor_type_
= cursor
.native_type();
170 cursor_rotation_
= display_
.rotation();
174 void CursorWindowController::SetCursorSet(ui::CursorSetType cursor_set
) {
175 cursor_set_
= cursor_set
;
179 void CursorWindowController::SetVisibility(bool visible
) {
183 cursor_window_
->Show();
185 cursor_window_
->Hide();
188 void CursorWindowController::SetContainer(aura::Window
* container
) {
189 if (container_
== container
)
191 container_
= container
;
193 cursor_window_
.reset();
197 // Reusing the window does not work when the display is disconnected.
198 // Just creates a new one instead. crbug.com/384218.
199 cursor_window_
.reset(new aura::Window(delegate_
.get()));
200 cursor_window_
->SetTransparent(true);
201 cursor_window_
->Init(aura::WINDOW_LAYER_TEXTURED
);
202 cursor_window_
->set_ignore_events(true);
203 cursor_window_
->set_owned_by_parent(false);
206 container
->AddChild(cursor_window_
.get());
207 cursor_window_
->Show();
208 SetBoundsInScreen(container
->bounds());
211 void CursorWindowController::SetBoundsInScreen(const gfx::Rect
& bounds
) {
212 bounds_in_screen_
= bounds
;
216 void CursorWindowController::UpdateCursorImage() {
218 // TODO(hshi): support custom cursor set.
219 if (!ui::GetCursorDataFor(cursor_set_
,
221 display_
.device_scale_factor(),
226 const gfx::ImageSkia
* image
=
227 ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id
);
228 gfx::ImageSkia rotated
= *image
;
229 if (!is_cursor_compositing_enabled_
) {
230 switch (cursor_rotation_
) {
231 case gfx::Display::ROTATE_0
:
233 case gfx::Display::ROTATE_90
:
234 rotated
= gfx::ImageSkiaOperations::CreateRotatedImage(
235 *image
, SkBitmapOperations::ROTATION_90_CW
);
237 rotated
.width() - hot_point_
.y(),
240 case gfx::Display::ROTATE_180
:
241 rotated
= gfx::ImageSkiaOperations::CreateRotatedImage(
242 *image
, SkBitmapOperations::ROTATION_180_CW
);
244 rotated
.height() - hot_point_
.x(),
245 rotated
.width() - hot_point_
.y());
247 case gfx::Display::ROTATE_270
:
248 rotated
= gfx::ImageSkiaOperations::CreateRotatedImage(
249 *image
, SkBitmapOperations::ROTATION_270_CW
);
252 rotated
.height() - hot_point_
.x());
256 hot_point_
= ui::ConvertPointToDIP(Shell::GetPrimaryRootWindow()->layer(),
259 delegate_
->SetCursorImage(rotated
, display_
);
260 if (cursor_window_
) {
261 cursor_window_
->SetBounds(gfx::Rect(delegate_
->size()));
262 cursor_window_
->SchedulePaintInRect(
263 gfx::Rect(cursor_window_
->bounds().size()));