- add sources.
[platform/framework/web/crosswalk.git] / src / ui / base / cursor / cursor_loader_x11.cc
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/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"
23
24 namespace {
25
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:
30       return XC_fleur;
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;
47     case ui::kCursorNone:
48     case ui::kCursorGrab:
49     case ui::kCursorGrabbing:
50       // TODO(jamescook): Need cursors for these.  crbug.com/111650
51       return XC_left_ptr;
52
53 #if defined(OS_CHROMEOS)
54     case ui::kCursorNull:
55     case ui::kCursorPointer:
56     case ui::kCursorNoDrop:
57     case ui::kCursorNotAllowed:
58     case ui::kCursorCopy:
59     case ui::kCursorMove:
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:
70     case ui::kCursorCell:
71     case ui::kCursorContextMenu:
72     case ui::kCursorCross:
73     case ui::kCursorHelp:
74     case ui::kCursorWait:
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:
85     case ui::kCursorHand:
86       // In some environments, the image assets are not set (e.g. in
87       // content-browsertests, content-shell etc.).
88       return XC_left_ptr;
89 #else  // defined(OS_CHROMEOS)
90     case ui::kCursorNull:
91       return XC_left_ptr;
92     case ui::kCursorPointer:
93       return XC_left_ptr;
94     case ui::kCursorMove:
95       return XC_fleur;
96     case ui::kCursorCross:
97       return XC_crosshair;
98     case ui::kCursorHand:
99       return XC_hand2;
100     case ui::kCursorIBeam:
101       return XC_xterm;
102     case ui::kCursorProgress:
103     case ui::kCursorWait:
104       return XC_watch;
105     case ui::kCursorHelp:
106       return XC_question_arrow;
107     case ui::kCursorEastResize:
108       return XC_right_side;
109     case ui::kCursorNorthResize:
110       return XC_top_side;
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:
122       return XC_left_side;
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:
133       NOTREACHED();
134       return XC_left_ptr;
135   }
136   NOTREACHED() << "Case not handled for " << native_cursor.native_type();
137   return XC_left_ptr;
138 }
139
140 }  // namespace
141
142 namespace ui {
143
144 CursorLoader* CursorLoader::Create() {
145   return new CursorLoaderX11;
146 }
147
148 CursorLoaderX11::CursorLoaderX11()
149     : invisible_cursor_(CreateInvisibleCursor(), gfx::GetXDisplay()) {
150 }
151
152 CursorLoaderX11::~CursorLoaderX11() {
153   UnloadAll();
154 }
155
156 void CursorLoaderX11::LoadImageCursor(int id,
157                                       int resource_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);
167
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.
171 }
172
173 void CursorLoaderX11::LoadAnimatedCursor(int id,
174                                          int resource_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;
190
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);
195
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());
200
201     XcursorImage* x_image = SkBitmapToXcursorImage(&cropped, hotpoint);
202
203     x_image->delay = frame_delay_ms;
204     x_images->images[frame] = x_image;
205   }
206
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.
210 }
211
212 void CursorLoaderX11::UnloadAll() {
213   for (ImageCursorMap::const_iterator it = cursors_.begin();
214        it != cursors_.end(); ++it)
215     UnrefCustomXCursor(it->second);
216
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);
222   }
223 }
224
225 void CursorLoaderX11::SetPlatformCursor(gfx::NativeCursor* cursor) {
226   DCHECK(cursor);
227
228   ::Cursor xcursor;
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));
238   } else {
239     xcursor = ImageCursorFromNative(kCursorPointer);
240   }
241
242   cursor->SetPlatformCursor(xcursor);
243 }
244
245 bool CursorLoaderX11::IsImageCursor(gfx::NativeCursor native_cursor) {
246   int type = native_cursor.native_type();
247   return cursors_.count(type) || animated_cursors_.count(type);
248 }
249
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;
255
256   ImageCursorMap::iterator find = cursors_.find(type);
257   if (find != cursors_.end())
258     return cursors_[type];
259   return GetXCursor(CursorShapeFromNative(native_cursor));
260 }
261
262 void ScaleAndRotateCursorBitmapAndHotpoint(float scale,
263                                            gfx::Display::Rotation rotation,
264                                            SkBitmap* bitmap,
265                                            gfx::Point* hotpoint) {
266   switch (rotation) {
267     case gfx::Display::ROTATE_0:
268       break;
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);
273       break;
274     case gfx::Display::ROTATE_180:
275       hotpoint->SetPoint(
276           bitmap->width() - hotpoint->x(), bitmap->height() - hotpoint->y());
277       *bitmap = SkBitmapOperations::Rotate(
278           *bitmap, SkBitmapOperations::ROTATION_180_CW);
279       break;
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);
284       break;
285   }
286
287   if (scale < FLT_EPSILON) {
288     NOTREACHED() << "Scale must be larger than 0.";
289     scale = 1.0f;
290   }
291
292   if (scale == 1.0f)
293     return;
294
295   gfx::Size scaled_size = gfx::ToFlooredSize(
296       gfx::ScaleSize(gfx::Size(bitmap->width(), bitmap->height()), scale));
297
298   *bitmap = skia::ImageOperations::Resize(
299       *bitmap,
300       skia::ImageOperations::RESIZE_BETTER,
301       scaled_size.width(),
302       scaled_size.height());
303   *hotpoint = gfx::ToFlooredPoint(gfx::ScalePoint(*hotpoint, scale));
304 }
305
306 }  // namespace ui