KWin
Loading...
Searching...
No Matches
xcursor.c
Go to the documentation of this file.
1/*
2 * Copyright © 2002 Keith Packard
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25
26#define _DEFAULT_SOURCE
27#include "xcursor.h"
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <dirent.h>
32
33/*
34 * From libXcursor/include/X11/extensions/Xcursor.h
35 */
36
37#define XcursorTrue 1
38#define XcursorFalse 0
39
40/*
41 * Cursor files start with a header. The header
42 * contains a magic number, a version number and a
43 * table of contents which has type and offset information
44 * for the remaining tables in the file.
45 *
46 * File minor versions increment for compatible changes
47 * File major versions increment for incompatible changes (never, we hope)
48 *
49 * Chunks of the same type are always upward compatible. Incompatible
50 * changes are made with new chunk types; the old data can remain under
51 * the old type. Upward compatible changes can add header data as the
52 * header lengths are specified in the file.
53 *
54 * File:
55 * FileHeader
56 * LISTofChunk
57 *
58 * FileHeader:
59 * CARD32 magic magic number
60 * CARD32 header bytes in file header
61 * CARD32 version file version
62 * CARD32 ntoc number of toc entries
63 * LISTofFileToc toc table of contents
64 *
65 * FileToc:
66 * CARD32 type entry type
67 * CARD32 subtype entry subtype (size for images)
68 * CARD32 position absolute file position
69 */
70
71#define XCURSOR_MAGIC 0x72756358 /* "Xcur" LSBFirst */
72
73/*
74 * Current Xcursor version number. Will be substituted by configure
75 * from the version in the libXcursor configure.ac file.
76 */
77
78#define XCURSOR_LIB_MAJOR 1
79#define XCURSOR_LIB_MINOR 1
80#define XCURSOR_LIB_REVISION 13
81#define XCURSOR_LIB_VERSION ((XCURSOR_LIB_MAJOR * 10000) + \
82 (XCURSOR_LIB_MINOR * 100) + \
83 (XCURSOR_LIB_REVISION))
84
85/*
86 * This version number is stored in cursor files; changes to the
87 * file format require updating this version number
88 */
89#define XCURSOR_FILE_MAJOR 1
90#define XCURSOR_FILE_MINOR 0
91#define XCURSOR_FILE_VERSION ((XCURSOR_FILE_MAJOR << 16) | (XCURSOR_FILE_MINOR))
92#define XCURSOR_FILE_HEADER_LEN (4 * 4)
93#define XCURSOR_FILE_TOC_LEN (3 * 4)
94
95typedef struct _XcursorFileToc {
96 XcursorUInt type; /* chunk type */
97 XcursorUInt subtype; /* subtype (size for images) */
98 XcursorUInt position; /* absolute position in file */
100
101typedef struct _XcursorFileHeader {
102 XcursorUInt magic; /* magic number */
103 XcursorUInt header; /* byte length of header */
104 XcursorUInt version; /* file version number */
105 XcursorUInt ntoc; /* number of toc entries */
106 XcursorFileToc *tocs; /* table of contents */
108
109/*
110 * The rest of the file is a list of chunks, each tagged by type
111 * and version.
112 *
113 * Chunk:
114 * ChunkHeader
115 * <extra type-specific header fields>
116 * <type-specific data>
117 *
118 * ChunkHeader:
119 * CARD32 header bytes in chunk header + type header
120 * CARD32 type chunk type
121 * CARD32 subtype chunk subtype
122 * CARD32 version chunk type version
123 */
124
125#define XCURSOR_CHUNK_HEADER_LEN (4 * 4)
126
127typedef struct _XcursorChunkHeader {
128 XcursorUInt header; /* bytes in chunk header */
129 XcursorUInt type; /* chunk type */
130 XcursorUInt subtype; /* chunk subtype (size for images) */
131 XcursorUInt version; /* version of this type */
133
134/*
135 * Here's a list of the known chunk types
136 */
137
138/*
139 * Comments consist of a 4-byte length field followed by
140 * UTF-8 encoded text
141 *
142 * Comment:
143 * ChunkHeader header chunk header
144 * CARD32 length bytes in text
145 * LISTofCARD8 text UTF-8 encoded text
146 */
147
148#define XCURSOR_COMMENT_TYPE 0xfffe0001
149#define XCURSOR_COMMENT_VERSION 1
150#define XCURSOR_COMMENT_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (1 *4))
151#define XCURSOR_COMMENT_COPYRIGHT 1
152#define XCURSOR_COMMENT_LICENSE 2
153#define XCURSOR_COMMENT_OTHER 3
154#define XCURSOR_COMMENT_MAX_LEN 0x100000
155
161
162/*
163 * Each cursor image occupies a separate image chunk.
164 * The length of the image header follows the chunk header
165 * so that future versions can extend the header without
166 * breaking older applications
167 *
168 * Image:
169 * ChunkHeader header chunk header
170 * CARD32 width actual width
171 * CARD32 height actual height
172 * CARD32 xhot hot spot x
173 * CARD32 yhot hot spot y
174 * CARD32 delay animation delay
175 * LISTofCARD32 pixels ARGB pixels
176 */
177
178#define XCURSOR_IMAGE_TYPE 0xfffd0002
179#define XCURSOR_IMAGE_VERSION 1
180#define XCURSOR_IMAGE_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (5*4))
181#define XCURSOR_IMAGE_MAX_SIZE 0x7fff /* 32767x32767 max cursor size */
182
184
186 void *closure;
187 int (*read) (XcursorFile *file, unsigned char *buf, int len);
188 int (*write) (XcursorFile *file, unsigned char *buf, int len);
189 int (*seek) (XcursorFile *file, long offset, int whence);
190};
191
192typedef struct _XcursorComments {
193 int ncomment; /* number of comments */
194 XcursorComment **comments; /* array of XcursorComment pointers */
196
197/*
198 * From libXcursor/src/file.c
199 */
200
201static XcursorImage *
202XcursorImageCreate (int width, int height)
203{
204 XcursorImage *image;
205
206 if (width < 0 || height < 0)
207 return NULL;
208 if (width > XCURSOR_IMAGE_MAX_SIZE || height > XCURSOR_IMAGE_MAX_SIZE)
209 return NULL;
210
211 image = malloc (sizeof (XcursorImage) +
212 width * height * sizeof (XcursorPixel));
213 if (!image)
214 return NULL;
216 image->pixels = (XcursorPixel *) (image + 1);
217 image->size = width > height ? width : height;
218 image->width = width;
219 image->height = height;
220 image->delay = 0;
221 return image;
222}
223
224static void
225XcursorImageDestroy (XcursorImage *image)
226{
227 free (image);
228}
229
230static XcursorImages *
231XcursorImagesCreate (int size)
232{
233 XcursorImages *images;
234
235 images = malloc (sizeof (XcursorImages) +
236 size * sizeof (XcursorImage *));
237 if (!images)
238 return NULL;
239 images->nimage = 0;
240 images->images = (XcursorImage **) (images + 1);
241 return images;
242}
243
244void
246{
247 int n;
248
249 if (!images)
250 return;
251
252 for (n = 0; n < images->nimage; n++)
253 XcursorImageDestroy (images->images[n]);
254 free (images);
255}
256
257static XcursorBool
258_XcursorReadUInt (XcursorFile *file, XcursorUInt *u)
259{
260 unsigned char bytes[4];
261
262 if (!file || !u)
263 return XcursorFalse;
264
265 if ((*file->read) (file, bytes, 4) != 4)
266 return XcursorFalse;
267
268 *u = ((XcursorUInt)(bytes[0]) << 0) |
269 ((XcursorUInt)(bytes[1]) << 8) |
270 ((XcursorUInt)(bytes[2]) << 16) |
271 ((XcursorUInt)(bytes[3]) << 24);
272 return XcursorTrue;
273}
274
275static void
276_XcursorFileHeaderDestroy (XcursorFileHeader *fileHeader)
277{
278 free (fileHeader);
279}
280
281static XcursorFileHeader *
282_XcursorFileHeaderCreate (XcursorUInt ntoc)
283{
284 XcursorFileHeader *fileHeader;
285
286 if (ntoc > 0x10000)
287 return NULL;
288 fileHeader = malloc (sizeof (XcursorFileHeader) +
289 ntoc * sizeof (XcursorFileToc));
290 if (!fileHeader)
291 return NULL;
292 fileHeader->magic = XCURSOR_MAGIC;
293 fileHeader->header = XCURSOR_FILE_HEADER_LEN;
294 fileHeader->version = XCURSOR_FILE_VERSION;
295 fileHeader->ntoc = ntoc;
296 fileHeader->tocs = (XcursorFileToc *) (fileHeader + 1);
297 return fileHeader;
298}
299
300static XcursorFileHeader *
301_XcursorReadFileHeader (XcursorFile *file)
302{
303 XcursorFileHeader head, *fileHeader;
304 XcursorUInt skip;
305 unsigned int n;
306
307 if (!file)
308 return NULL;
309
310 if (!_XcursorReadUInt (file, &head.magic))
311 return NULL;
312 if (head.magic != XCURSOR_MAGIC)
313 return NULL;
314 if (!_XcursorReadUInt (file, &head.header))
315 return NULL;
316 if (!_XcursorReadUInt (file, &head.version))
317 return NULL;
318 if (!_XcursorReadUInt (file, &head.ntoc))
319 return NULL;
320 skip = head.header - XCURSOR_FILE_HEADER_LEN;
321 if (skip)
322 if ((*file->seek) (file, skip, SEEK_CUR) == EOF)
323 return NULL;
324 fileHeader = _XcursorFileHeaderCreate (head.ntoc);
325 if (!fileHeader)
326 return NULL;
327 fileHeader->magic = head.magic;
328 fileHeader->header = head.header;
329 fileHeader->version = head.version;
330 fileHeader->ntoc = head.ntoc;
331 for (n = 0; n < fileHeader->ntoc; n++)
332 {
333 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].type))
334 break;
335 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].subtype))
336 break;
337 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].position))
338 break;
339 }
340 if (n != fileHeader->ntoc)
341 {
342 _XcursorFileHeaderDestroy (fileHeader);
343 return NULL;
344 }
345 return fileHeader;
346}
347
348static XcursorBool
349_XcursorSeekToToc (XcursorFile *file,
350 XcursorFileHeader *fileHeader,
351 int toc)
352{
353 if (!file || !fileHeader || \
354 (*file->seek) (file, fileHeader->tocs[toc].position, SEEK_SET) == EOF)
355 return XcursorFalse;
356 return XcursorTrue;
357}
358
359static XcursorBool
360_XcursorFileReadChunkHeader (XcursorFile *file,
361 XcursorFileHeader *fileHeader,
362 int toc,
363 XcursorChunkHeader *chunkHeader)
364{
365 if (!file || !fileHeader || !chunkHeader)
366 return XcursorFalse;
367 if (!_XcursorSeekToToc (file, fileHeader, toc))
368 return XcursorFalse;
369 if (!_XcursorReadUInt (file, &chunkHeader->header))
370 return XcursorFalse;
371 if (!_XcursorReadUInt (file, &chunkHeader->type))
372 return XcursorFalse;
373 if (!_XcursorReadUInt (file, &chunkHeader->subtype))
374 return XcursorFalse;
375 if (!_XcursorReadUInt (file, &chunkHeader->version))
376 return XcursorFalse;
377 /* sanity check */
378 if (chunkHeader->type != fileHeader->tocs[toc].type ||
379 chunkHeader->subtype != fileHeader->tocs[toc].subtype)
380 return XcursorFalse;
381 return XcursorTrue;
382}
383
384#define dist(a,b) ((a) > (b) ? (a) - (b) : (b) - (a))
385
386static XcursorDim
387_XcursorFindBestSize (XcursorFileHeader *fileHeader,
388 XcursorDim size,
389 int *nsizesp)
390{
391 unsigned int n;
392 int nsizes = 0;
393 XcursorDim bestSize = 0;
394 XcursorDim thisSize;
395
396 if (!fileHeader || !nsizesp)
397 return 0;
398
399 for (n = 0; n < fileHeader->ntoc; n++)
400 {
401 if (fileHeader->tocs[n].type != XCURSOR_IMAGE_TYPE)
402 continue;
403 thisSize = fileHeader->tocs[n].subtype;
404 if (!bestSize || dist (thisSize, size) < dist (bestSize, size))
405 {
406 bestSize = thisSize;
407 nsizes = 1;
408 }
409 else if (thisSize == bestSize)
410 nsizes++;
411 }
412 *nsizesp = nsizes;
413 return bestSize;
414}
415
416static int
417_XcursorFindImageToc (XcursorFileHeader *fileHeader,
418 XcursorDim size,
419 int count)
420{
421 unsigned int toc;
422 XcursorDim thisSize;
423
424 if (!fileHeader)
425 return 0;
426
427 for (toc = 0; toc < fileHeader->ntoc; toc++)
428 {
429 if (fileHeader->tocs[toc].type != XCURSOR_IMAGE_TYPE)
430 continue;
431 thisSize = fileHeader->tocs[toc].subtype;
432 if (thisSize != size)
433 continue;
434 if (!count)
435 break;
436 count--;
437 }
438 if (toc == fileHeader->ntoc)
439 return -1;
440 return toc;
441}
442
443static XcursorImage *
444_XcursorReadImage (XcursorFile *file,
445 XcursorFileHeader *fileHeader,
446 int toc)
447{
448 XcursorChunkHeader chunkHeader;
449 XcursorImage head;
450 XcursorImage *image;
451 int n;
452 XcursorPixel *p;
453
454 if (!file || !fileHeader)
455 return NULL;
456
457 if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader))
458 return NULL;
459 if (!_XcursorReadUInt (file, &head.width))
460 return NULL;
461 if (!_XcursorReadUInt (file, &head.height))
462 return NULL;
463 if (!_XcursorReadUInt (file, &head.xhot))
464 return NULL;
465 if (!_XcursorReadUInt (file, &head.yhot))
466 return NULL;
467 if (!_XcursorReadUInt (file, &head.delay))
468 return NULL;
469 /* sanity check data */
470 if (head.width > XCURSOR_IMAGE_MAX_SIZE ||
472 return NULL;
473 if (head.width == 0 || head.height == 0)
474 return NULL;
475 if (head.xhot > head.width || head.yhot > head.height)
476 return NULL;
477
478 /* Create the image and initialize it */
479 image = XcursorImageCreate (head.width, head.height);
480 if (image == NULL)
481 return NULL;
482 if (chunkHeader.version < image->version)
483 image->version = chunkHeader.version;
484 image->size = chunkHeader.subtype;
485 image->xhot = head.xhot;
486 image->yhot = head.yhot;
487 image->delay = head.delay;
488 n = image->width * image->height;
489 p = image->pixels;
490 while (n--)
491 {
492 if (!_XcursorReadUInt (file, p))
493 {
494 XcursorImageDestroy (image);
495 return NULL;
496 }
497 p++;
498 }
499 return image;
500}
501
502static XcursorImages *
503XcursorXcFileLoadImages (XcursorFile *file, int size)
504{
505 XcursorFileHeader *fileHeader;
506 XcursorDim bestSize;
507 int nsize;
508 XcursorImages *images;
509 int n;
510 int toc;
511
512 if (!file || size < 0)
513 return NULL;
514 fileHeader = _XcursorReadFileHeader (file);
515 if (!fileHeader)
516 return NULL;
517 bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize);
518 if (!bestSize)
519 {
520 _XcursorFileHeaderDestroy (fileHeader);
521 return NULL;
522 }
523 images = XcursorImagesCreate (nsize);
524 if (!images)
525 {
526 _XcursorFileHeaderDestroy (fileHeader);
527 return NULL;
528 }
529 for (n = 0; n < nsize; n++)
530 {
531 toc = _XcursorFindImageToc (fileHeader, bestSize, n);
532 if (toc < 0)
533 break;
534 images->images[images->nimage] = _XcursorReadImage (file, fileHeader,
535 toc);
536 if (!images->images[images->nimage])
537 break;
538 images->nimage++;
539 }
540 _XcursorFileHeaderDestroy (fileHeader);
541 if (images->nimage != nsize)
542 {
543 XcursorImagesDestroy (images);
544 images = NULL;
545 }
546 return images;
547}
548
549static int
550_XcursorStdioFileRead (XcursorFile *file, unsigned char *buf, int len)
551{
552 FILE *f = file->closure;
553 return fread (buf, 1, len, f);
554}
555
556static int
557_XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len)
558{
559 FILE *f = file->closure;
560 return fwrite (buf, 1, len, f);
561}
562
563static int
564_XcursorStdioFileSeek (XcursorFile *file, long offset, int whence)
565{
566 FILE *f = file->closure;
567 return fseek (f, offset, whence);
568}
569
570static void
571_XcursorStdioFileInitialize (FILE *stdfile, XcursorFile *file)
572{
573 file->closure = stdfile;
574 file->read = _XcursorStdioFileRead;
575 file->write = _XcursorStdioFileWrite;
576 file->seek = _XcursorStdioFileSeek;
577}
578
580XcursorFileLoadImages (const char *file, int size)
581{
582 XcursorFile f;
583 XcursorImages *images;
584
585 FILE *fp = fopen(file, "r");
586 if (!fp)
587 return NULL;
588
589 _XcursorStdioFileInitialize (fp, &f);
590 images = XcursorXcFileLoadImages (&f, size);
591 fclose(fp);
592
593 return images;
594}
XcursorUInt version
Definition xcursor.c:131
XcursorUInt type
Definition xcursor.c:129
XcursorUInt header
Definition xcursor.c:128
XcursorUInt subtype
Definition xcursor.c:130
char * comment
Definition xcursor.c:159
XcursorUInt version
Definition xcursor.c:157
XcursorUInt comment_type
Definition xcursor.c:158
XcursorComment ** comments
Definition xcursor.c:194
XcursorFileToc * tocs
Definition xcursor.c:106
XcursorUInt header
Definition xcursor.c:103
XcursorUInt version
Definition xcursor.c:104
XcursorUInt ntoc
Definition xcursor.c:105
XcursorUInt magic
Definition xcursor.c:102
int(* read)(XcursorFile *file, unsigned char *buf, int len)
Definition xcursor.c:187
int(* write)(XcursorFile *file, unsigned char *buf, int len)
Definition xcursor.c:188
void * closure
Definition xcursor.c:186
int(* seek)(XcursorFile *file, long offset, int whence)
Definition xcursor.c:189
XcursorUInt position
Definition xcursor.c:98
XcursorUInt type
Definition xcursor.c:96
XcursorUInt subtype
Definition xcursor.c:97
XcursorUInt delay
Definition xcursor.h:48
XcursorDim width
Definition xcursor.h:44
XcursorDim yhot
Definition xcursor.h:47
XcursorDim xhot
Definition xcursor.h:46
XcursorUInt version
Definition xcursor.h:42
XcursorPixel * pixels
Definition xcursor.h:49
XcursorDim size
Definition xcursor.h:43
XcursorDim height
Definition xcursor.h:45
XcursorImage ** images
Definition xcursor.h:57
struct _XcursorFileToc XcursorFileToc
#define dist(a, b)
Definition xcursor.c:384
#define XcursorTrue
Definition xcursor.c:37
void XcursorImagesDestroy(XcursorImages *images)
Definition xcursor.c:245
XcursorImages * XcursorFileLoadImages(const char *file, int size)
Definition xcursor.c:580
struct _XcursorComments XcursorComments
#define XCURSOR_FILE_HEADER_LEN
Definition xcursor.c:92
#define XCURSOR_FILE_VERSION
Definition xcursor.c:91
struct _XcursorFileHeader XcursorFileHeader
#define XCURSOR_IMAGE_VERSION
Definition xcursor.c:179
#define XCURSOR_IMAGE_MAX_SIZE
Definition xcursor.c:181
#define XCURSOR_MAGIC
Definition xcursor.c:71
#define XcursorFalse
Definition xcursor.c:38
struct _XcursorComment XcursorComment
#define XCURSOR_IMAGE_TYPE
Definition xcursor.c:178
struct _XcursorChunkHeader XcursorChunkHeader
int XcursorBool
Definition xcursor.h:35
uint32_t XcursorUInt
Definition xcursor.h:36
XcursorUInt XcursorPixel
Definition xcursor.h:39
XcursorUInt XcursorDim
Definition xcursor.h:38