linux_joystick.c
Go to the documentation of this file.
1 //========================================================================
2 // GLFW 3.3 Linux - www.glfw.org
3 //------------------------------------------------------------------------
4 // Copyright (c) 2002-2006 Marcus Geelnard
5 // Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org>
6 //
7 // This software is provided 'as-is', without any express or implied
8 // warranty. In no event will the authors be held liable for any damages
9 // arising from the use of this software.
10 //
11 // Permission is granted to anyone to use this software for any purpose,
12 // including commercial applications, and to alter it and redistribute it
13 // freely, subject to the following restrictions:
14 //
15 // 1. The origin of this software must not be misrepresented; you must not
16 // claim that you wrote the original software. If you use this software
17 // in a product, an acknowledgment in the product documentation would
18 // be appreciated but is not required.
19 //
20 // 2. Altered source versions must be plainly marked as such, and must not
21 // be misrepresented as being the original software.
22 //
23 // 3. This notice may not be removed or altered from any source
24 // distribution.
25 //
26 //========================================================================
27 
28 #include "internal.h"
29 
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/inotify.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <dirent.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 
41 #ifndef SYN_DROPPED // < v2.6.39 kernel headers
42 // Workaround for CentOS-6, which is supported till 2020-11-30, but still on v2.6.32
43 #define SYN_DROPPED 3
44 #endif
45 
46 // Apply an EV_KEY event to the specified joystick
47 //
48 static void handleKeyEvent(_GLFWjoystick* js, int code, int value)
49 {
51  js->linjs.keyMap[code - BTN_MISC],
52  value ? GLFW_PRESS : GLFW_RELEASE);
53 }
54 
55 // Apply an EV_ABS event to the specified joystick
56 //
57 static void handleAbsEvent(_GLFWjoystick* js, int code, int value)
58 {
59  const int index = js->linjs.absMap[code];
60 
61  if (code >= ABS_HAT0X && code <= ABS_HAT3Y)
62  {
63  static const char stateMap[3][3] =
64  {
68  };
69 
70  const int hat = (code - ABS_HAT0X) / 2;
71  const int axis = (code - ABS_HAT0X) % 2;
72  int* state = js->linjs.hats[hat];
73 
74  // NOTE: Looking at several input drivers, it seems all hat events use
75  // -1 for left / up, 0 for centered and 1 for right / down
76  if (value == 0)
77  state[axis] = 0;
78  else if (value < 0)
79  state[axis] = 1;
80  else if (value > 0)
81  state[axis] = 2;
82 
83  _glfwInputJoystickHat(js, index, stateMap[state[0]][state[1]]);
84  }
85  else
86  {
87  const struct input_absinfo* info = &js->linjs.absInfo[code];
88  float normalized = value;
89 
90  const int range = info->maximum - info->minimum;
91  if (range)
92  {
93  // Normalize to 0.0 -> 1.0
94  normalized = (normalized - info->minimum) / range;
95  // Normalize to -1.0 -> 1.0
96  normalized = normalized * 2.0f - 1.0f;
97  }
98 
99  _glfwInputJoystickAxis(js, index, normalized);
100  }
101 }
102 
103 // Poll state of absolute axes
104 //
105 static void pollAbsState(_GLFWjoystick* js)
106 {
107  int code;
108 
109  for (code = 0; code < ABS_CNT; code++)
110  {
111  if (js->linjs.absMap[code] < 0)
112  continue;
113 
114  struct input_absinfo* info = &js->linjs.absInfo[code];
115 
116  if (ioctl(js->linjs.fd, EVIOCGABS(code), info) < 0)
117  continue;
118 
119  handleAbsEvent(js, code, info->value);
120  }
121 }
122 
123 #define isBitSet(bit, arr) (arr[(bit) / 8] & (1 << ((bit) % 8)))
124 
125 // Attempt to open the specified joystick device
126 //
127 static GLFWbool openJoystickDevice(const char* path)
128 {
129  int jid, code;
130  char name[256] = "";
131  char guid[33] = "";
132  char evBits[(EV_CNT + 7) / 8] = {0};
133  char keyBits[(KEY_CNT + 7) / 8] = {0};
134  char absBits[(ABS_CNT + 7) / 8] = {0};
135  int axisCount = 0, buttonCount = 0, hatCount = 0;
136  struct input_id id;
137  _GLFWjoystickLinux linjs = {0};
138  _GLFWjoystick* js = NULL;
139 
140  for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
141  {
142  if (!_glfw.joysticks[jid].present)
143  continue;
144  if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0)
145  return GLFW_FALSE;
146  }
147 
148  linjs.fd = open(path, O_RDONLY | O_NONBLOCK);
149  if (linjs.fd == -1)
150  return GLFW_FALSE;
151 
152  if (ioctl(linjs.fd, EVIOCGBIT(0, sizeof(evBits)), evBits) < 0 ||
153  ioctl(linjs.fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits) < 0 ||
154  ioctl(linjs.fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits) < 0 ||
155  ioctl(linjs.fd, EVIOCGID, &id) < 0)
156  {
158  "Linux: Failed to query input device: %s",
159  strerror(errno));
160  close(linjs.fd);
161  return GLFW_FALSE;
162  }
163 
164  // Ensure this device supports the events expected of a joystick
165  if (!isBitSet(EV_KEY, evBits) || !isBitSet(EV_ABS, evBits))
166  {
167  close(linjs.fd);
168  return GLFW_FALSE;
169  }
170 
171  if (ioctl(linjs.fd, EVIOCGNAME(sizeof(name)), name) < 0)
172  strncpy(name, "Unknown", sizeof(name));
173 
174  // Generate a joystick GUID that matches the SDL 2.0.5+ one
175  if (id.vendor && id.product && id.version)
176  {
177  sprintf(guid, "%02x%02x0000%02x%02x0000%02x%02x0000%02x%02x0000",
178  id.bustype & 0xff, id.bustype >> 8,
179  id.vendor & 0xff, id.vendor >> 8,
180  id.product & 0xff, id.product >> 8,
181  id.version & 0xff, id.version >> 8);
182  }
183  else
184  {
185  sprintf(guid, "%02x%02x0000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00",
186  id.bustype & 0xff, id.bustype >> 8,
187  name[0], name[1], name[2], name[3],
188  name[4], name[5], name[6], name[7],
189  name[8], name[9], name[10]);
190  }
191 
192  for (code = BTN_MISC; code < KEY_CNT; code++)
193  {
194  if (!isBitSet(code, keyBits))
195  continue;
196 
197  linjs.keyMap[code - BTN_MISC] = buttonCount;
198  buttonCount++;
199  }
200 
201  for (code = 0; code < ABS_CNT; code++)
202  {
203  linjs.absMap[code] = -1;
204  if (!isBitSet(code, absBits))
205  continue;
206 
207  if (code >= ABS_HAT0X && code <= ABS_HAT3Y)
208  {
209  linjs.absMap[code] = hatCount;
210  hatCount++;
211  // Skip the Y axis
212  code++;
213  }
214  else
215  {
216  if (ioctl(linjs.fd, EVIOCGABS(code), &linjs.absInfo[code]) < 0)
217  continue;
218 
219  linjs.absMap[code] = axisCount;
220  axisCount++;
221  }
222  }
223 
224  js = _glfwAllocJoystick(name, guid, axisCount, buttonCount, hatCount);
225  if (!js)
226  {
227  close(linjs.fd);
228  return GLFW_FALSE;
229  }
230 
231  strncpy(linjs.path, path, sizeof(linjs.path) - 1);
232  memcpy(&js->linjs, &linjs, sizeof(linjs));
233 
234  pollAbsState(js);
235 
237  return GLFW_TRUE;
238 }
239 
240 #undef isBitSet
241 
242 // Frees all resources associated with the specified joystick
243 //
244 static void closeJoystick(_GLFWjoystick* js)
245 {
246  close(js->linjs.fd);
247  _glfwFreeJoystick(js);
249 }
250 
251 // Lexically compare joysticks by name; used by qsort
252 //
253 static int compareJoysticks(const void* fp, const void* sp)
254 {
255  const _GLFWjoystick* fj = fp;
256  const _GLFWjoystick* sj = sp;
257  return strcmp(fj->linjs.path, sj->linjs.path);
258 }
259 
260 
264 
265 // Initialize joystick interface
266 //
268 {
269  DIR* dir;
270  int count = 0;
271  const char* dirname = "/dev/input";
272 
273  _glfw.linjs.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
274  if (_glfw.linjs.inotify > 0)
275  {
276  // HACK: Register for IN_ATTRIB to get notified when udev is done
277  // This works well in practice but the true way is libudev
278 
279  _glfw.linjs.watch = inotify_add_watch(_glfw.linjs.inotify,
280  dirname,
281  IN_CREATE | IN_ATTRIB | IN_DELETE);
282  }
283 
284  // Continue without device connection notifications if inotify fails
285 
286  if (regcomp(&_glfw.linjs.regex, "^event[0-9]\\+$", 0) != 0)
287  {
288  _glfwInputError(GLFW_PLATFORM_ERROR, "Linux: Failed to compile regex");
289  return GLFW_FALSE;
290  }
291 
292  dir = opendir(dirname);
293  if (dir)
294  {
295  struct dirent* entry;
296 
297  while ((entry = readdir(dir)))
298  {
299  regmatch_t match;
300 
301  if (regexec(&_glfw.linjs.regex, entry->d_name, 1, &match, 0) != 0)
302  continue;
303 
304  char path[PATH_MAX];
305 
306  snprintf(path, sizeof(path), "%s/%s", dirname, entry->d_name);
307 
308  if (openJoystickDevice(path))
309  count++;
310  }
311 
312  closedir(dir);
313  }
314 
315  // Continue with no joysticks if enumeration fails
316 
317  qsort(_glfw.joysticks, count, sizeof(_GLFWjoystick), compareJoysticks);
318  return GLFW_TRUE;
319 }
320 
321 // Close all opened joystick handles
322 //
324 {
325  int jid;
326 
327  for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
328  {
329  _GLFWjoystick* js = _glfw.joysticks + jid;
330  if (js->present)
331  closeJoystick(js);
332  }
333 
334  regfree(&_glfw.linjs.regex);
335 
336  if (_glfw.linjs.inotify > 0)
337  {
338  if (_glfw.linjs.watch > 0)
339  inotify_rm_watch(_glfw.linjs.inotify, _glfw.linjs.watch);
340 
341  close(_glfw.linjs.inotify);
342  }
343 }
344 
346 {
347  ssize_t offset = 0;
348  char buffer[16384];
349 
350  if (_glfw.linjs.inotify <= 0)
351  return;
352 
353  const ssize_t size = read(_glfw.linjs.inotify, buffer, sizeof(buffer));
354 
355  while (size > offset)
356  {
357  regmatch_t match;
358  const struct inotify_event* e = (struct inotify_event*) (buffer + offset);
359 
360  offset += sizeof(struct inotify_event) + e->len;
361 
362  if (regexec(&_glfw.linjs.regex, e->name, 1, &match, 0) != 0)
363  continue;
364 
365  char path[PATH_MAX];
366  snprintf(path, sizeof(path), "/dev/input/%s", e->name);
367 
368  if (e->mask & (IN_CREATE | IN_ATTRIB))
369  openJoystickDevice(path);
370  else if (e->mask & IN_DELETE)
371  {
372  int jid;
373 
374  for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
375  {
376  if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0)
377  {
379  break;
380  }
381  }
382  }
383  }
384 }
385 
386 
390 
392 {
393  // Read all queued events (non-blocking)
394  for (;;)
395  {
396  struct input_event e;
397 
398  errno = 0;
399  if (read(js->linjs.fd, &e, sizeof(e)) < 0)
400  {
401  // Reset the joystick slot if the device was disconnected
402  if (errno == ENODEV)
403  closeJoystick(js);
404 
405  break;
406  }
407 
408  if (e.type == EV_SYN)
409  {
410  if (e.code == SYN_DROPPED)
411  _glfw.linjs.dropped = GLFW_TRUE;
412  else if (e.code == SYN_REPORT)
413  {
414  _glfw.linjs.dropped = GLFW_FALSE;
415  pollAbsState(js);
416  }
417  }
418 
419  if (_glfw.linjs.dropped)
420  continue;
421 
422  if (e.type == EV_KEY)
423  handleKeyEvent(js, e.code, e.value);
424  else if (e.type == EV_ABS)
425  handleAbsEvent(js, e.code, e.value);
426  }
427 
428  return js->present;
429 }
430 
432 {
433 }
434 
void _glfwDetectJoystickConnectionLinux(void)
#define GLFW_HAT_LEFT
Definition: glfw3.h:325
void _glfwPlatformUpdateGamepadGUID(char *guid)
void _glfwInputJoystickButton(_GLFWjoystick *js, int button, char value)
Definition: input.c:385
GLuint const GLchar * name
#define GLFW_HAT_UP
Definition: glfw3.h:322
void _glfwFreeJoystick(_GLFWjoystick *js)
Definition: input.c:447
static int compareJoysticks(const void *fp, const void *sp)
GLsizei const GLchar *const * path
Definition: glext.h:4276
GLfloat value
#define GLFW_FALSE
Zero.
Definition: glfw3.h:287
_GLFWjoystick * _glfwAllocJoystick(const char *name, const char *guid, int axisCount, int buttonCount, int hatCount)
Definition: input.c:411
GLFWbool present
Definition: internal.h:478
static void pollAbsState(_GLFWjoystick *js)
GLuint entry
Definition: glext.h:10991
e
Definition: rmse.py:177
int GLFWbool
Definition: internal.h:61
GLenum GLfloat * buffer
void _glfwInputJoystick(_GLFWjoystick *js, int event)
Definition: input.c:368
GLuint index
#define GLFW_RELEASE
The key or mouse button was released.
Definition: glfw3.h:297
def info(name, value, persistent=False)
Definition: test.py:301
#define GLFW_DISCONNECTED
Definition: glfw3.h:1059
static void closeJoystick(_GLFWjoystick *js)
#define GLFW_HAT_RIGHT
Definition: glfw3.h:323
#define GLFW_CONNECTED
Definition: glfw3.h:1058
GLenum mode
GLsizeiptr size
#define GLFW_PLATFORM_ERROR
A platform-specific error occurred that does not match any of the more specific categories.
Definition: glfw3.h:726
_GLFWlibrary _glfw
Definition: init.c:44
int _glfwPlatformPollJoystick(_GLFWjoystick *js, int mode)
#define GLFW_HAT_LEFT_UP
Definition: glfw3.h:328
static void handleAbsEvent(_GLFWjoystick *js, int code, int value)
#define isBitSet(bit, arr)
#define GLFW_JOYSTICK_LAST
Definition: glfw3.h:574
#define GLFW_HAT_RIGHT_DOWN
Definition: glfw3.h:327
void _glfwInputError(int code, const char *format,...)
Definition: init.c:129
#define GLFW_HAT_DOWN
Definition: glfw3.h:324
void _glfwInputJoystickHat(_GLFWjoystick *js, int hat, char value)
Definition: input.c:392
#define GLFW_HAT_RIGHT_UP
Definition: glfw3.h:326
void _glfwInputJoystickAxis(_GLFWjoystick *js, int axis, float value)
Definition: input.c:378
#define GLFW_TRUE
One.
Definition: glfw3.h:279
#define GLFW_HAT_LEFT_DOWN
Definition: glfw3.h:329
static void handleKeyEvent(_GLFWjoystick *js, int code, int value)
GLint GLenum GLboolean normalized
#define GLFW_HAT_CENTERED
Definition: glfw3.h:321
GLint GLsizei count
void _glfwTerminateJoysticksLinux(void)
unsigned char * hats
Definition: internal.h:483
GLsizei range
#define NULL
Definition: tinycthread.c:47
_GLFWjoystick joysticks[GLFW_JOYSTICK_LAST+1]
Definition: internal.h:531
GLFWbool _glfwInitJoysticksLinux(void)
#define GLFW_PRESS
The key or mouse button was pressed.
Definition: glfw3.h:304
#define SYN_DROPPED
GLintptr offset
static GLFWbool openJoystickDevice(const char *path)


librealsense2
Author(s): Sergey Dorodnicov , Doron Hirshberg , Mark Horn , Reagan Lopez , Itay Carpis
autogenerated on Mon May 3 2021 02:47:21