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/cursor/cursor_util.h"
16 #include "ui/base/resource/resource_bundle.h"
17 #include "ui/base/x/x11_util.h"
18 #include "ui/gfx/image/image.h"
19 #include "ui/gfx/image/image_skia.h"
20 #include "ui/gfx/point_conversions.h"
21 #include "ui/gfx/size_conversions.h"
22 #include "ui/gfx/skbitmap_operations.h"
23 #include "ui/gfx/skia_util.h"
27 // Returns X font cursor shape from an Aura cursor.
28 int CursorShapeFromNative(const gfx::NativeCursor
& native_cursor
) {
29 switch (native_cursor
.native_type()) {
30 case ui::kCursorMiddlePanning
:
32 case ui::kCursorEastPanning
:
33 return XC_sb_right_arrow
;
34 case ui::kCursorNorthPanning
:
35 return XC_sb_up_arrow
;
36 case ui::kCursorNorthEastPanning
:
37 return XC_top_right_corner
;
38 case ui::kCursorNorthWestPanning
:
39 return XC_top_left_corner
;
40 case ui::kCursorSouthPanning
:
41 return XC_sb_down_arrow
;
42 case ui::kCursorSouthEastPanning
:
43 return XC_bottom_right_corner
;
44 case ui::kCursorSouthWestPanning
:
45 return XC_bottom_left_corner
;
46 case ui::kCursorWestPanning
:
47 return XC_sb_left_arrow
;
50 case ui::kCursorGrabbing
:
51 // TODO(jamescook): Need cursors for these. crbug.com/111650
54 #if defined(OS_CHROMEOS)
56 case ui::kCursorPointer
:
57 case ui::kCursorNoDrop
:
58 case ui::kCursorNotAllowed
:
61 case ui::kCursorEastResize
:
62 case ui::kCursorNorthResize
:
63 case ui::kCursorSouthResize
:
64 case ui::kCursorWestResize
:
65 case ui::kCursorNorthEastResize
:
66 case ui::kCursorNorthWestResize
:
67 case ui::kCursorSouthWestResize
:
68 case ui::kCursorSouthEastResize
:
69 case ui::kCursorIBeam
:
70 case ui::kCursorAlias
:
72 case ui::kCursorContextMenu
:
73 case ui::kCursorCross
:
76 case ui::kCursorNorthSouthResize
:
77 case ui::kCursorEastWestResize
:
78 case ui::kCursorNorthEastSouthWestResize
:
79 case ui::kCursorNorthWestSouthEastResize
:
80 case ui::kCursorProgress
:
81 case ui::kCursorColumnResize
:
82 case ui::kCursorRowResize
:
83 case ui::kCursorVerticalText
:
84 case ui::kCursorZoomIn
:
85 case ui::kCursorZoomOut
:
87 // In some environments, the image assets are not set (e.g. in
88 // content-browsertests, content-shell etc.).
90 #else // defined(OS_CHROMEOS)
93 case ui::kCursorPointer
:
97 case ui::kCursorCross
:
101 case ui::kCursorIBeam
:
103 case ui::kCursorProgress
:
104 case ui::kCursorWait
:
106 case ui::kCursorHelp
:
107 return XC_question_arrow
;
108 case ui::kCursorEastResize
:
109 return XC_right_side
;
110 case ui::kCursorNorthResize
:
112 case ui::kCursorNorthEastResize
:
113 return XC_top_right_corner
;
114 case ui::kCursorNorthWestResize
:
115 return XC_top_left_corner
;
116 case ui::kCursorSouthResize
:
117 return XC_bottom_side
;
118 case ui::kCursorSouthEastResize
:
119 return XC_bottom_right_corner
;
120 case ui::kCursorSouthWestResize
:
121 return XC_bottom_left_corner
;
122 case ui::kCursorWestResize
:
124 case ui::kCursorNorthSouthResize
:
125 return XC_sb_v_double_arrow
;
126 case ui::kCursorEastWestResize
:
127 return XC_sb_h_double_arrow
;
128 case ui::kCursorColumnResize
:
129 return XC_sb_h_double_arrow
;
130 case ui::kCursorRowResize
:
131 return XC_sb_v_double_arrow
;
132 #endif // defined(OS_CHROMEOS)
133 case ui::kCursorCustom
:
137 NOTREACHED() << "Case not handled for " << native_cursor
.native_type();
145 CursorLoader
* CursorLoader::Create() {
146 return new CursorLoaderX11
;
149 CursorLoaderX11::CursorLoaderX11()
150 : invisible_cursor_(CreateInvisibleCursor(), gfx::GetXDisplay()) {
153 CursorLoaderX11::~CursorLoaderX11() {
157 void CursorLoaderX11::LoadImageCursor(int id
,
159 const gfx::Point
& hot
) {
160 const gfx::ImageSkia
* image
=
161 ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id
);
162 const gfx::ImageSkiaRep
& image_rep
= image
->GetRepresentation(scale());
163 SkBitmap bitmap
= image_rep
.sk_bitmap();
164 gfx::Point hotpoint
= hot
;
165 // TODO(oshima): The cursor should use resource scale factor when
166 // fractional scale factor is enabled. crbug.com/372212
167 ScaleAndRotateCursorBitmapAndHotpoint(
168 scale() / image_rep
.scale(), rotation(), &bitmap
, &hotpoint
);
170 XcursorImage
* x_image
= SkBitmapToXcursorImage(&bitmap
, hotpoint
);
171 cursors_
[id
] = CreateReffedCustomXCursor(x_image
);
172 // |image_rep| is owned by the resource bundle. So we do not need to free it.
175 void CursorLoaderX11::LoadAnimatedCursor(int id
,
177 const gfx::Point
& hot
,
178 int frame_delay_ms
) {
179 // TODO(oshima|tdanderson): Support rotation and fractional scale factor.
180 const gfx::ImageSkia
* image
=
181 ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id
);
182 const gfx::ImageSkiaRep
& image_rep
= image
->GetRepresentation(scale());
183 SkBitmap bitmap
= image_rep
.sk_bitmap();
184 int frame_width
= bitmap
.height();
185 int frame_height
= frame_width
;
186 int total_width
= bitmap
.width();
187 DCHECK_EQ(total_width
% frame_width
, 0);
188 int frame_count
= total_width
/ frame_width
;
189 DCHECK_GT(frame_count
, 0);
190 XcursorImages
* x_images
= XcursorImagesCreate(frame_count
);
191 x_images
->nimage
= frame_count
;
193 for (int frame
= 0; frame
< frame_count
; ++frame
) {
194 gfx::Point hotpoint
= hot
;
195 int x_offset
= frame_width
* frame
;
196 DCHECK_LE(x_offset
+ frame_width
, total_width
);
198 SkBitmap cropped
= SkBitmapOperations::CreateTiledBitmap(
199 bitmap
, x_offset
, 0, frame_width
, frame_height
);
200 DCHECK_EQ(frame_width
, cropped
.width());
201 DCHECK_EQ(frame_height
, cropped
.height());
203 XcursorImage
* x_image
= SkBitmapToXcursorImage(&cropped
, hotpoint
);
205 x_image
->delay
= frame_delay_ms
;
206 x_images
->images
[frame
] = x_image
;
209 animated_cursors_
[id
] = std::make_pair(
210 XcursorImagesLoadCursor(gfx::GetXDisplay(), x_images
), x_images
);
211 // |bitmap| is owned by the resource bundle. So we do not need to free it.
214 void CursorLoaderX11::UnloadAll() {
215 for (ImageCursorMap::const_iterator it
= cursors_
.begin();
216 it
!= cursors_
.end(); ++it
)
217 UnrefCustomXCursor(it
->second
);
219 // Free animated cursors and images.
220 for (AnimatedCursorMap::iterator it
= animated_cursors_
.begin();
221 it
!= animated_cursors_
.end(); ++it
) {
222 XcursorImagesDestroy(it
->second
.second
); // also frees individual frames.
223 XFreeCursor(gfx::GetXDisplay(), it
->second
.first
);
227 void CursorLoaderX11::SetPlatformCursor(gfx::NativeCursor
* cursor
) {
231 if (IsImageCursor(*cursor
))
232 xcursor
= ImageCursorFromNative(*cursor
);
233 else if (*cursor
== kCursorNone
)
234 xcursor
= invisible_cursor_
.get();
235 else if (*cursor
== kCursorCustom
)
236 xcursor
= cursor
->platform();
237 else if (scale() == 1.0f
&& rotation() == gfx::Display::ROTATE_0
) {
238 xcursor
= GetXCursor(CursorShapeFromNative(*cursor
));
240 xcursor
= ImageCursorFromNative(kCursorPointer
);
243 cursor
->SetPlatformCursor(xcursor
);
246 const XcursorImage
* CursorLoaderX11::GetXcursorImageForTest(int id
) {
247 return test::GetCachedXcursorImage(cursors_
[id
]);
250 bool CursorLoaderX11::IsImageCursor(gfx::NativeCursor native_cursor
) {
251 int type
= native_cursor
.native_type();
252 return cursors_
.count(type
) || animated_cursors_
.count(type
);
255 ::Cursor
CursorLoaderX11::ImageCursorFromNative(
256 gfx::NativeCursor native_cursor
) {
257 int type
= native_cursor
.native_type();
258 if (animated_cursors_
.count(type
))
259 return animated_cursors_
[type
].first
;
261 ImageCursorMap::iterator find
= cursors_
.find(type
);
262 if (find
!= cursors_
.end())
263 return cursors_
[type
];
264 return GetXCursor(CursorShapeFromNative(native_cursor
));