[5/10] x11drv: Add Xcursor support, prefer it when available.

H. Verbeet hverbeet at gmail.com
Sat Aug 5 16:39:41 CDT 2006


This adds Xcursor support to winex11.drv. Xcursor allows animated and
multicolor cursors to be used.
-------------- next part --------------
diff --git a/configure.ac b/configure.ac
index fa74bee..f27418e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1228,6 +1228,7 @@ dnl **** Get the soname for libraries th
 if test "$LIBEXT" = "so" -o "$LIBEXT" = "dylib"
 then
   WINE_GET_SONAME(X11,XCreateWindow,[$X_LIBS $X_EXTRA_LIBS])
+  WINE_GET_SONAME(Xcursor,XcursorImageLoadCursor,[$X_LIBS -lX11 $X_EXTRA_LIBS])
   WINE_GET_SONAME(Xext,XextCreateExtension,[$X_LIBS -lX11 $X_EXTRA_LIBS])
   WINE_GET_SONAME(Xi,XOpenDevice,[$X_LIBS -lXext -lX11 $X_EXTRA_LIBS])
   WINE_GET_SONAME(Xrender,XRenderQueryExtension,[$X_LIBS -lXext -lX11 $X_EXTRA_LIBS])
diff --git a/dlls/winex11.drv/Makefile.in b/dlls/winex11.drv/Makefile.in
index 4e5248e..c2abdb1 100644
--- a/dlls/winex11.drv/Makefile.in
+++ b/dlls/winex11.drv/Makefile.in
@@ -39,6 +39,7 @@ C_SRCS = \
 	xdnd.c \
 	xfont.c \
 	xim.c \
+	xcursor.c \
 	xrandr.c \
 	xrender.c \
 	xvidmode.c
diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c
index b3ee171..7e409e3 100644
--- a/dlls/winex11.drv/init.c
+++ b/dlls/winex11.drv/init.c
@@ -89,6 +89,9 @@ static void device_init(void)
     /* Initialize XRender */
     X11DRV_XRender_Init();
 
+    /* Init Xcursor */
+    X11DRV_Xcursor_init();
+
     palette_size = X11DRV_PALETTE_Init();
 
     X11DRV_BITMAP_Init();
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c
index f9c3e54..5d3681a 100644
--- a/dlls/winex11.drv/mouse.c
+++ b/dlls/winex11.drv/mouse.c
@@ -2,6 +2,7 @@
  * X11 mouse driver
  *
  * Copyright 1998 Ulrich Weigand
+ * Copyright (C) 2006 Henri Verbeet
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -352,6 +353,153 @@ void X11DRV_send_mouse_input( HWND hwnd,
 
 
 /***********************************************************************
+ *              create_cursor_image
+ *
+ * Create an XcursorImage from a cursor_frame_t
+ */
+static XcursorImage *create_cursor_image( cursor_frame_t *frame )
+{
+    int x, xmax;
+    int y, ymax;
+    int and_size, xor_size;
+    unsigned char *and_bits, *and_ptr, *xor_bits, *xor_ptr;
+    XcursorPixel *pixel_ptr;
+    XcursorImage *image;
+
+    and_size = frame->and_width_bytes * frame->height;
+    and_ptr = and_bits = frame->bits;
+
+    xor_size = frame->xor_width_bytes * frame->height;
+    xor_ptr = xor_bits = frame->bits + and_size;
+
+    ymax = (frame->height > 32) ? 32 : frame->height;
+    xmax = (frame->width > 32) ? 32 : frame->width;
+
+    image = xcursor_ops.XcursorImageCreate( xmax, ymax );
+    pixel_ptr = image->pixels;
+
+    /* On windows, to calculate the color for a pixel, first an AND is done
+     * with the background and the "and" bitmap, then an XOR with the "xor"
+     * bitmap. This means that when the data in the "and" bitmap is 0, the
+     * pixel will get the color as specified in the "xor" bitmap.
+     * However, if the data in the "and" bitmap is 1, the result will be the
+     * background XOR'ed with the value in the "xor" bitmap. In case the "xor"
+     * data is completely black (0x000000) the pixel will become transparent,
+     * in case it's white (0xffffff) the pixel will become the inverse of the
+     * background color.
+     *
+     * Since we can't support inverting colors, we map the grayscale value of
+     * the "xor" data to the alpha channel, and xor the the color with either
+     * black or white.
+     */
+    for (y = 0; y < ymax; ++y)
+    {
+        and_ptr = and_bits + (y * frame->and_width_bytes);
+        xor_ptr = xor_bits + (y * frame->xor_width_bytes);
+
+        for (x = 0; x < xmax; ++x)
+        {
+            /* Xcursor pixel data is in ARGB format, with A in the high byte */
+            switch (frame->bpp)
+            {
+                case 24:
+                    /* BGR, 8 bits each */
+                    *pixel_ptr = *xor_ptr++;
+                    *pixel_ptr |= *xor_ptr++ << 8;
+                    *pixel_ptr |= *xor_ptr++ << 16;
+                    break;
+
+                case 16:
+                    /* BGR, 5 red, 6 green, 5 blue */
+                    *pixel_ptr = (*xor_ptr & 0xf8) | ((*xor_ptr & 0x07) << 13);
+                    ++xor_ptr;
+                    *pixel_ptr |= ((*xor_ptr & 0xe0) << 5) | ((*xor_ptr & 0x1f) << 19);
+                    break;
+
+                case 1:
+                    if (*xor_ptr & (1 << (7 - (x & 7)))) *pixel_ptr = 0xffffff;
+                    else *pixel_ptr = 0;
+                    if ((x & 7) == 7) ++xor_ptr;
+                    break;
+
+                default:
+                    FIXME("Currently no support for cursors with %d bits per pixel\n", frame->bpp);
+                    return 0;
+            }
+            /* Alpha channel */
+            if (~*and_ptr & (1 << (7 - (x & 7)))) *pixel_ptr |= 0xff << 24;
+            else if (*pixel_ptr)
+            {
+                int alpha = (*pixel_ptr & 0xff) * 0.30f
+                        + ((*pixel_ptr & 0xff00) >> 8) * 0.55f
+                        + ((*pixel_ptr & 0xff0000) >> 16) * 0.15f;
+                *pixel_ptr ^= ((x + y) % 2) ? 0xffffff : 0x000000;
+                *pixel_ptr |= alpha << 24;
+            }
+
+            if ((x & 7) == 7) ++and_ptr;
+            ++pixel_ptr;
+        }
+    }
+
+    return image;
+}
+
+
+/***********************************************************************
+ *              create_xcursor_cursor
+ *
+ * Use Xcursor to create an X cursor from a Windows one.
+ */
+static Cursor create_xcursor_cursor( Display *display, const cursor_t *cursor_object )
+{
+    unsigned int i;
+    Cursor cursor;
+    XcursorImage *image;
+    XcursorImages *images;
+
+    if (!cursor_object) /* Create an empty cursor */
+    {
+        image = xcursor_ops.XcursorImageCreate( 1, 1 );
+        image->xhot = 0;
+        image->yhot = 0;
+        *(image->pixels) = 0;
+        cursor = xcursor_ops.XcursorImageLoadCursor( display, image );
+        xcursor_ops.XcursorImageDestroy( image );
+
+        return cursor;
+    }
+
+    images = xcursor_ops.XcursorImagesCreate( cursor_object->num_frames );
+    for (i = 0; i < cursor_object->num_frames; ++i)
+    {
+        cursor_frame_t *frame = &cursor_object->frames[i];
+        image = create_cursor_image( frame );
+        if (!image) return 0;
+
+        /* Make sure hotspot is valid */
+        image->xhot = frame->xhot;
+        image->yhot = frame->yhot;
+        if (image->xhot < 0 || image->xhot >= image->width ||
+                image->yhot < 0 || image->yhot >= image->height)
+        {
+            image->xhot = image->width / 2;
+            image->yhot = image->height / 2;
+        }
+
+        image->delay = cursor_object->delay;
+
+        images->images[images->nimage++] = image;
+    }
+
+    cursor = xcursor_ops.XcursorImagesLoadCursor( display, images );
+    xcursor_ops.XcursorImagesDestroy( images );
+
+    return cursor;
+}
+
+
+/***********************************************************************
  *		create_cursor
  *
  * Create an X cursor from a Windows one.
@@ -654,7 +802,15 @@ void X11DRV_SetCursor( const cursor_t *c
         /* If in desktop mode, set the cursor on the desktop window */
 
         wine_tsx11_lock();
-        cursor = create_cursor( gdi_display, cursor_object );
+        if (xcursor_ops.XcursorImageLoadCursor)
+        {
+            cursor = create_xcursor_cursor( gdi_display, cursor_object );
+        }
+        else
+        {
+            cursor = create_cursor( gdi_display, cursor_object );
+        }
+
         if (cursor)
         {
             XDefineCursor( gdi_display, root_window, cursor );
@@ -669,7 +825,15 @@ void X11DRV_SetCursor( const cursor_t *c
         struct x11drv_thread_data *data = x11drv_thread_data();
 
         wine_tsx11_lock();
-        cursor = create_cursor( data->display, cursor_object );
+        if (xcursor_ops.XcursorImageLoadCursor)
+        {
+            cursor = create_xcursor_cursor( data->display, cursor_object );
+        }
+        else
+        {
+            cursor = create_cursor( data->display, cursor_object );
+        }
+
         if (cursor)
         {
             if (data->cursor) XFreeCursor( data->display, data->cursor );
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index e5027c4..0d610c1 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -229,6 +229,7 @@ extern BOOL X11DRV_SwapBuffers(X11DRV_PD
 
 /* X11 driver internal functions */
 
+extern void X11DRV_Xcursor_init(void);
 extern void X11DRV_BITMAP_Init(void);
 extern void X11DRV_FONT_Init( int log_pixels_x, int log_pixels_y );
 
@@ -429,6 +430,39 @@ extern void X11DRV_DIB_CopyDIBSection(X1
 struct _DCICMD;
 extern INT X11DRV_DCICommand(INT cbInput, const struct _DCICMD *lpCmd, LPVOID lpOutData);
 
+/* Xcursor */
+typedef unsigned int    XcursorUInt;
+typedef XcursorUInt     XcursorPixel;
+typedef XcursorUInt     XcursorDim;
+
+typedef struct {
+    XcursorUInt     version;
+    XcursorDim      size;
+    XcursorDim      width;
+    XcursorDim      height;
+    XcursorDim      xhot;
+    XcursorDim      yhot;
+    XcursorUInt     delay;
+    XcursorPixel    *pixels;
+} XcursorImage;
+
+typedef struct {
+    int             nimage;
+    XcursorImage    **images;
+    char            *name;
+} XcursorImages;
+
+typedef struct {
+    XcursorImage *(*XcursorImageCreate)(int, int);
+    XcursorImages *(*XcursorImagesCreate)(int);
+    void (*XcursorImageDestroy)(XcursorImage *);
+    void (*XcursorImagesDestroy)(XcursorImages *);
+    Cursor (*XcursorImageLoadCursor)(Display *, XcursorImage *);
+    Cursor (*XcursorImagesLoadCursor)(Display *, XcursorImages *);
+} xcursor_functions;
+
+extern xcursor_functions xcursor_ops;
+
 /**************************************************************************
  * X11 GDI driver
  */
diff --git a/dlls/winex11.drv/xcursor.c b/dlls/winex11.drv/xcursor.c
new file mode 100644
index 0000000..bfcf8c7
--- /dev/null
+++ b/dlls/winex11.drv/xcursor.c
@@ -0,0 +1,53 @@
+/*
+ * Xcursor function pointers
+ *
+ * Copyright (C) 2006 Henri Verbeet
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+#include "config.h"
+#include "wine/port.h"
+#include "wine/debug.h"
+#include "wine/library.h"
+#include "x11drv.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(xcursor);
+
+#ifndef SONAME_LIBXCURSOR
+#define SONAME_LIBXCURSOR "libXcursor.so"
+#endif
+
+xcursor_functions xcursor_ops;
+
+static void error( void )
+{
+    memset( &xcursor_ops, 0, sizeof(xcursor_ops) );
+}
+
+void X11DRV_Xcursor_init( void )
+{
+    void *xcursor_handle = wine_dlopen( SONAME_LIBXCURSOR, RTLD_NOW, NULL, 0 );
+
+#define LOAD_FUNCPTR(f) \
+    if(!(xcursor_ops.f = wine_dlsym( xcursor_handle, #f, NULL, 0)) ) \
+        { error(); return; }
+    LOAD_FUNCPTR(XcursorImageCreate)
+    LOAD_FUNCPTR(XcursorImagesCreate)
+    LOAD_FUNCPTR(XcursorImageDestroy)
+    LOAD_FUNCPTR(XcursorImagesDestroy)
+    LOAD_FUNCPTR(XcursorImageLoadCursor)
+    LOAD_FUNCPTR(XcursorImagesLoadCursor)
+#undef LOAD_FUNCPTR
+}


More information about the wine-patches mailing list