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 "skia/ext/image_operations.h"
13 #include "ui/base/cursor/cursor.h"
14 #include "ui/base/cursor/cursor_util.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(scale());
162 SkBitmap bitmap
= image_rep
.sk_bitmap();
163 gfx::Point hotpoint
= hot
;
164 // TODO(oshima): The cursor should use resource scale factor when
165 // fractional scale factor is enabled. crbug.com/372212
166 ScaleAndRotateCursorBitmapAndHotpoint(
167 scale() / image_rep
.scale(), rotation(), &bitmap
, &hotpoint
);
169 XcursorImage
* x_image
= SkBitmapToXcursorImage(&bitmap
, hotpoint
);
170 cursors_
[id
] = CreateReffedCustomXCursor(x_image
);
171 // |image_rep| is owned by the resource bundle. So we do not need to free it.
174 void CursorLoaderX11::LoadAnimatedCursor(int id
,
176 const gfx::Point
& hot
,
177 int frame_delay_ms
) {
178 // TODO(oshima|tdanderson): Support rotation and fractional scale factor.
179 const gfx::ImageSkia
* image
=
180 ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id
);
181 const gfx::ImageSkiaRep
& image_rep
= image
->GetRepresentation(scale());
182 SkBitmap bitmap
= image_rep
.sk_bitmap();
183 int frame_width
= bitmap
.height();
184 int frame_height
= frame_width
;
185 int total_width
= bitmap
.width();
186 DCHECK_EQ(total_width
% frame_width
, 0);
187 int frame_count
= total_width
/ frame_width
;
188 DCHECK_GT(frame_count
, 0);
189 XcursorImages
* x_images
= XcursorImagesCreate(frame_count
);
190 x_images
->nimage
= frame_count
;
192 for (int frame
= 0; frame
< frame_count
; ++frame
) {
193 gfx::Point hotpoint
= hot
;
194 int x_offset
= frame_width
* frame
;
195 DCHECK_LE(x_offset
+ frame_width
, total_width
);
197 SkBitmap cropped
= SkBitmapOperations::CreateTiledBitmap(
198 bitmap
, x_offset
, 0, frame_width
, frame_height
);
199 DCHECK_EQ(frame_width
, cropped
.width());
200 DCHECK_EQ(frame_height
, cropped
.height());
202 XcursorImage
* x_image
= SkBitmapToXcursorImage(&cropped
, hotpoint
);
204 x_image
->delay
= frame_delay_ms
;
205 x_images
->images
[frame
] = x_image
;
208 animated_cursors_
[id
] = std::make_pair(
209 XcursorImagesLoadCursor(gfx::GetXDisplay(), x_images
), x_images
);
210 // |bitmap| is owned by the resource bundle. So we do not need to free it.
213 void CursorLoaderX11::UnloadAll() {
214 for (ImageCursorMap::const_iterator it
= cursors_
.begin();
215 it
!= cursors_
.end(); ++it
)
216 UnrefCustomXCursor(it
->second
);
218 // Free animated cursors and images.
219 for (AnimatedCursorMap::iterator it
= animated_cursors_
.begin();
220 it
!= animated_cursors_
.end(); ++it
) {
221 XcursorImagesDestroy(it
->second
.second
); // also frees individual frames.
222 XFreeCursor(gfx::GetXDisplay(), it
->second
.first
);
226 void CursorLoaderX11::SetPlatformCursor(gfx::NativeCursor
* cursor
) {
230 if (IsImageCursor(*cursor
))
231 xcursor
= ImageCursorFromNative(*cursor
);
232 else if (*cursor
== kCursorNone
)
233 xcursor
= invisible_cursor_
.get();
234 else if (*cursor
== kCursorCustom
)
235 xcursor
= cursor
->platform();
236 else if (scale() == 1.0f
&& rotation() == gfx::Display::ROTATE_0
) {
237 xcursor
= GetXCursor(CursorShapeFromNative(*cursor
));
239 xcursor
= ImageCursorFromNative(kCursorPointer
);
242 cursor
->SetPlatformCursor(xcursor
);
245 const XcursorImage
* CursorLoaderX11::GetXcursorImageForTest(int id
) {
246 return test::GetCachedXcursorImage(cursors_
[id
]);
249 bool CursorLoaderX11::IsImageCursor(gfx::NativeCursor native_cursor
) {
250 int type
= native_cursor
.native_type();
251 return cursors_
.count(type
) || animated_cursors_
.count(type
);
254 ::Cursor
CursorLoaderX11::ImageCursorFromNative(
255 gfx::NativeCursor native_cursor
) {
256 int type
= native_cursor
.native_type();
257 if (animated_cursors_
.count(type
))
258 return animated_cursors_
[type
].first
;
260 ImageCursorMap::iterator find
= cursors_
.find(type
);
261 if (find
!= cursors_
.end())
262 return cursors_
[type
];
263 return GetXCursor(CursorShapeFromNative(native_cursor
));