Implemented X11 cursor support.
--- 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)