One of the major advantages of Emacs is that in addition to working on graphical interfaces, it can be used from a terminal as well.
However, some features (such as many keybindings) are not available when using Emacs in terminals, due to ancient protocol limitations.
This post shows how to make the most of Emacs on a terminal (urxvt).
Table of Contents
“I don’t care about the why, just show me how!" I got you
The objective is to get all Emacs keybindings to work when running emacs inside the urxvt terminal.
The following video demonstrates that emacs running in urxvt can distinguish between C-p and C-S-p:
The following table1 shows all the mappings that can work with emacs in the terminal:
Space key with shift
[2-8] (note: xterm.el supports other key binds)
“g” and space keys
s or s-S
from space to “~” (almost 8 bits characters without control sequences)
H or H-S
same as s-S, but use Hyper modifier
Emacs and Terminal Keybindings
If you are an Emacs user, you might have noticed that some keybindings do not work when starting Emacs without a window system (-nw or --no-window-system).
The explaination is relatively simple:
Whereas the Emacs instance running under X11 has access to all keyboard events (keyup, keydown) etc., the instance running in a terminal emulator is much more limited.
Among other shortcomings, Emacs running inside a terminal cannot distinguish betweenC-<key> (ctrl + key) and C-S-<key> (ctrl + shift + key)!
Try it for yourself: open a new terminal and execute the cat command.
All terminal input will now be provided to the cat command as stdin and echoed back stdout and therefore your terminal.
Now press C-o and C-S-o and observe the difference (spoiler: there is none).
What’s up with the ^ characters?
Those characters are used in combination with [A-Z] characters in order to display ASCII control characters in caret notation.
You might have seen this in utilities like less, which show the caret notation for non printable characters like the windows carriage return ^M.
But what exactly are they there for?
Let’s do a little test and insert all literal C-<key> sequences in alphabetical order into an Emacs buffer.
We can prefix our ctrl + key commands with C-q to do a quoted insert and prevent Emacs from executing keybindings associated with the input.
You will see two things:
The entered characters have a special font face as they represent non printable bytes
The characters ^I and ^J seem to be missing (more on that in a bit)
If you did the steps above, your buffer should look like this:
Let’s inspect it further by using the emacs-integrated hex editor: hexl-mode!
This allows us to see the underlying byte representation of those characters we just typed.
We can see that the characters we typed are represented by the bytes 0x01-0x1a.
We can have a look into the ASCII table to reveal their meaning.
(The table also includes 0x1b-0x1f)
start of heading
start of text
end of text
end of transmission
data link escape
device control 1
device control 2
device control 3
device control 4
end of block
end of medium
If you look at the hex-codes 0x09 and 0x0a (where ^I and ^J should have been), you can see that those characters represent a horizontal tab and a new line.
This explains why we cannot see them in our emacs buffer.
Instead of displaying the control characters in caret notation, emacs simply renders them as a tab and a newline!
As you can see, there are no ASCII control characters represented by a caret and a lowercase character.
Therefore the terminal simply can not distinguish between a C-<key> and a C-S-<key> keypress.
Aside: How the Terminal handles control characters
Before looking for a way to work around this limitation, let us learn about the handling of those control characters a bit more.
While the process behind handling a ^C (end of text in ASCII) input is more complicated, it leads to a SIGINT signal being sent.
It shows that the meaning behind the ASCII characters has changed over the course of the last decades and should not be take literally.
You can check how your terminal interprets those control characters by executing the command stty -a:
As you can see ^C is translate into “intr”, which causes a SIGINT to be sent.
How urxvt Solves This Problem
Both, xterm and urxvt, can work around this limitation by being able to capture the keyboard input from the X window system, just like Emacs does in X11 mode.
This however is the case for most terminal emulators.
The difference that makes xterm and urxvt stand out is their configurability and key-remapping capabilities.
Instead of handling the key sequence C-p and C-S-p both as ^P, two distinct actions can be executed.
The idea is to configure both – Emacs and urxvt – in a way that:
The pressed key sequence can be uniquely represented by control characters in the terminal
Emacs is able to translate the control characters back to the original key sequence
Of course I’m not the first one to discover this possibility since the package xterm-keybinder aims to solve this exact problem.
The rest of the article will focus on how xterm-keybinder works under the hood and how to configure it.
The emacs package xterm-keybinder aims to provide the translation logic for both previously described translation steps.
For the first translation step (keypress to control characters), the package provides a start script which starts urxvt with the right configuration.
You can either use this script, or move the configuration into the .Xdefaults file if you want this configuration by default.
I recommend using the .Xdefaults file.
As you can see below, the urxvt configuration remaps several key-combinations, which previously could not be represented in the terminal.
The mapping follows a specific scheme:
In the example above, urxvt is configured to map the non-terminal-representable key-combinations ending in -F to sequences lead by a control character.
You might wonder about the red ^[ (=“escape”/ESC) control character.
In Emacs, emitting this control character is equal to pressing Meta (e.g. M-f is equal to ESC f).
In our example whenever C-S-F is pressed, urxvt emits the sequence M-[ [ = F.
You can confirm this by using cat in your terminal:
If you would open your emacs -nw now and tried to press C-S-f, you’d disappointedly notice that Emacs does not recognize this input yet.
In fact, if you try to type that key combination manually inside Emacs, you should notice that the key combination is not used (unless you configured it otherwise).
To make Emacs translate the control characters back to the original key-combination, some more work is necessary.
You could manually register the sequences that are defined in the ~/.Xdefaults configuration, but that would be very elaborate.
As a proof-of-concept, let’s define two sequences (for multicursor) by hand in urxvt and in Emacs:
This is enough to get the two keybindings working!
To automatically setup all other keybindings, refer to the xterm-keybinder documentation1 and the complete configuration files below.
If you don’t care about the details, you should be able to get up and running if you integrate the following configuration into your ~/.emacs and your ~/.Xdefaults file.
When making changes to the ~/.Xdefaults file, reload the configuration and restart urxvt: