x11: fixed mouse and keyboard input when XInput2 isn't available

This commit is contained in:
Sam Lantinga 2024-03-26 13:01:00 -07:00
parent fed5f3a81b
commit c47f9b9b54
5 changed files with 87 additions and 62 deletions

View File

@ -1525,15 +1525,24 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
- ButtonPress - ButtonPress
- ButtonRelease - ButtonRelease
XInput2 has more precise information, e.g., to distinguish different input devices. */ XInput2 has more precise information, e.g., to distinguish different input devices. */
#ifndef SDL_VIDEO_DRIVER_X11_XINPUT2
case KeyPress: case KeyPress:
case KeyRelease: case KeyRelease:
{ {
if (data->using_xinput2) {
// This input is being handled by XInput2
break;
}
X11_HandleKeyEvent(_this, data, SDL_GLOBAL_KEYBOARD_ID, xevent); X11_HandleKeyEvent(_this, data, SDL_GLOBAL_KEYBOARD_ID, xevent);
} break; } break;
case MotionNotify: case MotionNotify:
{ {
if (data->using_xinput2) {
// This input is being handled by XInput2
break;
}
SDL_Mouse *mouse = SDL_GetMouse(); SDL_Mouse *mouse = SDL_GetMouse();
if (!mouse->relative_mode || mouse->relative_mode_warp) { if (!mouse->relative_mode || mouse->relative_mode_warp) {
#ifdef DEBUG_MOTION #ifdef DEBUG_MOTION
@ -1547,15 +1556,24 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
case ButtonPress: case ButtonPress:
{ {
if (data->using_xinput2) {
// This input is being handled by XInput2
break;
}
X11_HandleButtonPress(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button, X11_HandleButtonPress(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button,
xevent->xbutton.x, xevent->xbutton.y, xevent->xbutton.time); xevent->xbutton.x, xevent->xbutton.y, xevent->xbutton.time);
} break; } break;
case ButtonRelease: case ButtonRelease:
{ {
if (data->using_xinput2) {
// This input is being handled by XInput2
break;
}
X11_HandleButtonRelease(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button); X11_HandleButtonRelease(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button);
} break; } break;
#endif /* !SDL_VIDEO_DRIVER_X11_XINPUT2 */
case PropertyNotify: case PropertyNotify:
{ {

View File

@ -363,7 +363,10 @@ static int X11_WarpMouseGlobal(float x, float y)
static int X11_SetRelativeMouseMode(SDL_bool enabled) static int X11_SetRelativeMouseMode(SDL_bool enabled)
{ {
return X11_Xinput2IsInitialized() ? 0 : SDL_Unsupported(); if (!X11_Xinput2IsInitialized()) {
return SDL_Unsupported();
}
return 0;
} }
static int X11_CaptureMouse(SDL_Window *window) static int X11_CaptureMouse(SDL_Window *window)

View File

@ -59,6 +59,7 @@ struct SDL_WindowData
int border_right; int border_right;
int border_top; int border_top;
int border_bottom; int border_bottom;
SDL_bool using_xinput2;
SDL_bool mouse_grabbed; SDL_bool mouse_grabbed;
Uint64 last_focus_event_time; Uint64 last_focus_event_time;
PendingFocusEnum pending_focus; PendingFocusEnum pending_focus;

View File

@ -288,18 +288,32 @@ static void xinput2_pen_ensure_window(SDL_VideoDevice *_this, const SDL_Pen *pen
} }
#endif #endif
int X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie) void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
{ {
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata; SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
if (cookie->extension != xinput2_opcode) { if (cookie->extension != xinput2_opcode) {
return 0; return;
} }
switch (cookie->evtype) { switch (cookie->evtype) {
case XI_HierarchyChanged:
{
const XIHierarchyEvent *hierev = (const XIHierarchyEvent *)cookie->data;
int i;
for (i = 0; i < hierev->num_info; i++) {
if (hierev->info[i].flags & XISlaveRemoved) {
xinput2_remove_device_info(videodata, hierev->info[i].deviceid);
}
}
videodata->xinput_hierarchy_changed = SDL_TRUE;
} break;
case XI_PropertyEvent: case XI_PropertyEvent:
case XI_DeviceChanged: case XI_DeviceChanged:
{ {
// FIXME: We shouldn't rescan all devices for pen changes every time a property or active slave changes
X11_InitPen(_this); X11_InitPen(_this);
} break; } break;
@ -327,18 +341,18 @@ int X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
videodata->global_mouse_changed = SDL_TRUE; videodata->global_mouse_changed = SDL_TRUE;
if (is_pen) { if (is_pen) {
return 0; /* Pens check for XI_Motion instead */ break; /* Pens check for XI_Motion instead */
} }
/* Non-pen: */ /* Non-pen: */
if (!mouse->relative_mode || mouse->relative_mode_warp) { if (!mouse->relative_mode || mouse->relative_mode_warp) {
return 0; break;
} }
devinfo = xinput2_get_device_info(videodata, rawev->deviceid); devinfo = xinput2_get_device_info(videodata, rawev->deviceid);
if (!devinfo) { if (!devinfo) {
return 0; /* oh well. */ break; /* oh well. */
} }
parse_valuators(rawev->raw_values, rawev->valuators.mask, parse_valuators(rawev->raw_values, rawev->valuators.mask,
@ -355,19 +369,6 @@ int X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
SDL_SendMouseMotion(0, mouse->focus, (SDL_MouseID)rawev->sourceid, SDL_TRUE, (float)processed_coords[0], (float)processed_coords[1]); SDL_SendMouseMotion(0, mouse->focus, (SDL_MouseID)rawev->sourceid, SDL_TRUE, (float)processed_coords[0], (float)processed_coords[1]);
devinfo->prev_coords[0] = coords[0]; devinfo->prev_coords[0] = coords[0];
devinfo->prev_coords[1] = coords[1]; devinfo->prev_coords[1] = coords[1];
return 1;
} break;
case XI_HierarchyChanged:
{
const XIHierarchyEvent *hierev = (const XIHierarchyEvent *)cookie->data;
int i;
for (i = 0; i < hierev->num_info; i++) {
if (hierev->info[i].flags & XISlaveRemoved) {
xinput2_remove_device_info(videodata, hierev->info[i].deviceid);
}
}
videodata->xinput_hierarchy_changed = SDL_TRUE;
} break; } break;
case XI_KeyPress: case XI_KeyPress:
@ -379,7 +380,7 @@ int X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
if (xev->deviceid != xev->sourceid) { if (xev->deviceid != xev->sourceid) {
/* Discard events from "Master" devices to avoid duplicates. */ /* Discard events from "Master" devices to avoid duplicates. */
return 1; break;
} }
if (cookie->evtype == XI_KeyPress) { if (cookie->evtype == XI_KeyPress) {
@ -436,7 +437,7 @@ int X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
SDL_WindowData *windowdata = X11_FindWindow(_this, xev->event); SDL_WindowData *windowdata = X11_FindWindow(_this, xev->event);
if (windowdata && X11_TriggerHitTestAction(_this, windowdata, pen->last.x, pen->last.y)) { if (windowdata && X11_TriggerHitTestAction(_this, windowdata, pen->last.x, pen->last.y)) {
SDL_SendWindowEvent(windowdata->window, SDL_EVENT_WINDOW_HIT_TEST, 0, 0); SDL_SendWindowEvent(windowdata->window, SDL_EVENT_WINDOW_HIT_TEST, 0, 0);
return 1; /* Don't pass on this event */ break; /* Don't pass on this event */
} }
} }
SDL_SendPenTipEvent(0, pen->header.id, SDL_SendPenTipEvent(0, pen->header.id,
@ -446,14 +447,13 @@ int X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
pressed ? SDL_PRESSED : SDL_RELEASED, pressed ? SDL_PRESSED : SDL_RELEASED,
button - 1); button - 1);
} }
return 1;
} else { } else {
/* Otherwise assume a regular mouse */ /* Otherwise assume a regular mouse */
SDL_WindowData *windowdata = xinput2_get_sdlwindowdata(videodata, xev->event); SDL_WindowData *windowdata = xinput2_get_sdlwindowdata(videodata, xev->event);
if (xev->deviceid != xev->sourceid) { if (xev->deviceid != xev->sourceid) {
/* Discard events from "Master" devices to avoid duplicates. */ /* Discard events from "Master" devices to avoid duplicates. */
return 1; break;
} }
if (pressed) { if (pressed) {
@ -465,21 +465,23 @@ int X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
} }
} break; } break;
/* Register to receive XI_Motion (which deactivates MotionNotify), so that we can distinguish /* Register to receive XI_Motion (which deactivates MotionNotify), so that we can distinguish
real mouse motions from synthetic ones, for multitouch and pen support. */ real mouse motions from synthetic ones, for multitouch and pen support. */
case XI_Motion: case XI_Motion:
{ {
const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data; const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
const SDL_Pen *pen = SDL_GetPenPtr(X11_PenIDFromDeviceID(xev->deviceid)); const SDL_Pen *pen = SDL_GetPenPtr(X11_PenIDFromDeviceID(xev->deviceid));
#if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
int pointer_emulated = (xev->flags & XIPointerEmulated); SDL_bool pointer_emulated = ((xev->flags & XIPointerEmulated) != 0);
#endif /* SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH */ #else
SDL_bool pointer_emulated = SDL_FALSE;
#endif
videodata->global_mouse_changed = SDL_TRUE; videodata->global_mouse_changed = SDL_TRUE;
if (xev->deviceid != xev->sourceid) { if (xev->deviceid != xev->sourceid) {
/* Discard events from "Master" devices to avoid duplicates. */ /* Discard events from "Master" devices to avoid duplicates. */
return 1; break;
} }
if (pen) { if (pen) {
@ -497,10 +499,9 @@ int X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
SDL_SendPenMotion(0, pen->header.id, SDL_SendPenMotion(0, pen->header.id,
SDL_TRUE, SDL_TRUE,
&pen_status); &pen_status);
return 1; break;
} }
#if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
if (!pointer_emulated) { if (!pointer_emulated) {
SDL_Mouse *mouse = SDL_GetMouse(); SDL_Mouse *mouse = SDL_GetMouse();
if (!mouse->relative_mode || mouse->relative_mode_warp) { if (!mouse->relative_mode || mouse->relative_mode_warp) {
@ -511,8 +512,6 @@ int X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
} }
} }
} }
return 1;
#endif /* SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH */
} break; } break;
#if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
@ -523,8 +522,8 @@ int X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event); SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y); xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y);
SDL_SendTouch(0, xev->sourceid, xev->detail, window, SDL_TRUE, x, y, 1.0); SDL_SendTouch(0, xev->sourceid, xev->detail, window, SDL_TRUE, x, y, 1.0);
return 1;
} break; } break;
case XI_TouchEnd: case XI_TouchEnd:
{ {
const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data; const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
@ -532,8 +531,8 @@ int X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event); SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y); xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y);
SDL_SendTouch(0, xev->sourceid, xev->detail, window, SDL_FALSE, x, y, 1.0); SDL_SendTouch(0, xev->sourceid, xev->detail, window, SDL_FALSE, x, y, 1.0);
return 1;
} break; } break;
case XI_TouchUpdate: case XI_TouchUpdate:
{ {
const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data; const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
@ -541,12 +540,10 @@ int X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event); SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y); xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y);
SDL_SendTouchMotion(0, xev->sourceid, xev->detail, window, x, y, 1.0); SDL_SendTouchMotion(0, xev->sourceid, xev->detail, window, x, y, 1.0);
return 1;
} break; } break;
#endif /* SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH */ #endif /* SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH */
} }
#endif /* SDL_VIDEO_DRIVER_X11_XINPUT2 */ #endif /* SDL_VIDEO_DRIVER_X11_XINPUT2 */
return 0;
} }
void X11_InitXinput2Multitouch(SDL_VideoDevice *_this) void X11_InitXinput2Multitouch(SDL_VideoDevice *_this)
@ -592,36 +589,40 @@ int X11_Xinput2IsInitialized(void)
SDL_bool X11_Xinput2SelectMouseAndKeyboard(SDL_VideoDevice *_this, SDL_Window *window) SDL_bool X11_Xinput2SelectMouseAndKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
{ {
SDL_WindowData *windowdata = (SDL_WindowData *)window->driverdata;
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
const SDL_VideoData *data = (SDL_VideoData *)_this->driverdata; 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); if (X11_Xinput2IsInitialized()) {
eventmask.mask = mask; XIEventMask eventmask;
eventmask.deviceid = XIAllDevices; unsigned char mask[4] = { 0, 0, 0, 0 };
XISetMask(mask, XI_KeyPress); eventmask.mask_len = sizeof(mask);
XISetMask(mask, XI_KeyRelease); eventmask.mask = mask;
XISetMask(mask, XI_ButtonPress); eventmask.deviceid = XIAllDevices;
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, XISetMask(mask, XI_KeyPress);
window_data->xwindow, XISetMask(mask, XI_KeyRelease);
&eventmask, 1) == Success) { XISetMask(mask, XI_ButtonPress);
return SDL_TRUE; 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, windowdata->xwindow, &eventmask, 1) == Success) {
windowdata->using_xinput2 = SDL_TRUE;
} else {
SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Could not enable XInput2 event handling\n");
windowdata->using_xinput2 = SDL_FALSE;
}
} }
SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Could not enable XInput2 mouse event handling\n");
#endif #endif
return SDL_FALSE; return windowdata->using_xinput2;
} }
int X11_Xinput2IsMultitouchSupported(void) int X11_Xinput2IsMultitouchSupported(void)
@ -731,6 +732,8 @@ void X11_Xinput2UpdateDevices(SDL_VideoDevice *_this, SDL_bool initial_check)
Uint32 *new_touch_devices = NULL; Uint32 *new_touch_devices = NULL;
SDL_bool send_event = !initial_check; SDL_bool send_event = !initial_check;
SDL_assert(X11_Xinput2IsInitialized());
info = X11_XIQueryDevice(data->display, XIAllDevices, &ndevices); info = X11_XIQueryDevice(data->display, XIAllDevices, &ndevices);
old_keyboards = SDL_GetKeyboards(&old_keyboard_count); old_keyboards = SDL_GetKeyboards(&old_keyboard_count);

View File

@ -32,7 +32,7 @@ typedef struct XGenericEventCookie XGenericEventCookie;
extern SDL_bool X11_InitXinput2(SDL_VideoDevice *_this); extern SDL_bool X11_InitXinput2(SDL_VideoDevice *_this);
extern void X11_InitXinput2Multitouch(SDL_VideoDevice *_this); extern void X11_InitXinput2Multitouch(SDL_VideoDevice *_this);
extern int X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie); extern void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie);
extern int X11_Xinput2IsInitialized(void); extern int X11_Xinput2IsInitialized(void);
extern int X11_Xinput2IsMultitouchSupported(void); extern int X11_Xinput2IsMultitouchSupported(void);
extern void X11_Xinput2SelectTouch(SDL_VideoDevice *_this, SDL_Window *window); extern void X11_Xinput2SelectTouch(SDL_VideoDevice *_this, SDL_Window *window);