/* SDL - Simple DirectMedia Layer Copyright (C) 1997-2011 Sam Lantinga 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 Sam Lantinga slouken@libsdl.org */ #include "SDL_config.h" #if SDL_VIDEO_DRIVER_X11 #include "SDL_assert.h" #include "SDL_x11video.h" #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; } #if SDL_VIDEO_DRIVER_X11_XCURSOR static Cursor X11_CreateXCursorCursor(SDL_Surface * surface, int hot_x, int hot_y) { Display *display = GetDisplay(); Cursor cursor = None; XcursorImage *image; image = XcursorImageCreate(surface->w, surface->h); if (!image) { SDL_OutOfMemory(); return None; } image->xhot = hot_x; image->yhot = hot_y; image->delay = 0; SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888); SDL_assert(surface->pitch == surface->w * 4); SDL_memcpy(image->pixels, surface->pixels, surface->h * surface->pitch); cursor = XcursorImageLoadCursor(display, image); XcursorImageDestroy(image); return cursor; } #endif /* SDL_VIDEO_DRIVER_X11_XCURSOR */ 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; } /* Code below assumes ARGB pixel format */ SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888); 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 x11_cursor = None; #if SDL_VIDEO_DRIVER_X11_XCURSOR if (SDL_X11_HAVE_XCURSOR) { x11_cursor = X11_CreateXCursorCursor(surface, hot_x, hot_y); } #endif if (x11_cursor == None) { x11_cursor = X11_CreatePixmapCursor(surface, hot_x, hot_y); } cursor->driverdata = (void*)x11_cursor; } 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(); } #endif /* SDL_VIDEO_DRIVER_X11 */ /* vi: set ts=4 sw=4 expandtab: */