xref: /aosp_15_r20/external/wayland/cursor/wayland-cursor.c (revision 84e872a0dc482bffdb63672969dd03a827d67c73)
1 /*
2  * Copyright © 2012 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the
13  * next paragraph) shall be included in all copies or substantial
14  * portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25 
26 #include "config.h"
27 #include "xcursor.h"
28 #include "wayland-cursor.h"
29 #include "wayland-client.h"
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdint.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <sys/mman.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 
39 #include "os-compatibility.h"
40 
41 #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
42 
43 struct shm_pool {
44 	struct wl_shm_pool *pool;
45 	int fd;
46 	unsigned int size;
47 	unsigned int used;
48 	char *data;
49 };
50 
51 static struct shm_pool *
shm_pool_create(struct wl_shm * shm,int size)52 shm_pool_create(struct wl_shm *shm, int size)
53 {
54 	struct shm_pool *pool;
55 
56 	pool = malloc(sizeof *pool);
57 	if (!pool)
58 		return NULL;
59 
60 	pool->fd = os_create_anonymous_file(size);
61 	if (pool->fd < 0)
62 		goto err_free;
63 
64 	pool->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED,
65 			  pool->fd, 0);
66 
67 	if (pool->data == MAP_FAILED)
68 		goto err_close;
69 
70 	pool->pool = wl_shm_create_pool(shm, pool->fd, size);
71 	pool->size = size;
72 	pool->used = 0;
73 
74 	return pool;
75 
76 err_close:
77 	close(pool->fd);
78 err_free:
79 	free(pool);
80 	return NULL;
81 }
82 
83 static int
shm_pool_resize(struct shm_pool * pool,int size)84 shm_pool_resize(struct shm_pool *pool, int size)
85 {
86 	if (os_resize_anonymous_file(pool->fd, size) < 0)
87 		return 0;
88 
89 	wl_shm_pool_resize(pool->pool, size);
90 
91 	munmap(pool->data, pool->size);
92 
93 	pool->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED,
94 			  pool->fd, 0);
95 	if (pool->data == MAP_FAILED)
96 		return 0;
97 	pool->size = size;
98 
99 	return 1;
100 }
101 
102 static int
shm_pool_allocate(struct shm_pool * pool,int size)103 shm_pool_allocate(struct shm_pool *pool, int size)
104 {
105 	int offset;
106 
107 	if (pool->used + size > pool->size)
108 		if (!shm_pool_resize(pool, 2 * pool->size + size))
109 			return -1;
110 
111 	offset = pool->used;
112 	pool->used += size;
113 
114 	return offset;
115 }
116 
117 static void
shm_pool_destroy(struct shm_pool * pool)118 shm_pool_destroy(struct shm_pool *pool)
119 {
120 	munmap(pool->data, pool->size);
121 	wl_shm_pool_destroy(pool->pool);
122 	close(pool->fd);
123 	free(pool);
124 }
125 
126 
127 struct wl_cursor_theme {
128 	unsigned int cursor_count;
129 	struct wl_cursor **cursors;
130 	struct wl_shm *shm;
131 	struct shm_pool *pool;
132 	int size;
133 };
134 
135 struct cursor_image {
136 	struct wl_cursor_image image;
137 	struct wl_cursor_theme *theme;
138 	struct wl_buffer *buffer;
139 	int offset; /* data offset of this image in the shm pool */
140 };
141 
142 struct cursor {
143 	struct wl_cursor cursor;
144 	uint32_t total_delay; /* length of the animation in ms */
145 };
146 
147 /** Get an shm buffer for a cursor image
148  *
149  * \param image The cursor image
150  * \return An shm buffer for the cursor image. The user should not destroy
151  * the returned buffer.
152  */
153 WL_EXPORT struct wl_buffer *
wl_cursor_image_get_buffer(struct wl_cursor_image * image)154 wl_cursor_image_get_buffer(struct wl_cursor_image *image)
155 {
156 	struct cursor_image *img = (struct cursor_image *) image;
157 	struct wl_cursor_theme *theme = img->theme;
158 
159 	if (!img->buffer) {
160 		img->buffer =
161 			wl_shm_pool_create_buffer(theme->pool->pool,
162 						  img->offset,
163 						  image->width, image->height,
164 						  image->width * 4,
165 						  WL_SHM_FORMAT_ARGB8888);
166 	};
167 
168 	return img->buffer;
169 }
170 
171 static void
wl_cursor_image_destroy(struct wl_cursor_image * image)172 wl_cursor_image_destroy(struct wl_cursor_image *image)
173 {
174 	struct cursor_image *img = (struct cursor_image *) image;
175 
176 	if (img->buffer)
177 		wl_buffer_destroy(img->buffer);
178 
179 	free(img);
180 }
181 
182 static void
wl_cursor_destroy(struct wl_cursor * cursor)183 wl_cursor_destroy(struct wl_cursor *cursor)
184 {
185 	unsigned int i;
186 
187 	for (i = 0; i < cursor->image_count; i++)
188 		wl_cursor_image_destroy(cursor->images[i]);
189 
190 	free(cursor->images);
191 	free(cursor->name);
192 	free(cursor);
193 }
194 
195 #include "cursor-data.h"
196 
197 static struct wl_cursor *
wl_cursor_create_from_data(struct cursor_metadata * metadata,struct wl_cursor_theme * theme)198 wl_cursor_create_from_data(struct cursor_metadata *metadata,
199 			   struct wl_cursor_theme *theme)
200 {
201 	struct cursor *cursor;
202 	struct cursor_image *image;
203 	int size;
204 
205 	cursor = malloc(sizeof *cursor);
206 	if (!cursor)
207 		return NULL;
208 
209 	cursor->cursor.image_count = 1;
210 	cursor->cursor.images = malloc(sizeof *cursor->cursor.images);
211 	if (!cursor->cursor.images)
212 		goto err_free_cursor;
213 
214 	cursor->cursor.name = strdup(metadata->name);
215 	cursor->total_delay = 0;
216 
217 	image = malloc(sizeof *image);
218 	if (!image)
219 		goto err_free_images;
220 
221 	cursor->cursor.images[0] = (struct wl_cursor_image *) image;
222 	image->theme = theme;
223 	image->buffer = NULL;
224 	image->image.width = metadata->width;
225 	image->image.height = metadata->height;
226 	image->image.hotspot_x = metadata->hotspot_x;
227 	image->image.hotspot_y = metadata->hotspot_y;
228 	image->image.delay = 0;
229 
230 	size = metadata->width * metadata->height * sizeof(uint32_t);
231 	image->offset = shm_pool_allocate(theme->pool, size);
232 
233 	if (image->offset < 0)
234 		goto err_free_image;
235 
236 	memcpy(theme->pool->data + image->offset,
237 	       cursor_data + metadata->offset, size);
238 
239 	return &cursor->cursor;
240 
241 err_free_image:
242 	free(image);
243 
244 err_free_images:
245 	free(cursor->cursor.name);
246 	free(cursor->cursor.images);
247 
248 err_free_cursor:
249 	free(cursor);
250 	return NULL;
251 }
252 
253 static void
load_fallback_theme(struct wl_cursor_theme * theme)254 load_fallback_theme(struct wl_cursor_theme *theme)
255 {
256 	uint32_t i;
257 
258 	theme->cursor_count = ARRAY_LENGTH(cursor_metadata);
259 	theme->cursors = malloc(theme->cursor_count * sizeof(*theme->cursors));
260 
261 	if (theme->cursors == NULL) {
262 		theme->cursor_count = 0;
263 		return;
264 	}
265 
266 	for (i = 0; i < theme->cursor_count; ++i) {
267 		theme->cursors[i] =
268 			wl_cursor_create_from_data(&cursor_metadata[i], theme);
269 
270 		if (theme->cursors[i] == NULL)
271 			break;
272 	}
273 	theme->cursor_count = i;
274 }
275 
276 static struct wl_cursor *
wl_cursor_create_from_xcursor_images(struct xcursor_images * images,struct wl_cursor_theme * theme)277 wl_cursor_create_from_xcursor_images(struct xcursor_images *images,
278 				     struct wl_cursor_theme *theme)
279 {
280 	struct cursor *cursor;
281 	struct cursor_image *image;
282 	int i, size;
283 
284 	cursor = malloc(sizeof *cursor);
285 	if (!cursor)
286 		return NULL;
287 
288 	cursor->cursor.images =
289 		malloc(images->nimage * sizeof cursor->cursor.images[0]);
290 	if (!cursor->cursor.images) {
291 		free(cursor);
292 		return NULL;
293 	}
294 
295 	cursor->cursor.name = strdup(images->name);
296 	cursor->total_delay = 0;
297 
298 	for (i = 0; i < images->nimage; i++) {
299 		image = malloc(sizeof *image);
300 		if (image == NULL)
301 			break;
302 
303 		image->theme = theme;
304 		image->buffer = NULL;
305 
306 		image->image.width = images->images[i]->width;
307 		image->image.height = images->images[i]->height;
308 		image->image.hotspot_x = images->images[i]->xhot;
309 		image->image.hotspot_y = images->images[i]->yhot;
310 		image->image.delay = images->images[i]->delay;
311 
312 		size = image->image.width * image->image.height * 4;
313 		image->offset = shm_pool_allocate(theme->pool, size);
314 		if (image->offset < 0) {
315 			free(image);
316 			break;
317 		}
318 
319 		/* copy pixels to shm pool */
320 		memcpy(theme->pool->data + image->offset,
321 		       images->images[i]->pixels, size);
322 		cursor->total_delay += image->image.delay;
323 		cursor->cursor.images[i] = (struct wl_cursor_image *) image;
324 	}
325 	cursor->cursor.image_count = i;
326 
327 	if (cursor->cursor.image_count == 0) {
328 		free(cursor->cursor.name);
329 		free(cursor->cursor.images);
330 		free(cursor);
331 		return NULL;
332 	}
333 
334 	return &cursor->cursor;
335 }
336 
337 static void
load_callback(struct xcursor_images * images,void * data)338 load_callback(struct xcursor_images *images, void *data)
339 {
340 	struct wl_cursor_theme *theme = data;
341 	struct wl_cursor *cursor;
342 
343 	if (wl_cursor_theme_get_cursor(theme, images->name)) {
344 		xcursor_images_destroy(images);
345 		return;
346 	}
347 
348 	cursor = wl_cursor_create_from_xcursor_images(images, theme);
349 
350 	if (cursor) {
351 		theme->cursor_count++;
352 		theme->cursors =
353 			realloc(theme->cursors,
354 				theme->cursor_count * sizeof theme->cursors[0]);
355 
356 		if (theme->cursors == NULL) {
357 			theme->cursor_count--;
358 			free(cursor);
359 		} else {
360 			theme->cursors[theme->cursor_count - 1] = cursor;
361 		}
362 	}
363 
364 	xcursor_images_destroy(images);
365 }
366 
367 /** Load a cursor theme to memory shared with the compositor
368  *
369  * \param name The name of the cursor theme to load. If %NULL, the default
370  * theme will be loaded.
371  * \param size Desired size of the cursor images.
372  * \param shm The compositor's shm interface.
373  *
374  * \return An object representing the theme that should be destroyed with
375  * wl_cursor_theme_destroy() or %NULL on error. If no theme with the given
376  * name exists, a default theme will be loaded.
377  */
378 WL_EXPORT struct wl_cursor_theme *
wl_cursor_theme_load(const char * name,int size,struct wl_shm * shm)379 wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm)
380 {
381 	struct wl_cursor_theme *theme;
382 
383 	theme = malloc(sizeof *theme);
384 	if (!theme)
385 		return NULL;
386 
387 	if (!name)
388 		name = "default";
389 
390 	theme->size = size;
391 	theme->cursor_count = 0;
392 	theme->cursors = NULL;
393 
394 	theme->pool = shm_pool_create(shm, size * size * 4);
395 	if (!theme->pool)
396 		goto out_error_pool;
397 
398 	xcursor_load_theme(name, size, load_callback, theme);
399 
400 	if (theme->cursor_count == 0)
401 		xcursor_load_theme(NULL, size, load_callback, theme);
402 
403 	if (theme->cursor_count == 0)
404 		load_fallback_theme(theme);
405 
406 	return theme;
407 
408 out_error_pool:
409 	free(theme);
410 	return NULL;
411 }
412 
413 /** Destroys a cursor theme object
414  *
415  * \param theme The cursor theme to be destroyed
416  */
417 WL_EXPORT void
wl_cursor_theme_destroy(struct wl_cursor_theme * theme)418 wl_cursor_theme_destroy(struct wl_cursor_theme *theme)
419 {
420 	unsigned int i;
421 
422 	for (i = 0; i < theme->cursor_count; i++)
423 		wl_cursor_destroy(theme->cursors[i]);
424 
425 	shm_pool_destroy(theme->pool);
426 
427 	free(theme->cursors);
428 	free(theme);
429 }
430 
431 /** Get the cursor for a given name from a cursor theme
432  *
433  * \param theme The cursor theme
434  * \param name Name of the desired cursor
435  * \return The theme's cursor of the given name or %NULL if there is no
436  * such cursor
437  */
438 WL_EXPORT struct wl_cursor *
wl_cursor_theme_get_cursor(struct wl_cursor_theme * theme,const char * name)439 wl_cursor_theme_get_cursor(struct wl_cursor_theme *theme,
440 			   const char *name)
441 {
442 	unsigned int i;
443 
444 	for (i = 0; i < theme->cursor_count; i++) {
445 		if (strcmp(name, theme->cursors[i]->name) == 0)
446 			return theme->cursors[i];
447 	}
448 
449 	return NULL;
450 }
451 
452 /** Find the frame for a given elapsed time in a cursor animation
453  *  as well as the time left until next cursor change.
454  *
455  * \param cursor The cursor
456  * \param time Elapsed time in ms since the beginning of the animation
457  * \param duration pointer to uint32_t to store time left for this image or
458  *                 zero if the cursor won't change.
459  *
460  * \return The index of the image that should be displayed for the
461  * given time in the cursor animation.
462  */
463 WL_EXPORT int
wl_cursor_frame_and_duration(struct wl_cursor * cursor,uint32_t time,uint32_t * duration)464 wl_cursor_frame_and_duration(struct wl_cursor *cursor, uint32_t time,
465 			     uint32_t *duration)
466 {
467 	struct cursor *cur = (struct cursor *) cursor;
468 	uint32_t t;
469 	int i;
470 
471 	if (cur->cursor.image_count == 1 || cur->total_delay == 0) {
472 		if (duration)
473 			*duration = 0;
474 		return 0;
475 	}
476 
477 	i = 0;
478 	t = time % cur->total_delay;
479 
480 	/* If there is a 0 delay in the image set then this
481 	 * loop breaks on it and we display that cursor until
482 	 * time % cursor->total_delay wraps again.
483 	 * Since a 0 delay is silly, and we've never actually
484 	 * seen one in a cursor file, we haven't bothered to
485 	 * "fix" this.
486 	 */
487 	while (t - cur->cursor.images[i]->delay < t)
488 		t -= cur->cursor.images[i++]->delay;
489 
490 	if (!duration)
491 		return i;
492 
493 	/* Make sure we don't accidentally tell the caller this is
494 	 * a static cursor image.
495 	 */
496 	if (t >= cur->cursor.images[i]->delay)
497 		*duration = 1;
498 	else
499 		*duration = cur->cursor.images[i]->delay - t;
500 
501 	return i;
502 }
503 
504 /** Find the frame for a given elapsed time in a cursor animation
505  *
506  * \param cursor The cursor
507  * \param time Elapsed time in ms since the beginning of the animation
508  *
509  * \return The index of the image that should be displayed for the
510  * given time in the cursor animation.
511  */
512 WL_EXPORT int
wl_cursor_frame(struct wl_cursor * cursor,uint32_t time)513 wl_cursor_frame(struct wl_cursor *cursor, uint32_t time)
514 {
515 	return wl_cursor_frame_and_duration(cursor, time, NULL);
516 }
517