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 | |
38 | static void |
39 | gdk_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 | |
46 | void |
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 | |
52 | void |
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 | |
64 | static 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 | |
104 | static const char * |
105 | name_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 | |
118 | static struct wl_cursor * |
119 | gdk_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 | |
141 | static void |
142 | buffer_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 | |
150 | static const struct wl_buffer_listener buffer_listener = { |
151 | buffer_release_callback |
152 | }; |
153 | |
154 | struct 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 | |
220 | from_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 | |
267 | none: |
268 | *hotspot_x = 0; |
269 | *hotspot_y = 0; |
270 | *width = 0; |
271 | *height = 0; |
272 | *scale = 1; |
273 | |
274 | return NULL; |
275 | } |
276 | |
277 | guint |
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 | |