Last modified: 2024-01-21 14:27
Although the keyboard layout described below should in principle be implementable on any Linux distribution, the implementation details provided are specific to good old distributions like Slackware. Newfangled distributions ship with a heavyweight desktop environment that grabs the modifier keys for its own voluminous list of shortcuts. Figuring out how to make them cooperate is somebody else's job.
Layouts that introduce dead keys or other magical combining behaviors for plain `'"~^ keys are not to my taste. Taking those options off the table, a character such as á can still be entered in several other ways:
But Chrome OS introduced me to extended layouts where the Alt key gets you accents, and I'd like to expand on that.
The use case is editing Latin-1 text in Emacs. Both the X keyboard extension (XKB) and Emacs itself get involved in the interpretation of modifier keys; a good solution requires cooperation between the two.
Control and Meta belong to Emacs. Alt is free except for a few combinations that are captured by the X server or window manager.
Muscle memory cannot be argued with. In Emacs, I use the left Control key for all C- combinations and I use Escape for all M- combinations. I literally never touch the right Control key or use Alt as a substitute for the nonexistent Meta key.
XKB already has the layouts us(dvorak-intl) "English (Dvorak, international with dead keys)" and us(dvorak-alt-intl) "English (Dvorak alternative international no dead keys)." Neither one is adequate.
Emacs already has the input methods latin-prefix, latin-1-prefix, latin-1-postfix, and latin-1-alt-postfix. All of them introduce magical behaviors for `'"~^.
C | S | A | A | S | H | M | |
---|---|---|---|---|---|---|---|
Ctrl | Super | Alt | Space | Alt | Super | Hyper | Compose Meta |
The right Control key is used for both Meta (unshifted) and Compose (shifted).
Some cheap keyboards omit the right Win (Super) key. No umlauts for you!
Key | A | S | H | AS | AH | SH | ASH |
---|---|---|---|---|---|---|---|
1! | ¼¡ | ¹ | |||||
2@ | ½ | ² | |||||
3# | ¾ | ³ | |||||
'" | ´¨ | ||||||
,< | ¸« | ||||||
.> | ·» | ||||||
pP | ¶ | ||||||
yY | ýÝ | ÿ | ¥ | ||||
aA | áÁ | äÄ | àÀ | â | ãà | åÅ | ª |
oO | óÓ | öÖ | òÒ | ôÔ | õÕ | øØ | º |
eE | éÉ | ëË | èÈ | êÊ | æÆ | ||
uU | úÚ | üÜ | ùÙ | ûÛ | |||
iI | íÍ | ïÏ | ìÌ | îÎ | |||
xX | × |
Key | A | S | AS |
---|---|---|---|
cC | çÇ | © | ¢¤ |
rR | ® | ||
lL | £ | ||
/? | ÷¿ | ||
=+ | ± | ||
\| | ¬¦ | ||
dD | ðÐ | ° | |
tT | þÞ | ||
nN | ñÑ | ||
sS | ß | § | |
-_ | SHY¯ | ||
mM | µ | ||
SP | NBSP |
See /usr/share/X11/locale/iso8859-1/Compose for alternative, equivalent compose sequences.
Code | Character | Modal | Compose |
---|---|---|---|
a0 | NBSP | A-SP | SP SP |
a1 | ¡ | A-! | !! |
a2 | ¢ | A-S-c | /c |
a3 | £ | A-l | -l |
a4 | ¤ | A-S-C | ox |
a5 | ¥ | H-y | =y |
a6 | ¦ | A-| | || |
a7 | § | S-s | s! |
a8 | ¨ | A-" | "" |
a9 | © | S-c | oc |
aa | ª | A-S-H-a | _a |
ab | « | A-< | << |
ac | ¬ | A-\ | -, |
ad | SHY | A-- | -- |
ae | ® | A-r | OR |
af | ¯ | A-_ | ^- |
b0 | ° | S-d | ^0 |
b1 | ± | A-= | +- |
b2 | ² | S-2 | ^2 |
b3 | ³ | S-3 | ^3 |
b4 | ´ | A-' | '' |
b5 | µ | A-m | /u |
b6 | ¶ | A-p | p! |
b7 | · | A-. | .^ |
b8 | ¸ | A-, | ,, |
b9 | ¹ | S-1 | ^1 |
ba | º | A-S-H-o | _o |
bb | » | A-> | >> |
bc | ¼ | A-1 | 14 |
bd | ½ | A-2 | 12 |
be | ¾ | A-3 | 34 |
bf | ¿ | A-? | ?? |
c0 | À | H-A | `A |
c1 | Á | A-A | 'A |
c2 | Â | A-S-A | ^A |
c3 | Ã | A-H-A | ~A |
c4 | Ä | S-A | "A |
c5 | Å | S-H-A | AA |
c6 | Æ | A-H-E | AE |
c7 | Ç | A-C | ,C |
c8 | È | H-E | `E |
c9 | É | A-E | 'E |
ca | Ê | A-S-E | ^E |
cb | Ë | S-E | "E |
cc | Ì | H-I | `I |
cd | Í | A-I | 'I |
ce | Î | A-S-I | ^I |
cf | Ï | S-I | "I |
d0 | Ð | A-D | -D |
d1 | Ñ | A-N | ~N |
d2 | Ò | H-O | `O |
d3 | Ó | A-O | 'O |
d4 | Ô | A-S-O | ^O |
d5 | Õ | A-H-O | ~O |
d6 | Ö | S-O | "O |
d7 | × | A-x | xx |
d8 | Ø | S-H-O | /O |
d9 | Ù | H-U | `U |
da | Ú | A-U | 'U |
db | Û | A-S-U | ^U |
dc | Ü | S-U | "U |
dd | Ý | A-Y | 'Y |
de | Þ | A-T | TH |
df | ß | A-s | ss |
e0 | à | H-a | `a |
e1 | á | A-a | 'a |
e2 | â | A-S-a | ^a |
e3 | ã | A-H-a | ~a |
e4 | ä | S-a | "a |
e5 | å | S-H-a | aa |
e6 | æ | A-H-e | ae |
e7 | ç | A-c | ,c |
e8 | è | H-e | `e |
e9 | é | A-e | 'e |
ea | ê | A-S-e | ^e |
eb | ë | S-e | "e |
ec | ì | H-i | `i |
ed | í | A-i | 'i |
ee | î | A-S-i | ^i |
ef | ï | S-i | "i |
f0 | ð | A-d | -d |
f1 | ñ | A-n | ~n |
f2 | ò | H-o | `o |
f3 | ó | A-o | 'o |
f4 | ô | A-S-o | ^o |
f5 | õ | A-H-o | ~o |
f6 | ö | S-o | "o |
f7 | ÷ | A-/ | -: |
f8 | ø | S-H-o | /o |
f9 | ù | H-u | `u |
fa | ú | A-u | 'u |
fb | û | A-S-u | ^u |
fc | ü | S-u | "u |
fd | ý | A-y | 'y |
fe | þ | A-t | th |
ff | ÿ | S-y | "y |
In theory, the new mappings could be implemented entirely in XKB. In practice, I found XKB to be overcomplicated, poorly documented, and impenetrable. Therefore, I have made the smallest change that suffices to get XKB out of the way, disregarding collateral damage to XKB's intricate system of rules and templates.
XKB's config files reside in /etc/X11/xkb. Which ones are relevant is
influenced by the values of XkbRules, XkbModel, XkbLayout, XkbVariant, and
XkbOptions that may be set in /etc/X11/xorg.conf, but XKB's rules and
defaults have the final say. The best clue about what XKB is actually doing
is the output of setxkbmap -print
.
bash-4.3$ fgrep -i xkb /etc/X11/xorg.conf Option "XkbModel" "pc104" Option "XkbLayout" "us" Option "XkbVariant" "dvorak" bash-4.3$ setxkbmap -print xkb_keymap { xkb_keycodes { include "evdev+aliases(qwerty)" }; xkb_types { include "complete" }; xkb_compat { include "complete" }; xkb_symbols { include "pc+us(dvorak)+inet(evdev)+terminate(ctrl_alt_bksp)" }; xkb_geometry { include "pc(pc104)" }; };
The link that I chose to break is the definition pc105 in the file symbols/pc. "But your keyboard model is pc104!" Yes, but pc105 is what XKB is using from that file.
Before:
key <LFSH> { [ Shift_L ] }; key <LCTL> { [ Control_L ] }; key <LWIN> { [ Super_L ] }; key <RTSH> { [ Shift_R ] }; key <RCTL> { [ Control_R ] }; key <RWIN> { [ Super_R ] }; key <MENU> { [ Menu ] }; // Beginning of modifier mappings. modifier_map Shift { Shift_L, Shift_R }; modifier_map Lock { Caps_Lock }; modifier_map Control{ Control_L, Control_R }; modifier_map Mod2 { Num_Lock }; modifier_map Mod4 { Super_L, Super_R }; // Fake keys for virtual<->real modifiers mapping: key <LVL3> { [ ISO_Level3_Shift ] }; key <MDSW> { [ Mode_switch ] }; modifier_map Mod5 { <LVL3>, <MDSW> }; key <ALT> { [ NoSymbol, Alt_L ] }; include "altwin(meta_alt)" key <META> { [ NoSymbol, Meta_L ] }; modifier_map Mod1 { <META> }; key <SUPR> { [ NoSymbol, Super_L ] }; modifier_map Mod4 { <SUPR> }; key <HYPR> { [ NoSymbol, Hyper_L ] }; modifier_map Mod4 { <HYPR> }; // End of modifier mappings.
"altwin(meta_alt)"
maps Alt to Meta in a way that is hard to
undo. For A- and M- to work as desired in Emacs, XKB has to establish Alt
and Meta as distinct modifiers with appropriate keysyms assigned.
After:
key <LFSH> { [ Shift_L ] }; key <LCTL> { [ Control_L ] }; key <LWIN> { [ Super_L ] }; key <LALT> { [ Alt_L ] }; key <RTSH> { [ Shift_R ] }; key <RALT> { [ Alt_R ] }; key <RWIN> { [ Super_R ] }; key <MENU> { [ Hyper_R ] }; key <RCTL> {type[Group1]="TWO_LEVEL", [Meta_R, Multi_key]}; modifier_map Shift { Shift_L, Shift_R }; modifier_map Lock { Caps_Lock }; modifier_map Control{ Control_L }; modifier_map Mod1 { Alt_L, Alt_R }; modifier_map Mod2 { Num_Lock }; modifier_map Mod3 { Meta_R }; modifier_map Mod4 { Super_L, Super_R }; modifier_map Mod5 { Hyper_R };
With the modifier map established by XKB, the character mapping can be done in an Emacs init file.
The code below uses literal characters instead of integers or symbolic names for the characters. The soft hyphen (SHY) and nonbreaking space (NBSP) characters are not self-evident in a web browser.
; -*- coding: iso-8859-1 -*- (global-set-key [(alt ? )] '(lambda () (interactive) (insert-char ? ))) (global-set-key [(alt ?!)] '(lambda () (interactive) (insert-char ?¡))) (global-set-key [(alt super ?c)] '(lambda () (interactive) (insert-char ?¢))) (global-set-key [(alt ?l)] '(lambda () (interactive) (insert-char ?£))) (global-set-key [(alt super ?C)] '(lambda () (interactive) (insert-char ?¤))) (global-set-key [(hyper ?y)] '(lambda () (interactive) (insert-char ?¥))) (global-set-key [(alt ?|)] '(lambda () (interactive) (insert-char ?¦))) (global-set-key [(super ?s)] '(lambda () (interactive) (insert-char ?§))) (global-set-key [(alt ?\")] '(lambda () (interactive) (insert-char ?¨))) (global-set-key [(super ?c)] '(lambda () (interactive) (insert-char ?©))) (global-set-key [(alt super hyper ?a)] '(lambda () (interactive) (insert-char ?ª))) (global-set-key [(alt ?<)] '(lambda () (interactive) (insert-char ?«))) (global-set-key [(alt ?\\)] '(lambda () (interactive) (insert-char ?¬))) (global-set-key [(alt ?-)] '(lambda () (interactive) (insert-char ?))) (global-set-key [(alt ?r)] '(lambda () (interactive) (insert-char ?®))) (global-set-key [(alt ?_)] '(lambda () (interactive) (insert-char ?¯))) (global-set-key [(super ?d)] '(lambda () (interactive) (insert-char ?°))) (global-set-key [(alt ?=)] '(lambda () (interactive) (insert-char ?±))) (global-set-key [(super ?2)] '(lambda () (interactive) (insert-char ?²))) (global-set-key [(super ?3)] '(lambda () (interactive) (insert-char ?³))) (global-set-key [(alt ?')] '(lambda () (interactive) (insert-char ?´))) (global-set-key [(alt ?m)] '(lambda () (interactive) (insert-char ?µ))) (global-set-key [(alt ?p)] '(lambda () (interactive) (insert-char ?¶))) (global-set-key [(alt ?.)] '(lambda () (interactive) (insert-char ?·))) (global-set-key [(alt ?,)] '(lambda () (interactive) (insert-char ?¸))) (global-set-key [(super ?1)] '(lambda () (interactive) (insert-char ?¹))) (global-set-key [(alt super hyper ?o)] '(lambda () (interactive) (insert-char ?º))) (global-set-key [(alt ?>)] '(lambda () (interactive) (insert-char ?»))) (global-set-key [(alt ?1)] '(lambda () (interactive) (insert-char ?¼))) (global-set-key [(alt ?2)] '(lambda () (interactive) (insert-char ?½))) (global-set-key [(alt ?3)] '(lambda () (interactive) (insert-char ?¾))) (global-set-key [(alt ??)] '(lambda () (interactive) (insert-char ?¿))) (global-set-key [(hyper ?A)] '(lambda () (interactive) (insert-char ?À))) (global-set-key [(alt ?A)] '(lambda () (interactive) (insert-char ?Á))) (global-set-key [(alt super ?A)] '(lambda () (interactive) (insert-char ?Â))) (global-set-key [(alt hyper ?A)] '(lambda () (interactive) (insert-char ?Ã))) (global-set-key [(super ?A)] '(lambda () (interactive) (insert-char ?Ä))) (global-set-key [(super hyper ?A)] '(lambda () (interactive) (insert-char ?Å))) (global-set-key [(alt hyper ?E)] '(lambda () (interactive) (insert-char ?Æ))) (global-set-key [(alt ?C)] '(lambda () (interactive) (insert-char ?Ç))) (global-set-key [(hyper ?E)] '(lambda () (interactive) (insert-char ?È))) (global-set-key [(alt ?E)] '(lambda () (interactive) (insert-char ?É))) (global-set-key [(alt super ?E)] '(lambda () (interactive) (insert-char ?Ê))) (global-set-key [(super ?E)] '(lambda () (interactive) (insert-char ?Ë))) (global-set-key [(hyper ?I)] '(lambda () (interactive) (insert-char ?Ì))) (global-set-key [(alt ?I)] '(lambda () (interactive) (insert-char ?Í))) (global-set-key [(alt super ?I)] '(lambda () (interactive) (insert-char ?Î))) (global-set-key [(super ?I)] '(lambda () (interactive) (insert-char ?Ï))) (global-set-key [(alt ?D)] '(lambda () (interactive) (insert-char ?Ð))) (global-set-key [(alt ?N)] '(lambda () (interactive) (insert-char ?Ñ))) (global-set-key [(hyper ?O)] '(lambda () (interactive) (insert-char ?Ò))) (global-set-key [(alt ?O)] '(lambda () (interactive) (insert-char ?Ó))) (global-set-key [(alt super ?O)] '(lambda () (interactive) (insert-char ?Ô))) (global-set-key [(alt hyper ?O)] '(lambda () (interactive) (insert-char ?Õ))) (global-set-key [(super ?O)] '(lambda () (interactive) (insert-char ?Ö))) (global-set-key [(alt ?x)] '(lambda () (interactive) (insert-char ?×))) (global-set-key [(super hyper ?O)] '(lambda () (interactive) (insert-char ?Ø))) (global-set-key [(hyper ?U)] '(lambda () (interactive) (insert-char ?Ù))) (global-set-key [(alt ?U)] '(lambda () (interactive) (insert-char ?Ú))) (global-set-key [(alt super ?U)] '(lambda () (interactive) (insert-char ?Û))) (global-set-key [(super ?U)] '(lambda () (interactive) (insert-char ?Ü))) (global-set-key [(alt ?Y)] '(lambda () (interactive) (insert-char ?Ý))) (global-set-key [(alt ?T)] '(lambda () (interactive) (insert-char ?Þ))) (global-set-key [(alt ?s)] '(lambda () (interactive) (insert-char ?ß))) (global-set-key [(hyper ?a)] '(lambda () (interactive) (insert-char ?à))) (global-set-key [(alt ?a)] '(lambda () (interactive) (insert-char ?á))) (global-set-key [(alt super ?a)] '(lambda () (interactive) (insert-char ?â))) (global-set-key [(alt hyper ?a)] '(lambda () (interactive) (insert-char ?ã))) (global-set-key [(super ?a)] '(lambda () (interactive) (insert-char ?ä))) (global-set-key [(super hyper ?a)] '(lambda () (interactive) (insert-char ?å))) (global-set-key [(alt hyper ?e)] '(lambda () (interactive) (insert-char ?æ))) (global-set-key [(alt ?c)] '(lambda () (interactive) (insert-char ?ç))) (global-set-key [(hyper ?e)] '(lambda () (interactive) (insert-char ?è))) (global-set-key [(alt ?e)] '(lambda () (interactive) (insert-char ?é))) (global-set-key [(alt super ?e)] '(lambda () (interactive) (insert-char ?ê))) (global-set-key [(super ?e)] '(lambda () (interactive) (insert-char ?ë))) (global-set-key [(hyper ?i)] '(lambda () (interactive) (insert-char ?ì))) (global-set-key [(alt ?i)] '(lambda () (interactive) (insert-char ?í))) (global-set-key [(alt super ?i)] '(lambda () (interactive) (insert-char ?î))) (global-set-key [(super ?i)] '(lambda () (interactive) (insert-char ?ï))) (global-set-key [(alt ?d)] '(lambda () (interactive) (insert-char ?ð))) (global-set-key [(alt ?n)] '(lambda () (interactive) (insert-char ?ñ))) (global-set-key [(hyper ?o)] '(lambda () (interactive) (insert-char ?ò))) (global-set-key [(alt ?o)] '(lambda () (interactive) (insert-char ?ó))) (global-set-key [(alt super ?o)] '(lambda () (interactive) (insert-char ?ô))) (global-set-key [(alt hyper ?o)] '(lambda () (interactive) (insert-char ?õ))) (global-set-key [(super ?o)] '(lambda () (interactive) (insert-char ?ö))) (global-set-key [(alt ?/)] '(lambda () (interactive) (insert-char ?÷))) (global-set-key [(super hyper ?o)] '(lambda () (interactive) (insert-char ?ø))) (global-set-key [(hyper ?u)] '(lambda () (interactive) (insert-char ?ù))) (global-set-key [(alt ?u)] '(lambda () (interactive) (insert-char ?ú))) (global-set-key [(alt super ?u)] '(lambda () (interactive) (insert-char ?û))) (global-set-key [(super ?u)] '(lambda () (interactive) (insert-char ?ü))) (global-set-key [(alt ?y)] '(lambda () (interactive) (insert-char ?ý))) (global-set-key [(alt ?t)] '(lambda () (interactive) (insert-char ?þ))) (global-set-key [(super ?y)] '(lambda () (interactive) (insert-char ?ÿ)))
Emacs deliberately ignores Caps Lock when other mod keys are down, so you get SIMóN BOLíVAR when you expected SIMÓN BOLÍVAR. Changing this behavior requires patching the C source.
xemacs-21.5.32: file src/event-Xt.c, function x_event_to_emacs_event
"Ignore the Caps_Lock key if:" ...
emacs-26.3: file src/keyboard.c, function make_lispy_event
"Caps-lock shouldn't affect interpretation of key chords:" ...