From 6ec100aeb7c714da606e05fc908d03a1fb123163 Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Sun, 30 Jun 2013 14:34:00 +0200 Subject: [PATCH] Now using inotify on Linux to avoid constantly polling joystick connections (#96) --- src/SFML/Window/JoystickManager.cpp | 3 + src/SFML/Window/Linux/JoystickImpl.cpp | 119 +++++++++++++++++++++---- src/SFML/Window/Linux/JoystickImpl.hpp | 12 +++ src/SFML/Window/OSX/JoystickImpl.cpp | 21 ++++- src/SFML/Window/OSX/JoystickImpl.hpp | 12 +++ src/SFML/Window/Win32/JoystickImpl.cpp | 33 ++++++- src/SFML/Window/Win32/JoystickImpl.hpp | 12 +++ 7 files changed, 189 insertions(+), 23 deletions(-) diff --git a/src/SFML/Window/JoystickManager.cpp b/src/SFML/Window/JoystickManager.cpp index 81a63d1ec..2ddcdfaae 100644 --- a/src/SFML/Window/JoystickManager.cpp +++ b/src/SFML/Window/JoystickManager.cpp @@ -93,6 +93,7 @@ void JoystickManager::update() //////////////////////////////////////////////////////////// JoystickManager::JoystickManager() { + JoystickImpl::initialize(); } @@ -104,6 +105,8 @@ JoystickManager::~JoystickManager() if (m_joysticks[i].state.connected) m_joysticks[i].joystick.close(); } + + JoystickImpl::cleanup(); } } // namespace priv diff --git a/src/SFML/Window/Linux/JoystickImpl.cpp b/src/SFML/Window/Linux/JoystickImpl.cpp index ead00b42a..4db4b9133 100644 --- a/src/SFML/Window/Linux/JoystickImpl.cpp +++ b/src/SFML/Window/Linux/JoystickImpl.cpp @@ -26,46 +26,133 @@ // Headers //////////////////////////////////////////////////////////// #include +#include +#include #include #include #include #include +namespace +{ + int notifyFd = -1; + int inputFd = -1; + bool plugged[sf::Joystick::Count]; + + void updatePluggedList() + { + for (unsigned int i = 0; i < sf::Joystick::Count; ++i) + { + char name[32]; + std::snprintf(name, sizeof(name), "/dev/input/js%u", i); + struct stat info; + plugged[i] = (stat(name, &info) == 0); + } + } + + bool canRead(int descriptor) + { + fd_set set; + FD_ZERO(&set); + FD_SET(descriptor, &set); + timeval timeout = {0, 0}; + return select(descriptor + 1, &set, NULL, NULL, &timeout) > 0 && + FD_ISSET(notifyFd, &set); + } +} + + namespace sf { namespace priv { +//////////////////////////////////////////////////////////// +void JoystickImpl::initialize() +{ + // Reset the array of plugged joysticks + std::fill(plugged, plugged + Joystick::Count, false); + + // Create the inotify instance + notifyFd = inotify_init(); + if (notifyFd < 0) + { + err() << "Failed to initialize inotify, joystick connections and disconnections won't be notified" << std::endl; + return; + } + + // Watch nodes created and deleted in the /dev/input directory + inputFd = inotify_add_watch(notifyFd, "/dev/input", IN_CREATE | IN_DELETE); + if (inputFd < 0) + { + err() << "Failed to initialize inotify, joystick connections and disconnections won't be notified" << std::endl; + return; + } + + // Do an initial scan + updatePluggedList(); +} + + +//////////////////////////////////////////////////////////// +void JoystickImpl::cleanup() +{ + // Stop watching the /dev/input directory + if (inputFd >= 0) + inotify_rm_watch(notifyFd, inputFd); + + // Close the inotify file descriptor + if (inputFd >= 0) + ::close(notifyFd); +} + + //////////////////////////////////////////////////////////// bool JoystickImpl::isConnected(unsigned int index) { - char name[32]; - std::sprintf(name, "/dev/input/js%u", index); + // First check if new joysticks were added/removed since last update + if (canRead(notifyFd)) + { + // Don't bother decomposing and interpreting the filename, just do a full scan + updatePluggedList(); - struct stat info; - return stat(name, &info) == 0; + // Flush all the pending events + while (canRead(notifyFd)) + { + char buffer[128]; + read(notifyFd, buffer, sizeof(buffer)); + } + } + + // Then check if the joystick is connected + return plugged[index]; } //////////////////////////////////////////////////////////// bool JoystickImpl::open(unsigned int index) { - char name[32]; - std::sprintf(name, "/dev/input/js%u", index); - - m_file = ::open(name, O_RDONLY); - if (m_file > 0) + if (plugged[index]) { - // Use non-blocking mode - fcntl(m_file, F_SETFL, O_NONBLOCK); + char name[32]; + std::snprintf(name, sizeof(name), "/dev/input/js%u", index); - // Retrieve the axes mapping - ioctl(m_file, JSIOCGAXMAP, m_mapping); + // Open the joystick's file descriptor (read-only and non-blocking) + m_file = ::open(name, O_RDONLY | O_NONBLOCK); + if (m_file >= 0) + { + // Retrieve the axes mapping + ioctl(m_file, JSIOCGAXMAP, m_mapping); - // Reset the joystick state - m_state = JoystickState(); + // Reset the joystick state + m_state = JoystickState(); - return true; + return true; + } + else + { + return false; + } } else { diff --git a/src/SFML/Window/Linux/JoystickImpl.hpp b/src/SFML/Window/Linux/JoystickImpl.hpp index c0dff11c8..869f1857d 100644 --- a/src/SFML/Window/Linux/JoystickImpl.hpp +++ b/src/SFML/Window/Linux/JoystickImpl.hpp @@ -49,6 +49,18 @@ class JoystickImpl { public : + //////////////////////////////////////////////////////////// + /// \brief Perform the global initialization of the joystick module + /// + //////////////////////////////////////////////////////////// + static void initialize(); + + //////////////////////////////////////////////////////////// + /// \brief Perform the global cleanup of the joystick module + /// + //////////////////////////////////////////////////////////// + static void cleanup(); + //////////////////////////////////////////////////////////// /// \brief Check if a joystick is currently connected /// diff --git a/src/SFML/Window/OSX/JoystickImpl.cpp b/src/SFML/Window/OSX/JoystickImpl.cpp index a5531189e..235ab4dd3 100644 --- a/src/SFML/Window/OSX/JoystickImpl.cpp +++ b/src/SFML/Window/OSX/JoystickImpl.cpp @@ -30,15 +30,16 @@ #include #include -// Translation unit namespace -namespace { - //////////////////////////////////////////////////////////// + +namespace +{ bool JoystickButtonSortPredicate(IOHIDElementRef b1, IOHIDElementRef b2) { return IOHIDElementGetUsage(b1) < IOHIDElementGetUsage(b2); } } + namespace sf { namespace priv @@ -47,6 +48,20 @@ namespace priv JoystickImpl::Location JoystickImpl::m_locationIDs[sf::Joystick::Count] = { 0 }; +//////////////////////////////////////////////////////////// +void JoystickImpl::initialize() +{ + // Nothing to do +} + + +//////////////////////////////////////////////////////////// +void JoystickImpl::cleanup() +{ + // Nothing to do +} + + //////////////////////////////////////////////////////////// bool JoystickImpl::isConnected(unsigned int index) { diff --git a/src/SFML/Window/OSX/JoystickImpl.hpp b/src/SFML/Window/OSX/JoystickImpl.hpp index d58da9d7e..fe1a8fa0c 100644 --- a/src/SFML/Window/OSX/JoystickImpl.hpp +++ b/src/SFML/Window/OSX/JoystickImpl.hpp @@ -46,6 +46,18 @@ class JoystickImpl { public : + //////////////////////////////////////////////////////////// + /// \brief Perform the global initialization of the joystick module + /// + //////////////////////////////////////////////////////////// + static void initialize(); + + //////////////////////////////////////////////////////////// + /// \brief Perform the global cleanup of the joystick module + /// + //////////////////////////////////////////////////////////// + static void cleanup(); + //////////////////////////////////////////////////////////// /// \brief Check if a joystick is currently connected /// diff --git a/src/SFML/Window/Win32/JoystickImpl.cpp b/src/SFML/Window/Win32/JoystickImpl.cpp index 8469b3ea1..c2645e8fe 100644 --- a/src/SFML/Window/Win32/JoystickImpl.cpp +++ b/src/SFML/Window/Win32/JoystickImpl.cpp @@ -35,10 +35,9 @@ namespace { struct ConnectionCache { - ConnectionCache() : connected(false), firstTime(true) {} + ConnectionCache() : connected(false) {} bool connected; sf::Clock timer; - bool firstTime; }; const sf::Time connectionRefreshDelay = sf::milliseconds(500); @@ -49,6 +48,33 @@ namespace sf { namespace priv { +//////////////////////////////////////////////////////////// +void JoystickImpl::initialize() +{ + // Perform the initial scan and populate the connection cache + for (unsigned int i = 0; i < Joystick::Count; ++i) + { + ConnectionCache& cache = connectionCache[i]; + + // Check if the joystick is connected + JOYINFOEX joyInfo; + joyInfo.dwSize = sizeof(joyInfo); + joyInfo.dwFlags = 0; + cache.connected = joyGetPosEx(JOYSTICKID1 + i, &joyInfo) == JOYERR_NOERROR; + + // start the timeout + cache.timer.restart(); + } +} + + +//////////////////////////////////////////////////////////// +void JoystickImpl::cleanup() +{ + // Nothing to do +} + + //////////////////////////////////////////////////////////// bool JoystickImpl::isConnected(unsigned int index) { @@ -56,10 +82,9 @@ bool JoystickImpl::isConnected(unsigned int index) // because of a strange (buggy?) behaviour of joyGetPosEx when joysticks // are just plugged/unplugged -- it takes really long and kills the app performances ConnectionCache& cache = connectionCache[index]; - if (cache.firstTime || (cache.timer.getElapsedTime() > connectionRefreshDelay)) + if (cache.timer.getElapsedTime() > connectionRefreshDelay) { cache.timer.restart(); - cache.firstTime = false; JOYINFOEX joyInfo; joyInfo.dwSize = sizeof(joyInfo); diff --git a/src/SFML/Window/Win32/JoystickImpl.hpp b/src/SFML/Window/Win32/JoystickImpl.hpp index 79cb70355..1630f998f 100644 --- a/src/SFML/Window/Win32/JoystickImpl.hpp +++ b/src/SFML/Window/Win32/JoystickImpl.hpp @@ -54,6 +54,18 @@ class JoystickImpl { public : + //////////////////////////////////////////////////////////// + /// \brief Perform the global initialization of the joystick module + /// + //////////////////////////////////////////////////////////// + static void initialize(); + + //////////////////////////////////////////////////////////// + /// \brief Perform the global cleanup of the joystick module + /// + //////////////////////////////////////////////////////////// + static void cleanup(); + //////////////////////////////////////////////////////////// /// \brief Check if a joystick is currently connected ///