Updated package changelog.
[profile/ivi/wayland.git] / cursor / xcursor.c
1 /*
2  * Copyright © 2002 Keith Packard
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of Keith Packard not be used in
9  * advertising or publicity pertaining to distribution of the software without
10  * specific, written prior permission.  Keith Packard makes no
11  * representations about the suitability of this software for any purpose.  It
12  * is provided "as is" without express or implied warranty.
13  *
14  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20  * PERFORMANCE OF THIS SOFTWARE.
21  */
22
23 #include "xcursor.h"
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <dirent.h>
28
29 /*
30  * From libXcursor/include/X11/extensions/Xcursor.h
31  */
32
33 #define XcursorTrue     1
34 #define XcursorFalse    0
35
36 /*
37  * Cursor files start with a header.  The header
38  * contains a magic number, a version number and a
39  * table of contents which has type and offset information
40  * for the remaining tables in the file.
41  *
42  * File minor versions increment for compatible changes
43  * File major versions increment for incompatible changes (never, we hope)
44  *
45  * Chunks of the same type are always upward compatible.  Incompatible
46  * changes are made with new chunk types; the old data can remain under
47  * the old type.  Upward compatible changes can add header data as the
48  * header lengths are specified in the file.
49  *
50  *  File:
51  *      FileHeader
52  *      LISTofChunk
53  *
54  *  FileHeader:
55  *      CARD32          magic       magic number
56  *      CARD32          header      bytes in file header
57  *      CARD32          version     file version
58  *      CARD32          ntoc        number of toc entries
59  *      LISTofFileToc   toc         table of contents
60  *
61  *  FileToc:
62  *      CARD32          type        entry type
63  *      CARD32          subtype     entry subtype (size for images)
64  *      CARD32          position    absolute file position
65  */
66
67 #define XCURSOR_MAGIC   0x72756358  /* "Xcur" LSBFirst */
68
69 /*
70  * Current Xcursor version number.  Will be substituted by configure
71  * from the version in the libXcursor configure.ac file.
72  */
73
74 #define XCURSOR_LIB_MAJOR 1
75 #define XCURSOR_LIB_MINOR 1
76 #define XCURSOR_LIB_REVISION 13
77 #define XCURSOR_LIB_VERSION     ((XCURSOR_LIB_MAJOR * 10000) + \
78                                  (XCURSOR_LIB_MINOR * 100) + \
79                                  (XCURSOR_LIB_REVISION))
80
81 /*
82  * This version number is stored in cursor files; changes to the
83  * file format require updating this version number
84  */
85 #define XCURSOR_FILE_MAJOR      1
86 #define XCURSOR_FILE_MINOR      0
87 #define XCURSOR_FILE_VERSION    ((XCURSOR_FILE_MAJOR << 16) | (XCURSOR_FILE_MINOR))
88 #define XCURSOR_FILE_HEADER_LEN (4 * 4)
89 #define XCURSOR_FILE_TOC_LEN    (3 * 4)
90
91 typedef struct _XcursorFileToc {
92     XcursorUInt     type;       /* chunk type */
93     XcursorUInt     subtype;    /* subtype (size for images) */
94     XcursorUInt     position;   /* absolute position in file */
95 } XcursorFileToc;
96
97 typedef struct _XcursorFileHeader {
98     XcursorUInt     magic;      /* magic number */
99     XcursorUInt     header;     /* byte length of header */
100     XcursorUInt     version;    /* file version number */
101     XcursorUInt     ntoc;       /* number of toc entries */
102     XcursorFileToc  *tocs;      /* table of contents */
103 } XcursorFileHeader;
104
105 /*
106  * The rest of the file is a list of chunks, each tagged by type
107  * and version.
108  *
109  *  Chunk:
110  *      ChunkHeader
111  *      <extra type-specific header fields>
112  *      <type-specific data>
113  *
114  *  ChunkHeader:
115  *      CARD32      header      bytes in chunk header + type header
116  *      CARD32      type        chunk type
117  *      CARD32      subtype     chunk subtype
118  *      CARD32      version     chunk type version
119  */
120
121 #define XCURSOR_CHUNK_HEADER_LEN    (4 * 4)
122
123 typedef struct _XcursorChunkHeader {
124     XcursorUInt     header;     /* bytes in chunk header */
125     XcursorUInt     type;       /* chunk type */
126     XcursorUInt     subtype;    /* chunk subtype (size for images) */
127     XcursorUInt     version;    /* version of this type */
128 } XcursorChunkHeader;
129
130 /*
131  * Here's a list of the known chunk types
132  */
133
134 /*
135  * Comments consist of a 4-byte length field followed by
136  * UTF-8 encoded text
137  *
138  *  Comment:
139  *      ChunkHeader header      chunk header
140  *      CARD32      length      bytes in text
141  *      LISTofCARD8 text        UTF-8 encoded text
142  */
143
144 #define XCURSOR_COMMENT_TYPE        0xfffe0001
145 #define XCURSOR_COMMENT_VERSION     1
146 #define XCURSOR_COMMENT_HEADER_LEN  (XCURSOR_CHUNK_HEADER_LEN + (1 *4))
147 #define XCURSOR_COMMENT_COPYRIGHT   1
148 #define XCURSOR_COMMENT_LICENSE     2
149 #define XCURSOR_COMMENT_OTHER       3
150 #define XCURSOR_COMMENT_MAX_LEN     0x100000
151
152 typedef struct _XcursorComment {
153     XcursorUInt     version;
154     XcursorUInt     comment_type;
155     char            *comment;
156 } XcursorComment;
157
158 /*
159  * Each cursor image occupies a separate image chunk.
160  * The length of the image header follows the chunk header
161  * so that future versions can extend the header without
162  * breaking older applications
163  *
164  *  Image:
165  *      ChunkHeader     header  chunk header
166  *      CARD32          width   actual width
167  *      CARD32          height  actual height
168  *      CARD32          xhot    hot spot x
169  *      CARD32          yhot    hot spot y
170  *      CARD32          delay   animation delay
171  *      LISTofCARD32    pixels  ARGB pixels
172  */
173
174 #define XCURSOR_IMAGE_TYPE          0xfffd0002
175 #define XCURSOR_IMAGE_VERSION       1
176 #define XCURSOR_IMAGE_HEADER_LEN    (XCURSOR_CHUNK_HEADER_LEN + (5*4))
177 #define XCURSOR_IMAGE_MAX_SIZE      0x7fff      /* 32767x32767 max cursor size */
178
179 typedef struct _XcursorFile XcursorFile;
180
181 struct _XcursorFile {
182     void    *closure;
183     int     (*read)  (XcursorFile *file, unsigned char *buf, int len);
184     int     (*write) (XcursorFile *file, unsigned char *buf, int len);
185     int     (*seek)  (XcursorFile *file, long offset, int whence);
186 };
187
188 typedef struct _XcursorComments {
189     int             ncomment;   /* number of comments */
190     XcursorComment  **comments; /* array of XcursorComment pointers */
191 } XcursorComments;
192
193 /*
194  * From libXcursor/src/file.c
195  */
196
197 static XcursorImage *
198 XcursorImageCreate (int width, int height)
199 {
200     XcursorImage    *image;
201
202     image = malloc (sizeof (XcursorImage) +
203                     width * height * sizeof (XcursorPixel));
204     if (!image)
205         return NULL;
206     image->version = XCURSOR_IMAGE_VERSION;
207     image->pixels = (XcursorPixel *) (image + 1);
208     image->size = width > height ? width : height;
209     image->width = width;
210     image->height = height;
211     image->delay = 0;
212     return image;
213 }
214
215 static void
216 XcursorImageDestroy (XcursorImage *image)
217 {
218     free (image);
219 }
220
221 static XcursorImages *
222 XcursorImagesCreate (int size)
223 {
224     XcursorImages   *images;
225
226     images = malloc (sizeof (XcursorImages) +
227                      size * sizeof (XcursorImage *));
228     if (!images)
229         return NULL;
230     images->nimage = 0;
231     images->images = (XcursorImage **) (images + 1);
232     images->name = NULL;
233     return images;
234 }
235
236 void
237 XcursorImagesDestroy (XcursorImages *images)
238 {
239     int n;
240
241     if (!images)
242         return;
243
244     for (n = 0; n < images->nimage; n++)
245         XcursorImageDestroy (images->images[n]);
246     if (images->name)
247         free (images->name);
248     free (images);
249 }
250
251 static void
252 XcursorImagesSetName (XcursorImages *images, const char *name)
253 {
254     char    *new;
255
256     if (!images || !name)
257         return;
258
259     new = malloc (strlen (name) + 1);
260
261     if (!new)
262         return;
263
264     strcpy (new, name);
265     if (images->name)
266         free (images->name);
267     images->name = new;
268 }
269
270 static XcursorBool
271 _XcursorReadUInt (XcursorFile *file, XcursorUInt *u)
272 {
273     unsigned char   bytes[4];
274
275     if (!file || !u)
276         return XcursorFalse;
277
278     if ((*file->read) (file, bytes, 4) != 4)
279         return XcursorFalse;
280     *u = ((bytes[0] << 0) |
281           (bytes[1] << 8) |
282           (bytes[2] << 16) |
283           (bytes[3] << 24));
284     return XcursorTrue;
285 }
286
287 static void
288 _XcursorFileHeaderDestroy (XcursorFileHeader *fileHeader)
289 {
290     free (fileHeader);
291 }
292
293 static XcursorFileHeader *
294 _XcursorFileHeaderCreate (int ntoc)
295 {
296     XcursorFileHeader   *fileHeader;
297
298     if (ntoc > 0x10000)
299         return NULL;
300     fileHeader = malloc (sizeof (XcursorFileHeader) +
301                          ntoc * sizeof (XcursorFileToc));
302     if (!fileHeader)
303         return NULL;
304     fileHeader->magic = XCURSOR_MAGIC;
305     fileHeader->header = XCURSOR_FILE_HEADER_LEN;
306     fileHeader->version = XCURSOR_FILE_VERSION;
307     fileHeader->ntoc = ntoc;
308     fileHeader->tocs = (XcursorFileToc *) (fileHeader + 1);
309     return fileHeader;
310 }
311
312 static XcursorFileHeader *
313 _XcursorReadFileHeader (XcursorFile *file)
314 {
315     XcursorFileHeader   head, *fileHeader;
316     XcursorUInt         skip;
317     unsigned int        n;
318
319     if (!file)
320         return NULL;
321
322     if (!_XcursorReadUInt (file, &head.magic))
323         return NULL;
324     if (head.magic != XCURSOR_MAGIC)
325         return NULL;
326     if (!_XcursorReadUInt (file, &head.header))
327         return NULL;
328     if (!_XcursorReadUInt (file, &head.version))
329         return NULL;
330     if (!_XcursorReadUInt (file, &head.ntoc))
331         return NULL;
332     skip = head.header - XCURSOR_FILE_HEADER_LEN;
333     if (skip)
334         if ((*file->seek) (file, skip, SEEK_CUR) == EOF)
335             return NULL;
336     fileHeader = _XcursorFileHeaderCreate (head.ntoc);
337     if (!fileHeader)
338         return NULL;
339     fileHeader->magic = head.magic;
340     fileHeader->header = head.header;
341     fileHeader->version = head.version;
342     fileHeader->ntoc = head.ntoc;
343     for (n = 0; n < fileHeader->ntoc; n++)
344     {
345         if (!_XcursorReadUInt (file, &fileHeader->tocs[n].type))
346             break;
347         if (!_XcursorReadUInt (file, &fileHeader->tocs[n].subtype))
348             break;
349         if (!_XcursorReadUInt (file, &fileHeader->tocs[n].position))
350             break;
351     }
352     if (n != fileHeader->ntoc)
353     {
354         _XcursorFileHeaderDestroy (fileHeader);
355         return NULL;
356     }
357     return fileHeader;
358 }
359
360 static XcursorBool
361 _XcursorSeekToToc (XcursorFile          *file,
362                    XcursorFileHeader    *fileHeader,
363                    int                  toc)
364 {
365     if (!file || !fileHeader || \
366         (*file->seek) (file, fileHeader->tocs[toc].position, SEEK_SET) == EOF)
367         return XcursorFalse;
368     return XcursorTrue;
369 }
370
371 static XcursorBool
372 _XcursorFileReadChunkHeader (XcursorFile        *file,
373                              XcursorFileHeader  *fileHeader,
374                              int                toc,
375                              XcursorChunkHeader *chunkHeader)
376 {
377     if (!file || !fileHeader || !chunkHeader)
378         return XcursorFalse;
379     if (!_XcursorSeekToToc (file, fileHeader, toc))
380         return XcursorFalse;
381     if (!_XcursorReadUInt (file, &chunkHeader->header))
382         return XcursorFalse;
383     if (!_XcursorReadUInt (file, &chunkHeader->type))
384         return XcursorFalse;
385     if (!_XcursorReadUInt (file, &chunkHeader->subtype))
386         return XcursorFalse;
387     if (!_XcursorReadUInt (file, &chunkHeader->version))
388         return XcursorFalse;
389     /* sanity check */
390     if (chunkHeader->type != fileHeader->tocs[toc].type ||
391         chunkHeader->subtype != fileHeader->tocs[toc].subtype)
392         return XcursorFalse;
393     return XcursorTrue;
394 }
395
396 #define dist(a,b)   ((a) > (b) ? (a) - (b) : (b) - (a))
397
398 static XcursorDim
399 _XcursorFindBestSize (XcursorFileHeader *fileHeader,
400                       XcursorDim        size,
401                       int               *nsizesp)
402 {
403     unsigned int n;
404     int         nsizes = 0;
405     XcursorDim  bestSize = 0;
406     XcursorDim  thisSize;
407
408     if (!fileHeader || !nsizesp)
409         return 0;
410
411     for (n = 0; n < fileHeader->ntoc; n++)
412     {
413         if (fileHeader->tocs[n].type != XCURSOR_IMAGE_TYPE)
414             continue;
415         thisSize = fileHeader->tocs[n].subtype;
416         if (!bestSize || dist (thisSize, size) < dist (bestSize, size))
417         {
418             bestSize = thisSize;
419             nsizes = 1;
420         }
421         else if (thisSize == bestSize)
422             nsizes++;
423     }
424     *nsizesp = nsizes;
425     return bestSize;
426 }
427
428 static int
429 _XcursorFindImageToc (XcursorFileHeader *fileHeader,
430                       XcursorDim        size,
431                       int               count)
432 {
433     unsigned int        toc;
434     XcursorDim          thisSize;
435
436     if (!fileHeader)
437         return 0;
438
439     for (toc = 0; toc < fileHeader->ntoc; toc++)
440     {
441         if (fileHeader->tocs[toc].type != XCURSOR_IMAGE_TYPE)
442             continue;
443         thisSize = fileHeader->tocs[toc].subtype;
444         if (thisSize != size)
445             continue;
446         if (!count)
447             break;
448         count--;
449     }
450     if (toc == fileHeader->ntoc)
451         return -1;
452     return toc;
453 }
454
455 static XcursorImage *
456 _XcursorReadImage (XcursorFile          *file,
457                    XcursorFileHeader    *fileHeader,
458                    int                  toc)
459 {
460     XcursorChunkHeader  chunkHeader;
461     XcursorImage        head;
462     XcursorImage        *image;
463     int                 n;
464     XcursorPixel        *p;
465
466     if (!file || !fileHeader)
467         return NULL;
468
469     if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader))
470         return NULL;
471     if (!_XcursorReadUInt (file, &head.width))
472         return NULL;
473     if (!_XcursorReadUInt (file, &head.height))
474         return NULL;
475     if (!_XcursorReadUInt (file, &head.xhot))
476         return NULL;
477     if (!_XcursorReadUInt (file, &head.yhot))
478         return NULL;
479     if (!_XcursorReadUInt (file, &head.delay))
480         return NULL;
481     /* sanity check data */
482     if (head.width >= 0x10000 || head.height > 0x10000)
483         return NULL;
484     if (head.width == 0 || head.height == 0)
485         return NULL;
486     if (head.xhot > head.width || head.yhot > head.height)
487         return NULL;
488
489     /* Create the image and initialize it */
490     image = XcursorImageCreate (head.width, head.height);
491     if (chunkHeader.version < image->version)
492         image->version = chunkHeader.version;
493     image->size = chunkHeader.subtype;
494     image->xhot = head.xhot;
495     image->yhot = head.yhot;
496     image->delay = head.delay;
497     n = image->width * image->height;
498     p = image->pixels;
499     while (n--)
500     {
501         if (!_XcursorReadUInt (file, p))
502         {
503             XcursorImageDestroy (image);
504             return NULL;
505         }
506         p++;
507     }
508     return image;
509 }
510
511 static XcursorImages *
512 XcursorXcFileLoadImages (XcursorFile *file, int size)
513 {
514     XcursorFileHeader   *fileHeader;
515     XcursorDim          bestSize;
516     int                 nsize;
517     XcursorImages       *images;
518     int                 n;
519     int                 toc;
520
521     if (!file || size < 0)
522         return NULL;
523     fileHeader = _XcursorReadFileHeader (file);
524     if (!fileHeader)
525         return NULL;
526     bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize);
527     if (!bestSize)
528     {
529         _XcursorFileHeaderDestroy (fileHeader);
530         return NULL;
531     }
532     images = XcursorImagesCreate (nsize);
533     if (!images)
534     {
535         _XcursorFileHeaderDestroy (fileHeader);
536         return NULL;
537     }
538     for (n = 0; n < nsize; n++)
539     {
540         toc = _XcursorFindImageToc (fileHeader, bestSize, n);
541         if (toc < 0)
542             break;
543         images->images[images->nimage] = _XcursorReadImage (file, fileHeader,
544                                                             toc);
545         if (!images->images[images->nimage])
546             break;
547         images->nimage++;
548     }
549     _XcursorFileHeaderDestroy (fileHeader);
550     if (images->nimage != nsize)
551     {
552         XcursorImagesDestroy (images);
553         images = NULL;
554     }
555     return images;
556 }
557
558 static int
559 _XcursorStdioFileRead (XcursorFile *file, unsigned char *buf, int len)
560 {
561     FILE    *f = file->closure;
562     return fread (buf, 1, len, f);
563 }
564
565 static int
566 _XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len)
567 {
568     FILE    *f = file->closure;
569     return fwrite (buf, 1, len, f);
570 }
571
572 static int
573 _XcursorStdioFileSeek (XcursorFile *file, long offset, int whence)
574 {
575     FILE    *f = file->closure;
576     return fseek (f, offset, whence);
577 }
578
579 static void
580 _XcursorStdioFileInitialize (FILE *stdfile, XcursorFile *file)
581 {
582     file->closure = stdfile;
583     file->read = _XcursorStdioFileRead;
584     file->write = _XcursorStdioFileWrite;
585     file->seek = _XcursorStdioFileSeek;
586 }
587
588 static XcursorImages *
589 XcursorFileLoadImages (FILE *file, int size)
590 {
591     XcursorFile f;
592
593     if (!file)
594         return NULL;
595
596     _XcursorStdioFileInitialize (file, &f);
597     return XcursorXcFileLoadImages (&f, size);
598 }
599
600 /*
601  * From libXcursor/src/library.c
602  */
603
604 #ifndef ICONDIR
605 #define ICONDIR "/usr/X11R6/lib/X11/icons"
606 #endif
607
608 #ifndef XCURSORPATH
609 #define XCURSORPATH "~/.icons:/usr/share/icons:/usr/share/pixmaps:~/.cursors:/usr/share/cursors/xorg-x11:"ICONDIR
610 #endif
611
612 static const char *
613 XcursorLibraryPath (void)
614 {
615     static const char   *path;
616
617     if (!path)
618     {
619         path = getenv ("XCURSOR_PATH");
620         if (!path)
621             path = XCURSORPATH;
622     }
623     return path;
624 }
625
626 static  void
627 _XcursorAddPathElt (char *path, const char *elt, int len)
628 {
629     int     pathlen = strlen (path);
630
631     /* append / if the path doesn't currently have one */
632     if (path[0] == '\0' || path[pathlen - 1] != '/')
633     {
634         strcat (path, "/");
635         pathlen++;
636     }
637     if (len == -1)
638         len = strlen (elt);
639     /* strip leading slashes */
640     while (len && elt[0] == '/')
641     {
642         elt++;
643         len--;
644     }
645     strncpy (path + pathlen, elt, len);
646     path[pathlen + len] = '\0';
647 }
648
649 static char *
650 _XcursorBuildThemeDir (const char *dir, const char *theme)
651 {
652     const char      *colon;
653     const char      *tcolon;
654     char            *full;
655     char            *home;
656     int             dirlen;
657     int             homelen;
658     int             themelen;
659     int             len;
660
661     if (!dir || !theme)
662         return NULL;
663
664     colon = strchr (dir, ':');
665     if (!colon)
666         colon = dir + strlen (dir);
667
668     dirlen = colon - dir;
669
670     tcolon = strchr (theme, ':');
671     if (!tcolon)
672         tcolon = theme + strlen (theme);
673
674     themelen = tcolon - theme;
675
676     home = NULL;
677     homelen = 0;
678     if (*dir == '~')
679     {
680         home = getenv ("HOME");
681         if (!home)
682             return NULL;
683         homelen = strlen (home);
684         dir++;
685         dirlen--;
686     }
687
688     /*
689      * add space for any needed directory separators, one per component,
690      * and one for the trailing null
691      */
692     len = 1 + homelen + 1 + dirlen + 1 + themelen + 1;
693
694     full = malloc (len);
695     if (!full)
696         return NULL;
697     full[0] = '\0';
698
699     if (home)
700         _XcursorAddPathElt (full, home, -1);
701     _XcursorAddPathElt (full, dir, dirlen);
702     _XcursorAddPathElt (full, theme, themelen);
703     return full;
704 }
705
706 static char *
707 _XcursorBuildFullname (const char *dir, const char *subdir, const char *file)
708 {
709     char    *full;
710
711     if (!dir || !subdir || !file)
712         return NULL;
713
714     full = malloc (strlen (dir) + 1 + strlen (subdir) + 1 + strlen (file) + 1);
715     if (!full)
716         return NULL;
717     full[0] = '\0';
718     _XcursorAddPathElt (full, dir, -1);
719     _XcursorAddPathElt (full, subdir, -1);
720     _XcursorAddPathElt (full, file, -1);
721     return full;
722 }
723
724 static const char *
725 _XcursorNextPath (const char *path)
726 {
727     char    *colon = strchr (path, ':');
728
729     if (!colon)
730         return NULL;
731     return colon + 1;
732 }
733
734 #define XcursorWhite(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
735 #define XcursorSep(c) ((c) == ';' || (c) == ',')
736
737 static char *
738 _XcursorThemeInherits (const char *full)
739 {
740     char    line[8192];
741     char    *result = NULL;
742     FILE    *f;
743
744     if (!full)
745         return NULL;
746
747     f = fopen (full, "r");
748     if (f)
749     {
750         while (fgets (line, sizeof (line), f))
751         {
752             if (!strncmp (line, "Inherits", 8))
753             {
754                 char    *l = line + 8;
755                 char    *r;
756                 while (*l == ' ') l++;
757                 if (*l != '=') continue;
758                 l++;
759                 while (*l == ' ') l++;
760                 result = malloc (strlen (l) + 1);
761                 if (result)
762                 {
763                     r = result;
764                     while (*l)
765                     {
766                         while (XcursorSep(*l) || XcursorWhite (*l)) l++;
767                         if (!*l)
768                             break;
769                         if (r != result)
770                             *r++ = ':';
771                         while (*l && !XcursorWhite(*l) &&
772                                !XcursorSep(*l))
773                             *r++ = *l++;
774                     }
775                     *r++ = '\0';
776                 }
777                 break;
778             }
779         }
780         fclose (f);
781     }
782     return result;
783 }
784
785 static FILE *
786 XcursorScanTheme (const char *theme, const char *name)
787 {
788     FILE        *f = NULL;
789     char        *full;
790     char        *dir;
791     const char  *path;
792     char        *inherits = NULL;
793     const char  *i;
794
795     if (!theme || !name)
796         return NULL;
797
798     /*
799      * Scan this theme
800      */
801     for (path = XcursorLibraryPath ();
802          path && f == NULL;
803          path = _XcursorNextPath (path))
804     {
805         dir = _XcursorBuildThemeDir (path, theme);
806         if (dir)
807         {
808             full = _XcursorBuildFullname (dir, "cursors", name);
809             if (full)
810             {
811                 f = fopen (full, "r");
812                 free (full);
813             }
814             if (!f && !inherits)
815             {
816                 full = _XcursorBuildFullname (dir, "", "index.theme");
817                 if (full)
818                 {
819                     inherits = _XcursorThemeInherits (full);
820                     free (full);
821                 }
822             }
823             free (dir);
824         }
825     }
826     /*
827      * Recurse to scan inherited themes
828      */
829     for (i = inherits; i && f == NULL; i = _XcursorNextPath (i))
830         f = XcursorScanTheme (i, name);
831     if (inherits != NULL)
832         free (inherits);
833     return f;
834 }
835
836 XcursorImages *
837 XcursorLibraryLoadImages (const char *file, const char *theme, int size)
838 {
839     FILE            *f = NULL;
840     XcursorImages   *images = NULL;
841
842     if (!file)
843         return NULL;
844
845     if (theme)
846         f = XcursorScanTheme (theme, file);
847     if (!f)
848         f = XcursorScanTheme ("default", file);
849     if (f)
850     {
851         images = XcursorFileLoadImages (f, size);
852         if (images)
853             XcursorImagesSetName (images, file);
854         fclose (f);
855     }
856     return images;
857 }
858
859 static void
860 load_all_cursors_from_dir(const char *path, int size,
861                           void (*load_callback)(XcursorImages *, void *),
862                           void *user_data)
863 {
864         FILE *f;
865         DIR *dir = opendir(path);
866         struct dirent *ent;
867         char *full;
868         XcursorImages *images;
869
870         if (!dir)
871                 return;
872
873         for(ent = readdir(dir); ent; ent = readdir(dir)) {
874 #ifdef _DIRENT_HAVE_D_TYPE
875                 if (ent->d_type != DT_UNKNOWN &&
876                     (ent->d_type != DT_REG && ent->d_type != DT_LNK))
877                         continue;
878 #endif
879
880                 full = _XcursorBuildFullname(path, "", ent->d_name);
881                 if (!full)
882                         continue;
883
884                 f = fopen(full, "r");
885                 if (!f) {
886                         free(full);
887                         continue;
888                 }
889
890                 images = XcursorFileLoadImages(f, size);
891
892                 if (images) {
893                         XcursorImagesSetName(images, ent->d_name);
894                         load_callback(images, user_data);
895                 }
896
897                 fclose (f);
898                 free(full);
899         }
900
901         closedir(dir);
902 }
903
904 /** Load all the cursor of a theme
905  *
906  * This function loads all the cursor images of a given theme and its
907  * inherited themes. Each cursor is loaded into an XcursorImages object
908  * which is passed to the caller's load callback. If a cursor appears
909  * more than once across all the inherited themes, the load callback
910  * will be called multiple times, with possibly different XcursorImages
911  * object which have the same name. The user is expected to destroy the
912  * XcursorImages objects passed to the callback with
913  * XcursorImagesDestroy().
914  *
915  * \param theme The name of theme that should be loaded
916  * \param size The desired size of the cursor images
917  * \param load_callback A callback function that will be called
918  * for each cursor loaded. The first parameter is the XcursorImages
919  * object representing the loaded cursor and the second is a pointer
920  * to data provided by the user.
921  * \param user_data The data that should be passed to the load callback
922  */
923 void
924 xcursor_load_theme(const char *theme, int size,
925                     void (*load_callback)(XcursorImages *, void *),
926                     void *user_data)
927 {
928         char *full, *dir;
929         char *inherits = NULL;
930         const char *path, *i;
931
932         if (!theme)
933                 theme = "default";
934
935         for (path = XcursorLibraryPath();
936              path;
937              path = _XcursorNextPath(path)) {
938                 dir = _XcursorBuildThemeDir(path, theme);
939                 if (!dir)
940                         continue;
941
942                 full = _XcursorBuildFullname(dir, "cursors", "");
943
944                 if (full) {
945                         load_all_cursors_from_dir(full, size, load_callback,
946                                                   user_data);
947                         free(full);
948                 }
949
950                 if (!inherits) {
951                         full = _XcursorBuildFullname(dir, "", "index.theme");
952                         if (full) {
953                                 inherits = _XcursorThemeInherits(full);
954                                 free(full);
955                         }
956                 }
957
958                 free(dir);
959         }
960
961         for (i = inherits; i; i = _XcursorNextPath(i))
962                 xcursor_load_theme(i, size, load_callback, user_data);
963
964         if (inherits)
965                 free(inherits);
966 }