mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-05-03 16:25:54 +08:00
refs #265 Linux raw input handling
This commit is contained in:
@@ -4,7 +4,14 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "keyboard_linux.h"
|
#include "keyboard_linux.h"
|
||||||
|
#include "keymapping_linux.h"
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QSocketNotifier>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
using namespace BlackMisc::Hardware;
|
using namespace BlackMisc::Hardware;
|
||||||
|
|
||||||
@@ -12,6 +19,7 @@ namespace BlackInput
|
|||||||
{
|
{
|
||||||
CKeyboardLinux::CKeyboardLinux(QObject *parent) :
|
CKeyboardLinux::CKeyboardLinux(QObject *parent) :
|
||||||
IKeyboard(parent),
|
IKeyboard(parent),
|
||||||
|
m_ignoreNextKey(false),
|
||||||
m_mode(Mode_Nominal)
|
m_mode(Mode_Nominal)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -22,6 +30,11 @@ namespace BlackInput
|
|||||||
|
|
||||||
bool CKeyboardLinux::init()
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +76,10 @@ namespace BlackInput
|
|||||||
if (receiver == nullptr)
|
if (receiver == nullptr)
|
||||||
return handle;
|
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_key = key;
|
||||||
handle.m_receiver = receiver;
|
handle.m_receiver = receiver;
|
||||||
handle.function = function;
|
handle.function = function;
|
||||||
@@ -87,6 +104,60 @@ namespace BlackInput
|
|||||||
m_registeredFunctions.clear();
|
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)
|
void CKeyboardLinux::sendCaptureNotification(const CKeyboardKey &key, bool isFinished)
|
||||||
{
|
{
|
||||||
if (isFinished)
|
if (isFinished)
|
||||||
@@ -107,4 +178,101 @@ namespace BlackInput
|
|||||||
functionHandle.function(isPressed);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,9 @@
|
|||||||
#include "blackmisc/hwkeyboardkey.h"
|
#include "blackmisc/hwkeyboardkey.h"
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
|
||||||
|
class QFileSystemWatcher;
|
||||||
|
class QFile;
|
||||||
|
|
||||||
namespace BlackInput
|
namespace BlackInput
|
||||||
{
|
{
|
||||||
//! \brief Linux implemenation of IKeyboard using hook procedure
|
//! \brief Linux implemenation of IKeyboard using hook procedure
|
||||||
@@ -60,6 +63,14 @@ namespace BlackInput
|
|||||||
//! \copydoc IKeyboard::unregisterHotkeyImpl()
|
//! \copydoc IKeyboard::unregisterHotkeyImpl()
|
||||||
virtual void unregisterAllHotkeysImpl() override;
|
virtual void unregisterAllHotkeysImpl() override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
//! Changed directory to linux devices
|
||||||
|
void deviceDirectoryChanged(const QString &);
|
||||||
|
|
||||||
|
//! Device is ready to read new input
|
||||||
|
void inputReadyRead(int);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -76,11 +87,27 @@ namespace BlackInput
|
|||||||
*/
|
*/
|
||||||
void callFunctionsBy(const BlackMisc::Hardware::CKeyboardKey &keySet, bool isPressed);
|
void callFunctionsBy(const BlackMisc::Hardware::CKeyboardKey &keySet, bool isPressed);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Add new raw input device
|
||||||
|
* \param filePath Path to device file
|
||||||
|
*/
|
||||||
|
void addRawInputDevice(const QString &filePath);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Process new key event
|
||||||
|
* \param virtualKeyCode
|
||||||
|
* \param isPressed
|
||||||
|
*/
|
||||||
|
void keyEvent(int virtualKeyCode, bool isPressed);
|
||||||
|
|
||||||
|
|
||||||
QHash<BlackMisc::Hardware::CKeyboardKey, QList<IKeyboard::RegistrationHandle>> m_registeredFunctions; //!< Registered hotkey functions
|
QHash<BlackMisc::Hardware::CKeyboardKey, QList<IKeyboard::RegistrationHandle>> m_registeredFunctions; //!< Registered hotkey functions
|
||||||
BlackMisc::Hardware::CKeyboardKey m_pressedKey; //!< Set of virtual keys pressed in the last cycle
|
BlackMisc::Hardware::CKeyboardKey m_pressedKey; //!< Set of virtual keys pressed in the last cycle
|
||||||
bool m_ignoreNextKey; //!< Is true if the next key needs to be ignored
|
bool m_ignoreNextKey; //!< Is true if the next key needs to be ignored
|
||||||
Mode m_mode; //!< Operation mode
|
Mode m_mode; //!< Operation mode
|
||||||
|
|
||||||
|
QFileSystemWatcher *m_devInputWatcher; //!< Watches the device file system for input devices
|
||||||
|
QHash<QString, QFile *> m_hashInputDevices; //!< Hash map containing all known input devices
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user