Merge commit '852f2a6343518919e5ca8d3c1bbcab9f493e3cd8'

This commit is contained in:
2024-01-17 17:02:59 +01:00
1244 changed files with 50102 additions and 28146 deletions

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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
@ -99,11 +99,9 @@ static int SetSelectionData(SDL_VideoDevice *_this, Atom selection, SDL_Clipboar
static void *CloneDataBuffer(const void *buffer, size_t *len)
{
void *clone = NULL;
if (*len > 0 && buffer != NULL) {
if (*len > 0 && buffer) {
clone = SDL_malloc((*len)+sizeof(Uint32));
if (clone == NULL) {
SDL_OutOfMemory();
} else {
if (clone) {
SDL_memcpy(clone, buffer, *len);
SDL_memset((Uint8 *)clone + *len, 0, sizeof(Uint32));
}
@ -228,7 +226,7 @@ SDL_bool X11_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
size_t length;
void *data;
data = X11_GetClipboardData(_this, mime_type, &length);
if (data != NULL) {
if (data) {
SDL_free(data);
}
return length > 0;

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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
@ -72,22 +72,22 @@ static void *X11_GetSym(const char *fnname, int *pHasModule)
int i;
void *fn = NULL;
for (i = 0; i < SDL_TABLESIZE(x11libs); i++) {
if (x11libs[i].lib != NULL) {
if (x11libs[i].lib) {
fn = SDL_LoadFunction(x11libs[i].lib, fnname);
if (fn != NULL) {
if (fn) {
break;
}
}
}
#if DEBUG_DYNAMIC_X11
if (fn != NULL)
if (fn)
printf("X11: Found '%s' in %s (%p)\n", fnname, x11libs[i].libname, fn);
else
printf("X11: Symbol '%s' NOT FOUND!\n", fnname);
#endif
if (fn == NULL) {
if (!fn) {
*pHasModule = 0; /* kill this module. */
}
@ -133,7 +133,7 @@ void SDL_X11_UnloadSymbols(void)
#ifdef SDL_VIDEO_DRIVER_X11_DYNAMIC
for (i = 0; i < SDL_TABLESIZE(x11libs); i++) {
if (x11libs[i].lib != NULL) {
if (x11libs[i].lib) {
SDL_UnloadObject(x11libs[i].lib);
x11libs[i].lib = NULL;
}
@ -154,7 +154,7 @@ int SDL_X11_LoadSymbols(void)
int i;
int *thismod = NULL;
for (i = 0; i < SDL_TABLESIZE(x11libs); i++) {
if (x11libs[i].libname != NULL) {
if (x11libs[i].libname) {
x11libs[i].lib = SDL_LoadObject(x11libs[i].libname);
}
}

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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
@ -39,8 +39,7 @@
#include "../../events/SDL_touch_c.h"
#include "../../core/linux/SDL_system_theme.h"
#include "../../SDL_utils_c.h"
#include <SDL3/SDL_syswm.h>
#include "../SDL_sysvideo.h"
#include <stdio.h>
@ -102,7 +101,7 @@ static void X11_ReadProperty(SDL_x11Prop *p, Display *disp, Window w, Atom prop)
int bytes_fetch = 0;
do {
if (ret != NULL) {
if (ret) {
X11_XFree(ret);
}
X11_XGetWindowProperty(disp, w, prop, 0, bytes_fetch, False, AnyPropertyType, &type, &fmt, &count, &bytes_left, &ret);
@ -181,14 +180,14 @@ static SDL_bool X11_KeyRepeat(Display *display, XEvent *event)
return d.found;
}
static SDL_bool X11_IsWheelEvent(Display *display, XEvent *event, int *xticks, int *yticks)
static SDL_bool X11_IsWheelEvent(Display *display, int button, int *xticks, int *yticks)
{
/* according to the xlib docs, no specific mouse wheel events exist.
However, the defacto standard is that the vertical wheel is X buttons
4 (up) and 5 (down) and a horizontal wheel is 6 (left) and 7 (right). */
/* Xlib defines "Button1" through 5, so we just use literals here. */
switch (event->xbutton.button) {
switch (button) {
case 4:
*yticks = 1;
return SDL_TRUE;
@ -224,7 +223,7 @@ static int X11_URIDecode(char *buf, int len)
{
int ri, wi, di;
char decode = '\0';
if (buf == NULL || len < 0) {
if (!buf || len < 0) {
errno = EINVAL;
return -1;
}
@ -300,7 +299,7 @@ static char *X11_URIToLocal(char *uri)
/* got a hostname? */
if (!local && uri[0] == '/' && uri[2] != '/') {
char *hostname_end = SDL_strchr(uri + 1, '/');
if (hostname_end != NULL) {
if (hostname_end) {
char hostname[257];
if (gethostname(hostname, 255) == 0) {
hostname[256] = '\0';
@ -324,27 +323,27 @@ static char *X11_URIToLocal(char *uri)
return file;
}
#ifdef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
static void X11_HandleGenericEvent(SDL_VideoData *videodata, XEvent *xev)
/* An X11 event hook */
static SDL_X11EventHook g_X11EventHook = NULL;
static void *g_X11EventHookData = NULL;
void SDL_SetX11EventHook(SDL_X11EventHook callback, void *userdata)
{
g_X11EventHook = callback;
g_X11EventHookData = userdata;
}
#ifdef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
static void X11_HandleGenericEvent(SDL_VideoDevice *_this, XEvent *xev)
{
SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
/* event is a union, so cookie == &event, but this is type safe. */
XGenericEventCookie *cookie = &xev->xcookie;
if (X11_XGetEventData(videodata->display, cookie)) {
X11_HandleXinput2Event(videodata, cookie);
/* Send a SDL_EVENT_SYSWM if the application wants them.
* Since event data is only available until XFreeEventData is called,
* the *only* way for an application to access it is to register an event filter/watcher
* and do all the processing on the SDL_EVENT_SYSWM inside the callback. */
if (SDL_EventEnabled(SDL_EVENT_SYSWM)) {
SDL_SysWMmsg wmmsg;
wmmsg.version = SDL_SYSWM_CURRENT_VERSION;
wmmsg.subsystem = SDL_SYSWM_X11;
wmmsg.msg.x11.event = *xev;
SDL_SendSysWMEvent(&wmmsg);
if (!g_X11EventHook || g_X11EventHook(g_X11EventHookData, xev)) {
X11_HandleXinput2Event(_this, cookie);
}
X11_XFreeEventData(videodata->display, cookie);
}
}
@ -561,13 +560,26 @@ static void InitiateWindowResize(SDL_VideoDevice *_this, const SDL_WindowData *d
X11_XSync(display, 0);
}
static SDL_bool ProcessHitTest(SDL_VideoDevice *_this, const SDL_WindowData *data, const XEvent *xev)
SDL_bool X11_ProcessHitTest(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y, SDL_bool force_new_result)
{
SDL_Window *window = data->window;
if (!window->hit_test) return SDL_FALSE;
const SDL_Point point = { x, y };
SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
if (!force_new_result && rc == data->hit_test_result) {
return SDL_TRUE;
}
X11_SetHitTestCursor(rc);
data->hit_test_result = rc;
return SDL_TRUE;
}
SDL_bool X11_TriggerHitTestAction(SDL_VideoDevice *_this, const SDL_WindowData *data, const float x, const float y)
{
SDL_Window *window = data->window;
if (window->hit_test) {
const SDL_Point point = { xev->xbutton.x, xev->xbutton.y };
const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
const SDL_Point point = { x, y };
static const int directions[] = {
_NET_WM_MOVERESIZE_SIZE_TOPLEFT, _NET_WM_MOVERESIZE_SIZE_TOP,
_NET_WM_MOVERESIZE_SIZE_TOPRIGHT, _NET_WM_MOVERESIZE_SIZE_RIGHT,
@ -575,7 +587,7 @@ static SDL_bool ProcessHitTest(SDL_VideoDevice *_this, const SDL_WindowData *dat
_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT, _NET_WM_MOVERESIZE_SIZE_LEFT
};
switch (rc) {
switch (data->hit_test_result) {
case SDL_HITTEST_DRAGGABLE:
InitiateWindowMove(_this, data, &point);
return SDL_TRUE;
@ -588,7 +600,7 @@ static SDL_bool ProcessHitTest(SDL_VideoDevice *_this, const SDL_WindowData *dat
case SDL_HITTEST_RESIZE_BOTTOM:
case SDL_HITTEST_RESIZE_BOTTOMLEFT:
case SDL_HITTEST_RESIZE_LEFT:
InitiateWindowResize(_this, data, &point, directions[rc - SDL_HITTEST_RESIZE_TOPLEFT]);
InitiateWindowResize(_this, data, &point, directions[data->hit_test_result - SDL_HITTEST_RESIZE_TOPLEFT]);
return SDL_TRUE;
default:
@ -689,7 +701,7 @@ static void X11_HandleClipboardEvent(SDL_VideoDevice *_this, const XEvent *xeven
/* FIXME: We don't support the X11 INCR protocol for large clipboards. Do we want that? - Yes, yes we do. */
/* This is a safe cast, XChangeProperty() doesn't take a const value, but it doesn't modify the data */
seln_data = (unsigned char *)clipboard->callback(clipboard->userdata, mime_type, &seln_length);
if (seln_data != NULL) {
if (seln_data) {
X11_XChangeProperty(display, req->requestor, req->property,
req->target, 8, PropModeReplace,
seln_data, seln_length);
@ -765,21 +777,21 @@ static Bool isReparentNotify(Display *display, XEvent *ev, XPointer arg)
static SDL_bool IsHighLatin1(const char *string, int length)
{
while (length-- > 0) {
Uint8 ch = (Uint8)*string;
if (ch >= 0x80) {
return SDL_TRUE;
}
++string;
}
return SDL_FALSE;
while (length-- > 0) {
Uint8 ch = (Uint8)*string;
if (ch >= 0x80) {
return SDL_TRUE;
}
++string;
}
return SDL_FALSE;
}
static int XLookupStringAsUTF8(XKeyEvent *event_struct, char *buffer_return, int bytes_buffer, KeySym *keysym_return, XComposeStatus *status_in_out)
{
int result = X11_XLookupString(event_struct, buffer_return, bytes_buffer, keysym_return, status_in_out);
if (IsHighLatin1(buffer_return, result)) {
char *utf8_text = SDL_iconv_string("UTF-8", "ISO-8859-1", buffer_return, result);
char *utf8_text = SDL_iconv_string("UTF-8", "ISO-8859-1", buffer_return, result + 1);
if (utf8_text) {
SDL_strlcpy(buffer_return, utf8_text, bytes_buffer);
SDL_free(utf8_text);
@ -791,6 +803,78 @@ static int XLookupStringAsUTF8(XKeyEvent *event_struct, char *buffer_return, int
return result;
}
SDL_WindowData *X11_FindWindow(SDL_VideoDevice *_this, Window window)
{
const SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
int i;
if (videodata && videodata->windowlist) {
for (i = 0; i < videodata->numwindows; ++i) {
if ((videodata->windowlist[i] != NULL) &&
(videodata->windowlist[i]->xwindow == window)) {
return videodata->windowlist[i];
}
}
}
return NULL;
}
void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, int button, const float x, const float y, const unsigned long time)
{
SDL_Window *window = windowdata->window;
const SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
Display *display = videodata->display;
int xticks = 0, yticks = 0;
#ifdef DEBUG_XEVENTS
printf("window %p: ButtonPress (X11 button = %d)\n", window, button);
#endif
if (X11_IsWheelEvent(display, button, &xticks, &yticks)) {
SDL_SendMouseWheel(0, window, 0, (float)-xticks, (float)yticks, SDL_MOUSEWHEEL_NORMAL);
} else {
SDL_bool ignore_click = SDL_FALSE;
if (button == Button1) {
if (X11_TriggerHitTestAction(_this, windowdata, x, y)) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_HIT_TEST, 0, 0);
return; /* don't pass this event on to app. */
}
} else if (button > 7) {
/* X button values 4-7 are used for scrolling, so X1 is 8, X2 is 9, ...
=> subtract (8-SDL_BUTTON_X1) to get value SDL expects */
button -= (8 - SDL_BUTTON_X1);
}
if (windowdata->last_focus_event_time) {
const int X11_FOCUS_CLICK_TIMEOUT = 10;
if (SDL_GetTicks() < (windowdata->last_focus_event_time + X11_FOCUS_CLICK_TIMEOUT)) {
ignore_click = !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, SDL_FALSE);
}
windowdata->last_focus_event_time = 0;
}
if (!ignore_click) {
SDL_SendMouseButton(0, window, 0, SDL_PRESSED, button);
}
}
X11_UpdateUserTime(windowdata, time);
}
void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, int button)
{
SDL_Window *window = windowdata->window;
const SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
Display *display = videodata->display;
/* The X server sends a Release event for each Press for wheels. Ignore them. */
int xticks = 0, yticks = 0;
#ifdef DEBUG_XEVENTS
printf("window %p: ButtonRelease (X11 button = %d)\n", data, xevent->xbutton.button);
#endif
if (!X11_IsWheelEvent(display, button, &xticks, &yticks)) {
if (button > 7) {
/* see explanation at case ButtonPress */
button -= (8 - SDL_BUTTON_X1);
}
SDL_SendMouseButton(0, window, 0, SDL_RELEASED, button);
}
}
void X11_GetBorderValues(SDL_WindowData *data)
{
SDL_VideoData *videodata = data->videodata;
@ -800,18 +884,24 @@ void X11_GetBorderValues(SDL_WindowData *data)
int format;
unsigned long nitems, bytes_after;
unsigned char *property;
if (X11_XGetWindowProperty(display, data->xwindow, videodata->_NET_FRAME_EXTENTS, 0, 16, 0, XA_CARDINAL, &type, &format, &nitems, &bytes_after, &property) == Success) {
if (type != None && nitems == 4) {
data->border_left = (int)((long *)property)[0];
data->border_right = (int)((long *)property)[1];
data->border_top = (int)((long *)property)[2];
data->border_bottom = (int)((long *)property)[3];
}
X11_XFree(property);
/* Some compositors will send extents even when the border hint is turned off. Ignore them in this case. */
if (!(data->window->flags & SDL_WINDOW_BORDERLESS)) {
if (X11_XGetWindowProperty(display, data->xwindow, videodata->_NET_FRAME_EXTENTS, 0, 16, 0, XA_CARDINAL, &type, &format, &nitems, &bytes_after, &property) == Success) {
if (type != None && nitems == 4) {
data->border_left = (int)((long *)property)[0];
data->border_right = (int)((long *)property)[1];
data->border_top = (int)((long *)property)[2];
data->border_bottom = (int)((long *)property)[3];
}
X11_XFree(property);
#ifdef DEBUG_XEVENTS
printf("New _NET_FRAME_EXTENTS: left=%d right=%d, top=%d, bottom=%d\n", data->border_left, data->border_right, data->border_top, data->border_bottom);
printf("New _NET_FRAME_EXTENTS: left=%d right=%d, top=%d, bottom=%d\n", data->border_left, data->border_right, data->border_top, data->border_bottom);
#endif
}
} else {
data->border_left = data->border_top = data->border_right = data->border_bottom = 0;
}
}
@ -865,27 +955,24 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
#ifdef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
if (xevent->type == GenericEvent) {
X11_HandleGenericEvent(videodata, xevent);
X11_HandleGenericEvent(_this, xevent);
return;
}
#endif
/* Calling the event hook for generic events happens in X11_HandleGenericEvent(), where the event data is available */
if (g_X11EventHook) {
if (!g_X11EventHook(g_X11EventHookData, xevent)) {
return;
}
}
#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
if (videodata->xrandr_event_base && (xevent->type == (videodata->xrandr_event_base + RRNotify))) {
X11_HandleXRandREvent(_this, xevent);
}
#endif
/* Send a SDL_EVENT_SYSWM if the application wants them */
if (SDL_EventEnabled(SDL_EVENT_SYSWM)) {
SDL_SysWMmsg wmmsg;
wmmsg.version = SDL_SYSWM_CURRENT_VERSION;
wmmsg.subsystem = SDL_SYSWM_X11;
wmmsg.msg.x11.event = *xevent;
SDL_SendSysWMEvent(&wmmsg);
}
#if 0
printf("type = %d display = %d window = %d\n",
xevent->type, xevent->xany.display, xevent->xany.window);
@ -893,19 +980,19 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
if (SDL_X11_HAVE_XFIXES &&
xevent->type == X11_GetXFixesSelectionNotifyEvent()) {
XFixesSelectionNotifyEvent *ev = (XFixesSelectionNotifyEvent *) xevent;
xevent->type == X11_GetXFixesSelectionNotifyEvent()) {
XFixesSelectionNotifyEvent *ev = (XFixesSelectionNotifyEvent *)xevent;
/* !!! FIXME: cache atoms */
Atom XA_CLIPBOARD = X11_XInternAtom(display, "CLIPBOARD", 0);
#ifdef DEBUG_XEVENTS
printf("window CLIPBOARD: XFixesSelectionNotify (selection = %s)\n",
X11_XGetAtomName(display, ev->selection));
X11_XGetAtomName(display, ev->selection));
#endif
if (ev->selection == XA_PRIMARY ||
(XA_CLIPBOARD != None && ev->selection == XA_CLIPBOARD)) {
(XA_CLIPBOARD != None && ev->selection == XA_CLIPBOARD)) {
SDL_SendClipboardUpdate();
return;
}
@ -918,17 +1005,9 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
return;
}
data = NULL;
if (videodata && videodata->windowlist) {
for (i = 0; i < videodata->numwindows; ++i) {
if ((videodata->windowlist[i] != NULL) &&
(videodata->windowlist[i]->xwindow == xevent->xany.window)) {
data = videodata->windowlist[i];
break;
}
}
}
if (data == NULL) {
data = X11_FindWindow(_this, xevent->xany.window);
if (!data) {
/* The window for KeymapNotify, etc events is 0 */
if (xevent->type == KeymapNotify) {
#ifdef DEBUG_XEVENTS
@ -1019,6 +1098,8 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
/* We ungrab in LeaveNotify, so we may need to grab again here */
SDL_UpdateWindowGrab(data->window);
X11_ProcessHitTest(_this, data, mouse->last_x, mouse->last_y, SDL_TRUE);
} break;
/* Losing mouse coverage? */
case LeaveNotify:
@ -1234,8 +1315,9 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
xevent->xconfigure.x, xevent->xconfigure.y,
xevent->xconfigure.width, xevent->xconfigure.height);
#endif
/* Real configure notify events are relative to the parent, synthetic events are absolute. */
if (!xevent->xconfigure.send_event) {
/* Real configure notify events are relative to the parent, synthetic events are absolute. */
if (!xevent->xconfigure.send_event)
{
unsigned int NumChildren;
Window ChildReturn, Root, Parent;
Window *Children;
@ -1250,33 +1332,39 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
if (xevent->xconfigure.x != data->last_xconfigure.x ||
xevent->xconfigure.y != data->last_xconfigure.y) {
SDL_Window *w;
int x = xevent->xconfigure.x;
int y = xevent->xconfigure.y;
if (!data->disable_size_position_events) {
SDL_Window *w;
int x = xevent->xconfigure.x;
int y = xevent->xconfigure.y;
SDL_GlobalToRelativeForWindow(data->window, x, y, &x, &y);
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED,
x, y);
data->pending_operation &= ~X11_PENDING_OP_MOVE;
SDL_GlobalToRelativeForWindow(data->window, x, y, &x, &y);
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED, x, y);
#ifdef SDL_USE_IME
if (SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) {
/* Update IME candidate list position */
SDL_IME_UpdateTextRect(NULL);
}
if (SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) {
/* Update IME candidate list position */
SDL_IME_UpdateTextRect(NULL);
}
#endif
for (w = data->window->first_child; w != NULL; w = w->next_sibling) {
/* Don't update hidden child windows, their relative position doesn't change */
if (!(w->flags & SDL_WINDOW_HIDDEN)) {
X11_UpdateWindowPosition(w);
for (w = data->window->first_child; w; w = w->next_sibling) {
/* Don't update hidden child windows, their relative position doesn't change */
if (!(w->flags & SDL_WINDOW_HIDDEN)) {
X11_UpdateWindowPosition(w, SDL_TRUE);
}
}
}
}
if (xevent->xconfigure.width != data->last_xconfigure.width ||
xevent->xconfigure.height != data->last_xconfigure.height) {
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESIZED,
xevent->xconfigure.width,
xevent->xconfigure.height);
if (!data->disable_size_position_events) {
data->pending_operation &= ~X11_PENDING_OP_RESIZE;
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESIZED,
xevent->xconfigure.width,
xevent->xconfigure.height);
}
}
data->last_xconfigure = xevent->xconfigure;
} break;
@ -1325,9 +1413,9 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
root_y = xevent->xclient.data.l[2] & 0xffff;
/* Translate from root to current window position */
X11_XTranslateCoordinates(display, DefaultRootWindow(display), data->xwindow,
root_x, root_y, &window_x, &window_y, &ChildReturn);
root_x, root_y, &window_x, &window_y, &ChildReturn);
SDL_SendDropPosition(data->window, NULL, (float)window_x, (float)window_y); /* FIXME, can we get the filename ? */
SDL_SendDropPosition(data->window, (float)window_x, (float)window_y);
}
/* reply with status */
@ -1409,6 +1497,12 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_EXPOSED, 0, 0);
} break;
/* Use XInput2 instead of the xevents API if possible, for:
- MotionNotify
- ButtonPress
- ButtonRelease
XInput2 has more precise information, e.g., to distinguish different input devices. */
#ifndef SDL_VIDEO_DRIVER_X11_XINPUT2
case MotionNotify:
{
SDL_Mouse *mouse = SDL_GetMouse();
@ -1417,61 +1511,22 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
printf("window %p: X11 motion: %d,%d\n", data, xevent->xmotion.x, xevent->xmotion.y);
#endif
X11_ProcessHitTest(_this, data, (float)xevent->xmotion.x, (float)xevent->xmotion.y, SDL_FALSE);
SDL_SendMouseMotion(0, data->window, 0, 0, (float)xevent->xmotion.x, (float)xevent->xmotion.y);
}
} break;
case ButtonPress:
{
int xticks = 0, yticks = 0;
#ifdef DEBUG_XEVENTS
printf("window %p: ButtonPress (X11 button = %d)\n", data, xevent->xbutton.button);
#endif
if (X11_IsWheelEvent(display, xevent, &xticks, &yticks)) {
SDL_SendMouseWheel(0, data->window, 0, (float)-xticks, (float)yticks, SDL_MOUSEWHEEL_NORMAL);
} else {
SDL_bool ignore_click = SDL_FALSE;
int button = xevent->xbutton.button;
if (button == Button1) {
if (ProcessHitTest(_this, data, xevent)) {
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_HIT_TEST, 0, 0);
break; /* don't pass this event on to app. */
}
} else if (button > 7) {
/* X button values 4-7 are used for scrolling, so X1 is 8, X2 is 9, ...
=> subtract (8-SDL_BUTTON_X1) to get value SDL expects */
button -= (8 - SDL_BUTTON_X1);
}
if (data->last_focus_event_time) {
const int X11_FOCUS_CLICK_TIMEOUT = 10;
if (SDL_GetTicks() < (data->last_focus_event_time + X11_FOCUS_CLICK_TIMEOUT)) {
ignore_click = !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, SDL_FALSE);
}
data->last_focus_event_time = 0;
}
if (!ignore_click) {
SDL_SendMouseButton(0, data->window, 0, SDL_PRESSED, button);
}
}
X11_UpdateUserTime(data, xevent->xbutton.time);
X11_HandleButtonPress(_this, data, xevent->xbutton.button,
xevent->xbutton.x, xevent->xbutton.y, xevent->xbutton.time);
} break;
case ButtonRelease:
{
int button = xevent->xbutton.button;
/* The X server sends a Release event for each Press for wheels. Ignore them. */
int xticks = 0, yticks = 0;
#ifdef DEBUG_XEVENTS
printf("window %p: ButtonRelease (X11 button = %d)\n", data, xevent->xbutton.button);
#endif
if (!X11_IsWheelEvent(display, xevent, &xticks, &yticks)) {
if (button > 7) {
/* see explanation at case ButtonPress */
button -= (8 - SDL_BUTTON_X1);
}
SDL_SendMouseButton(0, data->window, 0, SDL_RELEASED, button);
}
X11_HandleButtonRelease(_this, data, xevent->xbutton.button);
} break;
#endif /* !SDL_VIDEO_DRIVER_X11_XINPUT2 */
case PropertyNotify:
{
@ -1579,17 +1634,92 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
}
}
if ((changed & SDL_WINDOW_MAXIMIZED) && ((flags & SDL_WINDOW_MAXIMIZED) && !(flags & SDL_WINDOW_MINIMIZED))) {
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0);
}
if ((changed & SDL_WINDOW_MINIMIZED) && (flags & SDL_WINDOW_MINIMIZED)) {
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
}
if (((changed & SDL_WINDOW_MAXIMIZED) || (changed & SDL_WINDOW_MINIMIZED)) &&
(!(flags & SDL_WINDOW_MAXIMIZED) && !(flags & SDL_WINDOW_MINIMIZED))) {
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
}
if (!SDL_WINDOW_IS_POPUP(data->window)) {
if (changed & SDL_WINDOW_FULLSCREEN) {
data->pending_operation &= ~X11_PENDING_OP_FULLSCREEN;
if (flags & SDL_WINDOW_FULLSCREEN) {
if (!(flags & SDL_WINDOW_MINIMIZED)) {
const SDL_bool commit = data->requested_fullscreen_mode.displayID == 0 ||
SDL_memcmp(&data->window->current_fullscreen_mode, &data->requested_fullscreen_mode, sizeof(SDL_DisplayMode)) != 0;
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0);
if (commit) {
/* This was initiated by the compositor, or the mode was changed between the request and the window
* becoming fullscreen. Switch to the application requested mode if necessary.
*/
SDL_copyp(&data->window->current_fullscreen_mode, &data->window->requested_fullscreen_mode);
SDL_UpdateFullscreenMode(data->window, SDL_TRUE, SDL_TRUE);
} else {
SDL_UpdateFullscreenMode(data->window, SDL_TRUE, SDL_FALSE);
}
}
} else {
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0);
SDL_UpdateFullscreenMode(data->window, SDL_FALSE, SDL_FALSE);
/* Need to restore or update any limits changed while the window was fullscreen. */
X11_SetWindowMinMax(data->window, !!(flags & SDL_WINDOW_MAXIMIZED));
}
if ((flags & SDL_WINDOW_FULLSCREEN) &&
(data->border_top || data->border_left || data->border_bottom || data->border_right)) {
/* If the window is entering fullscreen and the borders are
* non-zero sized, turn off size events until the borders are
* shut off to avoid bogus window sizes and positions, and
* note that the old borders were non-zero for restoration.
*/
data->disable_size_position_events = SDL_TRUE;
data->previous_borders_nonzero = SDL_TRUE;
} else if (!(flags & SDL_WINDOW_FULLSCREEN) &&
data->previous_borders_nonzero &&
(!data->border_top && !data->border_left && !data->border_bottom && !data->border_right)) {
/* If the window is leaving fullscreen and the current borders
* are zero sized, but weren't when entering fullscreen, turn
* off size events until the borders come back to avoid bogus
* window sizes and positions.
*/
data->disable_size_position_events = SDL_TRUE;
data->previous_borders_nonzero = SDL_FALSE;
} else {
data->disable_size_position_events = SDL_FALSE;
data->previous_borders_nonzero = SDL_FALSE;
if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) {
data->toggle_borders = SDL_FALSE;
X11_SetWindowBordered(_this, data->window, !(data->window->flags & SDL_WINDOW_BORDERLESS));
}
}
}
if ((changed & SDL_WINDOW_MAXIMIZED) && ((flags & SDL_WINDOW_MAXIMIZED) && !(flags & SDL_WINDOW_MINIMIZED))) {
data->pending_operation &= ~X11_PENDING_OP_MAXIMIZE;
if ((changed & SDL_WINDOW_MINIMIZED)) {
data->pending_operation &= ~X11_PENDING_OP_RESTORE;
/* If coming out of minimized, send a restore event before sending maximized. */
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
}
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0);
}
if ((changed & SDL_WINDOW_MINIMIZED) && (flags & SDL_WINDOW_MINIMIZED)) {
data->pending_operation &= ~X11_PENDING_OP_MINIMIZE;
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
}
if (!(flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) {
data->pending_operation &= ~X11_PENDING_OP_RESTORE;
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
/* Restore the last known floating state if leaving maximized mode */
if (!(flags & SDL_WINDOW_FULLSCREEN)) {
data->pending_operation |= X11_PENDING_OP_MOVE | X11_PENDING_OP_RESIZE;
data->expected.x = data->window->floating.x - data->border_left;
data->expected.y = data->window->floating.y - data->border_top;
data->expected.w = data->window->floating.w;
data->expected.h = data->window->floating.h;
X11_XMoveWindow(display, data->xwindow, data->window->floating.x - data->border_left, data->window->floating.y - data->border_top);
X11_XResizeWindow(display, data->xwindow, data->window->floating.w, data->window->floating.h);
}
}
}
if (changed & SDL_WINDOW_OCCLUDED) {
SDL_SendWindowEvent(data->window, (flags & SDL_WINDOW_OCCLUDED) ? SDL_EVENT_WINDOW_OCCLUDED : SDL_EVENT_WINDOW_EXPOSED, 0, 0);
}
@ -1602,7 +1732,30 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
right approach, but it seems to work. */
X11_UpdateKeymap(_this, SDL_TRUE);
} else if (xevent->xproperty.atom == videodata->_NET_FRAME_EXTENTS) {
/* Re-enable size events if they were turned off waiting for the borders to come back
* when leaving fullscreen.
*/
data->disable_size_position_events = SDL_FALSE;
X11_GetBorderValues(data);
if (data->border_top != 0 || data->border_left != 0 || data->border_right != 0 || data->border_bottom != 0) {
/* Adjust if the window size/position changed to accommodate the borders. */
if (data->window->flags & SDL_WINDOW_MAXIMIZED) {
data->pending_operation |= X11_PENDING_OP_RESIZE;
data->expected.w = data->window->windowed.w;
data->expected.h = data->window->windowed.h;
X11_XResizeWindow(display, data->xwindow, data->window->windowed.w, data->window->windowed.h);
} else {
data->pending_operation |= X11_PENDING_OP_RESIZE | X11_PENDING_OP_MOVE;
data->expected.w = data->window->floating.w;
data->expected.h = data->window->floating.h;
X11_XMoveWindow(display, data->xwindow, data->window->floating.x - data->border_left, data->window->floating.y - data->border_top);
X11_XResizeWindow(display, data->xwindow, data->window->floating.w, data->window->floating.h);
}
}
if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) {
data->toggle_borders = SDL_FALSE;
X11_SetWindowBordered(_this, data->window, !(data->window->flags & SDL_WINDOW_BORDERLESS));
}
}
} break;
@ -1623,13 +1776,13 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
char *name = X11_XGetAtomName(display, target);
if (name) {
char *token = SDL_strtok_r((char *)p.data, "\r\n", &saveptr);
while (token != NULL) {
while (token) {
if (SDL_strcmp("text/plain", name) == 0) {
SDL_SendDropText(data->window, token);
} else if (SDL_strcmp("text/uri-list", name) == 0) {
char *fn = X11_URIToLocal(token);
if (fn) {
SDL_SendDropFile(data->window, fn);
SDL_SendDropFile(data->window, NULL, fn);
}
}
token = SDL_strtok_r(NULL, "\r\n", &saveptr);
@ -1782,6 +1935,22 @@ void X11_PumpEvents(SDL_VideoDevice *_this)
XEvent xevent;
int i;
/* Check if a display had the mode changed and is waiting for a window to asynchronously become
* fullscreen. If there is no fullscreen window past the elapsed timeout, revert the mode switch.
*/
for (i = 0; i < _this->num_displays; ++i) {
if (_this->displays[i]->driverdata->mode_switch_deadline_ns &&
SDL_GetTicksNS() >= _this->displays[i]->driverdata->mode_switch_deadline_ns) {
if (_this->displays[i]->fullscreen_window) {
_this->displays[i]->driverdata->mode_switch_deadline_ns = 0;
} else {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO,
"Time out elapsed after mode switch on display %" SDL_PRIu32 " with no window becoming fullscreen; reverting", _this->displays[i]->id);
SDL_SetDisplayModeForDisplay(_this->displays[i], NULL);
}
}
}
if (data->last_mode_change_deadline) {
if (SDL_GetTicks() >= data->last_mode_change_deadline) {
data->last_mode_change_deadline = 0; /* assume we're done. */

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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
@ -29,5 +29,10 @@ extern void X11_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window);
extern int X11_SuspendScreenSaver(SDL_VideoDevice *_this);
extern void X11_ReconcileKeyboardState(SDL_VideoDevice *_this);
extern void X11_GetBorderValues(SDL_WindowData *data);
extern void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *wdata, int button, const float x, const float y, const unsigned long time);
extern void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *wdata, int button);
extern SDL_WindowData *X11_FindWindow(SDL_VideoDevice *_this, Window window);
extern SDL_bool X11_ProcessHitTest(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y, SDL_bool force_new_result);
extern SDL_bool X11_TriggerHitTestAction(SDL_VideoDevice *_this, const SDL_WindowData *data, const float x, const float y);
#endif /* SDL_x11events_h_ */

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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
@ -127,8 +127,8 @@ int X11_CreateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, Uint
#endif /* not NO_SHARED_MEMORY */
*pixels = SDL_malloc((size_t)h * (*pitch));
if (*pixels == NULL) {
return SDL_OutOfMemory();
if (!*pixels) {
return -1;
}
data->ximage = X11_XCreateImage(display, data->visual,
@ -226,7 +226,7 @@ void X11_DestroyWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window)
SDL_WindowData *data = window->driverdata;
Display *display;
if (data == NULL) {
if (!data) {
/* The window wasn't fully initialized */
return;
}

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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
@ -190,15 +190,15 @@ static int X11_MessageBoxInit(SDL_MessageBoxDataX11 *data, const SDL_MessageBoxD
int num_missing = 0;
data->font_set = X11_XCreateFontSet(data->display, g_MessageBoxFont,
&missing, &num_missing, NULL);
if (missing != NULL) {
if (missing) {
X11_XFreeStringList(missing);
}
if (data->font_set == NULL) {
if (!data->font_set) {
return SDL_SetError("Couldn't load font %s", g_MessageBoxFont);
}
} else {
data->font_struct = X11_XLoadQueryFont(data->display, g_MessageBoxFontLatin1);
if (data->font_struct == NULL) {
if (!data->font_struct) {
return SDL_SetError("Couldn't load font %s", g_MessageBoxFontLatin1);
}
}
@ -239,13 +239,13 @@ static int X11_MessageBoxInitPositions(SDL_MessageBoxDataX11 *data)
const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
/* Go over text and break linefeeds into separate lines. */
if (messageboxdata != NULL && messageboxdata->message[0]) {
if (messageboxdata && messageboxdata->message[0]) {
const char *text = messageboxdata->message;
const int linecount = CountLinesOfText(text);
TextLineData *plinedata = (TextLineData *)SDL_malloc(sizeof(TextLineData) * linecount);
if (plinedata == NULL) {
return SDL_OutOfMemory();
if (!plinedata) {
return -1;
}
data->linedata = plinedata;
@ -272,7 +272,7 @@ static int X11_MessageBoxInitPositions(SDL_MessageBoxDataX11 *data)
text += length + 1;
/* Break if there are no more linefeeds. */
if (lf == NULL) {
if (!lf) {
break;
}
}
@ -361,12 +361,12 @@ static int X11_MessageBoxInitPositions(SDL_MessageBoxDataX11 *data)
/* Free SDL_MessageBoxData data. */
static void X11_MessageBoxShutdown(SDL_MessageBoxDataX11 *data)
{
if (data->font_set != NULL) {
if (data->font_set) {
X11_XFreeFontSet(data->display, data->font_set);
data->font_set = NULL;
}
if (data->font_struct != NULL) {
if (data->font_struct) {
X11_XFreeFont(data->display, data->font_struct);
data->font_struct = NULL;
}
@ -760,10 +760,10 @@ static int X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int
#if SDL_SET_LOCALE
origlocale = setlocale(LC_ALL, NULL);
if (origlocale != NULL) {
if (origlocale) {
origlocale = SDL_strdup(origlocale);
if (origlocale == NULL) {
return SDL_OutOfMemory();
if (!origlocale) {
return -1;
}
(void)setlocale(LC_ALL, "");
}

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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
@ -27,6 +27,11 @@
/* #define X11MODES_DEBUG */
/* Timeout and revert mode switches if the timespan has elapsed without the window becoming fullscreen.
* 5 seconds seems good from testing.
*/
#define MODE_SWITCH_TIMEOUT_NS SDL_NS_PER_SECOND * 5
/* I'm becoming more and more convinced that the application should never
* use XRandR, and it's the window manager's responsibility to track and
* manage display modes for fullscreen windows. Right now XRandR is completely
@ -536,7 +541,7 @@ static int X11_AddXRandRDisplay(SDL_VideoDevice *_this, Display *dpy, int screen
}
output_info = X11_XRRGetOutputInfo(dpy, res, outputid);
if (output_info == NULL || !output_info->crtc || output_info->connection == RR_Disconnected) {
if (!output_info || !output_info->crtc || output_info->connection == RR_Disconnected) {
X11_XRRFreeOutputInfo(output_info);
return 0; /* ignore this one. */
}
@ -548,7 +553,7 @@ static int X11_AddXRandRDisplay(SDL_VideoDevice *_this, Display *dpy, int screen
X11_XRRFreeOutputInfo(output_info);
crtc = X11_XRRGetCrtcInfo(dpy, res, output_crtc);
if (crtc == NULL) {
if (!crtc) {
return 0; /* oh well, ignore it. */
}
@ -564,14 +569,14 @@ static int X11_AddXRandRDisplay(SDL_VideoDevice *_this, Display *dpy, int screen
X11_XRRFreeCrtcInfo(crtc);
displaydata = (SDL_DisplayData *)SDL_calloc(1, sizeof(*displaydata));
if (displaydata == NULL) {
return SDL_OutOfMemory();
if (!displaydata) {
return -1;
}
modedata = (SDL_DisplayModeData *)SDL_calloc(1, sizeof(SDL_DisplayModeData));
if (modedata == NULL) {
if (!modedata) {
SDL_free(displaydata);
return SDL_OutOfMemory();
return -1;
}
modedata->xrandr_mode = modeID;
@ -626,11 +631,11 @@ static void X11_HandleXRandROutputChange(SDL_VideoDevice *_this, const XRROutput
}
if (ev->connection == RR_Disconnected) { /* output is going away */
if (display != NULL) {
if (display) {
SDL_DelVideoDisplay(display->id, SDL_TRUE);
}
} else if (ev->connection == RR_Connected) { /* output is coming online */
if (display != NULL) {
if (display) {
/* !!! FIXME: update rotation or current mode of existing display? */
} else {
Display *dpy = ev->display;
@ -638,7 +643,7 @@ static void X11_HandleXRandROutputChange(SDL_VideoDevice *_this, const XRROutput
XVisualInfo vinfo;
if (get_visualinfo(dpy, screen, &vinfo) == 0) {
XRRScreenResources *res = X11_XRRGetScreenResourcesCurrent(dpy, RootWindow(dpy, screen));
if (res == NULL || res->noutput == 0) {
if (!res || res->noutput == 0) {
if (res) {
X11_XRRFreeScreenResources(res);
}
@ -694,13 +699,13 @@ static int X11_InitModes_XRandR(SDL_VideoDevice *_this)
}
res = X11_XRRGetScreenResourcesCurrent(dpy, RootWindow(dpy, screen));
if (res == NULL || res->noutput == 0) {
if (!res || res->noutput == 0) {
if (res) {
X11_XRRFreeScreenResources(res);
}
res = X11_XRRGetScreenResources(dpy, RootWindow(dpy, screen));
if (res == NULL) {
if (!res) {
continue;
}
}
@ -767,14 +772,14 @@ static int X11_InitModes_StdXlib(SDL_VideoDevice *_this)
mode.format = pixelformat;
displaydata = (SDL_DisplayData *)SDL_calloc(1, sizeof(*displaydata));
if (displaydata == NULL) {
return SDL_OutOfMemory();
if (!displaydata) {
return -1;
}
modedata = (SDL_DisplayModeData *)SDL_calloc(1, sizeof(SDL_DisplayModeData));
if (modedata == NULL) {
if (!modedata) {
SDL_free(displaydata);
return SDL_OutOfMemory();
return -1;
}
mode.driverdata = modedata;
@ -861,7 +866,7 @@ int X11_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display)
if (output_info && output_info->connection != RR_Disconnected) {
for (i = 0; i < output_info->nmode; ++i) {
modedata = (SDL_DisplayModeData *)SDL_calloc(1, sizeof(SDL_DisplayModeData));
if (modedata == NULL) {
if (!modedata) {
continue;
}
mode.driverdata = modedata;
@ -903,6 +908,12 @@ int X11_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display, SD
viddata->last_mode_change_deadline = SDL_GetTicks() + (PENDING_FOCUS_TIME * 2);
if (mode != &sdl_display->desktop_mode) {
data->mode_switch_deadline_ns = SDL_GetTicksNS() + MODE_SWITCH_TIMEOUT_NS;
} else {
data->mode_switch_deadline_ns = 0;
}
#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
if (data->use_xrandr) {
Display *display = viddata->display;
@ -914,18 +925,18 @@ int X11_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display, SD
Status status;
res = X11_XRRGetScreenResources(display, RootWindow(display, data->screen));
if (res == NULL) {
if (!res) {
return SDL_SetError("Couldn't get XRandR screen resources");
}
output_info = X11_XRRGetOutputInfo(display, res, data->xrandr_output);
if (output_info == NULL || output_info->connection == RR_Disconnected) {
if (!output_info || output_info->connection == RR_Disconnected) {
X11_XRRFreeScreenResources(res);
return SDL_SetError("Couldn't get XRandR output info");
}
crtc = X11_XRRGetCrtcInfo(display, res, output_info->crtc);
if (crtc == NULL) {
if (!crtc) {
X11_XRRFreeOutputInfo(output_info);
X11_XRRFreeScreenResources(res);
return SDL_SetError("Couldn't get XRandR crtc info");

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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
@ -32,6 +32,8 @@ struct SDL_DisplayData
int x;
int y;
Uint64 mode_switch_deadline_ns;
SDL_bool use_xrandr;
#ifdef SDL_VIDEO_DRIVER_X11_XRANDR

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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
@ -31,6 +31,8 @@
/* FIXME: Find a better place to put this... */
static Cursor x11_empty_cursor = None;
static SDL_Cursor *sys_cursors[SDL_HITTEST_RESIZE_LEFT + 1];
static Display *GetDisplay(void)
{
return SDL_GetVideoDevice()->driverdata->display;
@ -67,14 +69,10 @@ static void X11_DestroyEmptyCursor(void)
static SDL_Cursor *X11_CreateDefaultCursor(void)
{
SDL_Cursor *cursor;
cursor = SDL_calloc(1, sizeof(*cursor));
SDL_Cursor *cursor = SDL_calloc(1, sizeof(*cursor));
if (cursor) {
/* None is used to indicate the default cursor */
cursor->driverdata = (void *)(uintptr_t)None;
} else {
SDL_OutOfMemory();
}
return cursor;
@ -88,7 +86,7 @@ static Cursor X11_CreateXCursorCursor(SDL_Surface *surface, int hot_x, int hot_y
XcursorImage *image;
image = X11_XcursorImageCreate(surface->w, surface->h);
if (image == NULL) {
if (!image) {
SDL_OutOfMemory();
return None;
}
@ -121,15 +119,13 @@ static Cursor X11_CreatePixmapCursor(SDL_Surface *surface, int hot_x, int hot_y)
size_t width_bytes = ((surface->w + 7) & ~((size_t)7)) / 8;
data_bits = SDL_calloc(1, surface->h * width_bytes);
if (data_bits == NULL) {
SDL_OutOfMemory();
if (!data_bits) {
return None;
}
mask_bits = SDL_calloc(1, surface->h * width_bytes);
if (mask_bits == NULL) {
if (!mask_bits) {
SDL_free(data_bits);
SDL_OutOfMemory();
return None;
}
@ -198,9 +194,7 @@ static Cursor X11_CreatePixmapCursor(SDL_Surface *surface, int hot_x, int hot_y)
static SDL_Cursor *X11_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
{
SDL_Cursor *cursor;
cursor = SDL_calloc(1, sizeof(*cursor));
SDL_Cursor *cursor = SDL_calloc(1, sizeof(*cursor));
if (cursor) {
Cursor x11_cursor = None;
@ -213,8 +207,6 @@ static SDL_Cursor *X11_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
x11_cursor = X11_CreatePixmapCursor(surface, hot_x, hot_y);
}
cursor->driverdata = (void *)(uintptr_t)x11_cursor;
} else {
SDL_OutOfMemory();
}
return cursor;
@ -267,6 +259,30 @@ static SDL_Cursor *X11_CreateSystemCursor(SDL_SystemCursor id)
case SDL_SYSTEM_CURSOR_HAND:
shape = XC_hand2;
break;
case SDL_SYSTEM_CURSOR_WINDOW_TOPLEFT:
shape = XC_top_left_corner;
break;
case SDL_SYSTEM_CURSOR_WINDOW_TOP:
shape = XC_top_side;
break;
case SDL_SYSTEM_CURSOR_WINDOW_TOPRIGHT:
shape = XC_top_right_corner;
break;
case SDL_SYSTEM_CURSOR_WINDOW_RIGHT:
shape = XC_right_side;
break;
case SDL_SYSTEM_CURSOR_WINDOW_BOTTOMRIGHT:
shape = XC_bottom_right_corner;
break;
case SDL_SYSTEM_CURSOR_WINDOW_BOTTOM:
shape = XC_bottom_side;
break;
case SDL_SYSTEM_CURSOR_WINDOW_BOTTOMLEFT:
shape = XC_bottom_left_corner;
break;
case SDL_SYSTEM_CURSOR_WINDOW_LEFT:
shape = XC_left_side;
break;
}
cursor = SDL_calloc(1, sizeof(*cursor));
@ -276,8 +292,6 @@ static SDL_Cursor *X11_CreateSystemCursor(SDL_SystemCursor id)
x11_cursor = X11_XCreateFontCursor(GetDisplay(), shape);
cursor->driverdata = (void *)(uintptr_t)x11_cursor;
} else {
SDL_OutOfMemory();
}
return cursor;
@ -330,13 +344,16 @@ static void X11_WarpMouseInternal(Window xwindow, float x, float y)
Display *display = videodata->display;
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
int deviceid = 0;
/* It seems XIWarpPointer() doesn't work correctly on multi-head setups:
* https://developer.blender.org/rB165caafb99c6846e53d11c4e966990aaffc06cea
*/
if (ScreenCount(display) == 1) {
X11_XIGetClientPointer(display, None, &deviceid);
if (X11_Xinput2IsInitialized()) {
/* It seems XIWarpPointer() doesn't work correctly on multi-head setups:
* https://developer.blender.org/rB165caafb99c6846e53d11c4e966990aaffc06cea
*/
if (ScreenCount(display) == 1) {
X11_XIGetClientPointer(display, None, &deviceid);
}
}
if (deviceid != 0) {
SDL_assert(SDL_X11_HAVE_XINPUT2);
X11_XIWarpPointer(display, deviceid, None, xwindow, 0.0, 0.0, 0, 0, x, y);
} else
#endif
@ -370,12 +387,7 @@ static int X11_WarpMouseGlobal(float x, float y)
static int X11_SetRelativeMouseMode(SDL_bool enabled)
{
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
if (X11_Xinput2IsInitialized()) {
return 0;
}
#endif
return SDL_Unsupported();
return X11_Xinput2IsInitialized() ? 0 : SDL_Unsupported();
}
static int X11_CaptureMouse(SDL_Window *window)
@ -413,12 +425,9 @@ static Uint32 X11_GetGlobalMouseState(float *x, float *y)
/* !!! FIXME: should we XSync() here first? */
#ifndef SDL_VIDEO_DRIVER_X11_XINPUT2
videodata->global_mouse_changed = SDL_TRUE;
#else
if (!SDL_X11_HAVE_XINPUT2)
if (!X11_Xinput2IsInitialized()) {
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? */
@ -427,7 +436,7 @@ static Uint32 X11_GetGlobalMouseState(float *x, float *y)
if (displays) {
for (i = 0; displays[i]; ++i) {
SDL_DisplayData *data = SDL_GetDisplayDriverData(displays[i]);
if (data != NULL) {
if (data) {
Window root, child;
int rootx, rooty, winx, winy;
unsigned int mask;
@ -477,6 +486,23 @@ void X11_InitMouse(SDL_VideoDevice *_this)
mouse->CaptureMouse = X11_CaptureMouse;
mouse->GetGlobalMouseState = X11_GetGlobalMouseState;
SDL_HitTestResult r = SDL_HITTEST_NORMAL;
while (r <= SDL_HITTEST_RESIZE_LEFT) {
switch (r) {
case SDL_HITTEST_NORMAL: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); break;
case SDL_HITTEST_DRAGGABLE: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); break;
case SDL_HITTEST_RESIZE_TOPLEFT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_WINDOW_TOPLEFT); break;
case SDL_HITTEST_RESIZE_TOP: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_WINDOW_TOP); break;
case SDL_HITTEST_RESIZE_TOPRIGHT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_WINDOW_TOPRIGHT); break;
case SDL_HITTEST_RESIZE_RIGHT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_WINDOW_RIGHT); break;
case SDL_HITTEST_RESIZE_BOTTOMRIGHT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_WINDOW_BOTTOMRIGHT); break;
case SDL_HITTEST_RESIZE_BOTTOM: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_WINDOW_BOTTOM); break;
case SDL_HITTEST_RESIZE_BOTTOMLEFT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_WINDOW_BOTTOMLEFT); break;
case SDL_HITTEST_RESIZE_LEFT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_WINDOW_LEFT); break;
}
r++;
}
SDL_SetDefaultCursor(X11_CreateDefaultCursor());
}
@ -485,8 +511,14 @@ void X11_QuitMouse(SDL_VideoDevice *_this)
SDL_VideoData *data = _this->driverdata;
SDL_XInput2DeviceInfo *i;
SDL_XInput2DeviceInfo *next;
int j;
for (i = data->mouse_device_info; i != NULL; i = next) {
for (j = 0; j < SDL_arraysize(sys_cursors); j++) {
X11_FreeCursor(sys_cursors[j]);
sys_cursors[j] = NULL;
}
for (i = data->mouse_device_info; i; i = next) {
next = i->next;
SDL_free(i);
}
@ -495,4 +527,13 @@ void X11_QuitMouse(SDL_VideoDevice *_this)
X11_DestroyEmptyCursor();
}
void X11_SetHitTestCursor(SDL_HitTestResult rc)
{
if (rc == SDL_HITTEST_NORMAL || rc == SDL_HITTEST_DRAGGABLE) {
SDL_SetCursor(NULL);
} else {
X11_ShowCursor(sys_cursors[rc]);
}
}
#endif /* SDL_VIDEO_DRIVER_X11 */

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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
@ -35,5 +35,6 @@ typedef struct SDL_XInput2DeviceInfo
extern void X11_InitMouse(SDL_VideoDevice *_this);
extern void X11_QuitMouse(SDL_VideoDevice *_this);
extern void X11_SetHitTestCursor(SDL_HitTestResult rc);
#endif /* SDL_x11mouse_h_ */

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 2021 NVIDIA Corporation
This software is provided 'as-is', without any express or implied
@ -193,7 +193,7 @@ int X11_GL_LoadLibrary(SDL_VideoDevice *_this, const char *path)
sizeof(struct
SDL_GLDriverData));
if (!_this->gl_data) {
return SDL_OutOfMemory();
return -1;
}
/* Load function pointers */
@ -237,6 +237,8 @@ int X11_GL_LoadLibrary(SDL_VideoDevice *_this, const char *path)
return SDL_SetError("GLX is not supported");
}
_this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_UNTESTED;
/* Initialize extensions */
/* See lengthy comment about the inc/dec in
../windows/SDL_windowsopengl.c. */
@ -902,7 +904,6 @@ int X11_GL_SetSwapInterval(SDL_VideoDevice *_this, int interval)
X11_GL_GetSwapInterval(_this, &currentInterval);
_this->gl_data->glXSwapIntervalEXT(display, drawable, currentInterval);
_this->gl_data->glXSwapIntervalEXT(display, drawable, interval);
status = 0;
swapinterval = interval;
} else if (_this->gl_data->glXSwapIntervalMESA) {
@ -925,6 +926,53 @@ int X11_GL_SetSwapInterval(SDL_VideoDevice *_this, int interval)
return status;
}
static SDL_GLSwapIntervalTearBehavior CheckSwapIntervalTearBehavior(SDL_VideoDevice *_this, Window drawable, unsigned int current_val, unsigned int current_allow_late)
{
/* Mesa and Nvidia interpret GLX_EXT_swap_control_tear differently, as of this writing, so
figure out which behavior we have.
Technical details: https://github.com/libsdl-org/SDL/issues/8004#issuecomment-1819603282 */
if (_this->gl_data->swap_interval_tear_behavior == SDL_SWAPINTERVALTEAR_UNTESTED) {
if (!_this->gl_data->HAS_GLX_EXT_swap_control_tear) {
_this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_UNKNOWN;
} else {
Display *display = _this->driverdata->display;
unsigned int allow_late_swap_tearing = 22;
int original_val = (int) current_val;
/*
* This is a workaround for a bug in NVIDIA drivers. Bug has been reported
* and will be fixed in a future release (probably 319.xx).
*
* There's a bug where glXSetSwapIntervalEXT ignores updates because
* it has the wrong value cached. To work around it, we just run a no-op
* update to the current value.
*/
_this->gl_data->glXSwapIntervalEXT(display, drawable, current_val);
/* set it to no swap interval and see how it affects GLX_LATE_SWAPS_TEAR_EXT... */
_this->gl_data->glXSwapIntervalEXT(display, drawable, 0);
_this->gl_data->glXQueryDrawable(display, drawable, GLX_LATE_SWAPS_TEAR_EXT, &allow_late_swap_tearing);
if (allow_late_swap_tearing == 0) { /* GLX_LATE_SWAPS_TEAR_EXT says whether late swapping is currently in use */
_this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_NVIDIA;
if (current_allow_late) {
original_val = -original_val;
}
} else if (allow_late_swap_tearing == 1) { /* GLX_LATE_SWAPS_TEAR_EXT says whether the Drawable can use late swapping at all */
_this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_MESA;
} else { /* unexpected outcome! */
_this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_UNKNOWN;
}
/* set us back to what it was originally... */
_this->gl_data->glXSwapIntervalEXT(display, drawable, original_val);
}
}
return _this->gl_data->swap_interval_tear_behavior;
}
int X11_GL_GetSwapInterval(SDL_VideoDevice *_this, int *interval)
{
if (_this->gl_data->glXSwapIntervalEXT) {
@ -935,6 +983,7 @@ int X11_GL_GetSwapInterval(SDL_VideoDevice *_this, int *interval)
unsigned int val = 0;
if (_this->gl_data->HAS_GLX_EXT_swap_control_tear) {
allow_late_swap_tearing = 22; /* set this to nonsense. */
_this->gl_data->glXQueryDrawable(display, drawable,
GLX_LATE_SWAPS_TEAR_EXT,
&allow_late_swap_tearing);
@ -943,12 +992,21 @@ int X11_GL_GetSwapInterval(SDL_VideoDevice *_this, int *interval)
_this->gl_data->glXQueryDrawable(display, drawable,
GLX_SWAP_INTERVAL_EXT, &val);
if ((allow_late_swap_tearing) && (val > 0)) {
*interval = -((int)val);
return 0;
*interval = (int)val;
switch (CheckSwapIntervalTearBehavior(_this, drawable, val, allow_late_swap_tearing)) {
case SDL_SWAPINTERVALTEAR_MESA:
*interval = (int)val; /* unsigned int cast to signed that generates negative value if necessary. */
break;
case SDL_SWAPINTERVALTEAR_NVIDIA:
default:
if ((allow_late_swap_tearing) && (val > 0)) {
*interval = -((int)val);
}
break;
}
*interval = (int)val;
return 0;
} else if (_this->gl_data->glXGetSwapIntervalMESA) {
int val = _this->gl_data->glXGetSwapIntervalMESA();

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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
@ -29,6 +29,14 @@
typedef void (*__GLXextFuncPtr)(void);
typedef enum SDL_GLSwapIntervalTearBehavior
{
SDL_SWAPINTERVALTEAR_UNTESTED,
SDL_SWAPINTERVALTEAR_UNKNOWN,
SDL_SWAPINTERVALTEAR_MESA,
SDL_SWAPINTERVALTEAR_NVIDIA
} SDL_GLSwapIntervalTearBehavior;
struct SDL_GLDriverData
{
int errorBase, eventBase;
@ -50,6 +58,8 @@ struct SDL_GLDriverData
int minor;
} es_profile_max_supported_version;
SDL_GLSwapIntervalTearBehavior swap_interval_tear_behavior;
Bool (*glXQueryExtension)(Display *, int *, int *);
__GLXextFuncPtr (*glXGetProcAddress)(const GLubyte *);
XVisualInfo *(*glXChooseVisual)(Display *, int, int *);

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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

View File

@ -0,0 +1,696 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 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"
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
#include "../../events/SDL_pen_c.h"
#include "../SDL_sysvideo.h"
#include "SDL_x11pen.h"
#include "SDL_x11video.h"
#include "SDL_x11xinput2.h"
#define PEN_ERASER_ID_MAXLEN 256 /* Max # characters of device name to scan */
#define PEN_ERASER_NAME_TAG "eraser" /* String constant to identify erasers */
#define DEBUG_PEN (SDL_PEN_DEBUG_NOID | SDL_PEN_DEBUG_NONWACOM | SDL_PEN_DEBUG_UNKNOWN_WACOM | SDL_PEN_DEBUG_NOSERIAL_WACOM)
#define SDL_PEN_AXIS_VALUATOR_MISSING -1
/* X11-specific information attached to each pen */
typedef struct xinput2_pen
{
float axis_min[SDL_PEN_NUM_AXES];
float axis_max[SDL_PEN_NUM_AXES];
float slider_bias; /* shift value to add to PEN_AXIS_SLIDER (before normalisation) */
float rotation_bias; /* rotation to add to PEN_AXIS_ROTATION (after normalisation) */
Sint8 valuator_for_axis[SDL_PEN_NUM_AXES]; /* SDL_PEN_AXIS_VALUATOR_MISSING if not supported */
} xinput2_pen;
/* X11 atoms */
static struct
{
int initialized; /* initialised to 0 */
Atom device_product_id;
Atom abs_pressure;
Atom abs_tilt_x;
Atom abs_tilt_y;
Atom wacom_serial_ids;
Atom wacom_tool_type;
} pen_atoms;
/*
* Mapping from X11 device IDs to pen IDs
*
* In X11, the same device ID may represent any number of pens. We
* thus cannot directly use device IDs as pen IDs.
*/
static struct
{
int num_pens_known; /* Number of currently known pens (based on their GUID); used to give pen ID to new pens */
int num_entries; /* Number of X11 device IDs that correspond to pens */
struct pen_device_id_mapping
{
Uint32 deviceid;
Uint32 pen_id;
} * entries; /* Current pen to device ID mappings */
} pen_map;
typedef enum
{
SDL_PEN_VENDOR_UNKNOWN = 0,
SDL_PEN_VENDOR_WACOM
} sdl_pen_vendor;
/* Information to identify pens during discovery */
typedef struct
{
sdl_pen_vendor vendor;
SDL_GUID guid;
SDL_PenSubtype heuristic_type; /* Distinguish pen+eraser devices with shared bus ID */
Uint32 devicetype_id, serial; /* used by PEN_VENDOR_WACOM */
Uint32 deviceid;
} pen_identity;
int X11_PenIDFromDeviceID(int deviceid)
{
int i;
for (i = 0; i < pen_map.num_entries; ++i) {
if (pen_map.entries[i].deviceid == deviceid) {
return pen_map.entries[i].pen_id;
}
}
return SDL_PEN_INVALID;
}
static void pen_atoms_ensure_initialized(SDL_VideoDevice *_this)
{
SDL_VideoData *data = (SDL_VideoData *)_this->driverdata;
if (pen_atoms.initialized) {
return;
}
/* Create atoms if they don't exist yet to pre-empt hotplugging updates */
pen_atoms.device_product_id = X11_XInternAtom(data->display, "Device Product ID", False);
pen_atoms.wacom_serial_ids = X11_XInternAtom(data->display, "Wacom Serial IDs", False);
pen_atoms.wacom_tool_type = X11_XInternAtom(data->display, "Wacom Tool Type", False);
pen_atoms.abs_pressure = X11_XInternAtom(data->display, "Abs Pressure", False);
pen_atoms.abs_tilt_x = X11_XInternAtom(data->display, "Abs Tilt X", False);
pen_atoms.abs_tilt_y = X11_XInternAtom(data->display, "Abs Tilt Y", False);
pen_atoms.initialized = 1;
}
/* Read out an integer property and store into a preallocated Sint32 array, extending 8 and 16 bit values suitably.
Returns number of Sint32s written (<= max_words), or 0 on error. */
static size_t xinput2_pen_get_int_property(SDL_VideoDevice *_this, int deviceid, Atom property, Sint32 *dest, size_t max_words)
{
const SDL_VideoData *data = (SDL_VideoData *)_this->driverdata;
Atom type_return;
int format_return;
unsigned long num_items_return;
unsigned long bytes_after_return;
unsigned char *output;
if (property == None) {
return 0;
}
if (Success != X11_XIGetProperty(data->display, deviceid,
property,
0, max_words, False,
XA_INTEGER, &type_return, &format_return,
&num_items_return, &bytes_after_return,
&output) ||
num_items_return == 0 || output == NULL) {
return 0;
}
if (type_return == XA_INTEGER) {
int k;
const int to_copy = SDL_min(max_words, num_items_return);
if (format_return == 8) {
Sint8 *numdata = (Sint8 *)output;
for (k = 0; k < to_copy; ++k) {
dest[k] = numdata[k];
}
} else if (format_return == 16) {
Sint16 *numdata = (Sint16 *)output;
for (k = 0; k < to_copy; ++k) {
dest[k] = numdata[k];
}
} else {
SDL_memcpy(dest, output, sizeof(Sint32) * to_copy);
}
X11_XFree(output);
return to_copy;
}
return 0; /* type mismatch */
}
/* 32 bit vendor + device ID from evdev */
static Uint32 xinput2_pen_evdevid(SDL_VideoDevice *_this, int deviceid)
{
#if !(SDL_PEN_DEBUG_NOID)
Sint32 ids[2];
pen_atoms_ensure_initialized(_this);
if (2 != xinput2_pen_get_int_property(_this, deviceid, pen_atoms.device_product_id, ids, 2)) {
return 0;
}
return ((ids[0] << 16) | (ids[1] & 0xffff));
#else /* Testing: pretend that we have no ID (not sure if this can happen IRL) */
return 0;
#endif
}
/* Gets reasonably-unique GUID for the device */
static void xinput2_pen_update_generic_guid(SDL_VideoDevice *_this, pen_identity *pident, int deviceid)
{
Uint32 evdevid = xinput2_pen_evdevid(_this, deviceid); /* also initialises pen_atoms */
if (!evdevid) {
/* Fallback: if no evdevid is available; try to at least distinguish devices within the
current session. This is a poor GUID and our last resort. */
evdevid = deviceid;
}
SDL_PenUpdateGUIDForGeneric(&pident->guid, 0, evdevid);
}
/* Identify Wacom devices (if SDL_TRUE is returned) and extract their device type and serial IDs */
static SDL_bool xinput2_wacom_deviceid(SDL_VideoDevice *_this, int deviceid, Uint32 *wacom_devicetype_id, Uint32 *wacom_serial)
{
#if !(SDL_PEN_DEBUG_NONWACOM) /* Can be disabled for testing */
Sint32 serial_id_buf[3];
int result;
pen_atoms_ensure_initialized(_this);
if ((result = xinput2_pen_get_int_property(_this, deviceid, pen_atoms.wacom_serial_ids, serial_id_buf, 3)) == 3) {
*wacom_devicetype_id = serial_id_buf[2];
*wacom_serial = serial_id_buf[1];
#if SDL_PEN_DEBUG_NOSERIAL_WACOM /* Disabled for testing? */
*wacom_serial = 0;
#endif
return SDL_TRUE;
}
#endif
return SDL_FALSE;
}
/* Heuristically determines if device is an eraser */
static SDL_bool xinput2_pen_is_eraser(SDL_VideoDevice *_this, int deviceid, char *devicename)
{
SDL_VideoData *data = (SDL_VideoData *)_this->driverdata;
char dev_name[PEN_ERASER_ID_MAXLEN];
int k;
pen_atoms_ensure_initialized(_this);
if (pen_atoms.wacom_tool_type != None) {
Atom type_return;
int format_return;
unsigned long num_items_return;
unsigned long bytes_after_return;
unsigned char *tooltype_name_info = NULL;
/* Try Wacom-specific method */
if (Success == X11_XIGetProperty(data->display, deviceid,
pen_atoms.wacom_tool_type,
0, 32, False,
AnyPropertyType, &type_return, &format_return,
&num_items_return, &bytes_after_return,
&tooltype_name_info) &&
tooltype_name_info != NULL && num_items_return > 0) {
SDL_bool result = SDL_FALSE;
char *tooltype_name = NULL;
if (type_return == XA_ATOM) {
/* Atom instead of string? Un-intern */
Atom atom = *((Atom *)tooltype_name_info);
if (atom != None) {
tooltype_name = X11_XGetAtomName(data->display, atom);
}
} else if (type_return == XA_STRING && format_return == 8) {
tooltype_name = (char *)tooltype_name_info;
}
if (tooltype_name) {
if (0 == SDL_strcasecmp(tooltype_name, PEN_ERASER_NAME_TAG)) {
result = SDL_TRUE;
}
X11_XFree(tooltype_name_info);
return result;
}
}
}
/* Non-Wacom device? */
/* We assume that a device is an eraser if its name contains the string "eraser".
* Unfortunately there doesn't seem to be a clean way to distinguish these cases (as of 2022-03). */
SDL_strlcpy(dev_name, devicename, PEN_ERASER_ID_MAXLEN);
/* lowercase device name string so we can use strstr() */
for (k = 0; dev_name[k]; ++k) {
dev_name[k] = tolower(dev_name[k]);
}
return (SDL_strstr(dev_name, PEN_ERASER_NAME_TAG)) ? SDL_TRUE : SDL_FALSE;
}
/* Gets GUID and other identifying information for the device using the best known method */
static pen_identity xinput2_identify_pen(SDL_VideoDevice *_this, int deviceid, char *name)
{
pen_identity pident;
pident.devicetype_id = 0ul;
pident.serial = 0ul;
pident.deviceid = deviceid;
pident.heuristic_type = SDL_PEN_TYPE_PEN;
SDL_memset(pident.guid.data, 0, sizeof(pident.guid.data));
if (xinput2_pen_is_eraser(_this, deviceid, name)) {
pident.heuristic_type = SDL_PEN_TYPE_ERASER;
}
if (xinput2_wacom_deviceid(_this, deviceid, &pident.devicetype_id, &pident.serial)) {
pident.vendor = SDL_PEN_VENDOR_WACOM;
SDL_PenUpdateGUIDForWacom(&pident.guid, pident.devicetype_id, pident.serial);
#if DEBUG_PEN
printf("[pen] Pen %d reports Wacom device_id %x\n",
deviceid, pident.devicetype_id);
#endif
} else {
pident.vendor = SDL_PEN_VENDOR_UNKNOWN;
}
if (!pident.serial) {
/* If the pen has a serial number, we can move it across tablets and retain its identity.
Otherwise, we use the evdev ID as part of its GUID, which may mean that we identify it with the tablet. */
xinput2_pen_update_generic_guid(_this, &pident, deviceid);
}
SDL_PenUpdateGUIDForType(&pident.guid, pident.heuristic_type);
return pident;
}
static void xinput2_pen_free_deviceinfo(Uint32 deviceid, void *x11_peninfo, void *context)
{
SDL_free(x11_peninfo);
}
static void xinput2_merge_deviceinfo(xinput2_pen *dest, xinput2_pen *src)
{
*dest = *src;
}
/**
* Fill in vendor-specific device information, if available
*
* For Wacom pens: identify number of buttons and extra axis (if present)
*
* \param _this global state
* \param dev The device to analyse
* \param pen The pen to initialise
* \param pident Pen identity information
* \param[out] valuator_5 Meaning of the valuator with offset 5, if any
* (written only if known and if the device has a 6th axis,
* e.g., for the Wacom Art Pen and Wacom Airbrush Pen)
* \param[out] axes Bitmask of all possibly supported axes
*
* This function identifies Wacom device types through a Wacom-specific device ID.
* It then fills in pen details from an internal database.
* If the device seems to be a Wacom pen/eraser but can't be identified, the function
* leaves "axes" untouched and sets the other outputs to common defaults.
*
* There is no explicit support for other vendors, though vendors that
* emulate the Wacom API might be supported.
*
* Unsupported devices will keep the default settings.
*/
static void xinput2_vendor_peninfo(SDL_VideoDevice *_this, const XIDeviceInfo *dev, SDL_Pen *pen, pen_identity pident, int *valuator_5, Uint32 *axes)
{
switch (pident.vendor) {
case SDL_PEN_VENDOR_WACOM:
{
if (SDL_PenModifyForWacomID(pen, pident.devicetype_id, axes)) {
if (*axes & SDL_PEN_AXIS_SLIDER_MASK) {
/* Air Brush Pen or eraser */
*valuator_5 = SDL_PEN_AXIS_SLIDER;
} else if (*axes & SDL_PEN_AXIS_ROTATION_MASK) {
/* Art Pen or eraser, or 6D Art Pen */
*valuator_5 = SDL_PEN_AXIS_ROTATION;
}
return;
} else {
#if DEBUG_PEN
printf("[pen] Could not identify wacom pen %d with device id %x, using default settings\n",
pident.deviceid, pident.devicetype_id);
#endif
break;
}
}
default:
#if DEBUG_PEN
printf("[pen] Pen %d is not from a known vendor\n", pident.deviceid);
#endif
break;
}
/* Fall back to default heuristics for identifying device type */
SDL_strlcpy(pen->name, dev->name, SDL_PEN_MAX_NAME);
pen->type = pident.heuristic_type;
}
/* Does this device have a valuator for pressure sensitivity? */
static SDL_bool xinput2_device_is_pen(SDL_VideoDevice *_this, const XIDeviceInfo *dev)
{
int classct;
pen_atoms_ensure_initialized(_this);
for (classct = 0; classct < dev->num_classes; ++classct) {
const XIAnyClassInfo *classinfo = dev->classes[classct];
switch (classinfo->type) {
case XIValuatorClass:
{
XIValuatorClassInfo *val_classinfo = (XIValuatorClassInfo *)classinfo;
Atom vname = val_classinfo->label;
if (vname == pen_atoms.abs_pressure) {
return SDL_TRUE;
}
}
}
}
return SDL_FALSE;
}
void X11_InitPen(SDL_VideoDevice *_this)
{
SDL_VideoData *data = (SDL_VideoData *)_this->driverdata;
int i;
XIDeviceInfo *device_info;
int num_device_info;
device_info = X11_XIQueryDevice(data->display, XIAllDevices, &num_device_info);
if (!device_info) {
return;
}
/* Reset the device id -> pen map */
if (pen_map.entries) {
SDL_free(pen_map.entries);
pen_map.entries = NULL;
pen_map.num_entries = 0;
}
SDL_PenGCMark();
for (i = 0; i < num_device_info; ++i) {
const XIDeviceInfo *dev = &device_info[i];
int classct;
xinput2_pen pen_device;
Uint32 capabilities = 0;
Uint32 axis_mask = ~0; /* Permitted axes (default: all) */
int valuator_5_axis = -1; /* For Wacom devices, the 6th valuator (offset 5) has a model-specific meaning */
pen_identity pident;
SDL_PenID pen_id;
SDL_Pen *pen;
int old_num_pens_known = pen_map.num_pens_known;
int k;
/* Only track physical devices that are enabled and look like pens */
if (dev->use != XISlavePointer || dev->enabled == 0 || !xinput2_device_is_pen(_this, dev)) {
continue;
}
pen_device.slider_bias = 0.0f;
pen_device.rotation_bias = 0.0f;
for (k = 0; k < SDL_PEN_NUM_AXES; ++k) {
pen_device.valuator_for_axis[k] = SDL_PEN_AXIS_VALUATOR_MISSING;
}
pident = xinput2_identify_pen(_this, dev->deviceid, dev->name);
pen_id = SDL_GetPenFromGUID(pident.guid);
if (pen_id == SDL_PEN_INVALID) {
/* We have never met this pen */
pen_id = ++pen_map.num_pens_known; /* start at 1 */
}
pen = SDL_PenModifyBegin(pen_id);
/* Complement XF86 driver information with vendor-specific details */
xinput2_vendor_peninfo(_this, dev, pen, pident, &valuator_5_axis, &axis_mask);
for (classct = 0; classct < dev->num_classes; ++classct) {
const XIAnyClassInfo *classinfo = dev->classes[classct];
switch (classinfo->type) {
case XIValuatorClass:
{
XIValuatorClassInfo *val_classinfo = (XIValuatorClassInfo *)classinfo;
Sint8 valuator_nr = val_classinfo->number;
Atom vname = val_classinfo->label;
int axis = -1;
float min = val_classinfo->min;
float max = val_classinfo->max;
if (vname == pen_atoms.abs_pressure) {
axis = SDL_PEN_AXIS_PRESSURE;
} else if (vname == pen_atoms.abs_tilt_x) {
axis = SDL_PEN_AXIS_XTILT;
} else if (vname == pen_atoms.abs_tilt_y) {
axis = SDL_PEN_AXIS_YTILT;
}
if (axis == -1 && valuator_nr == 5) {
/* Wacom model-specific axis support */
/* The meaning of the various axes is highly underspecitied in Xinput2.
* As of 2023-08-26, Wacom seems to be the only vendor to support these axes, so the code below
* captures the de-facto standard. */
axis = valuator_5_axis;
switch (axis) {
case SDL_PEN_AXIS_SLIDER:
/* cf. xinput2_wacom_peninfo for how this axis is used.
In all current cases, our API wants this value in 0..1, but the xf86 driver
starts at a negative offset, so we normalise here. */
pen_device.slider_bias = -min;
max -= min;
min = 0.0f;
break;
case SDL_PEN_AXIS_ROTATION:
/* The "0" value points to the left, rather than up, so we must
rotate 90 degrees counter-clockwise to have 0 point to the top. */
pen_device.rotation_bias = -90.0f;
break;
default:
break;
}
}
if (axis >= 0) {
capabilities |= SDL_PEN_AXIS_CAPABILITY(axis);
pen_device.valuator_for_axis[axis] = valuator_nr;
pen_device.axis_min[axis] = min;
pen_device.axis_max[axis] = max;
}
break;
}
default:
break;
}
}
/* We have a pen if and only if the device measures pressure */
if (capabilities & SDL_PEN_AXIS_PRESSURE_MASK) {
xinput2_pen *xinput2_deviceinfo;
Uint64 guid_a, guid_b;
/* Done collecting data, write to pen */
SDL_PenModifyAddCapabilities(pen, capabilities);
pen->guid = pident.guid;
if (pen->deviceinfo) {
/* Updating a known pen */
xinput2_deviceinfo = (xinput2_pen *)pen->deviceinfo;
xinput2_merge_deviceinfo(xinput2_deviceinfo, &pen_device);
} else {
/* Registering a new pen */
xinput2_deviceinfo = SDL_malloc(sizeof(xinput2_pen));
SDL_memcpy(xinput2_deviceinfo, &pen_device, sizeof(xinput2_pen));
}
pen->deviceinfo = xinput2_deviceinfo;
#if DEBUG_PEN
printf("[pen] pen %d [%04x] valuators pressure=%d, xtilt=%d, ytilt=%d [%s]\n",
pen->header.id, pen->header.flags,
pen_device.valuator_for_axis[SDL_PEN_AXIS_PRESSURE],
pen_device.valuator_for_axis[SDL_PEN_AXIS_XTILT],
pen_device.valuator_for_axis[SDL_PEN_AXIS_YTILT],
pen->name);
#endif
SDL_memcpy(&guid_a, &pen->guid.data[0], 8);
SDL_memcpy(&guid_b, &pen->guid.data[8], 8);
if (!(guid_a | guid_b)) {
#if DEBUG_PEN
printf("[pen] (pen eliminated due to zero GUID)\n");
#endif
pen->type = SDL_PEN_TYPE_NONE;
}
} else {
/* Not a pen, mark for deletion */
pen->type = SDL_PEN_TYPE_NONE;
}
SDL_PenModifyEnd(pen, SDL_TRUE);
if (pen->type != SDL_PEN_TYPE_NONE) {
const int map_pos = pen_map.num_entries;
/* We found a pen: add mapping */
if (pen_map.entries == NULL) {
pen_map.entries = SDL_calloc(sizeof(struct pen_device_id_mapping), 1);
pen_map.num_entries = 1;
} else {
pen_map.num_entries += 1;
pen_map.entries = SDL_realloc(pen_map.entries,
pen_map.num_entries * (sizeof(struct pen_device_id_mapping)));
}
pen_map.entries[map_pos].deviceid = dev->deviceid;
pen_map.entries[map_pos].pen_id = pen_id;
} else {
/* Revert pen number allocation */
pen_map.num_pens_known = old_num_pens_known;
}
}
X11_XIFreeDeviceInfo(device_info);
SDL_PenGCSweep(NULL, xinput2_pen_free_deviceinfo);
}
static void xinput2_normalize_pen_axes(const SDL_Pen *peninfo,
const xinput2_pen *xpen,
/* inout-mode paramters: */
float *coords)
{
int axis;
/* Normalise axes */
for (axis = 0; axis < SDL_PEN_NUM_AXES; ++axis) {
int valuator = xpen->valuator_for_axis[axis];
if (valuator != SDL_PEN_AXIS_VALUATOR_MISSING) {
float value = coords[axis];
float min = xpen->axis_min[axis];
float max = xpen->axis_max[axis];
if (axis == SDL_PEN_AXIS_SLIDER) {
value += xpen->slider_bias;
}
/* min ... 0 ... max */
if (min < 0.0) {
/* Normalise so that 0 remains 0.0 */
if (value < 0) {
value = value / (-min);
} else {
if (max == 0.0) {
value = 0.0f;
} else {
value = value / max;
}
}
} else {
/* 0 ... min ... max */
/* including 0.0 = min */
if (max == 0.0) {
value = 0.0f;
} else {
value = (value - min) / max;
}
}
switch (axis) {
case SDL_PEN_AXIS_XTILT:
case SDL_PEN_AXIS_YTILT:
if (peninfo->info.max_tilt > 0.0f) {
value *= peninfo->info.max_tilt; /* normalise to physical max */
}
break;
case SDL_PEN_AXIS_ROTATION:
/* normalised to -1..1, so let's convert to degrees */
value *= 180.0;
value += xpen->rotation_bias;
/* handle simple over/underflow */
if (value >= 180.0f) {
value -= 360.0f;
} else if (value < -180.0f) {
value += 360.0f;
}
break;
default:
break;
}
coords[axis] = value;
}
}
}
void X11_PenAxesFromValuators(const SDL_Pen *peninfo,
const double *input_values, const unsigned char *mask, const int mask_len,
/* out-mode parameters: */
float axis_values[SDL_PEN_NUM_AXES])
{
const xinput2_pen *pen = (xinput2_pen *)peninfo->deviceinfo;
int i;
for (i = 0; i < SDL_PEN_NUM_AXES; ++i) {
const int valuator = pen->valuator_for_axis[i];
if (valuator == SDL_PEN_AXIS_VALUATOR_MISSING || valuator >= mask_len * 8 || !(XIMaskIsSet(mask, valuator))) {
axis_values[i] = 0.0f;
} else {
axis_values[i] = input_values[valuator];
}
}
xinput2_normalize_pen_axes(peninfo, pen, axis_values);
}
#endif /* SDL_VIDEO_DRIVER_X11_XINPUT2 */
/* vi: set ts=4 sw=4 expandtab: */

View File

@ -0,0 +1,54 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 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"
#ifndef SDL_x11pen_h_
#define SDL_x11pen_h_
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
#include "SDL_x11video.h"
#include "../../events/SDL_pen_c.h"
/* Pressure-sensitive pen */
/* Forward definition for SDL_x11video.h */
struct SDL_VideoData;
/* Function definitions */
/* Detect XINPUT2 devices that are pens / erasers, or update the list after hotplugging */
extern void X11_InitPen(SDL_VideoDevice *_this);
/* Converts XINPUT2 valuators into pen axis information, including normalisation */
extern void X11_PenAxesFromValuators(const SDL_Pen *pen,
const double *input_values, const unsigned char *mask, const int mask_len,
/* out-mode parameters: */
float axis_values[SDL_PEN_NUM_AXES]);
/* Map X11 device ID to pen ID */
extern int X11_PenIDFromDeviceID(int deviceid);
#endif /* SDL_VIDEO_DRIVER_X11_XINPUT2 */
#endif /* SDL_x11pen_h_ */
/* vi: set ts=4 sw=4 expandtab: */

View File

@ -1,98 +0,0 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_VIDEO_DRIVER_X11
#include "SDL_x11video.h"
#include "SDL_x11shape.h"
#include "SDL_x11window.h"
#include "../SDL_shape_internals.h"
SDL_WindowShaper *X11_CreateShaper(SDL_Window *window)
{
SDL_WindowShaper *result = NULL;
#ifdef SDL_VIDEO_DRIVER_X11_XSHAPE
SDL_ShapeData *data = NULL;
if (SDL_X11_HAVE_XSHAPE) { /* Make sure X server supports it. */
result = SDL_malloc(sizeof(SDL_WindowShaper));
if (result == NULL) {
SDL_OutOfMemory();
return NULL;
}
result->window = window;
result->mode.mode = ShapeModeDefault;
result->mode.parameters.binarizationCutoff = 1;
data = SDL_malloc(sizeof(SDL_ShapeData));
if (data == NULL) {
SDL_free(result);
SDL_OutOfMemory();
return NULL;
}
result->driverdata = data;
data->bitmapsize = 0;
data->bitmap = NULL;
window->shaper = result;
}
#endif
return result;
}
int X11_SetWindowShape(SDL_WindowShaper *shaper, SDL_Surface *shape, SDL_WindowShapeMode *shape_mode)
{
#ifdef SDL_VIDEO_DRIVER_X11_XSHAPE
SDL_ShapeData *data = NULL;
SDL_WindowData *windowdata = NULL;
Pixmap shapemask;
#endif
if (shaper == NULL || shape == NULL || shaper->driverdata == NULL) {
return -1;
}
#ifdef SDL_VIDEO_DRIVER_X11_XSHAPE
if (shape->format->Amask == 0 && SDL_SHAPEMODEALPHA(shape_mode->mode)) {
return -2;
}
if (shape->w != shaper->window->w || shape->h != shaper->window->h) {
return -3;
}
data = shaper->driverdata;
/* Assume that shaper->alphacutoff already has a value, because SDL_SetWindowShape() should have given it one. */
SDL_CalculateShapeBitmap(shaper->mode, shape, data->bitmap, 8);
windowdata = shaper->window->driverdata;
shapemask = X11_XCreateBitmapFromData(windowdata->videodata->display, windowdata->xwindow, data->bitmap, shaper->window->w, shaper->window->h);
X11_XShapeCombineMask(windowdata->videodata->display, windowdata->xwindow, ShapeBounding, 0, 0, shapemask, ShapeSet);
X11_XSync(windowdata->videodata->display, False);
X11_XFreePixmap(windowdata->videodata->display, shapemask);
#endif
return 0;
}
#endif /* SDL_VIDEO_DRIVER_X11 */

View File

@ -1,37 +0,0 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifndef SDL_x11shape_h_
#define SDL_x11shape_h_
#include "../SDL_sysvideo.h"
typedef struct
{
void *bitmap;
Uint32 bitmapsize;
} SDL_ShapeData;
extern SDL_WindowShaper *X11_CreateShaper(SDL_Window *window);
extern int X11_SetWindowShape(SDL_WindowShaper *shaper, SDL_Surface *shape, SDL_WindowShapeMode *shape_mode);
#endif /* SDL_x11shape_h_ */

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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
@ -193,11 +193,19 @@ SDL_X11_SYM(void,XkbFreeKeyboard,(XkbDescPtr a,unsigned int b, Bool c),(a,b,c),)
SDL_X11_SYM(Bool,XkbSetDetectableAutoRepeat,(Display* a, Bool b, Bool* c),(a,b,c),return)
#endif
/* XKeycodeToKeysym is a deprecated function */
#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#if NeedWidePrototypes
SDL_X11_SYM(KeySym,XKeycodeToKeysym,(Display* a,unsigned int b,int c),(a,b,c),return)
#else
SDL_X11_SYM(KeySym,XKeycodeToKeysym,(Display* a,KeyCode b,int c),(a,b,c),return)
#endif
#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
#pragma GCC diagnostic pop
#endif
#ifdef X_HAVE_UTF8_STRING
SDL_X11_MODULE(UTF8)
@ -281,6 +289,7 @@ SDL_X11_SYM(Status,XIQueryVersion,(Display *a,int *b,int *c),(a,b,c),return)
SDL_X11_SYM(XIEventMask*,XIGetSelectedEvents,(Display *a,Window b,int *c),(a,b,c),return)
SDL_X11_SYM(Bool,XIGetClientPointer,(Display *a,Window b,int *c),(a,b,c),return)
SDL_X11_SYM(Bool,XIWarpPointer,(Display *a,int b,Window c,Window d,double e,double f,int g,int h,double i,double j),(a,b,c,d,e,f,g,h,i,j),return)
SDL_X11_SYM(Status,XIGetProperty,(Display *a,int b,Atom c,long d,long e,Bool f, Atom g, Atom *h, int *i, unsigned long *j, unsigned long *k, unsigned char **l),(a,b,c,d,e,f,g,h,i,j,k,l),return);
#endif
/* XRandR support */

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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
@ -24,16 +24,16 @@
#include <unistd.h> /* For getpid() and readlink() */
#include "../SDL_sysvideo.h"
#include "../SDL_pixels_c.h"
#include "../../core/linux/SDL_system_theme.h"
#include "../SDL_pixels_c.h"
#include "../SDL_sysvideo.h"
#include "SDL_x11video.h"
#include "SDL_x11framebuffer.h"
#include "SDL_x11shape.h"
#include "SDL_x11pen.h"
#include "SDL_x11touch.h"
#include "SDL_x11xinput2.h"
#include "SDL_x11video.h"
#include "SDL_x11xfixes.h"
#include "SDL_x11xinput2.h"
#ifdef SDL_VIDEO_OPENGL_EGL
#include "SDL_x11opengles.h"
@ -79,7 +79,7 @@ static int X11_SafetyNetErrHandler(Display *d, XErrorEvent *e)
if (!safety_net_triggered) {
safety_net_triggered = SDL_TRUE;
device = SDL_GetVideoDevice();
if (device != NULL) {
if (device) {
int i;
for (i = 0; i < device->num_displays; i++) {
SDL_VideoDisplay *display = device->displays[i];
@ -90,7 +90,7 @@ static int X11_SafetyNetErrHandler(Display *d, XErrorEvent *e)
}
}
if (orig_x11_errhandler != NULL) {
if (orig_x11_errhandler) {
return orig_x11_errhandler(d, e); /* probably terminate. */
}
@ -115,21 +115,19 @@ static SDL_VideoDevice *X11_CreateDevice(void)
/* Open the display first to be sure that X11 is available */
x11_display = X11_XOpenDisplay(display);
if (x11_display == NULL) {
if (!x11_display) {
SDL_X11_UnloadSymbols();
return NULL;
}
/* Initialize all variables that we clean on shutdown */
device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice));
if (device == NULL) {
SDL_OutOfMemory();
if (!device) {
return NULL;
}
data = (struct SDL_VideoData *)SDL_calloc(1, sizeof(SDL_VideoData));
if (data == NULL) {
if (!data) {
SDL_free(device);
SDL_OutOfMemory();
return NULL;
}
device->driverdata = data;
@ -142,7 +140,7 @@ static SDL_VideoDevice *X11_CreateDevice(void)
data->display = x11_display;
data->request_display = X11_XOpenDisplay(display);
if (data->request_display == NULL) {
if (!data->request_display) {
X11_XCloseDisplay(data->display);
SDL_free(device->driverdata);
SDL_free(device);
@ -180,7 +178,6 @@ static SDL_VideoDevice *X11_CreateDevice(void)
device->SendWakeupEvent = X11_SendWakeupEvent;
device->CreateSDLWindow = X11_CreateWindow;
device->CreateSDLWindowFrom = X11_CreateWindowFrom;
device->SetWindowTitle = X11_SetWindowTitle;
device->SetWindowIcon = X11_SetWindowIcon;
device->SetWindowPosition = X11_SetWindowPosition;
@ -207,20 +204,17 @@ static SDL_VideoDevice *X11_CreateDevice(void)
device->CreateWindowFramebuffer = X11_CreateWindowFramebuffer;
device->UpdateWindowFramebuffer = X11_UpdateWindowFramebuffer;
device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer;
device->GetWindowWMInfo = X11_GetWindowWMInfo;
device->SetWindowHitTest = X11_SetWindowHitTest;
device->AcceptDragAndDrop = X11_AcceptDragAndDrop;
device->FlashWindow = X11_FlashWindow;
device->ShowWindowSystemMenu = X11_ShowWindowSystemMenu;
device->SetWindowFocusable = X11_SetWindowFocusable;
device->SyncWindow = X11_SyncWindow;
#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
device->SetWindowMouseRect = X11_SetWindowMouseRect;
#endif /* SDL_VIDEO_DRIVER_X11_XFIXES */
device->shape_driver.CreateShaper = X11_CreateShaper;
device->shape_driver.SetWindowShape = X11_SetWindowShape;
#ifdef SDL_VIDEO_OPENGL_GLX
device->GL_LoadLibrary = X11_GL_LoadLibrary;
device->GL_GetProcAddress = X11_GL_GetProcAddress;
@ -281,7 +275,8 @@ static SDL_VideoDevice *X11_CreateDevice(void)
device->system_theme = SDL_SystemTheme_Get();
#endif
device->quirk_flags = VIDEO_DEVICE_QUIRK_HAS_POPUP_WINDOW_SUPPORT;
device->device_caps = VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT |
VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS;
return device;
}
@ -433,6 +428,10 @@ int X11_VideoInit(SDL_VideoDevice *_this)
X11_InitTouch(_this);
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
X11_InitPen(_this);
#endif
return 0;
}
@ -459,7 +458,7 @@ void X11_VideoQuit(SDL_VideoDevice *_this)
SDL_bool X11_UseDirectColorVisuals(void)
{
return SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ? SDL_FALSE : SDL_TRUE;
return (SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR") == NULL);
}
#endif /* SDL_VIDEO_DRIVER_X11 */

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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
@ -57,10 +57,10 @@ int X11_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path)
}
/* Load the Vulkan loader library */
if (path == NULL) {
if (!path) {
path = SDL_getenv("SDL_VULKAN_LIBRARY");
}
if (path == NULL) {
if (!path) {
path = DEFAULT_VULKAN;
}
_this->vulkan_config.loader_handle = SDL_LoadObject(path);
@ -84,7 +84,7 @@ int X11_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path)
(PFN_vkEnumerateInstanceExtensionProperties)
_this->vulkan_config.vkEnumerateInstanceExtensionProperties,
&extensionCount);
if (extensions == NULL) {
if (!extensions) {
goto fail;
}
for (i = 0; i < extensionCount; i++) {
@ -108,7 +108,7 @@ int X11_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path)
goto fail;
} else {
const char *libX11XCBLibraryName = SDL_getenv("SDL_X11_XCB_LIBRARY");
if (libX11XCBLibraryName == NULL) {
if (!libX11XCBLibraryName) {
libX11XCBLibraryName = "libX11-xcb.so";
}
videoData->vulkan_xlib_xcb_library = SDL_LoadObject(libX11XCBLibraryName);
@ -142,35 +142,35 @@ void X11_Vulkan_UnloadLibrary(SDL_VideoDevice *_this)
}
}
SDL_bool X11_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
unsigned *count,
const char **names)
char const* const* X11_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
Uint32 *count)
{
SDL_VideoData *videoData = _this->driverdata;
if (!_this->vulkan_config.loader_handle) {
SDL_SetError("Vulkan is not loaded");
return SDL_FALSE;
}
if (videoData->vulkan_xlib_xcb_library) {
static const char *const extensionsForXCB[] = {
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_XCB_SURFACE_EXTENSION_NAME,
};
return SDL_Vulkan_GetInstanceExtensions_Helper(
count, names, SDL_arraysize(extensionsForXCB), extensionsForXCB);
if(count) {
*count = SDL_arraysize(extensionsForXCB);
}
return extensionsForXCB;
} else {
static const char *const extensionsForXlib[] = {
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_XLIB_SURFACE_EXTENSION_NAME,
};
return SDL_Vulkan_GetInstanceExtensions_Helper(
count, names, SDL_arraysize(extensionsForXlib), extensionsForXlib);
if(count) {
*count = SDL_arraysize(extensionsForXlib);
}
return extensionsForXlib;
}
}
SDL_bool X11_Vulkan_CreateSurface(SDL_VideoDevice *_this,
SDL_Window *window,
VkInstance instance,
const struct VkAllocationCallbacks *allocator,
VkSurfaceKHR *surface)
{
SDL_VideoData *videoData = _this->driverdata;
@ -200,8 +200,7 @@ SDL_bool X11_Vulkan_CreateSurface(SDL_VideoDevice *_this,
return SDL_FALSE;
}
createInfo.window = (xcb_window_t)windowData->xwindow;
result = vkCreateXcbSurfaceKHR(instance, &createInfo,
NULL, surface);
result = vkCreateXcbSurfaceKHR(instance, &createInfo, allocator, surface);
if (result != VK_SUCCESS) {
SDL_SetError("vkCreateXcbSurfaceKHR failed: %s", SDL_Vulkan_GetResultString(result));
return SDL_FALSE;
@ -222,8 +221,7 @@ SDL_bool X11_Vulkan_CreateSurface(SDL_VideoDevice *_this,
createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
createInfo.dpy = videoData->display;
createInfo.window = (xcb_window_t)windowData->xwindow;
result = vkCreateXlibSurfaceKHR(instance, &createInfo,
NULL, surface);
result = vkCreateXlibSurfaceKHR(instance, &createInfo, allocator, surface);
if (result != VK_SUCCESS) {
SDL_SetError("vkCreateXlibSurfaceKHR failed: %s", SDL_Vulkan_GetResultString(result));
return SDL_FALSE;

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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
@ -32,12 +32,12 @@ typedef xcb_connection_t *(*PFN_XGetXCBConnection)(Display *dpy);
int X11_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path);
void X11_Vulkan_UnloadLibrary(SDL_VideoDevice *_this);
SDL_bool X11_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
unsigned *count,
const char **names);
char const* const* X11_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
Uint32 *count);
SDL_bool X11_Vulkan_CreateSurface(SDL_VideoDevice *_this,
SDL_Window *window,
VkInstance instance,
const struct VkAllocationCallbacks *allocator,
VkSurfaceKHR *surface);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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
@ -79,13 +79,31 @@ struct SDL_WindowData
PointerBarrier barrier[4];
SDL_Rect barrier_rect;
#endif /* SDL_VIDEO_DRIVER_X11_XFIXES */
SDL_Rect expected;
SDL_DisplayMode requested_fullscreen_mode;
enum
{
X11_PENDING_OP_NONE = 0x00,
X11_PENDING_OP_RESTORE = 0x01,
X11_PENDING_OP_MINIMIZE = 0x02,
X11_PENDING_OP_MAXIMIZE = 0x04,
X11_PENDING_OP_FULLSCREEN = 0x08,
X11_PENDING_OP_MOVE = 0x10,
X11_PENDING_OP_RESIZE = 0x20
} pending_operation;
SDL_bool window_was_maximized;
SDL_bool disable_size_position_events;
SDL_bool previous_borders_nonzero;
SDL_bool toggle_borders;
SDL_HitTestResult hit_test_result;
};
extern void X11_SetNetWMState(SDL_VideoDevice *_this, Window xwindow, Uint32 flags);
extern Uint32 X11_GetNetWMState(SDL_VideoDevice *_this, SDL_Window *window, Window xwindow);
extern int X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern int X11_CreateWindowFrom(SDL_VideoDevice *_this, SDL_Window *window, const void *data);
extern int X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props);
extern char *X11_GetWindowTitle(SDL_VideoDevice *_this, Window xwindow);
extern void X11_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
extern int X11_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon);
@ -106,19 +124,20 @@ extern void X11_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void X11_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool bordered);
extern void X11_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool resizable);
extern void X11_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool on_top);
extern void X11_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
extern int X11_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
extern void *X11_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size);
extern void X11_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed);
extern void X11_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed);
extern void X11_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern int X11_GetWindowWMInfo(SDL_VideoDevice *_this, SDL_Window *window, struct SDL_SysWMinfo *info);
extern int X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
extern void X11_AcceptDragAndDrop(SDL_Window *window, SDL_bool accept);
extern int X11_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
extern void X11_ShowWindowSystemMenu(SDL_Window *window, int x, int y);
extern int X11_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern int X11_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable);
int SDL_X11_SetWindowTitle(Display *display, Window xwindow, char *title);
void X11_UpdateWindowPosition(SDL_Window *window);
void X11_UpdateWindowPosition(SDL_Window *window, SDL_bool use_current_position);
void X11_SetWindowMinMax(SDL_Window *window, SDL_bool use_current);
#endif /* SDL_x11window_h_ */

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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
@ -22,9 +22,12 @@
#ifdef SDL_VIDEO_DRIVER_X11
#include "SDL_x11pen.h"
#include "SDL_x11video.h"
#include "SDL_x11xinput2.h"
#include "../../events/SDL_events_c.h"
#include "../../events/SDL_mouse_c.h"
#include "../../events/SDL_pen_c.h"
#include "../../events/SDL_touch_c.h"
#define MAX_AXIS 16
@ -74,19 +77,25 @@ static SDL_bool xinput2_version_atleast(const int version, const int wantmajor,
return version >= ((wantmajor * 1000) + wantminor);
}
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
static SDL_Window *xinput2_get_sdlwindow(SDL_VideoData *videodata, Window window)
static SDL_WindowData *xinput2_get_sdlwindowdata(SDL_VideoData *videodata, Window window)
{
int i;
for (i = 0; i < videodata->numwindows; i++) {
SDL_WindowData *d = videodata->windowlist[i];
if (d->xwindow == window) {
return d->window;
return d;
}
}
return NULL;
}
static SDL_Window *xinput2_get_sdlwindow(SDL_VideoData *videodata, Window window)
{
const SDL_WindowData *windowdata = xinput2_get_sdlwindowdata(videodata, window);
return windowdata ? windowdata->window : NULL;
}
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
static void xinput2_normalize_touch_coordinates(SDL_Window *window, double in_x, double in_y, float *out_x, float *out_y)
{
if (window) {
@ -188,10 +197,10 @@ static void xinput2_remove_device_info(SDL_VideoData *videodata, const int devic
SDL_XInput2DeviceInfo *prev = NULL;
SDL_XInput2DeviceInfo *devinfo;
for (devinfo = videodata->mouse_device_info; devinfo != NULL; devinfo = devinfo->next) {
for (devinfo = videodata->mouse_device_info; devinfo; devinfo = devinfo->next) {
if (devinfo->device_id == device_id) {
SDL_assert((devinfo == videodata->mouse_device_info) == (prev == NULL));
if (prev == NULL) {
if (!prev) {
videodata->mouse_device_info = devinfo->next;
} else {
prev->next = devinfo->next;
@ -212,10 +221,10 @@ static SDL_XInput2DeviceInfo *xinput2_get_device_info(SDL_VideoData *videodata,
int axis = 0;
int i;
for (devinfo = videodata->mouse_device_info; devinfo != NULL; devinfo = devinfo->next) {
for (devinfo = videodata->mouse_device_info; devinfo; devinfo = devinfo->next) {
if (devinfo->device_id == device_id) {
SDL_assert((devinfo == videodata->mouse_device_info) == (prev == NULL));
if (prev != NULL) { /* move this to the front of the list, assuming we'll get more from this one. */
if (prev) { /* move this to the front of the list, assuming we'll get more from this one. */
prev->next = devinfo->next;
devinfo->next = videodata->mouse_device_info;
videodata->mouse_device_info = devinfo;
@ -227,13 +236,12 @@ static SDL_XInput2DeviceInfo *xinput2_get_device_info(SDL_VideoData *videodata,
/* don't know about this device yet, query and cache it. */
devinfo = (SDL_XInput2DeviceInfo *)SDL_calloc(1, sizeof(SDL_XInput2DeviceInfo));
if (devinfo == NULL) {
SDL_OutOfMemory();
if (!devinfo) {
return NULL;
}
xidevinfo = X11_XIQueryDevice(videodata->display, device_id, &i);
if (xidevinfo == NULL) {
if (!xidevinfo) {
SDL_free(devinfo);
return NULL;
}
@ -246,7 +254,7 @@ static SDL_XInput2DeviceInfo *xinput2_get_device_info(SDL_VideoData *videodata,
for (i = 0; i < xidevinfo->num_classes; i++) {
const XIValuatorClassInfo *v = (const XIValuatorClassInfo *)xidevinfo->classes[i];
if (v->type == XIValuatorClass) {
devinfo->relative[axis] = (v->mode == XIModeRelative) ? SDL_TRUE : SDL_FALSE;
devinfo->relative[axis] = (v->mode == XIModeRelative);
devinfo->minval[axis] = v->min;
devinfo->maxval[axis] = v->max;
if (++axis >= 2) {
@ -262,18 +270,51 @@ static SDL_XInput2DeviceInfo *xinput2_get_device_info(SDL_VideoData *videodata,
return devinfo;
}
static void xinput2_pen_ensure_window(SDL_VideoDevice *_this, const SDL_Pen *pen, Window window)
{
/* When "flipping" a Wacom eraser pen, we get an XI_DeviceChanged event
* with the newly-activated pen, but this event is global for the display.
* We won't get a window until the pen starts triggering motion or
* button events, so we instead hook the pen to its window at that point. */
const SDL_WindowData *windowdata = X11_FindWindow(_this, window);
if (windowdata) {
SDL_SendPenWindowEvent(0, pen->header.id, windowdata->window);
}
}
#endif
int X11_HandleXinput2Event(SDL_VideoData *videodata, XGenericEventCookie *cookie)
int X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
{
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
if (cookie->extension != xinput2_opcode) {
return 0;
}
switch (cookie->evtype) {
case XI_PropertyEvent:
case XI_DeviceChanged:
{
X11_InitPen(_this);
} break;
case XI_Enter:
case XI_Leave:
{
const XIEnterEvent *enterev = (const XIEnterEvent *)cookie->data;
const SDL_WindowData *windowdata = X11_FindWindow(_this, enterev->event);
const SDL_Pen *pen = SDL_GetPenPtr(X11_PenIDFromDeviceID(enterev->sourceid));
SDL_Window *window = (windowdata && (cookie->evtype == XI_Enter)) ? windowdata->window : NULL;
if (pen) {
SDL_SendPenWindowEvent(0, pen->header.id, window);
}
} break;
case XI_RawMotion:
{
const XIRawEvent *rawev = (const XIRawEvent *)cookie->data;
const SDL_bool is_pen = X11_PenIDFromDeviceID(rawev->sourceid) != SDL_PEN_INVALID;
SDL_Mouse *mouse = SDL_GetMouse();
SDL_XInput2DeviceInfo *devinfo;
double coords[2];
@ -281,13 +322,18 @@ int X11_HandleXinput2Event(SDL_VideoData *videodata, XGenericEventCookie *cookie
int i;
videodata->global_mouse_changed = SDL_TRUE;
if (is_pen) {
return 0; /* Pens check for XI_Motion instead */
}
/* Non-pen: */
if (!mouse->relative_mode || mouse->relative_mode_warp) {
return 0;
}
devinfo = xinput2_get_device_info(videodata, rawev->deviceid);
if (devinfo == NULL) {
if (!devinfo) {
return 0; /* oh well. */
}
@ -317,6 +363,7 @@ int X11_HandleXinput2Event(SDL_VideoData *videodata, XGenericEventCookie *cookie
xinput2_remove_device_info(videodata, hierev->info[i].deviceid);
}
}
X11_InitPen(_this);
} break;
case XI_RawButtonPress:
@ -326,29 +373,108 @@ int X11_HandleXinput2Event(SDL_VideoData *videodata, XGenericEventCookie *cookie
case XI_RawTouchUpdate:
case XI_RawTouchEnd:
#endif
{
videodata->global_mouse_changed = SDL_TRUE;
break;
} break;
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
/* With multitouch, register to receive XI_Motion (which desctivates MotionNotify),
* so that we can distinguish real mouse motions from synthetic one. */
case XI_ButtonPress:
case XI_ButtonRelease:
{
const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
const SDL_Pen *pen = SDL_GetPenPtr(X11_PenIDFromDeviceID(xev->deviceid));
const int button = xev->detail;
const SDL_bool pressed = (cookie->evtype == XI_ButtonPress) ? SDL_TRUE : SDL_FALSE;
if (pen) {
xinput2_pen_ensure_window(_this, pen, xev->event);
/* Only report button event; if there was also pen movement / pressure changes, we expect
an XI_Motion event first anyway */
if (button == 1) {
/* button 1 is the pen tip */
if (pressed && SDL_PenPerformHitTest()) {
/* Check whether we should handle window resize / move events */
SDL_WindowData *windowdata = X11_FindWindow(_this, xev->event);
if (windowdata && X11_TriggerHitTestAction(_this, windowdata, pen->last.x, pen->last.y)) {
SDL_SendWindowEvent(windowdata->window, SDL_EVENT_WINDOW_HIT_TEST, 0, 0);
return 1; /* Don't pass on this event */
}
}
SDL_SendPenTipEvent(0, pen->header.id,
pressed ? SDL_PRESSED : SDL_RELEASED);
} else {
SDL_SendPenButton(0, pen->header.id,
pressed ? SDL_PRESSED : SDL_RELEASED,
button - 1);
}
return 1;
} else {
/* Otherwise assume a regular mouse */
SDL_WindowData *windowdata = xinput2_get_sdlwindowdata(videodata, xev->event);
if (xev->deviceid != xev->sourceid) {
/* Discard events from "Master" devices to avoid duplicates. */
return 1;
}
if (pressed) {
X11_HandleButtonPress(_this, windowdata, button,
xev->event_x, xev->event_y, xev->time);
} else {
X11_HandleButtonRelease(_this, windowdata, button);
}
}
} break;
/* Register to receive XI_Motion (which deactivates MotionNotify), so that we can distinguish
real mouse motions from synthetic ones, for multitouch and pen support. */
case XI_Motion:
{
const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
const SDL_Pen *pen = SDL_GetPenPtr(X11_PenIDFromDeviceID(xev->deviceid));
#if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
int pointer_emulated = (xev->flags & XIPointerEmulated);
#endif /* SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH */
if (xev->deviceid != xev->sourceid) {
/* Discard events from "Master" devices to avoid duplicates. */
return 1;
}
if (pen) {
SDL_PenStatusInfo pen_status;
pen_status.x = xev->event_x;
pen_status.y = xev->event_y;
X11_PenAxesFromValuators(pen,
xev->valuators.values, xev->valuators.mask, xev->valuators.mask_len,
&pen_status.axes[0]);
xinput2_pen_ensure_window(_this, pen, xev->event);
SDL_SendPenMotion(0, pen->header.id,
SDL_TRUE,
&pen_status);
return 1;
}
#if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
if (!pointer_emulated) {
SDL_Mouse *mouse = SDL_GetMouse();
if (!mouse->relative_mode || mouse->relative_mode_warp) {
SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
if (window) {
X11_ProcessHitTest(_this, window->driverdata, (float)xev->event_x, (float)xev->event_y, SDL_FALSE);
SDL_SendMouseMotion(0, window, 0, 0, (float)xev->event_x, (float)xev->event_y);
}
}
}
return 1;
#endif /* SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH */
} break;
#if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
case XI_TouchBegin:
{
const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
@ -376,10 +502,9 @@ int X11_HandleXinput2Event(SDL_VideoData *videodata, XGenericEventCookie *cookie
SDL_SendTouchMotion(0, xev->sourceid, xev->detail, window, x, y, 1.0);
return 1;
} break;
#endif
#endif /* SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH */
}
#endif
#endif /* SDL_VIDEO_DRIVER_X11_XINPUT2 */
return 0;
}
@ -460,6 +585,38 @@ int X11_Xinput2IsInitialized(void)
#endif
}
SDL_bool X11_Xinput2SelectMouse(SDL_VideoDevice *_this, SDL_Window *window)
{
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
const SDL_VideoData *data = (SDL_VideoData *)_this->driverdata;
XIEventMask eventmask;
unsigned char mask[4] = { 0, 0, 0, 0 };
SDL_WindowData *window_data = (SDL_WindowData *)window->driverdata;
eventmask.mask_len = sizeof(mask);
eventmask.mask = mask;
eventmask.deviceid = XIAllDevices;
XISetMask(mask, XI_ButtonPress);
XISetMask(mask, XI_ButtonRelease);
XISetMask(mask, XI_Motion);
XISetMask(mask, XI_Enter);
XISetMask(mask, XI_Leave);
/* Hotplugging: */
XISetMask(mask, XI_DeviceChanged);
XISetMask(mask, XI_HierarchyChanged);
XISetMask(mask, XI_PropertyEvent); /* E.g., when swapping tablet pens */
if (X11_XISelectEvents(data->display,
window_data->xwindow,
&eventmask, 1) == Success) {
return SDL_TRUE;
}
SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Could not enable XInput2 mouse event handling\n");
#endif
return SDL_FALSE;
}
int X11_Xinput2IsMultitouchSupported(void)
{
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH

View File

@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2024 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
@ -32,11 +32,12 @@ typedef struct XGenericEventCookie XGenericEventCookie;
extern void X11_InitXinput2(SDL_VideoDevice *_this);
extern void X11_InitXinput2Multitouch(SDL_VideoDevice *_this);
extern int X11_HandleXinput2Event(SDL_VideoData *videodata, XGenericEventCookie *cookie);
extern int X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie);
extern int X11_Xinput2IsInitialized(void);
extern int X11_Xinput2IsMultitouchSupported(void);
extern void X11_Xinput2SelectTouch(SDL_VideoDevice *_this, SDL_Window *window);
extern void X11_Xinput2GrabTouch(SDL_VideoDevice *_this, SDL_Window *window);
extern void X11_Xinput2UngrabTouch(SDL_VideoDevice *_this, SDL_Window *window);
extern SDL_bool X11_Xinput2SelectMouse(SDL_VideoDevice *_this, SDL_Window *window);
#endif /* SDL_x11xinput2_h_ */