This package allows configuring Emacs and a supported terminal emulator to handle keyboard input involving any combination of keys and modifiers.
Generally, terminal emulators and applications running in a terminal cannot reliably transmit and receive certain keystrokes (keys in combination with modifiers). For some key combinations, there is no consensus on how these events should be encoded on the wire (F-keys and movement keys plus modifiers); some other key combinations cannot be represented at all (such as Ctrl1 or CtrlShiftA). This can be an impediment for Emacs users, especially when you've already configured your Emacs in an environment unrestricted by these limitations (X11) and now wish to use it in the terminal as well.
The term-keys
package allows solving this problem by giving these key combinations a custom, unique encoding, overcoming the limitations of the protocol implemented by the terminal emulators.
To install term-keys
, first add my ELPA package archive. Add this to your init.el
and evaluate it:
(require 'package) ; you may already have this line
(add-to-list 'package-archives
'("cselpa" . "https://elpa.thecybershadow.net/packages/"))
Then, install it like any ELPA package (M-xpackage-install
RETterm-keys
).
Setting up term-keys
is a three-part process:
- Configure Emacs;
- Configure
term-keys
(optional); - Configure your terminal emulator.
See the sections below for more information.
Setting up term-keys
is as easy as:
(require 'term-keys)
(term-keys-mode t)
This will automatically set up current and new TTY frames to decode term-keys
key sequences.
If you prefer to enable it for each frame manually, you can do so by invoking (term-keys/init)
.
term-keys
is very configurable. Most things can be changed via Emacs' customize
interface - use M-xcustomize-group
RETterm-keys
to access it.
Some of the supplementary code for terminal emulator configuration can be configured as well.
Because it's not loaded by default (as it's only necessary for initial configuration), you need to load it explicitly before configuring it, e.g. using M-:(require 'term-keys-konsole)
, and only then invoking Emacs' customize
interface.
See the terminal's section in this file for more information.
The only part of term-keys
which cannot be configured via the customize
interface is the set of key combinations to support, as (counting all possible keys plus combinations of modifier keys) their total number is very large.
For this purpose, term-keys
allows specifying the name of a function to invoke, which shall implement this logic.
See the documentation of term-keys/want-key-p-func
and term-keys/want-key-p-def
(as well as the definition of the latter) for more information.
Each terminal emulator has its own method of configuration. Consult the section corresponding to your terminal emulator of choice below.
Note that you will need to update the terminal emulator configuration every time you change the term-keys
configuration.
There's three ways to configure urxvt
with term-keys
: via command-line parameters, via X resources, or by invoking a term-keys
function from Emacs.
Command-line configuration consists in adding the key bindings to the urxvt
invocation directly.
You can use term-keys/urxvt-script
to create a shell script in this manner:
(require 'term-keys-urxvt)
(with-temp-buffer
(insert (term-keys/urxvt-script))
(write-region (point-min) (point-max) "~/launch-urxvt-with-term-keys.sh"))
Afterwards, you can run e.g.:
$ sh ~/launch-urxvt-with-term-keys.sh -e emacs -nw
This will launch Emacs under an urxvt
instance configured for term-keys
.
X resource configuration consists in adding the term-keys
configuration to the X resources.
The X resources are global (per X session), and will apply to all newly-started urxvt
instances.
You can use term-keys/urxvt-xresources
to create the necessary configuration in this manner:
(require 'term-keys-urxvt)
(with-temp-buffer
(insert (term-keys/urxvt-xresources))
(append-to-file (point-min) (point-max) "~/.Xresources"))
Then use xrdb
to load the file into memory:
$ xrdb -merge ~/.Xresources
In addition to generating a static configuration as a shell script or X resources, you can ask term-keys
to invoke urxvt
directly.
This has the benefit that it will always use the up-to-date term-keys
configuration, but the downside that it must be done by invoking emacs
or emacsclient
.
For example:
$ emacsclient --eval '(term-keys/urxvt-run-emacs)'
See the term-keys/urxvt-*
definitions for more urxvt-specific help code.
You may also want to disable some default urxvt
shortcuts which may interfere with using Emacs.
Consider adding something like this to your ~/.Xresources
:
URxvt*iso14755: 0
URxvt.keysym.Shift-Insert: builtin-string:
URxvt.keysym.M-S: builtin-string:
xterm
is configured nearly identically as urxvt
;
thus, this section will be very similar to the urxvt
section above.
Note: xterm
supports an extended input mode; see modifyOtherKeys
et al in the xterm(1)
man page.
You may find it sufficient for your use case even without term-keys
.
As with urxvt
, there's two ways to configure xterm
: via command-line parameters or X resources.
Command-line configuration consists in adding the key bindings to the xterm
invocation directly.
You can use term-keys/xterm-script
to create a shell script in this manner:
(require 'term-keys-xterm)
(with-temp-buffer
(insert (term-keys/xterm-script))
(write-region (point-min) (point-max) "~/launch-xterm-with-term-keys.sh"))
Afterwards, you can run e.g.:
$ sh ~/launch-xterm-with-term-keys.sh -e emacs -nw
This will launch Emacs under an xterm
instance configured for term-keys
.
X resource configuration consists in adding the term-keys
configuration to the X resources.
The X resources are global (per X session), and will apply to all newly-started xterm
instances.
You can use term-keys/xterm-xresources
to create the necessary configuration in this manner:
(require 'term-keys-xterm)
(with-temp-buffer
(insert (term-keys/xterm-xresources))
(append-to-file (point-min) (point-max) "~/.Xresources"))
Then use xrdb
to load the file into memory:
$ xrdb -merge ~/.Xresources
In addition to generating a static configuration as a shell script or X resources, you can ask term-keys
to invoke xterm
directly.
This has the benefit that it will always use the up-to-date term-keys
configuration, but the downside that it must be done by invoking emacs
or emacsclient
.
For example:
$ emacsclient --eval '(term-keys/xterm-run-emacs)'
See the term-keys/xterm-*
definitions for more xterm-specific help code.
You may also want to disable the eightBitInput
xterm
option, e.g. with -xrm 'XTerm*eightBitInput: false'
.
kitty is configured via its kitty.conf
configuration file.
To configure kitty for term-keys
, use term-keys/kitty-conf
to generate a kitty.conf
fragment:
(require 'term-keys-kitty)
(with-temp-buffer
(insert (term-keys/kitty-conf))
(write-region (point-min) (point-max) "~/kitty-for-term-keys.conf"))
Then, add the output to your main kitty.conf
file.
You can customize kitty's mapping of GLFW modifiers to Emacs modifiers in the generated configuration using the respective customize
group, i.e.: M-:(progn (require 'term-keys-kitty) (customize-group 'term-keys/glfw))
wezterm is configured via its wezterm.lua
configuration file.
To configure wezterm for term-keys
, use term-keys/wezterm-conf
to generate a wezterm.lua
fragment:
(require 'term-keys-wezterm)
(with-temp-buffer
(insert (term-keys/wezterm-conf))
(write-region (point-min) (point-max) "~/wezterm-for-term-keys.lua"))
Then, add the output to your main wezterm.lua
file.
You can customize wezterm's mapping of GLFW modifiers to Emacs modifiers in the generated configuration using the respective customize
group, i.e.: M-:(progn (require 'term-keys-wezterm) (customize-group 'term-keys/glfw))
Alacritty is configured via its alacritty.yml
configuration file.
To configure alacritty for term-keys
, use term-keys/alacritty-config
to generate a alacritty.yml
fragment:
(require 'term-keys-alacritty)
(with-temp-buffer
(insert (term-keys/alacritty-config))
(write-region (point-min) (point-max) "~/alacritty-for-term-keys.yml"))
Then, add the output to your main alacritty.yml
file.
You can customize Alacritty's mapping of its supported modifiers to Emacs modifiers in the generated configuration using the respective customize
group, i.e.: M-:(progn (require 'term-keys-alacritty) (customize-group 'term-keys/alacritty))
Konsole provides an interface for adding new and editing existing escape sequences for key combinations
(Settings → Edit current profile... → Keyboard → Edit).
term-keys
can generate a Konsole keyboard profile according to its settings.
To do so:
-
Create a new keyboard profile, based on the default one.
(Select "Default (XFree 4)", and click "New...".)
Name it e.g.Emacs
. -
Append the output of
term-keys/konsole-keytab
to the newly created.keytab
file, e.g.:(require 'term-keys-konsole) (with-temp-buffer (insert (term-keys/konsole-keytab)) (append-to-file (point-min) (point-max) "~/.local/share/konsole/Emacs.keytab"))
-
Assign the keyboard profile to a new or existing Konsole profile.
You can customize the mapping of Konsole (Qt) modifiers to Emacs modifiers using the respective customize
group, i.e.: M-:(progn (require 'term-keys-konsole) (customize-group 'term-keys/konsole))
You may also want to disable some default Konsole shortcuts (Settings → Configure Shortcuts...), as they may interfere with standard Emacs commands.
Yakuake seems to share much with Konsole, and can be configured in the same way. See the section above for details.
The Linux console can be customized using .keymap
files and the loadkeys
program.
You can configure it for term-keys
as follows:
-
Use
term-keys/linux-keymap
to create a.keymap
file:(require 'term-keys-linux) (with-temp-buffer (insert (term-keys/linux-keymap)) (write-region (point-min) (point-max) "~/term-keys.keymap"))
-
Load the created
.keymap
file:$ sudo loadkeys ~/term-keys.keymap
To reset the layout to the default one, run sudo loadkeys -d
.
You will need to invoke loadkeys
after every boot; alternatively, on
systemd distributions, you can add a KEYMAP=
line to
/etc/vconsole.conf
.
You can customize some settings affecting the generated .keymap
files using the respective customize
group, i.e.: M-:(progn (require 'term-keys-linux) (customize-group 'term-keys/linux))
Note that the .keymap
files generated by term-keys/linux-keymap
assume a QWERTY keyboard layout.
If you use another layout (AZERTY, Dvorak, Colemak...), you will need to customize term-keys/mapping
and set the alphanumeric keynumbers accordingly.
Note also that Linux has a limitation on the number of customized keys. Custom key strings need to be assigned to "function keys", for which there are 256 slots by default (which includes the default strings for the F-keys and some other keys like Insert/Delete). This leaves about 234 slots for term-keys
, which is sufficient for the default configuration, but may not be sufficient for a custom one, especially if you enable support for additional modifiers.
st is configured by editing its config.h
and recompiling. The key sequences can be configured in this way as well.
You can configure st for term-keys
as follows:
-
Generate the initial
config.h
, e.g. by buildingst
once. -
Use the
term-keys/st-config-*
functions to create header files:(require 'term-keys-st) ;; Assuming st is checked out in ~/st (with-temp-buffer (insert (term-keys/st-config-key)) (write-region (point-min) (point-max) "~/st/config-term-keys-key.h")) (with-temp-buffer (insert (term-keys/st-config-mappedkeys)) (write-region (point-min) (point-max) "~/st/config-term-keys-mappedkeys.h"))
-
Update the definition of
mappedkeys
inconfig.h
as follows:static KeySym mappedkeys[] = { #include "config-term-keys-mappedkeys.h" -1 };
-
Update the definition of
key
inconfig.h
as follows:static Key key[] = { #include "config-term-keys-key.h" /* keysym mask string appkey appcursor crlf */ /* ( ... original definitions follow ... ) */
-
Rebuild st.
You can customize st's mapping of X11 modifiers to Emacs modifiers in the generated configuration using the respective customize
group, i.e.: M-:(progn (require 'term-keys-st) (customize-group 'term-keys/x11))
The standard macOS Terminal.app allows, to a certain extent, customizing escape sequences sent by key combinations
(Terminal → Preferences... → Profiles → (select a profile) → Keyboard).
term-keys
can generate a configuration XML file, which can be injected into Terminal.app's preferences plist file.
To do so:
-
Create a new profile. Name it e.g.
Emacs
. -
Enable the "Use Option as Meta key" option on the Keyboard tab.
-
Export the
term-keys
keymap XML file:(require 'term-keys-terminal-app) (with-temp-buffer (insert (term-keys/terminal-app-keymap-xml)) (append-to-file (point-min) (point-max) "~/term-keys.xml"))
-
Import the keymap into Terminal.app's preferences file:
$ plutil -replace 'Window Settings.Emacs.keyMapBoundKeys' \ -xml "$(cat ~/term-keys.xml)" \ ~/Library/Preferences/com.apple.Terminal.plist
(If you named your profile something other than
Emacs
, substitute its name in the command above.) -
Restart Terminal.app.
Note that the application's settings UI only allows configuring a small set of keys.
This limitation does not carry over to the underlying configuration file format, and thus does not apply to term-keys
-
the generated keymap file will contain definitions for all keys that generate a Unicode key code on macOS.
These entries will appear (displayed with the key's hex code instead of the key name) in the terminal emulator's configuration, but cannot be edited there.
You can customize the mapping of Terminal.app modifiers to Emacs modifiers using the respective customize
group, i.e.: M-:(progn (require 'term-keys-terminal-app) (customize-group 'term-keys/terminal-app))
To configure Windows Terminal for term-keys
, use term-keys/windows-terminal-json
to generate a json file.
(require 'term-keys-windows-terminal)
(with-temp-file "~/windows-terminal.json"
(insert (term-keys/windows-terminal-json)))
Then, add the contents to Windows Terminal settings.
These terminals don't (directly) support customizing key bindings, and thus cannot be used with term-keys
:
- PuTTY - Custom key mappings are a known frequently-requested feature.
- GNOME Terminal - Uses the Gnome VTE widget, which doesn't seem to support key binding customization.
- tilix, Guake, tilda - Use the same widget as GNOME Terminal.
- GNOME Terminator - Uses the same widget as GNOME Terminal, however has a plugin system. Perhaps writing a plugin is possible?
- termite - Uses the same widget as GNOME Terminal, however, it has a
modify_other_keys
option, similar to xterm'smodifyOtherKeys
. - mintty - No support for customizing key bindings, however, it already has some support for xterm's
modifyOtherKeys
protocol extensions. - Bitvise SSH Client - No support for customizing key bindings. Supports a custom extended protocol (bvterm), which seems to allow lossless keyboard input (including key-up events), however it is full of Windows-isms (much of the protocol uses the same data types as used by the Windows API), and getting Emacs to speak it would be a non-trivial task that's not likely to be achievable using just Emacs Lisp code.
- Terminology - No support for customizing key bindings at runtime (via a configuration file), however, this terminal generates its key/string translation tables using another terminal emulator (i.e. by sending keyboard events with
xdotool
to another terminal emulator and recording the resulting strings). Therefore, it may be possible to configure Terminology withterm-keys
by running its configuration script through another terminal pre-configured withterm-keys
(e.g.xterm
), then rebuilding Terminology using the resulting configuration.
-
xterm-keybinder is an Emacs package similar to this one.
term-keys
improves upon xterm-keybinder by supporting more terminals, better documentation, customizability, and extensibility, and improved wire efficiency. -
notty is an experimental terminal emulator which also aims at achieving lossless keyboard input by extending the ANSI protocol.
term-keys
does not use notty's protocol because its escape sequence prefix (^[{
) conflicts with a default Emacs command binding (backward-paragraph
). -
Fix Keyboard Input on Terminals is a proposal to improve input in terminals, which is to some extent implemented in
xterm
and Emacs. Unfortunately, the proposed protocol is vague and incomplete, as it does not cover modifier keys and many PC keyboard keys. -
The kitty terminal emulator (the one written in Python, not the PuTTY fork) implements its own protocol extensions for keyboard handling (though it can also be simply configured with term-keys as described above).