4

Using XKeycodeToKeysym (in C++) I can get the KeySym for a key-press event. From what I understand this already should respect the keyboard layout. However, when I switch a keyboard layout (between english and hebrew) I get the same KeySym. I suspect that the Xlib Keysym only honors keyboard layout defined in the X11 level? In my system the keyboard layouts are defined only in the desktop environment level (Mate). And if so, is there any way to get the correct character without using a toolkit like Qt / GTK? Must I handle each desktop environment separately?

[Edit]

I've tried the following (according to Andrey's suggestion), which doesn't work:

#include <X11/XKBlib.h>

#include <cstring>
#include <cassert>
#include <iostream>

int main() {
    Display *display = XOpenDisplay(nullptr);
    Window root, window;
    XSetWindowAttributes swa;
    root = DefaultRootWindow( display );
    XSelectInput( display, root, MappingNotify );
    std::memset(&swa, 0, sizeof swa );
    swa.event_mask = MappingNotify | KeyPressMask;
    window = XCreateWindow( display, root, 50, 200, 1024, 768, 0, CopyFromParent, InputOutput, CopyFromParent,
                             CWEventMask, &swa );
    XMapRaised( display, window );
    int xkbEventCode=0, n0=0, n1=0, n2=0, n3=0;
    bool isOk = XkbQueryExtension(  display, &n0, &xkbEventCode, &n1, &n2, &n3 ) ==True;
    assert( isOk );
    isOk = XkbSelectEvents( display, XkbUseCoreKbd, XkbAllEventsMask, XkbAllEventsMask ) ==True;
    assert( isOk );
    while (true) {
        XEvent event;
        std::memset( &event, 0, sizeof event );
        XNextEvent( display, &event );
        if (event.type == xkbEventCode)
            switch (reinterpret_cast<XkbEvent*>(&event)->any.xkb_type) {
              case XkbNewKeyboardNotify:
              case XkbMapNotify: {
                std::cout << "Keyboard mapping has changed." << std::endl;
                break;
                }
            default:  break;
            }
        else
            switch (event.type) {
              case KeyPress: {
                KeyCode keyCode = event.xkey.keycode;
                int keySymsPerkeyCode=0;
                KeySym *keySyms( XGetKeyboardMapping(display, keyCode, 1, &keySymsPerkeyCode)),
                       *keySym = keySyms;
                while (keySymsPerkeyCode--  &&  *keySym != NoSymbol) {
                    std::cout << *keySym << std::endl;
                    ++keySym;
                    }
                XFree( keySyms );
                break;
                }
              case MappingNotify: {
                std::cout << "Keyboard mapping has changed." << std::endl;
                break;
                }
              default:  break;
              }
        }
    return 0;
    }
Yaron Cohen-Tal
  • 2,005
  • 1
  • 15
  • 26
  • Possibly related: https://askubuntu.com/questions/1250059/x11-mappingnotify-event-only-fires-when-modifying-keyboard-layout-inside-gnome-t – matanster Jun 13 '20 at 22:06

1 Answers1

2

I can answer only at a lower level, don't know what XKeycodeToKeysym does.

You need to listen for MappingNotify event and each time you receive it re-read new mapping (usually this means keyboard layout / language changed ) with XGetKeyboardMapping

This is part of core X protocol, desktop environment / WM independent

update:

Looks like xlib enables XKeyboard extension by default. After it's enabled you need to explicitly express interest in mapping change events ( unlike with core protocol where MappingNotify is always sent to all clients ) - see http://www.x.org/archive/X11R7.5/doc/man/man3/XkbSelectEvents.3.html

 XkbSelectEvents (display, XkbUseCoreKbd, XkbAllEventsMask, XkbAllEventsMask);

After that you respond to XkbMapNotify event and do XkbGetMap() to get new layout

Andrey Sidorov
  • 24,905
  • 4
  • 62
  • 75
  • I'm afraid that function gives me the exact same KeySym, which doesn't take into account my keyboard layout in Mate. – Yaron Cohen-Tal Feb 14 '16 at 18:50
  • which one? XGetKeyboardMapping returns a new mapping after keyboard layout is changed. Also, do you get MappingNotify event when you change layout with Mate switcher ? – Andrey Sidorov Feb 14 '16 at 23:07
  • No, I don't get a `MappingNotify`, and `XGetKeyboardMapping` returns the same mapping. – Yaron Cohen-Tal Feb 15 '16 at 02:47
  • could you post simple example of the code you are using? – Andrey Sidorov Feb 15 '16 at 09:06
  • also, can you test with non-mate environment? ( gnome/ kde/ unity ) – Andrey Sidorov Feb 15 '16 at 09:07
  • I've added code. I've tested also with Gnome and KDE - the same. – Yaron Cohen-Tal Feb 15 '16 at 20:38
  • 1
    Interesting, looks like you don't get MappingNotify if XKEYBOARD extension is used ( and xlib automatically enables it ). I was not using xkeyboard in my (non xlib) code - http://www.x.org/releases/X11R7.7/doc/kbproto/xkbproto.html#Interactions_Between_XKB_and_the_Core_Protocol – Andrey Sidorov Feb 16 '16 at 05:42
  • Xkb event type stored in a sub field of event, top level id is the same for all events. See usage examples at https://github.com/search?l=cpp&q=XkbMapNotify&ref=searchresults&type=Code&utf8=%E2%9C%93 – Andrey Sidorov Feb 16 '16 at 12:24
  • You need to compare with `event->any.xkb_type`. Try to print `event.type` in your while loop and you'll see that it actually react on layout change – Andrey Sidorov Feb 16 '16 at 12:26
  • hi, your example is incomplete and with typos. This is what I tested: https://gist.github.com/sidorares/6a8cd9499b5b2024e55c – Andrey Sidorov Feb 16 '16 at 22:00
  • My example was incomplete and with typos because I've extracted it from my full program, because I prefered to post shorter code here. Anyway I've changed my question to contain the full code which doesn't work for me. Can u plz try the code in my question and tell me whether it works for u? – Yaron Cohen-Tal Feb 17 '16 at 12:19
  • your example works for me ( ubuntu 15.10 in virtualbox ) - prints "Keyboard mapping has changed" when I switch layout – Andrey Sidorov Feb 20 '16 at 23:48
  • Which desktop environment do you use? Did u define the keyboard mappings in X or in the desktop environemt? I'll install ubuntu 15.10 in vbox and I want to ducplicate exactly what u did, because now this starts to drive me crazy.. – Yaron Cohen-Tal Feb 21 '16 at 21:52
  • default ubuntu ( unity ) but I just checked and results are the same in IceWM ( or no WM ) if I change layout manually with `setxkbmap -layout ru` `setxkbmap -layout en_US` – Andrey Sidorov Feb 21 '16 at 22:31
  • Ok, I've tested in Ubuntu with Unity and indeed works. However, I then tried Ubuntu with Mate / GNOME / KDE, and again it didn't work. My guess is, from the desktop environments I've tested, Unity is the only one that changes the layout in X, and therefore X is aware of the change. Anyway, I'm still stuck with my problem, to which I doubt whether there is a solution at all. – Yaron Cohen-Tal Feb 22 '16 at 05:19
  • works for me even inside Xephyr / twm so might be gnome/mate/kde config issue – Andrey Sidorov Feb 22 '16 at 05:50
  • Works for me in KDE. – n. m. could be an AI Feb 22 '16 at 14:52
  • Andrey: Let's say I'm lucky and I get the mapping events. I still have the following questions: 1. What do the keySym-s mean? Are they in some encoding (I can't identify it). 2. I get several keySyms per keyCode. How do I know which one to choose? 3. What happens if a sequence of keys generates a single character? For example, in chinese it's impossible to map all characters to single keys. – Yaron Cohen-Tal Feb 22 '16 at 15:13
  • @n.m.: Using `setxkbmap` or chaging the layout in KDE? Because changing the layout in KDE doesn't work when I tested it. – Yaron Cohen-Tal Feb 22 '16 at 15:16
  • @AndreySidorov: Ok, I see now they're defined in "keysymdef.h", was just hoping it corresponds to some known encoding.. – Yaron Cohen-Tal Feb 22 '16 at 15:24
  • Hm, with the KDE switcher it doesn't work for me either. – n. m. could be an AI Feb 22 '16 at 15:55
  • ...but the KDE switcher doesn't actually switch the layout in my configuration! Only changes the country flags. I'm accessing this machine with VNC from Windows. Only the Windows layout switcher works. I will test it on my home machine later (KDE, direct access, no VNC), – n. m. could be an AI Feb 22 '16 at 16:09
  • What happens on my home computer is each keycode being mapped to SIX keycaps (I have a trilingual layout, and each language has normal and shifted keys). All six are printed for each KeyPress event. I never get any layout change events, because the layout stays the same. I guess in this situation you need to track the current group and shift state but I don't know how to track them. Gosh, things got complicated since last time I've looked. – n. m. could be an AI Feb 22 '16 at 20:45
  • @n.m. how do you select which group to use? – Andrey Sidorov Feb 22 '16 at 22:36
  • it is complicated indeed - http://www.x.org/releases/X11R7.7/doc/kbproto/xkbproto.html#Computing_Effective_Modifier_and_Group – Andrey Sidorov Feb 22 '16 at 22:38
  • @AndreySidorov I toggle with alt+space or alt+shift (set up with setxkbmap), plus KDE allows to assign hotkeys for each group. – n. m. could be an AI Feb 22 '16 at 22:45
  • Indeed the X Keyboard Extension, which overrides much of the standard X handling of keys, has a spec defining an alternative eventing flow for keyboard mapping changes. – matanster Jun 21 '20 at 22:05