mirror of
https://github.com/Relintai/sdl2_frt.git
synced 2024-12-29 20:27:12 +01:00
449 lines
13 KiB
C
449 lines
13 KiB
C
/*
|
|
Simple DirectMedia Layer
|
|
Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
*/
|
|
#include "../../SDL_internal.h"
|
|
|
|
#if SDL_VIDEO_DRIVER_X11
|
|
|
|
#include <X11/cursorfont.h>
|
|
#include "SDL_assert.h"
|
|
#include "SDL_x11video.h"
|
|
#include "SDL_x11mouse.h"
|
|
#include "SDL_x11xinput2.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_zeroa(data);
|
|
color.red = color.green = color.blue = 0;
|
|
pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
|
|
data, 1, 1);
|
|
if (pixmap) {
|
|
x11_empty_cursor = X11_XCreatePixmapCursor(display, pixmap, pixmap,
|
|
&color, &color, 0, 0);
|
|
X11_XFreePixmap(display, pixmap);
|
|
}
|
|
}
|
|
return x11_empty_cursor;
|
|
}
|
|
|
|
static void
|
|
X11_DestroyEmptyCursor(void)
|
|
{
|
|
if (x11_empty_cursor != None) {
|
|
X11_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 = X11_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 = X11_XcursorImageLoadCursor(display, image);
|
|
|
|
X11_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);
|
|
if (!data_bits) {
|
|
SDL_OutOfMemory();
|
|
return None;
|
|
}
|
|
|
|
mask_bits = SDL_calloc(1, surface->h * width_bytes);
|
|
if (!mask_bits) {
|
|
SDL_free(data_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 = bgBits = 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 = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
|
|
(char*)data_bits,
|
|
surface->w, surface->h);
|
|
mask_pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
|
|
(char*)mask_bits,
|
|
surface->w, surface->h);
|
|
cursor = X11_XCreatePixmapCursor(display, data_pixmap, mask_pixmap,
|
|
&fg, &bg, hot_x, hot_y);
|
|
X11_XFreePixmap(display, data_pixmap);
|
|
X11_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 SDL_Cursor *
|
|
X11_CreateSystemCursor(SDL_SystemCursor id)
|
|
{
|
|
SDL_Cursor *cursor;
|
|
unsigned int shape;
|
|
|
|
switch(id)
|
|
{
|
|
default:
|
|
SDL_assert(0);
|
|
return NULL;
|
|
/* X Font Cursors reference: */
|
|
/* http://tronche.com/gui/x/xlib/appendix/b/ */
|
|
case SDL_SYSTEM_CURSOR_ARROW: shape = XC_left_ptr; break;
|
|
case SDL_SYSTEM_CURSOR_IBEAM: shape = XC_xterm; break;
|
|
case SDL_SYSTEM_CURSOR_WAIT: shape = XC_watch; break;
|
|
case SDL_SYSTEM_CURSOR_CROSSHAIR: shape = XC_tcross; break;
|
|
case SDL_SYSTEM_CURSOR_WAITARROW: shape = XC_watch; break;
|
|
case SDL_SYSTEM_CURSOR_SIZENWSE: shape = XC_fleur; break;
|
|
case SDL_SYSTEM_CURSOR_SIZENESW: shape = XC_fleur; break;
|
|
case SDL_SYSTEM_CURSOR_SIZEWE: shape = XC_sb_h_double_arrow; break;
|
|
case SDL_SYSTEM_CURSOR_SIZENS: shape = XC_sb_v_double_arrow; break;
|
|
case SDL_SYSTEM_CURSOR_SIZEALL: shape = XC_fleur; break;
|
|
case SDL_SYSTEM_CURSOR_NO: shape = XC_pirate; break;
|
|
case SDL_SYSTEM_CURSOR_HAND: shape = XC_hand2; break;
|
|
}
|
|
|
|
cursor = SDL_calloc(1, sizeof(*cursor));
|
|
if (cursor) {
|
|
Cursor x11_cursor;
|
|
|
|
x11_cursor = X11_XCreateFontCursor(GetDisplay(), shape);
|
|
|
|
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) {
|
|
X11_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) {
|
|
X11_XDefineCursor(display, data->xwindow, x11_cursor);
|
|
} else {
|
|
X11_XUndefineCursor(display, data->xwindow);
|
|
}
|
|
}
|
|
X11_XFlush(display);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
WarpMouseInternal(Window xwindow, const int x, const int y)
|
|
{
|
|
SDL_VideoData *videodata = (SDL_VideoData *) SDL_GetVideoDevice()->driverdata;
|
|
Display *display = videodata->display;
|
|
X11_XWarpPointer(display, None, xwindow, 0, 0, 0, 0, x, y);
|
|
X11_XSync(display, False);
|
|
videodata->global_mouse_changed = SDL_TRUE;
|
|
}
|
|
|
|
static void
|
|
X11_WarpMouse(SDL_Window * window, int x, int y)
|
|
{
|
|
SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
|
|
WarpMouseInternal(data->xwindow, x, y);
|
|
}
|
|
|
|
static int
|
|
X11_WarpMouseGlobal(int x, int y)
|
|
{
|
|
WarpMouseInternal(DefaultRootWindow(GetDisplay()), x, y);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
X11_SetRelativeMouseMode(SDL_bool enabled)
|
|
{
|
|
#if SDL_VIDEO_DRIVER_X11_XINPUT2
|
|
if(X11_Xinput2IsInitialized())
|
|
return 0;
|
|
#else
|
|
SDL_Unsupported();
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
X11_CaptureMouse(SDL_Window *window)
|
|
{
|
|
Display *display = GetDisplay();
|
|
|
|
if (window) {
|
|
SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
|
|
const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
|
|
const int rc = X11_XGrabPointer(display, data->xwindow, False,
|
|
mask, GrabModeAsync, GrabModeAsync,
|
|
None, None, CurrentTime);
|
|
if (rc != GrabSuccess) {
|
|
return SDL_SetError("X server refused mouse capture");
|
|
}
|
|
} else {
|
|
X11_XUngrabPointer(display, CurrentTime);
|
|
}
|
|
|
|
X11_XSync(display, False);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static Uint32
|
|
X11_GetGlobalMouseState(int *x, int *y)
|
|
{
|
|
SDL_VideoData *videodata = (SDL_VideoData *) SDL_GetVideoDevice()->driverdata;
|
|
Display *display = GetDisplay();
|
|
const int num_screens = SDL_GetNumVideoDisplays();
|
|
int i;
|
|
|
|
/* !!! FIXME: should we XSync() here first? */
|
|
|
|
#if !SDL_VIDEO_DRIVER_X11_XINPUT2
|
|
videodata->global_mouse_changed = SDL_TRUE;
|
|
#endif
|
|
|
|
/* check if we have this cached since XInput last saw the mouse move. */
|
|
/* !!! FIXME: can we just calculate this from XInput's events? */
|
|
if (videodata->global_mouse_changed) {
|
|
for (i = 0; i < num_screens; i++) {
|
|
SDL_DisplayData *data = (SDL_DisplayData *) SDL_GetDisplayDriverData(i);
|
|
if (data != NULL) {
|
|
Window root, child;
|
|
int rootx, rooty, winx, winy;
|
|
unsigned int mask;
|
|
if (X11_XQueryPointer(display, RootWindow(display, data->screen), &root, &child, &rootx, &rooty, &winx, &winy, &mask)) {
|
|
XWindowAttributes root_attrs;
|
|
Uint32 buttons = 0;
|
|
buttons |= (mask & Button1Mask) ? SDL_BUTTON_LMASK : 0;
|
|
buttons |= (mask & Button2Mask) ? SDL_BUTTON_MMASK : 0;
|
|
buttons |= (mask & Button3Mask) ? SDL_BUTTON_RMASK : 0;
|
|
/* SDL_DisplayData->x,y point to screen origin, and adding them to mouse coordinates relative to root window doesn't do the right thing
|
|
* (observed on dual monitor setup with primary display being the rightmost one - mouse was offset to the right).
|
|
*
|
|
* Adding root position to root-relative coordinates seems to be a better way to get absolute position. */
|
|
X11_XGetWindowAttributes(display, root, &root_attrs);
|
|
videodata->global_mouse_position.x = root_attrs.x + rootx;
|
|
videodata->global_mouse_position.y = root_attrs.y + rooty;
|
|
videodata->global_mouse_buttons = buttons;
|
|
videodata->global_mouse_changed = SDL_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SDL_assert(!videodata->global_mouse_changed); /* The pointer wasn't on any X11 screen?! */
|
|
|
|
*x = videodata->global_mouse_position.x;
|
|
*y = videodata->global_mouse_position.y;
|
|
return videodata->global_mouse_buttons;
|
|
}
|
|
|
|
|
|
void
|
|
X11_InitMouse(_THIS)
|
|
{
|
|
SDL_Mouse *mouse = SDL_GetMouse();
|
|
|
|
mouse->CreateCursor = X11_CreateCursor;
|
|
mouse->CreateSystemCursor = X11_CreateSystemCursor;
|
|
mouse->ShowCursor = X11_ShowCursor;
|
|
mouse->FreeCursor = X11_FreeCursor;
|
|
mouse->WarpMouse = X11_WarpMouse;
|
|
mouse->WarpMouseGlobal = X11_WarpMouseGlobal;
|
|
mouse->SetRelativeMouseMode = X11_SetRelativeMouseMode;
|
|
mouse->CaptureMouse = X11_CaptureMouse;
|
|
mouse->GetGlobalMouseState = X11_GetGlobalMouseState;
|
|
|
|
SDL_SetDefaultCursor(X11_CreateDefaultCursor());
|
|
}
|
|
|
|
void
|
|
X11_QuitMouse(_THIS)
|
|
{
|
|
X11_DestroyEmptyCursor();
|
|
}
|
|
|
|
#endif /* SDL_VIDEO_DRIVER_X11 */
|
|
|
|
/* vi: set ts=4 sw=4 expandtab: */
|