aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@tika.localdomain>2006-11-25 13:54:35 +1100
committerBenjamin Herrenschmidt <benh@tika.localdomain>2006-11-25 13:54:35 +1100
commit8a73742a5ff96cfad0aff4e4fcb9ce1148d05e77 (patch)
tree8f33a984b51ea77a3eaf2ed85214a6e4230fa0be
parent821e5b92dcd4f118caa234b0ffaa0f59d192e6bf (diff)
downloadlibtwin-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.am1
-rw-r--r--twin.h22
-rw-r--r--twin_cursor.c238
-rw-r--r--twin_screen.c126
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 \
diff --git a/twin.h b/twin.h
index befc478..bb30ad4 100644
--- a/twin.h
+++ b/twin.h
@@ -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)
{