xcb: Captura de ponteiro assíncrono não propagando eventos

2

Estou tentando fazer uma imagem aparecer sob meu cursor sempre que clico na área de trabalho. Eu decidi usar o xcb para fazer isso. Eu imaginei que deveria capturar o ponteiro da janela raiz (eu realmente não sei uma maneira melhor de fazer isso), porque eu sempre quero que a imagem apareça, não importa onde eu clique. O aplicativo que exibe a imagem não deve interferir no meu fluxo de trabalho normal.

Veja como eu capturei o ponteiro até agora:

#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);
}

A linha de código que agarra o cursor está no método setup :

 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);

Tanto quanto eu posso dizer "autocorrigindo" man xcb_grab_button , meus eventos de ponteiro não devem ser influenciados se eu colocar XCB_GRAB_MODE_ASYNC como o quinto argumento para a função (o manual diz algo diferente, mas geralmente é muito ruim , então eu ficaria muito surpreso se não fosse por engano). No entanto, este não é o caso: quando clico, o clique é simplesmente engolido pelo meu aplicativo e, por exemplo, o Firefox não reage a ele.

Como posso pegar o cursor de forma que meus eventos de clique não sejam comidos? Se tal recurso não existir no X, você recomendaria que eu simplesmente "reenvie" os eventos de clique ou há uma opção melhor? Meu gerenciador de janelas é o i3, caso essa informação mude alguma coisa.

    
por PawkyPenguin 11.10.2017 / 05:10

1 resposta

2

Eu resolvi o mistério. Aparentemente XCB_GRAB_MODE_ASYNC não faz o que eu pensava: O manual (correto) xcb_grab_pointer diz que o processamento de evento do ponteiro continua normalmente. Eu pensei que isso se referiria aos eventos sendo propagados para outros clientes, mas esse não é o caso. Os eventos ainda são ouvidos apenas pelo meu aplicativo. Para propagar os eventos, eles precisam ser repetidos após o recebimento:

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);

Antes do loop while, o ponteiro pressionado e liberado para o botão index1 são capturados. O quinto argumento tem que ser XCB_GRAB_MODE_SYNC para que o servidor X enfileira eventos até que xcb_allow_events seja chamado (o manual do Xlib, man XGrabPointer , fornece informações detalhadas, embora eu não confie em detalhes, já que é não para XCB). A chamada subseqüente para xcb_allow_events dentro do while -loop descongela (descongela) o processamento de evento do ponteiro. Passar o parâmetro XCB_ALLOW_REPLAY_POINTER reproduz os eventos do botão.

É importante usar xcb_poll_for_event dentro do loop, embora eu não entenda completamente o motivo.

    
por 11.10.2017 / 15:50

Tags