summaryrefslogtreecommitdiff
path: root/public/kbd-xmodmap.html
diff options
context:
space:
mode:
Diffstat (limited to 'public/kbd-xmodmap.html')
-rw-r--r--public/kbd-xmodmap.html241
1 files changed, 241 insertions, 0 deletions
diff --git a/public/kbd-xmodmap.html b/public/kbd-xmodmap.html
new file mode 100644
index 0000000..985d55f
--- /dev/null
+++ b/public/kbd-xmodmap.html
@@ -0,0 +1,241 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>GNU/Linux Keyboard Maps: xmodmap — Luke T. Shumaker</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <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 -&gt; keysym</code>
+(client-side)</li>
+<li><code>keyboard: map keycode -&gt; modifier bitmask</code>
+(server-side)</li>
+<li><code>pointer: map physical button -&gt; 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-&gt;modifier lookup, and
+the mouse-button lookup, but the keycode-&gt;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>&lt;X11/keysymdef.h&gt;</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 &lt;&lt; 0 : shift
+1 &lt;&lt; 1 : lock
+1 &lt;&lt; 2 : control
+1 &lt;&lt; 3 : mod1
+1 &lt;&lt; 4 : mod2
+1 &lt;&lt; 5 : mod3
+1 &lt;&lt; 6 : mod4
+1 &lt;&lt; 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 -&gt; keysym</code></p>
+<ul>
+<li><a
+href="https://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:GetKeyboardMapping">GetKeyboardMapping</a>
+::
+<code>List&lt;keycode&gt; -&gt; Map&lt;keycode,List&lt;keysym&gt;&gt;</code></li>
+<li><a
+href="https://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:ChangeKeyboardMapping">ChangeKeyboardMapping</a>
+:: <code>Map&lt;keycode,List&lt;keysym&gt;&gt; -&gt; ()</code></li>
+</ul>
+<p><code>GetKeyboardMapping</code> returns the keycode-&gt;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 -&gt; modifier bitmask</code></p>
+<ul>
+<li><a
+href="https://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:GetModifierMapping">GetModifierMapping</a>
+:: <code>() -&gt; Map&lt;modifier,List&lt;keycode&gt;&gt;</code></li>
+<li><a
+href="https://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:SetModifierMapping">SetModifierMapping</a>
+:: <code>Map&lt;modifier,List&lt;keycode&gt;&gt; -&gt; ()</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 -&gt; logical button</code></p>
+<ul>
+<li><a
+href="https://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:GetPointerMapping">GetPointerMapping</a>
+<code>() -&gt; List&lt;logicalButton&gt;</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&lt;logicalButton&gt; -&gt; ()</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-&gt;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 -&gt; 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 -&gt; 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-&gt;keycode; the
+protocol only supports querying keycode-&gt;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 -&gt; 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 &#39;xmodmap-mode))
+ (define-generic-mode &#39;xmodmap-mode
+ &#39;(?!)
+ &#39;(&quot;add&quot; &quot;clear&quot; &quot;keycode&quot; &quot;keysym&quot; &quot;pointer&quot; &quot;remove&quot;)
+ nil
+ &#39;(&quot;[xX]modmap\\(rc\\)?\\&#39;&quot;)
+ nil
+ &quot;Simple mode for xmodmap files.&quot;))</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
+ <a class="em" href="/imworkingon/">my work</a> 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>