QT在Linux平台的全局热键(功能键双击效果)

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

 

 

 

你可能感兴趣的:(QT常用代码,QT,全局热键,Linux,双击ctrl)