目录
linux中键盘和鼠标事件都是在/dev/input中,名字为eventX或者mouse
步骤
- 创建一个继承线程类的KeyListener类
- 重写run函数
- 定义一个键盘的信号和一个鼠标的信号
- 创建对象关联信号使用
keylistener.cpp
#include "keylistener.h"
#include <QDebug>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
#include <QPoint>
KeyListener::KeyListener(const QString &devicePath, QObject *parent)
: QThread(parent), devicePath(devicePath) {}
KeyListener::~KeyListener() {
running = true;
// 等待线程安全退出
wait();
}
void KeyListener::run() {
// 初始化鼠标在左上角坐标
QPoint point(0,0);
// 打开键盘事件文件
int keyboardFd = open(devicePath.toStdString().c_str(), O_RDONLY);
if (keyboardFd < 0) {
qWarning() << "无法打开设备文件:" << devicePath;
return;
}
// 打开鼠标事件文件
mouseDevice = "/dev/input/event0";
int mouseFd = open(mouseDevice.toStdString().c_str(), O_RDONLY);
if (mouseFd < 0) {
qWarning() << "无法打开鼠标设备文件:" << mouseDevice;
close(keyboardFd);
return;
}
// input事件结构体
struct input_event ev;
// 记录 Alt 键状态
bool altPressed = false;
// 一直循环
while (running) {
// 定义文件描述符集合
fd_set readFds;
// 将文件描述符集合清零
FD_ZERO(&readFds);
// 将键盘描述符写入集合
FD_SET(keyboardFd, &readFds);
// 将鼠标描述符写入集合
FD_SET(mouseFd, &readFds);
// 获取较大值
int maxFd = std::max(keyboardFd, mouseFd);
// 使用 select() 监听多个文件描述符 需要检查的文件描述符范围是 [0, maxFd-1]
int ret = select(maxFd + 1, &readFds, nullptr, nullptr, nullptr);
if (ret < 0) {
qWarning() << "select() 失败";
break;
}
// 处理键盘事件 检查 keyboardFd(键盘文件描述符)是否就绪。如果就绪,说明键盘有输入数据可读取
if (FD_ISSET(keyboardFd, &readFds)) {
// 从键盘文件描述符中读取输入事件结构体数据
ssize_t bytes = read(keyboardFd, &ev, sizeof(struct input_event));
// 检查读取的数据是否完整,并确认事件类型为键盘按键事件(EV_KEY 表示按键事件)
if (bytes == sizeof(struct input_event) && ev.type == EV_KEY) {
// 检查是否是左Alt键(KEY_LEFTALT)或右Alt键(KEY_RIGHTALT)的按键事件
if (ev.code == KEY_LEFTALT || ev.code == KEY_RIGHTALT) {
// 更新 Alt 键状态
altPressed = ev.value == 1;
}
if (ev.code == KEY_B && ev.value == 1 && altPressed) {
// 触发 Alt+B 信号
emit altBPressed();
}
}
}
// 处理鼠标事件 检查 mouseFd 鼠标文件描述符是否就绪
if (FD_ISSET(mouseFd, &readFds)) {
// 从鼠标文件描述符中读取输入事件结构体数据
ssize_t bytes = read(mouseFd, &ev, sizeof(struct input_event));
// 检查读取的数据是否完整,并根据事件类型处理鼠标事件
if (bytes == sizeof(struct input_event) && ev.type == EV_REL) {
qDebug() << "鼠标移动事件,X、Y轴:" << ev.code << ", 移动量:" << ev.value;
// 根据事件代码(ev.code)确定是 X 轴还是 Y 轴的移动
if (ev.code == REL_X) {
// 累加 X 轴的移动值
point.setX(point.x() + ev.value);
} else if (ev.code == REL_Y) {
// 累加 Y 轴的移动值
point.setY(point.y() + ev.value);
}
// 发送鼠标移动事件传递鼠标坐标
emit mouseMove(point);
}
// 鼠标按键事件
else if (bytes == sizeof(struct input_event) && ev.type == EV_KEY)
{
qDebug() << "鼠标按键事件,按键:" << ev.code << ", 状态:" << ev.value;
}
}
}
close(keyboardFd);
close(mouseFd);
}
keylistener.h
#ifndef KEYLISTENER_H
#define KEYLISTENER_H
#include <QObject>
#include <QThread>
#include <QString>
#include <linux/input.h>
#include <fcntl.h>
#include <unistd.h>
#include <QDebug>
#include <QPoint>
class KeyListener : public QThread {
Q_OBJECT
public:
explicit KeyListener(const QString &devicePath, QObject *parent = nullptr);
~KeyListener();
void run() override; // 重写 QThread 的 run 方法
signals:
void altBPressed(); // 触发 Alt+B 的信号
void mouseMove(const QPoint &globalPos); // 发送鼠标坐标信号
private:
QString devicePath;// 键盘事件驱动路径
QString mouseDevice;// 鼠标事件驱动路径
bool running = true;
};
#endif // KEYLISTENER_H
使用方法
// 需要启用鼠标跟踪
this.setMouseTracking(true);
// 创建线程监听键盘事件
const QString devicePath = "/dev/input/event1";
m_listener = new KeyListener(devicePath, this);
// 键盘监听触发截图
connect(m_listener, &KeyListener::altBPressed,this, 键盘处理时间,Qt::QueuedConnection);
connect(m_listener, &KeyListener::mouseMove,this, 鼠标处理事件,Qt::QueuedConnection);
// 开始监听
m_listener->start();