-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge PR #748: New plugin: Add linux input device support
- Loading branch information
Showing
6 changed files
with
313 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,7 @@ PLUGIN_DIRS = \ | |
kodi \ | ||
lgsmarttv \ | ||
lifx \ | ||
linuxinput \ | ||
logilink \ | ||
mecelectronics \ | ||
meross \ | ||
|