Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/core/linux/SDL_evdev.c
10279 views
1
/*
2
Simple DirectMedia Layer
3
Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
4
5
This software is provided 'as-is', without any express or implied
6
warranty. In no event will the authors be held liable for any damages
7
arising from the use of this software.
8
9
Permission is granted to anyone to use this software for any purpose,
10
including commercial applications, and to alter it and redistribute it
11
freely, subject to the following restrictions:
12
13
1. The origin of this software must not be misrepresented; you must not
14
claim that you wrote the original software. If you use this software
15
in a product, an acknowledgment in the product documentation would be
16
appreciated but is not required.
17
2. Altered source versions must be plainly marked as such, and must not be
18
misrepresented as being the original software.
19
3. This notice may not be removed or altered from any source distribution.
20
*/
21
#include "SDL_internal.h"
22
23
#ifdef SDL_INPUT_LINUXEV
24
25
// This is based on the linux joystick driver
26
/* References: https://www.kernel.org/doc/Documentation/input/input.txt
27
* https://www.kernel.org/doc/Documentation/input/event-codes.txt
28
* /usr/include/linux/input.h
29
* The evtest application is also useful to debug the protocol
30
*/
31
32
#include "SDL_evdev.h"
33
#include "SDL_evdev_kbd.h"
34
35
#include <sys/stat.h>
36
#include <unistd.h>
37
#include <fcntl.h>
38
#include <sys/ioctl.h>
39
#include <linux/input.h>
40
41
#include "../../events/SDL_events_c.h"
42
#include "../../core/linux/SDL_evdev_capabilities.h"
43
#include "../../core/linux/SDL_udev.h"
44
45
// These are not defined in older Linux kernel headers
46
#ifndef SYN_DROPPED
47
#define SYN_DROPPED 3
48
#endif
49
#ifndef ABS_MT_SLOT
50
#define ABS_MT_SLOT 0x2f
51
#define ABS_MT_POSITION_X 0x35
52
#define ABS_MT_POSITION_Y 0x36
53
#define ABS_MT_TRACKING_ID 0x39
54
#define ABS_MT_PRESSURE 0x3a
55
#endif
56
#ifndef REL_WHEEL_HI_RES
57
#define REL_WHEEL_HI_RES 0x0b
58
#define REL_HWHEEL_HI_RES 0x0c
59
#endif
60
61
// The field to look up in struct input_event for integer seconds
62
#ifndef input_event_sec
63
#define input_event_sec time.tv_sec
64
#endif
65
66
// The field to look up in struct input_event for fractional seconds
67
#ifndef input_event_usec
68
#define input_event_usec time.tv_usec
69
#endif
70
71
typedef struct SDL_evdevlist_item
72
{
73
char *path;
74
int fd;
75
int udev_class;
76
77
// TODO: use this for every device, not just touchscreen
78
bool out_of_sync;
79
80
/* TODO: expand on this to have data for every possible class (mouse,
81
keyboard, touchpad, etc.). Also there's probably some things in here we
82
can pull out to the SDL_evdevlist_item i.e. name */
83
bool is_touchscreen;
84
struct
85
{
86
char *name;
87
88
int min_x, max_x, range_x;
89
int min_y, max_y, range_y;
90
int min_pressure, max_pressure, range_pressure;
91
92
int max_slots;
93
int current_slot;
94
struct
95
{
96
enum
97
{
98
EVDEV_TOUCH_SLOTDELTA_NONE = 0,
99
EVDEV_TOUCH_SLOTDELTA_DOWN,
100
EVDEV_TOUCH_SLOTDELTA_UP,
101
EVDEV_TOUCH_SLOTDELTA_MOVE
102
} delta;
103
int tracking_id;
104
int x, y, pressure;
105
} *slots;
106
107
} *touchscreen_data;
108
109
// Mouse state
110
bool high_res_wheel;
111
bool high_res_hwheel;
112
bool relative_mouse;
113
int mouse_x, mouse_y;
114
int mouse_wheel, mouse_hwheel;
115
int min_x, max_x, range_x;
116
int min_y, max_y, range_y;
117
118
struct SDL_evdevlist_item *next;
119
} SDL_evdevlist_item;
120
121
typedef struct SDL_EVDEV_PrivateData
122
{
123
int ref_count;
124
int num_devices;
125
SDL_evdevlist_item *first;
126
SDL_evdevlist_item *last;
127
SDL_EVDEV_keyboard_state *kbd;
128
} SDL_EVDEV_PrivateData;
129
130
static SDL_EVDEV_PrivateData *_this = NULL;
131
132
static SDL_Scancode SDL_EVDEV_translate_keycode(int keycode);
133
static void SDL_EVDEV_sync_device(SDL_evdevlist_item *item);
134
static bool SDL_EVDEV_device_removed(const char *dev_path);
135
static bool SDL_EVDEV_device_added(const char *dev_path, int udev_class);
136
#ifdef SDL_USE_LIBUDEV
137
static void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_event, int udev_class, const char *dev_path);
138
#endif // SDL_USE_LIBUDEV
139
140
static Uint8 EVDEV_MouseButtons[] = {
141
SDL_BUTTON_LEFT, // BTN_LEFT 0x110
142
SDL_BUTTON_RIGHT, // BTN_RIGHT 0x111
143
SDL_BUTTON_MIDDLE, // BTN_MIDDLE 0x112
144
SDL_BUTTON_X1, // BTN_SIDE 0x113
145
SDL_BUTTON_X2, // BTN_EXTRA 0x114
146
SDL_BUTTON_X2 + 1, // BTN_FORWARD 0x115
147
SDL_BUTTON_X2 + 2, // BTN_BACK 0x116
148
SDL_BUTTON_X2 + 3 // BTN_TASK 0x117
149
};
150
151
static bool SDL_EVDEV_SetRelativeMouseMode(bool enabled)
152
{
153
// Mice already send relative events through this interface
154
return true;
155
}
156
157
static void SDL_EVDEV_UpdateKeyboardMute(void)
158
{
159
if (SDL_EVDEV_GetDeviceCount(SDL_UDEV_DEVICE_KEYBOARD) > 0) {
160
SDL_EVDEV_kbd_set_muted(_this->kbd, true);
161
} else {
162
SDL_EVDEV_kbd_set_muted(_this->kbd, false);
163
}
164
}
165
166
bool SDL_EVDEV_Init(void)
167
{
168
if (!_this) {
169
_this = (SDL_EVDEV_PrivateData *)SDL_calloc(1, sizeof(*_this));
170
if (!_this) {
171
return false;
172
}
173
174
#ifdef SDL_USE_LIBUDEV
175
if (!SDL_UDEV_Init()) {
176
SDL_free(_this);
177
_this = NULL;
178
return false;
179
}
180
181
// Set up the udev callback
182
if (!SDL_UDEV_AddCallback(SDL_EVDEV_udev_callback)) {
183
SDL_UDEV_Quit();
184
SDL_free(_this);
185
_this = NULL;
186
return false;
187
}
188
189
// Force a scan to build the initial device list
190
SDL_UDEV_Scan();
191
#else
192
{
193
/* Allow the user to specify a list of devices explicitly of
194
the form:
195
deviceclass:path[,deviceclass:path[,...]]
196
where device class is an integer representing the
197
SDL_UDEV_deviceclass and path is the full path to
198
the event device. */
199
const char *devices = SDL_GetHint(SDL_HINT_EVDEV_DEVICES);
200
if (devices) {
201
/* Assume this is the old use of the env var and it is not in
202
ROM. */
203
char *rest = (char *)devices;
204
char *spec;
205
while ((spec = SDL_strtok_r(rest, ",", &rest))) {
206
char *endofcls = 0;
207
long cls = SDL_strtol(spec, &endofcls, 0);
208
if (endofcls) {
209
SDL_EVDEV_device_added(endofcls + 1, cls);
210
}
211
}
212
} else {
213
// TODO: Scan the devices manually, like a caveman
214
}
215
}
216
#endif // SDL_USE_LIBUDEV
217
218
_this->kbd = SDL_EVDEV_kbd_init();
219
220
SDL_EVDEV_UpdateKeyboardMute();
221
}
222
223
//SDL_GetMouse()->SetRelativeMouseMode = SDL_EVDEV_SetRelativeMouseMode;
224
225
_this->ref_count += 1;
226
227
return true;
228
}
229
230
void SDL_EVDEV_Quit(void)
231
{
232
if (!_this) {
233
return;
234
}
235
236
_this->ref_count -= 1;
237
238
if (_this->ref_count < 1) {
239
#ifdef SDL_USE_LIBUDEV
240
SDL_UDEV_DelCallback(SDL_EVDEV_udev_callback);
241
SDL_UDEV_Quit();
242
#endif // SDL_USE_LIBUDEV
243
244
// Remove existing devices
245
while (_this->first) {
246
SDL_EVDEV_device_removed(_this->first->path);
247
}
248
249
SDL_EVDEV_kbd_quit(_this->kbd);
250
251
SDL_assert(_this->first == NULL);
252
SDL_assert(_this->last == NULL);
253
SDL_assert(_this->num_devices == 0);
254
255
SDL_free(_this);
256
_this = NULL;
257
}
258
}
259
260
#ifdef SDL_USE_LIBUDEV
261
static void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_event, int udev_class,
262
const char *dev_path)
263
{
264
if (!dev_path) {
265
return;
266
}
267
268
switch (udev_event) {
269
case SDL_UDEV_DEVICEADDED:
270
if (!(udev_class & (SDL_UDEV_DEVICE_MOUSE | SDL_UDEV_DEVICE_HAS_KEYS | SDL_UDEV_DEVICE_TOUCHSCREEN | SDL_UDEV_DEVICE_TOUCHPAD))) {
271
return;
272
}
273
274
if (udev_class & SDL_UDEV_DEVICE_JOYSTICK) {
275
return;
276
}
277
278
SDL_EVDEV_device_added(dev_path, udev_class);
279
break;
280
case SDL_UDEV_DEVICEREMOVED:
281
SDL_EVDEV_device_removed(dev_path);
282
break;
283
default:
284
break;
285
}
286
}
287
#endif // SDL_USE_LIBUDEV
288
289
void SDL_EVDEV_SetVTSwitchCallbacks(void (*release_callback)(void*), void *release_callback_data,
290
void (*acquire_callback)(void*), void *acquire_callback_data)
291
{
292
SDL_EVDEV_kbd_set_vt_switch_callbacks(_this->kbd,
293
release_callback, release_callback_data,
294
acquire_callback, acquire_callback_data);
295
}
296
297
int SDL_EVDEV_GetDeviceCount(int device_class)
298
{
299
SDL_evdevlist_item *item;
300
int count = 0;
301
302
for (item = _this->first; item; item = item->next) {
303
if ((item->udev_class & device_class) == device_class) {
304
++count;
305
}
306
}
307
return count;
308
}
309
310
void SDL_EVDEV_Poll(void)
311
{
312
struct input_event events[32];
313
int i, j, len;
314
SDL_evdevlist_item *item;
315
SDL_Scancode scancode;
316
int mouse_button;
317
SDL_Mouse *mouse;
318
float norm_x, norm_y, norm_pressure;
319
320
if (!_this) {
321
return;
322
}
323
324
#ifdef SDL_USE_LIBUDEV
325
SDL_UDEV_Poll();
326
#endif
327
328
SDL_EVDEV_kbd_update(_this->kbd);
329
330
mouse = NULL; //SDL_GetMouse();
331
332
for (item = _this->first; item; item = item->next) {
333
while ((len = read(item->fd, events, sizeof(events))) > 0) {
334
len /= sizeof(events[0]);
335
for (i = 0; i < len; ++i) {
336
struct input_event *event = &events[i];
337
338
/* special handling for touchscreen, that should eventually be
339
used for all devices */
340
if (item->out_of_sync && item->is_touchscreen &&
341
event->type == EV_SYN && event->code != SYN_REPORT) {
342
break;
343
}
344
345
switch (event->type) {
346
case EV_KEY:
347
break;
348
if (event->code >= BTN_MOUSE && event->code < BTN_MOUSE + SDL_arraysize(EVDEV_MouseButtons)) {
349
Uint64 timestamp = SDL_EVDEV_GetEventTimestamp(event);
350
mouse_button = event->code - BTN_MOUSE;
351
//SDL_SendMouseButton(timestamp, mouse->focus, (SDL_MouseID)item->fd, EVDEV_MouseButtons[mouse_button], (event->value != 0));
352
break;
353
}
354
355
/* BTN_TOUCH event value 1 indicates there is contact with
356
a touchscreen or trackpad (earliest finger's current
357
position is sent in EV_ABS ABS_X/ABS_Y, switching to
358
next finger after earliest is released) */
359
if (item->is_touchscreen && event->code == BTN_TOUCH) {
360
if (item->touchscreen_data->max_slots == 1) {
361
if (event->value) {
362
item->touchscreen_data->slots[0].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
363
} else {
364
item->touchscreen_data->slots[0].delta = EVDEV_TOUCH_SLOTDELTA_UP;
365
}
366
}
367
break;
368
}
369
370
// Probably keyboard
371
{
372
Uint64 timestamp = SDL_EVDEV_GetEventTimestamp(event);
373
scancode = SDL_EVDEV_translate_keycode(event->code);
374
// if (event->value == 0) {
375
// SDL_SendKeyboardKey(timestamp, (SDL_KeyboardID)item->fd, event->code, scancode, false);
376
// } else if (event->value == 1 || event->value == 2 /* key repeated */) {
377
// SDL_SendKeyboardKey(timestamp, (SDL_KeyboardID)item->fd, event->code, scancode, true);
378
// }
379
SDL_EVDEV_kbd_keycode(_this->kbd, event->code, event->value);
380
}
381
break;
382
case EV_ABS:
383
switch (event->code) {
384
case ABS_MT_SLOT:
385
if (!item->is_touchscreen) { // FIXME: temp hack
386
break;
387
}
388
item->touchscreen_data->current_slot = event->value;
389
break;
390
case ABS_MT_TRACKING_ID:
391
if (!item->is_touchscreen) { // FIXME: temp hack
392
break;
393
}
394
if (event->value >= 0) {
395
item->touchscreen_data->slots[item->touchscreen_data->current_slot].tracking_id = event->value + 1;
396
item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
397
} else {
398
item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_UP;
399
}
400
break;
401
case ABS_MT_POSITION_X:
402
if (!item->is_touchscreen) { // FIXME: temp hack
403
break;
404
}
405
item->touchscreen_data->slots[item->touchscreen_data->current_slot].x = event->value;
406
if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
407
item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
408
}
409
break;
410
case ABS_MT_POSITION_Y:
411
if (!item->is_touchscreen) { // FIXME: temp hack
412
break;
413
}
414
item->touchscreen_data->slots[item->touchscreen_data->current_slot].y = event->value;
415
if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
416
item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
417
}
418
break;
419
case ABS_MT_PRESSURE:
420
if (!item->is_touchscreen) { // FIXME: temp hack
421
break;
422
}
423
item->touchscreen_data->slots[item->touchscreen_data->current_slot].pressure = event->value;
424
if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
425
item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
426
}
427
break;
428
case ABS_X:
429
if (item->is_touchscreen) {
430
if (item->touchscreen_data->max_slots != 1) {
431
break;
432
}
433
item->touchscreen_data->slots[0].x = event->value;
434
} else if (!item->relative_mouse) {
435
item->mouse_x = event->value;
436
}
437
break;
438
case ABS_Y:
439
if (item->is_touchscreen) {
440
if (item->touchscreen_data->max_slots != 1) {
441
break;
442
}
443
item->touchscreen_data->slots[0].y = event->value;
444
} else if (!item->relative_mouse) {
445
item->mouse_y = event->value;
446
}
447
break;
448
default:
449
break;
450
}
451
break;
452
case EV_REL:
453
switch (event->code) {
454
case REL_X:
455
if (item->relative_mouse) {
456
item->mouse_x += event->value;
457
}
458
break;
459
case REL_Y:
460
if (item->relative_mouse) {
461
item->mouse_y += event->value;
462
}
463
break;
464
case REL_WHEEL:
465
if (!item->high_res_wheel) {
466
item->mouse_wheel += event->value;
467
}
468
break;
469
case REL_WHEEL_HI_RES:
470
SDL_assert(item->high_res_wheel);
471
item->mouse_wheel += event->value;
472
break;
473
case REL_HWHEEL:
474
if (!item->high_res_hwheel) {
475
item->mouse_hwheel += event->value;
476
}
477
break;
478
case REL_HWHEEL_HI_RES:
479
SDL_assert(item->high_res_hwheel);
480
item->mouse_hwheel += event->value;
481
break;
482
default:
483
break;
484
}
485
break;
486
case EV_SYN:
487
switch (event->code) {
488
case SYN_REPORT:
489
// Send mouse axis changes together to ensure consistency and reduce event processing overhead
490
if (item->relative_mouse) {
491
if (item->mouse_x != 0 || item->mouse_y != 0) {
492
Uint64 timestamp = SDL_EVDEV_GetEventTimestamp(event);
493
//SDL_SendMouseMotion(timestamp, mouse->focus, (SDL_MouseID)item->fd, item->relative_mouse, (float)item->mouse_x, (float)item->mouse_y);
494
item->mouse_x = item->mouse_y = 0;
495
}
496
} else if (item->range_x > 0 && item->range_y > 0) {
497
int screen_w = 0, screen_h = 0;
498
const SDL_DisplayMode *mode = NULL;
499
500
if (mode) {
501
screen_w = mode->w;
502
screen_h = mode->h;
503
}
504
//SDL_SendMouseMotion(SDL_EVDEV_GetEventTimestamp(event), mouse->focus, (SDL_MouseID)item->fd, item->relative_mouse,
505
// (float)(item->mouse_x - item->min_x) * screen_w / item->range_x,
506
// (float)(item->mouse_y - item->min_y) * screen_h / item->range_y);
507
}
508
509
if (item->mouse_wheel != 0 || item->mouse_hwheel != 0) {
510
Uint64 timestamp = SDL_EVDEV_GetEventTimestamp(event);
511
const float denom = (item->high_res_hwheel ? 120.0f : 1.0f);
512
//SDL_SendMouseWheel(timestamp,
513
// mouse->focus, (SDL_MouseID)item->fd,
514
// item->mouse_hwheel / denom,
515
// item->mouse_wheel / denom,
516
// SDL_MOUSEWHEEL_NORMAL);
517
item->mouse_wheel = item->mouse_hwheel = 0;
518
}
519
520
if (!item->is_touchscreen) { // FIXME: temp hack
521
break;
522
}
523
524
for (j = 0; j < item->touchscreen_data->max_slots; j++) {
525
norm_x = (float)(item->touchscreen_data->slots[j].x - item->touchscreen_data->min_x) /
526
(float)item->touchscreen_data->range_x;
527
norm_y = (float)(item->touchscreen_data->slots[j].y - item->touchscreen_data->min_y) /
528
(float)item->touchscreen_data->range_y;
529
530
if (item->touchscreen_data->range_pressure > 0) {
531
norm_pressure = (float)(item->touchscreen_data->slots[j].pressure - item->touchscreen_data->min_pressure) /
532
(float)item->touchscreen_data->range_pressure;
533
} else {
534
// This touchscreen does not support pressure
535
norm_pressure = 1.0f;
536
}
537
538
/* FIXME: the touch's window shouldn't be null, but
539
* the coordinate space of touch positions needs to
540
* be window-relative in that case. */
541
switch (item->touchscreen_data->slots[j].delta) {
542
case EVDEV_TOUCH_SLOTDELTA_DOWN:
543
//SDL_SendTouch(SDL_EVDEV_GetEventTimestamp(event), item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, SDL_EVENT_FINGER_DOWN, norm_x, norm_y, norm_pressure);
544
item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
545
break;
546
case EVDEV_TOUCH_SLOTDELTA_UP:
547
//SDL_SendTouch(SDL_EVDEV_GetEventTimestamp(event), item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, SDL_EVENT_FINGER_UP, norm_x, norm_y, norm_pressure);
548
item->touchscreen_data->slots[j].tracking_id = 0;
549
item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
550
break;
551
case EVDEV_TOUCH_SLOTDELTA_MOVE:
552
//SDL_SendTouchMotion(SDL_EVDEV_GetEventTimestamp(event), item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, norm_x, norm_y, norm_pressure);
553
item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
554
break;
555
default:
556
break;
557
}
558
}
559
560
if (item->out_of_sync) {
561
item->out_of_sync = false;
562
}
563
break;
564
case SYN_DROPPED:
565
if (item->is_touchscreen) {
566
item->out_of_sync = true;
567
}
568
SDL_EVDEV_sync_device(item);
569
break;
570
default:
571
break;
572
}
573
break;
574
}
575
}
576
}
577
}
578
}
579
580
static SDL_Scancode SDL_EVDEV_translate_keycode(int keycode)
581
{
582
//SDL_Scancode scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_LINUX, keycode);
583
584
#ifdef DEBUG_SCANCODES
585
if (scancode == SDL_SCANCODE_UNKNOWN) {
586
/* BTN_TOUCH is handled elsewhere, but we might still end up here if
587
you get an unexpected BTN_TOUCH from something SDL believes is not
588
a touch device. In this case, we'd rather not get a misleading
589
SDL_Log message about an unknown key. */
590
if (keycode != BTN_TOUCH) {
591
SDL_Log("The key you just pressed is not recognized by SDL. To help "
592
"get this fixed, please report this to the SDL forums/mailing list "
593
"<https://discourse.libsdl.org/> EVDEV KeyCode %d",
594
keycode);
595
}
596
}
597
#endif // DEBUG_SCANCODES
598
599
return 0; //scancode;
600
}
601
602
static bool SDL_EVDEV_init_keyboard(SDL_evdevlist_item *item, int udev_class)
603
{
604
char name[128];
605
606
name[0] = '\0';
607
ioctl(item->fd, EVIOCGNAME(sizeof(name)), name);
608
609
//SDL_AddKeyboard((SDL_KeyboardID)item->fd, name, true);
610
611
return true;
612
}
613
614
static void SDL_EVDEV_destroy_keyboard(SDL_evdevlist_item *item)
615
{
616
//SDL_RemoveKeyboard((SDL_KeyboardID)item->fd, true);
617
}
618
619
static bool SDL_EVDEV_init_mouse(SDL_evdevlist_item *item, int udev_class)
620
{
621
char name[128];
622
int ret;
623
struct input_absinfo abs_info;
624
625
name[0] = '\0';
626
ioctl(item->fd, EVIOCGNAME(sizeof(name)), name);
627
628
//SDL_AddMouse((SDL_MouseID)item->fd, name, true);
629
630
ret = ioctl(item->fd, EVIOCGABS(ABS_X), &abs_info);
631
if (ret < 0) {
632
// no absolute mode info, continue
633
return true;
634
}
635
item->min_x = abs_info.minimum;
636
item->max_x = abs_info.maximum;
637
item->range_x = abs_info.maximum - abs_info.minimum;
638
639
ret = ioctl(item->fd, EVIOCGABS(ABS_Y), &abs_info);
640
if (ret < 0) {
641
// no absolute mode info, continue
642
return true;
643
}
644
item->min_y = abs_info.minimum;
645
item->max_y = abs_info.maximum;
646
item->range_y = abs_info.maximum - abs_info.minimum;
647
648
return true;
649
}
650
651
static void SDL_EVDEV_destroy_mouse(SDL_evdevlist_item *item)
652
{
653
//SDL_RemoveMouse((SDL_MouseID)item->fd, true);
654
}
655
656
static bool SDL_EVDEV_init_touchscreen(SDL_evdevlist_item *item, int udev_class)
657
{
658
int ret;
659
unsigned long xreq, yreq;
660
char name[64];
661
struct input_absinfo abs_info;
662
663
if (!item->is_touchscreen) {
664
return true;
665
}
666
667
item->touchscreen_data = SDL_calloc(1, sizeof(*item->touchscreen_data));
668
if (!item->touchscreen_data) {
669
return false;
670
}
671
672
ret = ioctl(item->fd, EVIOCGNAME(sizeof(name)), name);
673
if (ret < 0) {
674
SDL_free(item->touchscreen_data);
675
return SDL_SetError("Failed to get evdev touchscreen name");
676
}
677
678
item->touchscreen_data->name = SDL_strdup(name);
679
if (!item->touchscreen_data->name) {
680
SDL_free(item->touchscreen_data);
681
return false;
682
}
683
684
ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
685
if (ret < 0) {
686
SDL_free(item->touchscreen_data->name);
687
SDL_free(item->touchscreen_data);
688
return SDL_SetError("Failed to get evdev touchscreen limits");
689
}
690
691
if (abs_info.maximum == 0) {
692
item->touchscreen_data->max_slots = 1;
693
xreq = EVIOCGABS(ABS_X);
694
yreq = EVIOCGABS(ABS_Y);
695
} else {
696
item->touchscreen_data->max_slots = abs_info.maximum + 1;
697
xreq = EVIOCGABS(ABS_MT_POSITION_X);
698
yreq = EVIOCGABS(ABS_MT_POSITION_Y);
699
}
700
701
ret = ioctl(item->fd, xreq, &abs_info);
702
if (ret < 0) {
703
SDL_free(item->touchscreen_data->name);
704
SDL_free(item->touchscreen_data);
705
return SDL_SetError("Failed to get evdev touchscreen limits");
706
}
707
item->touchscreen_data->min_x = abs_info.minimum;
708
item->touchscreen_data->max_x = abs_info.maximum;
709
item->touchscreen_data->range_x = abs_info.maximum - abs_info.minimum;
710
711
ret = ioctl(item->fd, yreq, &abs_info);
712
if (ret < 0) {
713
SDL_free(item->touchscreen_data->name);
714
SDL_free(item->touchscreen_data);
715
return SDL_SetError("Failed to get evdev touchscreen limits");
716
}
717
item->touchscreen_data->min_y = abs_info.minimum;
718
item->touchscreen_data->max_y = abs_info.maximum;
719
item->touchscreen_data->range_y = abs_info.maximum - abs_info.minimum;
720
721
ret = ioctl(item->fd, EVIOCGABS(ABS_MT_PRESSURE), &abs_info);
722
if (ret < 0) {
723
SDL_free(item->touchscreen_data->name);
724
SDL_free(item->touchscreen_data);
725
return SDL_SetError("Failed to get evdev touchscreen limits");
726
}
727
item->touchscreen_data->min_pressure = abs_info.minimum;
728
item->touchscreen_data->max_pressure = abs_info.maximum;
729
item->touchscreen_data->range_pressure = abs_info.maximum - abs_info.minimum;
730
731
item->touchscreen_data->slots = SDL_calloc(
732
item->touchscreen_data->max_slots,
733
sizeof(*item->touchscreen_data->slots));
734
if (!item->touchscreen_data->slots) {
735
SDL_free(item->touchscreen_data->name);
736
SDL_free(item->touchscreen_data);
737
return false;
738
}
739
740
//ret = SDL_AddTouch(item->fd, // I guess our fd is unique enough
741
// (udev_class & SDL_UDEV_DEVICE_TOUCHPAD) ? SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE : SDL_TOUCH_DEVICE_DIRECT,
742
// item->touchscreen_data->name);
743
ret = -1;
744
if (ret < 0) {
745
SDL_free(item->touchscreen_data->slots);
746
SDL_free(item->touchscreen_data->name);
747
SDL_free(item->touchscreen_data);
748
return false;
749
}
750
751
return true;
752
}
753
754
static void SDL_EVDEV_destroy_touchscreen(SDL_evdevlist_item *item)
755
{
756
if (!item->is_touchscreen) {
757
return;
758
}
759
760
//SDL_DelTouch(item->fd);
761
SDL_free(item->touchscreen_data->slots);
762
SDL_free(item->touchscreen_data->name);
763
SDL_free(item->touchscreen_data);
764
}
765
766
static void SDL_EVDEV_sync_device(SDL_evdevlist_item *item)
767
{
768
#ifdef EVIOCGMTSLOTS
769
int i, ret;
770
struct input_absinfo abs_info;
771
/*
772
* struct input_mt_request_layout {
773
* __u32 code;
774
* __s32 values[num_slots];
775
* };
776
*
777
* this is the structure we're trying to emulate
778
*/
779
Uint32 *mt_req_code;
780
Sint32 *mt_req_values;
781
size_t mt_req_size;
782
783
// TODO: sync devices other than touchscreen
784
if (!item->is_touchscreen) {
785
return;
786
}
787
788
mt_req_size = sizeof(*mt_req_code) +
789
sizeof(*mt_req_values) * item->touchscreen_data->max_slots;
790
791
mt_req_code = SDL_calloc(1, mt_req_size);
792
if (!mt_req_code) {
793
return;
794
}
795
796
mt_req_values = (Sint32 *)mt_req_code + 1;
797
798
*mt_req_code = ABS_MT_TRACKING_ID;
799
ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
800
if (ret < 0) {
801
SDL_free(mt_req_code);
802
return;
803
}
804
for (i = 0; i < item->touchscreen_data->max_slots; i++) {
805
/*
806
* This doesn't account for the very edge case of the user removing their
807
* finger and replacing it on the screen during the time we're out of sync,
808
* which'll mean that we're not going from down -> up or up -> down, we're
809
* going from down -> down but with a different tracking id, meaning we'd
810
* have to tell SDL of the two events, but since we wait till SYN_REPORT in
811
* SDL_EVDEV_Poll to tell SDL, the current structure of this code doesn't
812
* allow it. Lets just pray to God it doesn't happen.
813
*/
814
if (item->touchscreen_data->slots[i].tracking_id == 0 &&
815
mt_req_values[i] >= 0) {
816
item->touchscreen_data->slots[i].tracking_id = mt_req_values[i] + 1;
817
item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
818
} else if (item->touchscreen_data->slots[i].tracking_id != 0 &&
819
mt_req_values[i] < 0) {
820
item->touchscreen_data->slots[i].tracking_id = 0;
821
item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_UP;
822
}
823
}
824
825
*mt_req_code = ABS_MT_POSITION_X;
826
ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
827
if (ret < 0) {
828
SDL_free(mt_req_code);
829
return;
830
}
831
for (i = 0; i < item->touchscreen_data->max_slots; i++) {
832
if (item->touchscreen_data->slots[i].tracking_id != 0 &&
833
item->touchscreen_data->slots[i].x != mt_req_values[i]) {
834
item->touchscreen_data->slots[i].x = mt_req_values[i];
835
if (item->touchscreen_data->slots[i].delta ==
836
EVDEV_TOUCH_SLOTDELTA_NONE) {
837
item->touchscreen_data->slots[i].delta =
838
EVDEV_TOUCH_SLOTDELTA_MOVE;
839
}
840
}
841
}
842
843
*mt_req_code = ABS_MT_POSITION_Y;
844
ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
845
if (ret < 0) {
846
SDL_free(mt_req_code);
847
return;
848
}
849
for (i = 0; i < item->touchscreen_data->max_slots; i++) {
850
if (item->touchscreen_data->slots[i].tracking_id != 0 &&
851
item->touchscreen_data->slots[i].y != mt_req_values[i]) {
852
item->touchscreen_data->slots[i].y = mt_req_values[i];
853
if (item->touchscreen_data->slots[i].delta ==
854
EVDEV_TOUCH_SLOTDELTA_NONE) {
855
item->touchscreen_data->slots[i].delta =
856
EVDEV_TOUCH_SLOTDELTA_MOVE;
857
}
858
}
859
}
860
861
*mt_req_code = ABS_MT_PRESSURE;
862
ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
863
if (ret < 0) {
864
SDL_free(mt_req_code);
865
return;
866
}
867
for (i = 0; i < item->touchscreen_data->max_slots; i++) {
868
if (item->touchscreen_data->slots[i].tracking_id != 0 &&
869
item->touchscreen_data->slots[i].pressure != mt_req_values[i]) {
870
item->touchscreen_data->slots[i].pressure = mt_req_values[i];
871
if (item->touchscreen_data->slots[i].delta ==
872
EVDEV_TOUCH_SLOTDELTA_NONE) {
873
item->touchscreen_data->slots[i].delta =
874
EVDEV_TOUCH_SLOTDELTA_MOVE;
875
}
876
}
877
}
878
879
ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
880
if (ret < 0) {
881
SDL_free(mt_req_code);
882
return;
883
}
884
item->touchscreen_data->current_slot = abs_info.value;
885
886
SDL_free(mt_req_code);
887
888
#endif // EVIOCGMTSLOTS
889
}
890
891
static bool SDL_EVDEV_device_added(const char *dev_path, int udev_class)
892
{
893
SDL_evdevlist_item *item;
894
unsigned long relbit[NBITS(REL_MAX)] = { 0 };
895
896
// Check to make sure it's not already in list.
897
for (item = _this->first; item; item = item->next) {
898
if (SDL_strcmp(dev_path, item->path) == 0) {
899
return false; // already have this one
900
}
901
}
902
903
item = (SDL_evdevlist_item *)SDL_calloc(1, sizeof(SDL_evdevlist_item));
904
if (!item) {
905
return false;
906
}
907
908
item->fd = open(dev_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
909
if (item->fd < 0) {
910
SDL_free(item);
911
return SDL_SetError("Unable to open %s", dev_path);
912
}
913
914
item->path = SDL_strdup(dev_path);
915
if (!item->path) {
916
close(item->fd);
917
SDL_free(item);
918
return false;
919
}
920
921
item->udev_class = udev_class;
922
923
if (ioctl(item->fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) >= 0) {
924
item->relative_mouse = test_bit(REL_X, relbit) && test_bit(REL_Y, relbit);
925
item->high_res_wheel = test_bit(REL_WHEEL_HI_RES, relbit);
926
item->high_res_hwheel = test_bit(REL_HWHEEL_HI_RES, relbit);
927
}
928
929
// For now, we just treat a touchpad like a touchscreen
930
if (udev_class & (SDL_UDEV_DEVICE_TOUCHSCREEN | SDL_UDEV_DEVICE_TOUCHPAD)) {
931
item->is_touchscreen = true;
932
if (!SDL_EVDEV_init_touchscreen(item, udev_class)) {
933
close(item->fd);
934
SDL_free(item->path);
935
SDL_free(item);
936
return false;
937
}
938
}
939
940
if (udev_class & SDL_UDEV_DEVICE_MOUSE) {
941
if (!SDL_EVDEV_init_mouse(item, udev_class)) {
942
close(item->fd);
943
SDL_free(item->path);
944
SDL_free(item);
945
return false;
946
}
947
}
948
949
if (udev_class & SDL_UDEV_DEVICE_KEYBOARD) {
950
if (!SDL_EVDEV_init_keyboard(item, udev_class)) {
951
close(item->fd);
952
SDL_free(item->path);
953
SDL_free(item);
954
return false;
955
}
956
}
957
958
if (!_this->last) {
959
_this->first = _this->last = item;
960
} else {
961
_this->last->next = item;
962
_this->last = item;
963
}
964
965
SDL_EVDEV_sync_device(item);
966
967
SDL_EVDEV_UpdateKeyboardMute();
968
969
++_this->num_devices;
970
return true;
971
}
972
973
static bool SDL_EVDEV_device_removed(const char *dev_path)
974
{
975
SDL_evdevlist_item *item;
976
SDL_evdevlist_item *prev = NULL;
977
978
for (item = _this->first; item; item = item->next) {
979
// found it, remove it.
980
if (SDL_strcmp(dev_path, item->path) == 0) {
981
if (prev) {
982
prev->next = item->next;
983
} else {
984
SDL_assert(_this->first == item);
985
_this->first = item->next;
986
}
987
if (item == _this->last) {
988
_this->last = prev;
989
}
990
991
if (item->is_touchscreen) {
992
SDL_EVDEV_destroy_touchscreen(item);
993
}
994
if (item->udev_class & SDL_UDEV_DEVICE_MOUSE) {
995
SDL_EVDEV_destroy_mouse(item);
996
}
997
if (item->udev_class & SDL_UDEV_DEVICE_KEYBOARD) {
998
SDL_EVDEV_destroy_keyboard(item);
999
}
1000
close(item->fd);
1001
SDL_free(item->path);
1002
SDL_free(item);
1003
SDL_EVDEV_UpdateKeyboardMute();
1004
_this->num_devices--;
1005
return true;
1006
}
1007
prev = item;
1008
}
1009
1010
return false;
1011
}
1012
1013
Uint64 SDL_EVDEV_GetEventTimestamp(struct input_event *event)
1014
{
1015
static Uint64 timestamp_offset;
1016
Uint64 timestamp;
1017
Uint64 now = SDL_GetTicksNS();
1018
1019
/* The kernel internally has nanosecond timestamps, but converts it
1020
to microseconds when delivering the events */
1021
timestamp = event->input_event_sec;
1022
timestamp *= SDL_NS_PER_SECOND;
1023
timestamp += SDL_US_TO_NS(event->input_event_usec);
1024
1025
if (!timestamp_offset) {
1026
timestamp_offset = (now - timestamp);
1027
}
1028
timestamp += timestamp_offset;
1029
1030
if (timestamp > now) {
1031
timestamp_offset -= (timestamp - now);
1032
timestamp = now;
1033
}
1034
return timestamp;
1035
}
1036
1037
#endif // SDL_INPUT_LINUXEV
1038
1039