1/* GDK - The GIMP Drawing Kit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/*
19 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 */
24
25#include "config.h"
26
27#define GDK_PIXBUF_ENABLE_BACKEND
28
29#include <string.h>
30
31#include "gdkprivate-wayland.h"
32#include "gdkcursorprivate.h"
33#include "gdkdisplay-wayland.h"
34#include "gdkwayland.h"
35#include <gdk-pixbuf/gdk-pixbuf.h>
36#include "cursor/wayland-cursor.h"
37
38static void
39gdk_wayland_cursor_remove_from_cache (gpointer data, GObject *cursor)
40{
41 GdkWaylandDisplay *display = data;
42
43 g_hash_table_remove (hash_table: display->cursor_surface_cache, key: cursor);
44}
45
46void
47_gdk_wayland_display_init_cursors (GdkWaylandDisplay *display)
48{
49 display->cursor_surface_cache = g_hash_table_new_full (hash_func: gdk_cursor_hash, key_equal_func: gdk_cursor_equal, NULL, value_destroy_func: (GDestroyNotify) cairo_surface_destroy);
50}
51
52void
53_gdk_wayland_display_finalize_cursors (GdkWaylandDisplay *display)
54{
55 GHashTableIter iter;
56 gpointer cursor;
57
58 g_hash_table_iter_init (iter: &iter, hash_table: display->cursor_surface_cache);
59 while (g_hash_table_iter_next (iter: &iter, key: &cursor, NULL))
60 g_object_weak_unref (G_OBJECT (cursor), notify: gdk_wayland_cursor_remove_from_cache, data: display);
61 g_hash_table_destroy (hash_table: display->cursor_surface_cache);
62}
63
64static const struct {
65 const char *css_name, *traditional_name;
66} name_map[] = {
67 { "default", "left_ptr" },
68 { "help", "question_arrow" },
69 { "context-menu", "left_ptr" },
70 { "pointer", "hand" },
71 { "progress", "left_ptr_watch" },
72 { "wait", "watch" },
73 { "cell", "crosshair" },
74 { "crosshair", "cross" },
75 { "text", "xterm" },
76 { "vertical-text","xterm" },
77 { "alias", "dnd-link" },
78 { "copy", "dnd-copy" },
79 { "move", "dnd-move" },
80 { "no-drop", "dnd-none" },
81 { "dnd-ask", "dnd-copy" }, /* not CSS, but we want to guarantee it anyway */
82 { "not-allowed", "crossed_circle" },
83 { "grab", "hand2" },
84 { "grabbing", "hand2" },
85 { "all-scroll", "left_ptr" },
86 { "col-resize", "h_double_arrow" },
87 { "row-resize", "v_double_arrow" },
88 { "n-resize", "top_side" },
89 { "e-resize", "right_side" },
90 { "s-resize", "bottom_side" },
91 { "w-resize", "left_side" },
92 { "ne-resize", "top_right_corner" },
93 { "nw-resize", "top_left_corner" },
94 { "se-resize", "bottom_right_corner" },
95 { "sw-resize", "bottom_left_corner" },
96 { "ew-resize", "h_double_arrow" },
97 { "ns-resize", "v_double_arrow" },
98 { "nesw-resize", "fd_double_arrow" },
99 { "nwse-resize", "bd_double_arrow" },
100 { "zoom-in", "left_ptr" },
101 { "zoom-out", "left_ptr" }
102};
103
104static const char *
105name_fallback (const char *name)
106{
107 int i;
108
109 for (i = 0; i < G_N_ELEMENTS (name_map); i++)
110 {
111 if (g_str_equal (v1: name_map[i].css_name, v2: name))
112 return name_map[i].traditional_name;
113 }
114
115 return NULL;
116}
117
118static struct wl_cursor *
119gdk_wayland_cursor_load_for_name (GdkWaylandDisplay *display_wayland,
120 struct wl_cursor_theme *theme,
121 int scale,
122 const char *name)
123{
124 struct wl_cursor *c;
125
126 c = wl_cursor_theme_get_cursor (theme, name, scale);
127 if (!c)
128 {
129 const char *fallback;
130
131 fallback = name_fallback (name);
132 if (fallback)
133 {
134 c = wl_cursor_theme_get_cursor (theme, name: fallback, scale);
135 }
136 }
137
138 return c;
139}
140
141static void
142buffer_release_callback (void *_data,
143 struct wl_buffer *wl_buffer)
144{
145 cairo_surface_t *cairo_surface = _data;
146
147 cairo_surface_destroy (surface: cairo_surface);
148}
149
150static const struct wl_buffer_listener buffer_listener = {
151 buffer_release_callback
152};
153
154struct wl_buffer *
155_gdk_wayland_cursor_get_buffer (GdkWaylandDisplay *display,
156 GdkCursor *cursor,
157 guint desired_scale,
158 guint image_index,
159 int *hotspot_x,
160 int *hotspot_y,
161 int *width,
162 int *height,
163 int *scale)
164{
165 GdkTexture *texture;
166
167 if (gdk_cursor_get_name (cursor))
168 {
169 struct wl_cursor *c;
170
171 if (g_str_equal (v1: gdk_cursor_get_name (cursor), v2: "none"))
172 goto none;
173
174 c = gdk_wayland_cursor_load_for_name (display_wayland: display,
175 theme: _gdk_wayland_display_get_cursor_theme (display_wayland: display),
176 scale: desired_scale,
177 name: gdk_cursor_get_name (cursor));
178 if (c)
179 {
180 struct wl_cursor_image *image;
181 int cursor_scale;
182
183 if (image_index >= c->image_count)
184 {
185 g_warning (G_STRLOC " out of bounds cursor image [%d / %d]",
186 image_index,
187 c->image_count - 1);
188 image_index = 0;
189 }
190
191 image = c->images[image_index];
192
193 cursor_scale = desired_scale;
194 if ((image->width % cursor_scale != 0) ||
195 (image->height % cursor_scale != 0))
196 {
197 g_warning (G_STRLOC " cursor image size (%dx%d) not an integer"
198 "multiple of scale (%d)", image->width, image->height,
199 cursor_scale);
200 cursor_scale = 1;
201 }
202
203 *hotspot_x = image->hotspot_x / cursor_scale;
204 *hotspot_y = image->hotspot_y / cursor_scale;
205
206 *width = image->width / cursor_scale;
207 *height = image->height / cursor_scale;
208 *scale = cursor_scale;
209
210 return wl_cursor_image_get_buffer (image);
211 }
212 }
213 else
214 {
215 cairo_surface_t *surface;
216 struct wl_buffer *buffer;
217
218 texture = g_object_ref (gdk_cursor_get_texture (cursor));
219
220from_texture:
221 surface = g_hash_table_lookup (hash_table: display->cursor_surface_cache, key: cursor);
222 if (surface == NULL)
223 {
224 surface = _gdk_wayland_display_create_shm_surface (display,
225 width: gdk_texture_get_width (texture),
226 height: gdk_texture_get_height (texture),
227 scale: 1);
228
229 gdk_texture_download (texture,
230 data: cairo_image_surface_get_data (surface),
231 stride: cairo_image_surface_get_stride (surface));
232 cairo_surface_mark_dirty (surface);
233
234 g_object_weak_ref (G_OBJECT (cursor), notify: gdk_wayland_cursor_remove_from_cache, data: display);
235 g_hash_table_insert (hash_table: display->cursor_surface_cache, key: cursor, value: surface);
236 }
237
238 *hotspot_x = gdk_cursor_get_hotspot_x (cursor);
239 *hotspot_y = gdk_cursor_get_hotspot_y (cursor);
240 *width = gdk_texture_get_width (texture);
241 *height = gdk_texture_get_height (texture);
242 *scale = 1;
243
244 cairo_surface_reference (surface);
245 buffer = _gdk_wayland_shm_surface_get_wl_buffer (surface);
246 wl_buffer_add_listener (wl_buffer: buffer, listener: &buffer_listener, data: surface);
247
248 g_object_unref (object: texture);
249
250 return buffer;
251 }
252
253 if (gdk_cursor_get_fallback (cursor))
254 return _gdk_wayland_cursor_get_buffer (display,
255 cursor: gdk_cursor_get_fallback (cursor),
256 desired_scale,
257 image_index,
258 hotspot_x, hotspot_y,
259 width, height,
260 scale);
261 else
262 {
263 texture = gdk_texture_new_from_resource (resource_path: "/org/gtk/libgdk/cursor/default");
264 goto from_texture;
265 }
266
267none:
268 *hotspot_x = 0;
269 *hotspot_y = 0;
270 *width = 0;
271 *height = 0;
272 *scale = 1;
273
274 return NULL;
275}
276
277guint
278_gdk_wayland_cursor_get_next_image_index (GdkWaylandDisplay *display,
279 GdkCursor *cursor,
280 guint scale,
281 guint current_image_index,
282 guint *next_image_delay)
283{
284 struct wl_cursor *c;
285
286 if (!gdk_cursor_get_name (cursor) ||
287 g_str_equal (v1: gdk_cursor_get_name (cursor), v2: "none"))
288 {
289 *next_image_delay = 0;
290 return current_image_index;
291 }
292
293 c = gdk_wayland_cursor_load_for_name (display_wayland: display,
294 theme: _gdk_wayland_display_get_cursor_theme (display_wayland: display),
295 scale,
296 name: gdk_cursor_get_name (cursor));
297
298 if (c)
299 {
300 if (current_image_index >= c->image_count)
301 {
302 g_warning (G_STRLOC " out of bounds cursor image [%d / %d]",
303 current_image_index, c->image_count - 1);
304 current_image_index = 0;
305 }
306
307 if (c->image_count == 1)
308 {
309 *next_image_delay = 0;
310 return current_image_index;
311 }
312 else
313 {
314 *next_image_delay = c->images[current_image_index]->delay;
315 return (current_image_index + 1) % c->image_count;
316 }
317 }
318
319 if (gdk_cursor_get_fallback (cursor))
320 return _gdk_wayland_cursor_get_next_image_index (display,
321 cursor: gdk_cursor_get_fallback (cursor),
322 scale,
323 current_image_index,
324 next_image_delay);
325
326 *next_image_delay = 0;
327 return current_image_index;
328}
329

source code of gtk/gdk/wayland/gdkcursor-wayland.c