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.
5 #include "ui/base/cursor/cursor_loader_x11.h"
9 #include <X11/cursorfont.h>
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"
26 // Returns X font cursor shape from an Aura cursor.
27 int CursorShapeFromNative(const gfx::NativeCursor& native_cursor) {
28 switch (native_cursor.native_type()) {
29 case ui::kCursorMiddlePanning:
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;
49 case ui::kCursorGrabbing:
50 // TODO(jamescook): Need cursors for these. crbug.com/111650
53 #if defined(OS_CHROMEOS)
55 case ui::kCursorPointer:
56 case ui::kCursorNoDrop:
57 case ui::kCursorNotAllowed:
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:
71 case ui::kCursorContextMenu:
72 case ui::kCursorCross:
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:
86 // In some environments, the image assets are not set (e.g. in
87 // content-browsertests, content-shell etc.).
89 #else // defined(OS_CHROMEOS)
92 case ui::kCursorPointer:
96 case ui::kCursorCross:
100 case ui::kCursorIBeam:
102 case ui::kCursorProgress:
103 case ui::kCursorWait:
105 case ui::kCursorHelp:
106 return XC_question_arrow;
107 case ui::kCursorEastResize:
108 return XC_right_side;
109 case ui::kCursorNorthResize:
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:
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:
136 NOTREACHED() << "Case not handled for " << native_cursor.native_type();
144 CursorLoader* CursorLoader::Create() {
145 return new CursorLoaderX11;
148 CursorLoaderX11::CursorLoaderX11()
149 : invisible_cursor_(CreateInvisibleCursor(), gfx::GetXDisplay()) {
152 CursorLoaderX11::~CursorLoaderX11() {
156 void CursorLoaderX11::LoadImageCursor(int 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 display().device_scale_factor());
163 SkBitmap bitmap = image_rep.sk_bitmap();
164 gfx::Point hotpoint = hot;
165 ScaleAndRotateCursorBitmapAndHotpoint(
166 scale(), display().rotation(), &bitmap, &hotpoint);
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.
173 void CursorLoaderX11::LoadAnimatedCursor(int 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 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;
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);
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());
201 XcursorImage* x_image = SkBitmapToXcursorImage(&cropped, hotpoint);
203 x_image->delay = frame_delay_ms;
204 x_images->images[frame] = x_image;
207 animated_cursors_[id] = std::make_pair(
208 XcursorImagesLoadCursor(gfx::GetXDisplay(), x_images), x_images);
209 // |bitmap| is owned by the resource bundle. So we do not need to free it.
212 void CursorLoaderX11::UnloadAll() {
213 for (ImageCursorMap::const_iterator it = cursors_.begin();
214 it != cursors_.end(); ++it)
215 UnrefCustomXCursor(it->second);
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(gfx::GetXDisplay(), it->second.first);
225 void CursorLoaderX11::SetPlatformCursor(gfx::NativeCursor* cursor) {
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));
239 xcursor = ImageCursorFromNative(kCursorPointer);
242 cursor->SetPlatformCursor(xcursor);
245 bool CursorLoaderX11::IsImageCursor(gfx::NativeCursor native_cursor) {
246 int type = native_cursor.native_type();
247 return cursors_.count(type) || animated_cursors_.count(type);
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;
256 ImageCursorMap::iterator find = cursors_.find(type);
257 if (find != cursors_.end())
258 return cursors_[type];
259 return GetXCursor(CursorShapeFromNative(native_cursor));
262 void ScaleAndRotateCursorBitmapAndHotpoint(float scale,
263 gfx::Display::Rotation rotation,
265 gfx::Point* hotpoint) {
267 case gfx::Display::ROTATE_0:
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);
274 case gfx::Display::ROTATE_180:
276 bitmap->width() - hotpoint->x(), bitmap->height() - hotpoint->y());
277 *bitmap = SkBitmapOperations::Rotate(
278 *bitmap, SkBitmapOperations::ROTATION_180_CW);
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);
287 if (scale < FLT_EPSILON) {
288 NOTREACHED() << "Scale must be larger than 0.";
295 gfx::Size scaled_size = gfx::ToFlooredSize(
296 gfx::ScaleSize(gfx::Size(bitmap->width(), bitmap->height()), scale));
298 *bitmap = skia::ImageOperations::Resize(
300 skia::ImageOperations::RESIZE_BETTER,
302 scaled_size.height());
303 *hotpoint = gfx::ToFlooredPoint(gfx::ScalePoint(*hotpoint, scale));