2010-10-27 12 views
24

Mi objetivo es tener un programa que duerma en segundo plano pero que pueda ser activado por el usuario mediante una "tecla de acceso rápido". Después de excavar el manual de Xlib y el manual de Xlib O'reilly, veo que la forma correcta de hacerlo es con XGrabKey. Sin embargo, mi comprensión del proceso es incorrecta ya que una simple prueba de concepto no funciona.Global Hotkey con X11/Xlib

Mi opinión es que si llamo XGrabKey con la ventana raíz que la grab_window y owner_events falsa, entonces cada vez que se pulsa la tecla de acceso directo a mi evento será enviado solamente a la ventana raíz. Si a continuación, selecciono KeyPress events desde la ventana raíz, y luego escucho los eventos X, debería obtener un evento de pulsación de tecla cuando se presiona la tecla rápida. He pegado un ejemplo mínimo a continuación.

Lo que espero es que cuando se ejecuta el programa, independientemente de qué ventana tenga el foco, si se presionan Ctrl + Shift + K, mi programa debería generar "¡Tecla presionada!" en la consola, y luego terminar.

Además, tengo entendido que si falla la XGrabKey, el controlador de errores predeterminado mostrará un mensaje, y como no es así, supongo que la llamada tiene éxito.

Obviamente, mi comprensión es defectuosa de alguna manera. ¿Alguien puede señalarme en la dirección correcta?

#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

En su código que use 'XK_Y', es posible ¿Quieres decir 'XK_K' en su lugar? –

Respuesta

19

Su programa funciona aquí. Supongo que tiene otro modificador activo, como NumLock. GrabKey solo funciona en la máscara de modificador exacta.

Por ejemplo, aquí tienes (GPL) Código del gestor de ventanas 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 hombre. Estás absolutamente en lo correcto. Bloq Num estaba activado. Muchas gracias. He perdido algunas horas en mi estupidez hoy, pero creo que me salvaste de perder muchas más. – cheshirekow

8

Si está utilizando/focalización GTK en X11, hay una biblioteca de C con una interfaz mucho más simple:

https://github.com/engla/keybinder

Incluye las encuadernaciones de Python, Lua y Vala. (También se menciona here.)

+1

una buena biblioteca. +1 para ti. Gracias :-) – madper

7

Con su máscara ControlMask | ShiftMask no obtendrá la clave si se mantiene otra tecla modificadora. Esto suena bien desde el principio, pero hay una trampa: NumLock, CapsLock y todos son tratados como modificadores también.

tiene dos opciones:.

  • XGrabKey() Usted lo llama varias veces, una para cada combinación explícita de que usted está interesado en
  • Usted llama XGrabKey() con AnyModifier y utilizar event.xkey.state para comprobar si los modificadores son como esperabas

El archivo de cabecera <X.h> define ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask y AnyModifier.

Las claves son:

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

Advertencia me enteré de las llaves por ModNMask tratando y que no sé si esto es válido para todos los/configuraciones/versiones de sistemas operativos máquinas /.

En su caso, es probable que desee asegurarse de que ShiftMask | CtrlMask esté configurado, Mod1Mask | Mod4Mask sean claros y que los demás sean ignorados.

que haría esta configuración a la apropiación clave:

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

Y esto para comprobar si los modificadores derecho se fijan:

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

El valor '128' corresponde a __ISO_Level3_Shift__ en mi sistema. –