38 votes

Pourquoi XGrabKey génère-t-il des événements supplémentaires de type focus-out et focus-in ?

Quelqu'un connaît-il une fonction xlib permettant de capturer un événement de pression sur une touche sans perdre le focus initial ? Comment s'en débarrasser ?

(ou "pour utiliser XGrabKey() sans générer de focusout de type Grab" ?)

(ou "Comment se débarrasser des événements NotifyGrab et NotifyUngrab au niveau du système ?)

La XGrabKey perdra le focus sur la touche pressée et le restaurera sur la touche relâchée.

Et je veux capturer l'appui sur la touche sans la faire fuir vers la fenêtre d'origine (tout comme XGrabKey peut le faire).

Références :

  1. ...XGrabKey va voler le focus... https://bugs.launchpad.net/gtkhotkey/+bug/390552/commentaires/8

  2. ...Le programme reçoit la commande de faire quelque chose en réponse à la combinaison de touches. Pendant ce temps, le programme a été temporairement concentré... Pendant XGrabKey(board), découvrir quelle fenêtre avait été focalisée

  3. ...La fonction XGrabKeyboard prend activement le contrôle du clavier et génère des événements FocusIn et FocusOut... http://www.x.org/archive/X11R6.8.0/doc/XGrabKeyboard.3.html#toc3

  4. ...je ne vois pas de moyen de fournir le comportement actuel de changement de bureau de metacity (changement et l'affichage de la boîte de dialogue popup en même temps) sans provoquer une mise au point de type Grab sur la fenêtre. focus out de type Grab sur la fenêtre... https://mail.gnome.org/archives/wm-spec-list/2007-May/msg00000.html

  5. ...Le mode plein écran ne devrait pas sortir sur les événements FocusOut avec NotifyGrab... https://bugzilla.mozilla.org/show_bug.cgi?id=578265

  6. La saisie du clavier ne permet pas de changer le focus ... La saisie du clavier ne permet pas de changer le focus

  7. Événements de mise au point générés par les saisies (à la fois la saisie active de XGrabKeyboard et la saisie passive de XGrabKey) http://www.x.org/releases/X11R7.6/doc/libX11/specs/libX11/libX11.html#Focus_Events_Generated_by_Grabs

  8. le code source de XGrabKey : http://cgit.freedesktop.org/xorg/lib/libX11/tree/src/GrKey.c Nous pourrions peut-être modifier cela pour nous débarrasser des événements de type "focus-out" ?

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

Je suis en train d'écrire un logiciel de mappage d'une touche à une combinaison de touches (et de mouvements de souris) : https://code.google.com/p/diyism-myboard/

Je l'ai réalisé dans Windows avec RegisterHotKey() et UnRegisterHotKey() : https://code.google.com/p/diyism-myboard/downloads/detail?name=MyBoard.pas

Et je veux le migrer vers Linux avec XGrabKey() et XUngrabKey() : https://code.google.com/p/diyism-myboard/downloads/detail?name=myboard.py

J'ai créé une prime de 10 $ pour résoudre ce problème. Nous avons besoin de plus de backers pour placer des bounties. https://www.bountysource.com/issues/1072081-right-button-menu-flashes-while-jkli-keys-move-the-mouse-pointer

12voto

Anthon Points 4119

Au début des années 90, je me suis penché sur les touches de raccourci globales pour Irix, ultrix et solaris, car il était facile de le faire sur mon ordinateur Acorn BBC. Finalement, nous avons décidé de résoudre ce problème d'une manière non portable à un niveau inférieur à xlib avec un code propriétaire. Comme l'installation de notre logiciel nécessitait de toute façon des privilèges de superutilisateur, nous avons pu insérer les hooks logiciels appropriés en tant que démons.

Pour Linux (de nos jours), vous devriez probablement chercher une solution logicielle en traitant l'événement clavier au niveau de l'OS. Je commencerais par jeter un coup d'oeil ici : http://code.google.com/p/logkeys/

Une solution plus générique serait d'avoir une petite carte PC avec une entrée et une sortie USB, qui agit sur l'ordinateur comme une souris et un clavier et traduit les touches du clavier selon les besoins. Mais cette solution n'est pas très flexible si vous souhaitez modifier souvent le mappage.

12voto

diyism Points 1191

Mon code actuel (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()

Lorsque j'appuie sur la touche "shift", le focus est perdu, et lorsque je la relâche, le focus revient.

6voto

diyism Points 1191

Enfin, comme vous le savez, linux est synonyme de liberté, j'ai modifié xserver pour me débarrasser du grab-style focusout :

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

Et j'ai aussi besoin de me débarrasser de XGrabKeyboard dans le menu contextuel 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

Maintenant, myboard.py fonctionne bien.

Si vous utilisez l'édition ubuntu raring-updates, vous pouvez essayer :

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

et :

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

5voto

hd1 Points 11522

Il semblerait que XQueryKeymap puisse vous aider. Voir ci-dessous pour Code source C++ que j'ai trouvé :

/* 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);
}

Notez que ce n'est pas un code testé, ni le mien - je l'ai simplement trouvé sur Internet.

3voto

Adam D. Ruppe Points 10493

J'ai une idée dont je suis presque sûr qu'elle fonctionnerait, mais je dois aller me coucher et je ne peux pas la tester moi-même, et elle n'est pas très jolie, car je ne pense pas qu'il y ait un moyen de faire ce que vous voulez dans X. Voici les étapes que j'ai en tête. En bref : désactiver le clavier dans X, lire les événements de l'api de niveau inférieur, et les alimenter sélectivement à X vous-même. Vous devez désactiver le clavier dans X car sinon, vous pourriez regarder l'événement, mais pas l'arrêter ; vous le liriez en même temps que X, sans l'intercepter.

Alors voilà, c'est réparti :

1) Exécuter xinput -list pour obtenir le clavier utilisé par X

2) Courir xinput list-props id pour trouver la propriété Device Enabled

3) Courir xinput set-prop id prop 0 pour désactiver le dispositif dans 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

Je ne sais pas comment xinput fonctionne au niveau de la xlib, je l'appellerais simplement le shell pour la simplicité de l'implémentation.

4) Ouvrir /dev/input/eventX où X est le périphérique du clavier. En fait, je rechercherais le nom (donné dans xinput -list) sous /dev/input/by-id et je l'ouvrirais de cette façon. Cela nécessitera probablement Root à un moment donné, puisque les autorisations sur ces derniers sont généralement assez restrictives.

5) Lire l'entrée du clavier à partir de là :

Le format des données des événements d'entrée est le suivant :

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
}

Les ints sont de 32 bits, les ushorts de 16 bits. Puisque vous n'êtes intéressé que par la saisie au clavier, vous pouvez faire cela assez simplement :

  • lire et ignorer 8 octets.

  • l'octet suivant doit être 1, puis le suivant est 0. Si ce n'est pas le cas, ignorez cet événement.

  • L'octet suivant est le petit bout du code de la clé, et comme il y a < 255 clés, c'est suffisant donc

  • sauter l'octet suivant.

  • lire l'octet suivant pour voir si le bouton est enfoncé ou relâché.

  • sauter les trois octets suivants

6) Lorsque vous recevez un événement que vous souhaitez piéger, occupez-vous-en vous-même. Sinon, utilisez XSendEvent pour l'envoyer à X afin qu'il puisse être traité normalement. Mapper le code matériel que vous obtenez de /dev/input vers le keysym approprié peut être une astuce, mais je suis presque certain qu'il y a une fonction dans xlib quelque part pour aider avec ça.

7) goto 5 et boucle jusqu'à ce que vous ayez terminé

8) Assurez-vous de tout remettre en place comme avant la sortie, ou vous pourriez casser l'entrée du clavier de l'utilisateur à X !

Je vous suggère de tester cela avec un second clavier usb, vous pouvez désactiver et écouter les claviers indépendamment les uns des autres avec /dev/input et xinput, donc si vous vous plantez, vous avez toujours le premier clavier en place et qui fonctionne normalement. (En fait, je pense que ce serait plutôt cool de le faire intentionnellement avec un deuxième clavier, doubler les hotkeys).

Mais oui, avoir besoin de Root et potentiellement laisser le clavier "détaché" de X n'est pas joli du tout, et que le forwarding-with-SendKey pourrait être plus facile à dire qu'à faire, mais je suis presque sûr que ceci serait et vous donner un maximum de flexibilité.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X