diff options
Diffstat (limited to 'public/kbd-xmodmap.html')
-rw-r--r-- | public/kbd-xmodmap.html | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/public/kbd-xmodmap.html b/public/kbd-xmodmap.html new file mode 100644 index 0000000..fdede30 --- /dev/null +++ b/public/kbd-xmodmap.html @@ -0,0 +1,239 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>GNU/Linux Keyboard Maps: xmodmap — Luke T. Shumaker</title> + <link rel="stylesheet" href="assets/style.css"> + <link rel="alternate" type="application/atom+xml" href="./index.atom" name="web log entries"/> +</head> +<body> +<header><a href="/">Luke T. Shumaker</a> » <a href=/blog>blog</a> » kbd-xmodmap</header> +<article> +<h1 id="gnulinux-keyboard-maps-xmodmap">GNU/Linux Keyboard Maps: +xmodmap</h1> +<p>The modmap subsystem is part of the core <a +href="https://www.x.org/releases/current/doc/xproto/x11protocol.html">X11 +protocol</a>. However, it has been replaced by the <a +href="https://www.x.org/releases/current/doc/kbproto/xkbproto.html">X +Keyboard (XKB) Extension</a> to the protocol, which defines a facade +that emulates the legacy modmap subsystem so that old programs still +work—including those that manipulate the modmap directly!</p> +<p>For people who like to Keep It Stupid Simple, the XKB extension looks +horribly complicated and gross—even ignoring protocol details, the +configuration syntax is a monstrosity! There’s no way to say something +like “I’d like to remap Caps-Lock to be Control”, you have to copy and +edit the entire keyboard definition, which includes mucking with vector +graphics of the physical keyboard layout! So it’s very tempting to +pretend that XKB doesn’t exist, and it’s still using modmap.</p> +<p>However, this is a leaky abstraction; for instance: when running the +<code>xmodmap</code> command to manipulate the modmap, if you have +multiple keyboards plugged in, the result can depend on which keyboard +you used to press “enter” after typing the command!</p> +<p>Despite only existing as a compatibility shim today, I think it is +important to understand the modmap subsystem to understand modern +XKB.</p> +<h2 id="conceptual-overview">Conceptual overview</h2> +<p>There are 3 fundamental tasks that the modmap subsystem performs:</p> +<ol type="1"> +<li><code>keyboard: map keycode -> keysym</code> +(client-side)</li> +<li><code>keyboard: map keycode -> modifier bitmask</code> +(server-side)</li> +<li><code>pointer: map physical button -> logical button</code> +(server-side)</li> +</ol> +<p>You’re thinking: “Great, so the X server does these things for us!” +Nope! Not entirely, anyway. It does the keycode->modifier lookup, and +the mouse-button lookup, but the keycode->keysym lookup must be done +client-side by querying the mapping stored on the server. Generally, +this is done automatically inside of libX11/libxcb, and the actual +client application code doesn’t need to worry about it.</p> +<p>So, what’s the difference between a keycode and a keysym, and how’s +the modifier bitmask work?</p> +<ul> +<li><p>keycode: A numeric ID for a hardware button; this is as close the +the hardware as X11 modmaps let us get. These are conceptually identical +to Linux kernel keycodes, but the numbers don’t match up. Xorg keycodes +are typically <code>linux_keycode+8</code>.</p></li> +<li><p>keysym: A 29-bit integer code that is meaningful to applications. +A mapping of these to symbolic names is defined in +<code><X11/keysymdef.h></code> and augmented by +<code>/usr/share/X11/XKeysymDB</code>. See: +<code>XStringToKeysym()</code> and <code>XKeysymToString()</code>. We +will generally use the symbolic name in the modmap file. The symbolic +names are case-sensitive.</p></li> +<li><p>Modifier state: An 8-bit bitmask of modifier keys (names are +case-insensitive):</p> +<pre><code>1 << 0 : shift +1 << 1 : lock +1 << 2 : control +1 << 3 : mod1 +1 << 4 : mod2 +1 << 5 : mod3 +1 << 6 : mod4 +1 << 7 : mod5</code></pre></li> +</ul> +<p>With that knowledge, and the libX11/libxcb API docs, you can probably +figure out how to interact with the modmap subsystem from C, but who +does that? Everyone just uses the <code>xmodmap(1)</code> command.</p> +<h2 id="the-x11-protocol">The X11 protocol</h2> +<p>As I said, the modifier and button lookup is handled server-side; +each of the <a +href="https://www.x.org/releases/current/doc/xproto/x11protocol.html#events:input">input +events</a> ({Key,Button}{Press,Release}, and MotionNotify) and <a +href="https://www.x.org/releases/current/doc/xproto/x11protocol.html#events:pointer_window">pointer +window events</a> ({Enter,Leave}Notify) include a bitmask of active +keyboard modifiers and pointer buttons. Each are given an 8-bit +bitmask—hence 8 key modifiers. For some reason, only up to Button5 is +included in the bitmask; the upper 3 bits are always zero; but the +Button{Press,Release} events will happily deliver events for up to +Button255!</p> +<p>The X11 protocol has 6 request types for dealing with these 3 +mappings; an accessor and a mutator pair for each. Since the 2 of the +mappings are done server-side, of these, most clients will only use +GetKeyboardMapping. Anyway, let’s look at those 6 requests, grouped by +the mappings that they work with (pardon the Java-like pseudo-code +syntax for indicating logical argument and return types):</p> +<ol type="1"> +<li><p><code>keyboard: map keycode -> keysym</code></p> +<ul> +<li><a +href="https://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:GetKeyboardMapping">GetKeyboardMapping</a> +:: +<code>List<keycode> -> Map<keycode,List<keysym>></code></li> +<li><a +href="https://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:ChangeKeyboardMapping">ChangeKeyboardMapping</a> +:: <code>Map<keycode,List<keysym>> -> ()</code></li> +</ul> +<p><code>GetKeyboardMapping</code> returns the keycode->keysym +mappings for the requested keycodes; this way clients can choose to look +up only the keycodes that they need to handle (the ones that got sent to +them). Each keycode gets a list of keysyms; which keysym they should use +from that list depends on which modifiers are pressed. +<code>ChangeKeyboardMapping</code> changes the mapping for the given +keycodes; not all keycodes must be given, any keycodes that aren’t +included in the request aren’t changed.</p></li> +<li><p><code>keyboard: map keycode -> modifier bitmask</code></p> +<ul> +<li><a +href="https://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:GetModifierMapping">GetModifierMapping</a> +:: <code>() -> Map<modifier,List<keycode>></code></li> +<li><a +href="https://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:SetModifierMapping">SetModifierMapping</a> +:: <code>Map<modifier,List<keycode>> -> ()</code></li> +</ul> +<p>The modifiers mapping is a lot smaller than the keysym mapping; you +must operate on the entire mapping at once. For each modifier bit, +there’s a list of keycodes that will cause that modifier bit to be +flipped in the events that are delivered while it is pressed.</p></li> +<li><p><code>pointer: map physical button -> logical button</code></p> +<ul> +<li><a +href="https://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:GetPointerMapping">GetPointerMapping</a> +<code>() -> List<logicalButton></code> (indexed by +<code>physicalButton-1</code>)</li> +<li><a +href="https://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:SetPointerMapping">SetPointerMapping</a> +<code>List<logicalButton> -> ()</code> (indexed by +<code>physicalButton-1</code>)</li> +</ul> +<p>Like the modifier mapping, the button mapping is expected to be +small, most mice only have 5-7 buttons (left, middle, right, scroll up, +scroll down, scroll left, scroll right—that’s right, X11 handles scroll +events as button presses), though some fancy gaming mice have more than +that, but not much more.</p></li> +</ol> +<p>I mentioned earlier that the keycode->keysym mapping isn’t +actually done by the X server, and is done in the client; whenever a +client receives a key event or pointer button event, it must do a +<code>Get*Mapping</code> request to see what that translates to. Of +course, doing a that for every keystroke would be crazy; but at the same +time, the each client is expected to know about changes to the mappings +that happen at run-time. So, each of the “set”/“change” commands +generate a <a +href="https://www.x.org/releases/current/doc/xproto/x11protocol.html#events:MappingNotify">MappingNotify</a> +event that is sent to all clients, so they know when they must dump +their cache of mappings.</p> +<p>For completeness, if you are looking at this as background for +understanding XKB, I should also mention:</p> +<ul> +<li><a +href="https://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:GetKeyboardControl">GetKeyboardControl</a></li> +<li><a +href="https://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:ChangeKeyboardControl">ChangeKeyboardControl</a></li> +<li><a +href="https://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:GetPointerControl">GetPointerControl</a></li> +<li><a +href="https://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:ChangePointerControl">ChangePointerControl</a></li> +</ul> +<h2 id="the-xmodmap-command">The <code>xmodmap</code> command</h2> +<p>The <code>xmodmap</code> command reads a configuration file and +modifies the maps in the X server to match. The <code>xmodmap</code> +config file has its own little quirky syntax. For one, the comment +character is <code>!</code> (and comments may only start at the +<em>beginning</em> of the line, but that’s fairly common).</p> +<p>There are 8 commands that <code>xmodmap</code> recognizes. Let’s look +at those, grouped by the 3 tasks that the modmap subsystem performs:</p> +<ol type="1"> +<li><p><code>keyboard: map keycode -> keysym</code></p> +<ul> +<li><p><code>keycode KEYCODE = PLAIN [SHIFT [MODE_SWITCH [MODE_SWITCH+SHIFT ]]]</code></p> +<p>Actually takes a list of up to 8 keysyms, but only the first 4 have +standard uses.</p></li> +<li><p><code>keysym OLD_KEYSYM = NEW_KEYSYMS...</code></p> +<p>Takes the keycodes mapped to <code>OLD_KEYSYM</code> and maps them to +<code>NEW_KEYSYM</code>.</p></li> +<li><p><code>keysym any = KEYSYMS...</code></p> +<p>Finds an otherwise unused keycode, and has it map to the specified +keysyms.</p></li> +</ul></li> +<li><p><code>keyboard: map keycode -> modifier bitmask</code></p> +<ul> +<li><code>clear MODIFIER</code></li> +<li><code>add MODIFIERNAME = KEYSYMS...</code></li> +<li><code>remove MODIFIERNAME = KEYSYMS...</code></li> +</ul> +<p>Wait, the modmap subsystem maps <em>keycodes</em> to modifiers, but +the commands take <em>keysyms</em>? Yup! When executing one of these +commands, it first looks up those keysyms in the keyboard map to +translate them in to a set of keycodes, then associates those keycodes +with that modifier. But how does it look up keysym->keycode; the +protocol only supports querying keycode->keysym? It <a +href="https://cgit.freedesktop.org/xorg/app/xmodmap/tree/handle.c?h=xmodmap-1.0.9#n59">loops</a> +over <em>every</em> keycode finding all the matches.</p></li> +<li><p><code>pointer: map physical button -> logical button</code></p> +<ul> +<li><p><code>pointer = default</code></p> +<p>This is equivalent to <code>pointer = 1 2 3 4 5 6...</code> where the +list is as long as the number of buttons that there are.</p></li> +<li><p><code>pointer = NUMBERS...</code></p> +<p><code>pointer = A B C D...</code> sets the physical button 1 to +logical button A, physical button 2 to logical button B, and so on. +Setting a physical button to logical button 0 disables that +button.</p></li> +</ul></li> +</ol> +<h2 id="appendix">Appendix:</h2> +<p>I use this snippet in my Emacs configuration to make editing xmodmap +files nicer:</p> +<pre><code>;; http://www.emacswiki.org/emacs/XModMapMode +(when (not (fboundp 'xmodmap-mode)) + (define-generic-mode 'xmodmap-mode + '(?!) + '("add" "clear" "keycode" "keysym" "pointer" "remove") + nil + '("[xX]modmap\\(rc\\)?\\'") + nil + "Simple mode for xmodmap files."))</code></pre> + +</article> +<footer> + <aside class="sponsor"><p>I'd love it if you <a class="em" + href="/sponsor/">sponsored me</a>. It will allow me to continue + my work on the GNU/Linux ecosystem. Thanks!</p></aside> + +<p>The content of this page is Copyright © 2018 <a href="mailto:lukeshu@lukeshu.com">Luke T. Shumaker</a>.</p> +<p>This page is licensed under the <a href="https://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0</a> license.</p> +</footer> +</body> +</html> |