1 #include "xkeygrabber.h"
2 
3 #include <X11/X.h>
4 #include <X11/XKBlib.h>
5 #include <X11/Xlib.h>
6 #include <X11/keysym.h>
7 
8 #include "globals.h"
9 
10 using std::vector;
11 using std::string;
12 
XKeyGrabber()13 XKeyGrabber::XKeyGrabber() {
14     updateNumlockMask();
15 }
16 
17 //! Obtains the current numlock mask value
updateNumlockMask()18 void XKeyGrabber::updateNumlockMask() {
19     XModifierKeymap *modmap;
20 
21     numlockMask_ = 0;
22     modmap = XGetModifierMapping(g_display);
23     for (size_t i = 0; i < 8; i++) {
24         for (int j = 0; j < modmap->max_keypermod; j++) {
25             if (modmap->modifiermap[i * modmap->max_keypermod + j]
26                     == XKeysymToKeycode(g_display, XK_Num_Lock)) {
27                 numlockMask_ = (1 << i);
28             }
29         }
30     }
31     XFreeModifiermap(modmap);
32 }
33 
34 /*!
35  * Derives a "normalized" KeyCombo from a given event.
36  *
37  * Normalization means stripping any ignored modifiers from the modifier mask
38  * (including the runtime-defined Numlock mask).
39  */
xEventToKeyCombo(XKeyEvent * ev) const40 KeyCombo XKeyGrabber::xEventToKeyCombo(XKeyEvent* ev) const {
41     KeyCombo combo = {};
42     combo.keysym = XkbKeycodeToKeysym(g_display, ev->keycode, 0, 0);
43     combo.modifiers_ = ev->state;
44 
45     // Normalize
46     combo.modifiers_ &= ~(numlockMask_ | LockMask);
47 
48     return combo;
49 }
50 
51 //! Grabs the given key combo
grabKeyCombo(const KeyCombo & keyCombo)52 void XKeyGrabber::grabKeyCombo(const KeyCombo& keyCombo) {
53     changeGrabbedState(keyCombo, true);
54 }
55 
56 //! Ungrabs the given key combo
ungrabKeyCombo(const KeyCombo & keyCombo)57 void XKeyGrabber::ungrabKeyCombo(const KeyCombo& keyCombo) {
58     changeGrabbedState(keyCombo, false);
59 }
60 
61 //! Removes all grabbed keys (without knowing them)
ungrabAll()62 void XKeyGrabber::ungrabAll() {
63     XUngrabKey(g_display, AnyKey, AnyModifier, g_root);
64 }
65 
66 //! Grabs/ungrabs a given key combo
changeGrabbedState(const KeyCombo & keyCombo,bool grabbed)67 void XKeyGrabber::changeGrabbedState(const KeyCombo& keyCombo, bool grabbed) {
68     // List of ignored modifiers (key combo will be grabbed for each of them):
69     const unsigned int ignModifiers[] = { 0, LockMask, numlockMask_, numlockMask_ | LockMask };
70 
71     KeyCode keycode = XKeysymToKeycode(g_display, keyCombo.keysym);
72     if (!keycode) {
73         // Ignore unknown keysym
74         return;
75     }
76 
77     // Grab/ungrab key for each modifier that is ignored (capslock, numlock)
78     for (auto& ignModifier : ignModifiers) {
79         if (grabbed) {
80             XGrabKey(g_display, keycode, ignModifier | keyCombo.modifiers_, g_root,
81                     True, GrabModeAsync, GrabModeAsync);
82         } else {
83             XUngrabKey(g_display, keycode, ignModifier | keyCombo.modifiers_, g_root);
84         }
85     }
86 }
87 
getPossibleKeySyms()88 vector<string> XKeyGrabber::getPossibleKeySyms() {
89     vector<string> ret;
90     int min, max;
91     XDisplayKeycodes(g_display, &min, &max);
92     int kc_count = max - min + 1;
93     int ks_per_kc; // count of keysysms per keycode
94     KeySym* keysyms;
95     keysyms = XGetKeyboardMapping(g_display, min, kc_count, &ks_per_kc);
96     // only symbols at a position i*ks_per_kc are symbols that are recieved in
97     // a keyevent, it should be the symbol for the keycode if no modifier is
98     // pressed
99     for (int i = 0; i < kc_count; i++) {
100         if (keysyms[i * ks_per_kc] != NoSymbol) {
101             char* str = XKeysymToString(keysyms[i * ks_per_kc]);
102             ret.push_back(str);
103         }
104     }
105     XFree(keysyms);
106 
107     return ret;
108 }
109