summaryrefslogtreecommitdiff
path: root/public/kbd-xmodmap.html
blob: fdede303a2bf803041c47ffec58d42010a34f311 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
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         -&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
      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>