2 * Copyright © 2002 Keith Packard
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.
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.
29 * From libXcursor/include/X11/extensions/Xcursor.h
33 #define XcursorFalse 0
36 * Cursor files start with a header. The header
37 * contains a magic number, a version number and a
38 * table of contents which has type and offset information
39 * for the remaining tables in the file.
41 * File minor versions increment for compatible changes
42 * File major versions increment for incompatible changes (never, we hope)
44 * Chunks of the same type are always upward compatible. Incompatible
45 * changes are made with new chunk types; the old data can remain under
46 * the old type. Upward compatible changes can add header data as the
47 * header lengths are specified in the file.
54 * CARD32 magic magic number
55 * CARD32 header bytes in file header
56 * CARD32 version file version
57 * CARD32 ntoc number of toc entries
58 * LISTofFileToc toc table of contents
61 * CARD32 type entry type
62 * CARD32 subtype entry subtype (size for images)
63 * CARD32 position absolute file position
66 #define XCURSOR_MAGIC 0x72756358 /* "Xcur" LSBFirst */
69 * Current Xcursor version number. Will be substituted by configure
70 * from the version in the libXcursor configure.ac file.
73 #define XCURSOR_LIB_MAJOR 1
74 #define XCURSOR_LIB_MINOR 1
75 #define XCURSOR_LIB_REVISION 13
76 #define XCURSOR_LIB_VERSION ((XCURSOR_LIB_MAJOR * 10000) + \
77 (XCURSOR_LIB_MINOR * 100) + \
78 (XCURSOR_LIB_REVISION))
81 * This version number is stored in cursor files; changes to the
82 * file format require updating this version number
84 #define XCURSOR_FILE_MAJOR 1
85 #define XCURSOR_FILE_MINOR 0
86 #define XCURSOR_FILE_VERSION ((XCURSOR_FILE_MAJOR << 16) | (XCURSOR_FILE_MINOR))
87 #define XCURSOR_FILE_HEADER_LEN (4 * 4)
88 #define XCURSOR_FILE_TOC_LEN (3 * 4)
90 typedef struct _XcursorFileToc {
91 XcursorUInt type; /* chunk type */
92 XcursorUInt subtype; /* subtype (size for images) */
93 XcursorUInt position; /* absolute position in file */
96 typedef struct _XcursorFileHeader {
97 XcursorUInt magic; /* magic number */
98 XcursorUInt header; /* byte length of header */
99 XcursorUInt version; /* file version number */
100 XcursorUInt ntoc; /* number of toc entries */
101 XcursorFileToc *tocs; /* table of contents */
105 * The rest of the file is a list of chunks, each tagged by type
110 * <extra type-specific header fields>
111 * <type-specific data>
114 * CARD32 header bytes in chunk header + type header
115 * CARD32 type chunk type
116 * CARD32 subtype chunk subtype
117 * CARD32 version chunk type version
120 #define XCURSOR_CHUNK_HEADER_LEN (4 * 4)
122 typedef struct _XcursorChunkHeader {
123 XcursorUInt header; /* bytes in chunk header */
124 XcursorUInt type; /* chunk type */
125 XcursorUInt subtype; /* chunk subtype (size for images) */
126 XcursorUInt version; /* version of this type */
127 } XcursorChunkHeader;
130 * Here's a list of the known chunk types
134 * Comments consist of a 4-byte length field followed by
138 * ChunkHeader header chunk header
139 * CARD32 length bytes in text
140 * LISTofCARD8 text UTF-8 encoded text
143 #define XCURSOR_COMMENT_TYPE 0xfffe0001
144 #define XCURSOR_COMMENT_VERSION 1
145 #define XCURSOR_COMMENT_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (1 *4))
146 #define XCURSOR_COMMENT_COPYRIGHT 1
147 #define XCURSOR_COMMENT_LICENSE 2
148 #define XCURSOR_COMMENT_OTHER 3
149 #define XCURSOR_COMMENT_MAX_LEN 0x100000
151 typedef struct _XcursorComment {
153 XcursorUInt comment_type;
158 * Each cursor image occupies a separate image chunk.
159 * The length of the image header follows the chunk header
160 * so that future versions can extend the header without
161 * breaking older applications
164 * ChunkHeader header chunk header
165 * CARD32 width actual width
166 * CARD32 height actual height
167 * CARD32 xhot hot spot x
168 * CARD32 yhot hot spot y
169 * CARD32 delay animation delay
170 * LISTofCARD32 pixels ARGB pixels
173 #define XCURSOR_IMAGE_TYPE 0xfffd0002
174 #define XCURSOR_IMAGE_VERSION 1
175 #define XCURSOR_IMAGE_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (5*4))
176 #define XCURSOR_IMAGE_MAX_SIZE 0x7fff /* 32767x32767 max cursor size */
178 typedef struct _XcursorFile XcursorFile;
180 struct _XcursorFile {
182 int (*read) (XcursorFile *file, unsigned char *buf, int len);
183 int (*write) (XcursorFile *file, unsigned char *buf, int len);
184 int (*seek) (XcursorFile *file, long offset, int whence);
187 typedef struct _XcursorComments {
188 int ncomment; /* number of comments */
189 XcursorComment **comments; /* array of XcursorComment pointers */
193 * From libXcursor/src/file.c
196 static XcursorImage *
197 XcursorImageCreate (int width, int height)
201 image = malloc (sizeof (XcursorImage) +
202 width * height * sizeof (XcursorPixel));
205 image->version = XCURSOR_IMAGE_VERSION;
206 image->pixels = (XcursorPixel *) (image + 1);
207 image->size = width > height ? width : height;
208 image->width = width;
209 image->height = height;
215 XcursorImageDestroy (XcursorImage *image)
220 static XcursorImages *
221 XcursorImagesCreate (int size)
223 XcursorImages *images;
225 images = malloc (sizeof (XcursorImages) +
226 size * sizeof (XcursorImage *));
230 images->images = (XcursorImage **) (images + 1);
236 XcursorImagesDestroy (XcursorImages *images)
243 for (n = 0; n < images->nimage; n++)
244 XcursorImageDestroy (images->images[n]);
251 XcursorImagesSetName (XcursorImages *images, const char *name)
255 if (!images || !name)
258 new = malloc (strlen (name) + 1);
270 _XcursorReadUInt (XcursorFile *file, XcursorUInt *u)
272 unsigned char bytes[4];
277 if ((*file->read) (file, bytes, 4) != 4)
279 *u = ((bytes[0] << 0) |
287 _XcursorFileHeaderDestroy (XcursorFileHeader *fileHeader)
292 static XcursorFileHeader *
293 _XcursorFileHeaderCreate (int ntoc)
295 XcursorFileHeader *fileHeader;
299 fileHeader = malloc (sizeof (XcursorFileHeader) +
300 ntoc * sizeof (XcursorFileToc));
303 fileHeader->magic = XCURSOR_MAGIC;
304 fileHeader->header = XCURSOR_FILE_HEADER_LEN;
305 fileHeader->version = XCURSOR_FILE_VERSION;
306 fileHeader->ntoc = ntoc;
307 fileHeader->tocs = (XcursorFileToc *) (fileHeader + 1);
311 static XcursorFileHeader *
312 _XcursorReadFileHeader (XcursorFile *file)
314 XcursorFileHeader head, *fileHeader;
321 if (!_XcursorReadUInt (file, &head.magic))
323 if (head.magic != XCURSOR_MAGIC)
325 if (!_XcursorReadUInt (file, &head.header))
327 if (!_XcursorReadUInt (file, &head.version))
329 if (!_XcursorReadUInt (file, &head.ntoc))
331 skip = head.header - XCURSOR_FILE_HEADER_LEN;
333 if ((*file->seek) (file, skip, SEEK_CUR) == EOF)
335 fileHeader = _XcursorFileHeaderCreate (head.ntoc);
338 fileHeader->magic = head.magic;
339 fileHeader->header = head.header;
340 fileHeader->version = head.version;
341 fileHeader->ntoc = head.ntoc;
342 for (n = 0; n < fileHeader->ntoc; n++)
344 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].type))
346 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].subtype))
348 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].position))
351 if (n != fileHeader->ntoc)
353 _XcursorFileHeaderDestroy (fileHeader);
360 _XcursorSeekToToc (XcursorFile *file,
361 XcursorFileHeader *fileHeader,
364 if (!file || !fileHeader || \
365 (*file->seek) (file, fileHeader->tocs[toc].position, SEEK_SET) == EOF)
371 _XcursorFileReadChunkHeader (XcursorFile *file,
372 XcursorFileHeader *fileHeader,
374 XcursorChunkHeader *chunkHeader)
376 if (!file || !fileHeader || !chunkHeader)
378 if (!_XcursorSeekToToc (file, fileHeader, toc))
380 if (!_XcursorReadUInt (file, &chunkHeader->header))
382 if (!_XcursorReadUInt (file, &chunkHeader->type))
384 if (!_XcursorReadUInt (file, &chunkHeader->subtype))
386 if (!_XcursorReadUInt (file, &chunkHeader->version))
389 if (chunkHeader->type != fileHeader->tocs[toc].type ||
390 chunkHeader->subtype != fileHeader->tocs[toc].subtype)
395 #define dist(a,b) ((a) > (b) ? (a) - (b) : (b) - (a))
398 _XcursorFindBestSize (XcursorFileHeader *fileHeader,
404 XcursorDim bestSize = 0;
407 if (!fileHeader || !nsizesp)
410 for (n = 0; n < fileHeader->ntoc; n++)
412 if (fileHeader->tocs[n].type != XCURSOR_IMAGE_TYPE)
414 thisSize = fileHeader->tocs[n].subtype;
415 if (!bestSize || dist (thisSize, size) < dist (bestSize, size))
420 else if (thisSize == bestSize)
428 _XcursorFindImageToc (XcursorFileHeader *fileHeader,
438 for (toc = 0; toc < fileHeader->ntoc; toc++)
440 if (fileHeader->tocs[toc].type != XCURSOR_IMAGE_TYPE)
442 thisSize = fileHeader->tocs[toc].subtype;
443 if (thisSize != size)
449 if (toc == fileHeader->ntoc)
454 static XcursorImage *
455 _XcursorReadImage (XcursorFile *file,
456 XcursorFileHeader *fileHeader,
459 XcursorChunkHeader chunkHeader;
465 if (!file || !fileHeader)
468 if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader))
470 if (!_XcursorReadUInt (file, &head.width))
472 if (!_XcursorReadUInt (file, &head.height))
474 if (!_XcursorReadUInt (file, &head.xhot))
476 if (!_XcursorReadUInt (file, &head.yhot))
478 if (!_XcursorReadUInt (file, &head.delay))
480 /* sanity check data */
481 if (head.width >= 0x10000 || head.height > 0x10000)
483 if (head.width == 0 || head.height == 0)
485 if (head.xhot > head.width || head.yhot > head.height)
488 /* Create the image and initialize it */
489 image = XcursorImageCreate (head.width, head.height);
490 if (chunkHeader.version < image->version)
491 image->version = chunkHeader.version;
492 image->size = chunkHeader.subtype;
493 image->xhot = head.xhot;
494 image->yhot = head.yhot;
495 image->delay = head.delay;
496 n = image->width * image->height;
500 if (!_XcursorReadUInt (file, p))
502 XcursorImageDestroy (image);
510 static XcursorImages *
511 XcursorXcFileLoadImages (XcursorFile *file, int size)
513 XcursorFileHeader *fileHeader;
516 XcursorImages *images;
520 if (!file || size < 0)
522 fileHeader = _XcursorReadFileHeader (file);
525 bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize);
528 _XcursorFileHeaderDestroy (fileHeader);
531 images = XcursorImagesCreate (nsize);
534 _XcursorFileHeaderDestroy (fileHeader);
537 for (n = 0; n < nsize; n++)
539 toc = _XcursorFindImageToc (fileHeader, bestSize, n);
542 images->images[images->nimage] = _XcursorReadImage (file, fileHeader,
544 if (!images->images[images->nimage])
548 _XcursorFileHeaderDestroy (fileHeader);
549 if (images->nimage != nsize)
551 XcursorImagesDestroy (images);
558 _XcursorStdioFileRead (XcursorFile *file, unsigned char *buf, int len)
560 FILE *f = file->closure;
561 return fread (buf, 1, len, f);
565 _XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len)
567 FILE *f = file->closure;
568 return fwrite (buf, 1, len, f);
572 _XcursorStdioFileSeek (XcursorFile *file, long offset, int whence)
574 FILE *f = file->closure;
575 return fseek (f, offset, whence);
579 _XcursorStdioFileInitialize (FILE *stdfile, XcursorFile *file)
581 file->closure = stdfile;
582 file->read = _XcursorStdioFileRead;
583 file->write = _XcursorStdioFileWrite;
584 file->seek = _XcursorStdioFileSeek;
587 static XcursorImages *
588 XcursorFileLoadImages (FILE *file, int size)
595 _XcursorStdioFileInitialize (file, &f);
596 return XcursorXcFileLoadImages (&f, size);
600 * From libXcursor/src/library.c
604 #define ICONDIR "/usr/X11R6/lib/X11/icons"
608 #define XCURSORPATH "~/.icons:/usr/share/icons:/usr/share/pixmaps:"ICONDIR
612 XcursorLibraryPath (void)
614 static const char *path;
618 path = getenv ("XCURSOR_PATH");
626 _XcursorAddPathElt (char *path, const char *elt, int len)
628 int pathlen = strlen (path);
630 /* append / if the path doesn't currently have one */
631 if (path[0] == '\0' || path[pathlen - 1] != '/')
638 /* strip leading slashes */
639 while (len && elt[0] == '/')
644 strncpy (path + pathlen, elt, len);
645 path[pathlen + len] = '\0';
649 _XcursorBuildThemeDir (const char *dir, const char *theme)
663 colon = strchr (dir, ':');
665 colon = dir + strlen (dir);
667 dirlen = colon - dir;
669 tcolon = strchr (theme, ':');
671 tcolon = theme + strlen (theme);
673 themelen = tcolon - theme;
679 home = getenv ("HOME");
682 homelen = strlen (home);
688 * add space for any needed directory separators, one per component,
689 * and one for the trailing null
691 len = 1 + homelen + 1 + dirlen + 1 + themelen + 1;
699 _XcursorAddPathElt (full, home, -1);
700 _XcursorAddPathElt (full, dir, dirlen);
701 _XcursorAddPathElt (full, theme, themelen);
706 _XcursorBuildFullname (const char *dir, const char *subdir, const char *file)
710 if (!dir || !subdir || !file)
713 full = malloc (strlen (dir) + 1 + strlen (subdir) + 1 + strlen (file) + 1);
717 _XcursorAddPathElt (full, dir, -1);
718 _XcursorAddPathElt (full, subdir, -1);
719 _XcursorAddPathElt (full, file, -1);
724 _XcursorNextPath (const char *path)
726 char *colon = strchr (path, ':');
733 #define XcursorWhite(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
734 #define XcursorSep(c) ((c) == ';' || (c) == ',')
737 _XcursorThemeInherits (const char *full)
746 f = fopen (full, "r");
749 while (fgets (line, sizeof (line), f))
751 if (!strncmp (line, "Inherits", 8))
755 while (*l == ' ') l++;
756 if (*l != '=') continue;
758 while (*l == ' ') l++;
759 result = malloc (strlen (l));
765 while (XcursorSep(*l) || XcursorWhite (*l)) l++;
770 while (*l && !XcursorWhite(*l) &&
785 XcursorScanTheme (const char *theme, const char *name)
791 char *inherits = NULL;
800 for (path = XcursorLibraryPath ();
802 path = _XcursorNextPath (path))
804 dir = _XcursorBuildThemeDir (path, theme);
807 full = _XcursorBuildFullname (dir, "cursors", name);
810 f = fopen (full, "r");
815 full = _XcursorBuildFullname (dir, "", "index.theme");
818 inherits = _XcursorThemeInherits (full);
826 * Recurse to scan inherited themes
828 for (i = inherits; i && f == NULL; i = _XcursorNextPath (i))
829 f = XcursorScanTheme (i, name);
830 if (inherits != NULL)
836 XcursorLibraryLoadImages (const char *file, const char *theme, int size)
839 XcursorImages *images = NULL;
845 f = XcursorScanTheme (theme, file);
847 f = XcursorScanTheme ("default", file);
850 images = XcursorFileLoadImages (f, size);
852 XcursorImagesSetName (images, file);