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 * |
52 | shm_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 | |
76 | err_close: |
77 | close(fd: pool->fd); |
78 | err_free: |
79 | free(ptr: pool); |
80 | return NULL; |
81 | } |
82 | |
83 | static int |
84 | shm_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 | |
102 | static int |
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, size: 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 |
118 | shm_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 | |
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 * |
154 | wl_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 | |
171 | static void |
172 | wl_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 | |
182 | static void |
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(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 | |
197 | static struct wl_cursor * |
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(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 | |
241 | err_free_image: |
242 | free(ptr: image); |
243 | |
244 | err_free_images: |
245 | free(ptr: cursor->cursor.name); |
246 | free(ptr: cursor->cursor.images); |
247 | |
248 | err_free_cursor: |
249 | free(ptr: cursor); |
250 | return NULL; |
251 | } |
252 | |
253 | static void |
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(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 | |
276 | static struct wl_cursor * |
277 | wl_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 | |
337 | static void |
338 | load_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 | */ |
378 | WL_EXPORT struct wl_cursor_theme * |
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(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 | |
408 | out_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 | */ |
417 | WL_EXPORT void |
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(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 | */ |
438 | WL_EXPORT struct wl_cursor * |
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(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 | */ |
463 | WL_EXPORT int |
464 | wl_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 | */ |
512 | WL_EXPORT int |
513 | wl_cursor_frame(struct wl_cursor *_cursor, uint32_t time) |
514 | { |
515 | return wl_cursor_frame_and_duration(_cursor, time, NULL); |
516 | } |
517 | |