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