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_assert.h"
43 #include "SDL_endian.h"
44 #include "SDL_scancode.h"
45 #include "../../events/SDL_events_c.h"
46 #include "../../events/scancodes_linux.h" /* adds linux_scancode_table */
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
SDL_EVDEV_SetRelativeMouseMode(SDL_bool enabled)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
SDL_EVDEV_Init(void)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
SDL_EVDEV_Quit(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
SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_event,int udev_class,const char * dev_path)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
SDL_EVDEV_Poll(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
SDL_EVDEV_translate_keycode(int keycode)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
SDL_EVDEV_init_touchscreen(SDL_evdevlist_item * item)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
SDL_EVDEV_destroy_touchscreen(SDL_evdevlist_item * item)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
SDL_EVDEV_sync_device(SDL_evdevlist_item * item)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
SDL_EVDEV_device_added(const char * dev_path,int udev_class)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
SDL_EVDEV_device_removed(const char * dev_path)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: */
798