diff --git a/README.md b/README.md index 933076ab..37babfce 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,8 @@ GLFW bundles a number of dependencies in the `deps/` directory. - [X11] Bugfix: Full screen override redirect windows were not always positioned over the specified monitor - [X11] Bugfix: Character input did not work for the default `"C"` locale + - [X11] Bugfix: Joysticks connected after `glfwInit` were not detected + (temporary inotify solution until proper libudev solution) ## Contact diff --git a/src/linux_joystick.c b/src/linux_joystick.c index 136a8c37..c93184eb 100644 --- a/src/linux_joystick.c +++ b/src/linux_joystick.c @@ -27,14 +27,14 @@ #include "internal.h" -#ifdef __linux__ +#if defined(__linux__) #include #include #include +#include #include #include -#include #include #include #include @@ -45,16 +45,34 @@ // Attempt to open the specified joystick device // -static int openJoystickDevice(int joy, const char* path) +static void openJoystickDevice(const char* path) { -#ifdef __linux__ +#if defined(__linux__) char axisCount, buttonCount; char name[256]; - int fd, version; + int joy, fd, version; + + for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + { + if (!_glfw.linux_js[joy].present) + continue; + + if (strcmp(_glfw.linux_js[joy].path, path) == 0) + return; + } + + for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + { + if (!_glfw.linux_js[joy].present) + break; + } + + if (joy > GLFW_JOYSTICK_LAST) + return; fd = open(path, O_RDONLY | O_NONBLOCK); if (fd == -1) - return GL_FALSE; + return; _glfw.linux_js[joy].fd = fd; @@ -64,13 +82,14 @@ static int openJoystickDevice(int joy, const char* path) { // It's an old 0.x interface (we don't support it) close(fd); - return GL_FALSE; + return; } if (ioctl(fd, JSIOCGNAME(sizeof(name)), name) < 0) strncpy(name, "Unknown", sizeof(name)); _glfw.linux_js[joy].name = strdup(name); + _glfw.linux_js[joy].path = strdup(path); ioctl(fd, JSIOCGAXES, &axisCount); _glfw.linux_js[joy].axisCount = (int) axisCount; @@ -83,17 +102,34 @@ static int openJoystickDevice(int joy, const char* path) _glfw.linux_js[joy].present = GL_TRUE; #endif // __linux__ - - return GL_TRUE; } // Polls for and processes events for all present joysticks // static void pollJoystickEvents(void) { -#ifdef __linux__ +#if defined(__linux__) int i; struct js_event e; + ssize_t offset = 0; + char buffer[16384]; + + const ssize_t size = read(_glfw.x11.inotify.fd, buffer, sizeof(buffer)); + + while (size > offset) + { + regmatch_t match; + const struct inotify_event* e = (struct inotify_event*) (buffer + offset); + + if (regexec(&_glfw.x11.inotify.regex, e->name, 1, &match, 0) == 0) + { + char path[20]; + snprintf(path, sizeof(path), "/dev/input/%s", e->name); + openJoystickDevice(path); + } + + offset += sizeof(struct inotify_event) + e->len; + } for (i = 0; i <= GLFW_JOYSTICK_LAST; i++) { @@ -113,6 +149,7 @@ static void pollJoystickEvents(void) free(_glfw.linux_js[i].axes); free(_glfw.linux_js[i].buttons); free(_glfw.linux_js[i].name); + free(_glfw.linux_js[i].path); memset(&_glfw.linux_js[i], 0, sizeof(_glfw.linux_js[i])); } @@ -150,58 +187,69 @@ static void pollJoystickEvents(void) // Initialize joystick interface // -void _glfwInitJoysticks(void) +int _glfwInitJoysticks(void) { -#ifdef __linux__ - int joy = 0; - size_t i; - regex_t regex; +#if defined(__linux__) + const char* dirname = "/dev/input"; DIR* dir; - const char* dirs[] = - { - "/dev/input", - "/dev" - }; + struct dirent* entry; - if (regcomp(®ex, "^js[0-9]\\+$", 0) != 0) + _glfw.x11.inotify.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (_glfw.x11.inotify.fd == -1) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to initialize inotify"); + return GL_FALSE; + } + + // HACK: Register for IN_ATTRIB as well to get notified when udev is done + // This works well in practice but the true way is libudev + + _glfw.x11.inotify.wd = inotify_add_watch(_glfw.x11.inotify.fd, + dirname, + IN_CREATE | IN_ATTRIB); + if (_glfw.x11.inotify.wd == -1) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to add watch to %s", dirname); + return GL_FALSE; + } + + if (regcomp(&_glfw.x11.inotify.regex, "^js[0-9]\\+$", 0) != 0) { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to compile regex"); - return; + return GL_FALSE; } - for (i = 0; i < sizeof(dirs) / sizeof(dirs[0]); i++) + dir = opendir(dirname); + if (!dir) { - struct dirent* entry; + _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to open %s", dirname); + return GL_FALSE; + } - dir = opendir(dirs[i]); - if (!dir) + while ((entry = readdir(dir))) + { + char path[20]; + regmatch_t match; + + if (regexec(&_glfw.x11.inotify.regex, entry->d_name, 1, &match, 0) != 0) continue; - while ((entry = readdir(dir))) - { - char path[20]; - regmatch_t match; - - if (regexec(®ex, entry->d_name, 1, &match, 0) != 0) - continue; - - snprintf(path, sizeof(path), "%s/%s", dirs[i], entry->d_name); - if (openJoystickDevice(joy, path)) - joy++; - } - - closedir(dir); + snprintf(path, sizeof(path), "%s/%s", dirname, entry->d_name); + openJoystickDevice(path); } - regfree(®ex); + closedir(dir); #endif // __linux__ + + return GL_TRUE; } // Close all opened joystick handles // void _glfwTerminateJoysticks(void) { -#ifdef __linux__ +#if defined(__linux__) int i; for (i = 0; i <= GLFW_JOYSTICK_LAST; i++) @@ -212,8 +260,17 @@ void _glfwTerminateJoysticks(void) free(_glfw.linux_js[i].axes); free(_glfw.linux_js[i].buttons); free(_glfw.linux_js[i].name); + free(_glfw.linux_js[i].path); } } + + regfree(&_glfw.x11.inotify.regex); + + if (_glfw.x11.inotify.wd > 0) + close(_glfw.x11.inotify.wd); + + if (_glfw.x11.inotify.fd > 0) + close(_glfw.x11.inotify.fd); #endif // __linux__ } diff --git a/src/linux_joystick.h b/src/linux_joystick.h index 8f713d84..b74a67ef 100644 --- a/src/linux_joystick.h +++ b/src/linux_joystick.h @@ -42,10 +42,11 @@ typedef struct _GLFWjoystickLinux unsigned char* buttons; int buttonCount; char* name; + char* path; } _GLFWjoystickLinux; -void _glfwInitJoysticks(void); +int _glfwInitJoysticks(void); void _glfwTerminateJoysticks(void); #endif // _linux_joystick_h_ diff --git a/src/x11_init.c b/src/x11_init.c index 2a122229..e2063a1a 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -749,8 +749,10 @@ int _glfwPlatformInit(void) if (!_glfwInitContextAPI()) return GL_FALSE; + if (!_glfwInitJoysticks()) + return GL_FALSE; + _glfwInitTimer(); - _glfwInitJoysticks(); return GL_TRUE; } diff --git a/src/x11_platform.h b/src/x11_platform.h index 20be5c62..17503b72 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -31,6 +31,8 @@ #include #include #include +#include + #include #include #include @@ -214,6 +216,14 @@ typedef struct _GLFWlibraryX11 int versionMinor; } xinerama; +#if defined(__linux__) + struct { + int fd; + int wd; + regex_t regex; + } inotify; +#endif + } _GLFWlibraryX11;