Global Hotkey avec X11/Xlib

mon but est d'avoir un programme qui dort en arrière-plan mais peut être activé par l'utilisateur via un certain"hotkey". En fouillant autour du manuel de Xlib et du manuel de Xlib o'Reilly, j'ai compris que la bonne façon d'y arriver est avec XGrabKey. Cependant, ma compréhension du processus est incorrecte comme une simple preuve de concept ne fonctionne pas.

ma compréhension est que si J'appelle XGrabKey avec la fenêtre root comme la fenêtre grab_window, et owner_events false, puis chaque fois que mon hotkey est pressé l'événement sera envoyé seulement à la fenêtre racine. Si je sélectionne alors les évènements KeyPress à partir de la fenêtre root, et que j'écoute ensuite les évènements X, je devrais obtenir un évènement key press lorsque la touche hotkey est pressée. J'ai collé un exemple minime ci-dessous.

ce que j'attends c'est que quand le programme est lancé, indépendamment de ce que la fenêtre a mis au point, si Ctrl+Shift+K est pressé, mon programme devrait sortir "touche chaude pressée!"dans la console, puis terminer.

de plus, je crois comprendre que si XGrabKey échoue, le gestionnaire d'erreurs par défaut affichera un message, et puisqu'il ne le fait pas, je suppose que l'appel réussit.

de toute évidence, ma compréhension est imparfaite. Quelqu'un peut me pointer dans la bonne direction?

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


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;
}
25
demandé sur cheshirekow 2010-10-28 00:08:00

3 réponses

Votre programme fonctionne ici. Je pense que vous avez un autre modificateur actif, comme NumLock. GrabKey ne fonctionne que sur le masque modificateur exact .

par exemple voici un code (GPL) de metacity window manager

/* 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);
}
20
répondu Havoc P 2010-10-27 20:47:02

si vous utilisez / ciblez gtk sur X11, il y a une bibliothèque C avec une interface beaucoup plus simple:

https://github.com/engla/keybinder

comprend les fixations Python, Lua et Vala. (Également mentionné ici .)

8
répondu Anukool 2017-05-23 12:03:05

avec votre masque ControlMask | ShiftMask vous n'obtiendrez pas la clé si une autre clé modificateur est détenue. Cela semble correct en premier lieu, mais il ya un piège: NumLock , CapsLock et tous les mêmes sont traités comme des modificateurs, aussi.

vous avez deux options:

  • vous appelez XGrabKey() plusieurs fois, une fois pour chaque combinaison explicite qui vous intéresse.
  • vous appelez XGrabKey() avec AnyModifier et utilisez event.xkey.state pour vérifier si les modificateurs sont comme vous l'aviez prévu.

le fichier d'en-tête <X.h> définit ShiftMask , LockMask , ControlMask , Mod1Mask , Mod2Mask , Mod3Mask , Mod4Mask , Mod5Mask et AnyModifier .

les clés sont:

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 | ???

Avertissement j'ai découvert l' ModNMask essayer et je ne sais pas si cela est valable sur toutes les machines / configurations / versions / systèmes d'exploitation.

dans votre cas, vous voulez probablement vous assurer que ShiftMask | CtrlMask est défini, Mod1Mask | Mod4Mask sont claires, et les autres à ignorer.

je le ferais pour configurer la clé grab:

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

et ceci pour vérifier si les bons modificateurs sont définis:

switch (ev.type)  {
case KeyPress:
    if ((ev.xkey.state & (ShiftMask | CtrlMask | Mod1Mask | Mod4Mask)) == (ShiftMask | CtrlMask))
        // ...
}
8
répondu Christian Hujer 2015-03-12 04:06:05