Global Hotkey con X11 / Xlib

Il mio objective è quello di avere un programma che dorme in background, ma può essere triggersto dall’utente tramite alcuni “hotkey”. Dalla ricerca del manuale di Xlib e del manuale di Xlib O’reilly, deduco che il modo corretto per farlo è con XGrabKey. Tuttavia, la mia comprensione del processo non è corretta in quanto una semplice dimostrazione del concetto non funziona.

La mia comprensione è che se chiamo XGrabKey con la finestra di root come grab_window e owner_events false, allora ogni volta che si preme il mio tasto di scelta rapida l’evento verrà inviato solo alla finestra di root. Se poi seleziono eventi KeyPress dalla finestra di root e poi ascolto eventi X, dovrei ricevere un evento key press quando si preme il tasto di scelta rapida. Ho incollato un esempio minimale di seguito.

Quello che mi aspetto è che quando il programma viene eseguito, indipendentemente da quale finestra abbia lo stato attivo, se si preme Ctrl + Shift + K, il mio programma dovrebbe produrre “Tasto caldo premuto!” nella console e quindi terminare.

Inoltre, è a mia conoscenza che se l’XGrabKey fallisce, il gestore di errori predefinito visualizzerà un messaggio, e poiché non lo è, suppongo che la chiamata abbia esito positivo.

Ovviamente, la mia comprensione è imperfetta in qualche modo. Qualcuno può indicarmi la giusta direzione?

    #include  #include  #include  using namespace std; int main() { 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 0; } 

    Il tuo programma funziona qui. La mia ipotesi è che tu abbia un altro modificatore attivo, come NumLock. GrabKey funziona solo sulla maschera del modificatore esatta .

    Ad esempio, ecco un codice (GPL) dal gestore di windows di metacity

     /* Grab/ungrab, ignoring all annoying modifiers like NumLock etc. */ static void meta_change_keygrab (MetaDisplay *display, Window xwindow, gboolean grab, int keysym, unsigned int keycode, int modmask) { unsigned int ignored_mask; /* Grab keycode/modmask, together with * all combinations of ignored modifiers. * X provides no better way to do this. */ meta_topic (META_DEBUG_KEYBINDINGS, "%s keybinding %s keycode %d mask 0x%x on 0x%lx\n", grab ? "Grabbing" : "Ungrabbing", keysym_name (keysym), keycode, modmask, xwindow); /* efficiency, avoid so many XSync() */ meta_error_trap_push (display); ignored_mask = 0; while (ignored_mask <= display->ignored_modifier_mask) { if (ignored_mask & ~(display->ignored_modifier_mask)) { /* Not a combination of ignored modifiers * (it contains some non-ignored modifiers) */ ++ignored_mask; continue; } if (meta_is_debugging ()) meta_error_trap_push_with_return (display); if (grab) XGrabKey (display->xdisplay, keycode, modmask | ignored_mask, xwindow, True, GrabModeAsync, GrabModeSync); else XUngrabKey (display->xdisplay, keycode, modmask | ignored_mask, xwindow); if (meta_is_debugging ()) { int result; result = meta_error_trap_pop_with_return (display, FALSE); if (grab && result != Success) { if (result == BadAccess) meta_warning (_("Some other program is already using the key %s with modifiers %x as a binding\n"), keysym_name (keysym), modmask | ignored_mask); else meta_topic (META_DEBUG_KEYBINDINGS, "Failed to grab key %s with modifiers %x\n", keysym_name (keysym), modmask | ignored_mask); } } ++ignored_mask; } meta_error_trap_pop (display, FALSE); } 

    Se stai usando / targeting gtk su X11, c’è una libreria C con un’interfaccia molto più semplice:

    https://github.com/engla/keybinder

    Include attacchi Python, Lua e Vala. (Anche menzionato qui .)

    Con la tua maschera ControlMask | ShiftMask ControlMask | ShiftMask non otterrai la chiave se viene tenuto ControlMask | ShiftMask un altro tasto modificatore. Questo suona bene, in primo luogo, ma c’è un trabocchetto: NumLock , CapsLock e simili sono tutti trattati come modificatori.

    Hai due opzioni:

    • Chiama XGrabKey() più volte, una volta per ogni combinazione esplicita a cui sei interessato.
    • Chiama XGrabKey() con AnyModifier e usa event.xkey.state per verificare se i modificatori sono come ti aspettavi.

    Il file di intestazione definisce ShiftMask , LockMask , ControlMask , Mod1Mask , Mod2Mask , Mod3Mask , Mod4Mask , Mod5Mask e AnyModifier .

    Le chiavi sono:

     Mask | Value | Key ------------+-------+------------ ShiftMask | 1 | Shift LockMask | 2 | Caps Lock ControlMask | 4 | Ctrl Mod1Mask | 8 | Alt Mod2Mask | 16 | Num Lock Mod3Mask | 32 | Scroll Lock Mod4Mask | 64 | Windows Mod5Mask | 128 | ??? 

    Avvertenza Ho scoperto le chiavi ModNMask provando e non so se questo è valido su tutte le macchine / configurazioni / versioni / sistemi operativi.

    Nel tuo caso, probabilmente vuoi assicurarti che ShiftMask | CtrlMask ShiftMask | CtrlMask è impostato, Mod1Mask | Mod4Mask Mod1Mask | Mod4Mask sono chiari e gli altri da ignorare.

    Lo farei per impostare la chiave di cattura:

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

    E questo per verificare se sono stati impostati i modificatori giusti:

     switch (ev.type) { case KeyPress: if ((ev.xkey.state & (ShiftMask | CtrlMask | Mod1Mask | Mod4Mask)) == (ShiftMask | CtrlMask)) // ... }