From 25d9055281a744944fab3016afcb3460610fec89 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 14 Apr 2023 16:13:33 +0200 Subject: [PATCH] xcursor: sync with libwayland xcursor.c was copied over from libwayland-cursor. A lot of cosmetic imporvements have been merged upstream. Copy-paste the new version. --- include/xcursor/xcursor.h | 42 +- xcursor/wlr_xcursor.c | 8 +- xcursor/xcursor.c | 1133 ++++++++++++++++--------------------- 3 files changed, 515 insertions(+), 668 deletions(-) diff --git a/include/xcursor/xcursor.h b/include/xcursor/xcursor.h index c21707b4..459f8166 100644 --- a/include/xcursor/xcursor.h +++ b/include/xcursor/xcursor.h @@ -28,37 +28,31 @@ #include -typedef int XcursorBool; -typedef uint32_t XcursorUInt; - -typedef XcursorUInt XcursorDim; -typedef XcursorUInt XcursorPixel; - -typedef struct _XcursorImage { - XcursorUInt version; /* version of the image data */ - XcursorDim size; /* nominal size for matching */ - XcursorDim width; /* actual width */ - XcursorDim height; /* actual height */ - XcursorDim xhot; /* hot spot x (must be inside image) */ - XcursorDim yhot; /* hot spot y (must be inside image) */ - XcursorUInt delay; /* animation delay to next frame (ms) */ - XcursorPixel *pixels; /* pointer to pixels */ -} XcursorImage; +struct xcursor_image { + uint32_t version; /* version of the image data */ + uint32_t size; /* nominal size for matching */ + uint32_t width; /* actual width */ + uint32_t height; /* actual height */ + uint32_t xhot; /* hot spot x (must be inside image) */ + uint32_t yhot; /* hot spot y (must be inside image) */ + uint32_t delay; /* animation delay to next frame (ms) */ + uint32_t *pixels; /* pointer to pixels */ +}; /* * Other data structures exposed by the library API */ -typedef struct _XcursorImages { - int nimage; /* number of images */ - XcursorImage **images; /* array of XcursorImage pointers */ - char *name; /* name used to load images */ -} XcursorImages; +struct xcursor_images { + int nimage; /* number of images */ + struct xcursor_image **images; /* array of XcursorImage pointers */ + char *name; /* name used to load images */ +}; void -XcursorImagesDestroy (XcursorImages *images); +xcursor_images_destroy(struct xcursor_images *images); void xcursor_load_theme(const char *theme, int size, - void (*load_callback)(XcursorImages *, void *), - void *user_data); + void (*load_callback)(struct xcursor_images *, void *), + void *user_data); #endif diff --git a/xcursor/wlr_xcursor.c b/xcursor/wlr_xcursor.c index b2badcb7..597a0b13 100644 --- a/xcursor/wlr_xcursor.c +++ b/xcursor/wlr_xcursor.c @@ -117,7 +117,7 @@ static void load_default_theme(struct wlr_xcursor_theme *theme) { } static struct wlr_xcursor *xcursor_create_from_xcursor_images( - XcursorImages *images, struct wlr_xcursor_theme *theme) { + struct xcursor_images *images, struct wlr_xcursor_theme *theme) { struct wlr_xcursor *cursor = calloc(1, sizeof(*cursor)); if (!cursor) { return NULL; @@ -170,11 +170,11 @@ static struct wlr_xcursor *xcursor_create_from_xcursor_images( return cursor; } -static void load_callback(XcursorImages *images, void *data) { +static void load_callback(struct xcursor_images *images, void *data) { struct wlr_xcursor_theme *theme = data; if (wlr_xcursor_theme_get_cursor(theme, images->name)) { - XcursorImagesDestroy(images); + xcursor_images_destroy(images); return; } @@ -192,7 +192,7 @@ static void load_callback(XcursorImages *images, void *data) { } } - XcursorImagesDestroy(images); + xcursor_images_destroy(images); } struct wlr_xcursor_theme *wlr_xcursor_theme_load(const char *name, int size) { diff --git a/xcursor/xcursor.c b/xcursor/xcursor.c index c706e9a2..4db8ab4d 100644 --- a/xcursor/xcursor.c +++ b/xcursor/xcursor.c @@ -24,20 +24,14 @@ */ #define _DEFAULT_SOURCE -#include +#include #include #include #include +#include #include "config.h" #include "xcursor/xcursor.h" -/* - * From libXcursor/include/X11/extensions/Xcursor.h - */ - -#define XcursorTrue 1 -#define XcursorFalse 0 - /* * Cursor files start with a header. The header * contains a magic number, a version number and a @@ -69,43 +63,31 @@ * CARD32 position absolute file position */ -#define XCURSOR_MAGIC 0x72756358 /* "Xcur" LSBFirst */ - -/* - * Current Xcursor version number. Will be substituted by configure - * from the version in the libXcursor configure.ac file. - */ - -#define XCURSOR_LIB_MAJOR 1 -#define XCURSOR_LIB_MINOR 1 -#define XCURSOR_LIB_REVISION 13 -#define XCURSOR_LIB_VERSION ((XCURSOR_LIB_MAJOR * 10000) + \ - (XCURSOR_LIB_MINOR * 100) + \ - (XCURSOR_LIB_REVISION)) +#define XCURSOR_MAGIC 0x72756358 /* "Xcur" LSBFirst */ /* * This version number is stored in cursor files; changes to the * file format require updating this version number */ -#define XCURSOR_FILE_MAJOR 1 -#define XCURSOR_FILE_MINOR 0 -#define XCURSOR_FILE_VERSION ((XCURSOR_FILE_MAJOR << 16) | (XCURSOR_FILE_MINOR)) -#define XCURSOR_FILE_HEADER_LEN (4 * 4) -#define XCURSOR_FILE_TOC_LEN (3 * 4) +#define XCURSOR_FILE_MAJOR 1 +#define XCURSOR_FILE_MINOR 0 +#define XCURSOR_FILE_VERSION ((XCURSOR_FILE_MAJOR << 16) | (XCURSOR_FILE_MINOR)) +#define XCURSOR_FILE_HEADER_LEN (4 * 4) +#define XCURSOR_FILE_TOC_LEN (3 * 4) -typedef struct _XcursorFileToc { - XcursorUInt type; /* chunk type */ - XcursorUInt subtype; /* subtype (size for images) */ - XcursorUInt position; /* absolute position in file */ -} XcursorFileToc; +struct xcursor_file_toc { + uint32_t type; /* chunk type */ + uint32_t subtype; /* subtype (size for images) */ + uint32_t position; /* absolute position in file */ +}; -typedef struct _XcursorFileHeader { - XcursorUInt magic; /* magic number */ - XcursorUInt header; /* byte length of header */ - XcursorUInt version; /* file version number */ - XcursorUInt ntoc; /* number of toc entries */ - XcursorFileToc *tocs; /* table of contents */ -} XcursorFileHeader; +struct xcursor_file_header { + uint32_t magic; /* magic number */ + uint32_t header; /* byte length of header */ + uint32_t version; /* file version number */ + uint32_t ntoc; /* number of toc entries */ + struct xcursor_file_toc *tocs; /* table of contents */ +}; /* * The rest of the file is a list of chunks, each tagged by type @@ -123,42 +105,14 @@ typedef struct _XcursorFileHeader { * CARD32 version chunk type version */ -#define XCURSOR_CHUNK_HEADER_LEN (4 * 4) +#define XCURSOR_CHUNK_HEADER_LEN (4 * 4) -typedef struct _XcursorChunkHeader { - XcursorUInt header; /* bytes in chunk header */ - XcursorUInt type; /* chunk type */ - XcursorUInt subtype; /* chunk subtype (size for images) */ - XcursorUInt version; /* version of this type */ -} XcursorChunkHeader; - -/* - * Here's a list of the known chunk types - */ - -/* - * Comments consist of a 4-byte length field followed by - * UTF-8 encoded text - * - * Comment: - * ChunkHeader header chunk header - * CARD32 length bytes in text - * LISTofCARD8 text UTF-8 encoded text - */ - -#define XCURSOR_COMMENT_TYPE 0xfffe0001 -#define XCURSOR_COMMENT_VERSION 1 -#define XCURSOR_COMMENT_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (1 *4)) -#define XCURSOR_COMMENT_COPYRIGHT 1 -#define XCURSOR_COMMENT_LICENSE 2 -#define XCURSOR_COMMENT_OTHER 3 -#define XCURSOR_COMMENT_MAX_LEN 0x100000 - -typedef struct _XcursorComment { - XcursorUInt version; - XcursorUInt comment_type; - char *comment; -} XcursorComment; +struct xcursor_chunk_header { + uint32_t header; /* bytes in chunk header */ + uint32_t type; /* chunk type */ + uint32_t subtype; /* chunk subtype (size for images) */ + uint32_t version; /* version of this type */ +}; /* * Each cursor image occupies a separate image chunk. @@ -176,439 +130,354 @@ typedef struct _XcursorComment { * LISTofCARD32 pixels ARGB pixels */ -#define XCURSOR_IMAGE_TYPE 0xfffd0002 -#define XCURSOR_IMAGE_VERSION 1 -#define XCURSOR_IMAGE_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (5*4)) -#define XCURSOR_IMAGE_MAX_SIZE 0x7fff /* 32767x32767 max cursor size */ - -typedef struct _XcursorFile XcursorFile; - -struct _XcursorFile { - void *closure; - int (*read) (XcursorFile *file, unsigned char *buf, int len); - int (*write) (XcursorFile *file, unsigned char *buf, int len); - int (*seek) (XcursorFile *file, long offset, int whence); -}; - -typedef struct _XcursorComments { - int ncomment; /* number of comments */ - XcursorComment **comments; /* array of XcursorComment pointers */ -} XcursorComments; +#define XCURSOR_IMAGE_TYPE 0xfffd0002 +#define XCURSOR_IMAGE_VERSION 1 +#define XCURSOR_IMAGE_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (5*4)) +#define XCURSOR_IMAGE_MAX_SIZE 0x7fff /* 32767x32767 max cursor size */ /* * From libXcursor/src/file.c */ -static XcursorImage * -XcursorImageCreate (int width, int height) +static struct xcursor_image * +xcursor_image_create(int width, int height) { - XcursorImage *image; + struct xcursor_image *image; - if (width < 0 || height < 0) - return NULL; - if (width > XCURSOR_IMAGE_MAX_SIZE || height > XCURSOR_IMAGE_MAX_SIZE) - return NULL; + if (width < 0 || height < 0) + return NULL; + if (width > XCURSOR_IMAGE_MAX_SIZE || height > XCURSOR_IMAGE_MAX_SIZE) + return NULL; - image = malloc (sizeof (XcursorImage) + - width * height * sizeof (XcursorPixel)); - if (!image) - return NULL; - image->version = XCURSOR_IMAGE_VERSION; - image->pixels = (XcursorPixel *) (image + 1); - image->size = width > height ? width : height; - image->width = width; - image->height = height; - image->delay = 0; - return image; + image = malloc(sizeof(struct xcursor_image) + + width * height * sizeof(uint32_t)); + if (!image) + return NULL; + image->version = XCURSOR_IMAGE_VERSION; + image->pixels = (uint32_t *) (image + 1); + image->size = width > height ? width : height; + image->width = width; + image->height = height; + image->delay = 0; + return image; } static void -XcursorImageDestroy (XcursorImage *image) +xcursor_image_destroy(struct xcursor_image *image) { - free (image); + free(image); } -static XcursorImages * -XcursorImagesCreate (int size) +static struct xcursor_images * +xcursor_images_create(int size) { - XcursorImages *images; + struct xcursor_images *images; - images = malloc (sizeof (XcursorImages) + - size * sizeof (XcursorImage *)); - if (!images) - return NULL; - images->nimage = 0; - images->images = (XcursorImage **) (images + 1); - images->name = NULL; - return images; + images = malloc(sizeof(struct xcursor_images) + + size * sizeof(struct xcursor_image *)); + if (!images) + return NULL; + images->nimage = 0; + images->images = (struct xcursor_image **) (images + 1); + images->name = NULL; + return images; } void -XcursorImagesDestroy (XcursorImages *images) +xcursor_images_destroy(struct xcursor_images *images) { - int n; + int n; - if (!images) - return; + if (!images) + return; - for (n = 0; n < images->nimage; n++) - XcursorImageDestroy (images->images[n]); - if (images->name) - free (images->name); - free (images); + for (n = 0; n < images->nimage; n++) + xcursor_image_destroy(images->images[n]); + free(images->name); + free(images); +} + +static bool +xcursor_read_uint(FILE *file, uint32_t *u) +{ + unsigned char bytes[4]; + + if (!file || !u) + return false; + + if (fread(bytes, 1, 4, file) != 4) + return false; + + *u = ((uint32_t)(bytes[0]) << 0) | + ((uint32_t)(bytes[1]) << 8) | + ((uint32_t)(bytes[2]) << 16) | + ((uint32_t)(bytes[3]) << 24); + return true; } static void -XcursorImagesSetName (XcursorImages *images, const char *name) +xcursor_file_header_destroy(struct xcursor_file_header *file_header) { - char *new; - - if (!images || !name) - return; - - new = malloc (strlen (name) + 1); - - if (!new) - return; - - strcpy (new, name); - if (images->name) - free (images->name); - images->name = new; + free(file_header); } -static XcursorBool -_XcursorReadUInt (XcursorFile *file, XcursorUInt *u) +static struct xcursor_file_header * +xcursor_file_header_create(uint32_t ntoc) { - unsigned char bytes[4]; + struct xcursor_file_header *file_header; - if (!file || !u) - return XcursorFalse; - - if ((*file->read) (file, bytes, 4) != 4) - return XcursorFalse; - - *u = ((XcursorUInt)(bytes[0]) << 0) | - ((XcursorUInt)(bytes[1]) << 8) | - ((XcursorUInt)(bytes[2]) << 16) | - ((XcursorUInt)(bytes[3]) << 24); - return XcursorTrue; + if (ntoc > 0x10000) + return NULL; + file_header = malloc(sizeof(struct xcursor_file_header) + + ntoc * sizeof(struct xcursor_file_toc)); + if (!file_header) + return NULL; + file_header->magic = XCURSOR_MAGIC; + file_header->header = XCURSOR_FILE_HEADER_LEN; + file_header->version = XCURSOR_FILE_VERSION; + file_header->ntoc = ntoc; + file_header->tocs = (struct xcursor_file_toc *) (file_header + 1); + return file_header; } -static void -_XcursorFileHeaderDestroy (XcursorFileHeader *fileHeader) +static struct xcursor_file_header * +xcursor_read_file_header(FILE *file) { - free (fileHeader); -} + struct xcursor_file_header head, *file_header; + uint32_t skip; + unsigned int n; -static XcursorFileHeader * -_XcursorFileHeaderCreate (XcursorUInt ntoc) -{ - XcursorFileHeader *fileHeader; + if (!file) + return NULL; - if (ntoc > 0x10000) - return NULL; - fileHeader = malloc (sizeof (XcursorFileHeader) + - ntoc * sizeof (XcursorFileToc)); - if (!fileHeader) - return NULL; - fileHeader->magic = XCURSOR_MAGIC; - fileHeader->header = XCURSOR_FILE_HEADER_LEN; - fileHeader->version = XCURSOR_FILE_VERSION; - fileHeader->ntoc = ntoc; - fileHeader->tocs = (XcursorFileToc *) (fileHeader + 1); - return fileHeader; -} - -static XcursorFileHeader * -_XcursorReadFileHeader (XcursorFile *file) -{ - XcursorFileHeader head, *fileHeader; - XcursorUInt skip; - unsigned int n; - - if (!file) - return NULL; - - if (!_XcursorReadUInt (file, &head.magic)) - return NULL; - if (head.magic != XCURSOR_MAGIC) - return NULL; - if (!_XcursorReadUInt (file, &head.header)) - return NULL; - if (!_XcursorReadUInt (file, &head.version)) - return NULL; - if (!_XcursorReadUInt (file, &head.ntoc)) - return NULL; - skip = head.header - XCURSOR_FILE_HEADER_LEN; - if (skip) - if ((*file->seek) (file, skip, SEEK_CUR) == EOF) - return NULL; - fileHeader = _XcursorFileHeaderCreate (head.ntoc); - if (!fileHeader) - return NULL; - fileHeader->magic = head.magic; - fileHeader->header = head.header; - fileHeader->version = head.version; - fileHeader->ntoc = head.ntoc; - for (n = 0; n < fileHeader->ntoc; n++) - { - if (!_XcursorReadUInt (file, &fileHeader->tocs[n].type)) - break; - if (!_XcursorReadUInt (file, &fileHeader->tocs[n].subtype)) - break; - if (!_XcursorReadUInt (file, &fileHeader->tocs[n].position)) - break; - } - if (n != fileHeader->ntoc) - { - _XcursorFileHeaderDestroy (fileHeader); - return NULL; - } - return fileHeader; -} - -static XcursorBool -_XcursorSeekToToc (XcursorFile *file, - XcursorFileHeader *fileHeader, - int toc) -{ - if (!file || !fileHeader || \ - (*file->seek) (file, fileHeader->tocs[toc].position, SEEK_SET) == EOF) - return XcursorFalse; - return XcursorTrue; -} - -static XcursorBool -_XcursorFileReadChunkHeader (XcursorFile *file, - XcursorFileHeader *fileHeader, - int toc, - XcursorChunkHeader *chunkHeader) -{ - if (!file || !fileHeader || !chunkHeader) - return XcursorFalse; - if (!_XcursorSeekToToc (file, fileHeader, toc)) - return XcursorFalse; - if (!_XcursorReadUInt (file, &chunkHeader->header)) - return XcursorFalse; - if (!_XcursorReadUInt (file, &chunkHeader->type)) - return XcursorFalse; - if (!_XcursorReadUInt (file, &chunkHeader->subtype)) - return XcursorFalse; - if (!_XcursorReadUInt (file, &chunkHeader->version)) - return XcursorFalse; - /* sanity check */ - if (chunkHeader->type != fileHeader->tocs[toc].type || - chunkHeader->subtype != fileHeader->tocs[toc].subtype) - return XcursorFalse; - return XcursorTrue; -} - -#define dist(a,b) ((a) > (b) ? (a) - (b) : (b) - (a)) - -static XcursorDim -_XcursorFindBestSize (XcursorFileHeader *fileHeader, - XcursorDim size, - int *nsizesp) -{ - unsigned int n; - int nsizes = 0; - XcursorDim bestSize = 0; - XcursorDim thisSize; - - if (!fileHeader || !nsizesp) - return 0; - - for (n = 0; n < fileHeader->ntoc; n++) - { - if (fileHeader->tocs[n].type != XCURSOR_IMAGE_TYPE) - continue; - thisSize = fileHeader->tocs[n].subtype; - if (!bestSize || dist (thisSize, size) < dist (bestSize, size)) - { - bestSize = thisSize; - nsizes = 1; + if (!xcursor_read_uint(file, &head.magic)) + return NULL; + if (head.magic != XCURSOR_MAGIC) + return NULL; + if (!xcursor_read_uint(file, &head.header)) + return NULL; + if (!xcursor_read_uint(file, &head.version)) + return NULL; + if (!xcursor_read_uint(file, &head.ntoc)) + return NULL; + skip = head.header - XCURSOR_FILE_HEADER_LEN; + if (skip) + if (fseek(file, skip, SEEK_CUR) == EOF) + return NULL; + file_header = xcursor_file_header_create(head.ntoc); + if (!file_header) + return NULL; + file_header->magic = head.magic; + file_header->header = head.header; + file_header->version = head.version; + file_header->ntoc = head.ntoc; + for (n = 0; n < file_header->ntoc; n++) { + if (!xcursor_read_uint(file, &file_header->tocs[n].type)) + break; + if (!xcursor_read_uint(file, &file_header->tocs[n].subtype)) + break; + if (!xcursor_read_uint(file, &file_header->tocs[n].position)) + break; } - else if (thisSize == bestSize) - nsizes++; - } - *nsizesp = nsizes; - return bestSize; -} - -static int -_XcursorFindImageToc (XcursorFileHeader *fileHeader, - XcursorDim size, - int count) -{ - unsigned int toc; - XcursorDim thisSize; - - if (!fileHeader) - return 0; - - for (toc = 0; toc < fileHeader->ntoc; toc++) - { - if (fileHeader->tocs[toc].type != XCURSOR_IMAGE_TYPE) - continue; - thisSize = fileHeader->tocs[toc].subtype; - if (thisSize != size) - continue; - if (!count) - break; - count--; - } - if (toc == fileHeader->ntoc) - return -1; - return toc; -} - -static XcursorImage * -_XcursorReadImage (XcursorFile *file, - XcursorFileHeader *fileHeader, - int toc) -{ - XcursorChunkHeader chunkHeader; - XcursorImage head; - XcursorImage *image; - int n; - XcursorPixel *p; - - if (!file || !fileHeader) - return NULL; - - if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader)) - return NULL; - if (!_XcursorReadUInt (file, &head.width)) - return NULL; - if (!_XcursorReadUInt (file, &head.height)) - return NULL; - if (!_XcursorReadUInt (file, &head.xhot)) - return NULL; - if (!_XcursorReadUInt (file, &head.yhot)) - return NULL; - if (!_XcursorReadUInt (file, &head.delay)) - return NULL; - /* sanity check data */ - if (head.width > XCURSOR_IMAGE_MAX_SIZE || - head.height > XCURSOR_IMAGE_MAX_SIZE) - return NULL; - if (head.width == 0 || head.height == 0) - return NULL; - if (head.xhot > head.width || head.yhot > head.height) - return NULL; - - /* Create the image and initialize it */ - image = XcursorImageCreate (head.width, head.height); - if (image == NULL) - return NULL; - if (chunkHeader.version < image->version) - image->version = chunkHeader.version; - image->size = chunkHeader.subtype; - image->xhot = head.xhot; - image->yhot = head.yhot; - image->delay = head.delay; - n = image->width * image->height; - p = image->pixels; - while (n--) - { - if (!_XcursorReadUInt (file, p)) - { - XcursorImageDestroy (image); - return NULL; + if (n != file_header->ntoc) { + xcursor_file_header_destroy(file_header); + return NULL; } - p++; - } - return image; + return file_header; } -static XcursorImages * -XcursorXcFileLoadImages (XcursorFile *file, int size) +static bool +xcursor_seek_to_toc(FILE *file, + struct xcursor_file_header *file_header, + int toc) { - XcursorFileHeader *fileHeader; - XcursorDim bestSize; - int nsize; - XcursorImages *images; - int n; - int toc; + if (!file || !file_header || + fseek(file, file_header->tocs[toc].position, SEEK_SET) == EOF) + return false; + return true; +} - if (!file || size < 0) - return NULL; - fileHeader = _XcursorReadFileHeader (file); - if (!fileHeader) - return NULL; - bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize); - if (!bestSize) - { - _XcursorFileHeaderDestroy (fileHeader); - return NULL; - } - images = XcursorImagesCreate (nsize); - if (!images) - { - _XcursorFileHeaderDestroy (fileHeader); - return NULL; - } - for (n = 0; n < nsize; n++) - { - toc = _XcursorFindImageToc (fileHeader, bestSize, n); - if (toc < 0) - break; - images->images[images->nimage] = _XcursorReadImage (file, fileHeader, - toc); - if (!images->images[images->nimage]) - break; - images->nimage++; - } - _XcursorFileHeaderDestroy (fileHeader); - if (images->nimage != nsize) - { - XcursorImagesDestroy (images); - images = NULL; - } - return images; +static bool +xcursor_file_read_chunk_header(FILE *file, + struct xcursor_file_header *file_header, + int toc, + struct xcursor_chunk_header *chunk_header) +{ + if (!file || !file_header || !chunk_header) + return false; + if (!xcursor_seek_to_toc(file, file_header, toc)) + return false; + if (!xcursor_read_uint(file, &chunk_header->header)) + return false; + if (!xcursor_read_uint(file, &chunk_header->type)) + return false; + if (!xcursor_read_uint(file, &chunk_header->subtype)) + return false; + if (!xcursor_read_uint(file, &chunk_header->version)) + return false; + /* sanity check */ + if (chunk_header->type != file_header->tocs[toc].type || + chunk_header->subtype != file_header->tocs[toc].subtype) + return false; + return true; +} + +static uint32_t +dist(uint32_t a, uint32_t b) +{ + return a > b ? a - b : b - a; +} + +static uint32_t +xcursor_file_best_size(struct xcursor_file_header *file_header, + uint32_t size, int *nsizesp) +{ + unsigned int n; + int nsizes = 0; + uint32_t best_size = 0; + uint32_t this_size; + + if (!file_header || !nsizesp) + return 0; + + for (n = 0; n < file_header->ntoc; n++) { + if (file_header->tocs[n].type != XCURSOR_IMAGE_TYPE) + continue; + this_size = file_header->tocs[n].subtype; + if (!best_size || dist(this_size, size) < dist(best_size, size)) { + best_size = this_size; + nsizes = 1; + } else if (this_size == best_size) { + nsizes++; + } + } + *nsizesp = nsizes; + return best_size; } static int -_XcursorStdioFileRead (XcursorFile *file, unsigned char *buf, int len) +xcursor_find_image_toc(struct xcursor_file_header *file_header, + uint32_t size, int count) { - FILE *f = file->closure; - return fread (buf, 1, len, f); + unsigned int toc; + uint32_t this_size; + + if (!file_header) + return 0; + + for (toc = 0; toc < file_header->ntoc; toc++) { + if (file_header->tocs[toc].type != XCURSOR_IMAGE_TYPE) + continue; + this_size = file_header->tocs[toc].subtype; + if (this_size != size) + continue; + if (!count) + break; + count--; + } + if (toc == file_header->ntoc) + return -1; + return toc; } -static int -_XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len) +static struct xcursor_image * +xcursor_read_image(FILE *file, + struct xcursor_file_header *file_header, + int toc) { - FILE *f = file->closure; - return fwrite (buf, 1, len, f); + struct xcursor_chunk_header chunk_header; + struct xcursor_image head; + struct xcursor_image *image; + int n; + uint32_t *p; + + if (!file || !file_header) + return NULL; + + if (!xcursor_file_read_chunk_header(file, file_header, toc, &chunk_header)) + return NULL; + if (!xcursor_read_uint(file, &head.width)) + return NULL; + if (!xcursor_read_uint(file, &head.height)) + return NULL; + if (!xcursor_read_uint(file, &head.xhot)) + return NULL; + if (!xcursor_read_uint(file, &head.yhot)) + return NULL; + if (!xcursor_read_uint(file, &head.delay)) + return NULL; + /* sanity check data */ + if (head.width > XCURSOR_IMAGE_MAX_SIZE || + head.height > XCURSOR_IMAGE_MAX_SIZE) + return NULL; + if (head.width == 0 || head.height == 0) + return NULL; + if (head.xhot > head.width || head.yhot > head.height) + return NULL; + + /* Create the image and initialize it */ + image = xcursor_image_create(head.width, head.height); + if (image == NULL) + return NULL; + if (chunk_header.version < image->version) + image->version = chunk_header.version; + image->size = chunk_header.subtype; + image->xhot = head.xhot; + image->yhot = head.yhot; + image->delay = head.delay; + n = image->width * image->height; + p = image->pixels; + while (n--) { + if (!xcursor_read_uint(file, p)) { + xcursor_image_destroy(image); + return NULL; + } + p++; + } + return image; } -static int -_XcursorStdioFileSeek (XcursorFile *file, long offset, int whence) +static struct xcursor_images * +xcursor_xc_file_load_images(FILE *file, int size) { - FILE *f = file->closure; - return fseek (f, offset, whence); -} + struct xcursor_file_header *file_header; + uint32_t best_size; + int nsize; + struct xcursor_images *images; + int n; + int toc; -static void -_XcursorStdioFileInitialize (FILE *stdfile, XcursorFile *file) -{ - file->closure = stdfile; - file->read = _XcursorStdioFileRead; - file->write = _XcursorStdioFileWrite; - file->seek = _XcursorStdioFileSeek; -} - -static XcursorImages * -XcursorFileLoadImages (FILE *file, int size) -{ - XcursorFile f; - - if (!file) - return NULL; - - _XcursorStdioFileInitialize (file, &f); - return XcursorXcFileLoadImages (&f, size); + if (!file || size < 0) + return NULL; + file_header = xcursor_read_file_header(file); + if (!file_header) + return NULL; + best_size = xcursor_file_best_size(file_header, (uint32_t) size, &nsize); + if (!best_size) { + xcursor_file_header_destroy(file_header); + return NULL; + } + images = xcursor_images_create(nsize); + if (!images) { + xcursor_file_header_destroy(file_header); + return NULL; + } + for (n = 0; n < nsize; n++) { + toc = xcursor_find_image_toc(file_header, best_size, n); + if (toc < 0) + break; + images->images[images->nimage] = xcursor_read_image(file, file_header, + toc); + if (!images->images[images->nimage]) + break; + images->nimage++; + } + xcursor_file_header_destroy(file_header); + if (images->nimage != nsize) { + xcursor_images_destroy(images); + images = NULL; + } + return images; } /* @@ -620,7 +489,7 @@ XcursorFileLoadImages (FILE *file, int size) #endif #ifndef XCURSORPATH -#define XCURSORPATH "~/.local/share/icons:~/.icons:/usr/share/icons:/usr/share/pixmaps:~/.cursors:/usr/share/cursors/xorg-x11:"ICONDIR +#define XCURSORPATH "~/.icons:/usr/share/icons:/usr/share/pixmaps:~/.cursors:/usr/share/cursors/xorg-x11:"ICONDIR #endif #define XDG_DATA_HOME_FALLBACK "~/.local/share" @@ -636,213 +505,200 @@ XcursorFileLoadImages (FILE *file, int size) * and must be freed by the caller. */ static char * -XcursorLibraryPath (void) +xcursor_library_path(void) { - const char *env_var; - char *path = NULL; - int pathlen = 0; + const char *env_var, *suffix; + char *path; + size_t path_size; env_var = getenv("XCURSOR_PATH"); - if (env_var) { - path = strdup(env_var); - } - else { - env_var = getenv("XDG_DATA_HOME"); - if (env_var && env_var[0] == '/') { - pathlen = strlen(env_var) + - strlen(CURSORDIR ":" XCURSORPATH) + 1; - path = malloc(pathlen); - snprintf(path, pathlen, "%s%s", env_var, - CURSORDIR ":" XCURSORPATH); - } - else { - path = strdup(XDG_DATA_HOME_FALLBACK ":" XCURSORPATH); - } - } + if (env_var) + return strdup(env_var); + + env_var = getenv("XDG_DATA_HOME"); + if (!env_var || env_var[0] != '/') + env_var = XDG_DATA_HOME_FALLBACK; + + suffix = CURSORDIR ":" XCURSORPATH; + path_size = strlen(env_var) + strlen(suffix) + 1; + path = malloc(path_size); + if (!path) + return NULL; + snprintf(path, path_size, "%s%s", env_var, suffix); return path; } -static void -_XcursorAddPathElt (char *path, const char *elt, int len) +static char * +xcursor_build_theme_dir(const char *dir, const char *theme) { - int pathlen = strlen (path); + const char *colon; + const char *tcolon; + char *full; + const char *home, *homesep; + int dirlen; + int homelen; + int themelen; + size_t full_size; - /* append / if the path doesn't currently have one */ - if (path[0] == '\0' || path[pathlen - 1] != '/') - { - strcat (path, "/"); - pathlen++; - } - if (len == -1) - len = strlen (elt); - /* strip leading slashes */ - while (len && elt[0] == '/') - { - elt++; - len--; - } - memcpy (path + pathlen, elt, len); - path[pathlen + len] = '\0'; + if (!dir || !theme) + return NULL; + + colon = strchr(dir, ':'); + if (!colon) + colon = dir + strlen(dir); + + dirlen = colon - dir; + + tcolon = strchr(theme, ':'); + if (!tcolon) + tcolon = theme + strlen(theme); + + themelen = tcolon - theme; + + home = ""; + homelen = 0; + homesep = ""; + if (*dir == '~') { + home = getenv("HOME"); + if (!home) + return NULL; + homelen = strlen(home); + homesep = "/"; + dir++; + dirlen--; + } + + /* + * add space for any needed directory separators, one per component, + * and one for the trailing null + */ + full_size = 1 + homelen + 1 + dirlen + 1 + themelen + 1; + full = malloc(full_size); + if (!full) + return NULL; + snprintf(full, full_size, "%s%s%.*s/%.*s", home, homesep, + dirlen, dir, themelen, theme); + return full; } static char * -_XcursorBuildThemeDir (const char *dir, const char *theme) +xcursor_build_fullname(const char *dir, const char *subdir, const char *file) { - const char *colon; - const char *tcolon; - char *full; - char *home; - int dirlen; - int homelen; - int themelen; - int len; + char *full; + size_t full_size; - if (!dir || !theme) - return NULL; + if (!dir || !subdir || !file) + return NULL; - colon = strchr (dir, ':'); - if (!colon) - colon = dir + strlen (dir); - - dirlen = colon - dir; - - tcolon = strchr (theme, ':'); - if (!tcolon) - tcolon = theme + strlen (theme); - - themelen = tcolon - theme; - - home = NULL; - homelen = 0; - if (*dir == '~') - { - home = getenv ("HOME"); - if (!home) - return NULL; - homelen = strlen (home); - dir++; - dirlen--; - } - - /* - * add space for any needed directory separators, one per component, - * and one for the trailing null - */ - len = 1 + homelen + 1 + dirlen + 1 + themelen + 1; - - full = malloc (len); - if (!full) - return NULL; - full[0] = '\0'; - - if (home) - _XcursorAddPathElt (full, home, -1); - _XcursorAddPathElt (full, dir, dirlen); - _XcursorAddPathElt (full, theme, themelen); - return full; -} - -static char * -_XcursorBuildFullname (const char *dir, const char *subdir, const char *file) -{ - char *full; - - if (!dir || !subdir || !file) - return NULL; - - full = malloc (strlen (dir) + 1 + strlen (subdir) + 1 + strlen (file) + 1); - if (!full) - return NULL; - full[0] = '\0'; - _XcursorAddPathElt (full, dir, -1); - _XcursorAddPathElt (full, subdir, -1); - _XcursorAddPathElt (full, file, -1); - return full; + full_size = strlen(dir) + 1 + strlen(subdir) + 1 + strlen(file) + 1; + full = malloc(full_size); + if (!full) + return NULL; + snprintf(full, full_size, "%s/%s/%s", dir, subdir, file); + return full; } static const char * -_XcursorNextPath (const char *path) +xcursor_next_path(const char *path) { - char *colon = strchr (path, ':'); + char *colon = strchr(path, ':'); - if (!colon) - return NULL; - return colon + 1; + if (!colon) + return NULL; + return colon + 1; } -#define XcursorWhite(c) ((c) == ' ' || (c) == '\t' || (c) == '\n') -#define XcursorSep(c) ((c) == ';' || (c) == ',') +static bool +xcursor_white(char c) +{ + return c == ' ' || c == '\t' || c == '\n'; +} + +static bool +xcursor_sep(char c) +{ + return c == ';' || c == ','; +} static char * -_XcursorThemeInherits (const char *full) +xcursor_theme_inherits(const char *full) { - char line[8192]; - char *result = NULL; - FILE *f; + char *line = NULL; + size_t line_size = 0; + char *result = NULL; + FILE *f; - if (!full) - return NULL; + if (!full) + return NULL; - f = fopen (full, "r"); - if (f) - { - while (fgets (line, sizeof (line), f)) - { - if (!strncmp (line, "Inherits", 8)) - { - char *l = line + 8; - char *r; - while (*l == ' ') l++; - if (*l != '=') continue; + f = fopen(full, "r"); + if (!f) + return NULL; + + while (getline(&line, &line_size, f) >= 0) { + const char *l; + char *r; + + if (strncmp(line, "Inherits", 8)) + continue; + + l = line + 8; + while (*l == ' ') + l++; + if (*l != '=') + continue; l++; - while (*l == ' ') l++; - result = malloc (strlen (l) + 1); - if (result) - { - r = result; - while (*l) - { - while (XcursorSep(*l) || XcursorWhite (*l)) l++; + while (*l == ' ') + l++; + result = malloc(strlen(l) + 1); + if (!result) + break; + + r = result; + while (*l) { + while (xcursor_sep(*l) || xcursor_white(*l)) + l++; if (!*l) - break; + break; if (r != result) - *r++ = ':'; - while (*l && !XcursorWhite(*l) && - !XcursorSep(*l)) - *r++ = *l++; - } - *r++ = '\0'; + *r++ = ':'; + while (*l && !xcursor_white(*l) && !xcursor_sep(*l)) + *r++ = *l++; } + *r++ = '\0'; + break; - } } - fclose (f); - } - return result; + + fclose(f); + free(line); + + return result; } static void load_all_cursors_from_dir(const char *path, int size, - void (*load_callback)(XcursorImages *, void *), + void (*load_callback)(struct xcursor_images *, void *), void *user_data) { FILE *f; DIR *dir = opendir(path); struct dirent *ent; char *full; - XcursorImages *images; + struct xcursor_images *images; if (!dir) return; - for(ent = readdir(dir); ent; ent = readdir(dir)) { + for (ent = readdir(dir); ent; ent = readdir(dir)) { #ifdef _DIRENT_HAVE_D_TYPE if (ent->d_type != DT_UNKNOWN && - (ent->d_type != DT_REG && ent->d_type != DT_LNK)) + ent->d_type != DT_REG && + ent->d_type != DT_LNK) continue; #endif - full = _XcursorBuildFullname(path, "", ent->d_name); + full = xcursor_build_fullname(path, "", ent->d_name); if (!full) continue; @@ -852,14 +708,14 @@ load_all_cursors_from_dir(const char *path, int size, continue; } - images = XcursorFileLoadImages(f, size); + images = xcursor_xc_file_load_images(f, size); if (images) { - XcursorImagesSetName(images, ent->d_name); + images->name = strdup(ent->d_name); load_callback(images, user_data); } - fclose (f); + fclose(f); free(full); } @@ -869,63 +725,60 @@ load_all_cursors_from_dir(const char *path, int size, /** Load all the cursor of a theme * * This function loads all the cursor images of a given theme and its - * inherited themes. Each cursor is loaded into an XcursorImages object + * inherited themes. Each cursor is loaded into an struct xcursor_images object * which is passed to the caller's load callback. If a cursor appears * more than once across all the inherited themes, the load callback - * will be called multiple times, with possibly different XcursorImages + * will be called multiple times, with possibly different struct xcursor_images * object which have the same name. The user is expected to destroy the - * XcursorImages objects passed to the callback with - * XcursorImagesDestroy(). + * struct xcursor_images objects passed to the callback with + * xcursor_images_destroy(). * * \param theme The name of theme that should be loaded * \param size The desired size of the cursor images * \param load_callback A callback function that will be called - * for each cursor loaded. The first parameter is the XcursorImages + * for each cursor loaded. The first parameter is the struct xcursor_images * object representing the loaded cursor and the second is a pointer * to data provided by the user. * \param user_data The data that should be passed to the load callback */ void xcursor_load_theme(const char *theme, int size, - void (*load_callback)(XcursorImages *, void *), - void *user_data) + void (*load_callback)(struct xcursor_images *, void *), + void *user_data) { char *full, *dir; char *inherits = NULL; - char *xcursor_path = NULL; const char *path, *i; + char *xcursor_path; if (!theme) theme = "default"; - xcursor_path = XcursorLibraryPath(); - for (path = xcursor_path; path; path = _XcursorNextPath(path)) { - dir = _XcursorBuildThemeDir(path, theme); + + xcursor_path = xcursor_library_path(); + for (path = xcursor_path; + path; + path = xcursor_next_path(path)) { + dir = xcursor_build_theme_dir(path, theme); if (!dir) continue; - full = _XcursorBuildFullname(dir, "cursors", ""); - - if (full) { - load_all_cursors_from_dir(full, size, load_callback, - user_data); - free(full); - } + full = xcursor_build_fullname(dir, "cursors", ""); + load_all_cursors_from_dir(full, size, load_callback, + user_data); + free(full); if (!inherits) { - full = _XcursorBuildFullname(dir, "", "index.theme"); - if (full) { - inherits = _XcursorThemeInherits(full); - free(full); - } + full = xcursor_build_fullname(dir, "", "index.theme"); + inherits = xcursor_theme_inherits(full); + free(full); } free(dir); } - for (i = inherits; i; i = _XcursorNextPath(i)) + for (i = inherits; i; i = xcursor_next_path(i)) xcursor_load_theme(i, size, load_callback, user_data); - if (inherits) - free(inherits); + free(inherits); free(xcursor_path); }