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