Implemented X11 cursor support.
authorSam Lantinga <slouken@libsdl.org>
Fri, 11 Mar 2011 13:27:25 -0800
changeset 5470 8f5e10ec4faf
parent 5469 810b7f9bac86
child 5471 179adad3ae6f
Implemented X11 cursor support.
src/video/x11/SDL_x11mouse.c
src/video/x11/SDL_x11sym.h
--- a/src/video/x11/SDL_x11mouse.c	Fri Mar 11 13:22:43 2011 -0800
+++ b/src/video/x11/SDL_x11mouse.c	Fri Mar 11 13:27:25 2011 -0800
@@ -24,14 +24,229 @@
 #include "SDL_x11mouse.h"
 #include "../../events/SDL_mouse_c.h"
 
+
+/* FIXME: Find a better place to put this... */
+static Cursor x11_empty_cursor = None;
+
+static Display *
+GetDisplay(void)
+{
+    return ((SDL_VideoData *)SDL_GetVideoDevice()->driverdata)->display;
+}
+
+static Cursor
+X11_CreateEmptyCursor()
+{
+    if (x11_empty_cursor == None) {
+        Display *display = GetDisplay();
+        char data[1];
+        XColor color;
+        Pixmap pixmap;
+
+        SDL_zero(data);
+        color.red = color.green = color.blue = 0;
+        pixmap = XCreateBitmapFromData(display, DefaultRootWindow(display),
+                                       data, 1, 1);
+        if (pixmap) {
+            x11_empty_cursor = XCreatePixmapCursor(display, pixmap, pixmap,
+                                                   &color, &color, 0, 0);
+            XFreePixmap(display, pixmap);
+        }
+    }
+    return x11_empty_cursor;
+}
+
+static void
+X11_DestroyEmptyCursor(void)
+{
+    if (x11_empty_cursor != None) {
+        XFreeCursor(GetDisplay(), x11_empty_cursor);
+        x11_empty_cursor = None;
+    }
+}
+
+static SDL_Cursor *
+X11_CreateDefaultCursor()
+{
+    SDL_Cursor *cursor;
+
+    cursor = SDL_calloc(1, sizeof(*cursor));
+    if (cursor) {
+        /* None is used to indicate the default cursor */
+        cursor->driverdata = (void*)None;
+    } else {
+        SDL_OutOfMemory();
+    }
+
+    return cursor;
+}
+
+static Cursor
+X11_CreatePixmapCursor(SDL_Surface * surface, int hot_x, int hot_y)
+{
+    Display *display = GetDisplay();
+    XColor fg, bg;
+    Cursor cursor = None;
+    Uint32 *ptr;
+    Uint8 *data_bits, *mask_bits;
+    Pixmap data_pixmap, mask_pixmap;
+    int x, y;
+    unsigned int rfg, gfg, bfg, rbg, gbg, bbg, fgBits, bgBits;
+    unsigned int width_bytes = ((surface->w + 7) & ~7) / 8;
+
+    data_bits = SDL_calloc(1, surface->h * width_bytes);
+    mask_bits = SDL_calloc(1, surface->h * width_bytes);
+    if (!data_bits || !mask_bits) {
+        SDL_OutOfMemory();
+        return None;
+    }
+
+    rfg = gfg = bfg = rbg = gbg = bbg = fgBits = 0;
+    for (y = 0; y < surface->h; ++y) {
+        ptr = (Uint32 *)((Uint8 *)surface->pixels + y * surface->pitch);
+        for (x = 0; x < surface->w; ++x) {
+            int alpha = (*ptr >> 24) & 0xff;
+            int red   = (*ptr >> 16) & 0xff;
+            int green = (*ptr >> 8) & 0xff;
+            int blue  = (*ptr >> 0) & 0xff;
+            if (alpha > 25) {
+                mask_bits[y * width_bytes + x / 8] |= (0x01 << (x % 8));
+
+                if ((red + green + blue) > 0x40) {
+                    fgBits++;
+                    rfg += red;
+                    gfg += green;
+                    bfg += blue;
+                    data_bits[y * width_bytes + x / 8] |= (0x01 << (x % 8));
+                } else {
+                    bgBits++;
+                    rbg += red;
+                    gbg += green;
+                    bbg += blue;
+                }
+            }
+            ++ptr;
+        }
+    }
+
+    if (fgBits) {
+        fg.red   = rfg * 257 / fgBits;
+        fg.green = gfg * 257 / fgBits;
+        fg.blue  = bfg * 257 / fgBits;
+    }
+    else fg.red = fg.green = fg.blue = 0;
+
+    if (bgBits) {
+        bg.red   = rbg * 257 / bgBits;
+        bg.green = gbg * 257 / bgBits;
+        bg.blue  = bbg * 257 / bgBits;
+    }
+    else bg.red = bg.green = bg.blue = 0;
+
+    data_pixmap = XCreateBitmapFromData(display, DefaultRootWindow(display),
+                                        data_bits, surface->w, surface->h);
+    mask_pixmap = XCreateBitmapFromData(display, DefaultRootWindow(display),
+                                        mask_bits, surface->w, surface->h);
+    cursor = XCreatePixmapCursor(display, data_pixmap, mask_pixmap,
+                                 &fg, &bg, hot_x, hot_y);
+	XFreePixmap(display, data_pixmap);
+	XFreePixmap(display, mask_pixmap);
+
+    return cursor;
+}
+
+static SDL_Cursor *
+X11_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
+{
+    SDL_Cursor *cursor;
+
+    cursor = SDL_calloc(1, sizeof(*cursor));
+    if (cursor) {
+        cursor->driverdata = (void*)X11_CreatePixmapCursor(surface, hot_x, hot_y);
+    } else {
+        SDL_OutOfMemory();
+    }
+
+    return cursor;
+}
+
+static void
+X11_FreeCursor(SDL_Cursor * cursor)
+{
+    Cursor x11_cursor = (Cursor)cursor->driverdata;
+
+    if (x11_cursor != None) {
+        XFreeCursor(GetDisplay(), x11_cursor);
+    }
+    SDL_free(cursor);
+}
+
+static int
+X11_ShowCursor(SDL_Cursor * cursor)
+{
+    Cursor x11_cursor = 0;
+
+    if (cursor) {
+        x11_cursor = (Cursor)cursor->driverdata;
+    } else {
+        x11_cursor = X11_CreateEmptyCursor();
+    }
+
+    /* FIXME: Is there a better way than this? */
+    {
+        SDL_VideoDevice *video = SDL_GetVideoDevice();
+        Display *display = GetDisplay();
+        SDL_Window *window;
+        SDL_WindowData *data;
+
+        for (window = video->windows; window; window = window->next) {
+            data = (SDL_WindowData *)window->driverdata;
+            if (x11_cursor != None) {
+                XDefineCursor(display, data->xwindow, x11_cursor);
+            } else {
+                XUndefineCursor(display, data->xwindow);
+            }
+        }
+        XFlush(display);
+    }
+    return 0;
+}
+
+static void
+X11_WarpMouse(SDL_Window * window, int x, int y)
+{
+    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+    Display *display = data->videodata->display;
+
+    XWarpPointer(display, None, data->xwindow, 0, 0, 0, 0, x, y);
+    XSync(display, False);
+}
+
+static int
+X11_SetRelativeMouseMode(SDL_bool enabled)
+{
+    SDL_Unsupported();
+    return -1;
+}
+
 void
 X11_InitMouse(_THIS)
 {
+    SDL_Mouse *mouse = SDL_GetMouse();
+
+    mouse->CreateCursor = X11_CreateCursor;
+    mouse->ShowCursor = X11_ShowCursor;
+    mouse->FreeCursor = X11_FreeCursor;
+    mouse->WarpMouse = X11_WarpMouse;
+    mouse->SetRelativeMouseMode = X11_SetRelativeMouseMode;
+
+    SDL_SetDefaultCursor(X11_CreateDefaultCursor());
 }
 
 void
 X11_QuitMouse(_THIS)
 {
+    X11_DestroyEmptyCursor();
 }
 
 /* vi: set ts=4 sw=4 expandtab: */
--- a/src/video/x11/SDL_x11sym.h	Fri Mar 11 13:22:43 2011 -0800
+++ b/src/video/x11/SDL_x11sym.h	Fri Mar 11 13:27:25 2011 -0800
@@ -98,6 +98,7 @@
 SDL_X11_SYM(int,XStoreColors,(Display* a,Colormap b,XColor* c,int d),(a,b,c,d),return)
 SDL_X11_SYM(Status,XStringListToTextProperty,(char** a,int b,XTextProperty* c),(a,b,c),return)
 SDL_X11_SYM(int,XSync,(Display* a,Bool b),(a,b),return)
+SDL_X11_SYM(int,XUndefineCursor,(Display* a,Window b),(a,b),return)
 SDL_X11_SYM(int,XUngrabKeyboard,(Display* a,Time b),(a,b),return)
 SDL_X11_SYM(int,XUngrabPointer,(Display* a,Time b),(a,b),return)
 SDL_X11_SYM(int,XUngrabServer,(Display* a),(a),return)