• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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"
24 
25 namespace {
26 
27 // Returns X font cursor shape from an Aura cursor.
CursorShapeFromNative(const gfx::NativeCursor & native_cursor)28 int CursorShapeFromNative(const gfx::NativeCursor& native_cursor) {
29   switch (native_cursor.native_type()) {
30     case ui::kCursorMiddlePanning:
31       return XC_fleur;
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;
48     case ui::kCursorNone:
49     case ui::kCursorGrab:
50     case ui::kCursorGrabbing:
51       // TODO(jamescook): Need cursors for these.  crbug.com/111650
52       return XC_left_ptr;
53 
54 #if defined(OS_CHROMEOS)
55     case ui::kCursorNull:
56     case ui::kCursorPointer:
57     case ui::kCursorNoDrop:
58     case ui::kCursorNotAllowed:
59     case ui::kCursorCopy:
60     case ui::kCursorMove:
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:
71     case ui::kCursorCell:
72     case ui::kCursorContextMenu:
73     case ui::kCursorCross:
74     case ui::kCursorHelp:
75     case ui::kCursorWait:
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:
86     case ui::kCursorHand:
87       // In some environments, the image assets are not set (e.g. in
88       // content-browsertests, content-shell etc.).
89       return XC_left_ptr;
90 #else  // defined(OS_CHROMEOS)
91     case ui::kCursorNull:
92       return XC_left_ptr;
93     case ui::kCursorPointer:
94       return XC_left_ptr;
95     case ui::kCursorMove:
96       return XC_fleur;
97     case ui::kCursorCross:
98       return XC_crosshair;
99     case ui::kCursorHand:
100       return XC_hand2;
101     case ui::kCursorIBeam:
102       return XC_xterm;
103     case ui::kCursorProgress:
104     case ui::kCursorWait:
105       return XC_watch;
106     case ui::kCursorHelp:
107       return XC_question_arrow;
108     case ui::kCursorEastResize:
109       return XC_right_side;
110     case ui::kCursorNorthResize:
111       return XC_top_side;
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:
123       return XC_left_side;
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:
134       NOTREACHED();
135       return XC_left_ptr;
136   }
137   NOTREACHED() << "Case not handled for " << native_cursor.native_type();
138   return XC_left_ptr;
139 }
140 
141 }  // namespace
142 
143 namespace ui {
144 
Create()145 CursorLoader* CursorLoader::Create() {
146   return new CursorLoaderX11;
147 }
148 
CursorLoaderX11()149 CursorLoaderX11::CursorLoaderX11()
150     : invisible_cursor_(CreateInvisibleCursor(), gfx::GetXDisplay()) {
151 }
152 
~CursorLoaderX11()153 CursorLoaderX11::~CursorLoaderX11() {
154   UnloadAll();
155 }
156 
LoadImageCursor(int id,int resource_id,const gfx::Point & hot)157 void CursorLoaderX11::LoadImageCursor(int id,
158                                       int resource_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);
169 
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.
173 }
174 
LoadAnimatedCursor(int id,int resource_id,const gfx::Point & hot,int frame_delay_ms)175 void CursorLoaderX11::LoadAnimatedCursor(int id,
176                                          int resource_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;
192 
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);
197 
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());
202 
203     XcursorImage* x_image = SkBitmapToXcursorImage(&cropped, hotpoint);
204 
205     x_image->delay = frame_delay_ms;
206     x_images->images[frame] = x_image;
207   }
208 
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.
212 }
213 
UnloadAll()214 void CursorLoaderX11::UnloadAll() {
215   for (ImageCursorMap::const_iterator it = cursors_.begin();
216        it != cursors_.end(); ++it)
217     UnrefCustomXCursor(it->second);
218 
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);
224   }
225 }
226 
SetPlatformCursor(gfx::NativeCursor * cursor)227 void CursorLoaderX11::SetPlatformCursor(gfx::NativeCursor* cursor) {
228   DCHECK(cursor);
229 
230   ::Cursor xcursor;
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));
239   } else {
240     xcursor = ImageCursorFromNative(kCursorPointer);
241   }
242 
243   cursor->SetPlatformCursor(xcursor);
244 }
245 
GetXcursorImageForTest(int id)246 const XcursorImage* CursorLoaderX11::GetXcursorImageForTest(int id) {
247   return test::GetCachedXcursorImage(cursors_[id]);
248 }
249 
IsImageCursor(gfx::NativeCursor native_cursor)250 bool CursorLoaderX11::IsImageCursor(gfx::NativeCursor native_cursor) {
251   int type = native_cursor.native_type();
252   return cursors_.count(type) || animated_cursors_.count(type);
253 }
254 
ImageCursorFromNative(gfx::NativeCursor native_cursor)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;
260 
261   ImageCursorMap::iterator find = cursors_.find(type);
262   if (find != cursors_.end())
263     return cursors_[type];
264   return GetXCursor(CursorShapeFromNative(native_cursor));
265 }
266 
267 }  // namespace ui
268