mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-04-04 16:56:53 +08:00
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
This commit is contained in:
committed by
Roland Winklmeier
parent
c94e5c0fbd
commit
2b07dfb789
@@ -7,29 +7,135 @@
|
||||
* contained in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "blackmisc/logmessage.h"
|
||||
#include "joystick_linux.h"
|
||||
#include <QFile>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QSocketNotifier>
|
||||
#include <QSignalMapper>
|
||||
#include <linux/joystick.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
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<void (QSignalMapper::*)(QObject *)>(&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<char *>(&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<void (QSignalMapper::*)()>(&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<QFile *>(object);
|
||||
Q_ASSERT(joystick);
|
||||
|
||||
struct js_event event;
|
||||
while (joystick->read(reinterpret_cast<char *>(&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
|
||||
|
||||
@@ -15,11 +15,17 @@
|
||||
#include "blackinput/joystick.h"
|
||||
#include "blackmisc/hardware/joystickbutton.h"
|
||||
#include "blackmisc/collection.h"
|
||||
#include <QSet>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
|
||||
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<QString, QFile *> m_joysticks; //!< All read joysticks, file path <-> file instance pairs
|
||||
QFileSystemWatcher *m_inputWatcher = nullptr;
|
||||
};
|
||||
|
||||
} // namespace BlackInput
|
||||
|
||||
@@ -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?
|
||||
|
||||
Reference in New Issue
Block a user