Search code examples
linuxinputunicodekeyboardxcb

How to get unicode input from xcb without further ado


during GameDev for a cross-platform title the questions arises how to support full Unicode input in ingame-chat. Using WinAPI on Windows and xcb on Linux for general key input there are the WM_KEY_* messages and xcb_key_* events respectively. This is just fine if you want to be notified about a pressed key on the keyboard.

Now this doesn't give you full text input. For example on Windows there is a additional message called WM_CHAR explicitly for text input. Key codes a translated to Unicode codepoints including the handling of ModifierKeys like shift. Even chinese input is possible.

Now what about xcb? All I can find according to that topic is outdated for at least four years now. Has there some progress been made? Is is now possible to get at least the translation from dead characters to KeySyms (like XFilterEvent did)? I don't need full Unicode input like holding alt-key and type a number on the numpad to get the corresp. codepoint on screen. Also I do not want to link huge libraries like GTK just for translating keyboard input to Unicode.

I know this is a repeated questions, but all are outdated so I ask again for the current state of text input in xcb.

Thank you!


Solution

  • Not sure if it will be of any help, but...

    You are probably looking for a family of X*LookupString functions:

    http://www.xfree86.org/current/Xutf8LookupString.3.html

    Now, I had the same problem as you, that is, I needed to port an application using WinAPI's WM_CHAR to one that uses xcb coupled with X11 (as such coupling is necessary for an OpenGL window). I didn't exactly need Unicode, but I was still lost because there was nothing to let me obtain composed input from the keystrokes (e.g. pressing shift and 7 should result in &)

    Unfortunately, to quote xcb's to-do list:

    Current status

    Currently, XCB only implements the bits given in the X Window System Protocol, and even this support is not complete compared to Xlib (character encoding, no XLookupString()...).

    So, if you have to use xcb, it makes things all the harder, as there are no equivalent X*LookupString functions for the key press event returned by xcb - xcb_key_press_event_t. What you need to do is to convert it to XKeyEvent understandable by, for example, XLookupString.

    Now, looking at the source of XLookupString , we can see it only ever uses three fields of XKeyEvent:

    • display - your Display
    • keycode- the pressed key
    • state - the state of the modifier keys, e.g. shift or caps lock

    It just so happens that there are corresponding fields in xcb_key_press_event_t that are pretty much identical to the ones we need. What I did to make my particular application work was this:

        // This is your generic event returned by, for example, xcb_poll_for_event
        const xcb_generic_event_t* event = ...;
    
        const auto* const press = reinterpret_cast<const xcb_key_press_event_t*>(event);
    
        // Now we have to prepare input understandable by the lookup function
    
        XKeyEvent keyev;
        keyev.display = display; // here you'll need to supply your Display*
        keyev.keycode = press->detail;
        keyev.state = press->state;
    
        std::array<char, 16> buf {};
    
        if (XLookupString(&keyev, buf.data(), buf.size(), nullptr, nullptr)) {
            // Now buf should contain a string with your characters;
            // I just need ascii so I always take only buf[0] and it works fine 
        }
        else {
            // Something's wrong... report error or something
        }
    

    It also looks like XLookupString outputs a keysym correspondent to the composed character, via its in-out KeySym *keysym_return argument. As you say that you also need keysyms, you might want to use it.

    I'm not doing a professional application, so this is all I needed, and I'm not really sure what's the course of action for full Unicode support, but if I ever get to it, I'll let you know.