1. Introduction

Because of its command-line heritage, Linux and its programs provide many keyboard shortcuts. Some are directly useful, while others can just confuse users. This mainly depends on the level of awareness.

In this tutorial, we’ll explore how to unfreeze the terminal after pressing Ctrl-S. First, we look at how key shortcuts are processed. Next, we see how a Linux system interprets them. After that, we discuss duplicate key bindings and how they affect software flow control. Finally, we go through the issues that software flow control shortcuts cause, as well as their solutions.

We tested the code in this tutorial on Debian 11 (Bullseye) with GNU Bash 5.1.4. It should work in most POSIX-compliant environments.

2. Terminal Shortcuts

Which keyboard combinations exist and what they do is strongly linked to the:

  • applicable standards, e.g., SUS or POSIX
  • Linux distribution
  • terminal emulator
  • the shell currently in use

Of course, listing all possible shortcuts by their source is outside our scope.

Further, the type of Linux we’re using determines the default shell, terminal emulator, and the standard we work in. It’s rare that the distribution itself provides any specific shortcuts within the command-line interface.

Considering this, let’s look at the shell and terminal emulator only.

2.1. Shell Keyboard Combinations

Shells may have different key bindings. Using Bash, we can take advantage of the bind built-in.

In particular, bind -p lists current command-line editing functions and their shortcuts. Since it’s usually a rather long list, we’ll use grep to filter by function:

$ bind -p | grep beginning-of-line
"\C-a": beginning-of-line
"\eOH": beginning-of-line
"\e[1~": beginning-of-line
"\e[H": beginning-of-line

In our case, there are four combinations to get the cursor to the current line’s start. How do we know which ones come from where? The common way is to read the manual.

On the other hand, we can search via a directly by keys:

$ bind -p | grep "\C-s"
"\C-s": forward-search-history

As a result, we see Ctrl+S (\C-s) triggers the forward-search-history function. Let’s remember that.

2.2. Terminal Emulator Shortcuts

In Linux, the kernel terminal driver termios maps keys to functions.

To check these low-level mappings, we can use stty:

$ stty --all
speed 38400 baud; rows 41; columns 168; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = ; eol2 = ; swtch = ; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W;
lnext = ^V; discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc

Here, we see some common signal control combinations like Ctrl+C (^C) for interrupting and Ctrl+Z (^Z) for suspending and backgrounding.

Also, we again see Ctrl+S (^S), but as a stop. So, how does Linux distinguish between this Ctrl+S and the one from above?

3. Keyboard Shortcut Interpretation

Whenever we use any keyboard combination, the following happens:

  1. Pressed and released keys produce scancodes.
  2. Keyboard drivers convert scancodes to keycodes, combining with Control and other modifiers.
  3. Keycodes become tty input characters via kernel keymaps.
  4. stty checks run on these characters.
  5. Anything that stty does not recognize is available for further processing.

Keeping the above in mind, we can easily answer our earlier question: Linux evaluates duplicate shortcut keys at the earliest stage.

Of course, without having knowledge of all combinations, a user might expect something else.

4. Duplicate Key Combinations

When stty and Bash both have the same shortcut, we now know that stty takes precedence with interpretation.

Lack of user familiarity is the main cause of collision issues, but they are not rare. Moreover, they are hard to detect and are best handled on a per-case basis. Indeed, some are much more critical than others.

For example, there is a similar situation with Ctrl+Z, famous as undo, but also for suspending processes. However, users can usually easily detect what happens when it’s pressed.

On the other hand, one may just use Ctrl+S by mistake since it also saves changes in many editors. Further, stty makes Ctrl+S a stop rather than, e.g.,  forward-search-history. The consequences are severe in this case. Let’s see why.

5. Software Flow Control

In Linux, software flow control is a concept borrowed from hardware protocols. In short, it ensures an endpoint does not get overrun with data.

This happens via two main triggers:

  • XOFF (0x13) to stop data flow
  • XON (0x11) to restart data flow

Thus, programs like terminal emulators can limit the data inflow (such as characters on the screen) to the rate of the other side, e.g., a human.

Actually, the triggers are simple data bytes sent in-band. To generate them manually, users employ keyboard shortcuts.

6. Issues with Software Flow Control Shortcuts

Going back to the example of Ctrl+S, we may now see the problem: Ctrl+S triggers an XOFF event. In practice, this action locks the current shell, stopping output. While halting a random process might be fine, locking the shell we’re working with ties our hands.

Essentially, there are several issues with the fact that Ctrl+S sends XOFF:

  • the shortcut is common in other areas of computer usage
  • there’s no feedback to the user that anything happened
  • it results in a complete blockage of output to the terminal
  • input is still accepted but only buffered

To end this blocked state from Ctrl+S (XOFF), we have to send an XON, usually via Ctrl+Q. In our case, we know this from the output of stty, where XON was also called start:

$ stty --all
[...]
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = ; eol2 = ; swtch = ; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W;
[...]

Once we press Ctrl+Q, all buffered input gets processed, and all output goes through as usual.

Because of this, unwitting users often go through the following scenario:

  • without paying attention, a user presses Ctrl+S, expecting another action
  • the terminal freezes and stops responding
  • the user tries keys and commands, often typing at random
  • if, by chance, the user does enter Ctrl+Q, everything is executed like a script

This scenario can cause irreparable damage. Apart from knowing about the mechanism, how else can we avoid problems?

7. Solving Terminal Shortcut Problems

There are several ways to prevent keyboard combinations from stopping our work and causing unexpected situations.

7.1. Documentation

While not very helpful for a tutorial, the first step, in this case, is reading the manuals. The main ones to consider are:

  • Linux distribution terminal driver documentation
  • shell manual
  • terminal emulator documentation

Since looking for obscure shortcuts in manuals is not ideal, let’s move on to some easier steps we can take.

7.2. Test Terminal Emulators

In some graphical user interfaces (GUIs), there are terminal emulators, which ignore stty shortcuts in favor of their own or the shell’s.

For example, recent versions of QTerminal do this by default. However, we still have options with other terminal emulators that don’t.

7.3. Manually Disable Software Flow Control

Employing stty, we can prevent the processing of XON and XOFF:

$ stty -ixon
$ stty -ixoff

After these commands, we can safely use the keys for start and stop from stty –all as they will no longer be interpreted by termios.

Including the above commands in our shell’s rc could make any terminal more user-friendly from the start.

To restore the processing of start and stop, we use stty ixon and stty ixoff.

7.4. Changing the XON/XOFF Hotkeys

Indeed, on some rare occasions, we might want to preserve the software flow control but free up Ctrl+S and Ctrl+Q. In this case, we still have the option of changing the bindings:

$ stty stop '^Y'
$ stty start '^U'

Here, we use Ctrl+Y and Ctrl+U. As a side note, we can also change any other stty binding, like intr for interrupt (default is Ctrl+C).

Of course, we can again use our shell’s rc for applying the setting between restarts.

8. Summary

In this article, we explored terminal shortcuts, how they are processed, and how we can modify their behavior. Particularly, we looked at software flow control, how it can confuse a user, and what we can do about that.

In conclusion, Linux has a complex way to interpret key combinations, but once users are aware of it, they can avoid unexpected behavior.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.