From 2b07dfb7895b5b5e5f464bb07465120d4190455e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Garapich?= Date: Fri, 3 Apr 2015 21:35:39 +0200 Subject: [PATCH] refs #401 Implementation of IJoystick for Linux Basically, the implementation uses the API described in the kernel documentation. It listens for new events that come from the device. It also supports joystick hotplugging. * https://www.kernel.org/doc/Documentation/input/joystick-api.txt --- src/blackinput/linux/joystick_linux.cpp | 116 +++++++++++++++++++++++- src/blackinput/linux/joystick_linux.h | 35 ++++++- src/blackinput/linux/keyboard_linux.cpp | 2 +- 3 files changed, 143 insertions(+), 10 deletions(-) diff --git a/src/blackinput/linux/joystick_linux.cpp b/src/blackinput/linux/joystick_linux.cpp index 97478d333..0de16d204 100644 --- a/src/blackinput/linux/joystick_linux.cpp +++ b/src/blackinput/linux/joystick_linux.cpp @@ -7,29 +7,135 @@ * contained in the LICENSE file. */ +#include "blackmisc/logmessage.h" #include "joystick_linux.h" +#include +#include +#include +#include +#include +#include +#include +using namespace BlackMisc; using namespace BlackMisc::Hardware; +namespace +{ + inline QString inputDevicesDir() + { + return QStringLiteral("/dev/input/"); + } +} + namespace BlackInput { CJoystickLinux::CJoystickLinux(QObject *parent) : - IJoystick(parent) + IJoystick(parent), + m_mapper(new QSignalMapper(this)), + m_inputWatcher(new QFileSystemWatcher(this)) { - } + connect(m_mapper, static_cast(&QSignalMapper::mapped), this, &CJoystickLinux::ps_readInput); - CJoystickLinux::~CJoystickLinux() - { + m_inputWatcher->addPath(inputDevicesDir()); + connect(m_inputWatcher, &QFileSystemWatcher::directoryChanged, this, &CJoystickLinux::ps_directoryChanged); + ps_directoryChanged(inputDevicesDir()); } void CJoystickLinux::startCapture() { + } void CJoystickLinux::triggerButton(const CJoystickButton button, bool isPressed) { - if(!isPressed) emit buttonUp(button); + if (!isPressed) emit buttonUp(button); else emit buttonDown(button); } + void CJoystickLinux::cleanupJoysticks() + { + for (auto it = m_joysticks.begin(); it != m_joysticks.end();) + { + if (!it.value()->exists()) + { + it.value()->deleteLater(); + it = m_joysticks.erase(it); + } + else + { + ++it; + } + } + } + + void CJoystickLinux::addJoystickDevice(const QString &path) + { + Q_ASSERT(!m_joysticks.contains(path)); + + QFile *joystick = new QFile(path, this); + if (joystick->open(QIODevice::ReadOnly)) + { + char name[256]; + if (ioctl(joystick->handle(), JSIOCGNAME(sizeof(name)), name) < 0) + { + strncpy(name, "Unknown", sizeof(name)); + } + + CLogMessage(this).info("Found joystick: %1") << name; + + fcntl(joystick->handle(), F_SETFL, O_NONBLOCK); + + /* Forward */ + struct js_event event; + while (joystick->read(reinterpret_cast(&event), sizeof(event)) == sizeof(event)) {} + + QSocketNotifier *notifier = new QSocketNotifier(joystick->handle(), QSocketNotifier::Read, joystick); + m_mapper->setMapping(notifier, joystick); + connect(notifier, &QSocketNotifier::activated, m_mapper, static_cast(&QSignalMapper::map)); + notifier->setEnabled(true); + + m_joysticks.insert(path, joystick); + } + else + { + joystick->deleteLater(); + } + } + + void CJoystickLinux::ps_directoryChanged(QString path) + { + cleanupJoysticks(); + + QDir dir(path, QLatin1String("js*"), 0, QDir::System); + for (const auto &entry : dir.entryInfoList()) + { + QString f = entry.absoluteFilePath(); + if (!m_joysticks.contains(f)) + { + addJoystickDevice(f); + } + } + } + + void CJoystickLinux::ps_readInput(QObject *object) + { + QFile *joystick = qobject_cast(object); + Q_ASSERT(joystick); + + struct js_event event; + while (joystick->read(reinterpret_cast(&event), sizeof(event)) == sizeof(event)) + { + switch (event.type & ~JS_EVENT_INIT) + { + case JS_EVENT_BUTTON: + if (event.value) + emit buttonDown(CJoystickButton(event.number)); + else + emit buttonUp(CJoystickButton(event.number)); + + break; + } + } + } } // namespace BlackInput diff --git a/src/blackinput/linux/joystick_linux.h b/src/blackinput/linux/joystick_linux.h index 7bd14362f..31bea5a7a 100644 --- a/src/blackinput/linux/joystick_linux.h +++ b/src/blackinput/linux/joystick_linux.h @@ -15,11 +15,17 @@ #include "blackinput/joystick.h" #include "blackmisc/hardware/joystickbutton.h" #include "blackmisc/collection.h" -#include +#include +#include + +class QFile; +class QFileSystemWatcher; +class QSignalMapper; namespace BlackInput { - //! Linux implemenation of IJoystick with DirectInput + //! Linux implemenation of IJoystick + //! \sa https://www.kernel.org/doc/Documentation/input/joystick-api.txt class CJoystickLinux : public IJoystick { Q_OBJECT @@ -33,7 +39,7 @@ namespace BlackInput CJoystickLinux &operator=(CJoystickLinux const &) = delete; //! \brief Destructor - virtual ~CJoystickLinux(); + virtual ~CJoystickLinux() = default; //! \copydoc IJoystick::startCapture() virtual void startCapture() override; @@ -45,10 +51,31 @@ namespace BlackInput friend class IJoystick; - //! Destructor + //! Removes all joysticks that are no longer present. + void cleanupJoysticks(); + + //! Adds new joystick input for reading + void addJoystickDevice(const QString &path); + + //! Constructor CJoystickLinux(QObject *parent = nullptr); + private slots: + + //! Slot for handling directory changes + //! \param path Watched directory path. + void ps_directoryChanged(QString path); + + //! Slot for reading the device handle + //! \param object QFile that has data to be read. + void ps_readInput(QObject *object); + + private: + IJoystick::Mode m_mode = ModeNominal; //!< Current working mode + QSignalMapper *m_mapper = nullptr; //!< Maps device handles + QMap m_joysticks; //!< All read joysticks, file path <-> file instance pairs + QFileSystemWatcher *m_inputWatcher = nullptr; }; } // namespace BlackInput diff --git a/src/blackinput/linux/keyboard_linux.cpp b/src/blackinput/linux/keyboard_linux.cpp index eaf5abd14..d5e1bcac5 100644 --- a/src/blackinput/linux/keyboard_linux.cpp +++ b/src/blackinput/linux/keyboard_linux.cpp @@ -131,7 +131,7 @@ namespace BlackInput quint8 events[EV_MAX/8 + 1]; memset(events, 0, sizeof(events)); - if ((ioctl(fd, EVIOCGVERSION, &version) >= 0) && (ioctl(fd, EVIOCGNAME(sizeof(name)), name)>=0) && (ioctl(fd, EVIOCGBIT(0,sizeof(events)), &events) >= 0) && test_bit(EV_KEY, events) && (((version >> 16) & 0xFF) > 0)) { + if ((ioctl(fd, EVIOCGVERSION, &version) >= 0) && (ioctl(fd, EVIOCGNAME(sizeof(name)), name)>=0) && (ioctl(fd, EVIOCGBIT(0,sizeof(events)), &events) >= 0) && test_bit(EV_KEY, events) && !test_bit(EV_SYN, events) && (((version >> 16) & 0xFF) > 0)) { name[255]=0; qDebug() << "CKeyboardLinux: " << qPrintable(inputFile->fileName()) << " " << name; // Is it grabbed by someone else?