Convert ASCII character to x11 keycode
Asked Answered
H

3

8

I have a small program that takes input in the form of ascii characters. I need to be able to convert these to keycodes for use with x11 functions. Is there a xlib function to do this or another library? Or will a large switch case work best?

Hatchery answered 10/9, 2012 at 0:1 Comment(2)
although this question is quite old, consider accepting @BobDoolittle's answer instead of mineMorey
Since this question is old, it's probably good to mention that most input is not given in ASCII these days, and solutions for converting UTF to key codes are possible, though more complicated or slightly less standardized.Marionmarionette
M
1

You can use XStringToKeysym to convert at least alphanumeric characters to KeySyms, followed by XKeysymToKeycode for conversion to KeyCodes:

Display *display = ...;
KeySym sym_a = XStringToKeysym("A");
KeyCode code_a = XKeysymToKeycode(display, sym_a);

As @BobDoolitle pointed out in his answer, while XStringToKeysym works for alphanumeric single-character strings, it fails in the ASCII general case. He provides a way to trivially handle the majority of ASCII characters (0x200x7F) although characters outside that range require more careful treatment.

Morey answered 10/9, 2012 at 1:0 Comment(1)
This doesn't work in the general case. XStringToKeysym maps KeySym names, not ASCII characters. You'll often be OK because for common printing characters the KeySym name matches the character, but it won't work for example for a space character.Mothering
M
14

This question has an old, wrong answer (from @oldrinb), that oddly has never been challenged. As stated in the comment, you can't use XStringToKeysym to map chars to KeySyms in a general way. It will work for letters and numbers, but that's about it, because the KeySym name happens to map directly for those ASCII characters. For other ASCII characters such as punctuation or space it won't work.

But you can do better than that. If you look at <X11/keysymdef.h> you find that for ASCII 0x20-0xFF, the characters map directly to XKeySyms. So, I'd say it's simpler to just use that range of characters directly as KeySyms, and just map the remaining 32 characters to their corresponding KeyCodes. So I'd say the code should more properly be:

Display *display = ...;
if ((int)c >= 0x20) {
    XKeysymToKeycode(display, (KeySym)c);
} else {
    ... // Exercise left to the reader :-)
}

The 'else' clause will require multiple KeyCodes since for example ASCII 1 (Control-A) is XK_A with the XK_CONTROL_R (or XK_CONTROL_L) Modifier. So you'd have to issue for example: XK_CONTROL_L DOWN, XK_A DOWN, XK_A UP, XK_CONTROL_L UP.

Here's a toy program that demonstrates this by echoing the first argument via simulated keyboard events:

#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xlib-xcb.h>
#include <xcb/xcb.h>
#include <xcb/xcb_event.h>
#include <xcb/xtest.h>

main(int argc, char **argv)
{
    char *pc;
    xcb_connection_t *xconn;
    KeyCode code_a;
    Display *dpy = XOpenDisplay(NULL);

    xconn = XGetXCBConnection(dpy);

    for (pc = argv[1]; *pc != '\0'; ++pc) {
        if (*pc >= (char)0x20) {
            code_a = XKeysymToKeycode(dpy, (KeySym)*pc);
            xcb_test_fake_input(xconn, XCB_KEY_PRESS, code_a, XCB_CURRENT_TIME, XCB_NONE, 0, 0, 0);
            xcb_test_fake_input(xconn, XCB_KEY_RELEASE, code_a, XCB_CURRENT_TIME, XCB_NONE, 0, 0, 0);
            xcb_flush(xconn);
        } else {
            fprintf(stderr, "Eeek - out-of-range character 0x%x\n", (unsigned int)*pc);
        }
    }
    XCloseDisplay(dpy);
}

You need to link it with: -lX11 -lxcb -lxcb-xtest -lX11-xcb

Disclaimer: No KeySyms were harmed in the writing of this code.

Mothering answered 10/9, 2014 at 17:54 Comment(2)
The answer to that depends on your business logic. There is no ASCII mapping in that case. What would you like your application to do when there is no mapping? You could ignore it, generate an error, ring a bell, or halt and catch fire. Whatever makes sense for your application.Mothering
Nice disclaimer.Hermeneutics
M
1

You can use XStringToKeysym to convert at least alphanumeric characters to KeySyms, followed by XKeysymToKeycode for conversion to KeyCodes:

Display *display = ...;
KeySym sym_a = XStringToKeysym("A");
KeyCode code_a = XKeysymToKeycode(display, sym_a);

As @BobDoolitle pointed out in his answer, while XStringToKeysym works for alphanumeric single-character strings, it fails in the ASCII general case. He provides a way to trivially handle the majority of ASCII characters (0x200x7F) although characters outside that range require more careful treatment.

Morey answered 10/9, 2012 at 1:0 Comment(1)
This doesn't work in the general case. XStringToKeysym maps KeySym names, not ASCII characters. You'll often be OK because for common printing characters the KeySym name matches the character, but it won't work for example for a space character.Mothering
M
0

You can do implement the mapping by hand too, from, say, the output of xmodmap -pke (but it would break on X11 installations that don't the same mapping). Something like this (for ASCII):

typedef struct{
    xcb_keycode_t code;
    u16           state;
}x11_key_t;

x11_key_t X11_ASCII_TO_KEY[] = {
    ['1'] ={10,0x0000}, ['!'] ={10,0x0001},
    ['2'] ={11,0x0000}, ['@'] ={11,0x0001},
    ['3'] ={12,0x0000}, ['#'] ={12,0x0001},
    ['4'] ={13,0x0000}, ['$'] ={13,0x0001},
    ['5'] ={14,0x0000}, ['%'] ={14,0x0001},
    ['6'] ={15,0x0000}, ['^'] ={15,0x0001},
    ['7'] ={16,0x0000}, ['&'] ={16,0x0001},
    ['8'] ={17,0x0000}, ['*'] ={17,0x0001},
    ['9'] ={18,0x0000}, ['('] ={18,0x0001},
    ['0'] ={19,0x0000}, [')'] ={19,0x0001},
    ['-'] ={20,0x0000}, ['_'] ={20,0x0001},
    ['='] ={21,0x0000}, ['+'] ={21,0x0001},
    ['q'] ={24,0x0000}, ['Q'] ={24,0x0001},
    ['w'] ={25,0x0000}, ['W'] ={25,0x0001},
    ['e'] ={26,0x0000}, ['E'] ={26,0x0001},
    ['r'] ={27,0x0000}, ['R'] ={27,0x0001},
    ['t'] ={28,0x0000}, ['T'] ={28,0x0001},
    ['y'] ={29,0x0000}, ['Y'] ={29,0x0001},
    ['u'] ={30,0x0000}, ['U'] ={30,0x0001},
    ['i'] ={31,0x0000}, ['I'] ={31,0x0001},
    ['o'] ={32,0x0000}, ['O'] ={32,0x0001},
    ['p'] ={33,0x0000}, ['P'] ={33,0x0001},
    ['['] ={34,0x0000}, ['{'] ={34,0x0001},
    [']'] ={35,0x0000}, ['}'] ={35,0x0001},
    ['\n']={36,0x0000},
    ['a'] ={38,0x0000}, ['A'] ={38,0x0001},
    ['s'] ={39,0x0000}, ['S'] ={39,0x0001},
    ['d'] ={40,0x0000}, ['D'] ={40,0x0001},
    ['f'] ={41,0x0000}, ['F'] ={41,0x0001},
    ['g'] ={42,0x0000}, ['G'] ={42,0x0001},
    ['h'] ={43,0x0000}, ['H'] ={43,0x0001},
    ['j'] ={44,0x0000}, ['J'] ={44,0x0001},
    ['k'] ={45,0x0000}, ['K'] ={45,0x0001},
    ['l'] ={46,0x0000}, ['L'] ={46,0x0001},
    [';'] ={47,0x0000}, [':'] ={47,0x0001},
    ['\'']={48,0x0000}, ['"'] ={48,0x0001},
    ['`'] ={49,0x0000}, ['~'] ={49,0x0001},
    ['?'] ={51,0x0000}, ['|'] ={51,0x0001},
    ['z'] ={52,0x0000}, ['Z'] ={52,0x0001},
    ['x'] ={53,0x0000}, ['X'] ={53,0x0001},
    ['c'] ={54,0x0000}, ['C'] ={54,0x0001},
    ['v'] ={55,0x0000}, ['V'] ={55,0x0001},
    ['b'] ={56,0x0000}, ['B'] ={56,0x0001},
    ['n'] ={57,0x0000}, ['N'] ={57,0x0001},
    ['m'] ={58,0x0000}, ['M'] ={58,0x0001},
    [','] ={59,0x0000}, ['<'] ={59,0x0001},
    ['.'] ={60,0x0000}, ['>'] ={60,0x0001},
    ['/'] ={61,0x0000}, ['\\']={61,0x0001},
    [' '] ={65,0x000},
};

And then you call it like:

i64   bdim = 2;
char* data = "hi";
fori(i, 0,bdim){
    x11_key_t key = X11_ASCII_TO_KEY[data[i]];
    printf("%02x %04x\n", key.code, key.state);
}

You can also implement it programatically, using the output from xcb_get_keyboard_mapping_unchecked() and xcb_get_modifier_mapping_unchecked().

Maw answered 14/7, 2022 at 19:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.