xref: /aosp_15_r20/external/wayland/cursor/xcursor.c (revision 84e872a0dc482bffdb63672969dd03a827d67c73)
1 /*
2  * Copyright © 2002 Keith Packard
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 #define _GNU_SOURCE
27 #include "xcursor.h"
28 #include <stdbool.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <dirent.h>
33 
34 /*
35  * Cursor files start with a header.  The header
36  * contains a magic number, a version number and a
37  * table of contents which has type and offset information
38  * for the remaining tables in the file.
39  *
40  * File minor versions increment for compatible changes
41  * File major versions increment for incompatible changes (never, we hope)
42  *
43  * Chunks of the same type are always upward compatible.  Incompatible
44  * changes are made with new chunk types; the old data can remain under
45  * the old type.  Upward compatible changes can add header data as the
46  * header lengths are specified in the file.
47  *
48  *  File:
49  *	FileHeader
50  *	LISTofChunk
51  *
52  *  FileHeader:
53  *	CARD32		magic	    magic number
54  *	CARD32		header	    bytes in file header
55  *	CARD32		version	    file version
56  *	CARD32		ntoc	    number of toc entries
57  *	LISTofFileToc   toc	    table of contents
58  *
59  *  FileToc:
60  *	CARD32		type	    entry type
61  *	CARD32		subtype	    entry subtype (size for images)
62  *	CARD32		position    absolute file position
63  */
64 
65 #define XCURSOR_MAGIC 0x72756358 /* "Xcur" LSBFirst */
66 
67 /*
68  * This version number is stored in cursor files; changes to the
69  * file format require updating this version number
70  */
71 #define XCURSOR_FILE_MAJOR 1
72 #define XCURSOR_FILE_MINOR 0
73 #define XCURSOR_FILE_VERSION ((XCURSOR_FILE_MAJOR << 16) | (XCURSOR_FILE_MINOR))
74 #define XCURSOR_FILE_HEADER_LEN (4 * 4)
75 #define XCURSOR_FILE_TOC_LEN (3 * 4)
76 
77 struct xcursor_file_toc {
78 	uint32_t type; /* chunk type */
79 	uint32_t subtype; /* subtype (size for images) */
80 	uint32_t position; /* absolute position in file */
81 };
82 
83 struct xcursor_file_header {
84 	uint32_t magic; /* magic number */
85 	uint32_t header; /* byte length of header */
86 	uint32_t version; /* file version number */
87 	uint32_t ntoc; /* number of toc entries */
88 	struct xcursor_file_toc *tocs; /* table of contents */
89 };
90 
91 /*
92  * The rest of the file is a list of chunks, each tagged by type
93  * and version.
94  *
95  *  Chunk:
96  *	ChunkHeader
97  *	<extra type-specific header fields>
98  *	<type-specific data>
99  *
100  *  ChunkHeader:
101  *	CARD32	    header	bytes in chunk header + type header
102  *	CARD32	    type	chunk type
103  *	CARD32	    subtype	chunk subtype
104  *	CARD32	    version	chunk type version
105  */
106 
107 #define XCURSOR_CHUNK_HEADER_LEN (4 * 4)
108 
109 struct xcursor_chunk_header {
110 	uint32_t header; /* bytes in chunk header */
111 	uint32_t type; /* chunk type */
112 	uint32_t subtype; /* chunk subtype (size for images) */
113 	uint32_t version; /* version of this type */
114 };
115 
116 /*
117  * Each cursor image occupies a separate image chunk.
118  * The length of the image header follows the chunk header
119  * so that future versions can extend the header without
120  * breaking older applications
121  *
122  *  Image:
123  *	ChunkHeader	header	chunk header
124  *	CARD32		width	actual width
125  *	CARD32		height	actual height
126  *	CARD32		xhot	hot spot x
127  *	CARD32		yhot	hot spot y
128  *	CARD32		delay	animation delay
129  *	LISTofCARD32	pixels	ARGB pixels
130  */
131 
132 #define XCURSOR_IMAGE_TYPE 0xfffd0002
133 #define XCURSOR_IMAGE_VERSION 1
134 #define XCURSOR_IMAGE_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (5*4))
135 #define XCURSOR_IMAGE_MAX_SIZE 0x7fff /* 32767x32767 max cursor size */
136 
137 /*
138  * From libXcursor/src/file.c
139  */
140 
141 static struct xcursor_image *
xcursor_image_create(int width,int height)142 xcursor_image_create(int width, int height)
143 {
144 	struct xcursor_image *image;
145 
146 	if (width < 0 || height < 0)
147 		return NULL;
148 	if (width > XCURSOR_IMAGE_MAX_SIZE || height > XCURSOR_IMAGE_MAX_SIZE)
149 		return NULL;
150 
151 	image = malloc(sizeof(struct xcursor_image) +
152 		       width * height * sizeof(uint32_t));
153 	if (!image)
154 		return NULL;
155 	image->version = XCURSOR_IMAGE_VERSION;
156 	image->pixels = (uint32_t *) (image + 1);
157 	image->size = width > height ? width : height;
158 	image->width = width;
159 	image->height = height;
160 	image->delay = 0;
161 	return image;
162 }
163 
164 static void
xcursor_image_destroy(struct xcursor_image * image)165 xcursor_image_destroy(struct xcursor_image *image)
166 {
167 	free(image);
168 }
169 
170 static struct xcursor_images *
xcursor_images_create(int size)171 xcursor_images_create(int size)
172 {
173 	struct xcursor_images *images;
174 
175 	images = malloc(sizeof(struct xcursor_images) +
176 			size * sizeof(struct xcursor_image *));
177 	if (!images)
178 		return NULL;
179 	images->nimage = 0;
180 	images->images = (struct xcursor_image **) (images + 1);
181 	images->name = NULL;
182 	return images;
183 }
184 
185 void
xcursor_images_destroy(struct xcursor_images * images)186 xcursor_images_destroy(struct xcursor_images *images)
187 {
188 	int n;
189 
190 	if (!images)
191 		return;
192 
193 	for (n = 0; n < images->nimage; n++)
194 		xcursor_image_destroy(images->images[n]);
195 	free(images->name);
196 	free(images);
197 }
198 
199 static bool
xcursor_read_uint(FILE * file,uint32_t * u)200 xcursor_read_uint(FILE *file, uint32_t *u)
201 {
202 	unsigned char bytes[4];
203 
204 	if (!file || !u)
205 		return false;
206 
207 	if (fread(bytes, 1, 4, file) != 4)
208 		return false;
209 
210 	*u = ((uint32_t)(bytes[0]) << 0) |
211 		 ((uint32_t)(bytes[1]) << 8) |
212 		 ((uint32_t)(bytes[2]) << 16) |
213 		 ((uint32_t)(bytes[3]) << 24);
214 	return true;
215 }
216 
217 static void
xcursor_file_header_destroy(struct xcursor_file_header * file_header)218 xcursor_file_header_destroy(struct xcursor_file_header *file_header)
219 {
220 	free(file_header);
221 }
222 
223 static struct xcursor_file_header *
xcursor_file_header_create(uint32_t ntoc)224 xcursor_file_header_create(uint32_t ntoc)
225 {
226 	struct xcursor_file_header *file_header;
227 
228 	if (ntoc > 0x10000)
229 		return NULL;
230 	file_header = malloc(sizeof(struct xcursor_file_header) +
231 			    ntoc * sizeof(struct xcursor_file_toc));
232 	if (!file_header)
233 		return NULL;
234 	file_header->magic = XCURSOR_MAGIC;
235 	file_header->header = XCURSOR_FILE_HEADER_LEN;
236 	file_header->version = XCURSOR_FILE_VERSION;
237 	file_header->ntoc = ntoc;
238 	file_header->tocs = (struct xcursor_file_toc *) (file_header + 1);
239 	return file_header;
240 }
241 
242 static struct xcursor_file_header *
xcursor_read_file_header(FILE * file)243 xcursor_read_file_header(FILE *file)
244 {
245 	struct xcursor_file_header head, *file_header;
246 	uint32_t skip;
247 	unsigned int n;
248 
249 	if (!file)
250 		return NULL;
251 
252 	if (!xcursor_read_uint(file, &head.magic))
253 		return NULL;
254 	if (head.magic != XCURSOR_MAGIC)
255 		return NULL;
256 	if (!xcursor_read_uint(file, &head.header))
257 		return NULL;
258 	if (!xcursor_read_uint(file, &head.version))
259 		return NULL;
260 	if (!xcursor_read_uint(file, &head.ntoc))
261 		return NULL;
262 	skip = head.header - XCURSOR_FILE_HEADER_LEN;
263 	if (skip)
264 		if (fseek(file, skip, SEEK_CUR) == EOF)
265 			return NULL;
266 	file_header = xcursor_file_header_create(head.ntoc);
267 	if (!file_header)
268 		return NULL;
269 	file_header->magic = head.magic;
270 	file_header->header = head.header;
271 	file_header->version = head.version;
272 	file_header->ntoc = head.ntoc;
273 	for (n = 0; n < file_header->ntoc; n++) {
274 		if (!xcursor_read_uint(file, &file_header->tocs[n].type))
275 			break;
276 		if (!xcursor_read_uint(file, &file_header->tocs[n].subtype))
277 			break;
278 		if (!xcursor_read_uint(file, &file_header->tocs[n].position))
279 			break;
280 	}
281 	if (n != file_header->ntoc) {
282 		xcursor_file_header_destroy(file_header);
283 		return NULL;
284 	}
285 	return file_header;
286 }
287 
288 static bool
xcursor_seek_to_toc(FILE * file,struct xcursor_file_header * file_header,int toc)289 xcursor_seek_to_toc(FILE *file,
290 		    struct xcursor_file_header *file_header,
291 		    int toc)
292 {
293 	if (!file || !file_header ||
294 	    fseek(file, file_header->tocs[toc].position, SEEK_SET) == EOF)
295 		return false;
296 	return true;
297 }
298 
299 static bool
xcursor_file_read_chunk_header(FILE * file,struct xcursor_file_header * file_header,int toc,struct xcursor_chunk_header * chunk_header)300 xcursor_file_read_chunk_header(FILE *file,
301 			       struct xcursor_file_header *file_header,
302 			       int toc,
303 			       struct xcursor_chunk_header *chunk_header)
304 {
305 	if (!file || !file_header || !chunk_header)
306 		return false;
307 	if (!xcursor_seek_to_toc(file, file_header, toc))
308 		return false;
309 	if (!xcursor_read_uint(file, &chunk_header->header))
310 		return false;
311 	if (!xcursor_read_uint(file, &chunk_header->type))
312 		return false;
313 	if (!xcursor_read_uint(file, &chunk_header->subtype))
314 		return false;
315 	if (!xcursor_read_uint(file, &chunk_header->version))
316 		return false;
317 	/* sanity check */
318 	if (chunk_header->type != file_header->tocs[toc].type ||
319 	    chunk_header->subtype != file_header->tocs[toc].subtype)
320 		return false;
321 	return true;
322 }
323 
324 static uint32_t
dist(uint32_t a,uint32_t b)325 dist(uint32_t a, uint32_t b)
326 {
327 	return a > b ? a - b : b - a;
328 }
329 
330 static uint32_t
xcursor_file_best_size(struct xcursor_file_header * file_header,uint32_t size,int * nsizesp)331 xcursor_file_best_size(struct xcursor_file_header *file_header,
332 		       uint32_t size, int *nsizesp)
333 {
334 	unsigned int n;
335 	int nsizes = 0;
336 	uint32_t best_size = 0;
337 	uint32_t this_size;
338 
339 	if (!file_header || !nsizesp)
340 		return 0;
341 
342 	for (n = 0; n < file_header->ntoc; n++) {
343 		if (file_header->tocs[n].type != XCURSOR_IMAGE_TYPE)
344 			continue;
345 		this_size = file_header->tocs[n].subtype;
346 		if (!best_size || dist(this_size, size) < dist(best_size, size)) {
347 			best_size = this_size;
348 			nsizes = 1;
349 		} else if (this_size == best_size) {
350 			nsizes++;
351 		}
352 	}
353 	*nsizesp = nsizes;
354 	return best_size;
355 }
356 
357 static int
xcursor_find_image_toc(struct xcursor_file_header * file_header,uint32_t size,int count)358 xcursor_find_image_toc(struct xcursor_file_header *file_header,
359 		       uint32_t size, int count)
360 {
361 	unsigned int toc;
362 	uint32_t this_size;
363 
364 	if (!file_header)
365 		return 0;
366 
367 	for (toc = 0; toc < file_header->ntoc; toc++) {
368 		if (file_header->tocs[toc].type != XCURSOR_IMAGE_TYPE)
369 			continue;
370 		this_size = file_header->tocs[toc].subtype;
371 		if (this_size != size)
372 			continue;
373 		if (!count)
374 			break;
375 		count--;
376 	}
377 	if (toc == file_header->ntoc)
378 		return -1;
379 	return toc;
380 }
381 
382 static struct xcursor_image *
xcursor_read_image(FILE * file,struct xcursor_file_header * file_header,int toc)383 xcursor_read_image(FILE *file,
384 		   struct xcursor_file_header *file_header,
385 		   int toc)
386 {
387 	struct xcursor_chunk_header chunk_header;
388 	struct xcursor_image head;
389 	struct xcursor_image *image;
390 	int n;
391 	uint32_t *p;
392 
393 	if (!file || !file_header)
394 		return NULL;
395 
396 	if (!xcursor_file_read_chunk_header(file, file_header, toc, &chunk_header))
397 		return NULL;
398 	if (!xcursor_read_uint(file, &head.width))
399 		return NULL;
400 	if (!xcursor_read_uint(file, &head.height))
401 		return NULL;
402 	if (!xcursor_read_uint(file, &head.xhot))
403 		return NULL;
404 	if (!xcursor_read_uint(file, &head.yhot))
405 		return NULL;
406 	if (!xcursor_read_uint(file, &head.delay))
407 		return NULL;
408 	/* sanity check data */
409 	if (head.width > XCURSOR_IMAGE_MAX_SIZE ||
410 	    head.height > XCURSOR_IMAGE_MAX_SIZE)
411 		return NULL;
412 	if (head.width == 0 || head.height == 0)
413 		return NULL;
414 	if (head.xhot > head.width || head.yhot > head.height)
415 		return NULL;
416 
417 	/* Create the image and initialize it */
418 	image = xcursor_image_create(head.width, head.height);
419 	if (image == NULL)
420 		return NULL;
421 	if (chunk_header.version < image->version)
422 		image->version = chunk_header.version;
423 	image->size = chunk_header.subtype;
424 	image->xhot = head.xhot;
425 	image->yhot = head.yhot;
426 	image->delay = head.delay;
427 	n = image->width * image->height;
428 	p = image->pixels;
429 	while (n--) {
430 		if (!xcursor_read_uint(file, p)) {
431 			xcursor_image_destroy(image);
432 			return NULL;
433 		}
434 		p++;
435 	}
436 	return image;
437 }
438 
439 static struct xcursor_images *
xcursor_xc_file_load_images(FILE * file,int size)440 xcursor_xc_file_load_images(FILE *file, int size)
441 {
442 	struct xcursor_file_header *file_header;
443 	uint32_t best_size;
444 	int nsize;
445 	struct xcursor_images *images;
446 	int n;
447 	int toc;
448 
449 	if (!file || size < 0)
450 		return NULL;
451 	file_header = xcursor_read_file_header(file);
452 	if (!file_header)
453 		return NULL;
454 	best_size = xcursor_file_best_size(file_header, (uint32_t) size, &nsize);
455 	if (!best_size) {
456 		xcursor_file_header_destroy(file_header);
457 		return NULL;
458 	}
459 	images = xcursor_images_create(nsize);
460 	if (!images) {
461 		xcursor_file_header_destroy(file_header);
462 		return NULL;
463 	}
464 	for (n = 0; n < nsize; n++) {
465 		toc = xcursor_find_image_toc(file_header, best_size, n);
466 		if (toc < 0)
467 			break;
468 		images->images[images->nimage] = xcursor_read_image(file, file_header,
469 								    toc);
470 		if (!images->images[images->nimage])
471 			break;
472 		images->nimage++;
473 	}
474 	xcursor_file_header_destroy(file_header);
475 	if (images->nimage != nsize) {
476 		xcursor_images_destroy(images);
477 		images = NULL;
478 	}
479 	return images;
480 }
481 
482 /*
483  * From libXcursor/src/library.c
484  */
485 
486 #ifndef ICONDIR
487 #define ICONDIR "/usr/X11R6/lib/X11/icons"
488 #endif
489 
490 #ifndef XCURSORPATH
491 #define XCURSORPATH "~/.icons:/usr/share/icons:/usr/share/pixmaps:~/.cursors:/usr/share/cursors/xorg-x11:"ICONDIR
492 #endif
493 
494 #define XDG_DATA_HOME_FALLBACK "~/.local/share"
495 #define CURSORDIR "/icons"
496 
497 /** Get search path for cursor themes
498  *
499  * This function builds the list of directories to look for cursor
500  * themes in.  The format is PATH-like: directories are separated by
501  * colons.
502  *
503  * The memory block returned by this function is allocated on the heap
504  * and must be freed by the caller.
505  */
506 static char *
xcursor_library_path(void)507 xcursor_library_path(void)
508 {
509 	const char *env_var, *suffix;
510 	char *path;
511 	size_t path_size;
512 
513 	env_var = getenv("XCURSOR_PATH");
514 	if (env_var)
515 		return strdup(env_var);
516 
517 	env_var = getenv("XDG_DATA_HOME");
518 	if (!env_var || env_var[0] != '/')
519 		env_var = XDG_DATA_HOME_FALLBACK;
520 
521 	suffix = CURSORDIR ":" XCURSORPATH;
522 	path_size = strlen(env_var) + strlen(suffix) + 1;
523 	path = malloc(path_size);
524 	if (!path)
525 		return NULL;
526 	snprintf(path, path_size, "%s%s", env_var, suffix);
527 	return path;
528 }
529 
530 static char *
xcursor_build_theme_dir(const char * dir,const char * theme)531 xcursor_build_theme_dir(const char *dir, const char *theme)
532 {
533 	const char *colon;
534 	const char *tcolon;
535 	char *full;
536 	const char *home, *homesep;
537 	int dirlen;
538 	int homelen;
539 	int themelen;
540 	size_t full_size;
541 
542 	if (!dir || !theme)
543 		return NULL;
544 
545 	colon = strchr(dir, ':');
546 	if (!colon)
547 		colon = dir + strlen(dir);
548 
549 	dirlen = colon - dir;
550 
551 	tcolon = strchr(theme, ':');
552 	if (!tcolon)
553 		tcolon = theme + strlen(theme);
554 
555 	themelen = tcolon - theme;
556 
557 	home = "";
558 	homelen = 0;
559 	homesep = "";
560 	if (*dir == '~') {
561 		home = getenv("HOME");
562 		if (!home)
563 			return NULL;
564 		homelen = strlen(home);
565 		homesep = "/";
566 		dir++;
567 		dirlen--;
568 	}
569 
570 	/*
571 	 * add space for any needed directory separators, one per component,
572 	 * and one for the trailing null
573 	 */
574 	full_size = 1 + homelen + 1 + dirlen + 1 + themelen + 1;
575 	full = malloc(full_size);
576 	if (!full)
577 		return NULL;
578 	snprintf(full, full_size, "%s%s%.*s/%.*s", home, homesep,
579 		 dirlen, dir, themelen, theme);
580 	return full;
581 }
582 
583 static char *
xcursor_build_fullname(const char * dir,const char * subdir,const char * file)584 xcursor_build_fullname(const char *dir, const char *subdir, const char *file)
585 {
586 	char *full;
587 	size_t full_size;
588 
589 	if (!dir || !subdir || !file)
590 		return NULL;
591 
592 	full_size = strlen(dir) + 1 + strlen(subdir) + 1 + strlen(file) + 1;
593 	full = malloc(full_size);
594 	if (!full)
595 		return NULL;
596 	snprintf(full, full_size, "%s/%s/%s", dir, subdir, file);
597 	return full;
598 }
599 
600 static const char *
xcursor_next_path(const char * path)601 xcursor_next_path(const char *path)
602 {
603 	char *colon = strchr(path, ':');
604 
605 	if (!colon)
606 		return NULL;
607 	return colon + 1;
608 }
609 
610 static bool
xcursor_white(char c)611 xcursor_white(char c)
612 {
613 	return c == ' ' || c == '\t' || c == '\n';
614 }
615 
616 static bool
xcursor_sep(char c)617 xcursor_sep(char c)
618 {
619 	return c == ';' || c == ',';
620 }
621 
622 static char *
xcursor_theme_inherits(const char * full)623 xcursor_theme_inherits(const char *full)
624 {
625 	char *line = NULL;
626 	size_t line_size = 0;
627 	char *result = NULL;
628 	FILE *f;
629 
630 	if (!full)
631 		return NULL;
632 
633 	f = fopen(full, "r");
634 	if (!f)
635 		return NULL;
636 
637 	while (getline(&line, &line_size, f) >= 0) {
638 		const char *l;
639 		char *r;
640 
641 		if (strncmp(line, "Inherits", 8))
642 			continue;
643 
644 		l = line + 8;
645 		while (*l == ' ')
646 			l++;
647 		if (*l != '=')
648 			continue;
649 		l++;
650 		while (*l == ' ')
651 			l++;
652 		result = malloc(strlen(l) + 1);
653 		if (!result)
654 			break;
655 
656 		r = result;
657 		while (*l) {
658 			while (xcursor_sep(*l) || xcursor_white(*l))
659 				l++;
660 			if (!*l)
661 				break;
662 			if (r != result)
663 				*r++ = ':';
664 			while (*l && !xcursor_white(*l) && !xcursor_sep(*l))
665 				*r++ = *l++;
666 		}
667 		*r++ = '\0';
668 
669 		break;
670 	}
671 
672 	fclose(f);
673 	free(line);
674 
675 	return result;
676 }
677 
678 static void
load_all_cursors_from_dir(const char * path,int size,void (* load_callback)(struct xcursor_images *,void *),void * user_data)679 load_all_cursors_from_dir(const char *path, int size,
680 			  void (*load_callback)(struct xcursor_images *, void *),
681 			  void *user_data)
682 {
683 	FILE *f;
684 	DIR *dir = opendir(path);
685 	struct dirent *ent;
686 	char *full;
687 	struct xcursor_images *images;
688 
689 	if (!dir)
690 		return;
691 
692 	for (ent = readdir(dir); ent; ent = readdir(dir)) {
693 #ifdef _DIRENT_HAVE_D_TYPE
694 		if (ent->d_type != DT_UNKNOWN &&
695 		    ent->d_type != DT_REG &&
696 		    ent->d_type != DT_LNK)
697 			continue;
698 #endif
699 
700 		full = xcursor_build_fullname(path, "", ent->d_name);
701 		if (!full)
702 			continue;
703 
704 		f = fopen(full, "r");
705 		if (!f) {
706 			free(full);
707 			continue;
708 		}
709 
710 		images = xcursor_xc_file_load_images(f, size);
711 
712 		if (images) {
713 			images->name = strdup(ent->d_name);
714 			load_callback(images, user_data);
715 		}
716 
717 		fclose(f);
718 		free(full);
719 	}
720 
721 	closedir(dir);
722 }
723 
724 /** Load all the cursor of a theme
725  *
726  * This function loads all the cursor images of a given theme and its
727  * inherited themes. Each cursor is loaded into an struct xcursor_images object
728  * which is passed to the caller's load callback. If a cursor appears
729  * more than once across all the inherited themes, the load callback
730  * will be called multiple times, with possibly different struct xcursor_images
731  * object which have the same name. The user is expected to destroy the
732  * struct xcursor_images objects passed to the callback with
733  * xcursor_images_destroy().
734  *
735  * \param theme The name of theme that should be loaded
736  * \param size The desired size of the cursor images
737  * \param load_callback A callback function that will be called
738  * for each cursor loaded. The first parameter is the struct xcursor_images
739  * object representing the loaded cursor and the second is a pointer
740  * to data provided by the user.
741  * \param user_data The data that should be passed to the load callback
742  */
743 void
xcursor_load_theme(const char * theme,int size,void (* load_callback)(struct xcursor_images *,void *),void * user_data)744 xcursor_load_theme(const char *theme, int size,
745 		   void (*load_callback)(struct xcursor_images *, void *),
746 		   void *user_data)
747 {
748 	char *full, *dir;
749 	char *inherits = NULL;
750 	const char *path, *i;
751 	char *xcursor_path;
752 
753 	if (!theme)
754 		theme = "default";
755 
756 	xcursor_path = xcursor_library_path();
757 	for (path = xcursor_path;
758 	     path;
759 	     path = xcursor_next_path(path)) {
760 		dir = xcursor_build_theme_dir(path, theme);
761 		if (!dir)
762 			continue;
763 
764 		full = xcursor_build_fullname(dir, "cursors", "");
765 		load_all_cursors_from_dir(full, size, load_callback,
766 					  user_data);
767 		free(full);
768 
769 		if (!inherits) {
770 			full = xcursor_build_fullname(dir, "", "index.theme");
771 			inherits = xcursor_theme_inherits(full);
772 			free(full);
773 		}
774 
775 		free(dir);
776 	}
777 
778 	for (i = inherits; i; i = xcursor_next_path(i))
779 		xcursor_load_theme(i, size, load_callback, user_data);
780 
781 	free(inherits);
782 	free(xcursor_path);
783 }
784