1// Copyright (c) 2012 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 "ui/base/cursor/cursor_loader_x11.h" 6 7#include <float.h> 8#include <X11/Xlib.h> 9#include <X11/cursorfont.h> 10 11#include "base/logging.h" 12#include "grit/ui_resources.h" 13#include "skia/ext/image_operations.h" 14#include "ui/base/cursor/cursor.h" 15#include "ui/base/resource/resource_bundle.h" 16#include "ui/base/x/x11_util.h" 17#include "ui/gfx/image/image.h" 18#include "ui/gfx/image/image_skia.h" 19#include "ui/gfx/point_conversions.h" 20#include "ui/gfx/size_conversions.h" 21#include "ui/gfx/skbitmap_operations.h" 22#include "ui/gfx/skia_util.h" 23 24namespace { 25 26// Returns X font cursor shape from an Aura cursor. 27int CursorShapeFromNative(const gfx::NativeCursor& native_cursor) { 28 switch (native_cursor.native_type()) { 29 case ui::kCursorMiddlePanning: 30 return XC_fleur; 31 case ui::kCursorEastPanning: 32 return XC_sb_right_arrow; 33 case ui::kCursorNorthPanning: 34 return XC_sb_up_arrow; 35 case ui::kCursorNorthEastPanning: 36 return XC_top_right_corner; 37 case ui::kCursorNorthWestPanning: 38 return XC_top_left_corner; 39 case ui::kCursorSouthPanning: 40 return XC_sb_down_arrow; 41 case ui::kCursorSouthEastPanning: 42 return XC_bottom_right_corner; 43 case ui::kCursorSouthWestPanning: 44 return XC_bottom_left_corner; 45 case ui::kCursorWestPanning: 46 return XC_sb_left_arrow; 47 case ui::kCursorNone: 48 case ui::kCursorGrab: 49 case ui::kCursorGrabbing: 50 // TODO(jamescook): Need cursors for these. crbug.com/111650 51 return XC_left_ptr; 52 53#if defined(OS_CHROMEOS) 54 case ui::kCursorNull: 55 case ui::kCursorPointer: 56 case ui::kCursorNoDrop: 57 case ui::kCursorNotAllowed: 58 case ui::kCursorCopy: 59 case ui::kCursorMove: 60 case ui::kCursorEastResize: 61 case ui::kCursorNorthResize: 62 case ui::kCursorSouthResize: 63 case ui::kCursorWestResize: 64 case ui::kCursorNorthEastResize: 65 case ui::kCursorNorthWestResize: 66 case ui::kCursorSouthWestResize: 67 case ui::kCursorSouthEastResize: 68 case ui::kCursorIBeam: 69 case ui::kCursorAlias: 70 case ui::kCursorCell: 71 case ui::kCursorContextMenu: 72 case ui::kCursorCross: 73 case ui::kCursorHelp: 74 case ui::kCursorWait: 75 case ui::kCursorNorthSouthResize: 76 case ui::kCursorEastWestResize: 77 case ui::kCursorNorthEastSouthWestResize: 78 case ui::kCursorNorthWestSouthEastResize: 79 case ui::kCursorProgress: 80 case ui::kCursorColumnResize: 81 case ui::kCursorRowResize: 82 case ui::kCursorVerticalText: 83 case ui::kCursorZoomIn: 84 case ui::kCursorZoomOut: 85 case ui::kCursorHand: 86 // In some environments, the image assets are not set (e.g. in 87 // content-browsertests, content-shell etc.). 88 return XC_left_ptr; 89#else // defined(OS_CHROMEOS) 90 case ui::kCursorNull: 91 return XC_left_ptr; 92 case ui::kCursorPointer: 93 return XC_left_ptr; 94 case ui::kCursorMove: 95 return XC_fleur; 96 case ui::kCursorCross: 97 return XC_crosshair; 98 case ui::kCursorHand: 99 return XC_hand2; 100 case ui::kCursorIBeam: 101 return XC_xterm; 102 case ui::kCursorProgress: 103 case ui::kCursorWait: 104 return XC_watch; 105 case ui::kCursorHelp: 106 return XC_question_arrow; 107 case ui::kCursorEastResize: 108 return XC_right_side; 109 case ui::kCursorNorthResize: 110 return XC_top_side; 111 case ui::kCursorNorthEastResize: 112 return XC_top_right_corner; 113 case ui::kCursorNorthWestResize: 114 return XC_top_left_corner; 115 case ui::kCursorSouthResize: 116 return XC_bottom_side; 117 case ui::kCursorSouthEastResize: 118 return XC_bottom_right_corner; 119 case ui::kCursorSouthWestResize: 120 return XC_bottom_left_corner; 121 case ui::kCursorWestResize: 122 return XC_left_side; 123 case ui::kCursorNorthSouthResize: 124 return XC_sb_v_double_arrow; 125 case ui::kCursorEastWestResize: 126 return XC_sb_h_double_arrow; 127 case ui::kCursorColumnResize: 128 return XC_sb_h_double_arrow; 129 case ui::kCursorRowResize: 130 return XC_sb_v_double_arrow; 131#endif // defined(OS_CHROMEOS) 132 case ui::kCursorCustom: 133 NOTREACHED(); 134 return XC_left_ptr; 135 } 136 NOTREACHED() << "Case not handled for " << native_cursor.native_type(); 137 return XC_left_ptr; 138} 139 140} // namespace 141 142namespace ui { 143 144CursorLoader* CursorLoader::Create() { 145 return new CursorLoaderX11; 146} 147 148CursorLoaderX11::CursorLoaderX11() 149 : invisible_cursor_(CreateInvisibleCursor(), GetXDisplay()) { 150} 151 152CursorLoaderX11::~CursorLoaderX11() { 153 UnloadAll(); 154} 155 156void CursorLoaderX11::LoadImageCursor(int id, 157 int resource_id, 158 const gfx::Point& hot) { 159 const gfx::ImageSkia* image = 160 ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id); 161 const gfx::ImageSkiaRep& image_rep = image->GetRepresentation( 162 GetScaleFactorFromScale(display().device_scale_factor())); 163 SkBitmap bitmap = image_rep.sk_bitmap(); 164 gfx::Point hotpoint = hot; 165 ScaleAndRotateCursorBitmapAndHotpoint( 166 scale(), display().rotation(), &bitmap, &hotpoint); 167 168 XcursorImage* x_image = SkBitmapToXcursorImage(&bitmap, hotpoint); 169 cursors_[id] = CreateReffedCustomXCursor(x_image); 170 // |image_rep| is owned by the resource bundle. So we do not need to free it. 171} 172 173void CursorLoaderX11::LoadAnimatedCursor(int id, 174 int resource_id, 175 const gfx::Point& hot, 176 int frame_delay_ms) { 177 const gfx::ImageSkia* image = 178 ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id); 179 const gfx::ImageSkiaRep& image_rep = image->GetRepresentation( 180 GetScaleFactorFromScale(display().device_scale_factor())); 181 SkBitmap bitmap = image_rep.sk_bitmap(); 182 int frame_width = bitmap.height(); 183 int frame_height = frame_width; 184 int total_width = bitmap.width(); 185 DCHECK_EQ(total_width % frame_width, 0); 186 int frame_count = total_width / frame_width; 187 DCHECK_GT(frame_count, 0); 188 XcursorImages* x_images = XcursorImagesCreate(frame_count); 189 x_images->nimage = frame_count; 190 191 for (int frame = 0; frame < frame_count; ++frame) { 192 gfx::Point hotpoint = hot; 193 int x_offset = frame_width * frame; 194 DCHECK_LE(x_offset + frame_width, total_width); 195 196 SkBitmap cropped = SkBitmapOperations::CreateTiledBitmap( 197 bitmap, x_offset, 0, frame_width, frame_height); 198 DCHECK_EQ(frame_width, cropped.width()); 199 DCHECK_EQ(frame_height, cropped.height()); 200 201 XcursorImage* x_image = SkBitmapToXcursorImage(&cropped, hotpoint); 202 203 x_image->delay = frame_delay_ms; 204 x_images->images[frame] = x_image; 205 } 206 207 animated_cursors_[id] = std::make_pair( 208 XcursorImagesLoadCursor(GetXDisplay(), x_images), x_images); 209 // |bitmap| is owned by the resource bundle. So we do not need to free it. 210} 211 212void CursorLoaderX11::UnloadAll() { 213 for (ImageCursorMap::const_iterator it = cursors_.begin(); 214 it != cursors_.end(); ++it) 215 UnrefCustomXCursor(it->second); 216 217 // Free animated cursors and images. 218 for (AnimatedCursorMap::iterator it = animated_cursors_.begin(); 219 it != animated_cursors_.end(); ++it) { 220 XcursorImagesDestroy(it->second.second); // also frees individual frames. 221 XFreeCursor(GetXDisplay(), it->second.first); 222 } 223} 224 225void CursorLoaderX11::SetPlatformCursor(gfx::NativeCursor* cursor) { 226 DCHECK(cursor); 227 228 ::Cursor xcursor; 229 if (IsImageCursor(*cursor)) 230 xcursor = ImageCursorFromNative(*cursor); 231 else if (*cursor == kCursorNone) 232 xcursor = invisible_cursor_.get(); 233 else if (*cursor == kCursorCustom) 234 xcursor = cursor->platform(); 235 else if (display().device_scale_factor() == 1.0f && 236 display().rotation() == gfx::Display::ROTATE_0) { 237 xcursor = GetXCursor(CursorShapeFromNative(*cursor)); 238 } else { 239 xcursor = ImageCursorFromNative(kCursorPointer); 240 } 241 242 cursor->SetPlatformCursor(xcursor); 243} 244 245bool CursorLoaderX11::IsImageCursor(gfx::NativeCursor native_cursor) { 246 int type = native_cursor.native_type(); 247 return cursors_.count(type) || animated_cursors_.count(type); 248} 249 250::Cursor CursorLoaderX11::ImageCursorFromNative( 251 gfx::NativeCursor native_cursor) { 252 int type = native_cursor.native_type(); 253 if (animated_cursors_.count(type)) 254 return animated_cursors_[type].first; 255 256 ImageCursorMap::iterator find = cursors_.find(type); 257 if (find != cursors_.end()) 258 return cursors_[type]; 259 return GetXCursor(CursorShapeFromNative(native_cursor)); 260} 261 262void ScaleAndRotateCursorBitmapAndHotpoint(float scale, 263 gfx::Display::Rotation rotation, 264 SkBitmap* bitmap, 265 gfx::Point* hotpoint) { 266 switch (rotation) { 267 case gfx::Display::ROTATE_0: 268 break; 269 case gfx::Display::ROTATE_90: 270 hotpoint->SetPoint(bitmap->height() - hotpoint->y(), hotpoint->x()); 271 *bitmap = SkBitmapOperations::Rotate( 272 *bitmap, SkBitmapOperations::ROTATION_90_CW); 273 break; 274 case gfx::Display::ROTATE_180: 275 hotpoint->SetPoint( 276 bitmap->width() - hotpoint->x(), bitmap->height() - hotpoint->y()); 277 *bitmap = SkBitmapOperations::Rotate( 278 *bitmap, SkBitmapOperations::ROTATION_180_CW); 279 break; 280 case gfx::Display::ROTATE_270: 281 hotpoint->SetPoint(hotpoint->y(), bitmap->width() - hotpoint->x()); 282 *bitmap = SkBitmapOperations::Rotate( 283 *bitmap, SkBitmapOperations::ROTATION_270_CW); 284 break; 285 } 286 287 if (scale < FLT_EPSILON) { 288 NOTREACHED() << "Scale must be larger than 0."; 289 scale = 1.0f; 290 } 291 292 if (scale == 1.0f) 293 return; 294 295 gfx::Size scaled_size = gfx::ToFlooredSize( 296 gfx::ScaleSize(gfx::Size(bitmap->width(), bitmap->height()), scale)); 297 298 *bitmap = skia::ImageOperations::Resize( 299 *bitmap, 300 skia::ImageOperations::RESIZE_BETTER, 301 scaled_size.width(), 302 scaled_size.height()); 303 *hotpoint = gfx::ToFlooredPoint(gfx::ScalePoint(*hotpoint, scale)); 304} 305 306} // namespace ui 307