// 因为 XRecord 的事件循环会堵塞当前线程,避免监听事件的时候应用程序卡主
// 我们建立一个继承于 QThread 的EventMonitor类,通过子线程进行事件监听操作
EventMonitor::EventMonitor(QObject *parent) : QThread(parent)
{
// 鼠标按下标志位,用于识别鼠标的拖拽操作
isPress = false;
}
void EventMonitor::run()
{
// 创建记录 XRecord 协议的 X 专用连接
Display* display = XOpenDisplay(0);
// 连接打开检查
if (display == 0) {
fprintf(stderr, "unable to open display\n");
return;
}
// 初始化 XRecordCreateContext 所需的 XRecordClientSpec 参数
// XRecordAllClients 的意思是 "记录所有 X Client" 的事件
XRecordClientSpec clients = XRecordAllClients;
// 创建 XRecordRange 变量,XRecordRange 用于控制记录事件的范围
XRecordRange* range = XRecordAllocRange();
// 记录事件范围检查
if (range == 0) {
fprintf(stderr, "unable to allocate XRecordRange\n");
return;
}
// 初始化记录事件范围,范围开头设置成 KeyPress, 范围结尾设置成 MotionNotify 后
// 事件的类型就包括 KeyPress、KeyRelase、ButtonPress、ButtonRelease、MotionNotify五种事件
memset(range, 0, sizeof(XRecordRange));
range->device_events.first = KeyPress;
range->device_events.last = MotionNotify;
// 根据上面的记录客户端类型和记录事件范围来创建 “记录上下文”
// 然后把 XRecordContext 传递给 XRecordEnableContext 函数来开启事件记录循环
XRecordContext context = XRecordCreateContext (display, 0, &clients, 1, &range, 1);
if (context == 0) {
fprintf(stderr, "XRecordCreateContext failed\n");
return;
}
// 释放 range 指针
XFree(range);
// XSync 的作用就是把上面的 X 代码立即发给 X Server
// 这样 X Server 接受到事件以后会立即发送给 XRecord 的 Client 连接
XSync(display, True);
// 建立一个专门读取 XRecord 协议数据的 X 链接
Display* display_datalink = XOpenDisplay(0);
// 连接打开检查
if (display_datalink == 0) {
fprintf(stderr, "unable to open second display\n");
return;
}
// 调用 XRecordEnableContext 函数建立 XRecord 上下文
// XRecordEnableContext 函数一旦调用就开始进入堵塞时的事件循环,直到线程或所属进程结束
// X Server 事件一旦发生就传递给事件处理回调函数
if (!XRecordEnableContext(display_datalink, context, callback, (XPointer) this)) {
fprintf(stderr, "XRecordEnableContext() failed\n");
return;
}
}
// handleRecordEvent 函数的wrapper,避免 XRecord 代码编译不过的问题
void EventMonitor::callback(XPointer ptr, XRecordInterceptData* data)
{
((EventMonitor *) ptr)->handleRecordEvent(data);
}
// 真实处理 X 事件监听的回调函数
void EventMonitor::handleRecordEvent(XRecordInterceptData* data)
{
if (data->category == XRecordFromServer) {
// 得到 xEvent 对象
xEvent * event = (xEvent *)data->data;
switch (event->u.u.type) {
case ButtonPress:
// 过滤掉滚轮事件后,发送 buttonPress 信号
if (filterWheelEvent(event->u.u.detail)) {
isPress = true;
emit buttonPress(
event->u.keyButtonPointer.rootX,
event->u.keyButtonPointer.rootY);
}
break;
case MotionNotify:
// 只有在按下鼠标的时候移动,才发送 buttonDrag 信号
if (isPress) {
emit buttonDrag(
event->u.keyButtonPointer.rootX,
event->u.keyButtonPointer.rootY);
}
break;
case ButtonRelease:
// 过滤掉滚轮事件后,发送 buttonRelase 信号
if (filterWheelEvent(event->u.u.detail)) {
isPress = false;
emit buttonRelease(
event->u.keyButtonPointer.rootX,
event->u.keyButtonPointer.rootY);
}
break;
case KeyPress:
// 发送 keyPress 信号,附带按键的 code
emit keyPress(((unsigned char*) data->data)[1]);
break;
case KeyRelease:
// 发送 keyRelease 信号,附带按键的 code
emit keyRelease(((unsigned char*) data->data)[1]);
break;
default:
break;
}
}
// 资源释放
fflush(stdout);
XRecordFreeData(data);
}
// 过滤滚轮事件
bool EventMonitor::filterWheelEvent(int detail)
{
return detail != WheelUp && detail != WheelDown && detail != WheelLeft && detail != WheelRight;
}