Skip to content

Commit

Permalink
x11: Attempt to deal with XInput2 devices with absolute coordinates.
Browse files Browse the repository at this point in the history
This is untested!

Reference Issue #1836.
  • Loading branch information
icculus committed Aug 4, 2022
1 parent dfd2c57 commit 5907db5
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 12 deletions.
10 changes: 10 additions & 0 deletions src/video/x11/SDL_x11mouse.c
Expand Up @@ -452,6 +452,16 @@ X11_InitMouse(_THIS)
void
X11_QuitMouse(_THIS)
{
SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
SDL_XInput2DeviceInfo *i;
SDL_XInput2DeviceInfo *next;

for (i = data->mouse_device_info; i != NULL; i = next) {
next = i->next;
SDL_free(i);
}
data->mouse_device_info = NULL;

X11_DestroyEmptyCursor();
}

Expand Down
11 changes: 11 additions & 0 deletions src/video/x11/SDL_x11mouse.h
Expand Up @@ -23,6 +23,17 @@
#ifndef SDL_x11mouse_h_
#define SDL_x11mouse_h_

typedef struct SDL_XInput2DeviceInfo
{
int device_id;
SDL_bool relative[2];
double minval[2];
double maxval[2];
double prev_coords[2];
Time prev_time;
struct SDL_XInput2DeviceInfo *next;
} SDL_XInput2DeviceInfo;

extern void X11_InitMouse(_THIS);
extern void X11_QuitMouse(_THIS);

Expand Down
2 changes: 2 additions & 0 deletions src/video/x11/SDL_x11video.h
Expand Up @@ -134,6 +134,8 @@ typedef struct SDL_VideoData
SDL_Point global_mouse_position;
Uint32 global_mouse_buttons;

SDL_XInput2DeviceInfo *mouse_device_info;

int xrandr_event_base;

#if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
Expand Down
139 changes: 127 additions & 12 deletions src/video/x11/SDL_x11xinput2.c
Expand Up @@ -167,14 +167,109 @@ X11_InitXinput2(_THIS)
}
#endif

if (X11_XISelectEvents(data->display, DefaultRootWindow(data->display), &eventmask,1) != Success) {
if (X11_XISelectEvents(data->display, DefaultRootWindow(data->display), &eventmask, 1) != Success) {
return;
}

SDL_zero(eventmask);
SDL_zeroa(mask);
eventmask.deviceid = XIAllDevices;
eventmask.mask_len = sizeof(mask);
eventmask.mask = mask;

XISetMask(mask, XI_HierarchyChanged);
if (X11_XISelectEvents(data->display, DefaultRootWindow(data->display), &eventmask, 1) != Success) {
return;
}
#endif
}

/* xi2 device went away? take it out of the list. */
static void
xinput2_remove_device_info(SDL_VideoData *videodata, const int device_id)
{
SDL_XInput2DeviceInfo *prev = NULL;
SDL_XInput2DeviceInfo *devinfo;

for (devinfo = videodata->mouse_device_info; devinfo != NULL; devinfo = devinfo->next) {
if (devinfo->device_id == device_id) {
SDL_assert((devinfo == videodata->mouse_device_info) == (prev == NULL));
if (prev == NULL) {
videodata->mouse_device_info = devinfo->next;
} else {
prev->next = devinfo->next;
}
SDL_free(devinfo);
return;
}
prev = devinfo;
}
}

static SDL_XInput2DeviceInfo *
xinput2_get_device_info(SDL_VideoData *videodata, const int device_id)
{
/* cache device info as we see new devices. */
SDL_XInput2DeviceInfo *prev = NULL;
SDL_XInput2DeviceInfo *devinfo;
XIDeviceInfo *xidevinfo;
int axis = 0;
int i;

for (devinfo = videodata->mouse_device_info; devinfo != NULL; 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. */
prev->next = devinfo->next;
devinfo->next = videodata->mouse_device_info;
videodata->mouse_device_info = devinfo;
}
return devinfo;
}
prev = devinfo;
}

/* don't know about this device yet, query and cache it. */
devinfo = (SDL_XInput2DeviceInfo *) SDL_calloc(1, sizeof (SDL_XInput2DeviceInfo));
if (!devinfo) {
SDL_OutOfMemory();
return NULL;
}

xidevinfo = X11_XIQueryDevice(videodata->display, device_id, &i);
if (!xidevinfo) {
SDL_free(devinfo);
return NULL;
}

devinfo->device_id = device_id;

/* !!! FIXME: this is sort of hacky because we only care about the first two axes we see, but any given
!!! FIXME: axis could be relative or absolute, and they might not even be the X and Y axes!
!!! FIXME: But we go on, for now. Maybe we need a more robust mouse API in SDL3... */
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->minval[axis] = v->min;
devinfo->maxval[axis] = v->max;
if (++axis >= 2) {
break;
}
}
}

X11_XIFreeDeviceInfo(xidevinfo);

devinfo->next = videodata->mouse_device_info;
videodata->mouse_device_info = devinfo;

return devinfo;
}


int
X11_HandleXinput2Event(SDL_VideoData *videodata,XGenericEventCookie *cookie)
X11_HandleXinput2Event(SDL_VideoData *videodata, XGenericEventCookie *cookie)
{
#if SDL_VIDEO_DRIVER_X11_XINPUT2
if (cookie->extension != xinput2_opcode) {
Expand All @@ -184,31 +279,51 @@ X11_HandleXinput2Event(SDL_VideoData *videodata,XGenericEventCookie *cookie)
case XI_RawMotion: {
const XIRawEvent *rawev = (const XIRawEvent*)cookie->data;
SDL_Mouse *mouse = SDL_GetMouse();
double relative_coords[2];
static Time prev_time = 0;
static double prev_rel_coords[2];
SDL_XInput2DeviceInfo *devinfo = xinput2_get_device_info(videodata, rawev->deviceid);
double coords[2];
double processed_coords[2];
int i;

videodata->global_mouse_changed = SDL_TRUE;

if (!mouse->relative_mode || mouse->relative_mode_warp) {
if (!devinfo || !mouse->relative_mode || mouse->relative_mode_warp) {

This comment has been minimized.

Copy link
@slouken

slouken Aug 9, 2022

Collaborator

Maybe we shouldn't bother getting the device info if we're not in relative mode?

This comment has been minimized.

Copy link
@icculus

icculus Aug 9, 2022

Author Collaborator

Done! b599205

return 0;
}

parse_valuators(rawev->raw_values,rawev->valuators.mask,
rawev->valuators.mask_len,relative_coords,2);
rawev->valuators.mask_len,coords,2);

if ((rawev->time == prev_time) && (relative_coords[0] == prev_rel_coords[0]) && (relative_coords[1] == prev_rel_coords[1])) {
if ((rawev->time == devinfo->prev_time) && (coords[0] == devinfo->prev_coords[0]) && (coords[1] == devinfo->prev_coords[1])) {
return 0; /* duplicate event, drop it. */
}

SDL_SendMouseMotion(mouse->focus,mouse->mouseID,1,(int)relative_coords[0],(int)relative_coords[1]);
prev_rel_coords[0] = relative_coords[0];
prev_rel_coords[1] = relative_coords[1];
prev_time = rawev->time;
for (i = 0; i < 2; i++) {
if (devinfo->relative[i]) {
processed_coords[i] = coords[i];
} else {
processed_coords[i] = devinfo->prev_coords[i] - coords[i]; /* convert absolute to relative */
}
}

SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int) processed_coords[0], (int) processed_coords[1]);
devinfo->prev_coords[0] = coords[0];
devinfo->prev_coords[1] = coords[1];
devinfo->prev_time = rawev->time;
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);
}
}
}
break;

case XI_RawButtonPress:
case XI_RawButtonRelease:
#if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
Expand Down

0 comments on commit 5907db5

Please sign in to comment.