diff options
author | Benjamin Herrenschmidt <benh@tika.localdomain> | 2006-11-25 13:54:35 +1100 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@tika.localdomain> | 2006-11-25 13:54:35 +1100 |
commit | 8a73742a5ff96cfad0aff4e4fcb9ce1148d05e77 (patch) | |
tree | 8f33a984b51ea77a3eaf2ed85214a6e4230fa0be | |
parent | 821e5b92dcd4f118caa234b0ffaa0f59d192e6bf (diff) | |
download | libtwin-8a73742a5ff96cfad0aff4e4fcb9ce1148d05e77.tar.gz |
Add cursor pixmap support
This adds support for a cursor pixmap that is always on top and
follows mouse movements.
It also provides a function for importing a cursor image from an
X.org ARGB cursor file.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | twin.h | 22 | ||||
-rw-r--r-- | twin_cursor.c | 238 | ||||
-rw-r--r-- | twin_screen.c | 126 |
4 files changed, 360 insertions, 27 deletions
diff --git a/Makefile.am b/Makefile.am index ed2aa9c..d638db5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -18,6 +18,7 @@ xtwin_SOURCES = \ twin_box.c \ twin_button.c \ twin_convolve.c \ + twin_cursor.c \ twin_dispatch.c \ twin_draw.c \ twin_glyphs.c \ @@ -153,6 +153,15 @@ typedef struct _twin_screen { */ twin_pixmap_t *pointer; /* + * mouse image (optional) + */ + twin_pixmap_t *cursor; + twin_coord_t curs_hx; + twin_coord_t curs_hy; + twin_coord_t curs_x; + twin_coord_t curs_y; + + /* * Output size */ twin_coord_t width, height; @@ -546,6 +555,15 @@ twin_fill (twin_pixmap_t *dst, twin_coord_t bottom); /* + * twin_cursor.c + */ +twin_pixmap_t *twin_get_default_cursor(int *hx, int *hy); + +twin_pixmap_t *twin_load_X_cursor(const char *file, int index, + int *hx, int *hy); + + +/* * twin_event.c */ @@ -970,6 +988,10 @@ twin_screen_set_background (twin_screen_t *screen, twin_pixmap_t *pixmap); twin_pixmap_t * twin_screen_get_background (twin_screen_t *screen); +void +twin_screen_set_cursor (twin_screen_t *screen, twin_pixmap_t *pixmap, + twin_fixed_t hotspot_x, twin_fixed_t hotspot_y); + twin_bool_t twin_screen_dispatch (twin_screen_t *screen, twin_event_t *event); diff --git a/twin_cursor.c b/twin_cursor.c new file mode 100644 index 0000000..f0e5fc3 --- /dev/null +++ b/twin_cursor.c @@ -0,0 +1,238 @@ +/* + * Manipulating twin cursor images + * + * Copyright 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org> + * + * This Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Twin Library; see the file COPYING. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <byteswap.h> +#include <endian.h> + +#include "twin.h" + +/* Make something better here ! */ + +static unsigned int twin_def_cursor_image[] = { + 0x00000000, 0x88ffffff, 0x88ffffff, 0x00000000, + 0x88ffffff, 0xff000000, 0xff000000, 0x88ffffff, + 0x88ffffff, 0xff000000, 0xff000000, 0x88ffffff, + 0x00000000, 0x88ffffff, 0x88ffffff, 0x00000000, +}; + +twin_pixmap_t *twin_get_default_cursor(int *hx, int *hy) +{ + twin_pixmap_t *cur; + twin_pointer_t data; + + data.argb32 = twin_def_cursor_image; + cur = twin_pixmap_create_const(TWIN_ARGB32, 4, 4, 16, data); + if (cur == NULL) + return NULL; + *hx = *hy = 2; + return cur; +} + +/* + * Bits extracted from Xcursor + */ + +#define XCURSOR_MAGIC 0x72756358 /* "Xcur" LSBFirst */ + +#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) + +/* + * 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: + * 0 CARD32 magic magic number + * 1 CARD32 header bytes in file header + * 2 CARD32 version file version + * 3 CARD32 ntoc number of toc entries + * LISTofFileToc toc table of contents + * + * FileToc: + * 0 CARD32 type entry type + * 1 CARD32 subtype entry subtype (size for images) + * 2 CARD32 position absolute file position + */ + + +/* + * 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: + * 0 CARD32 header bytes in chunk header + type header + * 1 CARD32 type chunk type + * 2 CARD32 subtype chunk subtype + * 3 CARD32 version chunk type version + */ + +#define XCURSOR_CHUNK_HEADER_LEN (4 * 4) + +/* + * 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: + * 0 ChunkHeader header chunk header + * 4 CARD32 width actual width + * 5 CARD32 height actual height + * 6 CARD32 xhot hot spot x + * 7 CARD32 yhot hot spot y + * 8 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 */ + +static inline int twin_read_header(int fd, uint32_t *buf, int size) +{ + int i, len; + + len = read(fd, buf, size); + if (len != size) + return 0; + +#if __BYTE_ORDER == __BIG_ENDIAN + for (i = 0; i < (len / 4); i++) + buf[i] = bswap_32(buf[i]); +#endif + return 1; +} + +twin_pixmap_t *twin_load_X_cursor(const char *file, int index, + int *hx, int *hy) +{ + int fd, img, i, toccnt; + uint32_t buffer[32], filepos, size; + twin_pixmap_t *cur = NULL; + + fd = open(file, O_RDONLY); + if (fd < 0) + return NULL; + if (!twin_read_header(fd, buffer, XCURSOR_FILE_HEADER_LEN)) + goto bail; + + /* check magic */ + if (buffer[0] != XCURSOR_MAGIC) + goto bail; + + /* check version. assume we support all minor versions */ + if ((buffer[2] >> 16) != XCURSOR_FILE_MAJOR) + goto bail; + + /* get number of TOC entries */ + toccnt = buffer[3]; + + /* seek to first toc entry (header len) */ + lseek(fd, buffer[1] , SEEK_SET); + + /* look for the index'th image in TOC */ + img = 0; + filepos = 0; + for (i = 0; filepos == 0 && i < toccnt; i++) { + if (!twin_read_header(fd, buffer, XCURSOR_FILE_TOC_LEN)) + goto bail; + if (buffer[0] == XCURSOR_IMAGE_TYPE) { + if (img == index) + filepos = buffer[2]; + img++; + } + } + /* check if found */ + if (filepos == 0) + goto bail; + + /* seek to image header and read it */ + lseek(fd, filepos, SEEK_SET); + if (!twin_read_header(fd, buffer, XCURSOR_IMAGE_HEADER_LEN)) + goto bail; + + /* check chunk type */ + if (buffer[1] != XCURSOR_IMAGE_TYPE) + goto bail; + + /* check image version */ + if (buffer[3] != XCURSOR_IMAGE_VERSION) + goto bail; + + /* get hotspot */ + *hx = buffer[6]; + *hy = buffer[7]; + + /* create pixmap */ + cur = twin_pixmap_create(TWIN_ARGB32, buffer[4], buffer[5]); + if (cur == NULL) + goto bail; + + /* load pixels */ + size = buffer[4] * buffer[5] * 4; + lseek(fd, filepos + buffer[0], SEEK_SET); + if (read(fd, cur->p.v, size) != size) { + twin_pixmap_destroy(cur); + goto bail; + } + +#if __BYTE_ORDER == __BIG_ENDIAN + for (i = 0; i < (size / 4); i++) + cur->p.argb32[i] = bswap_32(cur->p.argb32[i]); +#endif + + bail: + close(fd); + return cur; +} + diff --git a/twin_screen.c b/twin_screen.c index c5e489a..8de8007 100644 --- a/twin_screen.c +++ b/twin_screen.c @@ -28,7 +28,7 @@ twin_screen_create (twin_coord_t width, twin_put_span_t put_span, void *closure) { - twin_screen_t *screen = malloc (sizeof (twin_screen_t)); + twin_screen_t *screen = calloc (1, sizeof (twin_screen_t)); if (!screen) return 0; screen->top = 0; @@ -93,6 +93,15 @@ twin_screen_damage (twin_screen_t *screen, twin_coord_t left, twin_coord_t top, twin_coord_t right, twin_coord_t bottom) { + if (left < 0) + left = 0; + if (top < 0) + top = 0; + if (right > screen->width) + right = screen->width; + if (bottom > screen->height) + bottom = screen->height; + if (screen->damage.left == screen->damage.right) { screen->damage.left = left; @@ -131,6 +140,37 @@ twin_screen_damaged (twin_screen_t *screen) screen->damage.top < screen->damage.bottom); } +static void +twin_screen_span_pixmap(twin_screen_t *screen, twin_argb32_t *span, + twin_pixmap_t *p, twin_coord_t y, + twin_coord_t left, twin_coord_t right) +{ + twin_pointer_t dst; + twin_source_u src; + twin_coord_t p_left, p_right; + + /* bounds check in y */ + if (y < p->y) + return; + if (p->y + p->height <= y) + return; + /* bounds check in x*/ + p_left = left; + if (p_left < p->x) + p_left = p->x; + p_right = right; + if (p_right > p->x + p->width) + p_right = p->x + p->width; + if (p_left >= p_right) + return; + dst.argb32 = span + (p_left - left); + src.p = twin_pixmap_pointer (p, p_left - p->x, y - p->y); + if (p->format == TWIN_RGB16) + _twin_rgb16_source_argb32 (dst, src, p_right - p_left); + else + _twin_argb32_over_argb32 (dst, src, p_right - p_left); +} + void twin_screen_update (twin_screen_t *screen) { @@ -139,6 +179,11 @@ twin_screen_update (twin_screen_t *screen) twin_coord_t right = screen->damage.right; twin_coord_t bottom = screen->damage.bottom; + if (right > screen->width) + right = screen->width; + if (bottom > screen->height) + bottom = screen->height; + if (!screen->disable && left < right && top < bottom) { twin_argb32_t *span; @@ -181,33 +226,14 @@ twin_screen_update (twin_screen_t *screen) } else memset (span, 0xff, width * sizeof (twin_argb32_t)); + for (p = screen->bottom; p; p = p->up) - { - twin_pointer_t dst; - twin_source_u src; - twin_coord_t p_left, p_right; - - /* bounds check in y */ - if (y < p->y) - continue; - if (p->y + p->height <= y) - continue; - /* bounds check in x*/ - p_left = left; - if (p_left < p->x) - p_left = p->x; - p_right = right; - if (p_right > p->x + p->width) - p_right = p->x + p->width; - if (p_left >= p_right) - continue; - dst.argb32 = span + (p_left - left); - src.p = twin_pixmap_pointer (p, p_left - p->x, y - p->y); - if (p->format == TWIN_RGB16) - _twin_rgb16_source_argb32 (dst, src, p_right - p_left); - else - _twin_argb32_over_argb32 (dst, src, p_right - p_left); - } + twin_screen_span_pixmap(screen, span, p, y, left, right); + + if (screen->cursor) + twin_screen_span_pixmap(screen, span, screen->cursor, + y, left, right); + (*screen->put_span) (left, y, right, span, screen->closure); } free (span); @@ -253,6 +279,50 @@ twin_screen_get_background (twin_screen_t *screen) return screen->background; } +static void +twin_screen_damage_cursor(twin_screen_t *screen) +{ + twin_screen_damage (screen, + screen->cursor->x, + screen->cursor->y, + screen->cursor->x + screen->cursor->width, + screen->cursor->y + screen->cursor->height); +} + +void +twin_screen_set_cursor (twin_screen_t *screen, twin_pixmap_t *pixmap, + twin_fixed_t hotspot_x, twin_fixed_t hotspot_y) +{ + if (screen->cursor) { + twin_screen_damage_cursor(screen); + twin_pixmap_destroy(screen->cursor); + } + screen->cursor = pixmap; + screen->curs_hx = hotspot_x; + screen->curs_hy = hotspot_y; + pixmap->x = screen->curs_x - hotspot_x; + pixmap->y = screen->curs_y - hotspot_y; + if (pixmap) + twin_screen_damage_cursor(screen); +} + +static void +twin_screen_update_cursor(twin_screen_t *screen, + twin_coord_t x, twin_coord_t y) +{ + if (screen->cursor) + twin_screen_damage_cursor(screen); + + screen->curs_x = x; + screen->curs_y = y; + + if (screen->cursor) { + screen->cursor->x = screen->curs_x - screen->curs_hx; + screen->cursor->y = screen->curs_y - screen->curs_hy; + twin_screen_damage_cursor(screen); + } +} + twin_bool_t twin_screen_dispatch (twin_screen_t *screen, twin_event_t *event) @@ -263,6 +333,8 @@ twin_screen_dispatch (twin_screen_t *screen, case TwinEventMotion: case TwinEventButtonDown: case TwinEventButtonUp: + twin_screen_update_cursor(screen, event->u.pointer.screen_x, + event->u.pointer.screen_y); pixmap = screen->pointer; if (!pixmap) { |