mirror of
https://github.com/nymea/nymea-plugins.git
synced 2026-10-23 14:43:44 +02:00
New plugin: Add linux input device support
This commit is contained in:
parent
a35312b7f2
commit
10769b50f9
155
linuxinput/inputdevice.cpp
Normal file
155
linuxinput/inputdevice.cpp
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
#include "inputdevice.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
|
#include "extern-plugininfo.h"
|
||||||
|
#include "inputdeviceeventmonitor.h"
|
||||||
|
|
||||||
|
#include <libevdev/libevdev.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
InputDevice::InputDevice(QObject *parent)
|
||||||
|
: QObject{parent}
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
InputDevice::InputDevice(Info inputDeviceInfo, QObject *parent)
|
||||||
|
: QObject{parent},
|
||||||
|
m_inputDeviceInfo{inputDeviceInfo},
|
||||||
|
m_pollThread{new QThread(nullptr)}
|
||||||
|
{
|
||||||
|
qCDebug(dcLinuxInput()) << "Constructing" << inputDeviceInfo;
|
||||||
|
InputDeviceEventMonitor *monitor = new InputDeviceEventMonitor(inputDeviceInfo.eventFilePath, nullptr);
|
||||||
|
monitor->moveToThread(m_pollThread);
|
||||||
|
|
||||||
|
connect(m_pollThread, &QThread::started, monitor, &InputDeviceEventMonitor::process);
|
||||||
|
connect(monitor, &InputDeviceEventMonitor::finished, m_pollThread, &QThread::quit);
|
||||||
|
connect(monitor, &InputDeviceEventMonitor::finished, monitor, &InputDeviceEventMonitor::deleteLater);
|
||||||
|
connect(m_pollThread, &QThread::finished, monitor, &InputDeviceEventMonitor::deleteLater);
|
||||||
|
connect(m_pollThread, &QThread::finished, m_pollThread, [this](){
|
||||||
|
qCDebug(dcLinuxInput()) << "Monitoring thread finished" << m_inputDeviceInfo;
|
||||||
|
m_pollThread->deleteLater();
|
||||||
|
m_pollThread = nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(monitor, &InputDeviceEventMonitor::eventOccurred, this, &InputDevice::onEventOccurred);
|
||||||
|
|
||||||
|
qCDebug(dcLinuxInput()) << "Start monitoring" << inputDeviceInfo;
|
||||||
|
m_pollThread->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
InputDevice::~InputDevice()
|
||||||
|
{
|
||||||
|
if (m_pollThread) {
|
||||||
|
m_pollThread->terminate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InputDevice::Info InputDevice::inputDeviceInfo() const
|
||||||
|
{
|
||||||
|
return m_inputDeviceInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<InputDevice::Info> InputDevice::availableInputDevices()
|
||||||
|
{
|
||||||
|
QList<Info> inputeDevices;
|
||||||
|
QStringList addedInputIds;
|
||||||
|
QDir inputByIdDir("/dev/input/by-path/");
|
||||||
|
|
||||||
|
// Get the keyboards
|
||||||
|
qCDebug(dcLinuxInput()) << "---------- Keyboards:";
|
||||||
|
foreach (const QString &inputIdName, inputByIdDir.entryList({"*event-kbd"}, QDir::Dirs | QDir::NoDotAndDotDot)) {
|
||||||
|
Info info = parseDeviceInfo(Keyboard, QFileInfo(inputByIdDir.absolutePath() + QDir::separator() + inputIdName));
|
||||||
|
qCDebug(dcLinuxInput()) << " --> " << info;
|
||||||
|
inputeDevices.append(info);
|
||||||
|
addedInputIds.append(inputIdName);
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(dcLinuxInput()) << "---------- Mouse:";
|
||||||
|
foreach (const QString &inputIdName, inputByIdDir.entryList({"*event-mouse"}, QDir::Dirs | QDir::NoDotAndDotDot)) {
|
||||||
|
Info info = parseDeviceInfo(Mouse, QFileInfo(inputByIdDir.absolutePath() + QDir::separator() + inputIdName));
|
||||||
|
qCDebug(dcLinuxInput()) << " --> " << info;
|
||||||
|
inputeDevices.append(info);
|
||||||
|
addedInputIds.append(inputIdName);
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(dcLinuxInput()) << "---------- Other:";
|
||||||
|
foreach (const QString &inputIdName, inputByIdDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
|
||||||
|
if (addedInputIds.contains(inputIdName))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Info info = parseDeviceInfo(Other, QFileInfo(inputByIdDir.absolutePath() + QDir::separator() + inputIdName));
|
||||||
|
qCDebug(dcLinuxInput()) << " --> " << info;
|
||||||
|
inputeDevices.append(info);
|
||||||
|
addedInputIds.append(inputIdName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return inputeDevices;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputDevice::onEventOccurred(quint16 type, quint16 code, qint32 value)
|
||||||
|
{
|
||||||
|
if (type != EV_KEY)
|
||||||
|
return;
|
||||||
|
|
||||||
|
qCDebug(dcLinuxInput()) << "Event occurred" << type << code << value;
|
||||||
|
emit keyPressed(code, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString InputDevice::readFileContent(const QString &fileName)
|
||||||
|
{
|
||||||
|
QFile file(fileName);
|
||||||
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
qCWarning(dcLinuxInput()) << "Could not open file" << fileName << ":" << file.errorString();
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString content;
|
||||||
|
QTextStream in(&file);
|
||||||
|
content = in.readAll();
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return content.trimmed();
|
||||||
|
}
|
||||||
|
|
||||||
|
InputDevice::Info InputDevice::parseDeviceInfo(Type type, const QFileInfo &inputLinkFileInfo)
|
||||||
|
{
|
||||||
|
QFileInfo inputFileInfo(inputLinkFileInfo.symLinkTarget());
|
||||||
|
|
||||||
|
QDir inputsDir("/sys/class/input/");
|
||||||
|
QDir inputDir(inputsDir.absolutePath() + QDir::separator() + inputFileInfo.baseName() + QDir::separator() + "device");
|
||||||
|
|
||||||
|
Info info;
|
||||||
|
info.type = type;
|
||||||
|
info.id = QString(inputFileInfo.baseName()).remove("event").toInt();
|
||||||
|
info.name = readFileContent(inputDir.absolutePath() + QDir::separator() + "name");
|
||||||
|
info.phys = readFileContent(inputDir.absolutePath() + QDir::separator() + "phys");
|
||||||
|
info.eventFilePath = inputLinkFileInfo.symLinkTarget();
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug debug, InputDevice::Info inputDeviceInfo)
|
||||||
|
{
|
||||||
|
QDebugStateSaver saver(debug);
|
||||||
|
debug.nospace() << "Info(" << inputDeviceInfo.type
|
||||||
|
<< ", name: " << inputDeviceInfo.name
|
||||||
|
<< ", Phys: " << inputDeviceInfo.phys
|
||||||
|
<< ", " << inputDeviceInfo.eventFilePath
|
||||||
|
<< ')';
|
||||||
|
return debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug debug, InputDevice *inputDevice)
|
||||||
|
{
|
||||||
|
QDebugStateSaver saver(debug);
|
||||||
|
debug.nospace() << "InputDevice(" << inputDevice->inputDeviceInfo() << ')';
|
||||||
|
return debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
60
linuxinput/inputdevice.h
Normal file
60
linuxinput/inputdevice.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#ifndef INPUTDEVICE_H
|
||||||
|
#define INPUTDEVICE_H
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
#include <linux/input-event-codes.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
|
||||||
|
class InputDevice : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
enum Type {
|
||||||
|
Unknown,
|
||||||
|
Keyboard,
|
||||||
|
Mouse,
|
||||||
|
Other
|
||||||
|
};
|
||||||
|
Q_ENUM(Type)
|
||||||
|
|
||||||
|
typedef struct Info {
|
||||||
|
int id = -1;
|
||||||
|
Type type = Unknown;
|
||||||
|
QString name;
|
||||||
|
QString description;
|
||||||
|
QString phys;
|
||||||
|
QString eventFilePath;
|
||||||
|
} InputDeviceInfo;
|
||||||
|
|
||||||
|
explicit InputDevice(QObject *parent = nullptr);
|
||||||
|
explicit InputDevice(Info inputDeviceInfo, QObject *parent = nullptr);
|
||||||
|
~InputDevice();
|
||||||
|
|
||||||
|
InputDevice::Info inputDeviceInfo() const;
|
||||||
|
|
||||||
|
static QList<InputDevice::Info> availableInputDevices();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void keyPressed(quint16 code, qint32 value);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onEventOccurred(quint16 type, quint16 code, qint32 value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_eventFileName;
|
||||||
|
static QString readFileContent(const QString &fileName);
|
||||||
|
Info m_inputDeviceInfo;
|
||||||
|
QThread *m_pollThread = nullptr;
|
||||||
|
|
||||||
|
static InputDevice::Info parseDeviceInfo(Type type, const QFileInfo &inputLinkFileInfo);
|
||||||
|
};
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug debug, InputDevice::Info inputDeviceInfo);
|
||||||
|
QDebug operator<<(QDebug debug, InputDevice *inputDevice);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INPUTDEVICE_H
|
||||||
62
linuxinput/inputdeviceeventmonitor.cpp
Normal file
62
linuxinput/inputdeviceeventmonitor.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#include "inputdeviceeventmonitor.h"
|
||||||
|
#include "extern-plugininfo.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <libevdev/libevdev.h>
|
||||||
|
|
||||||
|
InputDeviceEventMonitor::InputDeviceEventMonitor(const QString &fileName, QObject *parent)
|
||||||
|
: QObject{parent},
|
||||||
|
m_fileName{fileName}
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputDeviceEventMonitor::process()
|
||||||
|
{
|
||||||
|
struct libevdev *device = nullptr;
|
||||||
|
|
||||||
|
int fd = open(m_fileName.toStdString().c_str(), O_RDWR|O_CLOEXEC);
|
||||||
|
if (fd < 0) {
|
||||||
|
qCWarning(dcLinuxInput()) << "Could not open file" << m_fileName;
|
||||||
|
emit finished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int status = libevdev_new_from_fd(fd, &device);
|
||||||
|
if (status != 0) {
|
||||||
|
qCWarning(dcLinuxInput()) << "Failed to create evdev from file descriptor";
|
||||||
|
close(fd);
|
||||||
|
emit finished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(dcLinuxInput()) << "Start monitoring events on" << m_fileName;
|
||||||
|
qCDebug(dcLinuxInput()) << "- name: " << libevdev_get_name(device);
|
||||||
|
qCDebug(dcLinuxInput()) << "- phys: " << libevdev_get_phys(device);
|
||||||
|
|
||||||
|
struct input_event event = {};
|
||||||
|
auto hasError = [](int v) {
|
||||||
|
return v < 0 && v != -EAGAIN;
|
||||||
|
};
|
||||||
|
auto hasNextEvent = [](int v) {
|
||||||
|
return v >= 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
while (status = libevdev_next_event(device, LIBEVDEV_READ_FLAG_NORMAL | LIBEVDEV_READ_FLAG_BLOCKING, &event), !hasError(status)) {
|
||||||
|
if (!hasNextEvent(status))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
emit eventOccurred(event.type, event.code, event.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
libevdev_free(device);
|
||||||
|
device = nullptr;
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
emit finished();
|
||||||
|
}
|
||||||
24
linuxinput/inputdeviceeventmonitor.h
Normal file
24
linuxinput/inputdeviceeventmonitor.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef INPUTDEVICEEVENTMONITOR_H
|
||||||
|
#define INPUTDEVICEEVENTMONITOR_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class InputDeviceEventMonitor : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit InputDeviceEventMonitor(const QString &fileName, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void process();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void eventOccurred(quint16 type, quint16 code, qint32 value);
|
||||||
|
void finished();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_fileName;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INPUTDEVICEEVENTMONITOR_H
|
||||||
@ -37,6 +37,7 @@ PLUGIN_DIRS = \
|
|||||||
kodi \
|
kodi \
|
||||||
lgsmarttv \
|
lgsmarttv \
|
||||||
lifx \
|
lifx \
|
||||||
|
linuxinput \
|
||||||
logilink \
|
logilink \
|
||||||
mecelectronics \
|
mecelectronics \
|
||||||
meross \
|
meross \
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user