是否有可能(或如何)创建一个与Windows中的全局钩子(SetWindowsHookEx())相似的机制(在Linux X11,C ++中)?
我希望能够抓住关键事件,但可能进一步传播。我正在尝试使用XGrabKey解决方案(例如在xbindkeys中)但是当我设置捕获关键事件时,此事件将被“消耗”。
此机制的要求如下:
示例代码如下所示:
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上实现它
答案 0 :(得分:3)
XSendEvent()
可能会发送它;但由于它被广泛认为是一个安全漏洞,大多数程序会忽略设置了send_event
标志的UI事件。
标准X11协议不允许这样做。 XInput 2.0扩展可能,但我对此表示怀疑; Windows假定每个程序都监听一个事件队列,以便程序可以拦截事件并阻止它从队列中发送到其他侦听器,每个X11客户端都有自己独立的队列,所有客户端都注册了一个事件在队列中收到它的独立副本。这意味着在正常情况下,错误的程序不可能阻止其他程序运行;但这也意味着,对于客户端必须阻止其他客户端的那些时间,它必须执行服务器抓取以防止服务器处理任何其他客户端的事件。
答案 1 :(得分:3)
使用XTest extension library中的XTestFakeKeyEvent()传播假按键/释放事件。
答案 2 :(得分:2)
我建议在输入设备级别上执行此操作,而不是在X11级别上执行此操作。 /dev/input/event<n>
为您提供输入事件。您可以读取那里的按键并确定它们是否应该进一步传播或被消耗。不幸的是,没有真正的文档,但头文件linux/include/input.h
是非常自我解释的。 evdev维护者也很乐意回复电子邮件。
答案 3 :(得分:2)
尝试从此页面编译简单代码:
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;
}
答案 4 :(得分:1)
不知道这是否有帮助,但我在一些代码中发现了这一点:
void XFakeKeypress(Display *display, int keysym)
{
XKeyEvent event;
Window current_focus_window;
int current_focus_revert;
XGetInputFocus(/* display = */ display, /* focus_return = */
¤t_focus_window, /* revert_to_return = */ ¤t_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));
}