File indexing completed on 2024-11-10 04:56:26
0001 /* 0002 * Copyright © 2002 Keith Packard 0003 * 0004 * Permission is hereby granted, free of charge, to any person obtaining 0005 * a copy of this software and associated documentation files (the 0006 * "Software"), to deal in the Software without restriction, including 0007 * without limitation the rights to use, copy, modify, merge, publish, 0008 * distribute, sublicense, and/or sell copies of the Software, and to 0009 * permit persons to whom the Software is furnished to do so, subject to 0010 * the following conditions: 0011 * 0012 * The above copyright notice and this permission notice (including the 0013 * next paragraph) shall be included in all copies or substantial 0014 * portions of the Software. 0015 * 0016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 0017 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 0018 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 0019 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 0020 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 0021 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 0022 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 0023 * SOFTWARE. 0024 */ 0025 0026 #define _DEFAULT_SOURCE 0027 #include "xcursor.h" 0028 #include <stdio.h> 0029 #include <stdlib.h> 0030 #include <string.h> 0031 #include <dirent.h> 0032 0033 /* 0034 * From libXcursor/include/X11/extensions/Xcursor.h 0035 */ 0036 0037 #define XcursorTrue 1 0038 #define XcursorFalse 0 0039 0040 /* 0041 * Cursor files start with a header. The header 0042 * contains a magic number, a version number and a 0043 * table of contents which has type and offset information 0044 * for the remaining tables in the file. 0045 * 0046 * File minor versions increment for compatible changes 0047 * File major versions increment for incompatible changes (never, we hope) 0048 * 0049 * Chunks of the same type are always upward compatible. Incompatible 0050 * changes are made with new chunk types; the old data can remain under 0051 * the old type. Upward compatible changes can add header data as the 0052 * header lengths are specified in the file. 0053 * 0054 * File: 0055 * FileHeader 0056 * LISTofChunk 0057 * 0058 * FileHeader: 0059 * CARD32 magic magic number 0060 * CARD32 header bytes in file header 0061 * CARD32 version file version 0062 * CARD32 ntoc number of toc entries 0063 * LISTofFileToc toc table of contents 0064 * 0065 * FileToc: 0066 * CARD32 type entry type 0067 * CARD32 subtype entry subtype (size for images) 0068 * CARD32 position absolute file position 0069 */ 0070 0071 #define XCURSOR_MAGIC 0x72756358 /* "Xcur" LSBFirst */ 0072 0073 /* 0074 * Current Xcursor version number. Will be substituted by configure 0075 * from the version in the libXcursor configure.ac file. 0076 */ 0077 0078 #define XCURSOR_LIB_MAJOR 1 0079 #define XCURSOR_LIB_MINOR 1 0080 #define XCURSOR_LIB_REVISION 13 0081 #define XCURSOR_LIB_VERSION ((XCURSOR_LIB_MAJOR * 10000) + \ 0082 (XCURSOR_LIB_MINOR * 100) + \ 0083 (XCURSOR_LIB_REVISION)) 0084 0085 /* 0086 * This version number is stored in cursor files; changes to the 0087 * file format require updating this version number 0088 */ 0089 #define XCURSOR_FILE_MAJOR 1 0090 #define XCURSOR_FILE_MINOR 0 0091 #define XCURSOR_FILE_VERSION ((XCURSOR_FILE_MAJOR << 16) | (XCURSOR_FILE_MINOR)) 0092 #define XCURSOR_FILE_HEADER_LEN (4 * 4) 0093 #define XCURSOR_FILE_TOC_LEN (3 * 4) 0094 0095 typedef struct _XcursorFileToc { 0096 XcursorUInt type; /* chunk type */ 0097 XcursorUInt subtype; /* subtype (size for images) */ 0098 XcursorUInt position; /* absolute position in file */ 0099 } XcursorFileToc; 0100 0101 typedef struct _XcursorFileHeader { 0102 XcursorUInt magic; /* magic number */ 0103 XcursorUInt header; /* byte length of header */ 0104 XcursorUInt version; /* file version number */ 0105 XcursorUInt ntoc; /* number of toc entries */ 0106 XcursorFileToc *tocs; /* table of contents */ 0107 } XcursorFileHeader; 0108 0109 /* 0110 * The rest of the file is a list of chunks, each tagged by type 0111 * and version. 0112 * 0113 * Chunk: 0114 * ChunkHeader 0115 * <extra type-specific header fields> 0116 * <type-specific data> 0117 * 0118 * ChunkHeader: 0119 * CARD32 header bytes in chunk header + type header 0120 * CARD32 type chunk type 0121 * CARD32 subtype chunk subtype 0122 * CARD32 version chunk type version 0123 */ 0124 0125 #define XCURSOR_CHUNK_HEADER_LEN (4 * 4) 0126 0127 typedef struct _XcursorChunkHeader { 0128 XcursorUInt header; /* bytes in chunk header */ 0129 XcursorUInt type; /* chunk type */ 0130 XcursorUInt subtype; /* chunk subtype (size for images) */ 0131 XcursorUInt version; /* version of this type */ 0132 } XcursorChunkHeader; 0133 0134 /* 0135 * Here's a list of the known chunk types 0136 */ 0137 0138 /* 0139 * Comments consist of a 4-byte length field followed by 0140 * UTF-8 encoded text 0141 * 0142 * Comment: 0143 * ChunkHeader header chunk header 0144 * CARD32 length bytes in text 0145 * LISTofCARD8 text UTF-8 encoded text 0146 */ 0147 0148 #define XCURSOR_COMMENT_TYPE 0xfffe0001 0149 #define XCURSOR_COMMENT_VERSION 1 0150 #define XCURSOR_COMMENT_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (1 *4)) 0151 #define XCURSOR_COMMENT_COPYRIGHT 1 0152 #define XCURSOR_COMMENT_LICENSE 2 0153 #define XCURSOR_COMMENT_OTHER 3 0154 #define XCURSOR_COMMENT_MAX_LEN 0x100000 0155 0156 typedef struct _XcursorComment { 0157 XcursorUInt version; 0158 XcursorUInt comment_type; 0159 char *comment; 0160 } XcursorComment; 0161 0162 /* 0163 * Each cursor image occupies a separate image chunk. 0164 * The length of the image header follows the chunk header 0165 * so that future versions can extend the header without 0166 * breaking older applications 0167 * 0168 * Image: 0169 * ChunkHeader header chunk header 0170 * CARD32 width actual width 0171 * CARD32 height actual height 0172 * CARD32 xhot hot spot x 0173 * CARD32 yhot hot spot y 0174 * CARD32 delay animation delay 0175 * LISTofCARD32 pixels ARGB pixels 0176 */ 0177 0178 #define XCURSOR_IMAGE_TYPE 0xfffd0002 0179 #define XCURSOR_IMAGE_VERSION 1 0180 #define XCURSOR_IMAGE_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (5*4)) 0181 #define XCURSOR_IMAGE_MAX_SIZE 0x7fff /* 32767x32767 max cursor size */ 0182 0183 typedef struct _XcursorFile XcursorFile; 0184 0185 struct _XcursorFile { 0186 void *closure; 0187 int (*read) (XcursorFile *file, unsigned char *buf, int len); 0188 int (*write) (XcursorFile *file, unsigned char *buf, int len); 0189 int (*seek) (XcursorFile *file, long offset, int whence); 0190 }; 0191 0192 typedef struct _XcursorComments { 0193 int ncomment; /* number of comments */ 0194 XcursorComment **comments; /* array of XcursorComment pointers */ 0195 } XcursorComments; 0196 0197 /* 0198 * From libXcursor/src/file.c 0199 */ 0200 0201 static XcursorImage * 0202 XcursorImageCreate (int width, int height) 0203 { 0204 XcursorImage *image; 0205 0206 if (width < 0 || height < 0) 0207 return NULL; 0208 if (width > XCURSOR_IMAGE_MAX_SIZE || height > XCURSOR_IMAGE_MAX_SIZE) 0209 return NULL; 0210 0211 image = malloc (sizeof (XcursorImage) + 0212 width * height * sizeof (XcursorPixel)); 0213 if (!image) 0214 return NULL; 0215 image->version = XCURSOR_IMAGE_VERSION; 0216 image->pixels = (XcursorPixel *) (image + 1); 0217 image->size = width > height ? width : height; 0218 image->width = width; 0219 image->height = height; 0220 image->delay = 0; 0221 return image; 0222 } 0223 0224 static void 0225 XcursorImageDestroy (XcursorImage *image) 0226 { 0227 free (image); 0228 } 0229 0230 static XcursorImages * 0231 XcursorImagesCreate (int size) 0232 { 0233 XcursorImages *images; 0234 0235 images = malloc (sizeof (XcursorImages) + 0236 size * sizeof (XcursorImage *)); 0237 if (!images) 0238 return NULL; 0239 images->nimage = 0; 0240 images->images = (XcursorImage **) (images + 1); 0241 return images; 0242 } 0243 0244 void 0245 XcursorImagesDestroy (XcursorImages *images) 0246 { 0247 int n; 0248 0249 if (!images) 0250 return; 0251 0252 for (n = 0; n < images->nimage; n++) 0253 XcursorImageDestroy (images->images[n]); 0254 free (images); 0255 } 0256 0257 static XcursorBool 0258 _XcursorReadUInt (XcursorFile *file, XcursorUInt *u) 0259 { 0260 unsigned char bytes[4]; 0261 0262 if (!file || !u) 0263 return XcursorFalse; 0264 0265 if ((*file->read) (file, bytes, 4) != 4) 0266 return XcursorFalse; 0267 0268 *u = ((XcursorUInt)(bytes[0]) << 0) | 0269 ((XcursorUInt)(bytes[1]) << 8) | 0270 ((XcursorUInt)(bytes[2]) << 16) | 0271 ((XcursorUInt)(bytes[3]) << 24); 0272 return XcursorTrue; 0273 } 0274 0275 static void 0276 _XcursorFileHeaderDestroy (XcursorFileHeader *fileHeader) 0277 { 0278 free (fileHeader); 0279 } 0280 0281 static XcursorFileHeader * 0282 _XcursorFileHeaderCreate (XcursorUInt ntoc) 0283 { 0284 XcursorFileHeader *fileHeader; 0285 0286 if (ntoc > 0x10000) 0287 return NULL; 0288 fileHeader = malloc (sizeof (XcursorFileHeader) + 0289 ntoc * sizeof (XcursorFileToc)); 0290 if (!fileHeader) 0291 return NULL; 0292 fileHeader->magic = XCURSOR_MAGIC; 0293 fileHeader->header = XCURSOR_FILE_HEADER_LEN; 0294 fileHeader->version = XCURSOR_FILE_VERSION; 0295 fileHeader->ntoc = ntoc; 0296 fileHeader->tocs = (XcursorFileToc *) (fileHeader + 1); 0297 return fileHeader; 0298 } 0299 0300 static XcursorFileHeader * 0301 _XcursorReadFileHeader (XcursorFile *file) 0302 { 0303 XcursorFileHeader head, *fileHeader; 0304 XcursorUInt skip; 0305 unsigned int n; 0306 0307 if (!file) 0308 return NULL; 0309 0310 if (!_XcursorReadUInt (file, &head.magic)) 0311 return NULL; 0312 if (head.magic != XCURSOR_MAGIC) 0313 return NULL; 0314 if (!_XcursorReadUInt (file, &head.header)) 0315 return NULL; 0316 if (!_XcursorReadUInt (file, &head.version)) 0317 return NULL; 0318 if (!_XcursorReadUInt (file, &head.ntoc)) 0319 return NULL; 0320 skip = head.header - XCURSOR_FILE_HEADER_LEN; 0321 if (skip) 0322 if ((*file->seek) (file, skip, SEEK_CUR) == EOF) 0323 return NULL; 0324 fileHeader = _XcursorFileHeaderCreate (head.ntoc); 0325 if (!fileHeader) 0326 return NULL; 0327 fileHeader->magic = head.magic; 0328 fileHeader->header = head.header; 0329 fileHeader->version = head.version; 0330 fileHeader->ntoc = head.ntoc; 0331 for (n = 0; n < fileHeader->ntoc; n++) 0332 { 0333 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].type)) 0334 break; 0335 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].subtype)) 0336 break; 0337 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].position)) 0338 break; 0339 } 0340 if (n != fileHeader->ntoc) 0341 { 0342 _XcursorFileHeaderDestroy (fileHeader); 0343 return NULL; 0344 } 0345 return fileHeader; 0346 } 0347 0348 static XcursorBool 0349 _XcursorSeekToToc (XcursorFile *file, 0350 XcursorFileHeader *fileHeader, 0351 int toc) 0352 { 0353 if (!file || !fileHeader || \ 0354 (*file->seek) (file, fileHeader->tocs[toc].position, SEEK_SET) == EOF) 0355 return XcursorFalse; 0356 return XcursorTrue; 0357 } 0358 0359 static XcursorBool 0360 _XcursorFileReadChunkHeader (XcursorFile *file, 0361 XcursorFileHeader *fileHeader, 0362 int toc, 0363 XcursorChunkHeader *chunkHeader) 0364 { 0365 if (!file || !fileHeader || !chunkHeader) 0366 return XcursorFalse; 0367 if (!_XcursorSeekToToc (file, fileHeader, toc)) 0368 return XcursorFalse; 0369 if (!_XcursorReadUInt (file, &chunkHeader->header)) 0370 return XcursorFalse; 0371 if (!_XcursorReadUInt (file, &chunkHeader->type)) 0372 return XcursorFalse; 0373 if (!_XcursorReadUInt (file, &chunkHeader->subtype)) 0374 return XcursorFalse; 0375 if (!_XcursorReadUInt (file, &chunkHeader->version)) 0376 return XcursorFalse; 0377 /* sanity check */ 0378 if (chunkHeader->type != fileHeader->tocs[toc].type || 0379 chunkHeader->subtype != fileHeader->tocs[toc].subtype) 0380 return XcursorFalse; 0381 return XcursorTrue; 0382 } 0383 0384 #define dist(a,b) ((a) > (b) ? (a) - (b) : (b) - (a)) 0385 0386 static XcursorDim 0387 _XcursorFindBestSize (XcursorFileHeader *fileHeader, 0388 XcursorDim size, 0389 int *nsizesp) 0390 { 0391 unsigned int n; 0392 int nsizes = 0; 0393 XcursorDim bestSize = 0; 0394 XcursorDim thisSize; 0395 0396 if (!fileHeader || !nsizesp) 0397 return 0; 0398 0399 for (n = 0; n < fileHeader->ntoc; n++) 0400 { 0401 if (fileHeader->tocs[n].type != XCURSOR_IMAGE_TYPE) 0402 continue; 0403 thisSize = fileHeader->tocs[n].subtype; 0404 if (!bestSize || dist (thisSize, size) < dist (bestSize, size)) 0405 { 0406 bestSize = thisSize; 0407 nsizes = 1; 0408 } 0409 else if (thisSize == bestSize) 0410 nsizes++; 0411 } 0412 *nsizesp = nsizes; 0413 return bestSize; 0414 } 0415 0416 static int 0417 _XcursorFindImageToc (XcursorFileHeader *fileHeader, 0418 XcursorDim size, 0419 int count) 0420 { 0421 unsigned int toc; 0422 XcursorDim thisSize; 0423 0424 if (!fileHeader) 0425 return 0; 0426 0427 for (toc = 0; toc < fileHeader->ntoc; toc++) 0428 { 0429 if (fileHeader->tocs[toc].type != XCURSOR_IMAGE_TYPE) 0430 continue; 0431 thisSize = fileHeader->tocs[toc].subtype; 0432 if (thisSize != size) 0433 continue; 0434 if (!count) 0435 break; 0436 count--; 0437 } 0438 if (toc == fileHeader->ntoc) 0439 return -1; 0440 return toc; 0441 } 0442 0443 static XcursorImage * 0444 _XcursorReadImage (XcursorFile *file, 0445 XcursorFileHeader *fileHeader, 0446 int toc) 0447 { 0448 XcursorChunkHeader chunkHeader; 0449 XcursorImage head; 0450 XcursorImage *image; 0451 int n; 0452 XcursorPixel *p; 0453 0454 if (!file || !fileHeader) 0455 return NULL; 0456 0457 if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader)) 0458 return NULL; 0459 if (!_XcursorReadUInt (file, &head.width)) 0460 return NULL; 0461 if (!_XcursorReadUInt (file, &head.height)) 0462 return NULL; 0463 if (!_XcursorReadUInt (file, &head.xhot)) 0464 return NULL; 0465 if (!_XcursorReadUInt (file, &head.yhot)) 0466 return NULL; 0467 if (!_XcursorReadUInt (file, &head.delay)) 0468 return NULL; 0469 /* sanity check data */ 0470 if (head.width > XCURSOR_IMAGE_MAX_SIZE || 0471 head.height > XCURSOR_IMAGE_MAX_SIZE) 0472 return NULL; 0473 if (head.width == 0 || head.height == 0) 0474 return NULL; 0475 if (head.xhot > head.width || head.yhot > head.height) 0476 return NULL; 0477 0478 /* Create the image and initialize it */ 0479 image = XcursorImageCreate (head.width, head.height); 0480 if (image == NULL) 0481 return NULL; 0482 if (chunkHeader.version < image->version) 0483 image->version = chunkHeader.version; 0484 image->size = chunkHeader.subtype; 0485 image->xhot = head.xhot; 0486 image->yhot = head.yhot; 0487 image->delay = head.delay; 0488 n = image->width * image->height; 0489 p = image->pixels; 0490 while (n--) 0491 { 0492 if (!_XcursorReadUInt (file, p)) 0493 { 0494 XcursorImageDestroy (image); 0495 return NULL; 0496 } 0497 p++; 0498 } 0499 return image; 0500 } 0501 0502 static XcursorImages * 0503 XcursorXcFileLoadImages (XcursorFile *file, int size) 0504 { 0505 XcursorFileHeader *fileHeader; 0506 XcursorDim bestSize; 0507 int nsize; 0508 XcursorImages *images; 0509 int n; 0510 int toc; 0511 0512 if (!file || size < 0) 0513 return NULL; 0514 fileHeader = _XcursorReadFileHeader (file); 0515 if (!fileHeader) 0516 return NULL; 0517 bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize); 0518 if (!bestSize) 0519 { 0520 _XcursorFileHeaderDestroy (fileHeader); 0521 return NULL; 0522 } 0523 images = XcursorImagesCreate (nsize); 0524 if (!images) 0525 { 0526 _XcursorFileHeaderDestroy (fileHeader); 0527 return NULL; 0528 } 0529 for (n = 0; n < nsize; n++) 0530 { 0531 toc = _XcursorFindImageToc (fileHeader, bestSize, n); 0532 if (toc < 0) 0533 break; 0534 images->images[images->nimage] = _XcursorReadImage (file, fileHeader, 0535 toc); 0536 if (!images->images[images->nimage]) 0537 break; 0538 images->nimage++; 0539 } 0540 _XcursorFileHeaderDestroy (fileHeader); 0541 if (images->nimage != nsize) 0542 { 0543 XcursorImagesDestroy (images); 0544 images = NULL; 0545 } 0546 return images; 0547 } 0548 0549 static int 0550 _XcursorStdioFileRead (XcursorFile *file, unsigned char *buf, int len) 0551 { 0552 FILE *f = file->closure; 0553 return fread (buf, 1, len, f); 0554 } 0555 0556 static int 0557 _XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len) 0558 { 0559 FILE *f = file->closure; 0560 return fwrite (buf, 1, len, f); 0561 } 0562 0563 static int 0564 _XcursorStdioFileSeek (XcursorFile *file, long offset, int whence) 0565 { 0566 FILE *f = file->closure; 0567 return fseek (f, offset, whence); 0568 } 0569 0570 static void 0571 _XcursorStdioFileInitialize (FILE *stdfile, XcursorFile *file) 0572 { 0573 file->closure = stdfile; 0574 file->read = _XcursorStdioFileRead; 0575 file->write = _XcursorStdioFileWrite; 0576 file->seek = _XcursorStdioFileSeek; 0577 } 0578 0579 XcursorImages * 0580 XcursorFileLoadImages (const char *file, int size) 0581 { 0582 XcursorFile f; 0583 XcursorImages *images; 0584 0585 FILE *fp = fopen(file, "r"); 0586 if (!fp) 0587 return NULL; 0588 0589 _XcursorStdioFileInitialize (fp, &f); 0590 images = XcursorXcFileLoadImages (&f, size); 0591 fclose(fp); 0592 0593 return images; 0594 }