Obtain keyboard layout and keysyms with XCB

6.4k views Asked by At

I'm creating an on-screen keyboard for Linux integrated in a simple Window Manager. I'm currently using XCB, and now I want to make the fake keystrokes. Everything works fine using xtest extension, except for the detail that I can't get the true keycodes from the keysyms I want to put.

Currently I'm using xcb_key_symbols_get_keycode, and then I use xcb_key_symbols_get_keysym to know which modifier I need to get that symbol. This works fine when the configured keyboard is the USA one. But I have a spanish keyboard, and this call only returns the USA configurations. In spanish keyboards several symbols are obtained with the right alt (altgr), but the former calls doesn't seem to support it.

So, how can I get the keycodes needed to get any keysym? I presume that I need the XKB extensions, but I've been unable to find them for XCB, and I don't want to rewrite the whole window manager for XLib.

Thanks.

2

There are 2 answers

1
étale-cohomology On

The following code will print the whole keycode-to-keysym mapping. It is equivalent to xmodmap -pk or xmodmap -pke. That is, given any keycode, it gives you the keysyms associated to that keycode.

(Except that it only shows keysym values, not keysym names, which you can find in /usr/include/X11/keysymdef.h or using Xlib's XKeysymToString(), and I don't think there exists an XCB port of that function, but you could write your own based on keysymdef.h.) (On my X server, there's 7 keysyms per keycode, and I'd like to know if the X server can support more than that...)

/*
gcc keymap.c -o keymap -lxcb && ./keymap
*/
#include <stdlib.h>
#include <stdio.h>
#include <xcb/xcb.h>

void xcb_show_keyboard_mapping(xcb_connection_t* connection, const xcb_setup_t* setup){
  xcb_get_keyboard_mapping_reply_t* keyboard_mapping =
    xcb_get_keyboard_mapping_reply(
      connection,
      xcb_get_keyboard_mapping(
        connection,
        setup->min_keycode,
        setup->max_keycode - setup->min_keycode + 1),
      NULL);

  int          nkeycodes = keyboard_mapping->length / keyboard_mapping->keysyms_per_keycode;
  int          nkeysyms  = keyboard_mapping->length;
  xcb_keysym_t* keysyms  = (xcb_keysym_t*)(keyboard_mapping + 1);  // `xcb_keycode_t` is just a `typedef u8`, and `xcb_keysym_t` is just a `typedef u32`
  printf("nkeycodes %u  nkeysyms %u  keysyms_per_keycode %u\n\n", nkeycodes, nkeysyms, keyboard_mapping->keysyms_per_keycode);

  for(int keycode_idx=0; keycode_idx < nkeycodes; ++keycode_idx){
    printf("keycode %3u ", setup->min_keycode + keycode_idx);
    for(int keysym_idx=0; keysym_idx < keyboard_mapping->keysyms_per_keycode; ++keysym_idx){
      printf(" %8x", keysyms[keysym_idx + keycode_idx * keyboard_mapping->keysyms_per_keycode]);
    }
    putchar('\n');
  }

  free(keyboard_mapping);
}

int main(){
  xcb_connection_t*  connection = xcb_connect(NULL, NULL);
  const xcb_setup_t* setup      = xcb_get_setup(connection);
  xcb_show_keyboard_mapping(connection, setup);
  xcb_disconnect(connection);
}

If you want the opposite mapping (keysyms-to-keycodes), you may want xcb_key_symbols_get_keycode(), I don't know.


And no, you don't need XKB to handle keyboard stuff. Most of the stuff you could possibly want can be done with the core X protocol, including but not limited to modifying the aforementioned keycode-to-keysym mapping, grabbing the keyboard, grabbing the mouse, sending keyboard/mouse input, using all 8 modifiers, and writing "pseudo Unicode" (ie. all the symbols you find in keysymdef.h, which I don't think is oficially Unicode but does contain a lot of stuff).

1
fleischie On

It's probably an outdated issue, but I find it still valid.

You can enable xkb extensions via xcb and then get the keymap, it's layout and the appropriate keysym for that layout via the xcb API (or more specifically from the xkb keymap's state).

Links:

I have to admit, that I did not actually do this in full, yet (only up to the keymap/layout/state thing). But it should be enough to get you started. :)