python - ¿Por qué XGrabKey genera eventos adicionales de enfoque y enfoque?




linux x11 (5)

¿Alguien sabe una función xlib para atrapar un evento de pulsación de tecla sin perder el foco original? Cómo deshacerse de él?

(o "para usar XGrabKey () sin generar un enfoque Grab-style"?)

(o "¿Cómo deshacerse de los eventos de enfoque NotifyGrab y NotifyUngrab a nivel del sistema?"

El XGrabKey perderá el foco en la tecla presionada y restaurará el foco en la tecla liberada.

Y quiero atrapar la pulsación de tecla sin fugarla a la ventana original (al igual que XGrabKey puede hacerlo).

Referencias

  1. ... XGrabKey robará el foco ... https://bugs.launchpad.net/gtkhotkey/+bug/390552/comments/8

  2. ... El programa recibe el control para hacer algo en respuesta a la combinación de teclas. Mientras tanto, el programa se ha centrado temporalmente ... Durante XGrabKey (placa), descubre qué ventana se ha centrado

  3. ... La función XGrabKeyboard toma de forma activa el control del teclado y genera los eventos FocusIn y FocusOut ... http://www.x.org/archive/X11R6.8.0/doc/XGrabKeyboard.3.html#toc3

  4. ... No puedo ver una manera de proporcionar el comportamiento de cambio de escritorio actual de metacity (cambiando y mostrando el cuadro de diálogo emergente al mismo tiempo) sin causar un enfoque tipo Grab en la ventana ... https://mail.gnome.org/archives/wm-spec-list/2007-May/msg00000.html

  5. ... El modo de pantalla completa no debería salir en los eventos de FocusOut con NotifyGrab ... https://bugzilla.mozilla.org/show_bug.cgi?id=578265

  6. agarrar el teclado no permite cambiar el enfoque ... agarrar el teclado no permite cambiar el enfoque

  7. Eventos de enfoque generados por Grabs (tanto la captura activa de XGrabKeyboard como la captura pasiva de XGrabKey) http://www.x.org/releases/X11R7.6/doc/libX11/specs/libX11/libX11.html#Focus_Events_Generated_by_Grabs

  8. el código fuente de XGrabKey: http://cgit.freedesktop.org/xorg/lib/libX11/tree/src/GrKey.c quizás podríamos modificar esto para deshacernos de los eventos de enfoque?

  9. hay "DoFocusEvents (keybd, oldWin, grab-> window, NotifyGrab);" en ActivateKeyboardGrab (): http://cgit.freedesktop.org/xorg/xserver/tree/dix/events.c

Estoy escribiendo un software de mapeo de combinación de teclas (y movimiento del mouse) con una sola tecla: https://code.google.com/p/diyism-myboard/

Me di cuenta en Windows con RegisterHotKey () y UnRegisterHotKey (): https://code.google.com/p/diyism-myboard/downloads/detail?name=MyBoard.pas

Y quiero migrarlo a Linux con XGrabKey () y XUngrabKey (): https://code.google.com/p/diyism-myboard/downloads/detail?name=myboard.py

Creé una recompensa de $ 10 para resolver este problema. Necesitamos más patrocinadores para colocar recompensas. https://www.bountysource.com/issues/1072081-right-button-menu-flashes-while-jkli-keys-move-the-mouse-pointer


Finalmente, como sabes que Linux significa libertad, modifiqué xserver para deshacerte del enfoque al estilo grab:

sudo apt-get build-dep xorg-server
apt-get source xorg-server
cd xorg-server-*
#modify or patch dix/events.c: comment off "DoFocusEvents(keybd, oldWin, grab->window, NotifyGrab);" in ActivateKeyboardGrab(), comment off "DoFocusEvents(keybd, grab->window, focusWin, NotifyUngrab);" in DeactivateKeyboardGrab()
sudo apt-get install devscripts
debuild -us -uc    #"-us -uc" to avoid the signature step
cd ..
sudo dpkg --install xserver-xorg-core_*.deb
#clear dependencies:
sudo apt-mark auto $(apt-cache showsrc xorg-server | grep Build-Depends | perl -p -e 's/(?:[\[(].+?[\])]|Build-Depends:|,|\|)//g')
sudo apt-get autoremove

Y también necesito deshacerme de XGrabKeyboard en el menú contextual de gtk:

sudo apt-get build-dep gtk+2.0
apt-get source gtk+2.0
cd gtk+2.0-*
#modify or patch it: add "return TRUE;" in first line of popup_grab_on_window() of gtk/gtkmenu.c
dpkg-source --commit
debuild -us -uc  #"-us -uc" to avoid the signature step, maybe need: sudo apt-get install devscripts
cd ..
sudo dpkg --install libgtk2.0-0_*.deb
#clear dependencies:
sudo apt-mark auto $(apt-cache showsrc gtk+2.0 | grep Build-Depends | perl -p -e 's/(?:[\[(].+?[\])]|Build-Depends:|,|\|)//g')
sudo apt-get autoremove

Ahora myboard.py funciona bien.

Si está utilizando ubuntu raring-updates edition, podría intentar:

https://code.google.com/p/diyism-myboard/downloads/detail?name=xserver-xorg-core_1.13.3-0ubuntu6.2_i386.deb

y:

https://code.google.com/p/diyism-myboard/downloads/detail?name=libgtk2.0-0_2.24.17-0ubuntu2_i386.deb


Mi código actual (de http://diyism-myboard.googlecode.com/files/myboard.py ):

disp=Display()
screen=disp.screen()
root=screen.root

def grab_key(key, mod):
    key_code=string_to_keycode(key)
    #3rd: bool owner_events, 4th: pointer_mode, 5th: keyboard_mode, X.GrabModeSync, X.GrabModeAsync
    root.grab_key(key_code, mod, 0, X.GrabModeAsync, X.GrabModeAsync)
    root.grab_key(key_code, mod|X.LockMask, 0, X.GrabModeAsync, X.GrabModeAsync) #caps lock
    root.grab_key(key_code, mod|X.Mod2Mask, 0, X.GrabModeAsync, X.GrabModeAsync) #num lock
    root.grab_key(key_code, mod|X.LockMask|X.Mod2Mask, 0, X.GrabModeAsync, X.GrabModeAsync)

def main():
    grab_key('Shift_L', X.NONE)
    grab_key('Shift_R', X.NONE)
    while 1:
          evt=root.display.next_event()
          if evt.type in [X.KeyPress, X.KeyRelease]: #ignore X.MappingNotify(=34)
             handle_event(evt)

if __name__ == '__main__':
   main()

Cuando presiono la tecla "shift", el foco se pierde, y cuando lo suelto, vuelve el foco.


Para escribir un software de asignación de teclas (acceso directo), también consulte libtermkey , una biblioteca de entrada de clave de terminal (escrita en C) que reconoce el informe de posición / botón del estilo XTerm, teclas especiales (como flecha y teclas de función), incluyendo " teclas modificadas como Ctrl-Left .

Hay, por ejemplo, POE::Wheel::TermKey , "un contenedor perl asíncrono alrededor de la biblioteca libtermkey , que proporciona una forma abstracta de leer eventos de pulsación de tecla en programas basados ​​en terminales".


Parece que XQueryKeymap lo resolverá. Vea a continuación el código fuente de C ++ que encontré :

/* compile with g++ keytest.cpp -LX11 -o keytest */
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>

double gettime() {
 timeval tim;
 gettimeofday(&tim, NULL);
 double t1=tim.tv_sec+(tim.tv_usec/1000000.0);
 return t1;
}

int main() {
 Display *display_name;
 int depth,screen,connection;
 display_name = XOpenDisplay(NULL);
 screen = DefaultScreen(display_name);
 depth = DefaultDepth(display_name,screen);
 connection = ConnectionNumber(display_name);
 printf("Keylogger started\n\nInfo about X11 connection:\n");
 printf(" The display is::%s\n",XDisplayName((char*)display_name));
 printf(" Width::%d\tHeight::%d\n",
 DisplayWidth(display_name,screen),
 DisplayHeight(display_name,screen));
 printf(" Connection number is %d\n",connection);

 if(depth == 1)
  printf(" You live in prehistoric times\n");
 else
  printf(" You've got a coloured monitor with depth of %d\n",depth);

 printf("\n\nLogging started.\n\n");

 char keys_return[32];
 while(1) {
  XQueryKeymap(display_name,keys_return);
  for (int i=0; i<32; i++) {
   if (keys_return[i] != 0) {
    int pos = 0;
    int num = keys_return[i];
    printf("%.20f: ",gettime());
    while (pos < 8) {
     if ((num & 0x01) == 1) {
      printf("%d ",i*8+pos);
     }
     pos++; num /= 2;
    }
    printf("\n");
   }
  }
  usleep(30000);
 }
 XCloseDisplay(display_name);
}

Tenga en cuenta que este no es un código probado, ni es mío, simplemente lo encontré en Internet.


Tengo una idea de que estoy bastante seguro de que funcionaría, pero tengo que irme a la cama y no puedo probarla por mí mismo, y no es bonita, ya que no creo que haya ninguna manera de hacer lo que quiero en X. Aquí están los pasos que tengo en mente. En resumen: desactive el teclado en X, lea los eventos desde la API de nivel inferior y vuélvalos a X usted mismo. Tienes que deshabilitar el teclado en X porque de lo contrario, podrías mirar el evento, pero no detenerlo; lo leerías junto a X, no lo interceptarías.

Entonces aquí está roto:

1) Ejecute xinput -list para obtener el teclado que X está usando

2) Ejecute xinput list-props id para encontrar la propiedad Device Enabled

3) Ejecute xinput set-prop id prop 0 para deshabilitar el dispositivo en X:

xinput -list
xinput list-props 12 # 12 is the id of the keyboard in the list... (example # btw)
xinput set-prop 12 119 0 # 119 is the "Device Enabled" prop, we turn it off

No sé cómo funciona xinput en el nivel xlib, simplemente lo llamaría al shell para simplificar la implementación.

4) Abra /dev/input/eventX , donde X es el dispositivo de teclado. De hecho, buscaría el nombre (dado en xinput -list) en / dev / input / by-id y lo abriría de esa manera. Esto probablemente requerirá root en algún momento, ya que los permisos sobre estos son generalmente bastante restrictivos.

5) Lee la entrada del teclado desde allí:

El formato de los datos de los eventos de entrada es:

struct input_event {
    int tv_sec; // time of the event
    int tv_usec; // ditto
    ushort type; // == 1 for key event
    ushort code; // key code, not the same as X keysyms, you should check it experimentally. 42 is left shift on mine, 54 is right shift
    int value; // for keys, 1 == pressed, 0 == released, 2 == repeat
}

las entradas son de 32 bits, los usuales son de 16 bits. Como solo te interesa la entrada de teclado, puedes hacerlo de forma sencilla:

  • lee e ignora 8 bytes.

  • el siguiente byte debe ser 1, luego el siguiente es 0. si no, omita este evento

  • El siguiente byte es el final del código de tecla, y como hay <255 teclas, eso es suficiente para

  • omita el siguiente byte.

  • lea el siguiente byte para ver si se presiona o se libera

  • omita los siguientes tres bytes

6) Cuando tengas un evento que te interesa atrapar, tráelo tú mismo. De lo contrario, use XSendEvent para enviarlo a X para que pueda procesarse normalmente. Asignar el código de hardware que obtienes de / dev / input al keysym apropiado puede ser un truco, pero estoy bastante seguro de que hay una función en xlib en alguna parte para ayudar con esto.

7) goto 5 y bucle hasta que hayas terminado

8) ¡Asegúrate de volver a configurar todo como era cuando salías, o podrías romper la entrada del teclado del usuario a X!

Sugiero probar esto con un segundo teclado usb, puedes desactivar y escuchar los teclados de forma independiente el uno del otro con / dev / input y xinput, por lo que si bloqueas, aún tienes el primer teclado y funciona normalmente. (En realidad, creo que sería genial hacerlo intencionalmente con un segundo teclado, ¡duplicar los atajos de teclado!)

Pero sí, necesitar root y potencialmente dejar el teclado "separado" de X no son nada bonitos, y ese reenvío con SendKey podría ser más fácil decirlo que hacerlo, pero estoy bastante seguro de que esto funcionaría y te daría la máxima flexibilidad .







xlib