Niels Horn's Blog

Random thoughts, tips & tricks about Slackware-Linux, Lego and Star Wars

QEMU and Brazilian keyboards

This post explains how to get QEMU working on a host with a pt_BR (ABNT2) keyboard fro Brazil. These keyboards have the “dead” accent-keys and a few modifications that are specific to this layout.

The Problem

For a long time there have been some problems with Brazilian keyboards and QEMU.
In theory, using the standard QEMU, without a special keyboard layout and the normal SDL interface, all keys should be passed directly to the guest operating system as native “scancodes”.
But the Brazilian ABNT2 keyboard layout has two extra keys that were not working in the guest:
- the key with the “slash” (’/'), “question-mark” (’?') and “degrees”
- the “period” next to the numeric keypad

As I use mostly Linux as the guest operating system, I tried to find out the keycodes with then “showkey” command, but nothing happened…
On the host, the two keys show up as 89 and 121 respectively.

Use the Source, Luke!

So QEMU was not sending the keycodes to the host operating system…
I started to study how keyboards are handled in general (OK, I already knew something about this), and how QEMU handles them.
This is the big advantage of Free, Open-Source Software: If you don’t like the way it works, you are free to change it :)

Traditionally, the first 88 keycodes are standard. This comes from the old PC keyboard that had only 88 keys. Then came the AT keyboard with 104 keys, and then came all the different layouts, “internet” and “multi-media” keyboards, etc…
And the two keys I needed are 89 and 121 – not in the 88-key standard!

Now QEMU is a program that runs under “X” in Linux, and here the standard keycodes are numbered 9 – 96 (console-keycode + 8).
In the QEMU source tree we can find the program that handles the SDL user-interface as ui/sdl.c
Here’s an interesting part of the code:

static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
{
    int keycode;
    static int has_evdev = -1;

    if (has_evdev == -1)
        has_evdev = check_for_evdev();

    keycode = ev->keysym.scancode;

    if (keycode < 9) {
        keycode = 0;
    } else if (keycode < 97) {
        keycode -= 8; /* just an offset */
    } else if (keycode < 158) {
        /* use conversion table */
        if (has_evdev)
            keycode = translate_evdev_keycode(keycode - 97);
        else
            keycode = translate_xfree86_keycode(keycode - 97);
    } else if (keycode == 208) { /* Hiragana_Katakana */
        keycode = 0x70;
    } else if (keycode == 211) { /* backslash */
        keycode = 0x73;
    } else {
        keycode = 0;
    }
    return keycode;
}

This part shows clearly that codes 9-96 are "translated" to the console-keycodes by subtracting 8 and the rest is handled by a translate_evdev_keycode function.
Nice! So I started to look for that translate function and found it in ui/x_keymap.c: (shortened for readability)

static const uint8_t evdev_keycode_to_pc_keycode[61] = {
    0,         /*  97 EVDEV - RO   ("Internet" Keyboards) */
    0,         /*  98 EVDEV - KATA (Katakana) */
    0,         /*  99 EVDEV - HIRA (Hiragana) */
    0x79,      /* 100 EVDEV - HENK (Henkan) */
...
    0,         /* 127 EVDEV - PAUS */
    0,         /* 128 EVDEV - ???? */
    0,         /* 129 EVDEV - I129 ("Internet" Keyboards) */
    0xf1,      /* 130 EVDEV - HNGL (Korean Hangul Latin toggle) */
    0xf2,      /* 131 EVDEV - HJCV (Korean Hangul Hanja toggle) */
...
    0,         /* 156 EVDEV - I157 */
    0,         /* 157 EVDEV - I158 */
};

uint8_t translate_xfree86_keycode(const int key)
{
    return x_keycode_to_pc_keycode[key];
}

uint8_t translate_evdev_keycode(const int key)
{
    return evdev_keycode_to_pc_keycode[key];
}

Now, I was looking for 89+8=97 and 121+8=129 and guess what? They are both defined as "0", so indeed - nothing is passed to the guest operating system :)

Filling in the blanks - a patch

That looked simple to solve - a small patch to ui/x_keymap.c, recompile QEMU and we're done!
This is the patch I created:

--- qemu-kvm-0.13.0/ui/x_keymap.c       2010-10-14 12:06:47.000000000 -0300
+++ qemu-kvm-0.13.0_patched/ui/x_keymap.c       2011-03-10 22:28:21.000000000 -0300
@@ -94,7 +94,7 @@
  */

 static const uint8_t evdev_keycode_to_pc_keycode[61] = {
-    0,         /*  97 EVDEV - RO   ("Internet" Keyboards) */
+    0x59,      /*  97 abnt2 slash-question */
     0,         /*  98 EVDEV - KATA (Katakana) */
     0,         /*  99 EVDEV - HIRA (Hiragana) */
     0x79,      /* 100 EVDEV - HENK (Henkan) */
@@ -126,7 +126,7 @@
     0,         /* 126 EVDEV - I126 ("Internet" Keyboards) */
     0,         /* 127 EVDEV - PAUS */
     0,         /* 128 EVDEV - ???? */
-    0,         /* 129 EVDEV - I129 ("Internet" Keyboards) */
+    0x79,      /* 129 abnt2 KP-period */
     0xf1,      /* 130 EVDEV - HNGL (Korean Hangul Latin toggle) */
     0xf2,      /* 131 EVDEV - HJCV (Korean Hangul Hanja toggle) */
     0x7d,      /* 132 AE13 (Yen)*/

I used 0x59 for the slash/question key (that's 89 in hex) and 0x79 for the numeric-period (that's 121 in hex).
Applied the patch, recompiled and tried it. Did it work?
Well, not completely - yet...
At least "showkey" was giving a keycode, but not the one I defined in the table!
For the slash/question key I found 117 and for the numeric-period 92.
I tried all kinds of combinations, but apparently QEMU does some other kind of translation I could not find in the source (anyone have an idea?)
In the end I gave up and decided to solve this in the guest operating system, as I at least had keycodes now that I could handle.

Handling the keys in the console

I work most of the time in the console on my virtual machines, so this was my number one priority and actually simple to solve.
The keymap in the console sits in /usr/share/kbd/keymaps/i386/qwerty and for the Brazilian ABNT2 keyboard is called br-abtn2.map.gz
We can edit this file directly with vim (which will handle the gzip extraction and compression for us).
I changed the following lines at the end:

...
keycode 117 = slash question degree
        control         keycode 117 = Delete
        alt             keycode 117 = Meta_slash

keycode  92 = period
        shift           keycode  92 = period
...

Note: these lines are already there, just changed the "89" to "117" and "121" to "92".

After this, I just had to do loadkeys br-abnt2.map and the missing keys were working in the console!

Handling the keys in X

OK, most of the time I use the console, but I do need to test programs in the graphical environment of X once in a while...
But this is quite simple as well.

X provides the possibility to map keys by creating a ".Xmodmap" file. You can create this file in your home directory (will work only for you) or system-wide for all users.
In Slackware this will be in /etc/X11/xinit/
The file is quite simple, just remember that in X the keycodes are the ones from the console +8:

keycode 125 = slash question
keycode 100 = period period

Conclusion

So, it was not the most elegant solution, but it works perfectly for me.
I have all the keys on my keyboard working in my guest operating systems now, even if they have the wrong keycodes. But as a user this doesn't bother me at all - I just want to be able to use the slash-key to change directories :)

Excerpt in Portuguese
Since this post is about a specific keyboard used here in Brazil, I finish with a small excerpt in Portuguese so that local users can find this article:

Esse artigo explica como alterar o QEMU para passar os "keycodes" para o sistema operacional que roda dentro dele, usando o teclado ABNT2 utilizado no Brasil. Depois é explicado como configurar o Linux para reconhecer de forma correta as teclas de "barra"-"ponto de interrogação" e o "ponto-decimal" ao lado to teclado numérico. Qualquer dúvida, podem deixar um comentário aqui!

Bookmark and Share

This entry was posted on Friday, March 11th, 2011 at 1:54 and is filed under Qemu, Slackware. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

3 Responses to “QEMU and Brazilian keyboards”

  1. StevenT Says:

    Hello

    i do not live in Brazil nor do I speak Protuguese, but i received a qwerty ABNT2 keyboard instead of a US English international.
    Instead of sending it back, i decided to keep this and just use it as is, would save me time and money.

    I have this slash-question key problem and im using windows7 on a laptop Asus M6.

    I tried using ABNT and ABNT2 but still the slash key doesnt work.

    I hope you can help me out, email me at stevent888@gmail.com

    thank you so much for your blog.

    I live in Belgium and we use azerty over here, and thats not so great for programming and gaming, thats why i decided to replace it with qwerty.

    Thanks

  2. Lilian Says:

    Olá Neil,

    Estou no momento residindo em Londres e acabei de comprar um laptop com Windows 7. Configurei o keyboard com o tal do ABNT2 para escrever em português e me deparei com o problema que vc descreve aqui no seu blog. Li todas as suas explicações, mas o problema é que sou “totalmente ignorante” em computação, ou em outras palavras “não entendi bulhufas” do que vc escreveu. Por favor, tem como vc me ajudar. Não tenho o question mark e é impossível ficar sem ele. Please, help me!
    Muito obrigada,

    Lilian

  3. Niels Horn Says:

    Olá Lilian,

    Acho que consegui resolver o teu problema via e-mail :)

    []’s,

    Niels

Leave a Reply



XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

It may take some time for your comment to appear, it is not necessary to submit it again.