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 "skia/ext/image_operations.h"
13#include "ui/base/cursor/cursor.h"
14#include "ui/base/cursor/cursor_util.h"
15#include "ui/base/x/x11_util.h"
16#include "ui/gfx/image/image.h"
17#include "ui/gfx/point_conversions.h"
18#include "ui/gfx/size_conversions.h"
19#include "ui/gfx/skbitmap_operations.h"
20#include "ui/gfx/skia_util.h"
21
22namespace {
23
24// Returns X font cursor shape from an Aura cursor.
25int CursorShapeFromNative(const gfx::NativeCursor& native_cursor) {
26  switch (native_cursor.native_type()) {
27    case ui::kCursorMiddlePanning:
28      return XC_fleur;
29    case ui::kCursorEastPanning:
30      return XC_sb_right_arrow;
31    case ui::kCursorNorthPanning:
32      return XC_sb_up_arrow;
33    case ui::kCursorNorthEastPanning:
34      return XC_top_right_corner;
35    case ui::kCursorNorthWestPanning:
36      return XC_top_left_corner;
37    case ui::kCursorSouthPanning:
38      return XC_sb_down_arrow;
39    case ui::kCursorSouthEastPanning:
40      return XC_bottom_right_corner;
41    case ui::kCursorSouthWestPanning:
42      return XC_bottom_left_corner;
43    case ui::kCursorWestPanning:
44      return XC_sb_left_arrow;
45    case ui::kCursorNone:
46    case ui::kCursorGrab:
47    case ui::kCursorGrabbing:
48      // TODO(jamescook): Need cursors for these.  crbug.com/111650
49      return XC_left_ptr;
50
51#if defined(OS_CHROMEOS)
52    case ui::kCursorNull:
53    case ui::kCursorPointer:
54    case ui::kCursorNoDrop:
55    case ui::kCursorNotAllowed:
56    case ui::kCursorCopy:
57    case ui::kCursorMove:
58    case ui::kCursorEastResize:
59    case ui::kCursorNorthResize:
60    case ui::kCursorSouthResize:
61    case ui::kCursorWestResize:
62    case ui::kCursorNorthEastResize:
63    case ui::kCursorNorthWestResize:
64    case ui::kCursorSouthWestResize:
65    case ui::kCursorSouthEastResize:
66    case ui::kCursorIBeam:
67    case ui::kCursorAlias:
68    case ui::kCursorCell:
69    case ui::kCursorContextMenu:
70    case ui::kCursorCross:
71    case ui::kCursorHelp:
72    case ui::kCursorWait:
73    case ui::kCursorNorthSouthResize:
74    case ui::kCursorEastWestResize:
75    case ui::kCursorNorthEastSouthWestResize:
76    case ui::kCursorNorthWestSouthEastResize:
77    case ui::kCursorProgress:
78    case ui::kCursorColumnResize:
79    case ui::kCursorRowResize:
80    case ui::kCursorVerticalText:
81    case ui::kCursorZoomIn:
82    case ui::kCursorZoomOut:
83    case ui::kCursorHand:
84      // In some environments, the image assets are not set (e.g. in
85      // content-browsertests, content-shell etc.).
86      return XC_left_ptr;
87#else  // defined(OS_CHROMEOS)
88    case ui::kCursorNull:
89      return XC_left_ptr;
90    case ui::kCursorPointer:
91      return XC_left_ptr;
92    case ui::kCursorMove:
93      return XC_fleur;
94    case ui::kCursorCross:
95      return XC_crosshair;
96    case ui::kCursorHand:
97      return XC_hand2;
98    case ui::kCursorIBeam:
99      return XC_xterm;
100    case ui::kCursorProgress:
101    case ui::kCursorWait:
102      return XC_watch;
103    case ui::kCursorHelp:
104      return XC_question_arrow;
105    case ui::kCursorEastResize:
106      return XC_right_side;
107    case ui::kCursorNorthResize:
108      return XC_top_side;
109    case ui::kCursorNorthEastResize:
110      return XC_top_right_corner;
111    case ui::kCursorNorthWestResize:
112      return XC_top_left_corner;
113    case ui::kCursorSouthResize:
114      return XC_bottom_side;
115    case ui::kCursorSouthEastResize:
116      return XC_bottom_right_corner;
117    case ui::kCursorSouthWestResize:
118      return XC_bottom_left_corner;
119    case ui::kCursorWestResize:
120      return XC_left_side;
121    case ui::kCursorNorthSouthResize:
122      return XC_sb_v_double_arrow;
123    case ui::kCursorEastWestResize:
124      return XC_sb_h_double_arrow;
125    case ui::kCursorColumnResize:
126      return XC_sb_h_double_arrow;
127    case ui::kCursorRowResize:
128      return XC_sb_v_double_arrow;
129#endif  // defined(OS_CHROMEOS)
130    case ui::kCursorCustom:
131      NOTREACHED();
132      return XC_left_ptr;
133  }
134  NOTREACHED() << "Case not handled for " << native_cursor.native_type();
135  return XC_left_ptr;
136}
137
138}  // namespace
139
140namespace ui {
141
142CursorLoader* CursorLoader::Create() {
143  return new CursorLoaderX11;
144}
145
146CursorLoaderX11::CursorLoaderX11()
147    : invisible_cursor_(CreateInvisibleCursor(), gfx::GetXDisplay()) {
148}
149
150CursorLoaderX11::~CursorLoaderX11() {
151  UnloadAll();
152}
153
154void CursorLoaderX11::LoadImageCursor(int id,
155                                      int resource_id,
156                                      const gfx::Point& hot) {
157  SkBitmap bitmap;
158  gfx::Point hotspot = hot;
159
160  GetImageCursorBitmap(resource_id, scale(), rotation(), &hotspot, &bitmap);
161  XcursorImage* x_image = SkBitmapToXcursorImage(&bitmap, hotspot);
162  cursors_[id] = CreateReffedCustomXCursor(x_image);
163}
164
165void CursorLoaderX11::LoadAnimatedCursor(int id,
166                                         int resource_id,
167                                         const gfx::Point& hot,
168                                         int frame_delay_ms) {
169  std::vector<SkBitmap> bitmaps;
170  gfx::Point hotspot = hot;
171
172  GetAnimatedCursorBitmaps(
173      resource_id, scale(), rotation(), &hotspot, &bitmaps);
174
175  XcursorImages* x_images = XcursorImagesCreate(bitmaps.size());
176  x_images->nimage = bitmaps.size();
177
178  for (unsigned int frame = 0; frame < bitmaps.size(); ++frame) {
179    XcursorImage* x_image = SkBitmapToXcursorImage(&bitmaps[frame], hotspot);
180    x_image->delay = frame_delay_ms;
181    x_images->images[frame] = x_image;
182  }
183
184  animated_cursors_[id] = std::make_pair(
185      XcursorImagesLoadCursor(gfx::GetXDisplay(), x_images), x_images);
186}
187
188void CursorLoaderX11::UnloadAll() {
189  for (ImageCursorMap::const_iterator it = cursors_.begin();
190       it != cursors_.end(); ++it)
191    UnrefCustomXCursor(it->second);
192
193  // Free animated cursors and images.
194  for (AnimatedCursorMap::iterator it = animated_cursors_.begin();
195       it != animated_cursors_.end(); ++it) {
196    XcursorImagesDestroy(it->second.second);  // also frees individual frames.
197    XFreeCursor(gfx::GetXDisplay(), it->second.first);
198  }
199}
200
201void CursorLoaderX11::SetPlatformCursor(gfx::NativeCursor* cursor) {
202  DCHECK(cursor);
203
204  ::Cursor xcursor;
205  if (IsImageCursor(*cursor))
206    xcursor = ImageCursorFromNative(*cursor);
207  else if (*cursor == kCursorNone)
208    xcursor =  invisible_cursor_.get();
209  else if (*cursor == kCursorCustom)
210    xcursor = cursor->platform();
211  else if (scale() == 1.0f && rotation() == gfx::Display::ROTATE_0) {
212    xcursor = GetXCursor(CursorShapeFromNative(*cursor));
213  } else {
214    xcursor = ImageCursorFromNative(kCursorPointer);
215  }
216
217  cursor->SetPlatformCursor(xcursor);
218}
219
220const XcursorImage* CursorLoaderX11::GetXcursorImageForTest(int id) {
221  return test::GetCachedXcursorImage(cursors_[id]);
222}
223
224bool CursorLoaderX11::IsImageCursor(gfx::NativeCursor native_cursor) {
225  int type = native_cursor.native_type();
226  return cursors_.count(type) || animated_cursors_.count(type);
227}
228
229::Cursor CursorLoaderX11::ImageCursorFromNative(
230    gfx::NativeCursor native_cursor) {
231  int type = native_cursor.native_type();
232  if (animated_cursors_.count(type))
233    return animated_cursors_[type].first;
234
235  ImageCursorMap::iterator find = cursors_.find(type);
236  if (find != cursors_.end())
237    return cursors_[type];
238  return GetXCursor(CursorShapeFromNative(native_cursor));
239}
240
241}  // namespace ui
242