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
43struct shm_pool {
44 struct wl_shm_pool *pool;
45 int fd;
46 unsigned int size;
47 unsigned int used;
48 char *data;
49};
50
51static struct shm_pool *
52shm_pool_create(struct wl_shm *shm, int size)
53{
54 struct shm_pool *pool;
55
56 pool = malloc(size: 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, len: size, PROT_READ | PROT_WRITE, MAP_SHARED,
65 fd: pool->fd, offset: 0);
66
67 if (pool->data == MAP_FAILED)
68 goto err_close;
69
70 pool->pool = wl_shm_create_pool(wl_shm: shm, fd: pool->fd, size);
71 pool->size = size;
72 pool->used = 0;
73
74 return pool;
75
76err_close:
77 close(fd: pool->fd);
78err_free:
79 free(ptr: pool);
80 return NULL;
81}
82
83static int
84shm_pool_resize(struct shm_pool *pool, int size)
85{
86 if (os_resize_anonymous_file(fd: pool->fd, size) < 0)
87 return 0;
88
89 wl_shm_pool_resize(wl_shm_pool: pool->pool, size);
90
91 munmap(addr: pool->data, len: pool->size);
92
93 pool->data = mmap(NULL, len: size, PROT_READ | PROT_WRITE, MAP_SHARED,
94 fd: pool->fd, offset: 0);
95 if (pool->data == (void *)-1)
96 return 0;
97 pool->size = size;
98
99 return 1;
100}
101
102static int
103shm_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, size: 2 * pool->size + size))
109 return -1;
110
111 offset = pool->used;
112 pool->used += size;
113
114 return offset;
115}
116
117static void
118shm_pool_destroy(struct shm_pool *pool)
119{
120 munmap(addr: pool->data, len: pool->size);
121 wl_shm_pool_destroy(wl_shm_pool: pool->pool);
122 close(fd: pool->fd);
123 free(ptr: pool);
124}
125
126
127struct 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
135struct 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
142struct 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 */
153WL_EXPORT struct wl_buffer *
154wl_cursor_image_get_buffer(struct wl_cursor_image *_img)
155{
156 struct cursor_image *image = (struct cursor_image *) _img;
157 struct wl_cursor_theme *theme = image->theme;
158
159 if (!image->buffer) {
160 image->buffer =
161 wl_shm_pool_create_buffer(wl_shm_pool: theme->pool->pool,
162 offset: image->offset,
163 width: _img->width, height: _img->height,
164 stride: _img->width * 4,
165 format: WL_SHM_FORMAT_ARGB8888);
166 };
167
168 return image->buffer;
169}
170
171static void
172wl_cursor_image_destroy(struct wl_cursor_image *_img)
173{
174 struct cursor_image *image = (struct cursor_image *) _img;
175
176 if (image->buffer)
177 wl_buffer_destroy(wl_buffer: image->buffer);
178
179 free(ptr: image);
180}
181
182static void
183wl_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(img: cursor->images[i]);
189
190 free(ptr: cursor->images);
191 free(ptr: cursor->name);
192 free(ptr: cursor);
193}
194
195#include "cursor-data.h"
196
197static struct wl_cursor *
198wl_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(size: sizeof *cursor);
206 if (!cursor)
207 return NULL;
208
209 cursor->cursor.image_count = 1;
210 cursor->cursor.images = malloc(size: sizeof *cursor->cursor.images);
211 if (!cursor->cursor.images)
212 goto err_free_cursor;
213
214 cursor->cursor.name = strdup(s: metadata->name);
215 cursor->total_delay = 0;
216
217 image = malloc(size: 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(pool: theme->pool, size);
232
233 if (image->offset < 0)
234 goto err_free_image;
235
236 memcpy(dest: theme->pool->data + image->offset,
237 src: cursor_data + metadata->offset, n: size);
238
239 return &cursor->cursor;
240
241err_free_image:
242 free(ptr: image);
243
244err_free_images:
245 free(ptr: cursor->cursor.name);
246 free(ptr: cursor->cursor.images);
247
248err_free_cursor:
249 free(ptr: cursor);
250 return NULL;
251}
252
253static void
254load_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(size: 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(metadata: &cursor_metadata[i], theme);
269
270 if (theme->cursors[i] == NULL)
271 break;
272 }
273 theme->cursor_count = i;
274}
275
276static struct wl_cursor *
277wl_cursor_create_from_xcursor_images(XcursorImages *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(size: sizeof *cursor);
285 if (!cursor)
286 return NULL;
287
288 cursor->cursor.images =
289 malloc(size: images->nimage * sizeof cursor->cursor.images[0]);
290 if (!cursor->cursor.images) {
291 free(ptr: cursor);
292 return NULL;
293 }
294
295 cursor->cursor.name = strdup(s: images->name);
296 cursor->total_delay = 0;
297
298 for (i = 0; i < images->nimage; i++) {
299 image = malloc(size: 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(pool: theme->pool, size);
314 if (image->offset < 0) {
315 free(ptr: image);
316 break;
317 }
318
319 /* copy pixels to shm pool */
320 memcpy(dest: theme->pool->data + image->offset,
321 src: images->images[i]->pixels, n: 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(ptr: cursor->cursor.name);
329 free(ptr: cursor->cursor.images);
330 free(ptr: cursor);
331 return NULL;
332 }
333
334 return &cursor->cursor;
335}
336
337static void
338load_callback(XcursorImages *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, name: images->name)) {
344 XcursorImagesDestroy(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(ptr: theme->cursors,
354 size: theme->cursor_count * sizeof theme->cursors[0]);
355
356 if (theme->cursors == NULL) {
357 theme->cursor_count--;
358 free(ptr: cursor);
359 } else {
360 theme->cursors[theme->cursor_count - 1] = cursor;
361 }
362 }
363
364 XcursorImagesDestroy(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 */
378WL_EXPORT struct wl_cursor_theme *
379wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm)
380{
381 struct wl_cursor_theme *theme;
382
383 theme = malloc(size: 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 * size * 4);
395 if (!theme->pool)
396 goto out_error_pool;
397
398 xcursor_load_theme(theme: name, size, load_callback, user_data: theme);
399
400 if (theme->cursor_count == 0)
401 xcursor_load_theme(NULL, size, load_callback, user_data: theme);
402
403 if (theme->cursor_count == 0)
404 load_fallback_theme(theme);
405
406 return theme;
407
408out_error_pool:
409 free(ptr: theme);
410 return NULL;
411}
412
413/** Destroys a cursor theme object
414 *
415 * \param theme The cursor theme to be destroyed
416 */
417WL_EXPORT void
418wl_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(cursor: theme->cursors[i]);
424
425 shm_pool_destroy(pool: theme->pool);
426
427 free(ptr: theme->cursors);
428 free(ptr: 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 */
438WL_EXPORT struct wl_cursor *
439wl_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(s1: name, s2: 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 */
463WL_EXPORT int
464wl_cursor_frame_and_duration(struct wl_cursor *_cursor, uint32_t time,
465 uint32_t *duration)
466{
467 struct cursor *cursor = (struct cursor *) _cursor;
468 uint32_t t;
469 int i;
470
471 if (cursor->cursor.image_count == 1 || cursor->total_delay == 0) {
472 if (duration)
473 *duration = 0;
474 return 0;
475 }
476
477 i = 0;
478 t = time % cursor->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 - cursor->cursor.images[i]->delay < t)
488 t -= cursor->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 >= cursor->cursor.images[i]->delay)
497 *duration = 1;
498 else
499 *duration = cursor->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 */
512WL_EXPORT int
513wl_cursor_frame(struct wl_cursor *_cursor, uint32_t time)
514{
515 return wl_cursor_frame_and_duration(_cursor, time, NULL);
516}
517

source code of gtk/subprojects/wayland/cursor/wayland-cursor.c