在 X11 中侦听键盘事件而不使用它们 - 键盘挂钩
Listening to keyboard events without consuming them in X11 - Keyboard hooking
我试图编写一个程序,在 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, ¤t_focus_window, ¤t_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, ¤t_focus_window, ¤t_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);
}
该程序仅适用于可以扩展到其他键的键 a。
但是当这个程序运行时,一些程序(例如Chromium)不会在其编辑框中显示闪光灯(光标)。此外,所有 KDE 热键都会被禁用。
如何解决这个问题?
快速而肮脏的例子
#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 %dn", (int)curFocus);
if (curFocus != root)
XSelectInput(d, curFocus, 0);
XGetInputFocus (d, &curFocus, &revert);
printf ("New focus is %dn", (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: %sn", buf);
}
else
{
printf ("Key is: %dn", (int)ks);
}
}
}
}
它不可靠,但大多数时候它都有效。(它显示我现在正在此框中键入的键)。您可以调查为什么它有时会失败;)它原则上也不能显示热键。热键是抓取的密钥,只有一个客户端可以获取抓取的密钥。除了加载为此目的设计的特殊 X11 扩展(例如 XEvIE)之外,这里绝对无能为力。
感谢 n.m. 的回答和 parsa 的评论,这是我的最终代码:
#include <X11/Xlib.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
void* TaskCode(void* parg)
{
int keycode = *((int*)parg);
cout<< "nn" << keycode << "nn";
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 << "nn" "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 << "nn";
return 0;
}
int main()
{
Display* display = XOpenDisplay(0);
Window root = DefaultRootWindow(display);
Window current_focus_window;
int revert;
XSetErrorHandler(MyX11ErrorHandler);
XGetInputFocus(display, ¤t_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, ¤t_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 Creator的项目.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<< "nn" << keycode << "nn";
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, ¤t_focus_window, ¤t_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
任何语言布局的无级
作为侦听 X 事件的替代方法,也可以直接侦听 Linux 输入事件: https://stackoverflow.com/a/27693340/21501
这样做的好处是可以修改正在进行的事件流,并阻止、编辑或生成输入事件。
收听所有事件的正确方法是使用 X 记录扩展库,这是 libXtst
的一部分,显然几乎安装在每个 X 系统上。此处记录了它,但由于文档不完整,因此您需要浏览以前的实现。这是一个不错的工作演示,这是一个更强大和完整的实现。
下面包括第一个示例的简化版本。
#include <stdio.h>
#include <X11/XKBlib.h>
#include <X11/extensions/record.h>
void key_pressed_cb(XPointer arg, XRecordInterceptData *d);
int scan(int verbose) {
XRecordRange* rr;
XRecordClientSpec rcs;
XRecordContext rc;
Display *dpy = XOpenDisplay(NULL);
rr = XRecordAllocRange();
rr->device_events.first = KeyPress;
rr->device_events.last = ButtonReleaseMask;
rcs = XRecordAllClients;
rc = XRecordCreateContext (dpy, 0, &rcs, 1, &rr, 1);
XFree (rr);
XRecordEnableContext(dpy, rc, key_pressed_cb, NULL);
}
void key_pressed_cb(XPointer arg, XRecordInterceptData *d) {
if (d->category != XRecordFromServer)
return;
int key = ((unsigned char*) d->data)[1];
int type = ((unsigned char*) d->data)[0] & 0x7F;
int repeat = d->data[2] & 1;
if(!repeat) {
switch (type) {
case KeyPress:
printf("key press %dn", key);
break;
case KeyRelease:
printf("key release %dn", key);
break;
case ButtonPress:
printf("button press %dn", key);
break;
case ButtonRelease:
printf("button release %dn", key);
break;
default:
break;
}
}
XRecordFreeData (d);
}
int main() {
scan(True);
return 0;
}
gcc -o x1 x1.c -lX11 -lXtst
- 物理键盘上的触发器按键
- 如何使用C/C++在MacOSX中获得键盘布局
- 视窗键盘输入 c++
- 为什么 KMS drmModeSetCrtc() 在 X11 会话中运行时会失败并被拒绝权限?
- 将鼠标和键盘输入发送到 unity3d 游戏 (Rust)
- C ++异步键盘输入(标准方式)
- 键盘不起作用 - Arduino Uno - 与变量有关的多个错误
- 如何在 WindowProc 处理程序中区分箭头键和数字键盘?
- 覆盖应用程序的低级别键盘挂钩问题
- C++键盘挂钩 CTRL 键卡住
- 在 OpenGL 中使用键盘移动 3D 形状,C++
- SFML 键盘条件或
- 找不到键盘,即使我包括键盘。
- 在C++中隐藏键盘记录器的控制台窗口
- 盖奇不读取键盘输入
- 使用Boost async_read和POSIX::stream_descriptor从键盘读取
- 在 Linux 上C++:在作为 systemd 服务运行时侦听键盘输入
- 是否可以将键盘输入绑定到 Win32 中的单个应用程序?
- Linux X11-全局键盘挂钩
- 在 X11 中侦听键盘事件而不使用它们 - 键盘挂钩