Torsten Förtsch
IT System Development & Security
Kaum macht man's richtig, schon geht's, ;-)

>> Home >> Tipps & Tricks >> Tastatur-Layout mit XKB

Keyboard Customizing mit XKB

Wie aus diesem Beitrag ersichtlich, bin ich als deutscher Programmierer manchmal mit der Notwendigkeit konfrontiert, Texte in Russisch zu schreiben. Als Programmierer hätte ich gern ein Keyboard, auf dem sich Zeichen wie {, }, ~, [, ] oder auch \ ohne große Verrenkung eingeben lassen. Als Deutscher brauche ich natürlich die Umlaute und das ß. Und Russisch soll die Tastatur dann auch noch unterstützen. Außerdem benutze ich gern KDE.

Der letzte Punkt führt zu kxkb, einem Programm, das im System Tray einen kleinen Tastatur-Umschalter einbaut. Seine Konfiguration habe ich hier grob beschrieben.

Intern benutzt kxkb das Programm setxkbmap für das eigentliche Umschalten. Die Kommandozeile wird im Konfigurationsdialog für jedes Layout angezeigt, kann aber nicht geändert werden. Für unser Vorhaben heißt das, wir müssen die bestehenden XKB-Konfigurationsdateien ändern und können keine neuen erfinden.

Ziel

Nach dieser kurzen Einleitung möchte ich erstmal das Ziel genauer definieren. Ich möchte eine normale amerikanische Tastatur-Belegung mit einigen Ausnahmen:

  1. AltGr+VOKAL soll einen kleingeschriebenen und Shift+AltGr+VOKAL einen großgeschriebenen Umlaut liefern
  2. LinksShift+RechtsShift, also Drücken der rechten Shift-Taste bei gedrückter linker Shift-Taste soll das russische Layout aktivieren bzw. wieder zurück ins amerikanische Layout schalten.
  3. die 5 Sondertasten meines Notebooks sollen funktionieren.

Doppel-Shift Umschaltung

Beginnen wir mit der Doppel-Shift-Umschaltung, weil das am einfachsten ist. Der erste Schritt ist im 3. Tab der kxkb-Konfiguration zu tun:

kxkb Options

Das war es aber leider noch nicht. Als nächstes muß in der Konfiguration der Tastatur-Kürzel noch das richtige gesetzt werden. Dazu wählt man in der KDE Konfiguration den Keyboard Shortcuts Dialog, sucht unter den globalen Shortcuts Switch to Next Keyboard Layout und drückt mit der Maus auf den Knopf neben dem Wort Custom. Das sich öffnende Fenster bietet die Möglichkeit, 2 Tastenkombinationen anzugeben. Man klickt einfach mit der Maus auf den entsprechenden Knopf und gibt mit der Tastatur den gewünschten Shortcut ein, in unserem Fall also Links-Shift + Rechts-Shift.

Keyboard Shortcuts

Ob es geklappt hat, kann man ausprobieren, indem man Links-Shift gedrückt hält und dabei Rechts-Shift drückt. Dabei sollte sich die Fahne im kxkb-Symbol im System Tray ändern.

Umlaute im amerikanischen Layout

Layout Varianten

Der Schlüssel liegt in den Layout-Varianten, die XKB ermöglicht. Zu einer Basis-Belegung können zusätzliche Varianten definiert werden. Wir werden eine neue Variante des amerikanischen Keyboards mit dem Namen opi (mein Spitzname) definieren, die die Umlaute entsprechend auf Altgr+[oua] legt und ß auf Altgr+s. Diese neue Variante kann man dann im KDE-Konfigurationsdialog auswählen.

So, bisher gab es für die Konfiguration ein graphisches Frontend. Das ist jetzt vorbei. Also starte schon mal Deinen Lieblingseditor. Die Dateien um die es geht liegen bei älteren X11-Versionen unter /etc/X11/xkb, in neueren aber unter /usr/share/X11/xkb.

Für die Default-Variante des amerikanischen Layouts gibt KDE als Kommando

setxkbmap -model pc104 -layout us

an. Für Varianten wird einfach -variant NAME angehängt. Wo sind die Varianten und das Default-Layout definiert? Dazu öffnen wir die Datei /usr/share/X11/xkb/symbols/us. Das us hier korrespondiert mit der -layout Option.

Die Datei beginnt nach ein paar Kommentaren mit folgenden Zeilen:

default
partial alphanumeric_keys modifier_keys 
xkb_symbols "basic" {

    name[Group1]= "U.S. English";

    // Alphanumeric section
    key <TLDE> {	[     grave,	asciitilde	]	};
    key <AE01> {	[	  1,	exclam 		]	};
    ...
    key <CAPS> {	[ Caps_Lock	]	};
    // End alphanumeric section
};

Damit ist das Default-Layout (basic) definiert. Jeder Taste sind 2 Symbole zugeordnet. Das erste ist vereinfacht gesagt das Zeichen, das erscheint beim Drücken ohne Shift, das zweite mit Shift.

Ein Stück weiter unten in der Datei finden wir die Definition der intl Variante. Hier sind 3 Dinge bemerkenswert:

partial alphanumeric_keys
xkb_symbols "intl" {

    name[Group1]= "U.S. English - International (with dead keys)";

    include "us(basic)"

    // Alphanumeric section
    key <TLDE> { [dead_grave, dead_tilde,         grave,       asciitilde ] };
    ...
    key <BKSL> { [ backslash,        bar,       notsign,        brokenbar ] };

    include "level3(ralt_switch)"
};

Erben bedeutet in diesem Zusammenhang, daß das Layout nicht alle Zeichen neu belegen muß. Es werden nur die überschrieben, die sich gegenüber basic ändern.

Die include Zeile am Ende aktiviert die Altgr-Taste, damit die Symbole 3 und 4 mit Altgr+Taste bzw. Altgr+Shift+Taste ausgegeben werden. Probier es mal aus. Aktiviere die intl-Variante des us-Layouts und drücke in einem Konsolenfenster auf Altgr+q. Es erscheint ein ä. Das ist nicht gerade das, was man als Deutscher erwarten würde.

So, jetzt haben wir die Grundlagen abgeguckt. Wir brauchen eine ähnliche Sektion in der Datei, die wir opi nennen. Sie wird sicher von irgendwas erben, und sie muß ralt_switch einbinden.

Hier ist meine Variante:

partial alphanumeric_keys
xkb_symbols "opi" {

    include "latin"

    name[Group1]="Opi";

    key <AD02>	{ [         w,          W,           ae,           AE ]	};
    key <AD03>	{ [         e,          E,     EuroSign,     EuroSign ]	};
    key <AC01>	{ [         a,          A,   adiaeresis,   Adiaeresis ]	};
    key <AC07>	{ [         j,          J,    downarrow,      uparrow ]	};
    key <AD07>	{ [         u,          U,   udiaeresis,   Udiaeresis ]	};
    key <AD09>	{ [         o,          O,   odiaeresis,   Odiaeresis ]	};

    include "level3(ralt_switch)"
};
Neue Layout-Variante

Wer genau hinschaut stellt fest, das dieser Block nicht von us(basic) erbt, sondern von latin. Das latin-Layout ist in einer anderen Datei, nämlich latin definiert. Mir gefiel es als Basis einfach besser.

Nun findet auch die KDE-Konfiguration unsere neue Version des us-Keyboards. Wähle sie aus und Du erhältst ein ä, wenn Du Altgr+a drückst.

Der zweite Teil der Aufgabe wäre damit erfüllt.

Sondertasten

Ich habe auf meinem Notebook 5 Sondertasten. Drei davon betreffen die Lautstärke, Lauter, Leiser und Stumm. Eine weitere ist als Calculator beschriftet und die letzte als Presentation. Bevor ich kxkb aktiviert hatte, funktionierten zumindest die Lautstärke-Tasten und die Calculator-Taste startete einen Rechner. Mit dem Umschalter taten sie nichts. Es war in dieser Hinsicht also ein Rückschritt. Es müßte doch irgendwie möglich sein, diese Tasten trotz XKB zu benutzen.

Als erstes gilt es herauszufinden, ob X11 mit den Tasten überhaupt irgendwas anfangen kann. Das macht man mit dem Programm xev. In einer Konsole gestartet gibt das Programm alle X-Ereignisse aus. Das kann sehr viel werden, also nicht erschrecken!

Irgendwann hat der Output ein Ende, dann ist es Zeit die Spezialtaste zu drücken und den Output zu betrachten. Bei mir gab das Programm für die Calculator-Taste folgendes aus:

KeyPress event, serial 31, synthetic NO, window 0x4800001,
    root 0xd0, subw 0x0, time 634446041, (-1156,254), root:(247,1051),
    state 0x0, keycode 161 (keysym 0x0, NoSymbol), same_screen YES,
    XLookupString gives 0 bytes:
    XmbLookupString gives 0 bytes:
    XFilterEvent returns: False

KeyRelease event, serial 31, synthetic NO, window 0x4800001,
    root 0xd0, subw 0x0, time 634446048, (-1156,254), root:(247,1051),
    state 0x0, keycode 161 (keysym 0x0, NoSymbol), same_screen YES,
    XLookupString gives 0 bytes:
    XFilterEvent returns: False

Wichtig für uns sind die Werte, die als keycode und als keysym ausgegeben werden. Mit einem Keysym von 0 ist die Taste wirkungslos. Keycode 161 ist die Nummer, die die Hardware für die Taste liefert.

Wir haben jetzt 2 Aufgaben vor uns. Zum einen muß die 161 in so einen <XXXX> Bezeichner für die Taste umgewandelt werden, wie er in den Dateien im Verzeichnis /usr/share/X11/xkb/symbols benutzt wid. Zum Zweiten müssen wir herausfinden, welches Keysym die gewünschte Aktion, nämlich Taschenrechner Starten, auslöst.

Für den ersten Teil sollte ich erstmal einen kurzen Eindruck davon geben, wie die einzelnen Konfigrationsdateien zusammenhängen. Richtige Dokumentation dazu ist schwer zu finden. Lesenswert ist diese Seite.

Als erstes suchen wir in /etc/X11/xorg.conf die Beschreibung der Tastatur. Auf meinem System steht dort folgendes:

Section "InputDevice"
  Driver       "kbd"
  Identifier   "Keyboard[0]"
  Option       "Protocol" "Standard"
  Option       "XkbLayout" "us"
  Option       "XkbModel" "microsoftpro"
  Option       "XkbRules" "xfree86"
EndSection

Interessant sind die Optionen XkbLayout, XkbModel und XkbRules. Letztere Angabe führt uns zu der Datei /usr/share/X11/xkb/rules/xfree86. Sie beschreibt eine Funktion im mathematischen Sinn, die als Parameter Model, Layout, Variant und Options bekommt und als Ergebnis einen Vektor (keycodes, types, compat, symbols, geometry) liefert. In unserem Fall sind Variant und Options nicht gesetzt. Die rules-Datei ist recht komplex. Eine kurze Beschreibung findet sich hier. Schön wäre ein kleines Programm, das eine rules-Datei und die nötigen Parameter als Input zu oben genanntem Vektor als Output verarbeiten würde. Leider habe ich kein solches Programm gefunden.

Lange Rede kurzer Sinn, ich glaube, die von meinem X-Server benutzte keycodes-Datei ist /usr/share/X11/xkb/keycodes/xfree86. Suchen wir doch einfach mal in dieser Datei nach unserem Keycode 161 aus dem Output von xev:

$ grep 161 /usr/share/X11/xkb/keycodes/xfree86
    <I21> =   161;

<I21> sieht doch schon verdammt nach so einem <XXXX> Bezeichner aus, oder?

Also versuchen wir mal, der Taste einen Buchstaben zu entlocken. Dazu füge ich zu meiner opi-Variante des us-Layouts folgende Zeile hinzu:

    key <I21>   { [ a ] };

Nach der Aktivierung mit setxkbmap -model pc104 -layout us -variant opi, gibt die Calculator-Taste plötzlich ein a aus. Auch ein Test mit xev zeigt, daß die Taste jetzt ein Keysym produziert.

KeyPress event, serial 31, synthetic NO, window 0x4200001,
    root 0xd0, subw 0x0, time 708196733, (-722,-25), root:(681,993),
    state 0x0, keycode 161 (keysym 0x61, a), same_screen YES,
    XKeysymToKeycode returns keycode: 38
    XLookupString gives 1 bytes: (61) "a"
    XmbLookupString gives 1 bytes: (61) "a"
    XFilterEvent returns: False

KeyRelease event, serial 31, synthetic NO, window 0x4200001,
    root 0xd0, subw 0x0, time 708196740, (-722,-25), root:(681,993),
    state 0x0, keycode 161 (keysym 0x61, a), same_screen YES,
    XKeysymToKeycode returns keycode: 38
    XLookupString gives 1 bytes: (61) "a"
    XFilterEvent returns: False

Damit wäre Teil 1 der Aufgabe erledigt. Jetzt muß noch ermittelt werden, was wir statt dem a schreiben müssen, damit der Taschenrechner startet.

Auf der Suche nach dem richtigen Keysym

Keysyms sind auch nur Zahlen, d.h. sie müssen irgendwo definiert sein. Auf meinem System gibt es in /usr/include/X11 eine Reihe Dateien mit keysym im Namen:

$ ls -l  /usr/include/X11/*keysym*.h
-rw-r--r-- 1 root root   2298 2007-09-21 23:51 /usr/include/X11/ap_keysym.h
-rw-r--r-- 1 root root   2886 2007-09-21 23:51 /usr/include/X11/DECkeysym.h
-rw-r--r-- 1 root root   6108 2007-09-21 23:51 /usr/include/X11/HPkeysym.h
-rw-r--r-- 1 root root 166043 2007-09-21 23:51 /usr/include/X11/keysymdef.h
-rw-r--r-- 1 root root   2892 2007-09-21 23:51 /usr/include/X11/keysym.h
-rw-r--r-- 1 root root   4095 2007-09-21 23:51 /usr/include/X11/Sunkeysym.h
-rw-r--r-- 1 root root   6403 2007-09-21 23:51 /usr/include/X11/XF86keysym.h

Durchsuchen wir diese doch mal nach dem Wort calculator:

$ grep -i calculator  /usr/include/X11/*keysym*.h
/usr/include/X11/XF86keysym.h:#define XF86XK_Calculator 0x1008FF1D

Fein, doch was können wir damit anfangen? Vielleicht reicht es ja, wenn wir in unserer Variante des us-Layouts das Symbol a in key <I21> { [ a ] }; durch XF86XK_Calculator ersetzen?

So einfach ist es leider nicht. Nach einigem Probieren ersetzte ich jedoch das a durch die Zahl 0x1008FF1D. Und plötzlich ging es. xev lieferte danach dies:

KeyRelease event, serial 31, synthetic NO, window 0x4200001,
    root 0xd0, subw 0x0, time 715284559, (-3,71), root:(1400,1089),
    state 0x0, keycode 161 (keysym 0x1008ff1d, XF86Calculator), same_screen YES,
    XLookupString gives 0 bytes:
    XFilterEvent returns: False

Offensichtlich hat das Symbol doch einen Namen, nämlich XF86Calculator. Also habe ich die Zahl in der Layout-Definition durch den Namen ersetzt und es funktionierte immer noch.

Was können wir daraus lernen?

  1. Man kann als Keysym direkt die Zahl hinschreiben
  2. Man kann danach xev für die Übersetzung in einen Namen benutzen
  3. Der Symbolname entspricht dem Namen in der Header-Datei unter /usr/include/X11 mit der Ausnahme, daß XK_ weggestrichen wird (auch wenn es nicht am Anfang steht). Aus XF86XK_Calculator wurde XF86Calculator

Letztere Beobachtung erwies sich als richtig für alle betrachteten Symbole.

Jetzt hatte ich genug Information, um alle Tasten meiner Tastatur zu belegen. Für die Präsentations-Taste hatte ich keine richtige Verwendung, also belegte ich sie mit dem Mail-Programm.

Das ist nun meine Erweiterung zum us-Layout:

partial alphanumeric_keys
xkb_symbols "opi" {

    include "latin"

    name[Group1]="Opi";

    key <AD02>  { [         w,          W,           ae,           AE ] };
    key <AD03>  { [         e,          E,     EuroSign,     EuroSign ] };
    key <AC01>  { [         a,          A,   adiaeresis,   Adiaeresis ] };
    key <AC07>  { [         j,          J,    downarrow,      uparrow ] };
    key <AD07>  { [         u,          U,   udiaeresis,   Udiaeresis ] };
    key <AD09>  { [         o,          O,   odiaeresis,   Odiaeresis ] };

    key <I21>   { [ XF86Calculator ] };
    key <K6D>   { [ XF86Mail ] };
    key <I20>   { [ XF86AudioMute ] };
    key <I2E>   { [ XF86AudioLowerVolume ] };
    key <I30>   { [ XF86AudioRaiseVolume ] };

    include "level3(ralt_switch)"
};

Und hier ist mein komplettes us-Layout.

Nachtrag zu openSUSE 11.1 mit KDE4

Der Tastatur-Umschalter aus KDE4 funktioniert etwas anders. Er benutzt nicht die Namen der Layout-Varianten aus der Datei /usr/share/X11/xkb/symbols/us, sondern bildet diese nochmal über die Datei /usr/share/X11/xkb/rules/xorg.xml in sprechende Namen ab. Sie enthält im Hauptelement <xkbConfigRegistry> ein Element <layoutList>. Dieses wiederum enthält für jede Layout-Datei in /usr/share/X11/xkb/symbols ein Element <layout>. Innerhalb jedes dieser Elemente gibt es ein Element <configItem> mit einem Unterelement <name>, das den Namen der Layout-Datei enthält. Wir suchen also folgendes Element:

<xkbConfigRegistry>
  ...
  <layoutList>
    <layout>
      <configItem>
        <name>us</name>
        <shortDescription>USA</shortDescription>
        <description>USA</description>
        <languageList><iso639Id>eng</iso639Id></languageList>
      </configItem>
      ...
   </layout>
  ...
</xkbConfigRegistry>

Zu jedem <layout> gibt es neben dem <configItem> eine <variantList>. Jedes Element dieser Liste beschreibt eine Layout-Variante. Wir müssen also ein neues Element in dieser Liste erzeugen:

<xkbConfigRegistry>
  ...
  <layoutList>
    <layout>
      <configItem>
        <name>us</name>
        <shortDescription>USA</shortDescription>
        <description>USA</description>
        <languageList><iso639Id>eng</iso639Id></languageList>
      </configItem>
      <variantList>
      ...
        <variant>
          <configItem>
            <name>opi</name>
            <description>Opi</description>
          </configItem>
        </variant>
      </variantList>
   </layout>
  ...
</xkbConfigRegistry>

Letzte Aktualisierung: 03.01.2009