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