2010-10-27 42 views
24

Mon but est d'avoir un programme qui dort en arrière-plan mais qui peut être activé par l'utilisateur via une "touche de raccourci". De creuser autour du manuel Xlib et le manuel Xlib O'reilly, je comprends que la bonne façon d'y parvenir est avec XGrabKey. Cependant, ma compréhension du processus est incorrecte, car une simple preuve de concept ne fonctionne pas.Hotkey global avec X11/Xlib

Ma compréhension est que si je l'appelle XGrabKey avec la fenêtre racine comme grab_window et owner_events false, chaque fois que mon raccourci clavier est enfoncé, l'événement sera envoyé seulement à la fenêtre racine. Si je sélectionne ensuite les événements KeyPress dans la fenêtre racine, puis que j'écoute des événements X, je devrais obtenir un événement de pression de touche lorsque la touche de raccourci est enfoncée. J'ai collé un exemple minimal ci-dessous. Ce que je m'attend à ce que lorsque le programme est exécuté, indépendamment de ce que la fenêtre a le focus, si Ctrl + Shift + K est pressé, mon programme devrait afficher "Hotkey pressé!" dans la console, puis se terminer. De plus, si je comprends bien, si XGrabKey échoue, le gestionnaire d'erreurs par défaut affichera un message, et comme ce n'est pas le cas, je suppose que l'appel réussit.

De toute évidence, ma compréhension est imparfaite en quelque sorte. Est-ce que quelqu'un peut-il me montrer 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; 
} 
+2

Dans votre code que vous utilisez 'XK_Y', vous pouvez veux dire 'XK_K' à la place? –

Répondre

19

Votre programme fonctionne ici. Ma conjecture est que vous avez un autre modificateur actif, tel que NumLock. GrabKey ne fonctionne que sur le masque de modificateur exact.

Par exemple est ici un code (GPL) de gestionnaire de fenêtres 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); 
} 
+4

Oh mec. Vous avez absolument raison. Le verrouillage numérique était activé. Merci beaucoup. J'ai perdu quelques heures sur ma stupidité aujourd'hui, mais je pense que tu m'as épargné de gaspiller plusieurs autres. – cheshirekow

8

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

https://github.com/engla/keybinder

Comprend les liaisons Python, Lua et Vala. (Également mentionné here.)

+1

une bonne bibliothèque. +1 pour vous Merci :-) – madper

7

Avec votre masque ControlMask | ShiftMask vous n'obtiendrez pas la clé si une autre touche de modification est maintenue. Cela semble correct en premier lieu, mais il y a un piège: NumLock, CapsLock et tous sont également traités comme des modificateurs, aussi.

Vous avez deux options:.

  • Vous appelez XGrabKey() plusieurs fois, une fois pour chaque combinaison explicite que vous êtes intéressé par
  • Vous appelez XGrabKey() avec AnyModifier et utilisez event.xkey.state pour vérifier si les modificateurs sont comme 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 les ModNMask clés en essayant 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 effacés, et les autres doivent être ignorés.

Je ferais ceci pour installer la benne clé:

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

Et ceci pour vérifier si les modificateurs sont réglés:

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

La valeur '128' correspond à __ISO_Level3_Shift__ sur mon système. –