xref: /aosp_15_r20/external/wayland/cursor/xcursor.c (revision 84e872a0dc482bffdb63672969dd03a827d67c73)
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