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. 4 5 #include "ash/display/cursor_window_controller.h" 6 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" 23 24 namespace ash { 25 26 class CursorWindowDelegate : public aura::WindowDelegate { 27 public: 28 CursorWindowDelegate() : is_cursor_compositing_enabled_(false) {} 29 virtual ~CursorWindowDelegate() {} 30 31 // aura::WindowDelegate overrides: 32 virtual gfx::Size GetMinimumSize() const OVERRIDE { return size_; } 33 virtual gfx::Size GetMaximumSize() const OVERRIDE { return size_; } 34 virtual void OnBoundsChanged(const gfx::Rect& old_bounds, 35 const gfx::Rect& new_bounds) OVERRIDE {} 36 virtual gfx::NativeCursor GetCursor(const gfx::Point& point) OVERRIDE { 37 return gfx::kNullCursor; 38 } 39 virtual int GetNonClientComponent( 40 const gfx::Point& point) const OVERRIDE { 41 return HTNOWHERE; 42 } 43 virtual bool ShouldDescendIntoChildForEventHandling( 44 aura::Window* child, 45 const gfx::Point& location) OVERRIDE { 46 return false; 47 } 48 virtual bool CanFocus() OVERRIDE { return false; } 49 virtual void OnCaptureLost() OVERRIDE {} 50 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { 51 canvas->DrawImageInt(cursor_image_, 0, 0); 52 } 53 virtual void OnDeviceScaleFactorChanged( 54 float device_scale_factor) OVERRIDE {} 55 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {} 56 virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE {} 57 virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE {} 58 virtual bool HasHitTestMask() const OVERRIDE { return false; } 59 virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE {} 60 61 // Sets cursor compositing mode on/off. 62 void SetCursorCompositingEnabled(bool enabled) { 63 is_cursor_compositing_enabled_ = enabled; 64 } 65 66 // Sets the cursor image for the |display|'s scale factor. 67 void SetCursorImage(const gfx::ImageSkia& image, 68 const gfx::Display& display) { 69 float scale_factor = display.device_scale_factor(); 70 const gfx::ImageSkiaRep& image_rep = image.GetRepresentation(scale_factor); 71 if (!is_cursor_compositing_enabled_) { 72 // Note that mirror window's scale factor is always 1.0f, therefore we 73 // need to take 2x's image and paint as if it's 1x image. 74 size_ = image_rep.pixel_size(); 75 cursor_image_ = gfx::ImageSkia::CreateFrom1xBitmap(image_rep.sk_bitmap()); 76 } else { 77 size_ = image.size(); 78 cursor_image_ = gfx::ImageSkia( 79 gfx::ImageSkiaRep(image_rep.sk_bitmap(), scale_factor)); 80 } 81 } 82 83 const gfx::Size size() const { return size_; } 84 85 private: 86 bool is_cursor_compositing_enabled_; 87 gfx::ImageSkia cursor_image_; 88 gfx::Size size_; 89 90 DISALLOW_COPY_AND_ASSIGN(CursorWindowDelegate); 91 }; 92 93 CursorWindowController::CursorWindowController() 94 : is_cursor_compositing_enabled_(false), 95 container_(NULL), 96 cursor_type_(ui::kCursorNone), 97 cursor_set_(ui::CURSOR_SET_NORMAL), 98 cursor_rotation_(gfx::Display::ROTATE_0), 99 delegate_(new CursorWindowDelegate()) { 100 } 101 102 CursorWindowController::~CursorWindowController() { 103 SetContainer(NULL); 104 } 105 106 void CursorWindowController::SetCursorCompositingEnabled(bool enabled) { 107 if (is_cursor_compositing_enabled_ != enabled) { 108 is_cursor_compositing_enabled_ = enabled; 109 delegate_->SetCursorCompositingEnabled(enabled); 110 UpdateCursorImage(); 111 UpdateContainer(); 112 } 113 } 114 115 void CursorWindowController::UpdateContainer() { 116 if (is_cursor_compositing_enabled_) { 117 gfx::Screen* screen = Shell::GetScreen(); 118 gfx::Display display = screen->GetDisplayNearestPoint( 119 screen->GetCursorScreenPoint()); 120 DCHECK(display.is_valid()); 121 if (display.is_valid()) 122 SetDisplay(display); 123 } else { 124 aura::Window* mirror_window = Shell::GetInstance()-> 125 display_controller()-> 126 mirror_window_controller()-> 127 GetWindow(); 128 if (mirror_window) 129 display_ = Shell::GetScreen()->GetPrimaryDisplay(); 130 SetContainer(mirror_window); 131 } 132 } 133 134 void CursorWindowController::SetDisplay(const gfx::Display& display) { 135 if (!is_cursor_compositing_enabled_) 136 return; 137 138 display_ = display; 139 aura::Window* root_window = Shell::GetInstance()->display_controller()-> 140 GetRootWindowForDisplayId(display.id()); 141 if (!root_window) 142 return; 143 144 SetContainer(GetRootWindowController(root_window)->GetContainer( 145 kShellWindowId_MouseCursorContainer)); 146 SetBoundsInScreen(display.bounds()); 147 } 148 149 void CursorWindowController::UpdateLocation() { 150 if (!cursor_window_) 151 return; 152 gfx::Point point = aura::Env::GetInstance()->last_mouse_location(); 153 if (!is_cursor_compositing_enabled_) { 154 Shell::GetPrimaryRootWindow()->GetHost()->ConvertPointToHost(&point); 155 } else { 156 point.Offset(-bounds_in_screen_.x(), -bounds_in_screen_.y()); 157 } 158 point.Offset(-hot_point_.x(), -hot_point_.y()); 159 gfx::Rect bounds = cursor_window_->bounds(); 160 bounds.set_origin(point); 161 cursor_window_->SetBounds(bounds); 162 } 163 164 void CursorWindowController::SetCursor(gfx::NativeCursor cursor) { 165 if (cursor_type_ == cursor.native_type() && 166 cursor_rotation_ == display_.rotation()) 167 return; 168 cursor_type_ = cursor.native_type(); 169 cursor_rotation_ = display_.rotation(); 170 UpdateCursorImage(); 171 } 172 173 void CursorWindowController::SetCursorSet(ui::CursorSetType cursor_set) { 174 cursor_set_ = cursor_set; 175 UpdateCursorImage(); 176 } 177 178 void CursorWindowController::SetVisibility(bool visible) { 179 if (!cursor_window_) 180 return; 181 if (visible) 182 cursor_window_->Show(); 183 else 184 cursor_window_->Hide(); 185 } 186 187 void CursorWindowController::SetContainer(aura::Window* container) { 188 if (container_ == container) 189 return; 190 container_ = container; 191 if (!container) { 192 cursor_window_.reset(); 193 return; 194 } 195 196 // Reusing the window does not work when the display is disconnected. 197 // Just creates a new one instead. crbug.com/384218. 198 cursor_window_.reset(new aura::Window(delegate_.get())); 199 cursor_window_->SetTransparent(true); 200 cursor_window_->Init(aura::WINDOW_LAYER_TEXTURED); 201 cursor_window_->set_ignore_events(true); 202 cursor_window_->set_owned_by_parent(false); 203 UpdateCursorImage(); 204 205 container->AddChild(cursor_window_.get()); 206 cursor_window_->Show(); 207 SetBoundsInScreen(container->bounds()); 208 } 209 210 void CursorWindowController::SetBoundsInScreen(const gfx::Rect& bounds) { 211 bounds_in_screen_ = bounds; 212 UpdateLocation(); 213 } 214 215 void CursorWindowController::UpdateCursorImage() { 216 int resource_id; 217 // TODO(hshi): support custom cursor set. 218 if (!ui::GetCursorDataFor(cursor_set_, 219 cursor_type_, 220 display_.device_scale_factor(), 221 &resource_id, 222 &hot_point_)) { 223 return; 224 } 225 const gfx::ImageSkia* image = 226 ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id); 227 gfx::ImageSkia rotated = *image; 228 if (!is_cursor_compositing_enabled_) { 229 switch (cursor_rotation_) { 230 case gfx::Display::ROTATE_0: 231 break; 232 case gfx::Display::ROTATE_90: 233 rotated = gfx::ImageSkiaOperations::CreateRotatedImage( 234 *image, SkBitmapOperations::ROTATION_90_CW); 235 hot_point_.SetPoint( 236 rotated.width() - hot_point_.y(), 237 hot_point_.x()); 238 break; 239 case gfx::Display::ROTATE_180: 240 rotated = gfx::ImageSkiaOperations::CreateRotatedImage( 241 *image, SkBitmapOperations::ROTATION_180_CW); 242 hot_point_.SetPoint( 243 rotated.height() - hot_point_.x(), 244 rotated.width() - hot_point_.y()); 245 break; 246 case gfx::Display::ROTATE_270: 247 rotated = gfx::ImageSkiaOperations::CreateRotatedImage( 248 *image, SkBitmapOperations::ROTATION_270_CW); 249 hot_point_.SetPoint( 250 hot_point_.y(), 251 rotated.height() - hot_point_.x()); 252 break; 253 } 254 } else { 255 hot_point_ = ui::ConvertPointToDIP(Shell::GetPrimaryRootWindow()->layer(), 256 hot_point_); 257 } 258 delegate_->SetCursorImage(rotated, display_); 259 if (cursor_window_) { 260 cursor_window_->SetBounds(gfx::Rect(delegate_->size())); 261 cursor_window_->SchedulePaintInRect( 262 gfx::Rect(cursor_window_->bounds().size())); 263 UpdateLocation(); 264 } 265 } 266 267 } // namespace ash 268