2010-10-27 13 views
24

Mein Ziel ist es, ein Programm zu haben, das im Hintergrund schläft, aber vom Benutzer über einen "Hotkey" aktiviert werden kann. Beim Durchforsten des Xlib-Handbuchs und des Xlib O'reilly-Handbuchs erkläre ich, dass der korrekte Weg dahin XGrabKey ist. Mein Verständnis des Prozesses ist jedoch falsch, da ein einfacher Proof of Concept nicht funktioniert.Global Hotkey mit X11/Xlib

Mein Verständnis ist, dass, wenn ich XGrabKey mit dem Root-Fenster als grab_window nennen, und owner_events falsch ist, dann, wenn mein Hotkey das Ereignis gedrückt wird nur in das Stammfenster gesendet werden. Wenn ich KeyPress-Ereignisse aus dem Hauptfenster auswähle und dann auf X-Ereignisse lausche, sollte ich beim Drücken des Hotkeys ein Tastendruckereignis erhalten. Ich habe unten ein minimales Beispiel eingefügt.

Was ich erwarte ist, dass, wenn das Programm ausgeführt wird, unabhängig davon, welches Fenster Fokus hat, wenn Strg + Shift + K gedrückt wird, sollte mein Programm "Hotkey gedrückt!" in der Konsole und dann beenden.

Darüber hinaus ist es mein Verständnis, dass, wenn der XGrabKey fehlschlägt, der Standardfehlerhandler eine Nachricht anzeigen wird, und da ich nicht davon ausgehe, dass der Aufruf erfolgreich ist.

Offensichtlich ist mein Verständnis irgendwie fehlerhaft. Kann mir jemand in die richtige Richtung zeigen?

#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

In Ihrem Code Sie 'XK_Y' verwenden, können Sie Möchtest du stattdessen 'XK_K' sagen? –

Antwort

19

Ihr Programm funktioniert hier. Meine Vermutung ist, dass Sie einen anderen Modifikator wie NumLock aktiv haben. GrabKey funktioniert nur auf der exakten Modifikationsmaske.

Denn hier Beispiel ist einig (GPL) Code von 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); 
} 
+4

Oh Mann. Du hast absolut recht. Num-Sperre war an. Vielen Dank. Ich habe heute ein paar Stunden mit meiner Dummheit verbracht, aber ich glaube, du hast mich davor bewahrt, noch mehr zu verschwenden. – cheshirekow

8

Wenn Sie mit/Targeting-gtk auf X11, gibt es eine C-Bibliothek mit einer viel einfacheren Schnittstelle:

https://github.com/engla/keybinder

Enthält Python-, Lua- und Vala-Bindungen. (Auch erwähnt here.)

+1

eine gute Bibliothek. +1 für dich. Danke :-) – madper

7

Mit Ihrer Maske ControlMask | ShiftMask erhalten Sie nicht den Schlüssel, wenn ein anderer Schlüssel gehalten wird. Das klingt in Ordnung, aber es gibt eine Falle: NumLock, CapsLock und ähnliche werden auch als Modifikatoren behandelt.

Sie haben zwei Möglichkeiten:.

  • Sie rufen XGrabKey() mehrere Male, einmal für jede explizite Kombination, die Sie interessiert sind
  • Sie rufen XGrabKey() mit AnyModifier und verwenden event.xkey.state zu prüfen, ob die Modifikatoren sind wie du es erwartet hast.

Die Header-Datei definiert <X.h>ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask und AnyModifier.

Die Tasten sind:

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

Warnung fand ich durch den Versuch über die ModNMask Schlüssel aus, und ich weiß nicht, ob dies auf allen Maschinen/Konfigurationen/versions/Betriebssysteme gültig ist.

In Ihrem Fall möchten Sie wahrscheinlich sicherstellen, dass ShiftMask | CtrlMask festgelegt ist, Mod1Mask | Mod4Mask sind klar, und die anderen ignoriert werden.

Ich würde dies den Schlüssel greifen Setup tun:

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

Und um dies zu überprüfen, ob die richtigen Modifikatoren eingestellt:

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

Der Wert '128' entspricht __ISO_Level3_Shift__ auf meinem System. –