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 "skia/ext/image_operations.h" 13#include "ui/base/cursor/cursor.h" 14#include "ui/base/cursor/cursor_util.h" 15#include "ui/base/x/x11_util.h" 16#include "ui/gfx/image/image.h" 17#include "ui/gfx/point_conversions.h" 18#include "ui/gfx/size_conversions.h" 19#include "ui/gfx/skbitmap_operations.h" 20#include "ui/gfx/skia_util.h" 21 22namespace { 23 24// Returns X font cursor shape from an Aura cursor. 25int CursorShapeFromNative(const gfx::NativeCursor& native_cursor) { 26 switch (native_cursor.native_type()) { 27 case ui::kCursorMiddlePanning: 28 return XC_fleur; 29 case ui::kCursorEastPanning: 30 return XC_sb_right_arrow; 31 case ui::kCursorNorthPanning: 32 return XC_sb_up_arrow; 33 case ui::kCursorNorthEastPanning: 34 return XC_top_right_corner; 35 case ui::kCursorNorthWestPanning: 36 return XC_top_left_corner; 37 case ui::kCursorSouthPanning: 38 return XC_sb_down_arrow; 39 case ui::kCursorSouthEastPanning: 40 return XC_bottom_right_corner; 41 case ui::kCursorSouthWestPanning: 42 return XC_bottom_left_corner; 43 case ui::kCursorWestPanning: 44 return XC_sb_left_arrow; 45 case ui::kCursorNone: 46 case ui::kCursorGrab: 47 case ui::kCursorGrabbing: 48 // TODO(jamescook): Need cursors for these. crbug.com/111650 49 return XC_left_ptr; 50 51#if defined(OS_CHROMEOS) 52 case ui::kCursorNull: 53 case ui::kCursorPointer: 54 case ui::kCursorNoDrop: 55 case ui::kCursorNotAllowed: 56 case ui::kCursorCopy: 57 case ui::kCursorMove: 58 case ui::kCursorEastResize: 59 case ui::kCursorNorthResize: 60 case ui::kCursorSouthResize: 61 case ui::kCursorWestResize: 62 case ui::kCursorNorthEastResize: 63 case ui::kCursorNorthWestResize: 64 case ui::kCursorSouthWestResize: 65 case ui::kCursorSouthEastResize: 66 case ui::kCursorIBeam: 67 case ui::kCursorAlias: 68 case ui::kCursorCell: 69 case ui::kCursorContextMenu: 70 case ui::kCursorCross: 71 case ui::kCursorHelp: 72 case ui::kCursorWait: 73 case ui::kCursorNorthSouthResize: 74 case ui::kCursorEastWestResize: 75 case ui::kCursorNorthEastSouthWestResize: 76 case ui::kCursorNorthWestSouthEastResize: 77 case ui::kCursorProgress: 78 case ui::kCursorColumnResize: 79 case ui::kCursorRowResize: 80 case ui::kCursorVerticalText: 81 case ui::kCursorZoomIn: 82 case ui::kCursorZoomOut: 83 case ui::kCursorHand: 84 // In some environments, the image assets are not set (e.g. in 85 // content-browsertests, content-shell etc.). 86 return XC_left_ptr; 87#else // defined(OS_CHROMEOS) 88 case ui::kCursorNull: 89 return XC_left_ptr; 90 case ui::kCursorPointer: 91 return XC_left_ptr; 92 case ui::kCursorMove: 93 return XC_fleur; 94 case ui::kCursorCross: 95 return XC_crosshair; 96 case ui::kCursorHand: 97 return XC_hand2; 98 case ui::kCursorIBeam: 99 return XC_xterm; 100 case ui::kCursorProgress: 101 case ui::kCursorWait: 102 return XC_watch; 103 case ui::kCursorHelp: 104 return XC_question_arrow; 105 case ui::kCursorEastResize: 106 return XC_right_side; 107 case ui::kCursorNorthResize: 108 return XC_top_side; 109 case ui::kCursorNorthEastResize: 110 return XC_top_right_corner; 111 case ui::kCursorNorthWestResize: 112 return XC_top_left_corner; 113 case ui::kCursorSouthResize: 114 return XC_bottom_side; 115 case ui::kCursorSouthEastResize: 116 return XC_bottom_right_corner; 117 case ui::kCursorSouthWestResize: 118 return XC_bottom_left_corner; 119 case ui::kCursorWestResize: 120 return XC_left_side; 121 case ui::kCursorNorthSouthResize: 122 return XC_sb_v_double_arrow; 123 case ui::kCursorEastWestResize: 124 return XC_sb_h_double_arrow; 125 case ui::kCursorColumnResize: 126 return XC_sb_h_double_arrow; 127 case ui::kCursorRowResize: 128 return XC_sb_v_double_arrow; 129#endif // defined(OS_CHROMEOS) 130 case ui::kCursorCustom: 131 NOTREACHED(); 132 return XC_left_ptr; 133 } 134 NOTREACHED() << "Case not handled for " << native_cursor.native_type(); 135 return XC_left_ptr; 136} 137 138} // namespace 139 140namespace ui { 141 142CursorLoader* CursorLoader::Create() { 143 return new CursorLoaderX11; 144} 145 146CursorLoaderX11::CursorLoaderX11() 147 : invisible_cursor_(CreateInvisibleCursor(), gfx::GetXDisplay()) { 148} 149 150CursorLoaderX11::~CursorLoaderX11() { 151 UnloadAll(); 152} 153 154void CursorLoaderX11::LoadImageCursor(int id, 155 int resource_id, 156 const gfx::Point& hot) { 157 SkBitmap bitmap; 158 gfx::Point hotspot = hot; 159 160 GetImageCursorBitmap(resource_id, scale(), rotation(), &hotspot, &bitmap); 161 XcursorImage* x_image = SkBitmapToXcursorImage(&bitmap, hotspot); 162 cursors_[id] = CreateReffedCustomXCursor(x_image); 163} 164 165void CursorLoaderX11::LoadAnimatedCursor(int id, 166 int resource_id, 167 const gfx::Point& hot, 168 int frame_delay_ms) { 169 std::vector<SkBitmap> bitmaps; 170 gfx::Point hotspot = hot; 171 172 GetAnimatedCursorBitmaps( 173 resource_id, scale(), rotation(), &hotspot, &bitmaps); 174 175 XcursorImages* x_images = XcursorImagesCreate(bitmaps.size()); 176 x_images->nimage = bitmaps.size(); 177 178 for (unsigned int frame = 0; frame < bitmaps.size(); ++frame) { 179 XcursorImage* x_image = SkBitmapToXcursorImage(&bitmaps[frame], hotspot); 180 x_image->delay = frame_delay_ms; 181 x_images->images[frame] = x_image; 182 } 183 184 animated_cursors_[id] = std::make_pair( 185 XcursorImagesLoadCursor(gfx::GetXDisplay(), x_images), x_images); 186} 187 188void CursorLoaderX11::UnloadAll() { 189 for (ImageCursorMap::const_iterator it = cursors_.begin(); 190 it != cursors_.end(); ++it) 191 UnrefCustomXCursor(it->second); 192 193 // Free animated cursors and images. 194 for (AnimatedCursorMap::iterator it = animated_cursors_.begin(); 195 it != animated_cursors_.end(); ++it) { 196 XcursorImagesDestroy(it->second.second); // also frees individual frames. 197 XFreeCursor(gfx::GetXDisplay(), it->second.first); 198 } 199} 200 201void CursorLoaderX11::SetPlatformCursor(gfx::NativeCursor* cursor) { 202 DCHECK(cursor); 203 204 ::Cursor xcursor; 205 if (IsImageCursor(*cursor)) 206 xcursor = ImageCursorFromNative(*cursor); 207 else if (*cursor == kCursorNone) 208 xcursor = invisible_cursor_.get(); 209 else if (*cursor == kCursorCustom) 210 xcursor = cursor->platform(); 211 else if (scale() == 1.0f && rotation() == gfx::Display::ROTATE_0) { 212 xcursor = GetXCursor(CursorShapeFromNative(*cursor)); 213 } else { 214 xcursor = ImageCursorFromNative(kCursorPointer); 215 } 216 217 cursor->SetPlatformCursor(xcursor); 218} 219 220const XcursorImage* CursorLoaderX11::GetXcursorImageForTest(int id) { 221 return test::GetCachedXcursorImage(cursors_[id]); 222} 223 224bool CursorLoaderX11::IsImageCursor(gfx::NativeCursor native_cursor) { 225 int type = native_cursor.native_type(); 226 return cursors_.count(type) || animated_cursors_.count(type); 227} 228 229::Cursor CursorLoaderX11::ImageCursorFromNative( 230 gfx::NativeCursor native_cursor) { 231 int type = native_cursor.native_type(); 232 if (animated_cursors_.count(type)) 233 return animated_cursors_[type].first; 234 235 ImageCursorMap::iterator find = cursors_.find(type); 236 if (find != cursors_.end()) 237 return cursors_[type]; 238 return GetXCursor(CursorShapeFromNative(native_cursor)); 239} 240 241} // namespace ui 242