mirror of
https://github.com/SFML/SFML.git
synced 2024-11-29 06:41:05 +08:00
Now using inotify on Linux to avoid constantly polling joystick connections (#96)
This commit is contained in:
parent
af81ac60f3
commit
6ec100aeb7
@ -93,6 +93,7 @@ void JoystickManager::update()
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
JoystickManager::JoystickManager()
|
JoystickManager::JoystickManager()
|
||||||
{
|
{
|
||||||
|
JoystickImpl::initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -104,6 +105,8 @@ JoystickManager::~JoystickManager()
|
|||||||
if (m_joysticks[i].state.connected)
|
if (m_joysticks[i].state.connected)
|
||||||
m_joysticks[i].joystick.close();
|
m_joysticks[i].joystick.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JoystickImpl::cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace priv
|
} // namespace priv
|
||||||
|
@ -26,46 +26,133 @@
|
|||||||
// Headers
|
// Headers
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
#include <SFML/Window/JoystickImpl.hpp>
|
#include <SFML/Window/JoystickImpl.hpp>
|
||||||
|
#include <SFML/System/Err.hpp>
|
||||||
|
#include <sys/inotify.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
|
|
||||||
|
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 sf
|
||||||
{
|
{
|
||||||
namespace priv
|
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)
|
bool JoystickImpl::isConnected(unsigned int index)
|
||||||
{
|
{
|
||||||
char name[32];
|
// First check if new joysticks were added/removed since last update
|
||||||
std::sprintf(name, "/dev/input/js%u", index);
|
if (canRead(notifyFd))
|
||||||
|
{
|
||||||
|
// Don't bother decomposing and interpreting the filename, just do a full scan
|
||||||
|
updatePluggedList();
|
||||||
|
|
||||||
struct stat info;
|
// Flush all the pending events
|
||||||
return stat(name, &info) == 0;
|
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)
|
bool JoystickImpl::open(unsigned int index)
|
||||||
{
|
{
|
||||||
char name[32];
|
if (plugged[index])
|
||||||
std::sprintf(name, "/dev/input/js%u", index);
|
|
||||||
|
|
||||||
m_file = ::open(name, O_RDONLY);
|
|
||||||
if (m_file > 0)
|
|
||||||
{
|
{
|
||||||
// Use non-blocking mode
|
char name[32];
|
||||||
fcntl(m_file, F_SETFL, O_NONBLOCK);
|
std::snprintf(name, sizeof(name), "/dev/input/js%u", index);
|
||||||
|
|
||||||
// Retrieve the axes mapping
|
// Open the joystick's file descriptor (read-only and non-blocking)
|
||||||
ioctl(m_file, JSIOCGAXMAP, m_mapping);
|
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
|
// Reset the joystick state
|
||||||
m_state = JoystickState();
|
m_state = JoystickState();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -49,6 +49,18 @@ class JoystickImpl
|
|||||||
{
|
{
|
||||||
public :
|
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
|
/// \brief Check if a joystick is currently connected
|
||||||
///
|
///
|
||||||
|
@ -30,15 +30,16 @@
|
|||||||
#include <SFML/Window/OSX/HIDInputManager.hpp>
|
#include <SFML/Window/OSX/HIDInputManager.hpp>
|
||||||
#include <SFML/Window/OSX/HIDJoystickManager.hpp>
|
#include <SFML/Window/OSX/HIDJoystickManager.hpp>
|
||||||
|
|
||||||
// Translation unit namespace
|
|
||||||
namespace {
|
namespace
|
||||||
////////////////////////////////////////////////////////////
|
{
|
||||||
bool JoystickButtonSortPredicate(IOHIDElementRef b1, IOHIDElementRef b2)
|
bool JoystickButtonSortPredicate(IOHIDElementRef b1, IOHIDElementRef b2)
|
||||||
{
|
{
|
||||||
return IOHIDElementGetUsage(b1) < IOHIDElementGetUsage(b2);
|
return IOHIDElementGetUsage(b1) < IOHIDElementGetUsage(b2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace sf
|
namespace sf
|
||||||
{
|
{
|
||||||
namespace priv
|
namespace priv
|
||||||
@ -47,6 +48,20 @@ namespace priv
|
|||||||
JoystickImpl::Location JoystickImpl::m_locationIDs[sf::Joystick::Count] = { 0 };
|
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)
|
bool JoystickImpl::isConnected(unsigned int index)
|
||||||
{
|
{
|
||||||
|
@ -46,6 +46,18 @@ class JoystickImpl
|
|||||||
{
|
{
|
||||||
public :
|
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
|
/// \brief Check if a joystick is currently connected
|
||||||
///
|
///
|
||||||
|
@ -35,10 +35,9 @@ namespace
|
|||||||
{
|
{
|
||||||
struct ConnectionCache
|
struct ConnectionCache
|
||||||
{
|
{
|
||||||
ConnectionCache() : connected(false), firstTime(true) {}
|
ConnectionCache() : connected(false) {}
|
||||||
bool connected;
|
bool connected;
|
||||||
sf::Clock timer;
|
sf::Clock timer;
|
||||||
bool firstTime;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const sf::Time connectionRefreshDelay = sf::milliseconds(500);
|
const sf::Time connectionRefreshDelay = sf::milliseconds(500);
|
||||||
@ -49,6 +48,33 @@ namespace sf
|
|||||||
{
|
{
|
||||||
namespace priv
|
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)
|
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
|
// because of a strange (buggy?) behaviour of joyGetPosEx when joysticks
|
||||||
// are just plugged/unplugged -- it takes really long and kills the app performances
|
// are just plugged/unplugged -- it takes really long and kills the app performances
|
||||||
ConnectionCache& cache = connectionCache[index];
|
ConnectionCache& cache = connectionCache[index];
|
||||||
if (cache.firstTime || (cache.timer.getElapsedTime() > connectionRefreshDelay))
|
if (cache.timer.getElapsedTime() > connectionRefreshDelay)
|
||||||
{
|
{
|
||||||
cache.timer.restart();
|
cache.timer.restart();
|
||||||
cache.firstTime = false;
|
|
||||||
|
|
||||||
JOYINFOEX joyInfo;
|
JOYINFOEX joyInfo;
|
||||||
joyInfo.dwSize = sizeof(joyInfo);
|
joyInfo.dwSize = sizeof(joyInfo);
|
||||||
|
@ -54,6 +54,18 @@ class JoystickImpl
|
|||||||
{
|
{
|
||||||
public :
|
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
|
/// \brief Check if a joystick is currently connected
|
||||||
///
|
///
|
||||||
|
Loading…
Reference in New Issue
Block a user