How to capture the global keys in Ubuntu in Qt application. I need to handle the keys like Ctrl, Shift even my qt application is minimized state also. Looks like LibQxt supports this. But as per my understanding this library won't have any support from Qt4. I am using Qt5.7. Do we have any other way to do this ?
1 Answers
This can be achieved using x11/xcb. The idea is to listen to a specific keyboard shortcut, system-wide, using the XGrabKey function from xlib, then catching the corresponding xcb event in the overridden nativeEventFilter
method of a QAbstractNativeEventFilter subclass.
As an example, let's activate an application minimized window, using the Ctrl-A shortcut from anywhere.
The project must reference the x11extra
qt module, and link the x11 library:
QT += x11extras
LIBS += -lX11
This is the filter header:
#include <QAbstractNativeEventFilter>
#include <QWidget>
class EventFilter : public QAbstractNativeEventFilter
{
public:
void setup(QWidget *target);
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result);
private:
int keycode;
QWidget * target;
};
and this is the implementation:
#include <xcb/xcb.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <QtX11Extras/QX11Info>
void EventFilter::setup(QWidget *target)
{
this->target = target;
Display * display = QX11Info::display();
unsigned int modifiers = ControlMask;
keycode = XKeysymToKeycode(display, XK_A);
XGrabKey(display, keycode, modifiers, DefaultRootWindow(display), 1, GrabModeAsync, GrabModeAsync);
}
bool EventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *)
{
if (eventType == "xcb_generic_event_t")
{
xcb_generic_event_t* xcbevent = static_cast<xcb_generic_event_t *>(message);
switch(xcbevent->response_type)
{
case XCB_KEY_PRESS:
xcb_key_press_event_t * keypress_event = static_cast<xcb_key_press_event_t *>(message);
if(keypress_event->state & XCB_MOD_MASK_CONTROL)
{
if(keypress_event->detail == keycode)
{
qDebug() << "ACTIVATING ...";
target->activateWindow();
}
}
}
}
return false;
}
In a main, we create a widget on the fly, instantiate, install and setup our filter.
#include "eventfilter.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
w.setGeometry(100, 100, 400, 300);
w.show();
EventFilter filter;
a.installNativeEventFilter(&filter);
filter.setup(&w);
return a.exec();
}
If the user minimize the widget, or the widget is deactivated, the Ctrl-A shortcut should prompt it back to the foreground.
Please notice that the XGrabKey call from the setup
method could fail if some other x client grabbed the same key combination, already.
Also notice that extra modifier masks will be delivered in the event state
field if some lock keys are on (e.g. on my system the pressed caps lock yelds an extra XCB_MOD_MASK_LOCK, and the num lock an extra XCB_MOD_MASK_2).

- 9,807
- 2
- 22
- 35
-
Thank you for the full example. But I can only capture Ctrl+A when the widget is in shown state. When I minimized the widget and tried to press Ctrl+A it won't detect. Also the below checking doesn't work for me. if(keypress_event->state == XCB_MOD_MASK_CONTROL) . For me the Ctrl+A return the keypress_event->state as value 20. Where the value of XCB_MOD_MASK_CONTROL is 4 in xproto.h (I am using Ubuntu in Virtual box. Dosn't think this won't make any issue). – GUI-Novice Mar 12 '18 at 10:28
-
So i tried to comment that check (if(keypress_event->state == XCB_MOD_MASK_CONTROL)) from above code. But my current issue is it won't detect the key when application is minimized state. So I am worrying about same issue will come even if the application stays in system tray. (my final aim is detect the global key when app is in system try) – GUI-Novice Mar 12 '18 at 10:34
-
About the wrong modifier: you're somehow receiving a `XCB_MOD_MASK_2` along with `XCB_MOD_MASK_CONTROL`. Just use the bitwise operator `&` instead of `=`. I edited the answer to address this issue, and added a `qDebug` in a strategic position. Can you please edit your code accordingly, and tell if the debugging line is ever reached? – p-a-o-l-o Mar 12 '18 at 11:17
-
Please try **registering** the modifier mask you're receiving, i.e. `unsigned int modifiers = ControlMask | Mod2Mask;` in `EventFilter::setup`. – p-a-o-l-o Mar 12 '18 at 11:27
-
Yes. I used bitwise operator and now the checking works. Still the "qDebug() << "ACTIVATING ..." is not printing when we minimize the application and press Ctrl+A. It only works when application is shown – GUI-Novice Mar 12 '18 at 11:33
-
Did you register the full modifier mask (ControlMask | Mod2Mask)? Please refer to my last comment, I cannot edit the answer with this because is very specific to your case. – p-a-o-l-o Mar 12 '18 at 11:35
-
Wow.. thats great. I set modifier mask in setup. Now I am able to capture the keys even in minimized state. Thanks @p-a-o-l-o for your great example. – GUI-Novice Mar 12 '18 at 11:38
-
@GUI-Novice please notice the answer update about lock keys (i.e. chek if your caps lock, num lock etc are on). Also, please, consider marking the answer as accepted just to point out it solved the reported issue. – p-a-o-l-o Mar 12 '18 at 12:55
-
@ p-a-o-l-o, I have added a 'QPushButton a; a.setText("test button"), a.hide();' before your QWidget. I am trying to display this QPushButton on Ctrl+A received. Finally I will change this button to my custom dialog. Here the issue is, the "qDebug() << "ACTIVATING ..." is not getting triggered when the QPushButton is hidden state. I don't know why. Any idea ? – GUI-Novice Mar 13 '18 at 12:10
-
Also, I have hidden your QWidget since I don't have anything to display on it. – GUI-Novice Mar 13 '18 at 12:38
-
Hi @GUI-Novice, can you please make a new question out of this issue? It's very hard to figure it out, otherwise. Moreover, you've got more chances it gets solved (I will be glad to try and probably many other people will). Don't forget to include all the relevant code. Thanks. – p-a-o-l-o Mar 13 '18 at 12:53
-
Is there any way to print the key name ? Question Link : https://stackoverflow.com/questions/49491918/getting-key-name-from-keycode-x11-xgrabkey – GUI-Novice Mar 27 '18 at 05:54