@@ -45,6 +45,20 @@ static bool xinput2_multitouch_supported;
45
45
* this extension */
46
46
static int xinput2_opcode ;
47
47
48
+ #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO
49
+ typedef struct
50
+ {
51
+ int device_id ;
52
+ int axis_id [2 ]; // 0 = horizontal, 1 = vertical
53
+ double prev_coord [2 ];
54
+ bool prev_coord_valid [2 ];
55
+ double line_unit [2 ]; // Specifies the amount of coordinate change to scroll one line
56
+ } SDL_XInput2ScrollableDevice ;
57
+
58
+ static SDL_XInput2ScrollableDevice * scrollable_devices ;
59
+ static int scrollable_device_count ;
60
+ #endif
61
+
48
62
static void parse_valuators (const double * input_values , const unsigned char * mask , int mask_len ,
49
63
double * output_values , int output_values_len )
50
64
{
@@ -95,6 +109,56 @@ static SDL_Window *xinput2_get_sdlwindow(SDL_VideoData *videodata, Window window
95
109
return windowdata ? windowdata -> window : NULL ;
96
110
}
97
111
112
+ #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO
113
+ static void xinput2_reset_scrollable_devices (SDL_VideoData * videodata )
114
+ {
115
+ for (int i = 0 ; i < scrollable_device_count ; ++ i ) {
116
+ scrollable_devices [i ].prev_coord_valid [0 ] = false;
117
+ scrollable_devices [i ].prev_coord_valid [1 ] = false;
118
+ }
119
+ }
120
+
121
+ static void xinput2_parse_scrollable_valuators (const XIDeviceEvent * xev )
122
+ {
123
+ SDL_Mouse * mouse = SDL_GetMouse ();
124
+
125
+ for (int i = 0 ; i < scrollable_device_count ; ++ i ) {
126
+ SDL_XInput2ScrollableDevice * sd = & scrollable_devices [i ];
127
+ // Filter out events from the master device to avoid duplicates.
128
+ if (xev -> sourceid == sd -> device_id ) {
129
+ int values_i = 0 ;
130
+ for (int j = 0 ; j < xev -> valuators .mask_len * 8 ; ++ j ) {
131
+ if (!XIMaskIsSet (xev -> valuators .mask , j )) {
132
+ continue ;
133
+ }
134
+
135
+ for (int k = 0 ; k < 2 ; ++ k ) {
136
+ if (sd -> axis_id [k ] == j ) {
137
+ const double current_val = xev -> valuators .values [values_i ];
138
+ const double delta = (sd -> prev_coord [k ] - current_val ) / sd -> line_unit [k ];
139
+ /* Ignore very large jumps that can happen as a result of overflowing
140
+ * the maximum range, as the driver will reset the position to zero
141
+ * at "something that's close to 2^32".
142
+ *
143
+ * http://who-t.blogspot.com/2012/06/xi-21-protocol-design-issues.html
144
+ */
145
+ if (sd -> prev_coord_valid [k ] && SDL_fabs (delta ) < (double )SDL_MAX_UINT32 * 0.95 ) {
146
+ const double x = k == 0 ? delta : 0 ;
147
+ const double y = k == 1 ? delta : 0 ;
148
+ SDL_SendMouseWheel (xev -> time , mouse -> focus , (SDL_MouseID )xev -> sourceid , x , y , SDL_MOUSEWHEEL_NORMAL );
149
+ }
150
+ sd -> prev_coord [k ] = current_val ;
151
+ sd -> prev_coord_valid [k ] = true;
152
+ }
153
+ }
154
+
155
+ ++ values_i ;
156
+ }
157
+ }
158
+ }
159
+ }
160
+ #endif // SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO
161
+
98
162
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
99
163
static void xinput2_normalize_touch_coordinates (SDL_Window * window , double in_x , double in_y , float * out_x , float * out_y )
100
164
{
@@ -126,7 +190,7 @@ bool X11_InitXinput2(SDL_VideoDevice *_this)
126
190
127
191
int version = 0 ;
128
192
XIEventMask eventmask ;
129
- unsigned char mask [4 ] = { 0 , 0 , 0 , 0 };
193
+ unsigned char mask [5 ] = { 0 , 0 , 0 , 0 , 0 };
130
194
int event , err ;
131
195
132
196
/* XInput2 is required for relative mouse mode, so you probably want to leave this enabled */
@@ -156,6 +220,11 @@ bool X11_InitXinput2(SDL_VideoDevice *_this)
156
220
157
221
xinput2_initialized = true;
158
222
223
+ #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO
224
+ /* Xinput 2.1 is required to provide precision scrolling info */
225
+ data -> xinput_scrolling = xinput2_version_atleast (version , 2 , 1 );
226
+ #endif
227
+
159
228
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH // Multitouch needs XInput 2.2
160
229
xinput2_multitouch_supported = xinput2_version_atleast (version , 2 , 2 );
161
230
#endif
@@ -171,6 +240,12 @@ bool X11_InitXinput2(SDL_VideoDevice *_this)
171
240
XISetMask (mask , XI_RawButtonPress );
172
241
XISetMask (mask , XI_RawButtonRelease );
173
242
243
+ #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO
244
+ if (data -> xinput_scrolling ) {
245
+ XISetMask (mask , XI_Motion );
246
+ }
247
+ #endif
248
+
174
249
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
175
250
// Enable raw touch events if supported
176
251
if (X11_Xinput2IsMultitouchSupported ()) {
@@ -199,6 +274,15 @@ bool X11_InitXinput2(SDL_VideoDevice *_this)
199
274
#endif
200
275
}
201
276
277
+ void X11_QuitXinput2 (SDL_VideoDevice * _this )
278
+ {
279
+ #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO
280
+ SDL_free (scrollable_devices );
281
+ scrollable_devices = NULL ;
282
+ scrollable_device_count = 0 ;
283
+ #endif
284
+ }
285
+
202
286
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
203
287
// xi2 device went away? take it out of the list.
204
288
static void xinput2_remove_device_info (SDL_VideoData * videodata , const int device_id )
@@ -435,13 +519,19 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
435
519
}
436
520
} break ;
437
521
522
+ #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO
523
+ case XI_Enter :
524
+ xinput2_reset_scrollable_devices (videodata );
525
+ break ;
526
+ #endif
527
+
438
528
/* Register to receive XI_Motion (which deactivates MotionNotify), so that we can distinguish
439
529
real mouse motions from synthetic ones, for multitouch and pen support. */
440
530
case XI_Motion :
441
531
{
442
532
const XIDeviceEvent * xev = (const XIDeviceEvent * )cookie -> data ;
443
- #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
444
- bool pointer_emulated = (( xev -> flags & XIPointerEmulated ) != 0 ) ;
533
+ #if defined( SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO ) || defined( SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH )
534
+ bool pointer_emulated = (xev -> flags & XIPointerEmulated ) != 0 ;
445
535
#else
446
536
bool pointer_emulated = false;
447
537
#endif
@@ -467,14 +557,23 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
467
557
SDL_SendPenAxis (0 , pen -> pen , window , (SDL_PenAxis ) i , axes [i ]);
468
558
}
469
559
}
470
- } else if (!pointer_emulated && xev -> deviceid == videodata -> xinput_master_pointer_device ) {
471
- // Use the master device for non-relative motion, as the slave devices can seemingly lag behind.
472
- SDL_Mouse * mouse = SDL_GetMouse ();
473
- if (!mouse -> relative_mode ) {
474
- SDL_Window * window = xinput2_get_sdlwindow (videodata , xev -> event );
475
- if (window ) {
476
- X11_ProcessHitTest (_this , window -> internal , (float )xev -> event_x , (float )xev -> event_y , false);
477
- SDL_SendMouseMotion (0 , window , SDL_GLOBAL_MOUSE_ID , false, (float )xev -> event_x , (float )xev -> event_y );
560
+ } else {
561
+ #if SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO
562
+ // Filter out events from the master device to avoid duplicates.
563
+ if (videodata -> xinput_scrolling && xev -> deviceid == xev -> sourceid ) {
564
+ xinput2_parse_scrollable_valuators (xev );
565
+ }
566
+ #endif
567
+
568
+ if (!pointer_emulated && xev -> deviceid == videodata -> xinput_master_pointer_device ) {
569
+ // Use the master device for non-relative motion, as the slave devices can seemingly lag behind.
570
+ SDL_Mouse * mouse = SDL_GetMouse ();
571
+ if (!mouse -> relative_mode ) {
572
+ SDL_Window * window = xinput2_get_sdlwindow (videodata , xev -> event );
573
+ if (window ) {
574
+ X11_ProcessHitTest (_this , window -> internal , (float )xev -> event_x , (float )xev -> event_y , false);
575
+ SDL_SendMouseMotion (0 , window , SDL_GLOBAL_MOUSE_ID , false, (float )xev -> event_x , (float )xev -> event_y );
576
+ }
478
577
}
479
578
}
480
579
}
@@ -516,29 +615,36 @@ void X11_InitXinput2Multitouch(SDL_VideoDevice *_this)
516
615
{
517
616
}
518
617
519
- void X11_Xinput2SelectTouch (SDL_VideoDevice * _this , SDL_Window * window )
618
+ void X11_Xinput2Select (SDL_VideoDevice * _this , SDL_Window * window )
520
619
{
521
- #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
522
- SDL_VideoData * data = NULL ;
620
+ #if defined(SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO ) || defined(SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH )
621
+ SDL_VideoData * data = _this -> internal ;
622
+ SDL_WindowData * window_data = window -> internal ;
523
623
XIEventMask eventmask ;
524
- unsigned char mask [4 ] = { 0 , 0 , 0 , 0 };
525
- SDL_WindowData * window_data = NULL ;
624
+ unsigned char mask [5 ] = { 0 , 0 , 0 , 0 , 0 };
526
625
527
- if (!X11_Xinput2IsMultitouchSupported ()) {
626
+ if (!data -> xinput_scrolling && ! X11_Xinput2IsMultitouchSupported ()) {
528
627
return ;
529
628
}
530
629
531
- data = _this -> internal ;
532
- window_data = window -> internal ;
533
-
534
630
eventmask .deviceid = XIAllMasterDevices ;
535
631
eventmask .mask_len = sizeof (mask );
536
632
eventmask .mask = mask ;
537
633
538
- XISetMask (mask , XI_TouchBegin );
539
- XISetMask (mask , XI_TouchUpdate );
540
- XISetMask (mask , XI_TouchEnd );
541
- XISetMask (mask , XI_Motion );
634
+ if (data -> xinput_scrolling ) {
635
+ /* Track enter events that inform us that we need to update
636
+ * the previous scroll coordinates since we cannot track
637
+ * them outside our window.
638
+ */
639
+ XISetMask (mask , XI_Enter );
640
+ }
641
+
642
+ if (X11_Xinput2IsMultitouchSupported ()) {
643
+ XISetMask (mask , XI_TouchBegin );
644
+ XISetMask (mask , XI_TouchUpdate );
645
+ XISetMask (mask , XI_TouchEnd );
646
+ XISetMask (mask , XI_Motion );
647
+ }
542
648
543
649
X11_XISelectEvents (data -> display , window_data -> xwindow , & eventmask , 1 );
544
650
#endif
@@ -740,6 +846,13 @@ void X11_Xinput2UpdateDevices(SDL_VideoDevice *_this, bool initial_check)
740
846
old_mice = SDL_GetMice (& old_mouse_count );
741
847
old_touch_devices = SDL_GetTouchDevices (& old_touch_count );
742
848
849
+ #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO
850
+ // Scroll devices don't get add/remove events, so just rebuild the list.
851
+ SDL_free (scrollable_devices );
852
+ scrollable_devices = NULL ;
853
+ scrollable_device_count = 0 ;
854
+ #endif
855
+
743
856
for (int i = 0 ; i < ndevices ; i ++ ) {
744
857
XIDeviceInfo * dev = & info [i ];
745
858
@@ -770,6 +883,38 @@ void X11_Xinput2UpdateDevices(SDL_VideoDevice *_this, bool initial_check)
770
883
break ;
771
884
}
772
885
886
+ #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO
887
+ bool found_scrollable_device = false;
888
+ for (int j = 0 ; j < dev -> num_classes ; j ++ ) {
889
+ const XIAnyClassInfo * class = dev -> classes [j ];
890
+ const XIScrollClassInfo * s = (XIScrollClassInfo * )class ;
891
+
892
+ if (class -> type != XIScrollClass ) {
893
+ continue ;
894
+ }
895
+
896
+ if (!found_scrollable_device ) {
897
+ scrollable_devices = SDL_realloc (scrollable_devices , (scrollable_device_count + 1 ) * sizeof (SDL_XInput2ScrollableDevice ));
898
+ if (!scrollable_devices ) {
899
+ // No memory, so just skip this
900
+ break ;
901
+ }
902
+ }
903
+
904
+ SDL_XInput2ScrollableDevice * sd = & scrollable_devices [scrollable_device_count ];
905
+ const int dir = s -> scroll_type == XIScrollTypeVertical ;
906
+ sd -> device_id = dev -> deviceid ;
907
+ sd -> axis_id [dir ] = s -> number ;
908
+ sd -> line_unit [dir ] = s -> increment ;
909
+
910
+ found_scrollable_device = true;
911
+ }
912
+
913
+ if (found_scrollable_device ) {
914
+ ++ scrollable_device_count ;
915
+ }
916
+ #endif
917
+
773
918
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
774
919
for (int j = 0 ; j < dev -> num_classes ; j ++ ) {
775
920
Uint64 touchID ;
0 commit comments