SDL  2.0
SDL_udev.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2018 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 
22 /*
23  * To list the properties of a device, try something like:
24  * udevadm info -a -n snd/hwC0D0 (for a sound card)
25  * udevadm info --query=all -n input/event3 (for a keyboard, mouse, etc)
26  * udevadm info --query=property -n input/event2
27  */
28 #include "SDL_udev.h"
29 
30 #ifdef SDL_USE_LIBUDEV
31 
32 #include <linux/input.h>
33 
34 #include "SDL_assert.h"
35 #include "SDL_loadso.h"
36 #include "SDL_timer.h"
37 #include "../unix/SDL_poll.h"
38 
39 static const char *SDL_UDEV_LIBS[] = { "libudev.so.1", "libudev.so.0" };
40 
41 #define _THIS SDL_UDEV_PrivateData *_this
42 static _THIS = NULL;
43 
44 static SDL_bool SDL_UDEV_load_sym(const char *fn, void **addr);
45 static int SDL_UDEV_load_syms(void);
46 static SDL_bool SDL_UDEV_hotplug_update_available(void);
47 static void device_event(SDL_UDEV_deviceevent type, struct udev_device *dev);
48 
49 static SDL_bool
50 SDL_UDEV_load_sym(const char *fn, void **addr)
51 {
52  *addr = SDL_LoadFunction(_this->udev_handle, fn);
53  if (*addr == NULL) {
54  /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
55  return SDL_FALSE;
56  }
57 
58  return SDL_TRUE;
59 }
60 
61 static int
62 SDL_UDEV_load_syms(void)
63 {
64  /* cast funcs to char* first, to please GCC's strict aliasing rules. */
65  #define SDL_UDEV_SYM(x) \
66  if (!SDL_UDEV_load_sym(#x, (void **) (char *) & _this->x)) return -1
67 
68  SDL_UDEV_SYM(udev_device_get_action);
69  SDL_UDEV_SYM(udev_device_get_devnode);
70  SDL_UDEV_SYM(udev_device_get_subsystem);
71  SDL_UDEV_SYM(udev_device_get_parent_with_subsystem_devtype);
72  SDL_UDEV_SYM(udev_device_get_property_value);
73  SDL_UDEV_SYM(udev_device_get_sysattr_value);
74  SDL_UDEV_SYM(udev_device_new_from_syspath);
75  SDL_UDEV_SYM(udev_device_unref);
76  SDL_UDEV_SYM(udev_enumerate_add_match_property);
77  SDL_UDEV_SYM(udev_enumerate_add_match_subsystem);
78  SDL_UDEV_SYM(udev_enumerate_get_list_entry);
79  SDL_UDEV_SYM(udev_enumerate_new);
80  SDL_UDEV_SYM(udev_enumerate_scan_devices);
81  SDL_UDEV_SYM(udev_enumerate_unref);
82  SDL_UDEV_SYM(udev_list_entry_get_name);
83  SDL_UDEV_SYM(udev_list_entry_get_next);
84  SDL_UDEV_SYM(udev_monitor_enable_receiving);
85  SDL_UDEV_SYM(udev_monitor_filter_add_match_subsystem_devtype);
86  SDL_UDEV_SYM(udev_monitor_get_fd);
87  SDL_UDEV_SYM(udev_monitor_new_from_netlink);
88  SDL_UDEV_SYM(udev_monitor_receive_device);
89  SDL_UDEV_SYM(udev_monitor_unref);
90  SDL_UDEV_SYM(udev_new);
91  SDL_UDEV_SYM(udev_unref);
92  SDL_UDEV_SYM(udev_device_new_from_devnum);
93  SDL_UDEV_SYM(udev_device_get_devnum);
94  #undef SDL_UDEV_SYM
95 
96  return 0;
97 }
98 
99 static SDL_bool
100 SDL_UDEV_hotplug_update_available(void)
101 {
102  if (_this->udev_mon != NULL) {
103  const int fd = _this->udev_monitor_get_fd(_this->udev_mon);
104  if (SDL_IOReady(fd, SDL_FALSE, 0)) {
105  return SDL_TRUE;
106  }
107  }
108  return SDL_FALSE;
109 }
110 
111 
112 int
113 SDL_UDEV_Init(void)
114 {
115  int retval = 0;
116 
117  if (_this == NULL) {
118  _this = (SDL_UDEV_PrivateData *) SDL_calloc(1, sizeof(*_this));
119  if(_this == NULL) {
120  return SDL_OutOfMemory();
121  }
122 
123  retval = SDL_UDEV_LoadLibrary();
124  if (retval < 0) {
125  SDL_UDEV_Quit();
126  return retval;
127  }
128 
129  /* Set up udev monitoring
130  * Listen for input devices (mouse, keyboard, joystick, etc) and sound devices
131  */
132 
133  _this->udev = _this->udev_new();
134  if (_this->udev == NULL) {
135  SDL_UDEV_Quit();
136  return SDL_SetError("udev_new() failed");
137  }
138 
139  _this->udev_mon = _this->udev_monitor_new_from_netlink(_this->udev, "udev");
140  if (_this->udev_mon == NULL) {
141  SDL_UDEV_Quit();
142  return SDL_SetError("udev_monitor_new_from_netlink() failed");
143  }
144 
145  _this->udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "input", NULL);
146  _this->udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "sound", NULL);
147  _this->udev_monitor_enable_receiving(_this->udev_mon);
148 
149  /* Do an initial scan of existing devices */
150  SDL_UDEV_Scan();
151 
152  }
153 
154  _this->ref_count += 1;
155 
156  return retval;
157 }
158 
159 void
160 SDL_UDEV_Quit(void)
161 {
162  SDL_UDEV_CallbackList *item;
163 
164  if (_this == NULL) {
165  return;
166  }
167 
168  _this->ref_count -= 1;
169 
170  if (_this->ref_count < 1) {
171 
172  if (_this->udev_mon != NULL) {
173  _this->udev_monitor_unref(_this->udev_mon);
174  _this->udev_mon = NULL;
175  }
176  if (_this->udev != NULL) {
177  _this->udev_unref(_this->udev);
178  _this->udev = NULL;
179  }
180 
181  /* Remove existing devices */
182  while (_this->first != NULL) {
183  item = _this->first;
184  _this->first = _this->first->next;
185  SDL_free(item);
186  }
187 
188  SDL_UDEV_UnloadLibrary();
189  SDL_free(_this);
190  _this = NULL;
191  }
192 }
193 
194 void
195 SDL_UDEV_Scan(void)
196 {
197  struct udev_enumerate *enumerate = NULL;
198  struct udev_list_entry *devs = NULL;
199  struct udev_list_entry *item = NULL;
200 
201  if (_this == NULL) {
202  return;
203  }
204 
205  enumerate = _this->udev_enumerate_new(_this->udev);
206  if (enumerate == NULL) {
207  SDL_UDEV_Quit();
208  SDL_SetError("udev_enumerate_new() failed");
209  return;
210  }
211 
212  _this->udev_enumerate_add_match_subsystem(enumerate, "input");
213  _this->udev_enumerate_add_match_subsystem(enumerate, "sound");
214 
215  _this->udev_enumerate_scan_devices(enumerate);
216  devs = _this->udev_enumerate_get_list_entry(enumerate);
217  for (item = devs; item; item = _this->udev_list_entry_get_next(item)) {
218  const char *path = _this->udev_list_entry_get_name(item);
219  struct udev_device *dev = _this->udev_device_new_from_syspath(_this->udev, path);
220  if (dev != NULL) {
221  device_event(SDL_UDEV_DEVICEADDED, dev);
222  _this->udev_device_unref(dev);
223  }
224  }
225 
226  _this->udev_enumerate_unref(enumerate);
227 }
228 
229 
230 void
231 SDL_UDEV_UnloadLibrary(void)
232 {
233  if (_this == NULL) {
234  return;
235  }
236 
237  if (_this->udev_handle != NULL) {
238  SDL_UnloadObject(_this->udev_handle);
239  _this->udev_handle = NULL;
240  }
241 }
242 
243 int
244 SDL_UDEV_LoadLibrary(void)
245 {
246  int retval = 0, i;
247 
248  if (_this == NULL) {
249  return SDL_SetError("UDEV not initialized");
250  }
251 
252  /* See if there is a udev library already loaded */
253  if (SDL_UDEV_load_syms() == 0) {
254  return 0;
255  }
256 
257 #ifdef SDL_UDEV_DYNAMIC
258  /* Check for the build environment's libudev first */
259  if (_this->udev_handle == NULL) {
260  _this->udev_handle = SDL_LoadObject(SDL_UDEV_DYNAMIC);
261  if (_this->udev_handle != NULL) {
262  retval = SDL_UDEV_load_syms();
263  if (retval < 0) {
264  SDL_UDEV_UnloadLibrary();
265  }
266  }
267  }
268 #endif
269 
270  if (_this->udev_handle == NULL) {
271  for( i = 0 ; i < SDL_arraysize(SDL_UDEV_LIBS); i++) {
272  _this->udev_handle = SDL_LoadObject(SDL_UDEV_LIBS[i]);
273  if (_this->udev_handle != NULL) {
274  retval = SDL_UDEV_load_syms();
275  if (retval < 0) {
276  SDL_UDEV_UnloadLibrary();
277  }
278  else {
279  break;
280  }
281  }
282  }
283 
284  if (_this->udev_handle == NULL) {
285  retval = -1;
286  /* Don't call SDL_SetError(): SDL_LoadObject already did. */
287  }
288  }
289 
290  return retval;
291 }
292 
293 #define BITS_PER_LONG (sizeof(unsigned long) * 8)
294 #define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
295 #define OFF(x) ((x)%BITS_PER_LONG)
296 #define LONG(x) ((x)/BITS_PER_LONG)
297 #define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
298 
299 static void get_caps(struct udev_device *dev, struct udev_device *pdev, const char *attr, unsigned long *bitmask, size_t bitmask_len)
300 {
301  const char *value;
302  char text[4096];
303  char *word;
304  int i;
305  unsigned long v;
306 
307  SDL_memset(bitmask, 0, bitmask_len*sizeof(*bitmask));
308  value = _this->udev_device_get_sysattr_value(pdev, attr);
309  if (!value) {
310  return;
311  }
312 
313  SDL_strlcpy(text, value, sizeof(text));
314  i = 0;
315  while ((word = SDL_strrchr(text, ' ')) != NULL) {
316  v = SDL_strtoul(word+1, NULL, 16);
317  if (i < bitmask_len) {
318  bitmask[i] = v;
319  }
320  ++i;
321  *word = '\0';
322  }
323  v = SDL_strtoul(text, NULL, 16);
324  if (i < bitmask_len) {
325  bitmask[i] = v;
326  }
327 }
328 
329 static int
330 guess_device_class(struct udev_device *dev)
331 {
332  int devclass = 0;
333  struct udev_device *pdev;
334  unsigned long bitmask_ev[NBITS(EV_MAX)];
335  unsigned long bitmask_abs[NBITS(ABS_MAX)];
336  unsigned long bitmask_key[NBITS(KEY_MAX)];
337  unsigned long bitmask_rel[NBITS(REL_MAX)];
338  unsigned long keyboard_mask;
339 
340  /* walk up the parental chain until we find the real input device; the
341  * argument is very likely a subdevice of this, like eventN */
342  pdev = dev;
343  while (pdev && !_this->udev_device_get_sysattr_value(pdev, "capabilities/ev")) {
344  pdev = _this->udev_device_get_parent_with_subsystem_devtype(pdev, "input", NULL);
345  }
346  if (!pdev) {
347  return 0;
348  }
349 
350  get_caps(dev, pdev, "capabilities/ev", bitmask_ev, SDL_arraysize(bitmask_ev));
351  get_caps(dev, pdev, "capabilities/abs", bitmask_abs, SDL_arraysize(bitmask_abs));
352  get_caps(dev, pdev, "capabilities/rel", bitmask_rel, SDL_arraysize(bitmask_rel));
353  get_caps(dev, pdev, "capabilities/key", bitmask_key, SDL_arraysize(bitmask_key));
354 
355  if (test_bit(EV_ABS, bitmask_ev) &&
356  test_bit(ABS_X, bitmask_abs) && test_bit(ABS_Y, bitmask_abs)) {
357  if (test_bit(BTN_STYLUS, bitmask_key) || test_bit(BTN_TOOL_PEN, bitmask_key)) {
358  ; /* ID_INPUT_TABLET */
359  } else if (test_bit(BTN_TOOL_FINGER, bitmask_key) && !test_bit(BTN_TOOL_PEN, bitmask_key)) {
360  ; /* ID_INPUT_TOUCHPAD */
361  } else if (test_bit(BTN_MOUSE, bitmask_key)) {
362  devclass |= SDL_UDEV_DEVICE_MOUSE; /* ID_INPUT_MOUSE */
363  } else if (test_bit(BTN_TOUCH, bitmask_key)) {
364  /* TODO: better determining between touchscreen and multitouch touchpad,
365  see https://github.com/systemd/systemd/blob/master/src/udev/udev-builtin-input_id.c */
366  devclass |= SDL_UDEV_DEVICE_TOUCHSCREEN; /* ID_INPUT_TOUCHSCREEN */
367  }
368 
369  if (test_bit(BTN_TRIGGER, bitmask_key) ||
370  test_bit(BTN_A, bitmask_key) ||
371  test_bit(BTN_1, bitmask_key) ||
372  test_bit(ABS_RX, bitmask_abs) ||
373  test_bit(ABS_RY, bitmask_abs) ||
374  test_bit(ABS_RZ, bitmask_abs) ||
375  test_bit(ABS_THROTTLE, bitmask_abs) ||
376  test_bit(ABS_RUDDER, bitmask_abs) ||
377  test_bit(ABS_WHEEL, bitmask_abs) ||
378  test_bit(ABS_GAS, bitmask_abs) ||
379  test_bit(ABS_BRAKE, bitmask_abs)) {
380  devclass |= SDL_UDEV_DEVICE_JOYSTICK; /* ID_INPUT_JOYSTICK */
381  }
382  }
383 
384  if (test_bit(EV_REL, bitmask_ev) &&
385  test_bit(REL_X, bitmask_rel) && test_bit(REL_Y, bitmask_rel) &&
386  test_bit(BTN_MOUSE, bitmask_key)) {
387  devclass |= SDL_UDEV_DEVICE_MOUSE; /* ID_INPUT_MOUSE */
388  }
389 
390  /* the first 32 bits are ESC, numbers, and Q to D; if we have any of
391  * those, consider it a keyboard device; do not test KEY_RESERVED, though */
392  keyboard_mask = 0xFFFFFFFE;
393  if ((bitmask_key[0] & keyboard_mask) != 0)
394  devclass |= SDL_UDEV_DEVICE_KEYBOARD; /* ID_INPUT_KEYBOARD */
395 
396  return devclass;
397 }
398 
399 static void
400 device_event(SDL_UDEV_deviceevent type, struct udev_device *dev)
401 {
402  const char *subsystem;
403  const char *val = NULL;
404  int devclass = 0;
405  const char *path;
406  SDL_UDEV_CallbackList *item;
407 
408  path = _this->udev_device_get_devnode(dev);
409  if (path == NULL) {
410  return;
411  }
412 
413  subsystem = _this->udev_device_get_subsystem(dev);
414  if (SDL_strcmp(subsystem, "sound") == 0) {
415  devclass = SDL_UDEV_DEVICE_SOUND;
416  } else if (SDL_strcmp(subsystem, "input") == 0) {
417  /* udev rules reference: http://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-input_id.c */
418 
419  val = _this->udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK");
420  if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
421  devclass |= SDL_UDEV_DEVICE_JOYSTICK;
422  }
423 
424  val = _this->udev_device_get_property_value(dev, "ID_INPUT_MOUSE");
425  if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
426  devclass |= SDL_UDEV_DEVICE_MOUSE;
427  }
428 
429  val = _this->udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN");
430  if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
431  devclass |= SDL_UDEV_DEVICE_TOUCHSCREEN;
432  }
433 
434  /* The undocumented rule is:
435  - All devices with keys get ID_INPUT_KEY
436  - From this subset, if they have ESC, numbers, and Q to D, it also gets ID_INPUT_KEYBOARD
437 
438  Ref: http://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-input_id.c#n183
439  */
440  val = _this->udev_device_get_property_value(dev, "ID_INPUT_KEY");
441  if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
442  devclass |= SDL_UDEV_DEVICE_KEYBOARD;
443  }
444 
445  if (devclass == 0) {
446  /* Fall back to old style input classes */
447  val = _this->udev_device_get_property_value(dev, "ID_CLASS");
448  if (val != NULL) {
449  if (SDL_strcmp(val, "joystick") == 0) {
450  devclass = SDL_UDEV_DEVICE_JOYSTICK;
451  } else if (SDL_strcmp(val, "mouse") == 0) {
452  devclass = SDL_UDEV_DEVICE_MOUSE;
453  } else if (SDL_strcmp(val, "kbd") == 0) {
454  devclass = SDL_UDEV_DEVICE_KEYBOARD;
455  } else {
456  return;
457  }
458  } else {
459  /* We could be linked with libudev on a system that doesn't have udev running */
460  devclass = guess_device_class(dev);
461  }
462  }
463  } else {
464  return;
465  }
466 
467  /* Process callbacks */
468  for (item = _this->first; item != NULL; item = item->next) {
469  item->callback(type, devclass, path);
470  }
471 }
472 
473 void
474 SDL_UDEV_Poll(void)
475 {
476  struct udev_device *dev = NULL;
477  const char *action = NULL;
478 
479  if (_this == NULL) {
480  return;
481  }
482 
483  while (SDL_UDEV_hotplug_update_available()) {
484  dev = _this->udev_monitor_receive_device(_this->udev_mon);
485  if (dev == NULL) {
486  break;
487  }
488  action = _this->udev_device_get_action(dev);
489 
490  if (SDL_strcmp(action, "add") == 0) {
491  /* Wait for the device to finish initialization */
492  SDL_Delay(100);
493 
494  device_event(SDL_UDEV_DEVICEADDED, dev);
495  } else if (SDL_strcmp(action, "remove") == 0) {
496  device_event(SDL_UDEV_DEVICEREMOVED, dev);
497  }
498 
499  _this->udev_device_unref(dev);
500  }
501 }
502 
503 int
504 SDL_UDEV_AddCallback(SDL_UDEV_Callback cb)
505 {
506  SDL_UDEV_CallbackList *item;
507  item = (SDL_UDEV_CallbackList *) SDL_calloc(1, sizeof (SDL_UDEV_CallbackList));
508  if (item == NULL) {
509  return SDL_OutOfMemory();
510  }
511 
512  item->callback = cb;
513 
514  if (_this->last == NULL) {
515  _this->first = _this->last = item;
516  } else {
517  _this->last->next = item;
518  _this->last = item;
519  }
520 
521  return 1;
522 }
523 
524 void
525 SDL_UDEV_DelCallback(SDL_UDEV_Callback cb)
526 {
527  SDL_UDEV_CallbackList *item;
528  SDL_UDEV_CallbackList *prev = NULL;
529 
530  for (item = _this->first; item != NULL; item = item->next) {
531  /* found it, remove it. */
532  if (item->callback == cb) {
533  if (prev != NULL) {
534  prev->next = item->next;
535  } else {
536  SDL_assert(_this->first == item);
537  _this->first = item->next;
538  }
539  if (item == _this->last) {
540  _this->last = prev;
541  }
542  SDL_free(item);
543  return;
544  }
545  prev = item;
546  }
547 
548 }
549 
550 
551 #endif /* SDL_USE_LIBUDEV */
552 
553 /* vi: set ts=4 sw=4 expandtab: */
#define SDL_strlcpy
int SDL_IOReady(int fd, SDL_bool forWrite, int timeoutMS)
Definition: SDL_poll.c:38
const GLdouble * v
Definition: SDL_opengl.h:2064
#define SDL_LoadObject
#define SDL_UnloadObject
static SDL_VideoDevice * _this
Definition: SDL_video.c:121
#define SDL_UDEV_DYNAMIC
Definition: SDL_config.h:405
SDL_bool retval
GLuint GLfloat * val
#define _THIS
#define SDL_free
GLenum const void * addr
GLsizei const GLfloat * value
#define SDL_Delay
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:169
#define NULL
Definition: begin_code.h:164
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:139
#define SDL_SetError
static char text[MAX_TEXT_LENGTH]
Definition: testime.c:47
#define SDL_calloc
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1571
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:93
GLsizei const GLchar *const * path
#define SDL_strcmp
void * SDL_LoadFunction(void *handle, const char *name)
#define SDL_strtoul
#define SDL_strrchr
GLuint64 GLenum GLint fd
Definition: gl2ext.h:1508
#define SDL_memset