1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2021 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
61typedef 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
98typedef 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
109static _THIS = NULL;
110
111static SDL_Scancode SDL_EVDEV_translate_keycode(int keycode);
112static void SDL_EVDEV_sync_device(SDL_evdevlist_item *item);
113static int SDL_EVDEV_device_removed(const char *dev_path);
114
115#if SDL_USE_LIBUDEV
116static int SDL_EVDEV_device_added(const char *dev_path, int udev_class);
117static void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class,
118 const char *dev_path);
119#endif /* SDL_USE_LIBUDEV */
120
121static 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
132static int
133SDL_EVDEV_SetRelativeMouseMode(SDL_bool enabled)
134{
135 /* Mice already send relative events through this interface */
136 return 0;
137}
138
139
140int
141SDL_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
180void
181SDL_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
212static 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 if ((udev_class & SDL_UDEV_DEVICE_JOYSTICK))
226 return;
227
228 SDL_EVDEV_device_added(dev_path, udev_class);
229 break;
230 case SDL_UDEV_DEVICEREMOVED:
231 SDL_EVDEV_device_removed(dev_path);
232 break;
233 default:
234 break;
235 }
236}
237#endif /* SDL_USE_LIBUDEV */
238
239void
240SDL_EVDEV_Poll(void)
241{
242 struct input_event events[32];
243 int i, j, len;
244 SDL_evdevlist_item *item;
245 SDL_Scancode scan_code;
246 int mouse_button;
247 SDL_Mouse *mouse;
248 float norm_x, norm_y, norm_pressure;
249
250 if (!_this) {
251 return;
252 }
253
254#if SDL_USE_LIBUDEV
255 SDL_UDEV_Poll();
256#endif
257
258 mouse = SDL_GetMouse();
259
260 for (item = _this->first; item != NULL; item = item->next) {
261 while ((len = read(item->fd, events, (sizeof events))) > 0) {
262 len /= sizeof(events[0]);
263 for (i = 0; i < len; ++i) {
264 /* special handling for touchscreen, that should eventually be
265 used for all devices */
266 if (item->out_of_sync && item->is_touchscreen &&
267 events[i].type == EV_SYN && events[i].code != SYN_REPORT) {
268 break;
269 }
270
271 switch (events[i].type) {
272 case EV_KEY:
273 if (events[i].code >= BTN_MOUSE && events[i].code < BTN_MOUSE + SDL_arraysize(EVDEV_MouseButtons)) {
274 mouse_button = events[i].code - BTN_MOUSE;
275 if (events[i].value == 0) {
276 SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_RELEASED, EVDEV_MouseButtons[mouse_button]);
277 } else if (events[i].value == 1) {
278 SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_PRESSED, EVDEV_MouseButtons[mouse_button]);
279 }
280 break;
281 }
282
283 /* BTH_TOUCH event value 1 indicates there is contact with
284 a touchscreen or trackpad (earlist finger's current
285 position is sent in EV_ABS ABS_X/ABS_Y, switching to
286 next finger after earlist is released) */
287 if (item->is_touchscreen && events[i].code == BTN_TOUCH) {
288 if (item->touchscreen_data->max_slots == 1) {
289 if (events[i].value)
290 item->touchscreen_data->slots[0].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
291 else
292 item->touchscreen_data->slots[0].delta = EVDEV_TOUCH_SLOTDELTA_UP;
293 }
294 break;
295 }
296
297 /* Probably keyboard */
298 scan_code = SDL_EVDEV_translate_keycode(events[i].code);
299 if (scan_code != SDL_SCANCODE_UNKNOWN) {
300 if (events[i].value == 0) {
301 SDL_SendKeyboardKey(SDL_RELEASED, scan_code);
302 } else if (events[i].value == 1 || events[i].value == 2 /* key repeated */) {
303 SDL_SendKeyboardKey(SDL_PRESSED, scan_code);
304 }
305 }
306 SDL_EVDEV_kbd_keycode(_this->kbd, events[i].code, events[i].value);
307 break;
308 case EV_ABS:
309 switch(events[i].code) {
310 case ABS_MT_SLOT:
311 if (!item->is_touchscreen) /* FIXME: temp hack */
312 break;
313 item->touchscreen_data->current_slot = events[i].value;
314 break;
315 case ABS_MT_TRACKING_ID:
316 if (!item->is_touchscreen) /* FIXME: temp hack */
317 break;
318 if (events[i].value >= 0) {
319 item->touchscreen_data->slots[item->touchscreen_data->current_slot].tracking_id = events[i].value;
320 item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
321 } else {
322 item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_UP;
323 }
324 break;
325 case ABS_MT_POSITION_X:
326 if (!item->is_touchscreen) /* FIXME: temp hack */
327 break;
328 item->touchscreen_data->slots[item->touchscreen_data->current_slot].x = events[i].value;
329 if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
330 item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
331 }
332 break;
333 case ABS_MT_POSITION_Y:
334 if (!item->is_touchscreen) /* FIXME: temp hack */
335 break;
336 item->touchscreen_data->slots[item->touchscreen_data->current_slot].y = events[i].value;
337 if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
338 item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
339 }
340 break;
341 case ABS_MT_PRESSURE:
342 if (!item->is_touchscreen) /* FIXME: temp hack */
343 break;
344 item->touchscreen_data->slots[item->touchscreen_data->current_slot].pressure = events[i].value;
345 if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
346 item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
347 }
348 break;
349 case ABS_X:
350 if (item->is_touchscreen) {
351 if (item->touchscreen_data->max_slots != 1)
352 break;
353 item->touchscreen_data->slots[0].x = events[i].value;
354 } else
355 SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, events[i].value, mouse->y);
356 break;
357 case ABS_Y:
358 if (item->is_touchscreen) {
359 if (item->touchscreen_data->max_slots != 1)
360 break;
361 item->touchscreen_data->slots[0].y = events[i].value;
362 } else
363 SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, mouse->x, events[i].value);
364 break;
365 default:
366 break;
367 }
368 break;
369 case EV_REL:
370 switch(events[i].code) {
371 case REL_X:
372 SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, events[i].value, 0);
373 break;
374 case REL_Y:
375 SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, 0, events[i].value);
376 break;
377 case REL_WHEEL:
378 SDL_SendMouseWheel(mouse->focus, mouse->mouseID, 0, events[i].value, SDL_MOUSEWHEEL_NORMAL);
379 break;
380 case REL_HWHEEL:
381 SDL_SendMouseWheel(mouse->focus, mouse->mouseID, events[i].value, 0, SDL_MOUSEWHEEL_NORMAL);
382 break;
383 default:
384 break;
385 }
386 break;
387 case EV_SYN:
388 switch (events[i].code) {
389 case SYN_REPORT:
390 if (!item->is_touchscreen) /* FIXME: temp hack */
391 break;
392
393 for(j = 0; j < item->touchscreen_data->max_slots; j++) {
394 norm_x = (float)(item->touchscreen_data->slots[j].x - item->touchscreen_data->min_x) /
395 (float)item->touchscreen_data->range_x;
396 norm_y = (float)(item->touchscreen_data->slots[j].y - item->touchscreen_data->min_y) /
397 (float)item->touchscreen_data->range_y;
398
399 if (item->touchscreen_data->range_pressure > 0) {
400 norm_pressure = (float)(item->touchscreen_data->slots[j].pressure - item->touchscreen_data->min_pressure) /
401 (float)item->touchscreen_data->range_pressure;
402 } else {
403 /* This touchscreen does not support pressure */
404 norm_pressure = 1.0f;
405 }
406
407 /* FIXME: the touch's window shouldn't be null, but
408 * the coordinate space of touch positions needs to
409 * be window-relative in that case. */
410 switch(item->touchscreen_data->slots[j].delta) {
411 case EVDEV_TOUCH_SLOTDELTA_DOWN:
412 SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, SDL_TRUE, norm_x, norm_y, norm_pressure);
413 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
414 break;
415 case EVDEV_TOUCH_SLOTDELTA_UP:
416 SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, SDL_FALSE, norm_x, norm_y, norm_pressure);
417 item->touchscreen_data->slots[j].tracking_id = -1;
418 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
419 break;
420 case EVDEV_TOUCH_SLOTDELTA_MOVE:
421 SDL_SendTouchMotion(item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, norm_x, norm_y, norm_pressure);
422 item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
423 break;
424 default:
425 break;
426 }
427 }
428
429 if (item->out_of_sync)
430 item->out_of_sync = 0;
431 break;
432 case SYN_DROPPED:
433 if (item->is_touchscreen)
434 item->out_of_sync = 1;
435 SDL_EVDEV_sync_device(item);
436 break;
437 default:
438 break;
439 }
440 break;
441 }
442 }
443 }
444 }
445}
446
447static SDL_Scancode
448SDL_EVDEV_translate_keycode(int keycode)
449{
450 SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
451
452 if (keycode < SDL_arraysize(linux_scancode_table)) {
453 scancode = linux_scancode_table[keycode];
454
455 if (scancode == SDL_SCANCODE_UNKNOWN) {
456 /* BTN_TOUCH is handled elsewhere, but we might still end up here if
457 you get an unexpected BTN_TOUCH from something SDL believes is not
458 a touch device. In this case, we'd rather not get a misleading
459 SDL_Log message about an unknown key. */
460 if (keycode != BTN_TOUCH) {
461 SDL_Log("The key you just pressed is not recognized by SDL. To help "
462 "get this fixed, please report this to the SDL forums/mailing list "
463 "<https://discourse.libsdl.org/> EVDEV KeyCode %d", keycode);
464 }
465 }
466 }
467
468 return scancode;
469}
470
471#ifdef SDL_USE_LIBUDEV
472static int
473SDL_EVDEV_init_touchscreen(SDL_evdevlist_item* item)
474{
475 int ret, i;
476 unsigned long xreq, yreq;
477 char name[64];
478 struct input_absinfo abs_info;
479
480 if (!item->is_touchscreen)
481 return 0;
482
483 item->touchscreen_data = SDL_calloc(1, sizeof(*item->touchscreen_data));
484 if (item->touchscreen_data == NULL)
485 return SDL_OutOfMemory();
486
487 ret = ioctl(item->fd, EVIOCGNAME(sizeof(name)), name);
488 if (ret < 0) {
489 SDL_free(item->touchscreen_data);
490 return SDL_SetError("Failed to get evdev touchscreen name");
491 }
492
493 item->touchscreen_data->name = SDL_strdup(name);
494 if (item->touchscreen_data->name == NULL) {
495 SDL_free(item->touchscreen_data);
496 return SDL_OutOfMemory();
497 }
498
499 ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
500 if (ret < 0) {
501 SDL_free(item->touchscreen_data->name);
502 SDL_free(item->touchscreen_data);
503 return SDL_SetError("Failed to get evdev touchscreen limits");
504 }
505
506 if (abs_info.maximum == 0) {
507 item->touchscreen_data->max_slots = 1;
508 xreq = EVIOCGABS(ABS_X);
509 yreq = EVIOCGABS(ABS_Y);
510 } else {
511 item->touchscreen_data->max_slots = abs_info.maximum + 1;
512 xreq = EVIOCGABS(ABS_MT_POSITION_X);
513 yreq = EVIOCGABS(ABS_MT_POSITION_Y);
514 }
515
516 ret = ioctl(item->fd, xreq, &abs_info);
517 if (ret < 0) {
518 SDL_free(item->touchscreen_data->name);
519 SDL_free(item->touchscreen_data);
520 return SDL_SetError("Failed to get evdev touchscreen limits");
521 }
522 item->touchscreen_data->min_x = abs_info.minimum;
523 item->touchscreen_data->max_x = abs_info.maximum;
524 item->touchscreen_data->range_x = abs_info.maximum - abs_info.minimum;
525
526 ret = ioctl(item->fd, yreq, &abs_info);
527 if (ret < 0) {
528 SDL_free(item->touchscreen_data->name);
529 SDL_free(item->touchscreen_data);
530 return SDL_SetError("Failed to get evdev touchscreen limits");
531 }
532 item->touchscreen_data->min_y = abs_info.minimum;
533 item->touchscreen_data->max_y = abs_info.maximum;
534 item->touchscreen_data->range_y = abs_info.maximum - abs_info.minimum;
535
536 ret = ioctl(item->fd, EVIOCGABS(ABS_MT_PRESSURE), &abs_info);
537 if (ret < 0) {
538 SDL_free(item->touchscreen_data->name);
539 SDL_free(item->touchscreen_data);
540 return SDL_SetError("Failed to get evdev touchscreen limits");
541 }
542 item->touchscreen_data->min_pressure = abs_info.minimum;
543 item->touchscreen_data->max_pressure = abs_info.maximum;
544 item->touchscreen_data->range_pressure = abs_info.maximum - abs_info.minimum;
545
546 item->touchscreen_data->slots = SDL_calloc(
547 item->touchscreen_data->max_slots,
548 sizeof(*item->touchscreen_data->slots));
549 if (item->touchscreen_data->slots == NULL) {
550 SDL_free(item->touchscreen_data->name);
551 SDL_free(item->touchscreen_data);
552 return SDL_OutOfMemory();
553 }
554
555 for(i = 0; i < item->touchscreen_data->max_slots; i++) {
556 item->touchscreen_data->slots[i].tracking_id = -1;
557 }
558
559 ret = SDL_AddTouch(item->fd, /* I guess our fd is unique enough */
560 SDL_TOUCH_DEVICE_DIRECT,
561 item->touchscreen_data->name);
562 if (ret < 0) {
563 SDL_free(item->touchscreen_data->slots);
564 SDL_free(item->touchscreen_data->name);
565 SDL_free(item->touchscreen_data);
566 return ret;
567 }
568
569 return 0;
570}
571#endif /* SDL_USE_LIBUDEV */
572
573static void
574SDL_EVDEV_destroy_touchscreen(SDL_evdevlist_item* item) {
575 if (!item->is_touchscreen)
576 return;
577
578 SDL_DelTouch(item->fd);
579 SDL_free(item->touchscreen_data->slots);
580 SDL_free(item->touchscreen_data->name);
581 SDL_free(item->touchscreen_data);
582}
583
584static void
585SDL_EVDEV_sync_device(SDL_evdevlist_item *item)
586{
587#ifdef EVIOCGMTSLOTS
588 int i, ret;
589 struct input_absinfo abs_info;
590 /*
591 * struct input_mt_request_layout {
592 * __u32 code;
593 * __s32 values[num_slots];
594 * };
595 *
596 * this is the structure we're trying to emulate
597 */
598 Uint32* mt_req_code;
599 Sint32* mt_req_values;
600 size_t mt_req_size;
601
602 /* TODO: sync devices other than touchscreen */
603 if (!item->is_touchscreen)
604 return;
605
606 mt_req_size = sizeof(*mt_req_code) +
607 sizeof(*mt_req_values) * item->touchscreen_data->max_slots;
608
609 mt_req_code = SDL_calloc(1, mt_req_size);
610 if (mt_req_code == NULL) {
611 return;
612 }
613
614 mt_req_values = (Sint32*)mt_req_code + 1;
615
616 *mt_req_code = ABS_MT_TRACKING_ID;
617 ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
618 if (ret < 0) {
619 SDL_free(mt_req_code);
620 return;
621 }
622 for(i = 0; i < item->touchscreen_data->max_slots; i++) {
623 /*
624 * This doesn't account for the very edge case of the user removing their
625 * finger and replacing it on the screen during the time we're out of sync,
626 * which'll mean that we're not going from down -> up or up -> down, we're
627 * going from down -> down but with a different tracking id, meaning we'd
628 * have to tell SDL of the two events, but since we wait till SYN_REPORT in
629 * SDL_EVDEV_Poll to tell SDL, the current structure of this code doesn't
630 * allow it. Lets just pray to God it doesn't happen.
631 */
632 if (item->touchscreen_data->slots[i].tracking_id < 0 &&
633 mt_req_values[i] >= 0) {
634 item->touchscreen_data->slots[i].tracking_id = mt_req_values[i];
635 item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
636 } else if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
637 mt_req_values[i] < 0) {
638 item->touchscreen_data->slots[i].tracking_id = -1;
639 item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_UP;
640 }
641 }
642
643 *mt_req_code = ABS_MT_POSITION_X;
644 ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
645 if (ret < 0) {
646 SDL_free(mt_req_code);
647 return;
648 }
649 for(i = 0; i < item->touchscreen_data->max_slots; i++) {
650 if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
651 item->touchscreen_data->slots[i].x != mt_req_values[i]) {
652 item->touchscreen_data->slots[i].x = mt_req_values[i];
653 if (item->touchscreen_data->slots[i].delta ==
654 EVDEV_TOUCH_SLOTDELTA_NONE) {
655 item->touchscreen_data->slots[i].delta =
656 EVDEV_TOUCH_SLOTDELTA_MOVE;
657 }
658 }
659 }
660
661 *mt_req_code = ABS_MT_POSITION_Y;
662 ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
663 if (ret < 0) {
664 SDL_free(mt_req_code);
665 return;
666 }
667 for(i = 0; i < item->touchscreen_data->max_slots; i++) {
668 if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
669 item->touchscreen_data->slots[i].y != mt_req_values[i]) {
670 item->touchscreen_data->slots[i].y = mt_req_values[i];
671 if (item->touchscreen_data->slots[i].delta ==
672 EVDEV_TOUCH_SLOTDELTA_NONE) {
673 item->touchscreen_data->slots[i].delta =
674 EVDEV_TOUCH_SLOTDELTA_MOVE;
675 }
676 }
677 }
678
679 *mt_req_code = ABS_MT_PRESSURE;
680 ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
681 if (ret < 0) {
682 SDL_free(mt_req_code);
683 return;
684 }
685 for(i = 0; i < item->touchscreen_data->max_slots; i++) {
686 if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
687 item->touchscreen_data->slots[i].pressure != mt_req_values[i]) {
688 item->touchscreen_data->slots[i].pressure = mt_req_values[i];
689 if (item->touchscreen_data->slots[i].delta ==
690 EVDEV_TOUCH_SLOTDELTA_NONE) {
691 item->touchscreen_data->slots[i].delta =
692 EVDEV_TOUCH_SLOTDELTA_MOVE;
693 }
694 }
695 }
696
697 ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
698 if (ret < 0) {
699 SDL_free(mt_req_code);
700 return;
701 }
702 item->touchscreen_data->current_slot = abs_info.value;
703
704 SDL_free(mt_req_code);
705
706#endif /* EVIOCGMTSLOTS */
707}
708
709#if SDL_USE_LIBUDEV
710static int
711SDL_EVDEV_device_added(const char *dev_path, int udev_class)
712{
713 int ret;
714 SDL_evdevlist_item *item;
715
716 /* Check to make sure it's not already in list. */
717 for (item = _this->first; item != NULL; item = item->next) {
718 if (SDL_strcmp(dev_path, item->path) == 0) {
719 return -1; /* already have this one */
720 }
721 }
722
723 item = (SDL_evdevlist_item *) SDL_calloc(1, sizeof (SDL_evdevlist_item));
724 if (item == NULL) {
725 return SDL_OutOfMemory();
726 }
727
728 item->fd = open(dev_path, O_RDONLY | O_NONBLOCK);
729 if (item->fd < 0) {
730 SDL_free(item);
731 return SDL_SetError("Unable to open %s", dev_path);
732 }
733
734 item->path = SDL_strdup(dev_path);
735 if (item->path == NULL) {
736 close(item->fd);
737 SDL_free(item);
738 return SDL_OutOfMemory();
739 }
740
741 if (udev_class & SDL_UDEV_DEVICE_TOUCHSCREEN) {
742 item->is_touchscreen = 1;
743
744 if ((ret = SDL_EVDEV_init_touchscreen(item)) < 0) {
745 close(item->fd);
746 SDL_free(item);
747 return ret;
748 }
749 }
750
751 if (_this->last == NULL) {
752 _this->first = _this->last = item;
753 } else {
754 _this->last->next = item;
755 _this->last = item;
756 }
757
758 SDL_EVDEV_sync_device(item);
759
760 return _this->num_devices++;
761}
762#endif /* SDL_USE_LIBUDEV */
763
764static int
765SDL_EVDEV_device_removed(const char *dev_path)
766{
767 SDL_evdevlist_item *item;
768 SDL_evdevlist_item *prev = NULL;
769
770 for (item = _this->first; item != NULL; item = item->next) {
771 /* found it, remove it. */
772 if (SDL_strcmp(dev_path, item->path) == 0) {
773 if (prev != NULL) {
774 prev->next = item->next;
775 } else {
776 SDL_assert(_this->first == item);
777 _this->first = item->next;
778 }
779 if (item == _this->last) {
780 _this->last = prev;
781 }
782 if (item->is_touchscreen) {
783 SDL_EVDEV_destroy_touchscreen(item);
784 }
785 close(item->fd);
786 SDL_free(item->path);
787 SDL_free(item);
788 _this->num_devices--;
789 return 0;
790 }
791 prev = item;
792 }
793
794 return -1;
795}
796
797
798#endif /* SDL_INPUT_LINUXEV */
799
800/* vi: set ts=4 sw=4 expandtab: */
801