Skip to content

Commit 67e5130

Browse files
committed
x11: Check axis labels when searching for relative axes
Prefer axes with the 'Rel X'/'Rel Y' labels, followed by 'Abs X'/'Abs Y', and only fall back to the old behavior of using the first two enumerated axes if no others are found. Fixes a FIXME when determining which axes to use for relative motion.
1 parent 91be1b0 commit 67e5130

File tree

2 files changed

+83
-47
lines changed

2 files changed

+83
-47
lines changed

src/video/x11/SDL_x11mouse.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
typedef struct SDL_XInput2DeviceInfo
2727
{
2828
int device_id;
29+
int number[2];
2930
bool relative[2];
3031
double minval[2];
3132
double maxval[2];

src/video/x11/SDL_x11xinput2.c

Lines changed: 82 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ static bool xinput2_multitouch_supported;
4545
* this extension */
4646
static int xinput2_opcode;
4747

48+
static Atom xinput2_rel_x_atom;
49+
static Atom xinput2_rel_y_atom;
50+
static Atom xinput2_abs_x_atom;
51+
static Atom xinput2_abs_y_atom;
52+
4853
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO
4954
typedef struct
5055
{
@@ -67,23 +72,40 @@ static int scrollable_device_count;
6772
static bool xinput2_scrolling_supported;
6873
#endif
6974

70-
static void parse_valuators(const double *input_values, const unsigned char *mask, int mask_len,
71-
double *output_values, int output_values_len)
75+
static void parse_relative_valuators(SDL_XInput2DeviceInfo *devinfo, const XIRawEvent *rawev)
7276
{
73-
int i = 0, z = 0;
74-
int top = mask_len * 8;
75-
if (top > MAX_AXIS) {
76-
top = MAX_AXIS;
77-
}
77+
double processed_coords[2] = { 0.0, 0.0 };
78+
int values_i = 0, found = 0;
79+
80+
for (int i = 0; i < rawev->valuators.mask_len * 8 && found < 2; ++i) {
81+
if (!XIMaskIsSet(rawev->valuators.mask, i)) {
82+
continue;
83+
}
84+
85+
for (int j = 0; j < 2; ++j) {
86+
if (devinfo->number[j] == i) {
87+
const double current_val = rawev->valuators.values[values_i];
88+
89+
if (devinfo->relative[j]) {
90+
processed_coords[j] = current_val;
91+
} else {
92+
processed_coords[j] = devinfo->prev_coords[j] - current_val; // convert absolute to relative
93+
}
7894

79-
SDL_memset(output_values, 0, output_values_len * sizeof(double));
80-
for (; i < top && z < output_values_len; i++) {
81-
if (XIMaskIsSet(mask, i)) {
82-
const int value = (int)*input_values;
83-
output_values[z] = value;
84-
input_values++;
95+
devinfo->prev_coords[j] = current_val;
96+
++found;
97+
98+
break;
99+
}
85100
}
86-
z++;
101+
102+
++values_i;
103+
}
104+
105+
// Relative mouse motion is delivered to the window with keyboard focus
106+
SDL_Mouse *mouse = SDL_GetMouse();
107+
if (mouse->relative_mode && SDL_GetKeyboardFocus()) {
108+
SDL_SendMouseMotion(rawev->time, mouse->focus, (SDL_MouseID)rawev->sourceid, true, (float)processed_coords[0], (float)processed_coords[1]);
87109
}
88110
}
89111

@@ -260,6 +282,12 @@ bool X11_InitXinput2(SDL_VideoDevice *_this)
260282
xinput2_multitouch_supported = xinput2_version_atleast(version, 2, 2);
261283
#endif
262284

285+
// Populate the atoms for finding relative axes
286+
xinput2_rel_x_atom = X11_XInternAtom(data->display, "Rel X", False);
287+
xinput2_rel_y_atom = X11_XInternAtom(data->display, "Rel Y", False);
288+
xinput2_abs_x_atom = X11_XInternAtom(data->display, "Abs X", False);
289+
xinput2_abs_y_atom = X11_XInternAtom(data->display, "Abs Y", False);
290+
263291
// Enable raw motion events for this display
264292
SDL_zero(eventmask);
265293
SDL_zeroa(mask);
@@ -345,7 +373,6 @@ static SDL_XInput2DeviceInfo *xinput2_get_device_info(SDL_VideoData *videodata,
345373
SDL_XInput2DeviceInfo *prev = NULL;
346374
SDL_XInput2DeviceInfo *devinfo;
347375
XIDeviceInfo *xidevinfo;
348-
int axis = 0;
349376
int i;
350377

351378
for (devinfo = videodata->mouse_device_info; devinfo; devinfo = devinfo->next) {
@@ -375,18 +402,49 @@ static SDL_XInput2DeviceInfo *xinput2_get_device_info(SDL_VideoData *videodata,
375402

376403
devinfo->device_id = device_id;
377404

378-
/* !!! FIXME: this is sort of hacky because we only care about the first two axes we see, but any given
379-
!!! FIXME: axis could be relative or absolute, and they might not even be the X and Y axes!
380-
!!! FIXME: But we go on, for now. Maybe we need a more robust mouse API in SDL3... */
405+
/* Search for relative axes with the following priority:
406+
* - Labelled 'Rel X'/'Rel Y'
407+
* - Labelled 'Abs X'/'Abs Y'
408+
* - The first two axes found
409+
*/
410+
bool have_rel_x = false, have_rel_y = false;
411+
bool have_abs_x = false, have_abs_y = false;
412+
int axis_index = 0;
381413
for (i = 0; i < xidevinfo->num_classes; i++) {
382414
const XIValuatorClassInfo *v = (const XIValuatorClassInfo *)xidevinfo->classes[i];
383415
if (v->type == XIValuatorClass) {
384-
devinfo->relative[axis] = (v->mode == XIModeRelative);
385-
devinfo->minval[axis] = v->min;
386-
devinfo->maxval[axis] = v->max;
387-
if (++axis >= 2) {
416+
if (v->label == xinput2_rel_x_atom || (v->label == xinput2_abs_x_atom && !have_rel_x) ||
417+
(axis_index == 0 && !have_rel_x && !have_abs_x)) {
418+
devinfo->number[0] = v->number;
419+
devinfo->relative[0] = (v->mode == XIModeRelative);
420+
devinfo->minval[0] = v->min;
421+
devinfo->maxval[0] = v->max;
422+
423+
if (v->label == xinput2_rel_x_atom) {
424+
have_rel_x = true;
425+
} else if (v->label == xinput2_abs_x_atom) {
426+
have_abs_x = true;
427+
}
428+
} else if (v->label == xinput2_rel_y_atom || (v->label == xinput2_abs_y_atom && !have_rel_y) ||
429+
(axis_index == 1 && !have_rel_y && !have_abs_y)) {
430+
devinfo->number[1] = v->number;
431+
devinfo->relative[1] = (v->mode == XIModeRelative);
432+
devinfo->minval[1] = v->min;
433+
devinfo->maxval[1] = v->max;
434+
435+
if (v->label == xinput2_rel_y_atom) {
436+
have_rel_y = true;
437+
} else if (v->label == xinput2_abs_y_atom) {
438+
have_abs_y = true;
439+
}
440+
}
441+
442+
// If two relative axes were found, nothing more to do.
443+
if (have_rel_x && have_rel_y) {
388444
break;
389445
}
446+
447+
++axis_index;
390448
}
391449
}
392450

@@ -437,41 +495,18 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
437495
{
438496
const XIRawEvent *rawev = (const XIRawEvent *)cookie->data;
439497
const bool is_pen = X11_FindPenByDeviceID(rawev->sourceid) != NULL;
440-
SDL_Mouse *mouse = SDL_GetMouse();
441-
SDL_XInput2DeviceInfo *devinfo;
442-
double coords[2];
443-
double processed_coords[2];
444-
int i;
445-
Uint64 timestamp = X11_GetEventTimestamp(rawev->time);
446498

447499
videodata->global_mouse_changed = true;
448500
if (is_pen) {
449501
break; // Pens check for XI_Motion instead
450502
}
451503

452-
devinfo = xinput2_get_device_info(videodata, rawev->deviceid);
504+
SDL_XInput2DeviceInfo *devinfo = xinput2_get_device_info(videodata, rawev->deviceid);
453505
if (!devinfo) {
454506
break; // oh well.
455507
}
456508

457-
parse_valuators(rawev->raw_values, rawev->valuators.mask,
458-
rawev->valuators.mask_len, coords, 2);
459-
460-
for (i = 0; i < 2; i++) {
461-
if (devinfo->relative[i]) {
462-
processed_coords[i] = coords[i];
463-
} else {
464-
processed_coords[i] = devinfo->prev_coords[i] - coords[i]; // convert absolute to relative
465-
}
466-
}
467-
468-
// Relative mouse motion is delivered to the window with keyboard focus
469-
if (mouse->relative_mode && SDL_GetKeyboardFocus()) {
470-
SDL_SendMouseMotion(timestamp, mouse->focus, (SDL_MouseID)rawev->sourceid, true, (float)processed_coords[0], (float)processed_coords[1]);
471-
}
472-
473-
devinfo->prev_coords[0] = coords[0];
474-
devinfo->prev_coords[1] = coords[1];
509+
parse_relative_valuators(devinfo, rawev);
475510
} break;
476511

477512
case XI_KeyPress:

0 commit comments

Comments
 (0)