X11 sending wrong key presses - c

I am making a simple xlib wrapper for recreation, but I am having a problem with X11 since I activated WSL: At the start of some programs, it starts sending a specific key press event and when I press something it stops.
I tried looking at some documentation and even debug input files on /dev/, but seems that WSL don't have them, at least I didn't found.
My window_init():
Display* display = XOpenDisplay(NULL);
Window root = DefaultRootWindow(display);
XSetWindowAttributes swa;
swa.event_mask = ExposureMask | PointerMotionMask | KeyPressMask | KeyReleaseMask;
Window window = XCreateWindow(display, root, 0, 0, w, h, 0, CopyFromParent, InputOutput, CopyFromParent, CWEv>
XStoreName(display, window, name);
XMapWindow(display, window);
XFlush(display);
My window_update():
XEvent event;
while (XPending(display->display)) {
char k;
XNextEvent(display->display, &event);
switch (event.type) {
case KeyPress:
k = XLookupKeysym(&event.xkey, 0)-XK_a;
if(k >= 0 && k<= 26){
keys_down[k] = 1;
pressed[k] = 1;
}
break;
}
}
I still didn't handle the release events property, but at the start of the execution the program dispatches lots of keypresses events.

Here is a fully verified working example of the same:
// SPDX-License-Identifier: CC0-1.0
#include <stdlib.h>
#include <stdio.h>
#include <X11/Xlib.h>
int main(void)
{
Display *display;
Window root, window;
XSetWindowAttributes attrs;
XEvent event;
Atom close_window;
display = XOpenDisplay(NULL);
if (!display) {
fprintf(stderr, "No display.\n");
return EXIT_FAILURE;
}
root = DefaultRootWindow(display);
attrs.event_mask = ExposureMask | PointerMotionMask | KeyPressMask | KeyReleaseMask;
window = XCreateWindow(display, root,
100, 100, 320, 240,
0, CopyFromParent, InputOutput, CopyFromParent,
CWEventMask, &attrs);
XStoreName(display, window, "Example Program");
close_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, window, &close_window, 1);
XMapWindow(display, window);
XFlush(display);
while (1) {
XNextEvent(display, &event);
if (event.type == ClientMessage && (Atom)(event.xclient.data.l[0]) == close_window)
break;
switch (event.type) {
case MotionNotify:
// It's just mouse motion events, so we won't mention those.
break;
case KeyPress:
printf("KeyPress event: state = %u (", event.xkey.state);
if (event.xkey.state & ShiftMask) printf(" Shift");
if (event.xkey.state & LockMask) printf(" CapsLock");
if (event.xkey.state & ControlMask) printf(" Control");
if (event.xkey.state & Mod1Mask) printf(" Mod1");
if (event.xkey.state & Mod2Mask) printf(" Mod2");
if (event.xkey.state & Mod3Mask) printf(" Mod3");
if (event.xkey.state & Mod4Mask) printf(" Mod4");
if (event.xkey.state & Mod5Mask) printf(" Mod5");
printf(" ), keycode = %u\n", event.xkey.keycode);
break;
case KeyRelease:
printf("KeyRelease event: state = %u (", event.xkey.state);
if (event.xkey.state & ShiftMask) printf(" Shift");
if (event.xkey.state & LockMask) printf(" CapsLock");
if (event.xkey.state & ControlMask) printf(" Control");
if (event.xkey.state & Mod1Mask) printf(" Mod1");
if (event.xkey.state & Mod2Mask) printf(" Mod2");
if (event.xkey.state & Mod3Mask) printf(" Mod3");
if (event.xkey.state & Mod4Mask) printf(" Mod4");
if (event.xkey.state & Mod5Mask) printf(" Mod5");
printf(" ), keycode = %u\n", event.xkey.keycode);
break;
case Expose:
printf("Expose event: x=%d, y=%d, width=%d, height=%d, count=%d\n", event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height, event.xexpose.count);
break;
default:
printf("Event type %d\n", event.type);
}
fflush(stdout);
}
XCloseDisplay(display);
return EXIT_SUCCESS;
}
When compiling, link against the X11 library.
Since Expose is not implemented, it is normal and expected for the contents of the window to be garbage (solid color, copy of existing window data, or anything else; it varies depending on X server and window manager).
If you run the program from the command line in Linux, and you keep Enter pressed, the first KeyPress event you get is the first autorepeat event. You do sometimes get the KeyRelease event corresponding to the Enter key being released after the window has been mapped, but before any autorepeat events. The first described event is usually the Expose event,
    Expose event: x=0, y=0, width=320, height=240, count=0
occasionally followed by the KeyRelease event corresponding to the Enter key,
    KeyRelease event: state = 8192 ( ), keycode = 36
but one should ignore any KeyRelease events anyway that do not correspond to previous KeyPress event, so that shouldn't be an issue. (Shift is Shift, Lock is CapsLock (when on/enabled), Control is Ctrl, Mod1 is Alt, Mod5 AltGr, Mod4 ⊞ (Windows key).)
The close_window atom is used to handle the window close button. When the button is pressed, the window manager will send a ClientMessage event containing the same atom.
If you get any KeyPress events corresponding to keypresses that occurred prior to the start of the program, it is a bug in WSL that you need to report to Microsoft. (Only KeyRelease events corresponding to keys that are released after the program started, and autorepeat KeyPress events corresponding to keys that are being pressed down when the program starts and afterwards, are expected to occur.)
(Personally, instead of WSL, I would recommend running a real Linux installation in a virtual machine in Windows, unless you explicitly intend your programs to be run under WSL instead of actual Linux installations. There just seem to be too many bugs in WSL, in my opinion; plus one can snapshot and start and stop the virtual machine as needed. Others disagree, of course.)

Related

Console does not record mouse wheel events

I am working on a TUI program with console on Windows 10. I would like to implement widgets that allow vertical scrolling when the user interacts with the mouse wheel. However the console does not record any of the mouse wheel events.
The sample code below from Microsoft does not record mouse wheel events even though my mouse wheel is working since windows manage to scroll the console content. All other mouse input events are working (click, move...).
#include <windows.h>
#include <stdio.h>
HANDLE hStdin;
DWORD fdwSaveOldMode;
VOID ErrorExit(LPCSTR);
VOID KeyEventProc(KEY_EVENT_RECORD); // the "case MOUSE_WHEELED:" in this function never happens
VOID MouseEventProc(MOUSE_EVENT_RECORD);
VOID ResizeEventProc(WINDOW_BUFFER_SIZE_RECORD);
int main(VOID)
{
DWORD cNumRead, fdwMode, i;
INPUT_RECORD irInBuf[128];
int counter=0;
// Get the standard input handle.
hStdin = GetStdHandle(STD_INPUT_HANDLE);
if (hStdin == INVALID_HANDLE_VALUE)
ErrorExit("GetStdHandle");
// Save the current input mode, to be restored on exit.
if (! GetConsoleMode(hStdin, &fdwSaveOldMode) )
ErrorExit("GetConsoleMode");
// Enable the window and mouse input events.
fdwMode = ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT;
if (! SetConsoleMode(hStdin, fdwMode) )
ErrorExit("SetConsoleMode");
// Loop to read and handle the next 100 input events.
while (counter++ <= 100)
{
// Wait for the events.
if (! ReadConsoleInput(
hStdin, // input buffer handle
irInBuf, // buffer to read into
128, // size of read buffer
&cNumRead) ) // number of records read
ErrorExit("ReadConsoleInput");
// Dispatch the events to the appropriate handler.
for (i = 0; i < cNumRead; i++)
{
switch(irInBuf[i].EventType)
{
case KEY_EVENT: // keyboard input
KeyEventProc(irInBuf[i].Event.KeyEvent);
break;
case MOUSE_EVENT: // mouse input
MouseEventProc(irInBuf[i].Event.MouseEvent);
break;
case WINDOW_BUFFER_SIZE_EVENT: // scrn buf. resizing
ResizeEventProc( irInBuf[i].Event.WindowBufferSizeEvent );
break;
case FOCUS_EVENT: // disregard focus events
case MENU_EVENT: // disregard menu events
break;
default:
ErrorExit("Unknown event type");
break;
}
}
}
// Restore input mode on exit.
SetConsoleMode(hStdin, fdwSaveOldMode);
return 0;
}
VOID ErrorExit (LPCSTR lpszMessage)
{
fprintf(stderr, "%s\n", lpszMessage);
// Restore input mode on exit.
SetConsoleMode(hStdin, fdwSaveOldMode);
ExitProcess(0);
}
VOID KeyEventProc(KEY_EVENT_RECORD ker)
{
printf("Key event: ");
if(ker.bKeyDown)
printf("key pressed\n");
else printf("key released\n");
}
VOID MouseEventProc(MOUSE_EVENT_RECORD mer)
{
#ifndef MOUSE_HWHEELED
#define MOUSE_HWHEELED 0x0008
#endif
printf("Mouse event: ");
switch(mer.dwEventFlags)
{
case 0:
if(mer.dwButtonState == FROM_LEFT_1ST_BUTTON_PRESSED)
{
printf("left button press \n");
}
else if(mer.dwButtonState == RIGHTMOST_BUTTON_PRESSED)
{
printf("right button press \n");
}
else
{
printf("button press\n");
}
break;
case DOUBLE_CLICK:
printf("double click\n");
break;
case MOUSE_HWHEELED:
printf("horizontal mouse wheel\n");
break;
case MOUSE_MOVED:
printf("mouse moved\n");
break;
case MOUSE_WHEELED:// this case never shows up when I use the wheel
printf("vertical mouse wheel\n");
break;
default:
printf("unknown\n");
break;
}
}
VOID ResizeEventProc(WINDOW_BUFFER_SIZE_RECORD wbsr)
{
printf("Resize event\n");
printf("Console screen buffer is %d columns by %d rows.\n", wbsr.dwSize.X, wbsr.dwSize.Y);
}

Terminal in XLIB Window Manager not receiving key presses from "OnBoard" on screen keyboard

Consider the following minimal window manager found online. It compiles and runs fine.
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
Display *display;
Window window;
XEvent event;
int s;
/* open connection with the server */
display = XOpenDisplay(NULL);
if (display == NULL)
{
fprintf(stderr, "Cannot open display\n");
exit(1);
}
s = DefaultScreen(display);
/* create window */
window = XCreateSimpleWindow(display, RootWindow(display, s), 10, 10, 200, 200, 1,
BlackPixel(display, s), WhitePixel(display, s));
/* select kind of events we are interested in */
XSelectInput(display, window, KeyPressMask | KeyReleaseMask );
/* map (show) the window */
XMapWindow(display, window);
/* event loop */
while (1)
{
XNextEvent(display, &event);
/* keyboard events */
if (event.type == KeyPress)
{
printf( "KeyPress: %x\n", event.xkey.keycode );
/* exit on ESC key press */
if ( event.xkey.keycode == 0x09 )
break;
}
else if (event.type == KeyRelease)
{
printf( "KeyRelease: %x\n", event.xkey.keycode );
}
}
/* close connection to server */
XCloseDisplay(display);
return 0;
}
In this window manager, I can load a terminal (such as xterm) and the "onboard" on screen keyboard program with ubuntu (server addition, with xinit installed). The onboard on screen keyboard is not sending key input to other windows in this minimal window manager (onboard loads on the bottom region of the screen).
Note that DWM minimalist window manager works as expected (the onboard input gets sent to all other windows). Im unable to find in the DWM source where this kind of thing is considered.
My question is this: How to I make this minialist window manager allow the Onboard on screen keyboard to send input to other windows?
Found the solution. I should have been using XSetInputFocus on the terminal, then input goes there correctly.
//e.window is the program window for the program that should get the input.
XSetInputFocus(mDisplay, e.window, RevertToPointerRoot, CurrentTime);

Recording multiple key presses in x11

I want to record simultaneous key presses and test a function in x11 using C. For example, I was able to set something like,
if 'Q' is pressed,
let the window resize.
But I could not find a method for doing the same with a key combination such as Ctrl+Enter, etc, so that when 'Ctrl+Enter' is pressed, the window resizes.
Is there any event type or mask or function in x11 for recording these simultaneous key events ?
The code below is what I have written so far for recording single keys and performing specified action.
// USES KEYBOARD KEY TO RESIZE A WINDOW
// Compile : gcc -o go key_and_win.c -lX11
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
Display *d;
Window window;
XEvent event, ev;
int s;
/* open connection with the server */
d = XOpenDisplay(NULL);
if (d == NULL)
{
fprintf(stderr, "Cannot open d\n");
exit(1);
}
s = DefaultScreen(d);
/* create window */
window = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 200, 200, 1,
BlackPixel(d, s), BlackPixel(d, s));
/* select kind of events we are interested in */
XSelectInput(d, window, StructureNotifyMask | ExposureMask | KeyPressMask | KeyReleaseMask );
/* map (show) the window */
XMapWindow(d, window);
/* event loop */
while (1)
{
XNextEvent(d, &event);
/* keyboard events */
if (event.type == KeyPress)
{
printf( "KeyPress: %x\n", event.xkey.keycode );
if(event.xkey.keycode == 0x18) // Resize on pressing Q as, key Q => 0x18
{
printf("Here in Q\n");
int r = XResizeWindow(d, window, 100, 200); // Resizing the window through Q keypress
if(r==BadValue || r==BadWindow)
printf("Error in resizing\n");
XNextEvent(d, &event); // To get ConfigureNotify event
if(event.type == ConfigureNotify)
printf("Resized!\n");
else
printf("Not resized\n");
//XMapWindow(d, window); // Map the resized window (not necessary)
}
/* exit on ESC key press */
if ( event.xkey.keycode == 0x09 )
break;
}
}
/* close connection to server */
XCloseDisplay(d);
return 0;
}
you have to look at event.xkey.state
from 10.5.2 Keyboard and Pointer Events :
The state member is set to indicate the logical state of the pointer buttons and modifier keys just prior to the event, which is the bitwise inclusive OR of one or more of the button or modifier key masks: Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask, ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, and Mod5Mask.

Capture Button Events in Xlib then passing the Event to the client

I'm working on a window manager, mainly as an exercise, and I'm facing a problem. I would like to be able to raise the clicked window to the top of the stack. Currently, I am using XGrabButton on Button1 and ControlMask to allow for moving of windows, and when I Ctrl+click the window, the desired effect is achieved. However, if I use XGrabButton on Button1 with AnyModifier, while the effect I am looking for is achieved, I can no longer interact with the client window (highlighting text, etc.) through the mouse button. I have tried Grabbing the button on EnterNotify, then ungrabbing the button as soon as the window is raised, but this appears to have no effect and the window manager acts as though I never grabbed the button at all.
My program is still relatively small, so here is the code:
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include "window_manager.h"
ewm_instance wm;
void
ewm_init()
{
wm._display = XOpenDisplay(NULL);
if (!wm._display) {
printf("Could not open display %s\n", XDisplayName(NULL));
}
wm._root = DefaultRootWindow(wm._display);
}
void
ewm_run()
{
XSelectInput(wm._display,
wm._root,
SubstructureRedirectMask | SubstructureNotifyMask |
KeyPressMask | KeyReleaseMask |
ButtonPressMask | ButtonReleaseMask);
XSync(wm._display, 0);
XGrabServer(wm._display);
Window returned_root, returned_parent;
Window *top_level_windows;
unsigned int num_top_level_windows;
XQueryTree(wm._display,
wm._root,
&returned_root,
&returned_parent,
&top_level_windows,
&num_top_level_windows);
XFree(top_level_windows);
XUngrabServer(wm._display);
XGrabButton(
wm._display,
Button1,
ControlMask,
wm._root,
0,
ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
GrabModeAsync,
GrabModeAsync,
None,
None);
XGrabButton(
wm._display,
Button1,
ControlMask,
wm._root,
0,
ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
GrabModeAsync,
GrabModeAsync,
None,
None);
for (;;) {
XEvent e;
XNextEvent(wm._display, &e);
switch (e.type) {
case CreateNotify:
printf("CreateNotify\n");
break;
case DestroyNotify:
printf("DestroyNotify\n");
break;
case ReparentNotify:
printf("ReparentNotify\n");
break;
case MapNotify:
printf("Mapping Window\n");
break;
case UnmapNotify:
printf("UnmapNotify\n");
break;
case ConfigureNotify:
printf("ConfigureNotify\n");
break;
case MapRequest:
printf("MapRequest\n");
ewm_on_map_request(&e.xmaprequest);
break;
case ConfigureRequest:
printf("ConfigureRequest\n");
break;
case ButtonPress:
printf("ButtonPress\n");
ewm_on_button_press(&e.xbutton);
break;
case ButtonRelease:
printf("ButtonRelease\n");
break;
case MotionNotify:
ewm_on_motion_notify(&e.xmotion);
break;
case KeyPress:
printf("KeyPress\n");
ewm_on_key_press(&e.xkey);
break;
case KeyRelease:
printf("KeyRelease\n");
break;
case EnterNotify:
ewm_on_enter_notify(&e.xcrossing);
break;
default:
printf("Something else\n");
}
}
}
void
ewm_on_map_request(const XMapRequestEvent *e)
{
XSelectInput(
wm._display,
e->window,
KeyPressMask | KeyReleaseMask |
EnterWindowMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask);
XMapWindow(wm._display, e->window);
XSetInputFocus(wm._display, e->window, RevertToPointerRoot, CurrentTime);
}
void
ewm_on_enter_notify(const XEnterWindowEvent *e)
{
printf("Entered window: %lu\n", e->window);
XSetInputFocus(wm._display, e->window, RevertToParent, CurrentTime);
}
void
ewm_on_key_press(const XKeyEvent *e)
{
if ((e->state & ControlMask) &&
e->keycode == XKeysymToKeycode(wm._display, XK_q)) {
printf("Destroying window\n");
XDestroyWindow(wm._display, e->window);
}
if ((e->state & ControlMask) &&
e->keycode == XKeysymToKeycode(wm._display, XK_Return)) {
printf("Enter Works\n");
system("urxvt &");
}
}
void
ewm_on_button_press(const XButtonEvent *e)
{
if (e->subwindow != 0) {
// Save initial cursor position;
wm._cursor_start_position = (Vector){e->x_root, e->y_root};
// Save initial window info
Window returned_root;
int x, y;
unsigned int width, height, depth, border_width;
XGetGeometry(wm._display,
e->subwindow,
&returned_root,
&x, &y,
&width, &height,
&border_width,
&depth);
wm._window_start_position = (Vector){x, y};
wm._window_start_size = (Size){width, height};
XRaiseWindow(wm._display, e->subwindow);
XSetInputFocus(wm._display, e->subwindow, RevertToParent, CurrentTime);
printf("Raising window %lu\n", e->subwindow);
printf("root id: %lu\n", wm._root);
XUngrabButton(wm._display, Button1, AnyModifier, e->subwindow);
}
}
void
ewm_on_motion_notify(const XMotionEvent *e)
{
const Vector drag_pos = {e->x_root, e->y_root};
const Vector delta = {
(drag_pos.x - wm._cursor_start_position.x),
(drag_pos.y - wm._cursor_start_position.y)
};
if ((e->state & Button1Mask) && (e->state & ControlMask)) {
const Vector dest_window_pos = {
(wm._window_start_position.x + delta.x),
(wm._window_start_position.y + delta.y)
};
if (e->subwindow != 0) {
XMoveWindow(wm._display,
e->subwindow,
dest_window_pos.x,
dest_window_pos.y);
}
}
}
void
ewm_cleanup()
{
XCloseDisplay(wm._display);
}
I have also tried to use XSendEvent, but based on the results I have gotten from it I don't think I understand what it is supposed to do very well. I am very new to Xlib programming so any help is greatly appreciated.
Thank you!
I had exactly the same problem. The comments in the original question helped, but they're not quite there yet, they're missing an important detail (point 1 ahead). Ultimately I found the hint here solved it.
Grab the event synchronously (notice GrabModeSync given as pointer_mode)
XGrabButton(dis, FOCUS_BUTTON, AnyModifier, root, False, BUTTONMASK, GrabModeSync, GrabModeAsync, None, None);
Use XAllowEvents and Xsync for the pass-through effect
XAllowEvents(display, ReplayPointer, ev->xbutton.time);
XSync(display, 0);

XCB – Not receiving motion notify events on all windows

I am trying to be notified about any pointer motion. Since I don't want to run as the window manager, I need to set XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_POINTER_MOTION on all windows which I do both on startup and when I get a create notify event.
This seems to work fine in general and I receive motion notify events on all windows. However, somehow, this isn't true for Google Chrome windows. I checked the event mask by explicitly querying it afterwards and it is correctly set. I also don't see anything unusual in the propagation mask.
What could cause Google Chrome to not report motion notify events? AFAIK, the X protocol doesn't allow that except for active pointer grabs which Chrome surely doesn't have.
Here is how I register myself on all existing windows. I call register_events on the root window and whenever I receive a create notify event as well:
static void register_events(xcb_window_t window) {
xcb_void_cookie_t cookie = xcb_change_window_attributes_checked(connection,
window, XCB_CW_EVENT_MASK, (uint32_t[]) { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_LEAVE_WINDOW });
xcb_generic_error_t *error = xcb_request_check(connection, cookie);
if (error != NULL) {
xcb_disconnect(connection);
errx(EXIT_FAILURE, "could not subscribe to events on a window, bailing out");
}
}
static void register_existing_windows(void) {
xcb_query_tree_reply_t *reply;
if ((reply = xcb_query_tree_reply(connection, xcb_query_tree(connection, root), 0)) == NULL) {
return;
}
int len = xcb_query_tree_children_length(reply);
xcb_window_t *children = xcb_query_tree_children(reply);
for (int i = 0; i < len; i++) {
register_events(children[i]);
}
xcb_flush(connection);
free(reply);
}
The Chrome windows appear to be comprised of quite the tree of nested child windows. It appears you'll need to walk the tree of windows and monitor them all. This code picks up pointer motion events across the entirety of my Chrome windows:
#include <stdio.h>
#include <stdlib.h>
#include <xcb/xcb.h>
#include <X11/Xlib.h>
static void register_events(xcb_connection_t *conn,
xcb_window_t window) {
xcb_void_cookie_t cookie =
xcb_change_window_attributes_checked(conn,
window, XCB_CW_EVENT_MASK,
(uint32_t[]) {
XCB_EVENT_MASK_POINTER_MOTION });
xcb_generic_error_t *error = xcb_request_check(conn, cookie);
if (error != NULL) {
xcb_disconnect(conn);
exit(-1);
}
}
static void register_existing_windows(xcb_connection_t *conn,
xcb_window_t root) {
int i, len;
xcb_window_t *children;
xcb_query_tree_reply_t *reply;
if ((reply = xcb_query_tree_reply(conn,
xcb_query_tree(conn, root), 0))
== NULL)
{
return;
}
len = xcb_query_tree_children_length(reply);
children = xcb_query_tree_children(reply);
for (i = 0; i < len; i++) {
register_events(conn, children[i]);
register_existing_windows(conn, children[i]);
}
xcb_flush(conn);
}
void main(void) {
int i=0;
/* Open the connection to the X server */
xcb_connection_t *conn = xcb_connect (NULL, NULL);
/* Get the first screen */
xcb_screen_t *screen = xcb_setup_roots_iterator (xcb_get_setup (conn)).data;
register_existing_windows(conn, screen->root);
while(1) {
xcb_generic_event_t *evt;
evt = xcb_wait_for_event(conn);
printf("%i\n", i++);
}
}
(That's just intended as proof of concept, and not very nice.)
While #Jay Kominek's answer was helpful and valid, I've come to realize now that using the Xinput extension provides a much better approach as it won't interfere with applications whatsoever.
Simply selecting on the entire tree causes all kinds of issues, e.g., hover doesn't work in Chrome anymore.
xcb provides xcb_grab_pointer to capture pointer event without registe on specific window.
#include <stdlib.h>
#include <stdio.h>
#include <xcb/xcb.h>
void
print_modifiers (uint32_t mask)
{
const char **mod, *mods[] = {
"Shift", "Lock", "Ctrl", "Alt",
"Mod2", "Mod3", "Mod4", "Mod5",
"Button1", "Button2", "Button3", "Button4", "Button5"
};
printf ("Modifier mask: ");
for (mod = mods ; mask; mask >>= 1, mod++)
if (mask & 1)
printf(*mod);
putchar ('\n');
}
int
main ()
{
xcb_connection_t *c;
xcb_screen_t *screen;
xcb_window_t win;
xcb_generic_event_t *e;
uint32_t mask = 0;
/* Open the connection to the X server */
c = xcb_connect (NULL, NULL);
/* Get the first screen */
screen = xcb_setup_roots_iterator (xcb_get_setup (c)).data;
mask = XCB_EVENT_MASK_BUTTON_PRESS |
XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION;
xcb_grab_pointer(c, false, screen->root, mask, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, XCB_CURRENT_TIME);
xcb_flush (c);
while ((e = xcb_wait_for_event (c))) {
switch (e->response_type & ~0x80) {
case XCB_BUTTON_PRESS: {
xcb_button_press_event_t *ev = (xcb_button_press_event_t *)e;
print_modifiers(ev->state);
switch (ev->detail) {
case 4:
printf ("Wheel Button up in window %ld, at coordinates (%d,%d)\n",
ev->event, ev->event_x, ev->event_y);
break;
case 5:
printf ("Wheel Button down in window %ld, at coordinates (%d,%d)\n",
ev->event, ev->event_x, ev->event_y);
break;
default:
printf ("Button %d pressed in window %ld, at coordinates (%d,%d)\n",
ev->detail, ev->event, ev->event_x, ev->event_y);
}
break;
}
case XCB_BUTTON_RELEASE: {
xcb_button_release_event_t *ev = (xcb_button_release_event_t *)e;
print_modifiers(ev->state);
printf ("Button %d released in window %ld, at coordinates (%d,%d)\n",
ev->detail, ev->event, ev->event_x, ev->event_y);
break;
}
case XCB_MOTION_NOTIFY: {
xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)e;
printf ("Mouse moved in window %ld, at coordinates (%d,%d)\n",
ev->event, ev->event_x, ev->event_y);
break;
}
default:
/* Unknown event type, ignore it */
printf("Unknown event: %d\n", e->response_type);
break;
}
/* Free the Generic Event */
free (e);
}
return 0;
}

Resources