1 // Copyright (c) 2011 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 "webkit/glue/webcursor.h"
6
7 #include <gdk/gdk.h>
8 #include <gtk/gtk.h>
9
10 #include "base/logging.h"
11 #include "third_party/WebKit/Source/WebKit/chromium/public/WebCursorInfo.h"
12 #include "ui/gfx/gtk_util.h"
13
14 using WebKit::WebCursorInfo;
15
16 namespace {
17
18 // webcursor_gtk_data.h is taken directly from WebKit's CursorGtk.h.
19 #include "webkit/glue/webcursor_gtk_data.h"
20
21 // This helper function is taken directly from WebKit's CursorGtk.cpp.
22 // It attempts to create a custom cursor from the data inlined in
23 // webcursor_gtk_data.h.
GetInlineCustomCursor(CustomCursorType type)24 GdkCursor* GetInlineCustomCursor(CustomCursorType type) {
25 static GdkCursor* CustomCursorsGdk[G_N_ELEMENTS(CustomCursors)];
26 GdkCursor* cursor = CustomCursorsGdk[type];
27 if (cursor)
28 return cursor;
29 const CustomCursor& custom = CustomCursors[type];
30 cursor = gdk_cursor_new_from_name(gdk_display_get_default(), custom.name);
31 if (!cursor) {
32 const GdkColor fg = { 0, 0, 0, 0 };
33 const GdkColor bg = { 65535, 65535, 65535, 65535 };
34 GdkPixmap* source = gdk_bitmap_create_from_data(NULL, custom.bits,
35 32, 32);
36 GdkPixmap* mask = gdk_bitmap_create_from_data(NULL, custom.mask_bits,
37 32, 32);
38 cursor = gdk_cursor_new_from_pixmap(source, mask, &fg, &bg,
39 custom.hot_x, custom.hot_y);
40 g_object_unref(source);
41 g_object_unref(mask);
42 }
43 CustomCursorsGdk[type] = cursor;
44 return cursor;
45 }
46
47 // For GTK 2.16 and beyond, GDK_BLANK_CURSOR is available. Before, we have to
48 // use a custom cursor.
49 #if !GTK_CHECK_VERSION(2, 16, 0)
50 // Get/create a custom cursor which is invisible.
GetInvisibleCustomCursor()51 GdkCursor* GetInvisibleCustomCursor() {
52 static GdkCursor* cursor = NULL;
53 if (cursor)
54 return cursor;
55 const char bits[] = { 0 };
56 const GdkColor color = { 0, 0, 0, 0 };
57 GdkPixmap* bitmap = gdk_bitmap_create_from_data(NULL, bits, 1, 1);
58 cursor = gdk_cursor_new_from_pixmap(bitmap, bitmap, &color, &color, 0, 0);
59 g_object_unref(bitmap);
60 return cursor;
61 }
62 #endif
63
64 } // end anonymous namespace
65
GetCursorType() const66 int WebCursor::GetCursorType() const {
67 // http://library.gnome.org/devel/gdk/2.12/gdk-Cursors.html has images
68 // of the default X theme, but beware that the user's cursor theme can
69 // change everything.
70 switch (type_) {
71 case WebCursorInfo::TypePointer:
72 return GDK_LAST_CURSOR;
73 case WebCursorInfo::TypeCross:
74 return GDK_CROSS;
75 case WebCursorInfo::TypeHand:
76 return GDK_HAND2;
77 case WebCursorInfo::TypeIBeam:
78 return GDK_XTERM;
79 case WebCursorInfo::TypeWait:
80 return GDK_WATCH;
81 case WebCursorInfo::TypeHelp:
82 return GDK_QUESTION_ARROW;
83 case WebCursorInfo::TypeEastResize:
84 return GDK_RIGHT_SIDE;
85 case WebCursorInfo::TypeNorthResize:
86 return GDK_TOP_SIDE;
87 case WebCursorInfo::TypeNorthEastResize:
88 return GDK_TOP_RIGHT_CORNER;
89 case WebCursorInfo::TypeNorthWestResize:
90 return GDK_TOP_LEFT_CORNER;
91 case WebCursorInfo::TypeSouthResize:
92 return GDK_BOTTOM_SIDE;
93 case WebCursorInfo::TypeSouthEastResize:
94 return GDK_BOTTOM_RIGHT_CORNER;
95 case WebCursorInfo::TypeSouthWestResize:
96 return GDK_BOTTOM_LEFT_CORNER;
97 case WebCursorInfo::TypeWestResize:
98 return GDK_LEFT_SIDE;
99 case WebCursorInfo::TypeNorthSouthResize:
100 return GDK_SB_V_DOUBLE_ARROW;
101 case WebCursorInfo::TypeEastWestResize:
102 return GDK_SB_H_DOUBLE_ARROW;
103 case WebCursorInfo::TypeNorthEastSouthWestResize:
104 case WebCursorInfo::TypeNorthWestSouthEastResize:
105 // There isn't really a useful cursor available for these.
106 NOTIMPLEMENTED(); return GDK_LAST_CURSOR;
107 case WebCursorInfo::TypeColumnResize:
108 return GDK_SB_H_DOUBLE_ARROW; // TODO(evanm): is this correct?
109 case WebCursorInfo::TypeRowResize:
110 return GDK_SB_V_DOUBLE_ARROW; // TODO(evanm): is this correct?
111 case WebCursorInfo::TypeMiddlePanning:
112 return GDK_FLEUR;
113 case WebCursorInfo::TypeEastPanning:
114 return GDK_SB_RIGHT_ARROW;
115 case WebCursorInfo::TypeNorthPanning:
116 return GDK_SB_UP_ARROW;
117 case WebCursorInfo::TypeNorthEastPanning:
118 return GDK_TOP_RIGHT_CORNER;
119 case WebCursorInfo::TypeNorthWestPanning:
120 return GDK_TOP_LEFT_CORNER;
121 case WebCursorInfo::TypeSouthPanning:
122 return GDK_SB_DOWN_ARROW;
123 case WebCursorInfo::TypeSouthEastPanning:
124 return GDK_BOTTOM_RIGHT_CORNER;
125 case WebCursorInfo::TypeSouthWestPanning:
126 return GDK_BOTTOM_LEFT_CORNER;
127 case WebCursorInfo::TypeWestPanning:
128 return GDK_SB_LEFT_ARROW;
129 case WebCursorInfo::TypeMove:
130 return GDK_FLEUR;
131 case WebCursorInfo::TypeVerticalText:
132 NOTIMPLEMENTED(); return GDK_LAST_CURSOR;
133 case WebCursorInfo::TypeCell:
134 NOTIMPLEMENTED(); return GDK_LAST_CURSOR;
135 case WebCursorInfo::TypeContextMenu:
136 NOTIMPLEMENTED(); return GDK_LAST_CURSOR;
137 case WebCursorInfo::TypeAlias:
138 NOTIMPLEMENTED(); return GDK_LAST_CURSOR;
139 case WebCursorInfo::TypeProgress:
140 return GDK_WATCH;
141 case WebCursorInfo::TypeNoDrop:
142 NOTIMPLEMENTED(); return GDK_LAST_CURSOR;
143 case WebCursorInfo::TypeCopy:
144 NOTIMPLEMENTED(); return GDK_LAST_CURSOR;
145 case WebCursorInfo::TypeNone:
146 // See comment above |GetInvisibleCustomCursor()|.
147 #if !GTK_CHECK_VERSION(2, 16, 0)
148 return GDK_CURSOR_IS_PIXMAP;
149 #else
150 return GDK_BLANK_CURSOR;
151 #endif
152 case WebCursorInfo::TypeNotAllowed:
153 NOTIMPLEMENTED(); return GDK_LAST_CURSOR;
154 case WebCursorInfo::TypeZoomIn:
155 case WebCursorInfo::TypeZoomOut:
156 case WebCursorInfo::TypeGrab:
157 case WebCursorInfo::TypeGrabbing:
158 case WebCursorInfo::TypeCustom:
159 return GDK_CURSOR_IS_PIXMAP;
160 }
161 NOTREACHED();
162 return GDK_LAST_CURSOR;
163 }
164
GetNativeCursor()165 gfx::NativeCursor WebCursor::GetNativeCursor() {
166 int type = GetCursorType();
167 if (type == GDK_CURSOR_IS_PIXMAP)
168 return GetCustomCursor();
169 return gfx::GetCursor(type);
170 }
171
GetCustomCursor()172 GdkCursor* WebCursor::GetCustomCursor() {
173 switch (type_) {
174 // See comment above |GetInvisibleCustomCursor()|.
175 #if !GTK_CHECK_VERSION(2, 16, 0)
176 case WebCursorInfo::TypeNone:
177 return GetInvisibleCustomCursor();
178 #endif
179 case WebCursorInfo::TypeZoomIn:
180 return GetInlineCustomCursor(CustomCursorZoomIn);
181 case WebCursorInfo::TypeZoomOut:
182 return GetInlineCustomCursor(CustomCursorZoomOut);
183 case WebCursorInfo::TypeGrab:
184 return GetInlineCustomCursor(CustomCursorGrab);
185 case WebCursorInfo::TypeGrabbing:
186 return GetInlineCustomCursor(CustomCursorGrabbing);
187 }
188
189 if (type_ != WebCursorInfo::TypeCustom) {
190 NOTREACHED();
191 return NULL;
192 }
193
194 SkBitmap bitmap;
195 bitmap.setConfig(SkBitmap::kARGB_8888_Config,
196 custom_size_.width(), custom_size_.height());
197 bitmap.allocPixels();
198 memcpy(bitmap.getAddr32(0, 0), custom_data_.data(), custom_data_.size());
199
200 GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&bitmap);
201 GdkCursor* cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),
202 pixbuf,
203 hotspot_.x(),
204 hotspot_.y());
205
206 gdk_pixbuf_unref(pixbuf);
207
208 if (unref_)
209 gdk_cursor_unref(unref_);
210 unref_ = cursor;
211 return cursor;
212 }
213
InitPlatformData()214 void WebCursor::InitPlatformData() {
215 unref_ = NULL;
216 return;
217 }
218
SerializePlatformData(Pickle * pickle) const219 bool WebCursor::SerializePlatformData(Pickle* pickle) const {
220 return true;
221 }
222
DeserializePlatformData(const Pickle * pickle,void ** iter)223 bool WebCursor::DeserializePlatformData(const Pickle* pickle, void** iter) {
224 return true;
225 }
226
IsPlatformDataEqual(const WebCursor & other) const227 bool WebCursor::IsPlatformDataEqual(const WebCursor& other) const {
228 return true;
229 }
230
CleanupPlatformData()231 void WebCursor::CleanupPlatformData() {
232 if (unref_) {
233 gdk_cursor_unref(unref_);
234 unref_ = NULL;
235 }
236 return;
237 }
238
CopyPlatformData(const WebCursor & other)239 void WebCursor::CopyPlatformData(const WebCursor& other) {
240 if (other.unref_)
241 unref_ = gdk_cursor_ref(other.unref_);
242 return;
243 }
244