SDL_evdev.c (28942B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> 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 "SDL.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" 48 49 /* These are not defined in older Linux kernel headers */ 50 #ifndef SYN_DROPPED 51 #define SYN_DROPPED 3 52 #endif 53 #ifndef ABS_MT_SLOT 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 59 #endif 60 61 typedef struct SDL_evdevlist_item 62 { 63 char *path; 64 int fd; 65 66 /* TODO: use this for every device, not just touchscreen */ 67 int out_of_sync; 68 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 */ 72 int is_touchscreen; 73 struct { 74 char* name; 75 76 int min_x, max_x, range_x; 77 int min_y, max_y, range_y; 78 int min_pressure, max_pressure, range_pressure; 79 80 int max_slots; 81 int current_slot; 82 struct { 83 enum { 84 EVDEV_TOUCH_SLOTDELTA_NONE = 0, 85 EVDEV_TOUCH_SLOTDELTA_DOWN, 86 EVDEV_TOUCH_SLOTDELTA_UP, 87 EVDEV_TOUCH_SLOTDELTA_MOVE 88 } delta; 89 int tracking_id; 90 int x, y, pressure; 91 } * slots; 92 93 } * touchscreen_data; 94 95 struct SDL_evdevlist_item *next; 96 } SDL_evdevlist_item; 97 98 typedef struct SDL_EVDEV_PrivateData 99 { 100 int ref_count; 101 int num_devices; 102 SDL_evdevlist_item *first; 103 SDL_evdevlist_item *last; 104 SDL_EVDEV_keyboard_state *kbd; 105 } SDL_EVDEV_PrivateData; 106 107 #undef _THIS 108 #define _THIS SDL_EVDEV_PrivateData *_this 109 static _THIS = NULL; 110 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); 114 115 #if SDL_USE_LIBUDEV 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 */ 120 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 */ 130 }; 131 132 static int 133 SDL_EVDEV_SetRelativeMouseMode(SDL_bool enabled) 134 { 135 /* Mice already send relative events through this interface */ 136 return 0; 137 } 138 139 140 int 141 SDL_EVDEV_Init(void) 142 { 143 if (_this == NULL) { 144 _this = (SDL_EVDEV_PrivateData*)SDL_calloc(1, sizeof(*_this)); 145 if (_this == NULL) { 146 return SDL_OutOfMemory(); 147 } 148 149 #if SDL_USE_LIBUDEV 150 if (SDL_UDEV_Init() < 0) { 151 SDL_free(_this); 152 _this = NULL; 153 return -1; 154 } 155 156 /* Set up the udev callback */ 157 if (SDL_UDEV_AddCallback(SDL_EVDEV_udev_callback) < 0) { 158 SDL_UDEV_Quit(); 159 SDL_free(_this); 160 _this = NULL; 161 return -1; 162 } 163 164 /* Force a scan to build the initial device list */ 165 SDL_UDEV_Scan(); 166 #else 167 /* TODO: Scan the devices manually, like a caveman */ 168 #endif /* SDL_USE_LIBUDEV */ 169 170 _this->kbd = SDL_EVDEV_kbd_init(); 171 } 172 173 SDL_GetMouse()->SetRelativeMouseMode = SDL_EVDEV_SetRelativeMouseMode; 174 175 _this->ref_count += 1; 176 177 return 0; 178 } 179 180 void 181 SDL_EVDEV_Quit(void) 182 { 183 if (_this == NULL) { 184 return; 185 } 186 187 _this->ref_count -= 1; 188 189 if (_this->ref_count < 1) { 190 #if SDL_USE_LIBUDEV 191 SDL_UDEV_DelCallback(SDL_EVDEV_udev_callback); 192 SDL_UDEV_Quit(); 193 #endif /* SDL_USE_LIBUDEV */ 194 195 SDL_EVDEV_kbd_quit(_this->kbd); 196 197 /* Remove existing devices */ 198 while(_this->first != NULL) { 199 SDL_EVDEV_device_removed(_this->first->path); 200 } 201 202 SDL_assert(_this->first == NULL); 203 SDL_assert(_this->last == NULL); 204 SDL_assert(_this->num_devices == 0); 205 206 SDL_free(_this); 207 _this = NULL; 208 } 209 } 210 211 #if SDL_USE_LIBUDEV 212 static void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_event, int udev_class, 213 const char* dev_path) 214 { 215 if (dev_path == NULL) { 216 return; 217 } 218 219 switch(udev_event) { 220 case SDL_UDEV_DEVICEADDED: 221 if (!(udev_class & (SDL_UDEV_DEVICE_MOUSE | SDL_UDEV_DEVICE_KEYBOARD | 222 SDL_UDEV_DEVICE_TOUCHSCREEN))) 223 return; 224 225 SDL_EVDEV_device_added(dev_path, udev_class); 226 break; 227 case SDL_UDEV_DEVICEREMOVED: 228 SDL_EVDEV_device_removed(dev_path); 229 break; 230 default: 231 break; 232 } 233 } 234 #endif /* SDL_USE_LIBUDEV */ 235 236 void 237 SDL_EVDEV_Poll(void) 238 { 239 struct input_event events[32]; 240 int i, j, len; 241 SDL_evdevlist_item *item; 242 SDL_Scancode scan_code; 243 int mouse_button; 244 SDL_Mouse *mouse; 245 float norm_x, norm_y, norm_pressure; 246 247 if (!_this) { 248 return; 249 } 250 251 #if SDL_USE_LIBUDEV 252 SDL_UDEV_Poll(); 253 #endif 254 255 mouse = SDL_GetMouse(); 256 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) { 265 break; 266 } 267 268 switch (events[i].type) { 269 case EV_KEY: 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]); 276 } 277 break; 278 } 279 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) { 286 if (events[i].value) 287 item->touchscreen_data->slots[0].delta = EVDEV_TOUCH_SLOTDELTA_DOWN; 288 else 289 item->touchscreen_data->slots[0].delta = EVDEV_TOUCH_SLOTDELTA_UP; 290 } 291 break; 292 } 293 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); 301 } 302 } 303 SDL_EVDEV_kbd_keycode(_this->kbd, events[i].code, events[i].value); 304 break; 305 case EV_ABS: 306 switch(events[i].code) { 307 case ABS_MT_SLOT: 308 if (!item->is_touchscreen) /* FIXME: temp hack */ 309 break; 310 item->touchscreen_data->current_slot = events[i].value; 311 break; 312 case ABS_MT_TRACKING_ID: 313 if (!item->is_touchscreen) /* FIXME: temp hack */ 314 break; 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; 318 } else { 319 item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_UP; 320 } 321 break; 322 case ABS_MT_POSITION_X: 323 if (!item->is_touchscreen) /* FIXME: temp hack */ 324 break; 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; 328 } 329 break; 330 case ABS_MT_POSITION_Y: 331 if (!item->is_touchscreen) /* FIXME: temp hack */ 332 break; 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; 336 } 337 break; 338 case ABS_MT_PRESSURE: 339 if (!item->is_touchscreen) /* FIXME: temp hack */ 340 break; 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; 344 } 345 break; 346 case ABS_X: 347 if (item->is_touchscreen) { 348 if (item->touchscreen_data->max_slots != 1) 349 break; 350 item->touchscreen_data->slots[0].x = events[i].value; 351 } else 352 SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, events[i].value, mouse->y); 353 break; 354 case ABS_Y: 355 if (item->is_touchscreen) { 356 if (item->touchscreen_data->max_slots != 1) 357 break; 358 item->touchscreen_data->slots[0].y = events[i].value; 359 } else 360 SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, mouse->x, events[i].value); 361 break; 362 default: 363 break; 364 } 365 break; 366 case EV_REL: 367 switch(events[i].code) { 368 case REL_X: 369 SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, events[i].value, 0); 370 break; 371 case REL_Y: 372 SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, 0, events[i].value); 373 break; 374 case REL_WHEEL: 375 SDL_SendMouseWheel(mouse->focus, mouse->mouseID, 0, events[i].value, SDL_MOUSEWHEEL_NORMAL); 376 break; 377 case REL_HWHEEL: 378 SDL_SendMouseWheel(mouse->focus, mouse->mouseID, events[i].value, 0, SDL_MOUSEWHEEL_NORMAL); 379 break; 380 default: 381 break; 382 } 383 break; 384 case EV_SYN: 385 switch (events[i].code) { 386 case SYN_REPORT: 387 if (!item->is_touchscreen) /* FIXME: temp hack */ 388 break; 389 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; 395 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; 399 } else { 400 /* This touchscreen does not support pressure */ 401 norm_pressure = 1.0f; 402 } 403 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; 411 break; 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; 416 break; 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; 420 break; 421 default: 422 break; 423 } 424 } 425 426 if (item->out_of_sync) 427 item->out_of_sync = 0; 428 break; 429 case SYN_DROPPED: 430 if (item->is_touchscreen) 431 item->out_of_sync = 1; 432 SDL_EVDEV_sync_device(item); 433 break; 434 default: 435 break; 436 } 437 break; 438 } 439 } 440 } 441 } 442 } 443 444 static SDL_Scancode 445 SDL_EVDEV_translate_keycode(int keycode) 446 { 447 SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN; 448 449 if (keycode < SDL_arraysize(linux_scancode_table)) { 450 scancode = linux_scancode_table[keycode]; 451 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); 461 } 462 } 463 } 464 465 return scancode; 466 } 467 468 #ifdef SDL_USE_LIBUDEV 469 static int 470 SDL_EVDEV_init_touchscreen(SDL_evdevlist_item* item) 471 { 472 int ret, i; 473 unsigned long xreq, yreq; 474 char name[64]; 475 struct input_absinfo abs_info; 476 477 if (!item->is_touchscreen) 478 return 0; 479 480 item->touchscreen_data = SDL_calloc(1, sizeof(*item->touchscreen_data)); 481 if (item->touchscreen_data == NULL) 482 return SDL_OutOfMemory(); 483 484 ret = ioctl(item->fd, EVIOCGNAME(sizeof(name)), name); 485 if (ret < 0) { 486 SDL_free(item->touchscreen_data); 487 return SDL_SetError("Failed to get evdev touchscreen name"); 488 } 489 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(); 494 } 495 496 ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info); 497 if (ret < 0) { 498 SDL_free(item->touchscreen_data->name); 499 SDL_free(item->touchscreen_data); 500 return SDL_SetError("Failed to get evdev touchscreen limits"); 501 } 502 503 if (abs_info.maximum == 0) { 504 item->touchscreen_data->max_slots = 1; 505 xreq = EVIOCGABS(ABS_X); 506 yreq = EVIOCGABS(ABS_Y); 507 } else { 508 item->touchscreen_data->max_slots = abs_info.maximum + 1; 509 xreq = EVIOCGABS(ABS_MT_POSITION_X); 510 yreq = EVIOCGABS(ABS_MT_POSITION_Y); 511 } 512 513 ret = ioctl(item->fd, xreq, &abs_info); 514 if (ret < 0) { 515 SDL_free(item->touchscreen_data->name); 516 SDL_free(item->touchscreen_data); 517 return SDL_SetError("Failed to get evdev touchscreen limits"); 518 } 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; 522 523 ret = ioctl(item->fd, yreq, &abs_info); 524 if (ret < 0) { 525 SDL_free(item->touchscreen_data->name); 526 SDL_free(item->touchscreen_data); 527 return SDL_SetError("Failed to get evdev touchscreen limits"); 528 } 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; 532 533 ret = ioctl(item->fd, EVIOCGABS(ABS_MT_PRESSURE), &abs_info); 534 if (ret < 0) { 535 SDL_free(item->touchscreen_data->name); 536 SDL_free(item->touchscreen_data); 537 return SDL_SetError("Failed to get evdev touchscreen limits"); 538 } 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; 542 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(); 550 } 551 552 for(i = 0; i < item->touchscreen_data->max_slots; i++) { 553 item->touchscreen_data->slots[i].tracking_id = -1; 554 } 555 556 ret = SDL_AddTouch(item->fd, /* I guess our fd is unique enough */ 557 SDL_TOUCH_DEVICE_DIRECT, 558 item->touchscreen_data->name); 559 if (ret < 0) { 560 SDL_free(item->touchscreen_data->slots); 561 SDL_free(item->touchscreen_data->name); 562 SDL_free(item->touchscreen_data); 563 return ret; 564 } 565 566 return 0; 567 } 568 #endif /* SDL_USE_LIBUDEV */ 569 570 static void 571 SDL_EVDEV_destroy_touchscreen(SDL_evdevlist_item* item) { 572 if (!item->is_touchscreen) 573 return; 574 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); 579 } 580 581 static void 582 SDL_EVDEV_sync_device(SDL_evdevlist_item *item) 583 { 584 #ifdef EVIOCGMTSLOTS 585 int i, ret; 586 struct input_absinfo abs_info; 587 /* 588 * struct input_mt_request_layout { 589 * __u32 code; 590 * __s32 values[num_slots]; 591 * }; 592 * 593 * this is the structure we're trying to emulate 594 */ 595 Uint32* mt_req_code; 596 Sint32* mt_req_values; 597 size_t mt_req_size; 598 599 /* TODO: sync devices other than touchscreen */ 600 if (!item->is_touchscreen) 601 return; 602 603 mt_req_size = sizeof(*mt_req_code) + 604 sizeof(*mt_req_values) * item->touchscreen_data->max_slots; 605 606 mt_req_code = SDL_calloc(1, mt_req_size); 607 if (mt_req_code == NULL) { 608 return; 609 } 610 611 mt_req_values = (Sint32*)mt_req_code + 1; 612 613 *mt_req_code = ABS_MT_TRACKING_ID; 614 ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code); 615 if (ret < 0) { 616 SDL_free(mt_req_code); 617 return; 618 } 619 for(i = 0; i < item->touchscreen_data->max_slots; i++) { 620 /* 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. 628 */ 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; 637 } 638 } 639 640 *mt_req_code = ABS_MT_POSITION_X; 641 ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code); 642 if (ret < 0) { 643 SDL_free(mt_req_code); 644 return; 645 } 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; 654 } 655 } 656 } 657 658 *mt_req_code = ABS_MT_POSITION_Y; 659 ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code); 660 if (ret < 0) { 661 SDL_free(mt_req_code); 662 return; 663 } 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; 672 } 673 } 674 } 675 676 *mt_req_code = ABS_MT_PRESSURE; 677 ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code); 678 if (ret < 0) { 679 SDL_free(mt_req_code); 680 return; 681 } 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; 690 } 691 } 692 } 693 694 ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info); 695 if (ret < 0) { 696 SDL_free(mt_req_code); 697 return; 698 } 699 item->touchscreen_data->current_slot = abs_info.value; 700 701 SDL_free(mt_req_code); 702 703 #endif /* EVIOCGMTSLOTS */ 704 } 705 706 #if SDL_USE_LIBUDEV 707 static int 708 SDL_EVDEV_device_added(const char *dev_path, int udev_class) 709 { 710 int ret; 711 SDL_evdevlist_item *item; 712 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 */ 717 } 718 } 719 720 item = (SDL_evdevlist_item *) SDL_calloc(1, sizeof (SDL_evdevlist_item)); 721 if (item == NULL) { 722 return SDL_OutOfMemory(); 723 } 724 725 item->fd = open(dev_path, O_RDONLY | O_NONBLOCK); 726 if (item->fd < 0) { 727 SDL_free(item); 728 return SDL_SetError("Unable to open %s", dev_path); 729 } 730 731 item->path = SDL_strdup(dev_path); 732 if (item->path == NULL) { 733 close(item->fd); 734 SDL_free(item); 735 return SDL_OutOfMemory(); 736 } 737 738 if (udev_class & SDL_UDEV_DEVICE_TOUCHSCREEN) { 739 item->is_touchscreen = 1; 740 741 if ((ret = SDL_EVDEV_init_touchscreen(item)) < 0) { 742 close(item->fd); 743 SDL_free(item); 744 return ret; 745 } 746 } 747 748 if (_this->last == NULL) { 749 _this->first = _this->last = item; 750 } else { 751 _this->last->next = item; 752 _this->last = item; 753 } 754 755 SDL_EVDEV_sync_device(item); 756 757 return _this->num_devices++; 758 } 759 #endif /* SDL_USE_LIBUDEV */ 760 761 static int 762 SDL_EVDEV_device_removed(const char *dev_path) 763 { 764 SDL_evdevlist_item *item; 765 SDL_evdevlist_item *prev = NULL; 766 767 for (item = _this->first; item != NULL; item = item->next) { 768 /* found it, remove it. */ 769 if (SDL_strcmp(dev_path, item->path) == 0) { 770 if (prev != NULL) { 771 prev->next = item->next; 772 } else { 773 SDL_assert(_this->first == item); 774 _this->first = item->next; 775 } 776 if (item == _this->last) { 777 _this->last = prev; 778 } 779 if (item->is_touchscreen) { 780 SDL_EVDEV_destroy_touchscreen(item); 781 } 782 close(item->fd); 783 SDL_free(item->path); 784 SDL_free(item); 785 _this->num_devices--; 786 return 0; 787 } 788 prev = item; 789 } 790 791 return -1; 792 } 793 794 795 #endif /* SDL_INPUT_LINUXEV */ 796 797 /* vi: set ts=4 sw=4 expandtab: */