mirror of
				https://gitlab.freedesktop.org/wayland/wayland.git
				synced 2025-10-29 05:40:16 -04:00 
			
		
		
		
	Introduce libwayland-cursor, a cursor helper library
The purpose of this library is to be the equivalent of libXcursor in the X world. This library is compatible with X cursor themes and loads them directly into an shm pool making it easy for the clients to get buffer for each cursor image. The code for handling the X cursor theme was taken from libXcursor. The files cursor/xcursor.[ch] are a stripped down version of that library containing only the interfaces necessary for implementing the wayland counterpart.
This commit is contained in:
		
							parent
							
								
									ff0d745674
								
							
						
					
					
						commit
						775002c6c0
					
				
					 8 changed files with 1375 additions and 1 deletions
				
			
		
							
								
								
									
										14
									
								
								cursor/Makefile.am
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								cursor/Makefile.am
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| lib_LTLIBRARIES = libwayland-cursor.la | ||||
| 
 | ||||
| include_HEADERS = wayland-cursor.h | ||||
| 
 | ||||
| libwayland_cursor_la_SOURCES =			\ | ||||
| 	wayland-cursor.c			\ | ||||
| 	xcursor.c				\ | ||||
| 	xcursor.h | ||||
| 
 | ||||
| pkgconfigdir = $(libdir)/pkgconfig | ||||
| pkgconfig_DATA = wayland-cursor.pc | ||||
| 
 | ||||
| AM_CFLAGS = $(GCC_CFLAGS) -I../src | ||||
| LDADD = $(top_builddir)/src/libwayland-client.la | ||||
							
								
								
									
										356
									
								
								cursor/wayland-cursor.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										356
									
								
								cursor/wayland-cursor.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,356 @@ | |||
| /*
 | ||||
|  * Copyright © 2012 Intel Corporation | ||||
|  * | ||||
|  * Permission to use, copy, modify, distribute, and sell this software and its | ||||
|  * documentation for any purpose is hereby granted without fee, provided that | ||||
|  * the above copyright notice appear in all copies and that both that copyright | ||||
|  * notice and this permission notice appear in supporting documentation, and | ||||
|  * that the name of the copyright holders not be used in advertising or | ||||
|  * publicity pertaining to distribution of the software without specific, | ||||
|  * written prior permission.  The copyright holders make no representations | ||||
|  * about the suitability of this software for any purpose.  It is provided "as | ||||
|  * is" without express or implied warranty. | ||||
|  * | ||||
|  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||||
|  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||||
|  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR | ||||
|  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | ||||
|  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | ||||
|  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | ||||
|  * OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include "xcursor.h" | ||||
| #include "wayland-cursor.h" | ||||
| #include "wayland-client.h" | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <unistd.h> | ||||
| #include <sys/mman.h> | ||||
| 
 | ||||
| struct shm_pool { | ||||
| 	struct wl_shm_pool *pool; | ||||
| 	int fd; | ||||
| 	unsigned int size; | ||||
| 	unsigned int used; | ||||
| 	char *data; | ||||
| }; | ||||
| 
 | ||||
| static struct shm_pool * | ||||
| shm_pool_create(struct wl_shm *shm, int size) | ||||
| { | ||||
| 	struct shm_pool *pool; | ||||
| 	char filename[] = "/tmp/wayland-shm-XXXXXX"; | ||||
| 
 | ||||
| 	pool = malloc(sizeof *pool); | ||||
| 	if (!pool) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	pool->fd = mkstemp(filename); | ||||
| 	if (pool->fd < 0) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	if (ftruncate(pool->fd, size) < 0) { | ||||
| 		close(pool->fd); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	pool->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, | ||||
| 			  pool->fd, 0); | ||||
| 	unlink(filename); | ||||
| 
 | ||||
| 	if (pool->data == MAP_FAILED) { | ||||
| 		close(pool->fd); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	pool->pool = wl_shm_create_pool(shm, pool->fd, size); | ||||
| 	pool->size = size; | ||||
| 	pool->used = 0; | ||||
| 
 | ||||
| 	return pool; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| shm_pool_resize(struct shm_pool *pool, int size) | ||||
| { | ||||
| 	if (ftruncate(pool->fd, size) < 0) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	wl_shm_pool_resize(pool->pool, size); | ||||
| 
 | ||||
| 	munmap(pool->data, pool->size); | ||||
| 
 | ||||
| 	pool->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, | ||||
| 			  pool->fd, 0); | ||||
| 	pool->size = size; | ||||
| 
 | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| shm_pool_allocate(struct shm_pool *pool, int size) | ||||
| { | ||||
| 	int offset; | ||||
| 
 | ||||
| 	if (pool->used + size > pool->size) | ||||
| 		if (!shm_pool_resize(pool, 2 * pool->size + size)) | ||||
| 			return -1; | ||||
| 
 | ||||
| 	offset = pool->used; | ||||
| 	pool->used += size; | ||||
| 
 | ||||
| 	return offset; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| shm_pool_destroy(struct shm_pool *pool) | ||||
| { | ||||
| 	munmap(pool->data, pool->size); | ||||
| 	wl_shm_pool_destroy(pool->pool); | ||||
| 	free(pool); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static const char *cursor_names[] = { | ||||
| 	"bottom_left_corner", | ||||
| 	"bottom_right_corner", | ||||
| 	"bottom_side", | ||||
| 	"grabbing", | ||||
| 	"left_ptr", | ||||
| 	"left_side", | ||||
| 	"right_side", | ||||
| 	"top_left_corner", | ||||
| 	"top_right_corner", | ||||
| 	"top_side", | ||||
| 	"xterm", | ||||
| 	"hand1", | ||||
| }; | ||||
| 
 | ||||
| struct wl_cursor_theme { | ||||
| 	unsigned int cursor_count; | ||||
| 	struct wl_cursor **cursors; | ||||
| 	struct wl_shm *shm; | ||||
| 	struct shm_pool *pool; | ||||
| 	char *name; | ||||
| 	int size; | ||||
| }; | ||||
| 
 | ||||
| struct cursor_image { | ||||
| 	struct wl_cursor_image image; | ||||
| 	struct wl_cursor_theme *theme; | ||||
| 	struct wl_buffer *buffer; | ||||
| 	int offset; /* data offset of this image in the shm pool */ | ||||
| }; | ||||
| 
 | ||||
| /** Get an shm buffer for a cursor image
 | ||||
|  * | ||||
|  * \param image The cursor image | ||||
|  * \return An shm buffer for the cursor image. The user should not destroy | ||||
|  * the returned buffer. | ||||
|  */ | ||||
| WL_EXPORT struct wl_buffer * | ||||
| wl_cursor_image_get_buffer(struct wl_cursor_image *_img) | ||||
| { | ||||
| 	struct cursor_image *image = (struct cursor_image *) _img; | ||||
| 	struct wl_cursor_theme *theme = image->theme; | ||||
| 
 | ||||
| 	if (!image->buffer) { | ||||
| 		image->buffer = | ||||
| 			wl_shm_pool_create_buffer(theme->pool->pool, | ||||
| 						  image->offset, | ||||
| 						  _img->width, _img->height, | ||||
| 						  _img->width * 4, | ||||
| 						  WL_SHM_FORMAT_ARGB8888); | ||||
| 	}; | ||||
| 
 | ||||
| 	return image->buffer; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| wl_cursor_image_destroy(struct wl_cursor_image *_img) | ||||
| { | ||||
| 	struct cursor_image *image = (struct cursor_image *) _img; | ||||
| 
 | ||||
| 	if (image->buffer) | ||||
| 		wl_buffer_destroy(image->buffer); | ||||
| 
 | ||||
| 	free(image); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| wl_cursor_destroy(struct wl_cursor *cursor) | ||||
| { | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	for (i = 0; i < cursor->image_count; i++) | ||||
| 		wl_cursor_image_destroy(cursor->images[i]); | ||||
| 
 | ||||
| 	free(cursor->name); | ||||
| 	free(cursor); | ||||
| } | ||||
| 
 | ||||
| static struct wl_cursor * | ||||
| load_cursor(struct wl_cursor_theme *theme, const char *name) | ||||
| { | ||||
| 	XcursorImages *images; | ||||
| 	struct wl_cursor *cursor; | ||||
| 	struct cursor_image *image; | ||||
| 	int i, size; | ||||
| 
 | ||||
| 	images = XcursorLibraryLoadImages(name, theme->name, theme->size); | ||||
| 	if (!images) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	cursor = malloc(sizeof *cursor); | ||||
| 	if (!cursor) { | ||||
| 		XcursorImagesDestroy(images); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	cursor->image_count = images->nimage; | ||||
| 	cursor->images = malloc(images->nimage * sizeof cursor->images[0]); | ||||
| 	if (!cursor->images) { | ||||
| 		XcursorImagesDestroy(images); | ||||
| 		free(cursor); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	cursor->name = strdup(name); | ||||
| 
 | ||||
| 	for (i = 0; i < images->nimage; i++) { | ||||
| 		image = malloc(sizeof *image); | ||||
| 		cursor->images[i] = (struct wl_cursor_image *) image; | ||||
| 
 | ||||
| 		image->theme = theme; | ||||
| 		image->buffer = NULL; | ||||
| 
 | ||||
| 		image->image.width = images->images[i]->width; | ||||
| 		image->image.height = images->images[i]->height; | ||||
| 		image->image.hotspot_x = images->images[i]->xhot; | ||||
| 		image->image.hotspot_y = images->images[i]->yhot; | ||||
| 		image->image.delay = images->images[i]->delay; | ||||
| 
 | ||||
| 		/* copy pixels to shm pool */ | ||||
| 		size = image->image.width * image->image.height * 4; | ||||
| 		image->offset = shm_pool_allocate(theme->pool, size); | ||||
| 		memcpy(theme->pool->data + image->offset, | ||||
| 		       images->images[i]->pixels, size); | ||||
| 	} | ||||
| 
 | ||||
| 	XcursorImagesDestroy(images); | ||||
| 
 | ||||
| 	return cursor; | ||||
| } | ||||
| 
 | ||||
| /** Load a cursor theme to memory shared with the compositor
 | ||||
|  * | ||||
|  * \param name The name of the cursor theme to load. If %NULL, the default | ||||
|  * theme will be loaded. | ||||
|  * \param size Desired size of the cursor images. | ||||
|  * \param shm The compositor's shm interface. | ||||
|  * | ||||
|  * \return An object representing the theme that should be destroyed with | ||||
|  * wl_cursor_theme_destroy() or %NULL on error. | ||||
|  */ | ||||
| WL_EXPORT struct wl_cursor_theme * | ||||
| wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm) | ||||
| { | ||||
| 	struct wl_cursor_theme *theme; | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	theme = malloc(sizeof *theme); | ||||
| 	if (!theme) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	if (!name) | ||||
| 		name = "default"; | ||||
| 
 | ||||
| 	theme->name = strdup(name); | ||||
| 	theme->size = size; | ||||
| 	theme->cursor_count = ARRAY_LENGTH(cursor_names); | ||||
| 
 | ||||
| 	theme->cursors = | ||||
| 		malloc(theme->cursor_count * sizeof theme->cursors[0]); | ||||
| 	if (!theme->cursors) { | ||||
| 		free(theme); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	theme->pool = | ||||
| 		shm_pool_create(shm, theme->cursor_count * size * size * 4); | ||||
| 
 | ||||
| 	for (i = 0; i < theme->cursor_count; i++) | ||||
| 		theme->cursors[i] = load_cursor(theme, cursor_names[i]); | ||||
| 
 | ||||
| 	return theme; | ||||
| } | ||||
| 
 | ||||
| /** Destroys a cursor theme object
 | ||||
|  * | ||||
|  * \param theme The cursor theme to be destroyed | ||||
|  */ | ||||
| WL_EXPORT void | ||||
| wl_cursor_theme_destroy(struct wl_cursor_theme *theme) | ||||
| { | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	for (i = 0; i < theme->cursor_count; i++) | ||||
| 		wl_cursor_destroy(theme->cursors[i]); | ||||
| 
 | ||||
| 	shm_pool_destroy(theme->pool); | ||||
| 
 | ||||
| 	free(theme->cursors); | ||||
| 	free(theme); | ||||
| } | ||||
| 
 | ||||
| /** Get the cursor for a given type from a cursor theme
 | ||||
|  * | ||||
|  * \param theme The cursor theme | ||||
|  * \patam type The desired cursor type | ||||
|  * \return The theme's cursor of the given type or %NULL if there is no | ||||
|  * such cursor | ||||
|  */ | ||||
| WL_EXPORT struct wl_cursor * | ||||
| wl_cursor_theme_get_cursor(struct wl_cursor_theme *theme, | ||||
| 			   enum wl_cursor_type type) | ||||
| { | ||||
| 	if (type < theme->cursor_count) | ||||
| 		return theme->cursors[type]; | ||||
| 
 | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| /** Get the cursor for a given name from a cursor theme
 | ||||
|  * | ||||
|  * \param theme The cursor theme | ||||
|  * \param name Name of the desired cursor | ||||
|  * \return The theme's cursor of the given name or %NULL if there is no | ||||
|  * such cursor | ||||
|  */ | ||||
| WL_EXPORT struct wl_cursor * | ||||
| wl_cursor_theme_get_cursor_by_name(struct wl_cursor_theme *theme, | ||||
| 				   const char *name) | ||||
| { | ||||
| 	unsigned int i; | ||||
| 	struct wl_cursor *cursor; | ||||
| 
 | ||||
| 	for (i = 0; i < theme->cursor_count; i++) { | ||||
| 		if (strcmp(name, theme->cursors[i]->name) == 0) | ||||
| 			return theme->cursors[i]; | ||||
| 	} | ||||
| 
 | ||||
| 	cursor = load_cursor(theme, name); | ||||
| 	if (!cursor) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	theme->cursor_count++; | ||||
| 	theme->cursors = | ||||
| 		realloc(theme->cursors, | ||||
| 			theme->cursor_count * sizeof theme->cursors[0]); | ||||
| 
 | ||||
| 	theme->cursors[theme->cursor_count - 1] = cursor; | ||||
| 
 | ||||
| 	return cursor; | ||||
| } | ||||
							
								
								
									
										78
									
								
								cursor/wayland-cursor.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								cursor/wayland-cursor.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,78 @@ | |||
| /*
 | ||||
|  * Copyright © 2012 Intel Corporation | ||||
|  * | ||||
|  * Permission to use, copy, modify, distribute, and sell this software and its | ||||
|  * documentation for any purpose is hereby granted without fee, provided that | ||||
|  * the above copyright notice appear in all copies and that both that copyright | ||||
|  * notice and this permission notice appear in supporting documentation, and | ||||
|  * that the name of the copyright holders not be used in advertising or | ||||
|  * publicity pertaining to distribution of the software without specific, | ||||
|  * written prior permission.  The copyright holders make no representations | ||||
|  * about the suitability of this software for any purpose.  It is provided "as | ||||
|  * is" without express or implied warranty. | ||||
|  * | ||||
|  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||||
|  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||||
|  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR | ||||
|  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | ||||
|  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | ||||
|  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | ||||
|  * OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef WAYLAND_CURSOR_H | ||||
| #define WAYLAND_CURSOR_H | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| enum wl_cursor_type { | ||||
| 	WL_CURSOR_BOTTOM_LEFT, | ||||
| 	WL_CURSOR_BOTTOM_RIGHT, | ||||
| 	WL_CURSOR_BOTTOM, | ||||
| 	WL_CURSOR_DRAGGING, | ||||
| 	WL_CURSOR_LEFT_PTR, | ||||
| 	WL_CURSOR_LEFT, | ||||
| 	WL_CURSOR_RIGHT, | ||||
| 	WL_CURSOR_TOP_LEFT, | ||||
| 	WL_CURSOR_TOP_RIGHT, | ||||
| 	WL_CURSOR_TOP, | ||||
| 	WL_CURSOR_IBEAM, | ||||
| 	WL_CURSOR_HAND1, | ||||
| }; | ||||
| 
 | ||||
| struct wl_cursor_theme; | ||||
| 
 | ||||
| struct wl_cursor_image { | ||||
| 	uint32_t width;		/* actual width */ | ||||
| 	uint32_t height;	/* actual height */ | ||||
| 	uint32_t hotspot_x;	/* hot spot x (must be inside image) */ | ||||
| 	uint32_t hotspot_y;	/* hot spot y (must be inside image) */ | ||||
| 	uint32_t delay;		/* animation delay to next frame (ms) */ | ||||
| }; | ||||
| 
 | ||||
| struct wl_cursor { | ||||
| 	unsigned int image_count; | ||||
| 	struct wl_cursor_image **images; | ||||
| 	char *name; | ||||
| }; | ||||
| 
 | ||||
| struct wl_shm; | ||||
| 
 | ||||
| struct wl_cursor_theme * | ||||
| wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm); | ||||
| 
 | ||||
| void | ||||
| wl_cursor_theme_destroy(struct wl_cursor_theme *theme); | ||||
| 
 | ||||
| struct wl_cursor * | ||||
| wl_cursor_theme_get_cursor(struct wl_cursor_theme *theme, | ||||
| 			   enum wl_cursor_type type); | ||||
| 
 | ||||
| struct wl_cursor * | ||||
| wl_cursor_theme_get_cursor_by_name(struct wl_cursor_theme *theme, | ||||
| 				   const char *name); | ||||
| 
 | ||||
| struct wl_buffer * | ||||
| wl_cursor_image_get_buffer(struct wl_cursor_image *image); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										10
									
								
								cursor/wayland-cursor.pc.in
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								cursor/wayland-cursor.pc.in
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| prefix=@prefix@ | ||||
| exec_prefix=${prefix} | ||||
| libdir=${exec_prefix}/lib | ||||
| includedir=${prefix}/include | ||||
|   | ||||
| Name: Wayland Cursor | ||||
| Description: Wayland cursor helper library | ||||
| Version: @WAYLAND_VERSION@ | ||||
| Cflags: -I${includedir} | ||||
| Libs: -L${libdir} -lwayland-cursor | ||||
							
								
								
									
										856
									
								
								cursor/xcursor.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										856
									
								
								cursor/xcursor.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,856 @@ | |||
| /*
 | ||||
|  * Copyright © 2002 Keith Packard | ||||
|  * | ||||
|  * Permission to use, copy, modify, distribute, and sell this software and its | ||||
|  * documentation for any purpose is hereby granted without fee, provided that | ||||
|  * the above copyright notice appear in all copies and that both that | ||||
|  * copyright notice and this permission notice appear in supporting | ||||
|  * documentation, and that the name of Keith Packard not be used in | ||||
|  * advertising or publicity pertaining to distribution of the software without | ||||
|  * specific, written prior permission.  Keith Packard makes no | ||||
|  * representations about the suitability of this software for any purpose.  It | ||||
|  * is provided "as is" without express or implied warranty. | ||||
|  * | ||||
|  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||||
|  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||||
|  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR | ||||
|  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | ||||
|  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | ||||
|  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||||
|  * PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include "xcursor.h" | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.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 | ||||
|  * table of contents which has type and offset information | ||||
|  * for the remaining tables in the file. | ||||
|  * | ||||
|  * File minor versions increment for compatible changes | ||||
|  * File major versions increment for incompatible changes (never, we hope) | ||||
|  * | ||||
|  * Chunks of the same type are always upward compatible.  Incompatible | ||||
|  * changes are made with new chunk types; the old data can remain under | ||||
|  * the old type.  Upward compatible changes can add header data as the | ||||
|  * header lengths are specified in the file. | ||||
|  * | ||||
|  *  File: | ||||
|  *	FileHeader | ||||
|  *	LISTofChunk | ||||
|  * | ||||
|  *  FileHeader: | ||||
|  *	CARD32		magic	    magic number | ||||
|  *	CARD32		header	    bytes in file header | ||||
|  *	CARD32		version	    file version | ||||
|  *	CARD32		ntoc	    number of toc entries | ||||
|  *	LISTofFileToc   toc	    table of contents | ||||
|  * | ||||
|  *  FileToc: | ||||
|  *	CARD32		type	    entry type | ||||
|  *	CARD32		subtype	    entry subtype (size for images) | ||||
|  *	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)) | ||||
| 
 | ||||
| /*
 | ||||
|  * 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) | ||||
| 
 | ||||
| typedef struct _XcursorFileToc { | ||||
|     XcursorUInt	    type;	/* chunk type */ | ||||
|     XcursorUInt	    subtype;	/* subtype (size for images) */ | ||||
|     XcursorUInt	    position;	/* absolute position in file */ | ||||
| } XcursorFileToc; | ||||
| 
 | ||||
| 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; | ||||
| 
 | ||||
| /*
 | ||||
|  * The rest of the file is a list of chunks, each tagged by type | ||||
|  * and version. | ||||
|  * | ||||
|  *  Chunk: | ||||
|  *	ChunkHeader | ||||
|  *	<extra type-specific header fields> | ||||
|  *	<type-specific data> | ||||
|  * | ||||
|  *  ChunkHeader: | ||||
|  *	CARD32	    header	bytes in chunk header + type header | ||||
|  *	CARD32	    type	chunk type | ||||
|  *	CARD32	    subtype	chunk subtype | ||||
|  *	CARD32	    version	chunk type version | ||||
|  */ | ||||
| 
 | ||||
| #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; | ||||
| 
 | ||||
| /*
 | ||||
|  * Each cursor image occupies a separate image chunk. | ||||
|  * The length of the image header follows the chunk header | ||||
|  * so that future versions can extend the header without | ||||
|  * breaking older applications | ||||
|  * | ||||
|  *  Image: | ||||
|  *	ChunkHeader	header	chunk header | ||||
|  *	CARD32		width	actual width | ||||
|  *	CARD32		height	actual height | ||||
|  *	CARD32		xhot	hot spot x | ||||
|  *	CARD32		yhot	hot spot y | ||||
|  *	CARD32		delay	animation delay | ||||
|  *	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; | ||||
| 
 | ||||
| /*
 | ||||
|  * From libXcursor/src/file.c | ||||
|  */ | ||||
| 
 | ||||
| static XcursorImage * | ||||
| XcursorImageCreate (int width, int height) | ||||
| { | ||||
|     XcursorImage    *image; | ||||
| 
 | ||||
|     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; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| XcursorImageDestroy (XcursorImage *image) | ||||
| { | ||||
|     free (image); | ||||
| } | ||||
| 
 | ||||
| static XcursorImages * | ||||
| XcursorImagesCreate (int size) | ||||
| { | ||||
|     XcursorImages   *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; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| XcursorImagesDestroy (XcursorImages *images) | ||||
| { | ||||
|     int	n; | ||||
| 
 | ||||
|     if (!images) | ||||
|         return; | ||||
| 
 | ||||
|     for (n = 0; n < images->nimage; n++) | ||||
| 	XcursorImageDestroy (images->images[n]); | ||||
|     if (images->name) | ||||
| 	free (images->name); | ||||
|     free (images); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| XcursorImagesSetName (XcursorImages *images, const char *name) | ||||
| { | ||||
|     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; | ||||
| } | ||||
| 
 | ||||
| static XcursorBool | ||||
| _XcursorReadUInt (XcursorFile *file, XcursorUInt *u) | ||||
| { | ||||
|     unsigned char   bytes[4]; | ||||
| 
 | ||||
|     if (!file || !u) | ||||
|         return XcursorFalse; | ||||
| 
 | ||||
|     if ((*file->read) (file, bytes, 4) != 4) | ||||
| 	return XcursorFalse; | ||||
|     *u = ((bytes[0] << 0) | | ||||
| 	  (bytes[1] << 8) | | ||||
| 	  (bytes[2] << 16) | | ||||
| 	  (bytes[3] << 24)); | ||||
|     return XcursorTrue; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| _XcursorFileHeaderDestroy (XcursorFileHeader *fileHeader) | ||||
| { | ||||
|     free (fileHeader); | ||||
| } | ||||
| 
 | ||||
| static XcursorFileHeader * | ||||
| _XcursorFileHeaderCreate (int ntoc) | ||||
| { | ||||
|     XcursorFileHeader	*fileHeader; | ||||
| 
 | ||||
|     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; | ||||
|     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) | ||||
| { | ||||
|     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; | ||||
| 	} | ||||
| 	else if (thisSize == bestSize) | ||||
| 	    nsizes++; | ||||
|     } | ||||
|     *nsizesp = nsizes; | ||||
|     return bestSize; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| _XcursorFindImageToc (XcursorFileHeader	*fileHeader, | ||||
| 		      XcursorDim	size, | ||||
| 		      int		count) | ||||
| { | ||||
|     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 >= 0x10000 || head.height > 0x10000) | ||||
| 	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 (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; | ||||
| 	} | ||||
| 	p++; | ||||
|     } | ||||
|     return image; | ||||
| } | ||||
| 
 | ||||
| static XcursorImages * | ||||
| XcursorXcFileLoadImages (XcursorFile *file, int size) | ||||
| { | ||||
|     XcursorFileHeader	*fileHeader; | ||||
|     XcursorDim		bestSize; | ||||
|     int			nsize; | ||||
|     XcursorImages	*images; | ||||
|     int			n; | ||||
|     int			toc; | ||||
| 
 | ||||
|     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 int | ||||
| _XcursorStdioFileRead (XcursorFile *file, unsigned char *buf, int len) | ||||
| { | ||||
|     FILE    *f = file->closure; | ||||
|     return fread (buf, 1, len, f); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| _XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len) | ||||
| { | ||||
|     FILE    *f = file->closure; | ||||
|     return fwrite (buf, 1, len, f); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| _XcursorStdioFileSeek (XcursorFile *file, long offset, int whence) | ||||
| { | ||||
|     FILE    *f = file->closure; | ||||
|     return fseek (f, offset, whence); | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * From libXcursor/src/library.c | ||||
|  */ | ||||
| 
 | ||||
| #ifndef ICONDIR | ||||
| #define ICONDIR "/usr/X11R6/lib/X11/icons" | ||||
| #endif | ||||
| 
 | ||||
| #ifndef XCURSORPATH | ||||
| #define XCURSORPATH "~/.icons:/usr/share/icons:/usr/share/pixmaps:"ICONDIR | ||||
| #endif | ||||
| 
 | ||||
| static const char * | ||||
| XcursorLibraryPath (void) | ||||
| { | ||||
|     static const char	*path; | ||||
| 
 | ||||
|     if (!path) | ||||
|     { | ||||
| 	path = getenv ("XCURSOR_PATH"); | ||||
| 	if (!path) | ||||
| 	    path = XCURSORPATH; | ||||
|     } | ||||
|     return path; | ||||
| } | ||||
| 
 | ||||
| static  void | ||||
| _XcursorAddPathElt (char *path, const char *elt, int len) | ||||
| { | ||||
|     int	    pathlen = strlen (path); | ||||
| 
 | ||||
|     /* 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--; | ||||
|     } | ||||
|     strncpy (path + pathlen, elt, len); | ||||
|     path[pathlen + len] = '\0'; | ||||
| } | ||||
| 
 | ||||
| static char * | ||||
| _XcursorBuildThemeDir (const char *dir, const char *theme) | ||||
| { | ||||
|     const char	    *colon; | ||||
|     const char	    *tcolon; | ||||
|     char	    *full; | ||||
|     char	    *home; | ||||
|     int		    dirlen; | ||||
|     int		    homelen; | ||||
|     int		    themelen; | ||||
|     int		    len; | ||||
| 
 | ||||
|     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 = 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; | ||||
| } | ||||
| 
 | ||||
| static const char * | ||||
| _XcursorNextPath (const char *path) | ||||
| { | ||||
|     char    *colon = strchr (path, ':'); | ||||
| 
 | ||||
|     if (!colon) | ||||
| 	return NULL; | ||||
|     return colon + 1; | ||||
| } | ||||
| 
 | ||||
| #define XcursorWhite(c)	((c) == ' ' || (c) == '\t' || (c) == '\n') | ||||
| #define XcursorSep(c) ((c) == ';' || (c) == ',') | ||||
| 
 | ||||
| static char * | ||||
| _XcursorThemeInherits (const char *full) | ||||
| { | ||||
|     char    line[8192]; | ||||
|     char    *result = NULL; | ||||
|     FILE    *f; | ||||
| 
 | ||||
|     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; | ||||
| 		l++; | ||||
| 		while (*l == ' ') l++; | ||||
| 		result = malloc (strlen (l)); | ||||
| 		if (result) | ||||
| 		{ | ||||
| 		    r = result; | ||||
| 		    while (*l) | ||||
| 		    { | ||||
| 			while (XcursorSep(*l) || XcursorWhite (*l)) l++; | ||||
| 			if (!*l) | ||||
| 			    break; | ||||
| 			if (r != result) | ||||
| 			    *r++ = ':'; | ||||
| 			while (*l && !XcursorWhite(*l) && | ||||
| 			       !XcursorSep(*l)) | ||||
| 			    *r++ = *l++; | ||||
| 		    } | ||||
| 		    *r++ = '\0'; | ||||
| 		} | ||||
| 		break; | ||||
| 	    } | ||||
| 	} | ||||
| 	fclose (f); | ||||
|     } | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| static FILE * | ||||
| XcursorScanTheme (const char *theme, const char *name) | ||||
| { | ||||
|     FILE	*f = NULL; | ||||
|     char	*full; | ||||
|     char	*dir; | ||||
|     const char  *path; | ||||
|     char	*inherits = NULL; | ||||
|     const char	*i; | ||||
| 
 | ||||
|     if (!theme || !name) | ||||
|         return NULL; | ||||
| 
 | ||||
|     /*
 | ||||
|      * Scan this theme | ||||
|      */ | ||||
|     for (path = XcursorLibraryPath (); | ||||
| 	 path && f == NULL; | ||||
| 	 path = _XcursorNextPath (path)) | ||||
|     { | ||||
| 	dir = _XcursorBuildThemeDir (path, theme); | ||||
| 	if (dir) | ||||
| 	{ | ||||
| 	    full = _XcursorBuildFullname (dir, "cursors", name); | ||||
| 	    if (full) | ||||
| 	    { | ||||
| 		f = fopen (full, "r"); | ||||
| 		free (full); | ||||
| 	    } | ||||
| 	    if (!f && !inherits) | ||||
| 	    { | ||||
| 		full = _XcursorBuildFullname (dir, "", "index.theme"); | ||||
| 		if (full) | ||||
| 		{ | ||||
| 		    inherits = _XcursorThemeInherits (full); | ||||
| 		    free (full); | ||||
| 		} | ||||
| 	    } | ||||
| 	    free (dir); | ||||
| 	} | ||||
|     } | ||||
|     /*
 | ||||
|      * Recurse to scan inherited themes | ||||
|      */ | ||||
|     for (i = inherits; i && f == NULL; i = _XcursorNextPath (i)) | ||||
| 	f = XcursorScanTheme (i, name); | ||||
|     if (inherits != NULL) | ||||
| 	free (inherits); | ||||
|     return f; | ||||
| } | ||||
| 
 | ||||
| XcursorImages * | ||||
| XcursorLibraryLoadImages (const char *file, const char *theme, int size) | ||||
| { | ||||
|     FILE	    *f = NULL; | ||||
|     XcursorImages   *images = NULL; | ||||
| 
 | ||||
|     if (!file) | ||||
|         return NULL; | ||||
| 
 | ||||
|     if (theme) | ||||
| 	f = XcursorScanTheme (theme, file); | ||||
|     if (!f) | ||||
| 	f = XcursorScanTheme ("default", file); | ||||
|     if (f) | ||||
|     { | ||||
| 	images = XcursorFileLoadImages (f, size); | ||||
| 	if (images) | ||||
| 	    XcursorImagesSetName (images, file); | ||||
| 	fclose (f); | ||||
|     } | ||||
|     return images; | ||||
| } | ||||
							
								
								
									
										58
									
								
								cursor/xcursor.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								cursor/xcursor.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,58 @@ | |||
| /*
 | ||||
|  * Copyright © 2002 Keith Packard | ||||
|  * | ||||
|  * Permission to use, copy, modify, distribute, and sell this software and its | ||||
|  * documentation for any purpose is hereby granted without fee, provided that | ||||
|  * the above copyright notice appear in all copies and that both that | ||||
|  * copyright notice and this permission notice appear in supporting | ||||
|  * documentation, and that the name of Keith Packard not be used in | ||||
|  * advertising or publicity pertaining to distribution of the software without | ||||
|  * specific, written prior permission.  Keith Packard makes no | ||||
|  * representations about the suitability of this software for any purpose.  It | ||||
|  * is provided "as is" without express or implied warranty. | ||||
|  * | ||||
|  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||||
|  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||||
|  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR | ||||
|  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | ||||
|  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | ||||
|  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||||
|  * PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _XCURSOR_H_ | ||||
| #define _XCURSOR_H_ | ||||
| 
 | ||||
| typedef int		XcursorBool; | ||||
| typedef unsigned int	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; | ||||
| 
 | ||||
| /*
 | ||||
|  * 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; | ||||
| 
 | ||||
| XcursorImages * | ||||
| XcursorLibraryLoadImages (const char *file, const char *theme, int size); | ||||
| 
 | ||||
| void | ||||
| XcursorImagesDestroy (XcursorImages *images); | ||||
| 
 | ||||
| #endif | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Ander Conselvan de Oliveira
						Ander Conselvan de Oliveira