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#include <os-compatibility.h>
39#include <glib.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 (ftruncate(fd: pool->fd, length: size) < 0)
87 return 0;
88
89#ifdef HAVE_POSIX_FALLOCATE
90 errno = posix_fallocate(fd: pool->fd, offset: 0, len: size);
91 if (errno != 0)
92 return 0;
93#endif
94
95 wl_shm_pool_resize(wl_shm_pool: pool->pool, size);
96
97 munmap(addr: pool->data, len: pool->size);
98
99 pool->data = mmap(NULL, len: size, PROT_READ | PROT_WRITE, MAP_SHARED,
100 fd: pool->fd, offset: 0);
101 if (pool->data == (void *)-1)
102 return 0;
103 pool->size = size;
104
105 return 1;
106}
107
108static int
109shm_pool_allocate(struct shm_pool *pool, int size)
110{
111 int offset;
112
113 if (pool->used + size > pool->size)
114 if (!shm_pool_resize(pool, size: 2 * pool->size + size))
115 return -1;
116
117 offset = pool->used;
118 pool->used += size;
119
120 return offset;
121}
122
123static void
124shm_pool_destroy(struct shm_pool *pool)
125{
126 munmap(addr: pool->data, len: pool->size);
127 wl_shm_pool_destroy(wl_shm_pool: pool->pool);
128 close(fd: pool->fd);
129 free(ptr: pool);
130}
131
132
133struct wl_cursor_theme {
134 unsigned int cursor_count;
135 struct wl_cursor **cursors;
136 struct wl_shm *shm;
137 struct shm_pool *pool;
138 int size;
139 char *path;
140};
141
142struct cursor_image {
143 struct wl_cursor_image image;
144 struct wl_cursor_theme *theme;
145 struct wl_buffer *buffer;
146 int offset; /* data offset of this image in the shm pool */
147};
148
149struct cursor {
150 struct wl_cursor cursor;
151 uint32_t total_delay; /* length of the animation in ms */
152};
153
154/** Get an shm buffer for a cursor image
155 *
156 * \param image The cursor image
157 * \return An shm buffer for the cursor image. The user should not destroy
158 * the returned buffer.
159 */
160struct wl_buffer *
161wl_cursor_image_get_buffer(struct wl_cursor_image *_img)
162{
163 struct cursor_image *image = (struct cursor_image *) _img;
164 struct wl_cursor_theme *theme = image->theme;
165
166 if (!image->buffer) {
167 image->buffer =
168 wl_shm_pool_create_buffer(wl_shm_pool: theme->pool->pool,
169 offset: image->offset,
170 width: _img->width, height: _img->height,
171 stride: _img->width * 4,
172 format: WL_SHM_FORMAT_ARGB8888);
173 };
174
175 return image->buffer;
176}
177
178static void
179wl_cursor_image_destroy(struct wl_cursor_image *_img)
180{
181 struct cursor_image *image = (struct cursor_image *) _img;
182
183 if (image->buffer)
184 wl_buffer_destroy(wl_buffer: image->buffer);
185
186 free(ptr: image);
187}
188
189static void
190wl_cursor_destroy(struct wl_cursor *cursor)
191{
192 unsigned int i;
193
194 for (i = 0; i < cursor->image_count; i++)
195 wl_cursor_image_destroy(img: cursor->images[i]);
196
197 free(ptr: cursor->images);
198 free(ptr: cursor->name);
199 free(ptr: cursor);
200}
201
202static struct wl_cursor *
203wl_cursor_create_from_xcursor_images(struct wl_cursor_theme *theme,
204 const char *name,
205 unsigned int size,
206 unsigned int scale)
207{
208 char *path;
209 XcursorImages *images;
210 struct cursor *cursor;
211 struct cursor_image *image;
212 int i, nbytes;
213 unsigned int load_size;
214 int load_scale = 1;
215
216 load_size = size * scale;
217
218 path = g_strconcat (string1: theme->path, "/", name, NULL);
219 images = xcursor_load_images (path, size: load_size);
220
221 if (!images)
222 {
223 g_free (mem: path);
224 return NULL;
225 }
226
227 if (images->images[0]->width != load_size ||
228 images->images[0]->height != load_size)
229 {
230 xcursor_images_destroy (images);
231 images = xcursor_load_images (path, size);
232 load_scale = scale;
233 }
234
235 g_free (mem: path);
236
237 cursor = malloc(size: sizeof *cursor);
238 if (!cursor) {
239 xcursor_images_destroy (images);
240 return NULL;
241 }
242
243 cursor->cursor.images =
244 malloc(size: images->nimage * sizeof cursor->cursor.images[0]);
245 if (!cursor->cursor.images) {
246 free(ptr: cursor);
247 xcursor_images_destroy (images);
248 return NULL;
249 }
250
251 cursor->cursor.name = strdup(s: name);
252 cursor->cursor.size = load_size;
253 cursor->total_delay = 0;
254
255 for (i = 0; i < images->nimage; i++) {
256 image = malloc(size: sizeof *image);
257 if (image == NULL)
258 break;
259
260 image->theme = theme;
261 image->buffer = NULL;
262
263 image->image.width = images->images[i]->width * load_scale;
264 image->image.height = images->images[i]->height * load_scale;
265 image->image.hotspot_x = images->images[i]->xhot * load_scale;
266 image->image.hotspot_y = images->images[i]->yhot * load_scale;
267 image->image.delay = images->images[i]->delay;
268
269 nbytes = image->image.width * image->image.height * 4;
270 image->offset = shm_pool_allocate(pool: theme->pool, size: nbytes);
271 if (image->offset < 0) {
272 free(ptr: image);
273 break;
274 }
275
276 if (load_scale == 1) {
277 /* copy pixels to shm pool */
278 memcpy(dest: theme->pool->data + image->offset,
279 src: images->images[i]->pixels, n: nbytes);
280 }
281 else {
282 /* scale image up while copying it */
283 for (int y = 0; y < image->image.height; y++) {
284 char *p = theme->pool->data + image->offset + y * image->image.width * 4;
285 char *q = ((char *)images->images[i]->pixels) + (y / load_scale) * images->images[i]->width * 4;
286 for (int x = 0; x < image->image.width; x++) {
287 p[4 * x] = q[4 * (x/load_scale)];
288 p[4 * x + 1] = q[4 * (x/load_scale) + 1];
289 p[4 * x + 2] = q[4 * (x/load_scale) + 2];
290 p[4 * x + 3] = q[4 * (x/load_scale) + 3];
291 }
292 }
293 }
294 cursor->total_delay += image->image.delay;
295 cursor->cursor.images[i] = (struct wl_cursor_image *) image;
296 }
297 cursor->cursor.image_count = i;
298
299 if (cursor->cursor.image_count == 0) {
300 free(ptr: cursor->cursor.name);
301 free(ptr: cursor->cursor.images);
302 free(ptr: cursor);
303 xcursor_images_destroy (images);
304 return NULL;
305 }
306
307 xcursor_images_destroy (images);
308
309 return &cursor->cursor;
310}
311
312static void
313load_cursor(struct wl_cursor_theme *theme,
314 const char *name,
315 unsigned int size,
316 unsigned int scale)
317{
318 struct wl_cursor *cursor;
319
320 cursor = wl_cursor_create_from_xcursor_images(theme, name, size, scale);
321
322 if (cursor) {
323 theme->cursor_count++;
324 theme->cursors =
325 realloc(ptr: theme->cursors,
326 size: theme->cursor_count * sizeof theme->cursors[0]);
327
328 if (theme->cursors == NULL) {
329 theme->cursor_count--;
330 free(ptr: cursor);
331 } else {
332 theme->cursors[theme->cursor_count - 1] = cursor;
333 }
334 }
335}
336
337/** Load a cursor theme to memory shared with the compositor
338 *
339 * \param name The name of the cursor theme to load. If %NULL, the default
340 * theme will be loaded.
341 * \param size Desired size of the cursor images.
342 * \param shm The compositor's shm interface.
343 *
344 * \return An object representing the theme that should be destroyed with
345 * wl_cursor_theme_destroy() or %NULL on error. If no theme with the given
346 * name exists, a default theme will be loaded.
347 */
348struct wl_cursor_theme *
349wl_cursor_theme_create(const char *path, int size, struct wl_shm *shm)
350{
351 struct wl_cursor_theme *theme;
352
353 theme = malloc(size: sizeof *theme);
354 if (!theme)
355 return NULL;
356
357 theme->path = strdup (s: path);
358 theme->size = size;
359 theme->cursor_count = 0;
360 theme->cursors = NULL;
361
362 theme->pool = shm_pool_create(shm, size: size * size * 4);
363 if (!theme->pool) {
364 free (ptr: theme->path);
365 free (ptr: theme);
366 return NULL;
367 }
368
369 return theme;
370}
371
372/** Destroys a cursor theme object
373 *
374 * \param theme The cursor theme to be destroyed
375 */
376void
377wl_cursor_theme_destroy(struct wl_cursor_theme *theme)
378{
379 unsigned int i;
380
381 for (i = 0; i < theme->cursor_count; i++)
382 wl_cursor_destroy(cursor: theme->cursors[i]);
383
384 shm_pool_destroy(pool: theme->pool);
385
386 free(ptr: theme->cursors);
387 free(ptr: theme->path);
388 free(ptr: theme);
389}
390
391/** Get the cursor for a given name from a cursor theme
392 *
393 * \param theme The cursor theme
394 * \param name Name of the desired cursor
395 * \return The theme's cursor of the given name or %NULL if there is no
396 * such cursor
397 */
398struct wl_cursor *
399wl_cursor_theme_get_cursor(struct wl_cursor_theme *theme,
400 const char *name,
401 unsigned int scale)
402{
403 unsigned int i;
404 unsigned int size;
405
406 size = theme->size * scale;
407
408 for (i = 0; i < theme->cursor_count; i++) {
409 if (size == theme->cursors[i]->size &&
410 strcmp(s1: name, s2: theme->cursors[i]->name) == 0)
411 return theme->cursors[i];
412 }
413
414 load_cursor (theme, name, size: theme->size, scale);
415
416 if (i < theme->cursor_count) {
417 if (size == theme->cursors[i]->size &&
418 strcmp (s1: name, s2: theme->cursors[theme->cursor_count - 1]->name) == 0)
419 return theme->cursors[theme->cursor_count - 1];
420 }
421
422 return NULL;
423}
424

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