Work with the global keyboard hot keys in Windows is a trivial task than Linux, since this has WinAPI prepared methods that do not require a large amount of code. And each hotkey is set in line ID, in which the hot key can be removed.
In Linux / Unix, which uses the graphics server is the X11, with respect to Qt must use the function to register / unregister hotkeys from XLib library, but the process has to the values obtained using XCB library functionality, which is being developed as an analogue XLib library, but is more low-level and written the C programming language. As in the case of Windows, for processing in the global hotkeys Qt 5 nativeEventFilter used method. I propose to make a separate class, inherited from QAbstractNativeEventFilter for processing hotkeys and set the filter to the entire application.
To set hotkeys to be used XKeysymToKeycode (to get the keys from KeySym sequence of code) and XGrabKey (to set the hotkey).
XUngrabKey function will be used for unregistration hotkey.
Project structure
- GlobalHotkeyLinux.pro - the profile of the project;
- mainwindow.h - header file of the main application window;
- mainwindow.cpp - file source code of the main application window;
- mainwindow.ui - file forms the main application window;
- main.cpp - the main source file;
- nativeeventfilter.h - header event filter file hotkeys;
- nativeeventfilter.cpp - file source hotkeys event filter.
GlobalHotkeyLinux.pro
I draw your attention to the fact that in order to obtain information about the events of the X11 window server using Qt QX11Info class, and to use it you need to connect x11extras unit, but that may not be enough, as in the standard package Qt5 these libraries simply will not , so they will need to install the following command:
sudo apt-get install qtx11extras5-dev-tools
In addition to work with libraries and xcb xlib you will need to specify the configuration of the project in view of X11:
CONFIG += link_pkgconfig PKGCONFIG += x11
Then we get the profile with the following contents.
#------------------------------------------------- # # Project created by QtCreator 2016-02-12T23:41:57 # #------------------------------------------------- QT += core gui QT += x11extras greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += c++11 CONFIG += link_pkgconfig PKGCONFIG += x11 TARGET = GlobalHotkeyLinux TEMPLATE = app SOURCES += main.cpp\ mainwindow.cpp \ nativeeventfilter.cpp HEADERS += mainwindow.h \ nativeeventfilter.h FORMS += mainwindow.ui
nativeeventfilter.h
In Qt, there are event filters classes that can be installed on the application window, or components directly on the entire application as we did this time. To do this, you need to create a class inherited from QAbstractNativeEventFilter, which is used to receive events from the operating system, which runs the application on Qt. Override the method natvieEventFilter, which will process and hotkey, and we make two methods: one to set the hotkey, and the second to turn it off.
#ifndef NATIVEEVENTFILTER_H #define NATIVEEVENTFILTER_H #include <QObject> #include <QAbstractNativeEventFilter> class NativeEventFilter : public QObject, public QAbstractNativeEventFilter { Q_OBJECT public: explicit NativeEventFilter(QObject *parent = 0); bool nativeEventFilter(const QByteArray &eventType, void *message, long *result); void setShortcut(); void unsetShortcut(); signals: void activated(); public slots: }; #endif // NATIVEEVENTFILTER_H
nativeeventfilter.cpp
The whole point of creating a filter that we easily can register a hotkey using XLib library, while dismantling events have with XCB library because the Qt, receiving data from the X11, has no idea about them in the context of Xlib, and understand this data how XCB.
#include "nativeeventfilter.h" #include <QVector> #include <QX11Info> #include <X11/Xlib.h> #include <X11/Xutil.h> #include <xcb/xcb.h> namespace { Display * m_display; // Connect to Server X11 Window m_win; // Grabing window - in this case, the entire system will be int keycode; unsigned int modifier; /* Vector additional modifiers Num Lock, Caps lock * They are also taken into account in X11, you therefore need all the possible combinations * */ QVector<quint32> maskModifiers(){ return QVector<quint32>() << 0 << Mod2Mask << LockMask << (Mod2Mask | LockMask); } } NativeEventFilter::NativeEventFilter(QObject *parent) : QObject(parent) { m_display = QX11Info::display(); // Create a connection to the server m_win = DefaultRootWindow(m_display); // and pull out of it captured by the window with a macro } bool NativeEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *result) { Q_UNUSED(eventType) Q_UNUSED(result) /* But event handling is built on the library instead of Xlib XCB. * Kind of like getting Qt event knows it as XCB events, * but does not know it as an event Xlib, although used more * Xlib easy syntax to set the hotkeys no one forbids us * */ xcb_key_press_event_t *keyEvent = 0; //So we check that it was xcb event if (eventType == "xcb_generic_event_t") { // cast message xcb event xcb_generic_event_t *event = static_cast<xcb_generic_event_t *>(message); // check that a keystroke occurred if ((event->response_type & 127) == XCB_KEY_PRESS){ // If so, then cast message keypress event keyEvent = static_cast<xcb_key_press_event_t *>(message); // Next, check whether it is necessary to hotkeys event foreach (quint32 maskMods, maskModifiers()) { if((keyEvent->state == (modifier | maskMods )) && keyEvent->detail == keycode){ emit activated(); // and send a signal return true; } } } } return false; } void NativeEventFilter::setShortcut() { unsetShortcut(); /* First to alleged reinsurance disable hotkey, * Despite the fact that the waste will be the first time in the parameters hotkey. * */ // get the key code of KeySym identify and connect to the X11 server keycode = XKeysymToKeycode(m_display, XK_E); modifier = ControlMask; // Зададим модификатор /* Now we go through all the possible combinations in view of Num Lock and Caps Lock, setting hotkeys * */ foreach (quint32 maskMods, maskModifiers()) { XGrabKey(m_display, // specify the connection to the X11 keycode , // keycode modifier | maskMods, // modifier with all the masks m_win, // grabing window True, // It is the owner of the application event. in this example it is not essential. GrabModeAsync, // Be sure to asynchronous processing mode, otherwise, risk of freeze system GrabModeAsync); } } void NativeEventFilter::unsetShortcut() { // Проходим по всем возможным комбинациям и удаляем хоткей foreach (quint32 maskMods, maskModifiers()) { XUngrabKey(m_display, keycode, modifier | maskMods, m_win); } }
mainwindow.h
The header file applications do not see anything special. Only a slot for receiving the signal and the filter itself, which will give the signal.
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include "nativeeventfilter.h" namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: void slotGlobalHotkey(); private: Ui::MainWindow *ui; NativeEventFilter *nativeEventFilter; }; #endif // MAINWINDOW_H
mainwindow.cpp
All that remains to do is to initialize the filter, set it to the entire application, connect the signal to the slot of the event handler and set the hotkey. Thus we get a working version of a global hotkey for Qt apps for linux.
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QApplication> #include <QMessageBox> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); nativeEventFilter = new NativeEventFilter(this); // Initialize filter qApp->installNativeEventFilter(nativeEventFilter); // Install it on an application // connect the filter signal to a slot connect(nativeEventFilter, &NativeEventFilter::activated, this, &MainWindow::slotGlobalHotkey); nativeEventFilter->setShortcut(); // Устанавилваем хоткей } MainWindow::~MainWindow() { delete ui; } void MainWindow::slotGlobalHotkey() { // And informs the user if he want us to hit HotKey QMessageBox::information(this,"Global Hotkey", "Global Hotkey is worked", QMessageBox::Ok); }
Archive of the project: GlobalHotkeyLinux
Result
The result is an application that will respond to the global hotkey Ctrl + E and display a message that the hot key is working, even if you will roll application or will be working with another application. Just do not try to capture the pressing PrintScreen key. It will not work most likely. The reason is that when using XGrabKey try to capture some hot key, it will be captured only in the case if any other client application that has not captured the gripper hotkey on the target window. And when you consider that in the same distro Ubuntu already have an application to create screenshots, which just reacts to the PrintScreen key, you with 100% probability will not be able to intercept this key until you will kill this program, or any other way not force it to release the hotkey.