@@ -45,6 +45,11 @@ static bool xinput2_multitouch_supported;
4545 * this extension */
4646static 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
4954typedef struct
5055{
@@ -67,23 +72,40 @@ static int scrollable_device_count;
6772static 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