Is it possible (or how) to create a mechanism (in Linux X11, C++) that works like a global hook in windows (SetWindowsHookEx())?
I would like to be able to catch the key
no idea if this helps, but I just found this in some code:
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));
}
XSendEvent()
probably does send it; but as it's widely regarded as a security hole, most programs ignore UI events with the send_event
flag set.
The standard X11 protocol doesn't allow this. The XInput 2.0 extension might, but I doubt it; while Windows assumes a single event queue that every program listens to, so that a program can intercept an event and prevent it from being sent down the queue to other listeners, every X11 client has its own independent queue and all clients that register interest in an event receive an independent copy of it in their queue. This means that under normal circumstances it's impossible for an errant program to block other programs from running; but it also means that, for those times when a client must block other clients, it must do a server grab to prevent the server from processing events for any other client.
Instead of doing this on the X11 level I recommend to do it on the input device level. /dev/input/event<n>
give you the input events. You can read off the keypresses there and decide if they should propagate further or be consumed. Unfortunately there's no real documentation for this, but the header file linux/include/input.h
is quite self explanatory. Also the evdev maintainer will gladly answer emails.
Use XTestFakeKeyEvent() from the XTest extension library to propagate fake key press / release events.
Try compile easy code from this page:
http://webhamster.ru/site/page/index/articles/comp/367
It's sample of get global keyboard event. This small app working as xinput.
Remark 1: Write device ID to mian.cpp (get ID by running xinput without parameters):
sprintf(deviceId, "9");
Remark 2: Compile command:
gcc ./main.cpp -lstdc++ -lX11 -lXext -lXi
Remakr 3: Before compile, install libxi-dev package:
apt-get install libxi-dev
File 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
File 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;
}