Linux X11 – 全局键盘钩子

是否可以(或如何)创build一个在Windows(SetWindowsHookEx())中像全局钩子一样工作的机制(在Linux X11,C ++中)?

我希望能够抓住关键事件,但有可能进一步传播 。 我试图使用XGrabKey解决scheme(就像在xbindkeys中 ),但是当我设置捕获关键事件,这个事件是“消耗”。

这个机制的要求如下:

  1. 全球/全系统 – 捕捉事件,不pipe重点窗口如何
  2. “抓住”和“通过”的可能性
  3. 它一定很快

示例代码如下所示:

bool myFlagIsSet = false; XEvent event; while (true) { while (XPending(display) > 0) { usleep(SLEEP_TIME); } XNextEvent(display, &event); switch (e.type) { case KeyPress: if (myFlagIsSet) { //do not propagate } // propagate break; case KeyRelease: if (myFlagIsSet) { //do not propagate } // propagate break; } } 

在Windows上我只是写道:

 if(event.isConsumed()) { return LRESULT(1); } //... return CallNextHookEx(hookHandle, nCode, wParam, lParam); 

我也尝试使用XUngrabKey和XSendEvent:

 switch (event.type) { case KeyPress: if (myFlagIsSet) { //do not propagate } // propagate XUngrabKey(...); XSendEvent(..., &event); XGrabKey(...); break; case KeyRelease: ... } 

不幸的是XSendEvent对于我来说不明的原因 – 即使XGrabKey行被评论,也不要发送这个事件。

是否有可能成功完成这个方法?

如果我被谴责失败,请提出一些其他的方法

编辑

我想使用Compiz Window Manager在Ubuntu GNOME上实现这个function

Solutions Collecting From Web of "Linux X11 – 全局键盘钩子"

XSendEvent()可能会发送它; 但是由于它被广泛认为是一个安全漏洞,大多数程序都会忽略使用send_event标志设置的UI事件。

标准的X11协议不允许这样做。 XInput 2.0扩展可能,但我怀疑它; 而Windows假定每个程序都监听一个单独的事件队列,以便程序可以拦截一个事件并阻止它被发送到其他监听器的队列中,每个X11客户端都有自己的独立队列,并且所有客户端都感兴趣事件在队列中接收它的独立副本。 这意味着在正常情况下,错误的程序不可能阻止其他程序运行; 但是这也意味着,当客户端必须阻止其他客户端时,它必须执行服务器抓取以防止服务器为其他客户端处理事件。

使用XTest 扩展库中的 XTestFakeKeyEvent()来传播伪造的按键/释放事件。

我不建议在输入设备级别上执行此操作。 /dev/input/event<n>给你输入事件。 你可以读取那里的按键,并决定是否应该进一步传播或消耗。 不幸的是没有真正的文档,但是头文件linux/include/input.h是很自我解释的。 evdev维护者也会很乐意回复邮件。

不知道这是否有帮助,但我只是在一些代码中找到了这个:

 void XFakeKeypress(Display *display, int keysym) { XKeyEvent event; Window current_focus_window; int current_focus_revert; XGetInputFocus(/* display = */ display, /* focus_return = */ &current_focus_window, /* revert_to_return = */ &current_focus_revert); event.type = /* (const) */ KeyPress; event.display = display; event.window = current_focus_window; event.root = DefaultRootWindow(/* display = */ display); event.subwindow = /* (const) */ None; event.time = 1000 * time(/* tloc = */ NULL); event.x = 0; event.y = 0; event.x_root = 0; event.y_root = 0; event.state = /* (const) */ ShiftMask; event.keycode = XKeysymToKeycode(/* display = */ display, /* keysym = */ keysym); event.same_screen = /* (const) */ True; XSendEvent(/* display = */ display, /* w = (const) */ InputFocus, /* propagate = (const) */ True, /* event_mask = (const) */ KeyPressMask, /* event_send = */ (XEvent *)(&event)); event.type = /* (const) */ KeyRelease; event.time = 1000 * time(/* tloc = */ NULL); XSendEvent(/* display = */ display, /* w = (const) */ InputFocus, /* propagate = (const) */ True, /* event_mask = (const) */ KeyReleaseMask, /* event_send = */ (XEvent *)(&event)); } 

试试从这个页面编译简单的代码:

http://webhamster.ru/site/page/index/articles/comp/367

这是获取全局键盘事件的样本。 这个小应用程序作为xinput工作。

备注1:将设备ID写入mian.cpp(通过不带参数运行xinput来获取ID):

 sprintf(deviceId, "9"); 

备注2:编译命令:

 gcc ./main.cpp -lstdc++ -lX11 -lXext -lXi 

Remakr 3:在编译之前,安装libxi-dev包:

 apt-get install libxi-dev 

文件main.h

 #include <X11/Xlib.h> #include <X11/extensions/XInput.h> #ifdef HAVE_XI2 #include <X11/extensions/XInput2.h> #endif #include <X11/Xutil.h> #include <stdio.h> #include <stdlib.h> extern int xi_opcode; /* xinput extension op code */ XDeviceInfo* find_device_info( Display *display, char *name, Bool only_extended); #if HAVE_XI2 XIDeviceInfo* xi2_find_device_info(Display *display, char *name); int xinput_version(Display* display); #endif 

文件main.cpp

 #include <iostream> #include <cstdio> #include <cstdlib> #include <X11/Xlib.h> #include <X11/Xutil.h> #include "main.h" #include <ctype.h> #include <string.h> using namespace std; int xi_opcode; #define INVALID_EVENT_TYPE -1 static int motion_type = INVALID_EVENT_TYPE; static int button_press_type = INVALID_EVENT_TYPE; static int button_release_type = INVALID_EVENT_TYPE; static int key_press_type = INVALID_EVENT_TYPE; static int key_release_type = INVALID_EVENT_TYPE; static int proximity_in_type = INVALID_EVENT_TYPE; static int proximity_out_type = INVALID_EVENT_TYPE; static int register_events(Display *dpy, XDeviceInfo *info, char *dev_name, Bool handle_proximity) { int number = 0; /* number of events registered */ XEventClass event_list[7]; int i; XDevice *device; Window root_win; unsigned long screen; XInputClassInfo *ip; screen = DefaultScreen(dpy); root_win = RootWindow(dpy, screen); device = XOpenDevice(dpy, info->id); if (!device) { printf("unable to open device '%s'\n", dev_name); return 0; } if (device->num_classes > 0) { for (ip = device->classes, i=0; i<info->num_classes; ip++, i++) { switch (ip->input_class) { case KeyClass: DeviceKeyPress(device, key_press_type, event_list[number]); number++; DeviceKeyRelease(device, key_release_type, event_list[number]); number++; break; case ButtonClass: DeviceButtonPress(device, button_press_type, event_list[number]); number++; DeviceButtonRelease(device, button_release_type, event_list[number]); number++; break; case ValuatorClass: DeviceMotionNotify(device, motion_type, event_list[number]); number++; if (handle_proximity) { ProximityIn(device, proximity_in_type, event_list[number]); number++; ProximityOut(device, proximity_out_type, event_list[number]); number++; } break; default: printf("unknown class\n"); break; } } if (XSelectExtensionEvent(dpy, root_win, event_list, number)) { printf("error selecting extended events\n"); return 0; } } return number; } static void print_events(Display *dpy) { XEvent Event; setvbuf(stdout, NULL, _IOLBF, 0); while(1) { XNextEvent(dpy, &Event); if (Event.type == motion_type) { int loop; XDeviceMotionEvent *motion = (XDeviceMotionEvent *) &Event; printf("motion "); for(loop=0; loop<motion->axes_count; loop++) { printf("a[%d]=%d ", motion->first_axis + loop, motion->axis_data[loop]); } printf("\n"); } else if ((Event.type == button_press_type) || (Event.type == button_release_type)) { int loop; XDeviceButtonEvent *button = (XDeviceButtonEvent *) &Event; printf("button %s %d ", (Event.type == button_release_type) ? "release" : "press ", button->button); for(loop=0; loop<button->axes_count; loop++) { printf("a[%d]=%d ", button->first_axis + loop, button->axis_data[loop]); } printf("\n"); } else if ((Event.type == key_press_type) || (Event.type == key_release_type)) { int loop; XDeviceKeyEvent *key = (XDeviceKeyEvent *) &Event; printf("key %s %d ", (Event.type == key_release_type) ? "release" : "press ", key->keycode); for(loop=0; loop<key->axes_count; loop++) { printf("a[%d]=%d ", key->first_axis + loop, key->axis_data[loop]); } printf("\n"); } else if ((Event.type == proximity_out_type) || (Event.type == proximity_in_type)) { int loop; XProximityNotifyEvent *prox = (XProximityNotifyEvent *) &Event; printf("proximity %s ", (Event.type == proximity_in_type) ? "in " : "out"); for(loop=0; loop<prox->axes_count; loop++) { printf("a[%d]=%d ", prox->first_axis + loop, prox->axis_data[loop]); } printf("\n"); } else { printf("what's that %d\n", Event.type); } } } // Определение версии библиотеки расширений, установленной для X11 int xinput_version(Display *display) { XExtensionVersion *version; static int vers = -1; if (vers != -1) return vers; version = XGetExtensionVersion(display, INAME); if (version && (version != (XExtensionVersion*) NoSuchExtension)) { vers = version->major_version; XFree(version); } #if HAVE_XI2 /* Announce our supported version so the server treats us correctly. */ if (vers >= XI_2_Major) { const char *forced_version; int maj = 2, min = 0; #if HAVE_XI22 min = 2; #elif HAVE_XI21 min = 1; #endif forced_version = getenv("XINPUT_XI2_VERSION"); if (forced_version) { if (sscanf(forced_version, "%d.%d", &maj, &min) != 2) { fprintf(stderr, "Invalid format of XINPUT_XI2_VERSION " "environment variable. Need major.minor\n"); exit(1); } printf("Overriding XI2 version to: %d.%d\n", maj, min); } XIQueryVersion(display, &maj, &min); } #endif return vers; } // Поиск информации об устройстве XDeviceInfo* find_device_info(Display *display, char *name, Bool only_extended) { XDeviceInfo *devices; XDeviceInfo *found = NULL; int loop; int num_devices; int len = strlen(name); Bool is_id = True; XID id = (XID)-1; for(loop=0; loop<len; loop++) { if (!isdigit(name[loop])) { is_id = False; break; } } if (is_id) { id = atoi(name); } devices = XListInputDevices(display, &num_devices); for(loop=0; loop<num_devices; loop++) { if ((!only_extended || (devices[loop].use >= IsXExtensionDevice)) && ((!is_id && strcmp(devices[loop].name, name) == 0) || (is_id && devices[loop].id == id))) { if (found) { fprintf(stderr, "Warning: There are multiple devices named '%s'.\n" "To ensure the correct one is selected, please use " "the device ID instead.\n\n", name); return NULL; } else { found = &devices[loop]; } } } return found; } int test(Display *display, char *deviceId) { XDeviceInfo *info; Bool handle_proximity = True; info = find_device_info(display, deviceId, True); if(!info) { printf("unable to find device '%s'\n", deviceId); exit(1); } else { if(register_events(display, info, deviceId, handle_proximity)) print_events(display); else { fprintf(stderr, "no event registered...\n"); exit(1); } } return 0; } int main() { Display *display; int event, error; // Инициируется указатель на текущий дисплей display = XOpenDisplay(NULL); if (display == NULL) { printf("Unable to connect to X server\n"); exit(1); } // Проверяется наличие расширений if(!XQueryExtension(display, "XInputExtension", &xi_opcode, &event, &error)) { printf("X Input extension not available.\n"); exit(1); } // Проверяется версия расширения, она не должна быть нулевой if(!xinput_version(display)) { printf("%s extension not available\n", INAME); exit(1); } char deviceId[10]; sprintf(deviceId, "9"); test(display, deviceId); XSync(display, False); XCloseDisplay(display); return 0; }