2

I am trying to make an image appear under my cursor whenever I click on my desktop. I decided to use xcb to accomplish this. I figured I should capture the pointer from the root window (I don't really know a better way to do it), because I always want the image to appear, no matter where I click. The application that displays the image should not interfere with my normal workflow.

Here is how I capture the pointer so far:

#include <X11/Xutil.h>
#include <X11/Xlib-xcb.h>
#include <X11/Xutil.h>
#include <xcb/xcb_aux.h>
#include <xcb/xcb.h>
#include <xcb/xproto.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
void setup(xcb_connection_t *connection) {
    xcb_generic_error_t *err;
    xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(connection)).data;
    xcb_void_cookie_t grab_cookie = xcb_grab_button(connection, True, screen->root, XCB_NONE, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, XCB_BUTTON_INDEX_1, XCB_MOD_MASK_ANY);
    xcb_generic_error_t *error = xcb_request_check(connection, grab_cookie);
    if (error != NULL) {
        xcb_disconnect(connection);
        perror("could not subscribe to events on a window, bailing out");
        exit(1);
    }  
    free(error);
        xcb_flush(connection);
}

int main(int argc, char *argv[]) {
    xcb_generic_event_t *e;
    Display *dpy = XOpenDisplay(NULL);
    xcb_connection_t *connection = XGetXCBConnection(dpy);
    setup(connection);
    while ((e = xcb_wait_for_event(connection))) {
        switch(e->response_type & ~0x80) {
            case XCB_BUTTON_PRESS:
                printf("Click.\n");
                break;
            default:
                break;
        }
        free(e);
    }
    xcb_ungrab_pointer(connection, XCB_TIME_CURRENT_TIME);
}

The code line that grabs the cursor is in the setup method:

 xcb_void_cookie_t grab_cookie = xcb_grab_button(connection, True, screen->root, XCB_NONE, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, XCB_BUTTON_INDEX_1, XCB_MOD_MASK_ANY);

As far as I can tell by "autocorrecting" man xcb_grab_button, my pointer events should not be influenced if I put XCB_GRAB_MODE_ASYNC as the fifth argument to the function (the manual says something different, but it is generally quite poor, so I would be very surprised if it weren't by mistake). However, this is not the case: When I click, the click simply gets swallowed by my application and, e.g., Firefox does not react to it.

How can grab the cursor such that my click events do not get eaten? If such a feature does not exist in X, would you recommend that I simply "resend" the click events or is there a better option? My window manager is i3, in case this information changes anything.

2

I solved the mystery. Apparently XCB_GRAB_MODE_ASYNC does not do what I thought it would: The (correct) xcb_grab_pointer manual says that the pointer event processing continues normally. I thought that this would refer to the events being propagated to other clients, but this is not the case. The events are still only heard by my application. To propagate the events, they need to be replayed after receival:

xcb_generic_event_t *e;
xcb_generic_error_t *err;
xcb_connection_t *connection = xcb_connect(NULL, NULL);
xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(connection)).data;
xcb_void_cookie_t grab_cookie = xcb_grab_button_checked(connection, True, screen->root, XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, XCB_BUTTON_INDEX_1, XCB_MOD_MASK_ANY);
xcb_generic_error_t *error = xcb_request_check(connection, grab_cookie);
if (error != NULL) {
    xcb_disconnect(connection);
    perror("could not subscribe to events on a window, bailing out");
    exit(1);
}  
free(error);
do {
    xcb_allow_events(connection, XCB_ALLOW_REPLAY_POINTER, XCB_CURRENT_TIME);
    e = xcb_poll_for_event(connection);
    if(!e) {
        continue;
    }
    switch(e->response_type & EVENT_MASK) {
        case XCB_BUTTON_RELEASE:
        case XCB_BUTTON_PRESS:
            printf("Hello.\n");
            break;
        default:
            break;
    free(e);
} while(1);
xcb_ungrab_pointer(connection, XCB_TIME_CURRENT_TIME);

Before the do-while-loop, the pointer press and release for the index1 button are grabbed. The fifth argument has to be XCB_GRAB_MODE_SYNC so that the X server queues events until xcb_allow_events is called (the Xlib manual, man XGrabPointer, gives detailed information, though I wouldn't rely on it in detail, given that it is not for XCB). The subsequent call to xcb_allow_events within the while-loop thaws (unfreezes) the pointer event processing. Passing the XCB_ALLOW_REPLAY_POINTER parameter replays the button events.

It's important to use xcb_poll_for_event within the loop, although I don't fully understand why.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.