Key remapping
gui.key_mapping rewrites one key combination into another before it
reaches the editor. It is useful when the operating system swallows a key, when
the window in focus claims a key you would rather bind to a command, or when a
physical key never produces a usable keystroke on its own. The classic example
is CapsLock.
This feature applies to the Rune GUI only. Both sides of a mapping use the same grammar as command bindings; see Key combination syntax for the full reference.
How it works
gui.key_mapping is a table of source → target entries. When you press a key
whose combination matches a source, Rune delivers the target combination
instead. The match is exact, including modifiers: <ctrl-left> only matches
Ctrl held with the Left arrow, not a bare Left arrow.
gui:
key_mapping:
"<capslock>": "<esc>"
config["gui"]["key_mapping"] = {"<capslock>": "<esc>"}
The example above makes CapsLock behave as Escape inside Rune. Targets can be
printable characters or named keys, so "<numlock>": "0" and
"<ctrl-left>": "<home>" are both valid.
When a command binding doesn't fire
The window in focus receives every key first. If it acts on that key, the event
stops there and command.key_bindings never runs. This is deliberate: it keeps
a shell, a full-screen program, and an editor in insert mode completely
transparent to typing. Any combination Rune claimed for itself globally could
never be typed into them again.
A binding therefore only fires on a combination the focused window declines. A
terminal is the strictest case, because it forwards almost everything to the
shell: plain characters, and every ctrl and shift combination. It leaves
alone only combinations that include alt or meta, which is why Rune's
shipped bindings live there.
So this binding opens the file explorer from an editor, but does nothing in a
terminal, where <ctrl-e> belongs to the shell:
- config.yaml
- config.star
command:
key_bindings:
"<ctrl-e>": "fexplorer"
config["command"]["key_bindings"]["<ctrl-e>"] = "fexplorer"
gui.key_mapping solves this without giving up the key you want to press.
The remap runs before the focused window ever sees the event, so you can press
one combination and have Rune route on another. Map your combination to one the
terminal declines, then bind the command to that target:
- config.yaml
- config.star
gui:
key_mapping:
"<ctrl-e>": "<alt-meta-e>"
command:
key_bindings:
"<alt-meta-e>": "fexplorer"
config["gui"]["key_mapping"]["<ctrl-e>"] = "<alt-meta-e>"
config["command"]["key_bindings"]["<alt-meta-e>"] = "fexplorer"
<ctrl-e> now opens the file explorer everywhere, terminals included. What
reaches the terminal is <alt-meta-e>, which it does not claim, so the binding
runs.
Two things to keep in mind:
- The source combination is spent. Nothing in Rune receives
<ctrl-e>after this, so the shell can no longer use it either. That is usually the point, but pick a combination you do not need to send through. - Remap individual combinations, not a bare modifier.
"<ctrl>": "<meta>"rewrites thectrlbit on every key, so<ctrl-c>would become<meta-c>and you would lose the ability to interrupt a running command.
gui.key_mapping versus gui.meta_key
To use Alt instead of Super for Rune's <meta-…> bindings, set
gui.meta_key to "<alt>". Do not remap
the bare modifier with "<alt>": "<meta>".
The two settings differ in who sees the change:
- A bare-modifier
gui.key_mappingentry rewrites every key before anything sees it. The editor never receives Alt again, so<alt-left>, readline's<alt-f>and every other Alt binding are lost. gui.meta_keychanges only how Rune reads its own key specs. Every window still receives Alt first, so editors, terminals, and input boxes keep their Alt keys, and Rune's command fires only when the focused window declines the key.
gui.key_mapping runs first and always names physical keys; gui.meta_key
never rewrites it.
Physical keys you can remap
Some physical keys do not normally produce a keystroke Rune can act on. They are available only as remap sources and targets, and never do anything on their own unless you map them here:
| Source | Physical key |
|---|---|
<capslock> | Caps Lock |
<numlock> | Num Lock |
<scrolllock> | Scroll Lock |
<menu> | Menu / Context-menu key |
If one of these keys is not listed in gui.key_mapping, pressing it does
nothing.
Keys that cannot be remapped
A few keys are never delivered when pressed and cannot be used as a mapping source or target, because no standard terminal escape sequence exists for them:
- Function keys above F12 (F13 and up).
- Print Screen.
- Pause / Break.
Pressing these in Rune has no effect, and listing them in gui.key_mapping does
nothing.
Why remap CapsLock here instead of in the OS?
Remapping CapsLock to Escape at the OS level changes the key symbol, but
Rune's GUI reads physical keys directly and may not see that substitution.
Mapping <capslock> in gui.key_mapping is reliable because it runs inside
Rune, after the physical key is read.
More generally, a keystroke can be intercepted before it reaches Rune by:
- macOS: Keyboard Shortcuts, Mission Control, Spotlight, or Accessibility settings.
- Linux: the desktop environment, the window manager / compositor, or the input-method framework.
gui.key_mapping cannot recover a keystroke that one of those layers consumes
entirely, but it does let you re-purpose keys that do reach Rune, including
physical keys Rune would otherwise ignore.
Linux: Super, Meta, and Hyper
On Linux, Rune reads four modifiers: ctrl, shift, alt, and meta. meta
is the Super key, the one most keyboards label with the Windows logo, so a
Super combination is written <meta-...> and pressing Super registers as
meta. This is expected, not a lost keypress.
If your xkb layout defines the historical Meta or Hyper modifiers on separate
keys (for example Hyper on Caps Lock), Rune does not yet see them as their own
modifiers, so pressing them does not register as a modifier. gui.key_mapping
cannot conjure a modifier Rune never receives, but you can still put such a key
to work by remapping it to something Rune does read. For example, send Escape
from the Caps Lock key:
gui:
key_mapping:
"<capslock>": "<esc>"
See Key combination syntax for the full modifier model on Linux.
Programmable keyboards and home-row mods
Keyboards running QMK, ZMK or ZSA's Oryx resolve tap-hold keys, such as home-row mods, in their firmware. When you hold a home-row key, the keyboard sends a real modifier key, the same one your laptop keyboard sends, so Rune needs no configuration to read it.
Do not rebuild a home-row mod with gui.key_mapping. A mapping has no notion
of tap versus hold, and Rune cannot tell which keyboard a key came from, so
remapping s changes the s key on every keyboard you use.
If a binding does not fire from your programmable keyboard, run the keydump
command, hold the home-row key and press the other key of the chord. For
example, with Command on a held S, pressing S+N prints one of:
<meta-n>: Rune receives the chord, so the problem is the binding. See A key binding is not firing.sandnas two separate keys: the firmware decided you tapped S rather than held it, and every application sees the same thing. Hold the key a little longer before pressing the next one, or tune your firmware's tap-hold settings, such as the tapping term or the "hold on other key press" option.<alt-n>or<ctrl-n>: the hold sends a different modifier from the one<meta>stands for.<meta>is Command on macOS and Super on Linux, which firmware tools label GUI, Cmd or Win. Change the hold in your firmware to send that key. On Linux with the vim, helix or standard editor, you can instead setgui.meta_keyto"<alt>".
Notes
- When you pick the vim or helix preset, first-run setup offers to map
<capslock>to<esc>and writes the entry for you. - The mapping is read once at startup. Edit your config and restart Rune to pick up changes.
- Invalid entries (an unparseable source or target, or a non-string target) are skipped and reported as a notification; the remaining valid entries still load.
- Remapping happens before key bindings are resolved, so
bind commands under
command.key_bindingsto the target key, not the source. For example, with"<capslock>": "<esc>", a binding on<esc>fires when you press CapsLock; a binding on<capslock>never matches, because Rune has already rewritten it to<esc>by then.
See also
- Key combination syntax: the grammar for both sides of a mapping.
- Bind a command to a key: how
command.key_bindingsmaps keys to commands. - Configuration: where
rune.star/ the YAML config live and how to open them.