-
Notifications
You must be signed in to change notification settings - Fork 220
Description
Full Compose Key Sequences with Keyd: Working but Possibly Too Complex?
Four Unicode characters brought me here: the em-dash, the left and right apostrophes, and the left and right quotation marks. I’d been copy-pasting them for too long and finally decided enough was enough.
Keyd looked like the right tool, and I’m glad I found it — but it took a bit of a journey to get everything working. I wanted to replicate the Compose key sequences found in the standard X11 Compose file, particularly:
# /usr/share/X11/locale/en_US.UTF-8/Compose:
<Multi_key> <minus> <minus> <minus> : "—" U2014 # EM DASH
<Multi_key> <less> <apostrophe> : "‘" U2018 # LEFT SINGLE QUOTATION MARK
<Multi_key> <greater> <apostrophe> : "’" U2019 # RIGHT SINGLE QUOTATION MARK
<Multi_key> <less> <quotedbl> : "“" U201c # LEFT DOUBLE QUOTATION MARK
<Multi_key> <greater> <quotedbl> : "”" U201d # RIGHT DOUBLE QUOTATION MARK
...
(And the reversed variants)
Context
I’m on Wayland with KDE, which has its own Compose key setting under:
Settings > Keyboard > Advanced > Key Bindings > Compose key position
I enabled that, setting Caps Lock
as my Compose key. But under Wayland, support is partial and inconsistent:
- Chromium respected the setting.
- Kate, Konsole, Plasma apps didn’t.
- VSCode worked.
- Most terminals did not.
In contrast, Keyd gave me reliable results — but recreating the sequence logic was a challenge. It wasn’t immediately obvious how to implement a three- or four-key chain like Compose-Minus-Minus-Minus, especially when Shift had to stay held across transitions.
The Solution
After trial and error, I learned about composite layers (e.g., [layer1+layer2+layer3]
). That was the breakthrough.
Here’s my working Keyd config to replicate the sequences above:
# /etc/keyd/default.conf
[ids]
*
[main]
capslock = oneshot(compose)
[compose]
minus = oneshot(compose_minus)
capslock = escape
backspace = capslock
apostrophe = oneshot(compose_apostrophe)
leftshift = layer(compose_shift)
[compose_apostrophe]
capslock = layer(compose)
leftshift = layer(compose_apostrophe_shift)
[compose_apostrophe_shift]
capslock = layer(compose)
comma = ‘
dot = ’
[compose_shift]
capslock = layer(compose)
apostrophe = oneshot(compose_shift_apostrophe)
comma = oneshot(compose_shift_comma)
dot = oneshot(compose_shift_dot)
[compose_shift_apostrophe]
capslock = layer(compose)
comma = “
dot = ”
[compose_shift_comma]
apostrophe = ‘
[compose+compose_shift+compose_shift_comma]
apostrophe = “
[compose_shift_dot]
apostrophe = ’
[compose+compose_shift+compose_shift_dot]
apostrophe = ”
[compose_minus]
capslock = layer(compose)
minus = oneshot(compose_minus_minus)
[compose_minus_minus]
capslock = layer(compose)
minus = —
N.B. I think I should have used capslock = clear()
instead of capslock = layer(compose)
Summary of Supported Sequences
#
# Press 1 Press 2 Press 3 Press 4 Output
# -----------------------------------------------------------------
# Compose Minus Minus Minus —
# Compose Apostrophe Shift+Comma ‘
# Compose Apostrophe Shift+Dot ’
# Compose Shift+Apostrophe Shift+Comma “
# Compose Shift+Apostrophe Shift+Dot ”
# Compose Shift+Comma Apostrophe ‘
# Compose Shift+Dot Apostrophe ‘
# Compose Shift+Comma Shift+Apostrophe ”
# Compose Shift+Dot Shift+Apostrophe “
The Concern
This config works. But it feels verbose. I also saw this warning:
WARNING: max layers (32) exceeded
While the current config doesn’t hit the limit, it’s easy to see how a similar approach could run into that wall with more sequences.
Moreover, in order for this to work in Chromium-and-co I need to replace the last key stroke with a macro like macro(leftcontrol+leftshift+u 2 0 1 c enter)
. But the issue is I have to duplicate the whole configuration, adding _alt
as a suffix to each layer. Unless there's a way to override just the last keypress — not just the entrypoint.
Also, it took a lot of trial and error to arrive here — composite layers aren’t documented in depth, and I didn’t find examples using this multi-layer chaining pattern.
N.B. Composite layers are documented, but can track key sequences (not just combos), and they can leverage the release of a key too, but this wasn’t obvious until I watched keyd listen
closely.
Feedback?
- Is this approach reasonable?
- Is there a simpler way to represent such sequences?
Thanks again for Keyd. It’s powerful — just hoping to help others skip the learning curve I hit.