Sending arbitrary keysyms/Unicode without xcb_change_keyboard_mapping()

Diego Alonso Cortez diego at mathisart.org
Sun Sep 22 09:54:28 UTC 2024


By running something like



xmodmap -pke



or calling xcb_get_keyboard_mapping_unchecked()



one can get the mapping of 8-bit X11 keycodes to 32-bit keysyms, where each keycode maps to (I think) 8 keysyms or so.



Then, one can send X11 keycodes to applications by calling xcb_send_event(). I have a helper function for that:





void xcb_send_key_press(xcb_connection_t* connection, xcb_window_t root, xcb_window_t window, xcb_keycode_t keycode){

  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;

  ev.same_screen   = 1;

  xcb_send_event(connection, 0, window, XCB_EVENT_MASK_NO_EVENT, (const char*)&ev);

}





I'm trying to send arbitrary Unicode codepoints (and, just as importantly, sequences of codepoints) to the X11 application/window currently in focus.

I know this is possible because "every possible Unicode character has already a keysym string defined algorithmically" (source: /usr/include/X11/keysymdef.h).



To send, eg., Unicode codepoint U+1f3f4, I'm doing:





#define X11_UNICODE_BASE  0x01000000

xcb_change_keyboard_mapping(connection, 1,255, 1,(const xcb_keysym_t[]){X11_UNICODE_BASE|0x01f3f4});

xcb_send_key_press(connection, screen->root, get_input_focus_reply->focus, 255);





where I'm remapping keycode 255 (last X11 keycode) on-the-fly to the desired Unicode codepoint.



But I can't figure out how to send keysyms directly, without having to call xcb_change_keyboard_mapping(), which is very, very slow if you call it several times in a row (in that case it freezes certain X11 things for several seconds, like mouse clicking and alt-tabbing).



So, for example, what I'm doing in order to send (say) the flag of England (7 Unicode codepoints), I'm doing (what effectually amounts to):





xcb_get_input_focus_cookie_t get_input_focus_cookie = xcb_get_input_focus_unchecked(connection);

xcb_get_input_focus_reply_t* get_input_focus_reply  = xcb_get_input_focus_reply(connection, get_input_focus_cookie, NULL);

xcb_change_keyboard_mapping(connection, 1,249, 1,(const xcb_keysym_t[]){X11_UNICODE_BASE|0x01f3f4});

xcb_send_key_press(connection, screen->root, get_input_focus_reply->focus, 249);

xcb_change_keyboard_mapping(connection, 1,250, 1,(const xcb_keysym_t[]){X11_UNICODE_BASE|0x0e0067});

xcb_send_key_press(connection, screen->root, get_input_focus_reply->focus, 250);

xcb_change_keyboard_mapping(connection, 1,251, 1,(const xcb_keysym_t[]){X11_UNICODE_BASE|0x0e0062});

xcb_send_key_press(connection, screen->root, get_input_focus_reply->focus, 251);

xcb_change_keyboard_mapping(connection, 1,252, 1,(const xcb_keysym_t[]){X11_UNICODE_BASE|0x0e0065});

xcb_send_key_press(connection, screen->root, get_input_focus_reply->focus, 252);

xcb_change_keyboard_mapping(connection, 1,253, 1,(const xcb_keysym_t[]){X11_UNICODE_BASE|0x0e006e});

xcb_send_key_press(connection, screen->root, get_input_focus_reply->focus, 253);

xcb_change_keyboard_mapping(connection, 1,254, 1,(const xcb_keysym_t[]){X11_UNICODE_BASE|0x0e0067});

xcb_send_key_press(connection, screen->root, get_input_focus_reply->focus, 254);

xcb_change_keyboard_mapping(connection, 1,255, 1,(const xcb_keysym_t[]){X11_UNICODE_BASE|0x0e007f});

xcb_send_key_press(connection, screen->root, get_input_focus_reply->focus, 255);

xcb_sync(connection);





which works, but looks insane to me, and is also slow (in the sense described before).



Though the following also works:





xcb_change_keyboard_mapping(connection, 7,249, 1,(const xcb_keysym_t[]){X11_UNICODE_BASE|0x01f3f4,X11_UNICODE_BASE|0x0e0067,X11_UNICODE_BASE|0x0e0062,X11_UNICODE_BASE|0x0e0065,X11_UNICODE_BASE|0x0e006e,X11_UNICODE_BASE|0x0e0067,X11_UNICODE_BASE|0x0e007f});

xcb_send_key_press(connection, screen->root, get_input_focus_reply->focus, 249);

xcb_send_key_press(connection, screen->root, get_input_focus_reply->focus, 250);

xcb_send_key_press(connection, screen->root, get_input_focus_reply->focus, 251);

xcb_send_key_press(connection, screen->root, get_input_focus_reply->focus, 252);

xcb_send_key_press(connection, screen->root, get_input_focus_reply->focus, 253);

xcb_send_key_press(connection, screen->root, get_input_focus_reply->focus, 254);

xcb_send_key_press(connection, screen->root, get_input_focus_reply->focus, 255);





But in general I can't use the trick of calling xcb_change_keyboard_mapping() with multiple desired Unicode values at once (which is way faster than doing it one at a time) because for my application I don't know in advance what Unicode codepoints will be sent by the user.

And, since X11 keycodes are merely 8-bits, they can't fit all of Unicode anyway (not even practical things, like all 3790 emoji, which by my count amount to 11,192 Unicode codepoints).





What would much more sense is to do something like:





xcb_send_keysym(connection, X11_UNICODE_BASE|0x01f3f4);

xcb_send_keysym(connection, X11_UNICODE_BASE|0x0e0067);

xcb_send_keysym(connection, X11_UNICODE_BASE|0x0e0062);

xcb_send_keysym(connection, X11_UNICODE_BASE|0x0e0065);

xcb_send_keysym(connection, X11_UNICODE_BASE|0x0e006e);

xcb_send_keysym(connection, X11_UNICODE_BASE|0x0e0067);

xcb_send_keysym(connection, X11_UNICODE_BASE|0x0e007f);





without the freeze that multiple calls to xcb_change_keyboard_mapping() cause.



So, how can I do that?



(I'm already doing this on Windows and Mac, but I've yet to find a solution for X11.)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/xcb/attachments/20240922/5aed42ef/attachment.htm>


More information about the Xcb mailing list