CuteKey or KuteKey

An adorable key daemon

Features

All the simple features you could want from a key daemon!

The features we have :party:

  • Remap combination of keys <XX> to <YY>, with left/right modifier support
  • Remapping in stages
  • Application-specific remaps (process-specific for console)
  • Works on wayland, x11, and the console
  • Importing other files using @include

The features we don’t have :tear:

  • Vim-style layers. That’s best left to a hotkey daemon
  • Running shell commands on keystrokes. Also best for a hotkey daemon
  • Keyboard-specific remaps. Considered for later

Methodology

Stages

To avoid recursive mappings while still allowing for “precedence” in mappings, we introduce a staging mechanism. This makes it clear what keycodes are coming in and going out

For example, if you want your capslock key to be indistinguishable from a control key in all cases, map it at [Stage 0] with <capslock> = <C->. Then if a mapping on [Stage 1] or higher maps control to something else, the capslock will already appear as a control on that stage!

Stages are numbered 0 through 100. They do not have to be consecutive

App-specific remaps

Under each [Stage X] heading, a subheading [[ app_title ]] can be added. These keys apply only when that app is focused. Multiple app titles, space separated, can be included this way

If the first “title” is a !, the match is negated. These keys will apply to all apps that aren’t listed in the heading. All matches are case-insensitive

[Stage 4]  # App-specific sub-headings can be used under any stage
[[ Alacritty xTeRm kiTTy ]]  # Applies to these 3 terminal apps
[[ ! Chromium firefox ]]     # Any apps other than chromium and firefox

Including files

Sometimes it’s helpful to have slightly different configurations per-system or per-keyboard, though with most mappings shared between the two. The @include directive inserts a file by adding the current stage to all stages in that file

For example given file gimp_keys.tomlish

# This file is at ~/gimp_keys.tomlish
[Stage 0]
<e> = <a>

[Stage 4]
[[ Gimp ]]
<C-a> = <C-r>

When we include it in another file like

[Stage 2]
[[ Chromium ]]
@include ~/gimp_keys.tomlish
<C-v> = <C-S-v>

The other file effectively expands to

[Stage 2]
[[ Chromium ]]
<e> = <a>
<C-v> = <C-S-v>

[Stage 6]
[[ Chromium Gimp ]]
<C-a> = <C-r>

Notice how the @include inherits the app scope and the stage scope from where it’s placed. This means placing an @include under [Stage 0] will import is “as is”. I recommend not making too many stages in files that are @included. Keep them atomic

Syntax

Configuration file is a TOML. Comments in TOML start with a #

Vim-ish syntax, see :h key-notation. Also supports fully qualified names from libxkb. Names themselves are case-insensitive, though modifiers are preferred to be capital

Modifiers:

rC  # Right control
lC  # Left control
# Using the (left/right) prefix is optional and applies to all modifiers
M   # Meta/super/logo
A   # Alt
S   # Shift

Example

The configuration file is very similar to a TOML. Some differences include:

  • Headings can include spaces
  • All variables must start with < and end with > on the left
  • Right side isn’t quoted
  • The @include directive
[Stage 0]
<capslock> = <C->  # Globally remap capslock to ctrl
<lC-> = <esc>      # Globally remap the bottom left control to escape
<esc> = <grave>    # Notice, no conflict is present with the previous mapping

# The following mapping won't cause recursion. Note here that the side is
# preserved, so the right alt will be mapped to the right meta
<A-> = <M->        # Globally remap both Alts to the corresponding Meta
<M-> = <A->        # Globally remap both Metas to the corresponding Alt

[Stage 1]
# The ! negates the match. Here, this binding will apply to all apps except
# alacritty, xterm, and kitty
[[ ! Alacritty Xterm Kitty ]]
<C-backetleft> = <esc>

<C-p> = <up>
<C-n> = <down>
<C-j> = <left>
<C-f> = <right>

# In this example we're remapping to control modifier to itself
<C-w> = <C-right>
# The above is shorthand for the following:
#   <lC-w> = <lC-right>
#   <rC-w> = <rC-right>
# That is the "side" of the control is preserved
<C-b> = <C-left>
<C-a> = <home>
<C-e> = <end>
<C-i> = <S-end>
<C-u> = <C-S-left>
<C-h> = <backspace>
<C-d> = <delete>

[Stage 2]
# The following line inserts another config file at this stage. Note that stage
# 0 in the included file becomes stage 2. Stage 1 becomes stage 3 and so on...
# It's recommended to only have `[Stage 0]` in included files
@include /home/emiliko/.config/cutekey_nofn.toml

[[ Chromium Firefox ]]  # This mapping applies to both firefox and chromium
<C-k> = <S-end>
<A-n> = <C-n>
<A-w> = <C-w>
<A-j> = <C-S-tab>
<A-k> = <C-tab>
<C-grave> = <grave>  # Prevents accidental midnight discord calls
<A-f> = <C-f>
<A-l> = <C-l>

[[ Chromium ]]
<A-S-n> = <C-S-n>

[[ Firefox ]]
<A-S-n> = <C-S-p>
炎 2022-2023 Written with 愛恋