SDL  2.0
SDL_evdev.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2016 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 
34 #include <sys/stat.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <sys/ioctl.h>
38 #include <limits.h> /* For the definition of PATH_MAX */
39 #include <linux/input.h>
40 #ifdef SDL_INPUT_LINUXKD
41 #include <linux/kd.h>
42 #include <linux/keyboard.h>
43 #include <linux/vt.h>
44 #include <linux/tiocl.h> /* for TIOCL_GETSHIFTSTATE */
45 #endif
46 
47 #include "SDL.h"
48 #include "SDL_assert.h"
49 #include "SDL_endian.h"
50 #include "../../core/linux/SDL_udev.h"
51 #include "SDL_scancode.h"
52 #include "../../events/SDL_events_c.h"
53 #include "../../events/scancodes_linux.h" /* adds linux_scancode_table */
54 
55 /* This isn't defined in older Linux kernel headers */
56 #ifndef SYN_DROPPED
57 #define SYN_DROPPED 3
58 #endif
59 
60 typedef struct SDL_evdevlist_item
61 {
62  char *path;
63  int fd;
64 
65  /* TODO: use this for every device, not just touchscreen */
66  int out_of_sync;
67 
68  /* TODO: expand on this to have data for every possible class (mouse,
69  keyboard, touchpad, etc.). Also there's probably some things in here we
70  can pull out to the SDL_evdevlist_item i.e. name */
71  int is_touchscreen;
72  struct {
73  char* name;
74 
75  int min_x, max_x, range_x;
76  int min_y, max_y, range_y;
77 
78  int max_slots;
79  int current_slot;
80  struct {
81  enum {
82  EVDEV_TOUCH_SLOTDELTA_NONE = 0,
83  EVDEV_TOUCH_SLOTDELTA_DOWN,
84  EVDEV_TOUCH_SLOTDELTA_UP,
85  EVDEV_TOUCH_SLOTDELTA_MOVE
86  } delta;
87  int tracking_id;
88  int x, y;
89  } * slots;
90  } * touchscreen_data;
91 
92  struct SDL_evdevlist_item *next;
93 } SDL_evdevlist_item;
94 
95 typedef struct SDL_EVDEV_PrivateData
96 {
97  SDL_evdevlist_item *first;
98  SDL_evdevlist_item *last;
99  int num_devices;
100  int ref_count;
101  int console_fd;
102  int kb_mode;
103 } SDL_EVDEV_PrivateData;
104 
105 #define _THIS SDL_EVDEV_PrivateData *_this
106 static _THIS = NULL;
107 
108 static SDL_Scancode SDL_EVDEV_translate_keycode(int keycode);
109 static void SDL_EVDEV_sync_device(SDL_evdevlist_item *item);
110 static int SDL_EVDEV_device_removed(const char *dev_path);
111 
112 #if SDL_USE_LIBUDEV
113 static int SDL_EVDEV_device_added(const char *dev_path, int udev_class);
114 void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class,
115  const char *dev_path);
116 #endif /* SDL_USE_LIBUDEV */
117 
118 static Uint8 EVDEV_MouseButtons[] = {
119  SDL_BUTTON_LEFT, /* BTN_LEFT 0x110 */
120  SDL_BUTTON_RIGHT, /* BTN_RIGHT 0x111 */
121  SDL_BUTTON_MIDDLE, /* BTN_MIDDLE 0x112 */
122  SDL_BUTTON_X1, /* BTN_SIDE 0x113 */
123  SDL_BUTTON_X2, /* BTN_EXTRA 0x114 */
124  SDL_BUTTON_X2 + 1, /* BTN_FORWARD 0x115 */
125  SDL_BUTTON_X2 + 2, /* BTN_BACK 0x116 */
126  SDL_BUTTON_X2 + 3 /* BTN_TASK 0x117 */
127 };
128 
129 static const char* EVDEV_consoles[] = {
130  /* "/proc/self/fd/0",
131  "/dev/tty",
132  "/dev/tty0", */ /* the tty ioctl's prohibit these */
133  "/dev/tty1",
134  "/dev/tty2",
135  "/dev/tty3",
136  "/dev/tty4",
137  "/dev/tty5",
138  "/dev/tty6",
139  "/dev/tty7", /* usually X is spawned in tty7 */
140  "/dev/vc/0",
141  "/dev/console"
142 };
143 
144 static int SDL_EVDEV_is_console(int fd) {
145  int type;
146 
147  return isatty(fd) && ioctl(fd, KDGKBTYPE, &type) == 0 &&
148  (type == KB_101 || type == KB_84);
149 }
150 
151 /* Prevent keystrokes from reaching the tty */
152 static int SDL_EVDEV_mute_keyboard(int tty_fd, int* old_kb_mode)
153 {
154  if (!SDL_EVDEV_is_console(tty_fd)) {
155  return SDL_SetError("Tried to mute an invalid tty");
156  }
157 
158  if (ioctl(tty_fd, KDGKBMODE, old_kb_mode) < 0) {
159  return SDL_SetError("Failed to get keyboard mode during muting");
160  }
161 
162  /* FIXME: atm this absolutely ruins the vt, and KDSKBMUTE isn't implemented
163  in the kernel */
164  /*
165  if (ioctl(tty_fd, KDSKBMODE, K_OFF) < 0) {
166  return SDL_SetError("Failed to set keyboard mode during muting");
167  }
168  */
169 
170  return 0;
171 }
172 
173 /* Restore the keyboard mode for given tty */
174 static void SDL_EVDEV_unmute_keyboard(int tty_fd, int kb_mode)
175 {
176  /* read above */
177  /*
178  if (ioctl(tty_fd, KDSKBMODE, kb_mode) < 0) {
179  SDL_Log("Failed to unmute keyboard");
180  }
181  */
182 }
183 
184 static int SDL_EVDEV_get_active_tty()
185 {
186  int i, fd, ret, tty = 0;
187  char tiocl;
188  struct vt_stat vt_state;
189  char path[PATH_MAX + 1];
190 
191  for(i = 0; i < SDL_arraysize(EVDEV_consoles); i++) {
192  fd = open(EVDEV_consoles[i], O_RDONLY);
193 
194  if (fd < 0 && !SDL_EVDEV_is_console(fd))
195  break;
196 
197  tiocl = TIOCL_GETFGCONSOLE;
198  if ((ret = ioctl(fd, TIOCLINUX, &tiocl)) >= 0)
199  tty = ret + 1;
200  else if (ioctl(fd, VT_GETSTATE, &vt_state) == 0)
201  tty = vt_state.v_active;
202 
203  close(fd);
204 
205  if (tty) {
206  sprintf(path, "/dev/tty%u", tty);
207  fd = open(path, O_RDONLY);
208  if (fd >= 0 && SDL_EVDEV_is_console(fd))
209  return fd;
210  }
211  }
212 
213  return SDL_SetError("Failed to determine active tty");
214 }
215 
216 int
217 SDL_EVDEV_Init(void)
218 {
219  if (_this == NULL) {
220  _this = (SDL_EVDEV_PrivateData*)SDL_calloc(1, sizeof(*_this));
221  if (_this == NULL) {
222  return SDL_OutOfMemory();
223  }
224 
225 #if SDL_USE_LIBUDEV
226  if (SDL_UDEV_Init() < 0) {
227  SDL_free(_this);
228  _this = NULL;
229  return -1;
230  }
231 
232  /* Set up the udev callback */
233  if (SDL_UDEV_AddCallback(SDL_EVDEV_udev_callback) < 0) {
234  SDL_UDEV_Quit();
235  SDL_free(_this);
236  _this = NULL;
237  return -1;
238  }
239 
240  /* Force a scan to build the initial device list */
241  SDL_UDEV_Scan();
242 #else
243  /* TODO: Scan the devices manually, like a caveman */
244 #endif /* SDL_USE_LIBUDEV */
245 
246  /* We need a physical terminal (not PTS) to be able to translate key
247  code to symbols via the kernel tables */
248  _this->console_fd = SDL_EVDEV_get_active_tty();
249 
250  /* Mute the keyboard so keystrokes only generate evdev events and do not
251  leak through to the console */
252  SDL_EVDEV_mute_keyboard(_this->console_fd, &_this->kb_mode);
253  }
254 
255  _this->ref_count += 1;
256 
257  return 0;
258 }
259 
260 void
261 SDL_EVDEV_Quit(void)
262 {
263  if (_this == NULL) {
264  return;
265  }
266 
267  _this->ref_count -= 1;
268 
269  if (_this->ref_count < 1) {
270 #if SDL_USE_LIBUDEV
271  SDL_UDEV_DelCallback(SDL_EVDEV_udev_callback);
272  SDL_UDEV_Quit();
273 #endif /* SDL_USE_LIBUDEV */
274 
275  if (_this->console_fd >= 0) {
276  SDL_EVDEV_unmute_keyboard(_this->console_fd, _this->kb_mode);
277  close(_this->console_fd);
278  }
279 
280  /* Remove existing devices */
281  while(_this->first != NULL) {
282  SDL_EVDEV_device_removed(_this->first->path);
283  }
284 
285  SDL_assert(_this->first == NULL);
286  SDL_assert(_this->last == NULL);
287  SDL_assert(_this->num_devices == 0);
288 
289  SDL_free(_this);
290  _this = NULL;
291  }
292 }
293 
294 #if SDL_USE_LIBUDEV
295 void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_event, int udev_class,
296  const char* dev_path)
297 {
298  if (dev_path == NULL) {
299  return;
300  }
301 
302  switch(udev_event) {
303  case SDL_UDEV_DEVICEADDED:
304  if (!(udev_class & (SDL_UDEV_DEVICE_MOUSE | SDL_UDEV_DEVICE_KEYBOARD |
305  SDL_UDEV_DEVICE_TOUCHSCREEN)))
306  return;
307 
308  SDL_EVDEV_device_added(dev_path, udev_class);
309  break;
310  case SDL_UDEV_DEVICEREMOVED:
311  SDL_EVDEV_device_removed(dev_path);
312  break;
313  default:
314  break;
315  }
316 }
317 #endif /* SDL_USE_LIBUDEV */
318 
319 #ifdef SDL_INPUT_LINUXKD
320 /* this logic is pulled from kbd_keycode() in drivers/tty/vt/keyboard.c in the
321  Linux kernel source */
322 static void SDL_EVDEV_do_text_input(unsigned short keycode) {
323  char shift_state;
324  int locks_state;
325  struct kbentry kbe;
326  unsigned char type;
327  char text[2] = { 0 };
328 
329  if (_this->console_fd < 0)
330  return;
331 
332  shift_state = TIOCL_GETSHIFTSTATE;
333  if (ioctl(_this->console_fd, TIOCLINUX, &shift_state) < 0) {
334  /* TODO: error */
335  return;
336  }
337 
338  kbe.kb_table = shift_state;
339  kbe.kb_index = keycode;
340 
341  if (ioctl(_this->console_fd, KDGKBENT, &kbe) < 0) {
342  /* TODO: error */
343  return;
344  }
345 
346  type = KTYP(kbe.kb_value);
347 
348  if (type < 0xf0) {
349  /*
350  * FIXME: keysyms with a type below 0xf0 represent a unicode character
351  * which requires special handling due to dead characters, diacritics,
352  * etc. For perfect input a proper way to deal with such characters
353  * should be implemented.
354  *
355  * For reference, the only place I was able to find out about this
356  * special 0xf0 value was in an unused? couple of patches listed below.
357  *
358  * http://ftp.tc.edu.tw/pub/docs/Unicode/utf8/linux-2.3.12-keyboard.diff
359  * http://ftp.tc.edu.tw/pub/docs/Unicode/utf8/linux-2.3.12-console.diff
360  */
361 
362  return;
363  }
364 
365  type -= 0xf0;
366 
367  /* if type is KT_LETTER then it can be affected by Caps Lock */
368  if (type == KT_LETTER) {
369  type = KT_LATIN;
370 
371  if (ioctl(_this->console_fd, KDGKBLED, &locks_state) < 0) {
372  /* TODO: error */
373  return;
374  }
375 
376  if (locks_state & K_CAPSLOCK) {
377  kbe.kb_table = shift_state ^ (1 << KG_SHIFT);
378 
379  if (ioctl(_this->console_fd, KDGKBENT, &kbe) < 0) {
380  /* TODO: error */
381  return;
382  }
383  }
384  }
385 
386  /* TODO: convert values >= 0x80 from ISO-8859-1? to UTF-8 */
387  if (type != KT_LATIN || KVAL(kbe.kb_value) >= 0x80)
388  return;
389 
390  *text = KVAL(kbe.kb_value);
391  SDL_SendKeyboardText(text);
392 }
393 #endif /* SDL_INPUT_LINUXKD */
394 
395 void
396 SDL_EVDEV_Poll(void)
397 {
398  struct input_event events[32];
399  int i, j, len;
400  SDL_evdevlist_item *item;
401  SDL_Scancode scan_code;
402  int mouse_button;
403  SDL_Mouse *mouse;
404  float norm_x, norm_y;
405 
406  if (!_this) {
407  return;
408  }
409 
410 #if SDL_USE_LIBUDEV
411  SDL_UDEV_Poll();
412 #endif
413 
414  mouse = SDL_GetMouse();
415 
416  for (item = _this->first; item != NULL; item = item->next) {
417  while ((len = read(item->fd, events, (sizeof events))) > 0) {
418  len /= sizeof(events[0]);
419  for (i = 0; i < len; ++i) {
420  /* special handling for touchscreen, that should eventually be
421  used for all devices */
422  if (item->out_of_sync && item->is_touchscreen &&
423  events[i].type == EV_SYN && events[i].code != SYN_REPORT) {
424  break;
425  }
426 
427  switch (events[i].type) {
428  case EV_KEY:
429  if (events[i].code >= BTN_MOUSE && events[i].code < BTN_MOUSE + SDL_arraysize(EVDEV_MouseButtons)) {
430  mouse_button = events[i].code - BTN_MOUSE;
431  if (events[i].value == 0) {
432  SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_RELEASED, EVDEV_MouseButtons[mouse_button]);
433  } else if (events[i].value == 1) {
434  SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_PRESSED, EVDEV_MouseButtons[mouse_button]);
435  }
436  break;
437  }
438 
439  /* Probably keyboard */
440  scan_code = SDL_EVDEV_translate_keycode(events[i].code);
441  if (scan_code != SDL_SCANCODE_UNKNOWN) {
442  if (events[i].value == 0) {
443  SDL_SendKeyboardKey(SDL_RELEASED, scan_code);
444  } else if (events[i].value == 1 || events[i].value == 2 /* key repeated */) {
445  SDL_SendKeyboardKey(SDL_PRESSED, scan_code);
446 #ifdef SDL_INPUT_LINUXKD
447  SDL_EVDEV_do_text_input(events[i].code);
448 #endif /* SDL_INPUT_LINUXKD */
449  }
450  }
451  break;
452  case EV_ABS:
453  switch(events[i].code) {
454  case ABS_MT_SLOT:
455  if (!item->is_touchscreen) /* FIXME: temp hack */
456  break;
457  item->touchscreen_data->current_slot = events[i].value;
458  break;
459  case ABS_MT_TRACKING_ID:
460  if (!item->is_touchscreen) /* FIXME: temp hack */
461  break;
462  if (events[i].value >= 0) {
463  item->touchscreen_data->slots[item->touchscreen_data->current_slot].tracking_id = events[i].value;
464  item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
465  } else {
466  item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_UP;
467  }
468  break;
469  case ABS_MT_POSITION_X:
470  if (!item->is_touchscreen) /* FIXME: temp hack */
471  break;
472  item->touchscreen_data->slots[item->touchscreen_data->current_slot].x = events[i].value;
473  if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
474  item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
475  }
476  break;
477  case ABS_MT_POSITION_Y:
478  if (!item->is_touchscreen) /* FIXME: temp hack */
479  break;
480  item->touchscreen_data->slots[item->touchscreen_data->current_slot].y = events[i].value;
481  if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
482  item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
483  }
484  break;
485  case ABS_X:
486  if (item->is_touchscreen) /* FIXME: temp hack */
487  break;
488  SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, events[i].value, mouse->y);
489  break;
490  case ABS_Y:
491  if (item->is_touchscreen) /* FIXME: temp hack */
492  break;
493  SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, mouse->x, events[i].value);
494  break;
495  default:
496  break;
497  }
498  break;
499  case EV_REL:
500  switch(events[i].code) {
501  case REL_X:
502  SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, events[i].value, 0);
503  break;
504  case REL_Y:
505  SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, 0, events[i].value);
506  break;
507  case REL_WHEEL:
508  SDL_SendMouseWheel(mouse->focus, mouse->mouseID, 0, events[i].value, SDL_MOUSEWHEEL_NORMAL);
509  break;
510  case REL_HWHEEL:
511  SDL_SendMouseWheel(mouse->focus, mouse->mouseID, events[i].value, 0, SDL_MOUSEWHEEL_NORMAL);
512  break;
513  default:
514  break;
515  }
516  break;
517  case EV_SYN:
518  switch (events[i].code) {
519  case SYN_REPORT:
520  if (!item->is_touchscreen) /* FIXME: temp hack */
521  break;
522 
523  for(j = 0; j < item->touchscreen_data->max_slots; j++) {
524  norm_x = (float)(item->touchscreen_data->slots[j].x - item->touchscreen_data->min_x) /
525  (float)item->touchscreen_data->range_x;
526  norm_y = (float)(item->touchscreen_data->slots[j].y - item->touchscreen_data->min_y) /
527  (float)item->touchscreen_data->range_y;
528 
529  switch(item->touchscreen_data->slots[j].delta) {
530  case EVDEV_TOUCH_SLOTDELTA_DOWN:
531  SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_TRUE, norm_x, norm_y, 1.0f);
532  item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
533  break;
534  case EVDEV_TOUCH_SLOTDELTA_UP:
535  SDL_SendTouch(item->fd, item->touchscreen_data->slots[j].tracking_id, SDL_FALSE, norm_x, norm_y, 1.0f);
536  item->touchscreen_data->slots[j].tracking_id = -1;
537  item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
538  break;
539  case EVDEV_TOUCH_SLOTDELTA_MOVE:
540  SDL_SendTouchMotion(item->fd, item->touchscreen_data->slots[j].tracking_id, norm_x, norm_y, 1.0f);
541  item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
542  break;
543  default:
544  break;
545  }
546  }
547 
548  if (item->out_of_sync)
549  item->out_of_sync = 0;
550  break;
551  case SYN_DROPPED:
552  if (item->is_touchscreen)
553  item->out_of_sync = 1;
554  SDL_EVDEV_sync_device(item);
555  break;
556  default:
557  break;
558  }
559  break;
560  }
561  }
562  }
563  }
564 }
565 
566 static SDL_Scancode
567 SDL_EVDEV_translate_keycode(int keycode)
568 {
570 
571  if (keycode < SDL_arraysize(linux_scancode_table))
572  scancode = linux_scancode_table[keycode];
573 
574  if (scancode == SDL_SCANCODE_UNKNOWN) {
575  SDL_Log("The key you just pressed is not recognized by SDL. To help "
576  "get this fixed, please report this to the SDL mailing list "
577  "<sdl@libsdl.org> EVDEV KeyCode %d\n", keycode);
578  }
579 
580  return scancode;
581 }
582 
583 #ifdef SDL_USE_LIBUDEV
584 static int
585 SDL_EVDEV_init_touchscreen(SDL_evdevlist_item* item)
586 {
587  int ret, i;
588  char name[64];
589  struct input_absinfo abs_info;
590 
591  if (!item->is_touchscreen)
592  return 0;
593 
594  item->touchscreen_data = SDL_calloc(1, sizeof(*item->touchscreen_data));
595  if (item->touchscreen_data == NULL)
596  return SDL_OutOfMemory();
597 
598  ret = ioctl(item->fd, EVIOCGNAME(sizeof(name)), name);
599  if (ret < 0) {
600  SDL_free(item->touchscreen_data);
601  return SDL_SetError("Failed to get evdev touchscreen name");
602  }
603 
604  item->touchscreen_data->name = SDL_strdup(name);
605  if (item->touchscreen_data->name == NULL) {
606  SDL_free(item->touchscreen_data);
607  return SDL_OutOfMemory();
608  }
609 
610  ret = ioctl(item->fd, EVIOCGABS(ABS_MT_POSITION_X), &abs_info);
611  if (ret < 0) {
612  SDL_free(item->touchscreen_data->name);
613  SDL_free(item->touchscreen_data);
614  return SDL_SetError("Failed to get evdev touchscreen limits");
615  }
616  item->touchscreen_data->min_x = abs_info.minimum;
617  item->touchscreen_data->max_x = abs_info.maximum;
618  item->touchscreen_data->range_x = abs_info.maximum - abs_info.minimum;
619 
620  ret = ioctl(item->fd, EVIOCGABS(ABS_MT_POSITION_Y), &abs_info);
621  if (ret < 0) {
622  SDL_free(item->touchscreen_data->name);
623  SDL_free(item->touchscreen_data);
624  return SDL_SetError("Failed to get evdev touchscreen limits");
625  }
626  item->touchscreen_data->min_y = abs_info.minimum;
627  item->touchscreen_data->max_y = abs_info.maximum;
628  item->touchscreen_data->range_y = abs_info.maximum - abs_info.minimum;
629 
630  ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
631  if (ret < 0) {
632  SDL_free(item->touchscreen_data->name);
633  SDL_free(item->touchscreen_data);
634  return SDL_SetError("Failed to get evdev touchscreen limits");
635  }
636  item->touchscreen_data->max_slots = abs_info.maximum + 1;
637 
638  item->touchscreen_data->slots = SDL_calloc(
639  item->touchscreen_data->max_slots,
640  sizeof(*item->touchscreen_data->slots));
641  if (item->touchscreen_data->slots == NULL) {
642  SDL_free(item->touchscreen_data->name);
643  SDL_free(item->touchscreen_data);
644  return SDL_OutOfMemory();
645  }
646 
647  for(i = 0; i < item->touchscreen_data->max_slots; i++) {
648  item->touchscreen_data->slots[i].tracking_id = -1;
649  }
650 
651  ret = SDL_AddTouch(item->fd, /* I guess our fd is unique enough */
652  item->touchscreen_data->name);
653  if (ret < 0) {
654  SDL_free(item->touchscreen_data->slots);
655  SDL_free(item->touchscreen_data->name);
656  SDL_free(item->touchscreen_data);
657  return ret;
658  }
659 
660  return 0;
661 }
662 #endif /* SDL_USE_LIBUDEV */
663 
664 static void
665 SDL_EVDEV_destroy_touchscreen(SDL_evdevlist_item* item) {
666  if (!item->is_touchscreen)
667  return;
668 
669  SDL_DelTouch(item->fd);
670  SDL_free(item->touchscreen_data->slots);
671  SDL_free(item->touchscreen_data->name);
672  SDL_free(item->touchscreen_data);
673 }
674 
675 static void
676 SDL_EVDEV_sync_device(SDL_evdevlist_item *item)
677 {
678 #ifdef EVIOCGMTSLOTS
679  int i, ret;
680  struct input_absinfo abs_info;
681  /*
682  * struct input_mt_request_layout {
683  * __u32 code;
684  * __s32 values[num_slots];
685  * };
686  *
687  * this is the structure we're trying to emulate
688  */
689  __u32* mt_req_code;
690  __s32* mt_req_values;
691  size_t mt_req_size;
692 
693  /* TODO: sync devices other than touchscreen */
694  if (!item->is_touchscreen)
695  return;
696 
697  mt_req_size = sizeof(*mt_req_code) +
698  sizeof(*mt_req_values) * item->touchscreen_data->max_slots;
699 
700  mt_req_code = SDL_calloc(1, mt_req_size);
701  if (mt_req_code == NULL) {
702  return;
703  }
704 
705  mt_req_values = (__s32*)mt_req_code + 1;
706 
707  *mt_req_code = ABS_MT_TRACKING_ID;
708  ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
709  if (ret < 0) {
710  SDL_free(mt_req_code);
711  return;
712  }
713  for(i = 0; i < item->touchscreen_data->max_slots; i++) {
714  /*
715  * This doesn't account for the very edge case of the user removing their
716  * finger and replacing it on the screen during the time we're out of sync,
717  * which'll mean that we're not going from down -> up or up -> down, we're
718  * going from down -> down but with a different tracking id, meaning we'd
719  * have to tell SDL of the two events, but since we wait till SYN_REPORT in
720  * SDL_EVDEV_Poll to tell SDL, the current structure of this code doesn't
721  * allow it. Lets just pray to God it doesn't happen.
722  */
723  if (item->touchscreen_data->slots[i].tracking_id < 0 &&
724  mt_req_values[i] >= 0) {
725  item->touchscreen_data->slots[i].tracking_id = mt_req_values[i];
726  item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
727  } else if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
728  mt_req_values[i] < 0) {
729  item->touchscreen_data->slots[i].tracking_id = -1;
730  item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_UP;
731  }
732  }
733 
734  *mt_req_code = ABS_MT_POSITION_X;
735  ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
736  if (ret < 0) {
737  SDL_free(mt_req_code);
738  return;
739  }
740  for(i = 0; i < item->touchscreen_data->max_slots; i++) {
741  if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
742  item->touchscreen_data->slots[i].x != mt_req_values[i]) {
743  item->touchscreen_data->slots[i].x = mt_req_values[i];
744  if (item->touchscreen_data->slots[i].delta ==
745  EVDEV_TOUCH_SLOTDELTA_NONE) {
746  item->touchscreen_data->slots[i].delta =
747  EVDEV_TOUCH_SLOTDELTA_MOVE;
748  }
749  }
750  }
751 
752  *mt_req_code = ABS_MT_POSITION_Y;
753  ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
754  if (ret < 0) {
755  SDL_free(mt_req_code);
756  return;
757  }
758  for(i = 0; i < item->touchscreen_data->max_slots; i++) {
759  if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
760  item->touchscreen_data->slots[i].y != mt_req_values[i]) {
761  item->touchscreen_data->slots[i].y = mt_req_values[i];
762  if (item->touchscreen_data->slots[i].delta ==
763  EVDEV_TOUCH_SLOTDELTA_NONE) {
764  item->touchscreen_data->slots[i].delta =
765  EVDEV_TOUCH_SLOTDELTA_MOVE;
766  }
767  }
768  }
769 
770  ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
771  if (ret < 0) {
772  SDL_free(mt_req_code);
773  return;
774  }
775  item->touchscreen_data->current_slot = abs_info.value;
776 
777  SDL_free(mt_req_code);
778 
779 #endif /* EVIOCGMTSLOTS */
780 }
781 
782 #if SDL_USE_LIBUDEV
783 static int
784 SDL_EVDEV_device_added(const char *dev_path, int udev_class)
785 {
786  int ret;
787  SDL_evdevlist_item *item;
788 
789  /* Check to make sure it's not already in list. */
790  for (item = _this->first; item != NULL; item = item->next) {
791  if (SDL_strcmp(dev_path, item->path) == 0) {
792  return -1; /* already have this one */
793  }
794  }
795 
796  item = (SDL_evdevlist_item *) SDL_calloc(1, sizeof (SDL_evdevlist_item));
797  if (item == NULL) {
798  return SDL_OutOfMemory();
799  }
800 
801  item->fd = open(dev_path, O_RDONLY | O_NONBLOCK);
802  if (item->fd < 0) {
803  SDL_free(item);
804  return SDL_SetError("Unable to open %s", dev_path);
805  }
806 
807  item->path = SDL_strdup(dev_path);
808  if (item->path == NULL) {
809  close(item->fd);
810  SDL_free(item);
811  return SDL_OutOfMemory();
812  }
813 
814  if (udev_class & SDL_UDEV_DEVICE_TOUCHSCREEN) {
815  item->is_touchscreen = 1;
816 
817  if ((ret = SDL_EVDEV_init_touchscreen(item)) < 0) {
818  close(item->fd);
819  SDL_free(item);
820  return ret;
821  }
822  }
823 
824  if (_this->last == NULL) {
825  _this->first = _this->last = item;
826  } else {
827  _this->last->next = item;
828  _this->last = item;
829  }
830 
831  SDL_EVDEV_sync_device(item);
832 
833  return _this->num_devices++;
834 }
835 #endif /* SDL_USE_LIBUDEV */
836 
837 static int
838 SDL_EVDEV_device_removed(const char *dev_path)
839 {
840  SDL_evdevlist_item *item;
841  SDL_evdevlist_item *prev = NULL;
842 
843  for (item = _this->first; item != NULL; item = item->next) {
844  /* found it, remove it. */
845  if (SDL_strcmp(dev_path, item->path) == 0) {
846  if (prev != NULL) {
847  prev->next = item->next;
848  } else {
849  SDL_assert(_this->first == item);
850  _this->first = item->next;
851  }
852  if (item == _this->last) {
853  _this->last = prev;
854  }
855  if (item->is_touchscreen) {
856  SDL_EVDEV_destroy_touchscreen(item);
857  }
858  close(item->fd);
859  SDL_free(item->path);
860  SDL_free(item);
861  _this->num_devices--;
862  return 0;
863  }
864  prev = item;
865  }
866 
867  return -1;
868 }
869 
870 
871 #endif /* SDL_INPUT_LINUXEV */
872 
873 /* vi: set ts=4 sw=4 expandtab: */
SDL_Mouse * SDL_GetMouse(void)
Definition: SDL_mouse.c:66
const GLint * first
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1567
SDL_Window * focus
Definition: SDL_mouse_c.h:77
#define SDL_BUTTON_RIGHT
Definition: SDL_mouse.h:284
static SDL_Event events[EVENT_BUF_SIZE]
Definition: testgesture.c:36
int SDL_SendTouch(SDL_TouchID id, SDL_FingerID fingerid, SDL_bool down, float x, float y, float pressure)
Definition: SDL_touch.c:216
GLuint const GLchar * name
#define SDL_BUTTON_X1
Definition: SDL_mouse.h:285
GLenum GLsizei len
SDL_MouseID mouseID
Definition: SDL_mouse_c.h:76
int SDL_SendKeyboardKey(Uint8 state, SDL_Scancode scancode)
Definition: SDL_keyboard.c:661
int SDL_SendTouchMotion(SDL_TouchID id, SDL_FingerID fingerid, float x, float y, float pressure)
Definition: SDL_touch.c:278
static SDL_VideoDevice * _this
Definition: SDL_video.c:118
#define SDL_Log
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1564
void * SDL_calloc(size_t nmemb, size_t size)
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1567
int SDL_SendMouseMotion(SDL_Window *window, SDL_MouseID mouseID, int relative, int x, int y)
Definition: SDL_mouse.c:188
#define SDL_BUTTON_LEFT
Definition: SDL_mouse.h:282
GLsizei const GLfloat * value
#define _THIS
uint8_t Uint8
An unsigned 8-bit integer type.
Definition: SDL_stdinc.h:143
int SDL_SendKeyboardText(const char *text)
Definition: SDL_keyboard.c:774
void SDL_free(void *mem)
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int int in j)
Definition: SDL_x11sym.h:50
void SDL_DelTouch(SDL_TouchID id)
Definition: SDL_touch.c:331
#define SDL_BUTTON_MIDDLE
Definition: SDL_mouse.h:283
int SDL_AddTouch(SDL_TouchID touchID, const char *name)
Definition: SDL_touch.c:130
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
#define SDL_assert(condition)
Definition: SDL_assert.h:167
#define NULL
Definition: begin_code.h:143
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
#define SDL_SetError
static char text[MAX_TEXT_LENGTH]
Definition: testime.c:47
static SDL_Scancode const linux_scancode_table[]
#define SDL_strdup
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:90
int SDL_SendMouseWheel(SDL_Window *window, SDL_MouseID mouseID, int x, int y, SDL_MouseWheelDirection direction)
Definition: SDL_mouse.c:420
GLsizei const GLchar *const * path
#define SDL_strcmp
#define SDL_PRESSED
Definition: SDL_events.h:50
#define SDL_BUTTON_X2
Definition: SDL_mouse.h:286
#define SDL_RELEASED
Definition: SDL_events.h:49
int SDL_SendMouseButton(SDL_Window *window, SDL_MouseID mouseID, Uint8 state, Uint8 button)
Definition: SDL_mouse.c:414
SDL_Scancode
The SDL keyboard scancode representation.
Definition: SDL_scancode.h:43
Uint32 type
Definition: SDL_events.h:527