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
37#include <wayland-cursor.h>
38
39#define GDK_TYPE_WAYLAND_CURSOR (_gdk_wayland_cursor_get_type ())
40#define GDK_WAYLAND_CURSOR(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_WAYLAND_CURSOR, GdkWaylandCursor))
41#define GDK_WAYLAND_CURSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_WAYLAND_CURSOR, GdkWaylandCursorClass))
42#define GDK_IS_WAYLAND_CURSOR(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_WAYLAND_CURSOR))
43#define GDK_IS_WAYLAND_CURSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_WAYLAND_CURSOR))
44#define GDK_WAYLAND_CURSOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_WAYLAND_CURSOR, GdkWaylandCursorClass))
45
46typedef struct _GdkWaylandCursor GdkWaylandCursor;
47typedef struct _GdkWaylandCursorClass GdkWaylandCursorClass;
48
49struct _GdkWaylandCursor
50{
51 GdkCursor cursor;
52 gchar *name;
53
54 struct
55 {
56 int hotspot_x, hotspot_y;
57 int width, height, scale;
58 cairo_surface_t *cairo_surface;
59 } surface;
60
61 struct wl_cursor *wl_cursor;
62 int scale;
63};
64
65struct _GdkWaylandCursorClass
66{
67 GdkCursorClass cursor_class;
68};
69
70GType _gdk_wayland_cursor_get_type (void);
71
72G_DEFINE_TYPE (GdkWaylandCursor, _gdk_wayland_cursor, GDK_TYPE_CURSOR)
73
74void
75_gdk_wayland_display_init_cursors (GdkWaylandDisplay *display)
76{
77 display->cursor_cache = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
78}
79
80void
81_gdk_wayland_display_finalize_cursors (GdkWaylandDisplay *display)
82{
83 g_hash_table_destroy (display->cursor_cache);
84}
85
86static const struct {
87 const gchar *css_name, *traditional_name;
88} name_map[] = {
89 { "default", "left_ptr" },
90 { "help", "left_ptr" },
91 { "context-menu", "left_ptr" },
92 { "pointer", "hand" },
93 { "progress", "left_ptr_watch" },
94 { "wait", "watch" },
95 { "cell", "crosshair" },
96 { "crosshair", "cross" },
97 { "text", "xterm" },
98 { "vertical-text","xterm" },
99 { "alias", "dnd-link" },
100 { "copy", "dnd-copy" },
101 { "move", "dnd-move" },
102 { "no-drop", "dnd-none" },
103 { "dnd-ask", "dnd-copy" }, /* not CSS, but we want to guarantee it anyway */
104 { "not-allowed", "crossed_circle" },
105 { "grab", "hand2" },
106 { "grabbing", "hand2" },
107 { "all-scroll", "left_ptr" },
108 { "col-resize", "h_double_arrow" },
109 { "row-resize", "v_double_arrow" },
110 { "n-resize", "top_side" },
111 { "e-resize", "right_side" },
112 { "s-resize", "bottom_side" },
113 { "w-resize", "left_side" },
114 { "ne-resize", "top_right_corner" },
115 { "nw-resize", "top_left_corner" },
116 { "se-resize", "bottom_right_corner" },
117 { "sw-resize", "bottom_left_corner" },
118 { "ew-resize", "h_double_arrow" },
119 { "ns-resize", "v_double_arrow" },
120 { "nesw-resize", "fd_double_arrow" },
121 { "nwse-resize", "bd_double_arrow" },
122 { "zoom-in", "left_ptr" },
123 { "zoom-out", "left_ptr" },
124 { NULL, NULL }
125};
126
127static const gchar *
128name_fallback (const gchar *name)
129{
130 gint i;
131
132 for (i = 0; name_map[i].css_name; i++)
133 {
134 if (g_str_equal (name_map[i].css_name, name))
135 return name_map[i].traditional_name;
136 }
137
138 return NULL;
139}
140
141static gboolean
142_gdk_wayland_cursor_update (GdkWaylandDisplay *display_wayland,
143 GdkWaylandCursor *cursor)
144{
145 struct wl_cursor *c;
146 struct wl_cursor_theme *theme;
147
148 /* Do nothing if this is not a wl_cursor cursor. */
149 if (cursor->name == NULL)
150 return FALSE;
151
152 theme = _gdk_wayland_display_get_scaled_cursor_theme (display_wayland,
153 cursor->scale);
154 c = wl_cursor_theme_get_cursor (theme, cursor->name);
155 if (!c)
156 {
157 const char *fallback;
158
159 fallback = name_fallback (cursor->name);
160 if (fallback)
161 {
162 c = wl_cursor_theme_get_cursor (theme, name_fallback (cursor->name));
163 if (!c)
164 c = wl_cursor_theme_get_cursor (theme, "left_ptr");
165 }
166 }
167
168 if (!c)
169 {
170 g_warning (G_STRLOC ": Unable to load %s from the cursor theme", cursor->name);
171 return FALSE;
172 }
173
174 cursor->wl_cursor = c;
175
176 return TRUE;
177}
178
179void
180_gdk_wayland_display_update_cursors (GdkWaylandDisplay *display)
181{
182 GHashTableIter iter;
183 const char *name;
184 GdkWaylandCursor *cursor;
185
186 g_hash_table_iter_init (&iter, display->cursor_cache);
187
188 while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &cursor))
189 _gdk_wayland_cursor_update (display, cursor);
190}
191
192static void
193gdk_wayland_cursor_finalize (GObject *object)
194{
195 GdkWaylandCursor *cursor = GDK_WAYLAND_CURSOR (object);
196
197 g_free (cursor->name);
198 if (cursor->surface.cairo_surface)
199 cairo_surface_destroy (cursor->surface.cairo_surface);
200
201 G_OBJECT_CLASS (_gdk_wayland_cursor_parent_class)->finalize (object);
202}
203
204static cairo_surface_t *
205gdk_wayland_cursor_get_surface (GdkCursor *cursor,
206 gdouble *x_hot,
207 gdouble *y_hot)
208{
209 return NULL;
210}
211
212struct wl_buffer *
213_gdk_wayland_cursor_get_buffer (GdkCursor *cursor,
214 guint image_index,
215 int *hotspot_x,
216 int *hotspot_y,
217 int *w,
218 int *h,
219 int *scale)
220{
221 GdkWaylandCursor *wayland_cursor = GDK_WAYLAND_CURSOR (cursor);
222
223 if (wayland_cursor->wl_cursor)
224 {
225 struct wl_cursor_image *image;
226
227 if (image_index >= wayland_cursor->wl_cursor->image_count)
228 {
229 g_warning (G_STRLOC " out of bounds cursor image [%d / %d]",
230 image_index,
231 wayland_cursor->wl_cursor->image_count - 1);
232 image_index = 0;
233 }
234
235 image = wayland_cursor->wl_cursor->images[image_index];
236
237 *hotspot_x = image->hotspot_x / wayland_cursor->scale;
238 *hotspot_y = image->hotspot_y / wayland_cursor->scale;
239
240 *w = image->width / wayland_cursor->scale;
241 *h = image->height / wayland_cursor->scale;
242 *scale = wayland_cursor->scale;
243
244 return wl_cursor_image_get_buffer (image);
245 }
246 else if (wayland_cursor->name == NULL) /* From surface */
247 {
248 *hotspot_x =
249 wayland_cursor->surface.hotspot_x / wayland_cursor->surface.scale;
250 *hotspot_y =
251 wayland_cursor->surface.hotspot_y / wayland_cursor->surface.scale;
252
253 *w = wayland_cursor->surface.width / wayland_cursor->surface.scale;
254 *h = wayland_cursor->surface.height / wayland_cursor->surface.scale;
255 *scale = wayland_cursor->surface.scale;
256
257 cairo_surface_reference (wayland_cursor->surface.cairo_surface);
258
259 if (wayland_cursor->surface.cairo_surface)
260 return _gdk_wayland_shm_surface_get_wl_buffer (wayland_cursor->surface.cairo_surface);
261 }
262
263 return NULL;
264}
265
266guint
267_gdk_wayland_cursor_get_next_image_index (GdkCursor *cursor,
268 guint current_image_index,
269 guint *next_image_delay)
270{
271 struct wl_cursor *wl_cursor = GDK_WAYLAND_CURSOR (cursor)->wl_cursor;
272
273 if (wl_cursor && wl_cursor->image_count > 1)
274 {
275 if (current_image_index >= wl_cursor->image_count)
276 {
277 g_warning (G_STRLOC " out of bounds cursor image [%d / %d]",
278 current_image_index, wl_cursor->image_count - 1);
279 current_image_index = 0;
280 }
281
282 /* Return the time to next image */
283 if (next_image_delay)
284 *next_image_delay = wl_cursor->images[current_image_index]->delay;
285
286 return (current_image_index + 1) % wl_cursor->image_count;
287 }
288 else
289 return current_image_index;
290}
291
292void
293_gdk_wayland_cursor_set_scale (GdkCursor *cursor,
294 guint scale)
295{
296 GdkWaylandDisplay *display_wayland =
297 GDK_WAYLAND_DISPLAY (gdk_cursor_get_display (cursor));
298 GdkWaylandCursor *wayland_cursor = GDK_WAYLAND_CURSOR (cursor);
299
300 if (scale > GDK_WAYLAND_MAX_THEME_SCALE)
301 {
302 g_warning (G_STRLOC ": cursor theme size %u too large", scale);
303 scale = GDK_WAYLAND_MAX_THEME_SCALE;
304 }
305
306 if (wayland_cursor->scale == scale)
307 return;
308
309 wayland_cursor->scale = scale;
310
311 _gdk_wayland_cursor_update (display_wayland, wayland_cursor);
312}
313
314static void
315_gdk_wayland_cursor_class_init (GdkWaylandCursorClass *wayland_cursor_class)
316{
317 GdkCursorClass *cursor_class = GDK_CURSOR_CLASS (wayland_cursor_class);
318 GObjectClass *object_class = G_OBJECT_CLASS (wayland_cursor_class);
319
320 object_class->finalize = gdk_wayland_cursor_finalize;
321
322 cursor_class->get_surface = gdk_wayland_cursor_get_surface;
323}
324
325static void
326_gdk_wayland_cursor_init (GdkWaylandCursor *cursor)
327{
328}
329
330static GdkCursor *
331_gdk_wayland_display_get_cursor_for_name_with_scale (GdkDisplay *display,
332 const gchar *name,
333 guint scale)
334{
335 GdkWaylandCursor *private;
336 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
337
338 g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
339
340 private = g_hash_table_lookup (display_wayland->cursor_cache, name);
341 if (private)
342 return GDK_CURSOR (g_object_ref (private));
343
344 private = g_object_new (GDK_TYPE_WAYLAND_CURSOR,
345 "cursor-type", GDK_CURSOR_IS_PIXMAP,
346 "display", display,
347 NULL);
348 private->name = g_strdup (name);
349 private->scale = scale;
350
351 /* Blank cursor case */
352 if (!name || g_str_equal (name, "none") || g_str_equal (name, "blank_cursor"))
353 return GDK_CURSOR (private);
354
355 if (!_gdk_wayland_cursor_update (display_wayland, private))
356 return GDK_CURSOR (private);
357
358 /* Insert into cache. */
359 g_hash_table_insert (display_wayland->cursor_cache,
360 private->name,
361 g_object_ref (private));
362 return GDK_CURSOR (private);
363}
364
365GdkCursor *
366_gdk_wayland_display_get_cursor_for_name (GdkDisplay *display,
367 const gchar *name)
368{
369 return _gdk_wayland_display_get_cursor_for_name_with_scale (display, name, 1);
370}
371
372GdkCursor *
373_gdk_wayland_display_get_cursor_for_type_with_scale (GdkDisplay *display,
374 GdkCursorType cursor_type,
375 guint scale)
376{
377 GEnumClass *enum_class;
378 GEnumValue *enum_value;
379 gchar *cursor_name;
380 GdkCursor *result;
381
382 enum_class = g_type_class_ref (GDK_TYPE_CURSOR_TYPE);
383 enum_value = g_enum_get_value (enum_class, cursor_type);
384 cursor_name = g_strdup (enum_value->value_nick);
385 g_strdelimit (cursor_name, "-", '_');
386 g_type_class_unref (enum_class);
387
388 result = _gdk_wayland_display_get_cursor_for_name_with_scale (display,
389 cursor_name,
390 scale);
391
392 g_free (cursor_name);
393
394 return result;
395}
396
397GdkCursor *
398_gdk_wayland_display_get_cursor_for_type (GdkDisplay *display,
399 GdkCursorType cursor_type)
400{
401 return _gdk_wayland_display_get_cursor_for_type_with_scale (display,
402 cursor_type,
403 1);
404}
405
406static void
407buffer_release_callback (void *_data,
408 struct wl_buffer *wl_buffer)
409{
410 cairo_surface_t *cairo_surface = _data;
411
412 cairo_surface_destroy (cairo_surface);
413}
414
415static const struct wl_buffer_listener buffer_listener = {
416 buffer_release_callback
417};
418
419GdkCursor *
420_gdk_wayland_display_get_cursor_for_surface (GdkDisplay *display,
421 cairo_surface_t *surface,
422 gdouble x,
423 gdouble y)
424{
425 GdkWaylandCursor *cursor;
426 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
427 struct wl_buffer *buffer;
428 cairo_t *cr;
429
430 cursor = g_object_new (GDK_TYPE_WAYLAND_CURSOR,
431 "cursor-type", GDK_CURSOR_IS_PIXMAP,
432 "display", display_wayland,
433 NULL);
434 cursor->name = NULL;
435 cursor->surface.hotspot_x = x;
436 cursor->surface.hotspot_y = y;
437
438 cursor->surface.scale = 1;
439
440 if (surface)
441 {
442 double sx, sy;
443 cairo_surface_get_device_scale (surface, &sx, &sy);
444 cursor->surface.scale = (int)sx;
445 cursor->surface.width = cairo_image_surface_get_width (surface);
446 cursor->surface.height = cairo_image_surface_get_height (surface);
447 }
448 else
449 {
450 cursor->surface.width = 1;
451 cursor->surface.height = 1;
452 }
453
454 cursor->surface.cairo_surface =
455 _gdk_wayland_display_create_shm_surface (display_wayland,
456 cursor->surface.width,
457 cursor->surface.height,
458 cursor->surface.scale);
459
460 buffer = _gdk_wayland_shm_surface_get_wl_buffer (cursor->surface.cairo_surface);
461 wl_buffer_add_listener (buffer, &buffer_listener, cursor->surface.cairo_surface);
462
463 if (surface)
464 {
465 cr = cairo_create (cursor->surface.cairo_surface);
466 cairo_set_source_surface (cr, surface, 0, 0);
467 cairo_paint (cr);
468 cairo_destroy (cr);
469 }
470
471 return GDK_CURSOR (cursor);
472}
473
474void
475_gdk_wayland_display_get_default_cursor_size (GdkDisplay *display,
476 guint *width,
477 guint *height)
478{
479 *width = 32;
480 *height = 32;
481}
482
483void
484_gdk_wayland_display_get_maximal_cursor_size (GdkDisplay *display,
485 guint *width,
486 guint *height)
487{
488 *width = 256;
489 *height = 256;
490}
491
492gboolean
493_gdk_wayland_display_supports_cursor_alpha (GdkDisplay *display)
494{
495 return TRUE;
496}
497
498gboolean
499_gdk_wayland_display_supports_cursor_color (GdkDisplay *display)
500{
501 return TRUE;
502}
503