Écouter les événements du clavier sans les consumr dans X11 – Accrochage au clavier

J’ai essayé d’écrire un programme qui accroche les messages du clavier pour prononcer le nom de chaque touche chaque fois qu’il est pressé dans Ubuntu (KDE); sans interférer avec l’action normale du clavier dans les programmes (annoncer simplement le nom de la clé).

Ceci est mon programme:

#include  #include  #include  #include  #include  using namespace std; void SendPressKeyEvent(Display *display, XKeyEvent xkey) { Window current_focus_window; int current_focus_revert; XGetInputFocus(display, &current_focus_window, &current_focus_revert); xkey.type = KeyPress; xkey.display = display; xkey.window = current_focus_window; xkey.root = DefaultRootWindow(display); xkey.subwindow = None; xkey.time = 1000 * time(0); xkey.x = 0; xkey.y = 0; xkey.x_root = 0; xkey.y_root = 0; xkey.same_screen = True; XSendEvent(display, InputFocus, True, KeyPressMask, (XEvent *)(&xkey)); } void SendReleaseKeyEvent(Display *display, XKeyEvent xkey) { Window current_focus_window; int current_focus_revert; XGetInputFocus(display, &current_focus_window, &current_focus_revert); xkey.type = KeyRelease; xkey.display = display; xkey.window = current_focus_window; xkey.root = DefaultRootWindow(display); xkey.subwindow = None; xkey.time = 1000 * time(0); xkey.x = 0; xkey.y = 0; xkey.x_root = 0; xkey.y_root = 0; xkey.same_screen = True; XSendEvent(display, InputFocus, True, KeyReleaseMask, (XEvent *)(&xkey)); } void *TaskCode(void* arg) { switch(*(int*)arg) { case 38: system("espeak -v en " "\"a\""); } return 0; } int main() { Display *display = XOpenDisplay(0); if(display == 0) exit(1); XGrabKeyboard(display, DefaultRootWindow(display), True, GrabModeAsync, GrabModeAsync, CurrentTime); XEvent event; while(true) { XNextEvent(display, &event); if(event.type == Expose) { } if(event.type == KeyPress) { SendPressKeyEvent(display,event.xkey); if(event.xkey.keycode == 38) { pthread_t thread; int thread_arg = event.xkey.keycode; pthread_create(&thread,0, TaskCode, (void*) &thread_arg); } } if(event.type == KeyRelease) SendReleaseKeyEvent(display,event.xkey); } XCloseDisplay(display); } 

Ce programme est juste pour la clé a qui peut être étendue à d’autres touches.

Mais lorsque ce programme est en cours d’exécution, certains programmes (par exemple, Chrome) ne montrent pas le clignotant (curseur) dans leurs zones d’édition. De plus, tous les raccourcis clavier de KDE sont désactivés.

Comment cela peut-il être corrigé?

Voici mon exemple rapide et sale

 #include  #include  #include  #include  #include  int main () { Display* d = XOpenDisplay(NULL); Window root = DefaultRootWindow(d); Window curFocus; char buf[17]; KeySym ks; XComposeStatus comp; int len; int revert; XGetInputFocus (d, &curFocus, &revert); XSelectInput(d, curFocus, KeyPressMask|KeyReleaseMask|FocusChangeMask); while (1) { XEvent ev; XNextEvent(d, &ev); switch (ev.type) { case FocusOut: printf ("Focus changed!\n"); printf ("Old focus is %d\n", (int)curFocus); if (curFocus != root) XSelectInput(d, curFocus, 0); XGetInputFocus (d, &curFocus, &revert); printf ("New focus is %d\n", (int)curFocus); if (curFocus == PointerRoot) curFocus = root; XSelectInput(d, curFocus, KeyPressMask|KeyReleaseMask|FocusChangeMask); break; case KeyPress: printf ("Got key!\n"); len = XLookupSsortingng(&ev.xkey, buf, 16, &ks, &comp); if (len > 0 && isprint(buf[0])) { buf[len]=0; printf("Ssortingng is: %s\n", buf); } else { printf ("Key is: %d\n", (int)ks); } } } } 

Ce n’est pas fiable mais la plupart du temps ça marche. (Il montre les clés que je tape dans cette boîte maintenant). Vous pouvez rechercher pourquoi il échoue parfois;) En outre, il ne peut pas afficher les raccourcis en principe. Les raccourcis sont des clés saisies et un seul client peut obtenir une clé saisie. Absolument rien ne peut être fait ici, à moins de charger une extension X11 spéciale conçue à cet effet (par exemple, XEvIE).

Grâce à la réponse de nm et au commentaire de parsa , voici mon code final:

 #include  #include  #include  using namespace std; void* TaskCode(void* parg) { int keycode = *((int*)parg); cout<< "\n\n" << keycode << "\n\n"; if(keycode == XKeysymToKeycode(XOpenDisplay(0),'a')) system("espeak -v en " "\"a\""); delete (int*)parg; return 0; } void Action(int keycode) { pthread_t thread; pthread_attr_t attrs; pthread_attr_init(&attrs); pthread_attr_setdetachstate(&attrs,PTHREAD_CREATE_DETACHED); pthread_attr_setstacksize(&attrs, 1000); int* pthread_arg = new int; *pthread_arg = keycode; pthread_create(&thread,&attrs, TaskCode, (void*) pthread_arg); } int MyX11ErrorHandler(Display *, XErrorEvent *error_event) { cout << "\n\n" "An X11-Functions error occured. Probably the focused window was closed.""\n" "This error will be ignored." "\n"; cout<< "error_code: " << (unsigned)error_event -> error_code << "\n"; cout<< "minor_code: " << (unsigned)error_event -> minor_code << "\n"; cout<< "request_code: " << (unsigned)error_event -> request_code << "\n"; cout<< "resourceid: " << error_event -> resourceid << "\n"; cout<< "serial; " << error_event -> serial << "\n"; cout<< "type: " << error_event -> type << "\n\n"; return 0; } int main() { Display* display = XOpenDisplay(0); Window root = DefaultRootWindow(display); Window current_focus_window; int revert; XSetErrorHandler(MyX11ErrorHandler); XGetInputFocus(display, &current_focus_window, &revert); XSelectInput(display,current_focus_window,KeyPressMask | KeyReleaseMask | FocusChangeMask); while(true) { XEvent event; XNextEvent(display, &event); switch (event.type) { case FocusOut: if(current_focus_window != root) XSelectInput(display, current_focus_window, 0); XGetInputFocus(display, &current_focus_window, &revert); if(current_focus_window == PointerRoot) current_focus_window = root; XSelectInput(display, current_focus_window, KeyPressMask|KeyReleaseMask|FocusChangeMask); break; case KeyPress: Action(event.xkey.keycode); break; } } } 

Ajoutez-les au fichier .pro du projet Qt Creator:

 LIBS += -lX11 LIBS += -lpthread LIBS += -lXtst 

Toute suggestion d'amélioration est appréciée.

Pour archiver, j'ajoute également mon code final à la saisie:

 #include  #include  #include  #include  #include  #include  using namespace std; void* TaskCode(void* parg) { int keycode = *((int*)parg); cout<< "\n\n" << keycode << "\n\n"; system("espeak -v en " "\"a\""); delete (int*)parg; return 0; } void SendKeyEvent(Display *display, XEvent event) { Window current_focus_window; XKeyEvent& xkey = event.xkey; int current_focus_revert; XGetInputFocus(display, &current_focus_window, &current_focus_revert); xkey.state = Mod2Mask; XSendEvent(display, InputFocus, True, xkey.type, (XEvent *)(&event)); } int GrabKey(Display* display, Window grab_window, int keycode) { unsigned int modifiers = Mod2Mask; // numlock on //Window grab_window = DefaultRootWindow(display); Bool owner_events = True; int pointer_mode = GrabModeAsync; int keyboard_mode = GrabModeAsync; XGrabKey(display, keycode, modifiers, grab_window, owner_events, pointer_mode, keyboard_mode); return keycode; } void UngrabKey(Display* display, Window grab_window, int keycode) { unsigned int modifiers = Mod2Mask; // numlock on // Window grab_window = DefaultRootWindow(display); XUngrabKey(display,keycode,modifiers,grab_window); } void Action(int keycode) { pthread_t thread; int* pthread_arg = new int; *pthread_arg = keycode; pthread_create(&thread,0, TaskCode, (void*) pthread_arg); } int main() { Display* display = XOpenDisplay(0); Window root = DefaultRootWindow(display); XEvent event; int keycode = XKeysymToKeycode(display,'a'); GrabKey(display,root,keycode); XSelectInput(display, root, KeyPressMask | KeyReleaseMask); while(true) { XNextEvent(display, &event); switch(event.type) { case KeyPress: Action(event.xkey.keycode); case KeyRelease: SendKeyEvent(display,event); default: break; } } XCloseDisplay(display); } 

Tout est bon sauf que, contrairement au code en question, il ignore la disposition des langues. En appuyant sur un type, peu importe la disposition de la langue!

Au lieu d’écouter les événements X, il est également possible d’écouter directement les événements d’entrée Linux: https://stackoverflow.com/a/27693340/21501

Cela présente l’avantage de pouvoir modifier le stream d’événements en vol et de bloquer, modifier ou générer des événements d’entrée.