2014-03-30 16 views
6

Ich habe versucht, ein Programm zu schreiben, das Tastaturmeldungen hakt, um den Namen jedes Schlüssels auszusprechen, wenn er in Ubuntu (KDE) gedrückt wird; ohne die normale Aktion der Tastatur in Programmen zu stören (nur den Schlüsselnamen ankündigend).Tastaturereignisse anhören, ohne sie in X11 zu konsumieren - Tastatur-Hooking

Das ist mein Programm:

#include <X11/Xlib.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <iostream> 

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

Dieses Programm ist nur für den Schlüssel einer, die auf andere Tasten erweitert werden kann.

Aber wenn dieses Programm ausgeführt wird, zeigen einige Programme (z. B. Chromium) nicht den Blinker (Cursor) in ihren Bearbeitungsfeldern. Außerdem werden alle KDE-Hotkeys deaktiviert.

Wie kann das behoben werden?

+1

holen Sie sich Hilfe von 'Pthread' Kerl. –

+0

(1) Erfahren Sie mehr über Threads; (2) Wer sagte, dass event.xkey.keycode der ASCII-Code sein musste? – EJP

+0

@parsaporahmad: danke. Ich werde meine Frage bearbeiten. – user2029077

Antwort

5

Hier ist mein quick and dirty Beispiel

#include <X11/X.h> 
#include <X11/Xlib.h> 
#include <X11/Xutil.h> 
#include <stdio.h> 
#include <ctype.h> 


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 = XLookupString(&ev.xkey, buf, 16, &ks, &comp); 
       if (len > 0 && isprint(buf[0])) 
       { 
        buf[len]=0; 
        printf("String is: %s\n", buf); 
       } 
       else 
       { 
        printf ("Key is: %d\n", (int)ks); 
       } 
     } 

    } 
} 

Es ist nicht zuverlässig, aber die meiste Zeit es funktioniert. (Es zeigt Schlüssel, die ich gerade in diese Box tippe). Sie können untersuchen, warum es manchmal fehlschlägt;) Es kann auch keine Hotkeys im Prinzip zeigen. Hotkeys sind Schlüssel, und nur ein Client kann einen gegriffenen Schlüssel erhalten. Absolut nichts kann hier getan werden, kurz bevor eine spezielle X11-Erweiterung geladen wird, die für diesen Zweck entworfen wurde (z.B. XEvIE).

+1

Scheint perfekt.Allerdings konnte ich das Problem mit 'XGrabKey' lösen, aber es gab ein neues Problem damit. Ich darf darüber in einer anderen Frage fragen. Übrigens, ich werde deine Antwort akzeptieren, sobald ich meinen Code mit deinem Code vervollständigen konnte. Vielen Dank. – user2029077

+1

Ihr Code ist in Ubuntu 13.10 KDE-Umgebung (noch) nicht fehlgeschlagen. Wie für Hotkeys, jetzt brauche ich sie nicht für 'espeak', aber mein zweiter Code in der Antwort, die ich hinzugefügt habe, kann Hotkeys verwalten, wenn nötig! – user2029077

4

Dank n.m. 's answer und parsa' s Kommentar, das ist meine letzte Code:

#include <X11/Xlib.h> 
#include <stdlib.h> 
#include <iostream> 

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; 
     } 
    } 
} 

diese summieren sich zu einem Qt Creator Projekt .proDatei:

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

Verbesserungsvorschläge wird geschätzt. außer dass

#include <X11/Xlib.h> 
#include <X11/Xutil.h> 

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <iostream> 

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

Alles ist gut, im Gegensatz zu dem fraglichen Code, es Sprache Layout ignoriert:

ich meinen letzten Code hinzufügen, auch mit greift zu archivieren. Drücken ein Typen a was immer wieder von Sprachlayout!

2

Als Alternative zu X Ereignissen zu hören, ist es auch möglich, Linux Eingabeereignisse direkt zu hören: https://stackoverflow.com/a/27693340/21501

Dies hat den Vorteil, dass es möglich ist, den Ereignisstrom im Flug zu ändern und zu blockieren, zu bearbeiten oder Generierung von Eingabeereignissen

+1

Danke Vladimir meinst du die Lösungen von @InnocentBystander? http://stackoverflow.com/a/27693340/1828637 – Noitidart