LInux下的全局热键
关于全局热键的实现, 参照了
Linux全局事件监听技术: https://www.jianshu.com/p/80cf81413d31
使用XRecord实现Linux全局事件监听:扩展库安装篇: https://www.jianshu.com/p/8d14705dda90
使用此库, 实现了我需要的双击功能
效果:双击ctrl, 调试输出 “双击了ctrl ”
关于库的详情, 可以在上边的 Linux全局事件监听技术 链接当中查看。 这里对库做了一点小修改(加了点注释,调整了点代码风格, 加了个stop函数[事实上,我并没有验证这个stop函数有没有效果])
event_monitor.h
/* -*- Mode: C++; indent-tabs-mode: nil; tab-width: 4 -*-
* -*- coding: utf-8 -*-
*
* Copyright (C) 2011 ~ 2017 Deepin, Inc.
* 2011 ~ 2017 Wang Yong
*
* Author: Wang Yong
* Maintainer: Wang Yong
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef EVENTMONITOR_H
#define EVENTMONITOR_H
#include
#include
#include
// Virtual button codes that are not defined by X11.
#define Button1 1
#define Button2 2
#define Button3 3
#define WheelUp 4
#define WheelDown 5
#define WheelLeft 6
#define WheelRight 7
#define XButton1 8
#define XButton2 9
class EventMonitor : public QThread
{
Q_OBJECT
public:
EventMonitor( QObject *parent = nullptr );
signals:
void buttonPress(int x, int y);
void buttonDrag(int x, int y);
void buttonRelease(int x, int y);
void keyPress(int code);
void keyRelease(int code);
protected:
bool filterWheelEvent(int detail);
static void callback(XPointer trash, XRecordInterceptData* data);
void handleRecordEvent(XRecordInterceptData *);
void run();
public slots:
void stop();
private:
bool isPress;
Display* m_display;
XRecordContext m_context;
};
#endif
event_monitor.cpp
/* -*- Mode: C++; indent-tabs-mode: nil; tab-width: 4 -*-
* -*- coding: utf-8 -*-
*
* Copyright (C) 2011 ~ 2017 Deepin, Inc.
* 2011 ~ 2017 Wang Yong
*
* Author: Wang Yong
* Maintainer: Wang Yong
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "event_monitor.h"
#include
EventMonitor::EventMonitor(QObject *parent) : QThread(parent)
{
m_display = nullptr;
isPress = false;
}
void EventMonitor::run()
{
Display* display = XOpenDisplay( nullptr );
if ( !display )
{
fprintf(stderr, "unable to open display\n");
return;
}
// Receive from ALL clients, including future clients.
XRecordClientSpec clients = XRecordAllClients;
XRecordRange* range = XRecordAllocRange();
if ( !range )
{
fprintf(stderr, "unable to allocate XRecordRange\n");
return;
}
// Receive KeyPress, KeyRelease, ButtonPress, ButtonRelease and MotionNotify events.
memset(range, 0, sizeof(XRecordRange));
range->device_events.first = KeyPress;
range->device_events.last = MotionNotify;
// And create the XRECORD context.
m_context = XRecordCreateContext(display, 0, &clients, 1, &range, 1);
if (m_context == 0)
{
fprintf(stderr, "XRecordCreateContext failed\n");
return;
}
XFree(range);
XSync(display, True);
// Display* display_datalink = XOpenDisplay(0);
m_display = XOpenDisplay(nullptr);
if ( !m_display )
{
fprintf(stderr, "unable to open second display\n");
return;
}
if (!XRecordEnableContext(m_display, m_context, callback, reinterpret_cast(this) ) )
{
fprintf(stderr, "XRecordEnableContext() failed\n");
return;
}
}
void EventMonitor::stop()
{
XRecordDisableContext( m_display, m_context );
XFlush( m_display );
}
void EventMonitor::callback(XPointer ptr, XRecordInterceptData* data)
{
// ((EventMonitor *) ptr)->handleRecordEvent(data);
reinterpret_cast(ptr)->handleRecordEvent( data );
}
void EventMonitor::handleRecordEvent(XRecordInterceptData* data)
{
if (data->category == XRecordFromServer)
{
xEvent * event = reinterpret_cast(data->data);
switch (event->u.u.type)
{
case ButtonPress: //鼠标按下
if (filterWheelEvent(event->u.u.detail))
{
isPress = true;
emit buttonPress( event->u.keyButtonPointer.rootX, event->u.keyButtonPointer.rootY);
}
break;
case MotionNotify: //鼠标拖动
if (isPress)
{
emit buttonDrag( event->u.keyButtonPointer.rootX, event->u.keyButtonPointer.rootY);
}
break;
case ButtonRelease: //鼠标松开
if (filterWheelEvent(event->u.u.detail))
{
isPress = false;
emit buttonRelease( event->u.keyButtonPointer.rootX, event->u.keyButtonPointer.rootY);
}
break;
case KeyPress: //键盘按下
emit keyPress( static_cast(data->data)[1] ); break;
case KeyRelease: //键盘弹起
emit keyRelease( static_cast(data->data)[1] ); break;
default:
break;
}
}
fflush(stdout);
XRecordFreeData(data);
}
bool EventMonitor::filterWheelEvent(int detail)
{
return detail != WheelUp && detail != WheelDown && detail != WheelLeft && detail != WheelRight;
}
接下来就是widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include
class EventMonitor;
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void onKeyPress(); //键盘被按下槽函数
private:
Ui::Widget *ui;
EventMonitor* m_keyThread; //按键监测线程
QTimer *m_timer; //双击判定定时器
bool m_isActive; //双击是否有效
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include
#include "event_monitor.h"
#include
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
m_keyThread = new EventMonitor;
m_isActive = false;
m_timer = new QTimer;
connect( m_timer, &QTimer::timeout, this, [this](){ m_isActive=false; } );
connect( m_keyThread, &EventMonitor::keyPress, this, &Widget::onKeyPress );
m_keyThread->start();
}
Widget::~Widget()
{
m_keyThread->stop();
m_keyThread->deleteLater();
delete ui;
}
void Widget::onKeyPress()
{
if( m_isActive )
{
qDebug() << "双击了ctrl";
}
m_isActive = true;
if( m_timer->isActive() )
{
m_timer->stop();
}
m_timer->start( 200 );
}
需要注意, 在pro文件当中,需要做一些修改( 可能是只有我这里需要 )
.pro文件
QT += x11extras
QT += dbus
LIBS += -lX11 -lXext -lXtst
QMAKE_CXXFLAGS += -g
#PKGCONFIG += xcb xcb-util #这里是从github下载下来的工程里边的参数,但是我这里加上这一个会报错。因此屏蔽这一行, 加上了下方的 CONFIG += auto-detected
CONFIG += auto-detected