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