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?