2014-03-30 45 views
6

我試圖編寫一個程序,掛鉤鍵盤消息發音每個鍵的名稱,每當它在Ubuntu(KDE)中按下;而不會干擾程序中鍵盤的正常動作(只是宣佈鍵名)。聽取鍵盤事件,而不消耗他們在X11 - 鍵盤掛鉤

這是我的計劃:

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

這個程序是隻爲關鍵一個它可以擴展到其他按鍵。

但是,當該程序正在運行時,某些程序(例如Chromium)不會在其編輯框中顯示閃爍指示器(光標)。所有的KDE熱鍵都會被禁用。

這怎麼解決?

+1

得到'pthread'夥計的幫助。 –

+0

(1)瞭解線程; (2)誰說event.xkey.keycode必須是ASCII碼? – EJP

+0

@parsaporahmad:謝謝。我會編輯我的問題。 – user2029077

回答

5

這裏是我的快速和骯髒的例子

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

    } 
} 

這是不可靠的,但大部分作品的時間。 (它正在顯示我現在正在輸入此框中的鍵)。你可以調查它爲什麼有時會失敗;)它也不能顯示熱鍵原則上。熱鍵是抓取的鍵,只有一個客戶端可以獲得抓取的鍵。在這裏完全沒有任何東西可以完成,只需加載專門用於此目的的特殊X11擴展(例如XEvIE)即可。

+1

看起來很完美。但是我可以用'XGrabKey'來解決這個問題,但是有一個新問題。我可能會在另一個問題上提出這個問題。順便說一句,我會接受你的答案,一旦我可以使用你的完成我的代碼。謝謝。 – user2029077

+1

在Ubuntu 13.10 KDE環境中,我的代碼並沒有失敗。至於熱鍵,現在我不需要'espeak',但我添加的答案中的第二個代碼可以根據需要管理熱鍵! – user2029077

4

由於n.m.answerparsa的評論,這是我的最終代碼:

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

這些添加到Qt創建者的項目.pro文件:

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

任何改進建議非常感謝。

要存檔我也抓住加我的最終代碼:

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

一切都只是好,不像有問題的代碼,它忽略了語言的佈局。按a類型a任何regregless的語言佈局!

2

作爲替代傾聽X事件,它也可以聽直接Linux的輸入事件:https://stackoverflow.com/a/27693340/21501

這樣做,它可能改變飛行事件流的效益,塊,編輯或生成輸入事件。

+1

感謝弗拉基米爾你的意思是@InnocentBystander的解決方案? http://stackoverflow.com/a/27693340/1828637 – Noitidart