2012-07-03 12 views
6

Quiero registrar todos los eventos presionados de tecla entrante sin importar qué ventana esté enfocada o dónde esté el puntero.X Captura de eventos KeyPress/Release independientemente de Window in focus

He escrito un código de muestra que debe capturar los eventos de tecla presionada de la ventana actual en foco.

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <locale.h> 
#include <stdint.h> 
#include <stdarg.h> 
#include <errno.h> 
#include <pthread.h> 
#include <X11/Xlib.h> 
#include <X11/Xos.h> 
#include <X11/Xfuncs.h> 
#include <X11/Xutil.h> 

#include <X11/Xatom.h> 
int _invalid_window_handler(Display *dsp, XErrorEvent *err) { 
    return 0; 
} 

int main() 
{ 
    Display *display = XOpenDisplay(NULL); 
    int iError; 
    KeySym k; 
    int revert_to; 
    Window window; 
    XEvent event; 
    Time time; 
    XSetErrorHandler(_invalid_window_handler); 
    XGetInputFocus(display, &window, &revert_to); 
    XSelectInput(display, window, KeyPressMask | KeyReleaseMask); 
    iError = XGrabKeyboard(display, window, 
          KeyPressMask | KeyReleaseMask, 
          GrabModeAsync, 
          GrabModeAsync, 
          CurrentTime); 
    if (iError != GrabSuccess && iError == AlreadyGrabbed) { 
     XUngrabPointer(display, CurrentTime); 
     XFlush(display); 
     printf("Already Grabbed\n");  
    } else if (iError == GrabSuccess) { 
     printf("Grabbed\n"); 
    } 
    while(1) { 
      XNextEvent(display,&event); 
      switch (event.type) { 
       case KeyPress : printf("Key Pressed\n"); break; 
       case KeyRelease : printf("Key Released\n"); break; 
       case EnterNotify : printf("Enter\n"); break; 
      } 
    } 
    XCloseDisplay(display); 
    return 0; 
} 

Estoy llamando XGrabKeyboard para capturar el teclado como la aplicación que creó la ventana pudieran tener los eventos de teclado ya agarrado. Con el código mencionado anteriormente, puedo agarrar el teclado pero no puedo recibir eventos KeyPress o KeyRelease para ninguna tecla del teclado dentro del ciclo while. ¿Hay algo que me falta en el código debido a que no puedo recibir los eventos? Cualquier ayuda es muy apreciada.

Mi objetivo final es capturar eventos de pulsación de tecla en la pantalla, independientemente de la ventana en foco. He dado el código de muestra para solo ventana en foco para que el código sea legible. Haría XQueryTree para obtener todas las ventanas y aplicar la misma lógica dada anteriormente para obtener el resultado esperado.

+0

por qué no utilizar el subsistema de evento, entonces? –

+0

¿Podría explicar el subsistema de eventos? He dado KeyPressMask, KeyReleaseMask como los eventos para capturar en la llamada a XSelectInput. Gracias –

+0

Ignacio Vazquez-Abrams se refiere al subsistema de entrada del kernel de Linux, que es accesible a través de los dispositivos de caracteres en '/ dev/input /'. Omite por completo X y obtiene los eventos HID directamente del kernel, pero también requiere privilegios de root. –

Respuesta

4

Necesita tener una ventana asignada para poder agarrar el teclado. Aquí está una prueba de concepto:

#include <X11/Xlib.h> 
#include <X11/keysym.h> 
#include <stdio.h> 

int main() 
{ 
    Display *display; 
    Window window, rootwindow; 
    XEvent event; 
    KeySym escape; 

    display = XOpenDisplay(NULL); 
    rootwindow = DefaultRootWindow(display); 
    window = XCreateWindow(display, rootwindow, 
          -99, -99, 1, 1, /* x, y, width, height */ 
          0, 0, InputOnly, /* border, depth, class */ 
          CopyFromParent, /* visual */ 
          0, NULL); /* valuemask and attributes */ 

    XSelectInput(display, window, StructureNotifyMask | SubstructureRedirectMask | ResizeRedirectMask | KeyPressMask | KeyReleaseMask); 
    XLowerWindow(display, window); 
    XMapWindow(display, window); 

    do { 
     XNextEvent(display, &event); 
    } while (event.type != MapNotify); 

    XGrabKeyboard(display, window, False, GrabModeAsync, GrabModeAsync, CurrentTime); 
    XLowerWindow(display, window); 

    escape = XKeysymToKeycode(display, XK_Escape); 
    printf("\nPress ESC to exit.\n\n"); 
    fflush(stdout); 

    while (1) { 

     XNextEvent(display, &event); 

     if (event.type == KeyPress) { 
      printf("KeyPress: keycode %u state %u\n", event.xkey.keycode, event.xkey.state); 
      fflush(stdout); 

     } else 
     if (event.type == KeyRelease) { 

      printf("KeyRelease: keycode %u state %u\n", event.xkey.keycode, event.xkey.state); 
      fflush(stdout); 

      if (event.xkey.keycode == escape) 
       break; 
     } else 
     if (event.type == UnmapNotify) { 

      XUngrabKeyboard(display, CurrentTime); 
      XDestroyWindow(display, window); 
      XCloseDisplay(display); 

      display = XOpenDisplay(NULL); 
      rootwindow = DefaultRootWindow(display); 
      window = XCreateWindow(display, rootwindow, 
            -99, -99, 1, 1, /* x, y, width, height */ 
            0, 0, InputOnly, /* border, depth, class */ 
            CopyFromParent, /* visual */ 
            0, NULL); /* valuemask and attributes */ 

      XSelectInput(display, window, StructureNotifyMask | SubstructureRedirectMask | ResizeRedirectMask | KeyPressMask | KeyReleaseMask); 
      XLowerWindow(display, window); 
      XMapWindow(display, window); 

      do { 
       XNextEvent(display, &event); 
      } while (event.type != MapNotify); 

      XGrabKeyboard(display, window, False, GrabModeAsync, GrabModeAsync, CurrentTime); 
      XLowerWindow(display, window); 

      escape = XKeysymToKeycode(display, XK_Escape); 

     } else { 

      printf("Event type %d\n", event.type); 
      fflush(stdout); 
     } 
    } 

    XUngrabKeyboard(display, CurrentTime); 

    XDestroyWindow(display, window); 
    XCloseDisplay(display); 
    return 0; 
} 

Se utiliza una ventana pequeña (ni siquiera se molestan en establecer un título para ello) se reduce a la parte inferior de la pila de ventanas, por lo que pasa detrás de las ventanas existentes. Puede comunicarse con el administrador de ventanas (WM) para hacer que la ventana sea nítida y transparente, o que esté iconizada, de modo que no haya una ventana visible en la pantalla; el código anterior no molesta.

El truco que utilicé es que cada vez que el usuario logra desasignar la ventana, digamos, moviéndose a otra área de trabajo, el código destruye la ventana anterior, crea una nueva y vuelve a agarrar el teclado. Debe ser lo suficientemente rápido como para no perder ninguna presión de tecla. Puede haber otras formas de hacerlo, pero sospecho que requieren una interacción más cercana con el administrador de ventanas.

Tenga en cuenta que nunca he tenido que agarrar el teclado de forma tan persistente, por lo que el enfoque anterior probablemente no sea el más simple. Fue solo un enfoque que creo que funciona; hay probablemente mejores.

+1

¿Hay alguna manera de capturar los eventos del teclado al mismo tiempo que les permite ser recibidos por el resto de las aplicaciones X? –

+3

Puede usar 'XSendEvent (display, InputFocus, True, KeyPressMask | KeyReleaseMask, event)' para enviar eventos de teclado capturados a la ventana que tiene el foco de entrada actual, pero ese suele ser el enfoque equivocado. Si desea controlar todas las pulsaciones de teclas, * no *: por mi parte, no me gustan los rastreadores de teclado. Para ver cómo se implementan las teclas de acceso rápido/autocódigos, ver p. 'GtkHotKey' o' libtomboy/tomboykeybinder.c'; agarran claves específicas en la ventana raíz (es decir, cuando no son manejadas por otras ventanas) y lo hacen bien. –

+1

Gracias!Solo para el registro, quiero monitorear todas las pulsaciones de teclas para asegurarme de que mi viejo teclado no me está jugando ningún truco. –

3

El siguiente comando imprimir una lista de todos los eventos de toda la sesión X en la consola:

$ xinput test-xi2 --root 

Ejemplo de salida:

⎡ Virtual core pointer      id=2 [master pointer (3)] 
⎜ ↳ Virtual core XTEST pointer    id=4 [slave pointer (2)] 
⎜ ↳ USB Mouse         id=10 [slave pointer (2)] 
⎜ ↳ MCE IR Keyboard/Mouse (ite-cir)   id=11 [slave pointer (2)] 
⎣ Virtual core keyboard      id=3 [master keyboard (2)] 
    ↳ Virtual core XTEST keyboard    id=5 [slave keyboard (3)] 
    ↳ Power Button        id=6 [slave keyboard (3)] 
    ↳ Video Bus         id=7 [slave keyboard (3)] 
    ↳ Power Button        id=8 [slave keyboard (3)] 
    ↳ Oracle USB Keyboard      id=9 [slave keyboard (3)] 
    ↳ ITE8713 CIR transceiver     id=12 [slave keyboard (3)] 
EVENT type 14 (RawKeyRelease) 
    device: 3 (9) 
    detail: 36 
    valuators: 

EVENT type 3 (KeyRelease) 
    device: 9 (9) 
    detail: 36 
    flags: 
    root: 1324.55/821.81 
    event: 1324.55/821.81 
    buttons: 
    modifiers: locked 0x10 latched 0 base 0 effective: 0x10 
    group: locked 0 latched 0 base 0 effective: 0 
    valuators: 
    windows: root 0x9c event 0x9c child 0x7291d5 
EVENT type 15 (RawButtonPress) 
    device: 2 (10) 
    detail: 1 
    valuators: 
    flags: 

EVENT type 4 (ButtonPress) 
    device: 10 (10) 
    detail: 1 
    flags: 
    root: 1324.55/821.81 
    event: 1324.55/821.81 
    buttons: 
    modifiers: locked 0x10 latched 0 base 0 effective: 0x10 
    group: locked 0 latched 0 base 0 effective: 0 
    valuators: 
    windows: root 0x9c event 0x9c child 0x7291d5 
EVENT type 16 (RawButtonRelease) 
    device: 2 (10) 
    detail: 1 
    valuators: 
    flags: 
Cuestiones relacionadas