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 | |
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 (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 | |
108 | static int |
109 | shm_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 | |
123 | static void |
124 | shm_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 | |
133 | struct 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 | |
142 | struct 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 | |
149 | struct 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 | */ |
160 | struct wl_buffer * |
161 | wl_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 | |
178 | static void |
179 | wl_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 | |
189 | static void |
190 | wl_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 | |
202 | static struct wl_cursor * |
203 | wl_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 | |
312 | static void |
313 | load_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 | */ |
348 | struct wl_cursor_theme * |
349 | wl_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 | */ |
376 | void |
377 | wl_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 | */ |
398 | struct wl_cursor * |
399 | wl_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 | |