在 GNU/Linux 中使用键盘快捷键启动应用程序功能

Launch application function using keyboard shortcut in GNU/Linux

我在 GNU/Linux 中使用 Qt 创建了一个应用程序,我在后台使用 运行。我想在用户按下某些组合键时执行某些应用程序功能,例如 Ctrl+Alt+A...

我知道这是可能的,这个问题中提供了 Gnome Pie does it but I don't know how I can capture the keys. I tried using the examples,但是其中 none 有效......我也不想 运行 我的应用程序作为 root .. .

任何人都可以给我一些资源或给我一些提示吗?

编辑:

@iharob 建议我应该使用 libkeybinder。我找到了,试过了,但它使用 GTK 并且 GTK 不能很好地与 Qt 一起玩...我什至不是 GTK 初学者,从未与它,但我认为 GTK 事件循环与 Qt 事件循环冲突;当我从回调中发出 Qt 信号时,该信号在按下键后被调用(也是在调用 gtk_init 之后),应用程序崩溃。

如果我可以创建一个 class,只要按下键盘组合键(例如 Ctrl+Alt+A)就会发出信号,那就太好了。

简单看一下 libkeybinder's very small source 就知道它在 X 显示器的根 window.

上安装了一个 keygrab。

这应该是可行的,但并不容易,需要一些知识和低层次的理解X Window System protocol. It should be possible for both Qt and libxcb才能在一个进程中和平共处。我尝试实现这样的方法如下:

  1. 启动一个单独的线程。
  2. 该线程将打开一个单独的连接到 X 服务器,枚举显示器上的所有屏幕,获取每个屏幕的根 window,每个根 window 上的 install a key grab,然后输入从 xcb_connection_t 句柄读取 X 事件的循环。
  3. 收到按键事件后(我希望在此循环中处理的唯一按键事件将是与抓取的按键对应的按键事件),立即ungrab the keyboard以便X服务器可以继续其快乐方式,然后以某种形式或方式通知您的应用程序的主线程,该键已被按下。
  4. 您的应用程序必须有一些方法在需要退出时停止此线程。

可能的解决方案是模拟此行为 - 有一个小型独立应用程序向您的后台进程发送信号(有许多变体可以执行此操作,signal() 可能是最简单的)。然后在特定环境的 window 管理器中附加该应用程序以获得所需的键绑定。可能需要学习如何为各种 window 经理做到这一点,但结果可能更清晰、实施起来更快。

据我所知和 @SamVarshavchik 指出 libkeybinder 在后台使用 libx11 所以你可以只使用 libx11为了摆脱不太 Qt 友好的 GTK 事件循环。 AFAIK KDEKAction 对其全局短键使用相同的技术,因此我认为该技术将与 Qt 的事件循环一起使用。

话虽如此,您可以使用所提供的热键示例 here:

x11_hot_key.pro:

#-------------------------------------------------
#
# Project created by QtCreator 2015-05-04T01:47:22
#
#-------------------------------------------------

QT       += core

QT       -= gui

TARGET = x11_hot_key
CONFIG   += console
CONFIG   -= app_bundle

TEMPLATE = app


SOURCES += main.cpp

CONFIG  += link_pkgconfig
PKGCONFIG += x11

main.cpp:

#include <QCoreApplication>

#include <iostream>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Display*    dpy     = XOpenDisplay(0);
    Window      root    = DefaultRootWindow(dpy);
    XEvent      ev;

    unsigned int    modifiers       = ControlMask | ShiftMask;
    int             keycode         = XKeysymToKeycode(dpy,XK_Y);
    Window          grab_window     =  root;
    Bool            owner_events    = False;
    int             pointer_mode    = GrabModeAsync;
    int             keyboard_mode   = GrabModeAsync;

    XGrabKey(dpy, keycode, modifiers, grab_window, owner_events, pointer_mode,
             keyboard_mode);

    XSelectInput(dpy, root, KeyPressMask );
    while(true)
    {
        bool shouldQuit = false;
        XNextEvent(dpy, &ev);
        switch(ev.type)
        {
        case KeyPress:
            cout << "Hot key pressed!" << endl;
            XUngrabKey(dpy,keycode,modifiers,grab_window);
            shouldQuit = true;

        default:
            break;
        }

        if(shouldQuit)
            break;
    }

    XCloseDisplay(dpy);

    return a.exec();
}

或者你可以只使用 this simple library as presented here,它也有一些简单的例子和​​一个方便的 Makefile 供你相处。

由于我不了解 XGrabKey 的异步通信者,您将遇到的问题是 while(true) 循环从不 returns 并阻塞主线程,因此应用程序所以你想要的是将它移动到一个单独的线程中并使用 signalsslots 将它连接到主线程。不过,这应该不是什么大问题,也不会影响您的应用程序的性能,因为 AFAIK XNextEvent 会阻塞直到您按下键,这样处理器就不会无用地处理...

希望这对您有所帮助。