Прослушивание событий клавиатуры без их использования в X11 — перехват клавиатуры

Я пытался написать программу, которая перехватывает сообщения клавиатуры, чтобы произносить имя каждой клавиши при каждом нажатии в Ubuntu (KDE); не мешая нормальному действию клавиатуры в программах (просто объявляя имя клавиши).

Это моя программа:

#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 отключаются.

Как это можно исправить?

6

Решение

Вот мой быстрый и грязный пример

#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).

6

Другие решения

Благодаря Н.М.«s ответ а также ПарсаКомментарий, это мой окончательный код:

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

Добавьте их в файл .pro проекта Qt Creator:

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 какой бы ни был языковой макет!

5

В качестве альтернативы прослушиванию событий X можно также напрямую прослушивать входные события Linux: https://stackoverflow.com/a/27693340/21501

Преимущество заключается в том, что можно изменять поток событий в полете, а также блокировать, редактировать или генерировать входные события.

2
По вопросам рекламы [email protected]