<p>I didn't like fact that UCW layout is in fact just an overlay and not a
"proper" level 3, so I started studying how to create a custom keyboard layout.
Few notable resources: <aclass="reference external"href="TODO">XKB page on ArchWiki</a>, manpages for
<aclass="reference external"href="TODO">xkeyboard-config(7)</a>, <aclass="reference external"href="TODO">setxkbmap(1)</a> and <aclass="reference external"href="TODO">xkbcomp(1)</a> and looking into
<ttclass="docutils literal">/usr/share/X11/xkb/symbols/cz</tt> (found by grepping <ttclass="docutils literal">/usr/share/X11</tt> for
"ucw").</p>
<p>Another reason for creating my own layouts is various tweaks esp. on laptop
keyboards. For example, the laptop I am typing this sentence on has broken the
up arrow key, so I would like to map it somewhere. I use <ttclass="docutils literal">xmodmap</tt> for that,
but if this could be contained in a single layout, it would make stuff simpler
for me. (And as I will later learn, <ttclass="docutils literal">xmodmap</tt><aclass="reference external"href="TODO">does not work well</a> with xkb
layouts…)</p>
<p>Also, I have another tweak on my keyboards: Compose key. This really calls for
the custom layout! And when at it, I should create one for the ttys as well…</p>
<p>But this had quite a low priority (the <ttclass="docutils literal">kbd.sh</tt> script in <em>all</em> my homes does
the job well enough), so I didn't get to it in time. Luckily, maybe – if I were
quicker I would not end up in this rabbit hole. At this point, I knew the basic
stuff and was reasonably sure that I could hack it together, at least somehow;
what was stopping me was lack of time and not being sure how I want to manage
machine-specific tweaks in two different layout syntaxes (xkb for X11 and kbd
<p>… and he had a problem and asked me if I would have a look into it. Apparently,
<aclass="reference external"href="TODO">group switching didn't work in XWayland</a>. At first, I wanted to work it around
by finally creating the Unified UCW keymap™, but I wanted to learn more about
XKB (from TODO GUIDES), which took me quite long, again. (Studying, working,
helping with camps <em>and</em> fixing layouts takes some time…)</p>
<p>Also, since the issue is apparently bigger, solving all group switches would be
a better and more useful solution than just hacking up the Unified UCW keymap
(even though I want my tweaks to eventually be xkb-based anyway).</p>
<p>I had very little experience with Wayland until then, mostly because X11 worked
for me rather well. I tried running Sway few times before, but usually quickly
reverted to using Xorg for various reasons (Nvidia GPU in laptop, very laggy
mouse, maybe <em>this exact</em> issue with UCW layout) which I didn't feel like
solving at that moment.</p>
<p>I wanted to help my friend, though, so I started looking more into using Sway
on another laptop and reading about internals of input system on <aclass="reference external"href="TODO">Peter
Hutterer's blog</a> TODO NAME SPELLING? Occasionally I would randomly google
(<aclass="reference external"href="TODO">with DuckDuckGo</a> :-D) for the issue with group switching, just in case…</p>
</div>
<divclass="section"id="the-nerd-snipe-issue">
<h2>The nerd snipe issue</h2>
<p><aclass="reference external"href="TODO">TODO SOMEONE</a> on GitHub found out an interesting workaround: when they
<em>disabled</em> the group switch, the UCW layout <em>started working</em>. Complete "huh?"
moment, I knew I wanted to know more about why that worked. The
<aclass="reference external"href="TODO">xkeyboard-common(7) manpage</a> says about more group switching options, so I
randomly tried switching by Menu key in XWayland (using <ttclass="docutils literal">setxkbmap</tt> even
though it complained and was supposed to do nothing), and ended up with <em>both</em>
CapsLock and Menu adding diacritics.</p>
<p>After a bit of mathematic thinking (more in my <aclass="reference external"href="TODO">comment of the main issue</a>) I
concluded that it was actually working fine, just that the <ttclass="docutils literal">SetGroup</tt> action
was evaluated <em>twice</em>, which would overflow back to the first group when two
layouts are set (probably the most common case). Mathematic term for this issue
is "arithmetic in the Z_2 group" :-)</p>
<p>Corollary 2: A simple workaround is adding the UCW overlay twice, e.g.
<ttclass="docutils literal">setxkbmap us,cz,cz <spanclass="pre">-variant</span> ,ucw,ucw</tt> (and thus changing the algebraic group
to be Z_3).</p>
<p>I think that another workaround would be to change the <ttclass="docutils literal">compat</tt> rules for
XWayland, but it feels nasty to have such a quirk in xkeyboard-config database.
Changing them for everyone (in <ttclass="docutils literal">compat/evdev</tt> directly) might break other
systems, so that also does not seem to be a good way.</p>
<p>Corollary 3: We are fixing XWayland (or the way it processes events from Wayland).</p>
</div>
<divclass="section"id="learning-what-happens">
<h2>Learning what happens</h2>
<p>I continued reading, this time mostly <aclass="reference external"href="TODO">who-t's post series about custom
layouts</a>, <aclass="reference external"href="TODO">core Wayland protocol</a> as well as the source code of various
tools (<ttclass="docutils literal">xev</tt>, <ttclass="docutils literal">wev</tt>, <ttclass="docutils literal">xkbcli <spanclass="pre">interactive-wayland</span></tt>&c.) Given that I knew
very little about the inner workings of the stack, I wanted to find some code
that would be run and "enhance" it with so many debug prints that I would
understand what was the state of various parts of the Xwayland's "stacked xkb".
I kind of knew that one part of this is <em>somewhere</em> in the XWayland server,
whose code felt intimidating, so I wanted to determine where to start nudging
it from what APIs the clients use.</p>
<divclass="admonition tip">
<pclass="first admonition-title">Tip</p>
<p>Downloading various packages in Linux is rather simple, as well as their
rebuilding. In Arch, I can get the package's PKGBUILD just with <ttclass="docutils literal">yay <spanclass="pre">-G</span>
package</tt>, build it with <ttclass="docutils literal">makepkg</tt> and install it using <ttclass="docutils literal">yay <spanclass="pre">-U</span>
<spanclass="pre">package-….pkg.tar</span></tt>. Makepkg also has some options which allow me to tweak
the source code after downloading it and before building it.</p>
<pclass="last">Other distros are probably similar, e.g. for Debian-based distros one can
use <ttclass="docutils literal">apt source</tt>, <ttclass="docutils literal">debuild</tt> and <ttclass="docutils literal">apt install</tt> to do the same.</p>
</div>
<p>One interesting observation is that contrary to my expectation, XWayland does
not seem to use libxkbcommon (according to the <ttclass="docutils literal">/proc/PID/maps</tt> file). This
can have several reasons, but its source code also contains a slightly tweaked
version of (Xorg-style) xkb, which might mean I will be dealing with <aclass="reference external"href="TODO">the ugly
code</a> :-/ (Actually, XWayland might not process key events itself, instead
just passing them to clients, but this seems inconsistent with the issue – what
else would be introducing the second group switch?)</p>
<p>So now I need to understand a (hopefully small) parts of two protocols: X11 to
understand what the X11 clients receive from XWayland, and Wayland to
understand what XWayland gets from my compositor.</p>
<divclass="section"id="understanding-x11">
<h3>Understanding X11</h3>
<p>This part is maybe the easier one, because <ttclass="docutils literal">xev</tt> pretty much dumps the data.
I should check in the server code as well, but for the Czech chords <ttclass="docutils literal">xev</tt>
seems to receive <ttclass="docutils literal">KeyEvents</tt> with state <ttclass="docutils literal">0x2000</tt> under Xorg (i3wm) and with
<ttclass="docutils literal">0x4000</tt> under XWayland when three groups are set up (for two groups the
state is <ttclass="docutils literal">0x0</tt>, i.e. the same as for English keypresses). State bits this
high encode the active group and are described in <aclass="reference external"href="https://www.x.org/releases/current/doc/kbproto/xkbproto.html#Computing_A_State_Field_from_an_XKB_State">the relevant part</a>
of <aclass="reference external"href="https://www.x.org/releases/current/doc/kbproto/xkbproto.html">the XKB extension documentation</a>.</p>
<p>We can see that if we set the <ttclass="docutils literal">GroupsWrap</tt> control of XKB (I don't know
where/how yet) to <ttclass="docutils literal">ClampIntoRange</tt> it should also help work around the
problem. But our goal is to align behaviour of XWayland and JustWayland, i.e.
prevent XWayland seeing two group shifts, not to hack our way in either
protocol, so we do not stop here.</p>
<!-- Read also xkbproto: Server Internal Modifiers and Ignore Locks Behavior (might explain sth?) -->
<p>At this point, I had an approximate idea of how XKB is supposed to do,
hopefully. I have not yet read the implementation in XWayland though…</p>
</div>
<divclass="section"id="understanding-wayland">
<h3>Understanding Wayland</h3>
<p>This part is supposed to also be simple: the core Wayland protocol will send us
<aclass="reference external"href="TODO">wl_keyboard::keymap</a>, <aclass="reference external"href="TODO">wl_keyboard::key</a> and
data, but <ttclass="docutils literal">xkb_v1</tt>-style keycodes are probably close (they are off by 8,
which is kind of expected).</p>
<p>While exploring what happens I found out there is a bug in the stable version
1.0.0 of <ttclass="docutils literal">wev</tt>, where it forgets to include the serial number for
wl_keyboard::modifiers. It has already <aclass="reference external"href="https://git.sr.ht/~sircmpwn/wev/commit/83de8e931ab04ce3322a58b359d8effa7901b21c">been patched</a>
15 months ago, but the patch is not included in a release yet.</p>
<p>But I am also interested in what is exchanged between the compositor and
XWayland server. And there does not seem to be any kind of sniffer yet, so <aclass="reference external"href="https://gitea.ledoian.cz/LEdoian/sopass/">I
<aclass="footnote-reference"href="#wiresharkify"id="footnote-reference-1">[1]</a>. Only then I learned that I can set the environment variable
<ttclass="docutils literal">WAYLAND_DEBUG=1</tt> to get dumps of all the calls that are happening. (To my
defense, this does not seem to be very documented. I only found a small note in
the <aclass="reference external"href="TODO">building guide</a>. Even the <aclass="reference external"href="TODO">debugging extras page</a> does
not mention this.)</p>
<!-- should fix the wl docs instead of ranting, though… -->
<p>By running <ttclass="docutils literal">WAYLAND_DEBUG=1 Xwayland :15</tt> and <ttclass="docutils literal"><spanclass="pre">DISPLAY=:15</span> xev</tt>, we do
learn what we thought was happening:</p>
<ulclass="simple">
<li>XWayland gets XKB v1 keymap (I trust sway to supply the same map as to
<li>XWayland gets the correct (Wayland core) events from the compositor</li>
<li>XWayland sends a wrong (X11 with XKB) events to <ttclass="docutils literal">xev</tt>.</li>
</ul>
<p>This confirms that XWayland is really the culprit here.</p>
</div>
</div>
<divclass="section"id="digging-into-xwayland">
<h2>Digging into XWayland</h2>
<p>At this point, we have all the tools, references to documentation and knowledge
we could possibly have, so now we get our hands dirty. A simple <ttclass="docutils literal">grep <spanclass="pre">-r</span>
wl_keyboard src/</tt> tells us that the keyboard handling occurs in the file
<ttclass="docutils literal"><spanclass="pre">hw/xwayland/xwayland-input.c</span></tt> in functions <ttclass="docutils literal">keyboard_handle_*</tt>, which look
like the libwayland-client handlers we have seen in <ttclass="docutils literal">wev</tt>.</p>