2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "../../SDL_internal.h"
23 #ifdef SDL_INPUT_LINUXEV
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
32 #include "SDL_evdev.h"
33 #include "SDL_evdev_kbd.h"
38 #include <sys/ioctl.h>
39 #include <linux/input.h>
42 #include "SDL_endian.h"
43 #include "SDL_scancode.h"
44 #include "../../events/SDL_events_c.h"
45 #include "../../events/scancodes_linux.h" /* adds linux_scancode_table */
46 #include "../../core/linux/SDL_evdev_capabilities.h"
47 #include "../../core/linux/SDL_udev.h"
49 /* These are not defined in older Linux kernel headers */
54 #define ABS_MT_SLOT 0x2f
55 #define ABS_MT_POSITION_X 0x35
56 #define ABS_MT_POSITION_Y 0x36
57 #define ABS_MT_TRACKING_ID 0x39
58 #define ABS_MT_PRESSURE 0x3a
61 typedef struct SDL_evdevlist_item
66 /* TODO: use this for every device, not just touchscreen */
69 /* TODO: expand on this to have data for every possible class (mouse,
70 keyboard, touchpad, etc.). Also there's probably some things in here we
71 can pull out to the SDL_evdevlist_item i.e. name */
76 int min_x, max_x, range_x;
77 int min_y, max_y, range_y;
78 int min_pressure, max_pressure, range_pressure;
84 EVDEV_TOUCH_SLOTDELTA_NONE = 0,
85 EVDEV_TOUCH_SLOTDELTA_DOWN,
86 EVDEV_TOUCH_SLOTDELTA_UP,
87 EVDEV_TOUCH_SLOTDELTA_MOVE
95 struct SDL_evdevlist_item *next;
98 typedef struct SDL_EVDEV_PrivateData
102 SDL_evdevlist_item *first;
103 SDL_evdevlist_item *last;
104 SDL_EVDEV_keyboard_state *kbd;
105 } SDL_EVDEV_PrivateData;
108 #define _THIS SDL_EVDEV_PrivateData *_this
111 static SDL_Scancode SDL_EVDEV_translate_keycode(int keycode);
112 static void SDL_EVDEV_sync_device(SDL_evdevlist_item *item);
113 static int SDL_EVDEV_device_removed(const char *dev_path);
116 static int SDL_EVDEV_device_added(const char *dev_path, int udev_class);
117 static void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class,
118 const char *dev_path);
119 #endif /* SDL_USE_LIBUDEV */
121 static Uint8 EVDEV_MouseButtons[] = {
122 SDL_BUTTON_LEFT, /* BTN_LEFT 0x110 */
123 SDL_BUTTON_RIGHT, /* BTN_RIGHT 0x111 */
124 SDL_BUTTON_MIDDLE, /* BTN_MIDDLE 0x112 */
125 SDL_BUTTON_X1, /* BTN_SIDE 0x113 */
126 SDL_BUTTON_X2, /* BTN_EXTRA 0x114 */
127 SDL_BUTTON_X2 + 1, /* BTN_FORWARD 0x115 */
128 SDL_BUTTON_X2 + 2, /* BTN_BACK 0x116 */
129 SDL_BUTTON_X2 + 3 /* BTN_TASK 0x117 */
133 SDL_EVDEV_SetRelativeMouseMode(SDL_bool enabled)
135 /* Mice already send relative events through this interface */
144 _this = (SDL_EVDEV_PrivateData*)SDL_calloc(1, sizeof(*_this));
146 return SDL_OutOfMemory();
150 if (SDL_UDEV_Init() < 0) {
156 /* Set up the udev callback */
157 if (SDL_UDEV_AddCallback(SDL_EVDEV_udev_callback) < 0) {
164 /* Force a scan to build the initial device list */
167 /* TODO: Scan the devices manually, like a caveman */
168 #endif /* SDL_USE_LIBUDEV */
170 _this->kbd = SDL_EVDEV_kbd_init();
173 SDL_GetMouse()->SetRelativeMouseMode = SDL_EVDEV_SetRelativeMouseMode;
175 _this->ref_count += 1;
187 _this->ref_count -= 1;
189 if (_this->ref_count < 1) {
191 SDL_UDEV_DelCallback(SDL_EVDEV_udev_callback);
193 #endif /* SDL_USE_LIBUDEV */
195 SDL_EVDEV_kbd_quit(_this->kbd);
197 /* Remove existing devices */
198 while(_this->first != NULL) {
199 SDL_EVDEV_device_removed(_this->first->path);
202 SDL_assert(_this->first == NULL);
203 SDL_assert(_this->last == NULL);
204 SDL_assert(_this->num_devices == 0);
212 static void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_event, int udev_class,
213 const char* dev_path)
215 if (dev_path == NULL) {
220 case SDL_UDEV_DEVICEADDED:
221 if (!(udev_class & (SDL_UDEV_DEVICE_MOUSE | SDL_UDEV_DEVICE_KEYBOARD |
222 SDL_UDEV_DEVICE_TOUCHSCREEN)))
225 SDL_EVDEV_device_added(dev_path, udev_class);
227 case SDL_UDEV_DEVICEREMOVED:
228 SDL_EVDEV_device_removed(dev_path);
234 #endif /* SDL_USE_LIBUDEV */
239 struct input_event events[32];
241 SDL_evdevlist_item *item;
242 SDL_Scancode scan_code;
245 float norm_x, norm_y, norm_pressure;
255 mouse = SDL_GetMouse();
257 for (item = _this->first; item != NULL; item = item->next) {
258 while ((len = read(item->fd, events, (sizeof events))) > 0) {
259 len /= sizeof(events[0]);
260 for (i = 0; i < len; ++i) {
261 /* special handling for touchscreen, that should eventually be
262 used for all devices */
263 if (item->out_of_sync && item->is_touchscreen &&
264 events[i].type == EV_SYN && events[i].code != SYN_REPORT) {
268 switch (events[i].type) {
270 if (events[i].code >= BTN_MOUSE && events[i].code < BTN_MOUSE + SDL_arraysize(EVDEV_MouseButtons)) {
271 mouse_button = events[i].code - BTN_MOUSE;
272 if (events[i].value == 0) {
273 SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_RELEASED, EVDEV_MouseButtons[mouse_button]);
274 } else if (events[i].value == 1) {
275 SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_PRESSED, EVDEV_MouseButtons[mouse_button]);
280 /* BTH_TOUCH event value 1 indicates there is contact with
281 a touchscreen or trackpad (earlist finger's current
282 position is sent in EV_ABS ABS_X/ABS_Y, switching to
283 next finger after earlist is released) */
284 if (item->is_touchscreen && events[i].code == BTN_TOUCH) {
285 if (item->touchscreen_data->max_slots == 1) {
287 item->touchscreen_data->slots[0].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
289 item->touchscreen_data->slots[0].delta = EVDEV_TOUCH_SLOTDELTA_UP;
294 /* Probably keyboard */
295 scan_code = SDL_EVDEV_translate_keycode(events[i].code);
296 if (scan_code != SDL_SCANCODE_UNKNOWN) {
297 if (events[i].value == 0) {
298 SDL_SendKeyboardKey(SDL_RELEASED, scan_code);
299 } else if (events[i].value == 1 || events[i].value == 2 /* key repeated */) {
300 SDL_SendKeyboardKey(SDL_PRESSED, scan_code);
303 SDL_EVDEV_kbd_keycode(_this->kbd, events[i].code, events[i].value);
306 switch(events[i].code) {
308 if (!item->is_touchscreen) /* FIXME: temp hack */
310 item->touchscreen_data->current_slot = events[i].value;
312 case ABS_MT_TRACKING_ID:
313 if (!item->is_touchscreen) /* FIXME: temp hack */
315 if (events[i].value >= 0) {
316 item->touchscreen_data->slots[item->touchscreen_data->current_slot].tracking_id = events[i].value;
317 item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
319 item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_UP;
322 case ABS_MT_POSITION_X:
323 if (!item->is_touchscreen) /* FIXME: temp hack */
325 item->touchscreen_data->slots[item->touchscreen_data->current_slot].x = events[i].value;
326 if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
327 item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
330 case ABS_MT_POSITION_Y:
331 if (!item->is_touchscreen) /* FIXME: temp hack */
333 item->touchscreen_data->slots[item->touchscreen_data->current_slot].y = events[i].value;
334 if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
335 item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
338 case ABS_MT_PRESSURE:
339 if (!item->is_touchscreen) /* FIXME: temp hack */
341 item->touchscreen_data->slots[item->touchscreen_data->current_slot].pressure = events[i].value;
342 if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
343 item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
347 if (item->is_touchscreen) {
348 if (item->touchscreen_data->max_slots != 1)
350 item->touchscreen_data->slots[0].x = events[i].value;
352 SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, events[i].value, mouse->y);
355 if (item->is_touchscreen) {
356 if (item->touchscreen_data->max_slots != 1)
358 item->touchscreen_data->slots[0].y = events[i].value;
360 SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, mouse->x, events[i].value);
367 switch(events[i].code) {
369 SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, events[i].value, 0);
372 SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, 0, events[i].value);
375 SDL_SendMouseWheel(mouse->focus, mouse->mouseID, 0, events[i].value, SDL_MOUSEWHEEL_NORMAL);
378 SDL_SendMouseWheel(mouse->focus, mouse->mouseID, events[i].value, 0, SDL_MOUSEWHEEL_NORMAL);
385 switch (events[i].code) {
387 if (!item->is_touchscreen) /* FIXME: temp hack */
390 for(j = 0; j < item->touchscreen_data->max_slots; j++) {
391 norm_x = (float)(item->touchscreen_data->slots[j].x - item->touchscreen_data->min_x) /
392 (float)item->touchscreen_data->range_x;
393 norm_y = (float)(item->touchscreen_data->slots[j].y - item->touchscreen_data->min_y) /
394 (float)item->touchscreen_data->range_y;
396 if (item->touchscreen_data->range_pressure > 0) {
397 norm_pressure = (float)(item->touchscreen_data->slots[j].pressure - item->touchscreen_data->min_pressure) /
398 (float)item->touchscreen_data->range_pressure;
400 /* This touchscreen does not support pressure */
401 norm_pressure = 1.0f;
404 /* FIXME: the touch's window shouldn't be null, but
405 * the coordinate space of touch positions needs to
406 * be window-relative in that case. */
407 switch(item->touchscreen_data->slots[j].delta) {
408 case EVDEV_TOUCH_SLOTDELTA_DOWN:
409 SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, SDL_TRUE, norm_x, norm_y, norm_pressure);
410 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
412 case EVDEV_TOUCH_SLOTDELTA_UP:
413 SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, SDL_FALSE, norm_x, norm_y, norm_pressure);
414 item->touchscreen_data->slots[j].tracking_id = -1;
415 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
417 case EVDEV_TOUCH_SLOTDELTA_MOVE:
418 SDL_SendTouchMotion(item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, norm_x, norm_y, norm_pressure);
419 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
426 if (item->out_of_sync)
427 item->out_of_sync = 0;
430 if (item->is_touchscreen)
431 item->out_of_sync = 1;
432 SDL_EVDEV_sync_device(item);
445 SDL_EVDEV_translate_keycode(int keycode)
447 SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
449 if (keycode < SDL_arraysize(linux_scancode_table)) {
450 scancode = linux_scancode_table[keycode];
452 if (scancode == SDL_SCANCODE_UNKNOWN) {
453 /* BTN_TOUCH is handled elsewhere, but we might still end up here if
454 you get an unexpected BTN_TOUCH from something SDL believes is not
455 a touch device. In this case, we'd rather not get a misleading
456 SDL_Log message about an unknown key. */
457 if (keycode != BTN_TOUCH) {
458 SDL_Log("The key you just pressed is not recognized by SDL. To help "
459 "get this fixed, please report this to the SDL forums/mailing list "
460 "<https://discourse.libsdl.org/> EVDEV KeyCode %d", keycode);
468 #ifdef SDL_USE_LIBUDEV
470 SDL_EVDEV_init_touchscreen(SDL_evdevlist_item* item)
473 unsigned long xreq, yreq;
475 struct input_absinfo abs_info;
477 if (!item->is_touchscreen)
480 item->touchscreen_data = SDL_calloc(1, sizeof(*item->touchscreen_data));
481 if (item->touchscreen_data == NULL)
482 return SDL_OutOfMemory();
484 ret = ioctl(item->fd, EVIOCGNAME(sizeof(name)), name);
486 SDL_free(item->touchscreen_data);
487 return SDL_SetError("Failed to get evdev touchscreen name");
490 item->touchscreen_data->name = SDL_strdup(name);
491 if (item->touchscreen_data->name == NULL) {
492 SDL_free(item->touchscreen_data);
493 return SDL_OutOfMemory();
496 ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
498 SDL_free(item->touchscreen_data->name);
499 SDL_free(item->touchscreen_data);
500 return SDL_SetError("Failed to get evdev touchscreen limits");
503 if (abs_info.maximum == 0) {
504 item->touchscreen_data->max_slots = 1;
505 xreq = EVIOCGABS(ABS_X);
506 yreq = EVIOCGABS(ABS_Y);
508 item->touchscreen_data->max_slots = abs_info.maximum + 1;
509 xreq = EVIOCGABS(ABS_MT_POSITION_X);
510 yreq = EVIOCGABS(ABS_MT_POSITION_Y);
513 ret = ioctl(item->fd, xreq, &abs_info);
515 SDL_free(item->touchscreen_data->name);
516 SDL_free(item->touchscreen_data);
517 return SDL_SetError("Failed to get evdev touchscreen limits");
519 item->touchscreen_data->min_x = abs_info.minimum;
520 item->touchscreen_data->max_x = abs_info.maximum;
521 item->touchscreen_data->range_x = abs_info.maximum - abs_info.minimum;
523 ret = ioctl(item->fd, yreq, &abs_info);
525 SDL_free(item->touchscreen_data->name);
526 SDL_free(item->touchscreen_data);
527 return SDL_SetError("Failed to get evdev touchscreen limits");
529 item->touchscreen_data->min_y = abs_info.minimum;
530 item->touchscreen_data->max_y = abs_info.maximum;
531 item->touchscreen_data->range_y = abs_info.maximum - abs_info.minimum;
533 ret = ioctl(item->fd, EVIOCGABS(ABS_MT_PRESSURE), &abs_info);
535 SDL_free(item->touchscreen_data->name);
536 SDL_free(item->touchscreen_data);
537 return SDL_SetError("Failed to get evdev touchscreen limits");
539 item->touchscreen_data->min_pressure = abs_info.minimum;
540 item->touchscreen_data->max_pressure = abs_info.maximum;
541 item->touchscreen_data->range_pressure = abs_info.maximum - abs_info.minimum;
543 item->touchscreen_data->slots = SDL_calloc(
544 item->touchscreen_data->max_slots,
545 sizeof(*item->touchscreen_data->slots));
546 if (item->touchscreen_data->slots == NULL) {
547 SDL_free(item->touchscreen_data->name);
548 SDL_free(item->touchscreen_data);
549 return SDL_OutOfMemory();
552 for(i = 0; i < item->touchscreen_data->max_slots; i++) {
553 item->touchscreen_data->slots[i].tracking_id = -1;
556 ret = SDL_AddTouch(item->fd, /* I guess our fd is unique enough */
557 SDL_TOUCH_DEVICE_DIRECT,
558 item->touchscreen_data->name);
560 SDL_free(item->touchscreen_data->slots);
561 SDL_free(item->touchscreen_data->name);
562 SDL_free(item->touchscreen_data);
568 #endif /* SDL_USE_LIBUDEV */
571 SDL_EVDEV_destroy_touchscreen(SDL_evdevlist_item* item) {
572 if (!item->is_touchscreen)
575 SDL_DelTouch(item->fd);
576 SDL_free(item->touchscreen_data->slots);
577 SDL_free(item->touchscreen_data->name);
578 SDL_free(item->touchscreen_data);
582 SDL_EVDEV_sync_device(SDL_evdevlist_item *item)
586 struct input_absinfo abs_info;
588 * struct input_mt_request_layout {
590 * __s32 values[num_slots];
593 * this is the structure we're trying to emulate
596 Sint32* mt_req_values;
599 /* TODO: sync devices other than touchscreen */
600 if (!item->is_touchscreen)
603 mt_req_size = sizeof(*mt_req_code) +
604 sizeof(*mt_req_values) * item->touchscreen_data->max_slots;
606 mt_req_code = SDL_calloc(1, mt_req_size);
607 if (mt_req_code == NULL) {
611 mt_req_values = (Sint32*)mt_req_code + 1;
613 *mt_req_code = ABS_MT_TRACKING_ID;
614 ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
616 SDL_free(mt_req_code);
619 for(i = 0; i < item->touchscreen_data->max_slots; i++) {
621 * This doesn't account for the very edge case of the user removing their
622 * finger and replacing it on the screen during the time we're out of sync,
623 * which'll mean that we're not going from down -> up or up -> down, we're
624 * going from down -> down but with a different tracking id, meaning we'd
625 * have to tell SDL of the two events, but since we wait till SYN_REPORT in
626 * SDL_EVDEV_Poll to tell SDL, the current structure of this code doesn't
627 * allow it. Lets just pray to God it doesn't happen.
629 if (item->touchscreen_data->slots[i].tracking_id < 0 &&
630 mt_req_values[i] >= 0) {
631 item->touchscreen_data->slots[i].tracking_id = mt_req_values[i];
632 item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
633 } else if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
634 mt_req_values[i] < 0) {
635 item->touchscreen_data->slots[i].tracking_id = -1;
636 item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_UP;
640 *mt_req_code = ABS_MT_POSITION_X;
641 ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
643 SDL_free(mt_req_code);
646 for(i = 0; i < item->touchscreen_data->max_slots; i++) {
647 if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
648 item->touchscreen_data->slots[i].x != mt_req_values[i]) {
649 item->touchscreen_data->slots[i].x = mt_req_values[i];
650 if (item->touchscreen_data->slots[i].delta ==
651 EVDEV_TOUCH_SLOTDELTA_NONE) {
652 item->touchscreen_data->slots[i].delta =
653 EVDEV_TOUCH_SLOTDELTA_MOVE;
658 *mt_req_code = ABS_MT_POSITION_Y;
659 ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
661 SDL_free(mt_req_code);
664 for(i = 0; i < item->touchscreen_data->max_slots; i++) {
665 if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
666 item->touchscreen_data->slots[i].y != mt_req_values[i]) {
667 item->touchscreen_data->slots[i].y = mt_req_values[i];
668 if (item->touchscreen_data->slots[i].delta ==
669 EVDEV_TOUCH_SLOTDELTA_NONE) {
670 item->touchscreen_data->slots[i].delta =
671 EVDEV_TOUCH_SLOTDELTA_MOVE;
676 *mt_req_code = ABS_MT_PRESSURE;
677 ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
679 SDL_free(mt_req_code);
682 for(i = 0; i < item->touchscreen_data->max_slots; i++) {
683 if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
684 item->touchscreen_data->slots[i].pressure != mt_req_values[i]) {
685 item->touchscreen_data->slots[i].pressure = mt_req_values[i];
686 if (item->touchscreen_data->slots[i].delta ==
687 EVDEV_TOUCH_SLOTDELTA_NONE) {
688 item->touchscreen_data->slots[i].delta =
689 EVDEV_TOUCH_SLOTDELTA_MOVE;
694 ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
696 SDL_free(mt_req_code);
699 item->touchscreen_data->current_slot = abs_info.value;
701 SDL_free(mt_req_code);
703 #endif /* EVIOCGMTSLOTS */
708 SDL_EVDEV_device_added(const char *dev_path, int udev_class)
711 SDL_evdevlist_item *item;
713 /* Check to make sure it's not already in list. */
714 for (item = _this->first; item != NULL; item = item->next) {
715 if (SDL_strcmp(dev_path, item->path) == 0) {
716 return -1; /* already have this one */
720 item = (SDL_evdevlist_item *) SDL_calloc(1, sizeof (SDL_evdevlist_item));
722 return SDL_OutOfMemory();
725 item->fd = open(dev_path, O_RDONLY | O_NONBLOCK);
728 return SDL_SetError("Unable to open %s", dev_path);
731 item->path = SDL_strdup(dev_path);
732 if (item->path == NULL) {
735 return SDL_OutOfMemory();
738 if (udev_class & SDL_UDEV_DEVICE_TOUCHSCREEN) {
739 item->is_touchscreen = 1;
741 if ((ret = SDL_EVDEV_init_touchscreen(item)) < 0) {
748 if (_this->last == NULL) {
749 _this->first = _this->last = item;
751 _this->last->next = item;
755 SDL_EVDEV_sync_device(item);
757 return _this->num_devices++;
759 #endif /* SDL_USE_LIBUDEV */
762 SDL_EVDEV_device_removed(const char *dev_path)
764 SDL_evdevlist_item *item;
765 SDL_evdevlist_item *prev = NULL;
767 for (item = _this->first; item != NULL; item = item->next) {
768 /* found it, remove it. */
769 if (SDL_strcmp(dev_path, item->path) == 0) {
771 prev->next = item->next;
773 SDL_assert(_this->first == item);
774 _this->first = item->next;
776 if (item == _this->last) {
779 if (item->is_touchscreen) {
780 SDL_EVDEV_destroy_touchscreen(item);
783 SDL_free(item->path);
785 _this->num_devices--;
795 #endif /* SDL_INPUT_LINUXEV */
797 /* vi: set ts=4 sw=4 expandtab: */