refs #265 Linux raw input handling

This commit is contained in:
Roland Winklmeier
2014-06-10 11:11:50 +02:00
parent 498dc5e1df
commit 66bcea8e20
2 changed files with 195 additions and 0 deletions

View File

@@ -4,7 +4,14 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "keyboard_linux.h"
#include "keymapping_linux.h"
#include <QDebug>
#include <QFileSystemWatcher>
#include <QDir>
#include <QFile>
#include <QSocketNotifier>
#include <linux/input.h>
#include <fcntl.h>
using namespace BlackMisc::Hardware;
@@ -12,6 +19,7 @@ namespace BlackInput
{
CKeyboardLinux::CKeyboardLinux(QObject *parent) :
IKeyboard(parent),
m_ignoreNextKey(false),
m_mode(Mode_Nominal)
{
}
@@ -22,6 +30,11 @@ namespace BlackInput
bool CKeyboardLinux::init()
{
QString dir = QLatin1String("/dev/input");
m_devInputWatcher = new QFileSystemWatcher(QStringList(dir), this);
connect(m_devInputWatcher, &QFileSystemWatcher::directoryChanged, this, &CKeyboardLinux::deviceDirectoryChanged);
deviceDirectoryChanged(dir);
return true;
}
@@ -63,6 +76,10 @@ namespace BlackInput
if (receiver == nullptr)
return handle;
// FIXME. Remove virtual key code, because the one we receive from Qt is different to the linux native code.
// If we don't remove it, the hash lookup fails later.
key.setNativeVirtualKey(0);
handle.m_key = key;
handle.m_receiver = receiver;
handle.function = function;
@@ -87,6 +104,60 @@ namespace BlackInput
m_registeredFunctions.clear();
}
void CKeyboardLinux::deviceDirectoryChanged(const QString &dir)
{
QDir eventFiles(dir, QLatin1String("event*"), 0, QDir::System);
foreach (QFileInfo fileInfo, eventFiles.entryInfoList())
{
QString path = fileInfo.absoluteFilePath();
if (!m_hashInputDevices.contains(path))
addRawInputDevice(path);
}
}
void CKeyboardLinux::inputReadyRead(int)
{
struct input_event eventInput;
QFile *fileInput=qobject_cast<QFile *>(sender()->parent());
if (!fileInput)
return;
bool found = false;
while (fileInput->read(reinterpret_cast<char *>(&eventInput), sizeof(eventInput)) == sizeof(eventInput))
{
found = true;
if (eventInput.type != EV_KEY)
continue;
bool isPressed = false;
switch (eventInput.value)
{
case 0:
isPressed = false;
break;
case 1:
isPressed = true;
break;
default:
continue;
}
int keyCode = eventInput.code;
keyEvent(keyCode, isPressed);
}
if (!found) {
int fd = fileInput->handle();
int version = 0;
if ((ioctl(fd, EVIOCGVERSION, &version) < 0) || (((version >> 16) & 0xFF) < 1)) {
qWarning("CKeyboardLinux: Removing dead input device %s", qPrintable(fileInput->fileName()));
m_hashInputDevices.remove(fileInput->fileName());
fileInput->deleteLater();
}
}
}
void CKeyboardLinux::sendCaptureNotification(const CKeyboardKey &key, bool isFinished)
{
if (isFinished)
@@ -107,4 +178,101 @@ namespace BlackInput
functionHandle.function(isPressed);
}
}
#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8)))
void CKeyboardLinux::addRawInputDevice(const QString &filePath)
{
QFile *inputFile = new QFile(filePath, this);
if (inputFile->open(QIODevice::ReadOnly))
{
int fd = inputFile->handle();
int version;
char name[256];
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)) {
name[255]=0;
qDebug() << "CKeyboardLinux: " << qPrintable(inputFile->fileName()) << " " << name;
// Is it grabbed by someone else?
if ((ioctl(fd, EVIOCGRAB, 1) < 0)) {
qWarning("CKeyboardLinux: Device exclusively grabbed by someone else (X11 using exclusive-mode evdev?)");
inputFile->deleteLater();
} else {
ioctl(fd, EVIOCGRAB, 0);
fcntl(inputFile->handle(), F_SETFL, O_NONBLOCK);
connect(new QSocketNotifier(inputFile->handle(), QSocketNotifier::Read, inputFile), SIGNAL(activated(int)), this, SLOT(inputReadyRead(int)));
m_hashInputDevices.insert(inputFile->fileName(), inputFile);
}
}
else
inputFile->deleteLater();
}
else
inputFile->deleteLater();
}
void CKeyboardLinux::keyEvent(int virtualKeyCode, bool isPressed)
{
if (CKeyMappingLinux::isMouseButton(virtualKeyCode))
return;
BlackMisc::Hardware::CKeyboardKey lastPressedKey = m_pressedKey;
if (m_ignoreNextKey)
{
m_ignoreNextKey = false;
return;
}
bool isFinished = false;
if (isPressed)
{
if (CKeyMappingLinux::isModifier(virtualKeyCode))
m_pressedKey.addModifier(CKeyMappingLinux::convertToModifier(virtualKeyCode));
else
{
m_pressedKey.setKey(CKeyMappingLinux::convertToKey(virtualKeyCode));
m_pressedKey.setNativeVirtualKey(0);
}
}
else
{
if (CKeyMappingLinux::isModifier(virtualKeyCode))
m_pressedKey.removeModifier(CKeyMappingLinux::convertToModifier(virtualKeyCode));
else
{
m_pressedKey.setKey(Qt::Key_unknown);
m_pressedKey.setNativeVirtualKey(0);
}
isFinished = true;
}
if (lastPressedKey == m_pressedKey)
return;
#ifdef DEBUG_KEYBOARD
qDebug() << "Virtual key: " << virtualKeyCode;
#endif
if (m_mode == Mode_Capture)
{
if (isFinished)
{
sendCaptureNotification(lastPressedKey, true);
m_mode = Mode_Nominal;
}
else
{
sendCaptureNotification(m_pressedKey, false);
}
}
else
{
callFunctionsBy(lastPressedKey, false);
callFunctionsBy(m_pressedKey, true);
}
}
}