How to send key event to application using XCB?
Asked Answered
N

3

14

How can I send a key press or key release event to a window (the currently active window) from another program using XCB?

I found some tutorials using XLib, however I would like to use XCB.

I guess I will have to call xcb_send_event, however I have no idea what to pass it as parameters.

Nightjar answered 9/1, 2012 at 19:34 Comment(3)
You should note that most applications block send_events() as it is a blatant security whole. Back in the early X11 days we used it to type into other users xterms. :-)Architecture
I am trying to record my keypresses and then play them again, i.e. recording macros to save time.Nightjar
You're better off doing in on the application level than low level by X11.Architecture
G
13

You should be able to use the XTEST extension to fake input to the active window, using the xcb_test_fake_input() function.

#include <xcb/xtest.h>
...
xcb_test_fake_input(c, XCB_KEY_PRESS, keycode, XCB_CURRENT_TIME, XCB_NONE, 0, 0, 0);
xcb_test_fake_input(c, XCB_KEY_RELEASE, keycode, XCB_CURRENT_TIME, XCB_NONE, 0, 0, 0);

See the xte program in xcb/demos for a working example.

Government answered 19/2, 2013 at 12:28 Comment(2)
Thank you for sharing this! Most apps are blocking send_event and test_fake_input sounds like it does the trick. to compile with g++ is there something to include? I currently compile with g++ main.cc -o trigger -std=c++11 -lxcb. Thanks!Rhodia
@Rhodia I was able to compile with -lxcb-xtest. (e.g. gcc test.c -lxcb -lxcb-xtest -o test). I had to install the package libxcb-xtest0-dev.Delwyn
T
3

Possible alternative: xcb_send_event()

See: X11R7.7 API Documentation

Example from there:

/*
 * Tell the given window that it was configured to a size of 800x600 pixels.
 */
void my_example(xcb_connection_t* conn, xcb_window_t window) {

    //
    // Every X11 event is 32 bytes long. Therefore, XCB will copy 32 bytes.
    // In order to properly initialize these bytes, we allocate 32 bytes even
    // though we only need less for an xcb_configure_notify_event_t
    //
    xcb_configure_notify_event_t* event = calloc(32, 1);

    event->event = window;
    event->window = window;
    event->response_type = XCB_CONFIGURE_NOTIFY;

    event->x = 0;
    event->y = 0;
    event->width = 800;
    event->height = 600;

    event->border_width = 0;
    event->above_sibling = XCB_NONE;
    event->override_redirect = false;

    xcb_send_event(conn, false, window, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char*) event);
    xcb_flush(conn);
    free(event);
}
Tachometer answered 21/9, 2021 at 21:8 Comment(0)
P
1

With vanilla XCB (ie. without extensions like XTEST), using xcb_send_event():

xcb_screen_t* xcb_get_screen(xcb_connection_t* connection, int screen_idx){  // Return a screen from its number!
    const xcb_setup_t* setup = xcb_get_setup(connection);
    for(xcb_screen_iterator_t screen_it = xcb_setup_roots_iterator(setup);  screen_it.rem;  --screen_idx, xcb_screen_next(&screen_it))
        if(screen_idx==0) return screen_it.data;
    return NULL;
}

fdefi void xcb_send_key_press(xcb_connection_t* connection, xcb_window_t root, xcb_window_t window, xcb_keycode_t keycode){  // http://t-sato.in.coocan.jp/xvkbd/events.html
    xcb_key_press_event_t ev;  memset(&ev,0x00,sizeof(xcb_key_press_event_t));
    ev.response_type = XCB_KEY_PRESS;
    ev.detail        = keycode;
    ev.time          = XCB_CURRENT_TIME;
    ev.root          = root;
    ev.event         = window;
    ev.state         = 0x0000;  // xcb_mod_mask_t: XCB_MOD_MASK_SHIFT XCB_MOD_MASK_LOCK XCB_MOD_MASK_CONTROL XCB_MOD_MASK_1 XCB_MOD_MASK_2 XCB_MOD_MASK_3 XCB_MOD_MASK_4 XCB_MOD_MASK_5 XCB_MOD_MASK_ANY
    ev.same_screen   = 1;
    xcb_send_event(connection, 0/*1*/, window/*XCB_SEND_EVENT_DEST_ITEM_FOCUS*/, XCB_EVENT_MASK_NO_EVENT/*XCB_EVENT_MASK_KEY_PRESS XCB_EVENT_MASK_NO_EVENT*/, (const char*)&ev);  // @XCB_SEND_EVENT_DEST_ITEM_FOCUS needs to have @propate set to 1?  // every X11 event is 32 bytes
}

int main(){  // תִּשְׂמָ֑ח
    int xcb_screen_idx;
    xcb_connection_t*  xcb_connection = xcb_connect(NULL,&xcb_screen_idx);
    xcb_screen_t*      xcb_screen     = xcb_get_screen(xcb_connection,xcb_screen_idx);  // xcb_meta(xcb_connection,xcb_screen_idx);

    xcb_get_input_focus_cookie_t get_input_focus_cookie = xcb_get_input_focus_unchecked(xcb_connection);
    xcb_get_input_focus_reply_t* get_input_focus_reply  = xcb_get_input_focus_reply(xcb_connection, get_input_focus_cookie, NULL);
    xcb_send_key_press(xcb_connection, xcb_screen->root, get_input_focus_reply->focus, 0x26);
    free(get_input_focus_reply);

    xcb_disconnect(xcb_connection);
}
Pooley answered 14/7, 2022 at 9:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.