DEV Community

Cover image for Terminator layouts from the command line: from two to five terminals
minnogit
minnogit

Posted on AI-assisted

Terminator layouts from the command line: from two to five terminals

One of the first things I do every morning at work is open a few terminals. I use Terminator, a terminal emulator for Linux that splits one window into several terminals. A saved arrangement of splits is called a layout, and Terminator can open a layout directly at startup.

The first time, I built my layout from the preferences window. It works, but the generated configuration is full of fields you don't need (uuid, size, position, last_active_term). Writing layouts by hand turned out to be shorter and easier to change, and everything can be done from the terminal. Here are the layouts I tested with Terminator 2.1.6, from two to five terminals.

How a layout works

A layout is a tree:

  • a Window at the root;
  • splits inside it: HPaned puts two children side by side, VPaned stacks them one above the other;
  • Terminal as the leaves.

Each element has a name (any name you like, as long as it is unique within the layout), a type and a parent. The two children of a split also have an order: 0 is the left or top one, 1 the right or bottom one. Without other options each split is 50/50, and ratio changes it.

Layouts live in ~/.config/terminator/config, under the [layouts] section. If you have never changed Terminator's preferences, the file doesn't exist yet:

sudo apt install terminator
mkdir -p ~/.config/terminator
nano ~/.config/terminator/config
Enter fullscreen mode Exit fullscreen mode

The layouts below go under [layouts]. If the file already exists, add them to that section instead of creating a second one.

Two terminals side by side

+--------+------------------+
|        |                  |
|  left  |      right       |
|        |                  |
+--------+------------------+
Enter fullscreen mode Exit fullscreen mode
[layouts]
  [[two]]
    [[[window]]]
      type = Window
      parent = ""
    [[[split]]]
      type = HPaned
      parent = window
      ratio = 0.3
    [[[left]]]
      type = Terminal
      parent = split
      order = 0
    [[[right]]]
      type = Terminal
      parent = split
      order = 1
Enter fullscreen mode Exit fullscreen mode

With ratio = 0.3 the left terminal takes 30% of the width. Remove the line for two equal halves.

Two columns, the first split in two

+-------------+-------------+
|  top left   |             |
+-------------+    right    |
| bottom left |             |
+-------------+-------------+
Enter fullscreen mode Exit fullscreen mode
  [[three]]
    [[[window]]]
      type = Window
      parent = ""
    [[[columns]]]
      type = HPaned
      parent = window
    [[[left_split]]]
      type = VPaned
      parent = columns
      order = 0
    [[[top_left]]]
      type = Terminal
      parent = left_split
      order = 0
    [[[bottom_left]]]
      type = Terminal
      parent = left_split
      order = 1
    [[[right]]]
      type = Terminal
      parent = columns
      order = 1
Enter fullscreen mode Exit fullscreen mode

The first child of columns is not a terminal but another split, and that is all it takes to nest them.

A grid of four

+-------------+-------------+
|             |             |
+-------------+-------------+
|             |             |
+-------------+-------------+
Enter fullscreen mode Exit fullscreen mode
  [[grid]]
    [[[window]]]
      type = Window
      parent = ""
      maximised = True
    [[[columns]]]
      type = HPaned
      parent = window
    [[[left]]]
      type = VPaned
      parent = columns
      order = 0
    [[[right]]]
      type = VPaned
      parent = columns
      order = 1
    [[[t1]]]
      type = Terminal
      parent = left
      order = 0
    [[[t2]]]
      type = Terminal
      parent = left
      order = 1
    [[[t3]]]
      type = Terminal
      parent = right
      order = 0
    [[[t4]]]
      type = Terminal
      parent = right
      order = 1
Enter fullscreen mode Exit fullscreen mode

This is the layout I open every morning. maximised = True makes the window fill the screen.

A spiral of five

This one follows a geometric rule: split the screen in two side by side, then split the second half again, alternating the direction each time.

+-----------+-----------------+
|           |        2        |
|           +--------+--------+
|     1     |        |   4    |
|           |   3    +--------+
|           |        |   5    |
+-----------+--------+--------+
Enter fullscreen mode Exit fullscreen mode

The terminals get smaller at each step, so the font gets smaller with them. The font belongs to a profile, and each terminal can use a different one. Profiles go under [profiles]:

[profiles]
  [[large]]
    font = Monospace 12
    use_system_font = False
  [[medium]]
    font = Monospace 10
    use_system_font = False
  [[small]]
    font = Monospace 8
    use_system_font = False
Enter fullscreen mode Exit fullscreen mode
  [[spiral]]
    [[[window]]]
      type = Window
      parent = ""
    [[[s1]]]
      type = HPaned
      parent = window
    [[[t1]]]
      type = Terminal
      parent = s1
      order = 0
      profile = large
    [[[s2]]]
      type = VPaned
      parent = s1
      order = 1
    [[[t2]]]
      type = Terminal
      parent = s2
      order = 0
      profile = medium
    [[[s3]]]
      type = HPaned
      parent = s2
      order = 1
    [[[t3]]]
      type = Terminal
      parent = s3
      order = 0
      profile = medium
    [[[s4]]]
      type = VPaned
      parent = s3
      order = 1
    [[[t4]]]
      type = Terminal
      parent = s4
      order = 0
      profile = small
    [[[t5]]]
      type = Terminal
      parent = s4
      order = 1
      profile = small
Enter fullscreen mode Exit fullscreen mode

Each split is the second child (order = 1) of the previous one, and the type alternates between HPaned and VPaned. The first terminal, the largest, uses large, the next two medium and the last two small.

Fonts and colors per terminal

A profile can also change the font family and the colors, so the two smallest terminals can look different from the others. For them I use Terminus, a monospace font designed to stay sharp at small sizes, and a dark green background:

sudo apt install fonts-terminus
fc-list : family | grep -i terminus
Enter fullscreen mode Exit fullscreen mode

On Debian the package installs the TTF version, and the family name is Terminus (TTF). With just Terminus, Terminator doesn't find the font and falls back to another one with badly spaced letters, so check the exact name with fc-list. The small profile becomes:

  [[small]]
    font = Terminus (TTF) 9
    use_system_font = False
    background_color = "#0f2a1d"
    foreground_color = "#9fe8b0"
Enter fullscreen mode Exit fullscreen mode

A different background helps to recognize a terminal at a glance, for example the ones following logs. foreground_color only changes plain text: the colors of the prompt (user in green, path in blue) come from bash's PS1, not from Terminator.

Opening a layout

terminator --list-layouts     # check that Terminator reads them
terminator --layout=grid
Enter fullscreen mode Exit fullscreen mode

If a Terminator window is already open, the new command doesn't start a new program: by default Terminator runs as a single instance, and the request goes to the instance already running. That instance still has the configuration it read at startup, so a layout added afterwards is unknown to it, and you get a window with a single terminal. Close all Terminator windows before trying a new layout, or start a separate instance with -u (--no-dbus):

terminator -u --layout=grid
Enter fullscreen mode Exit fullscreen mode

A command in each terminal

Each terminal can start with a command, for example a connection to a server:

    [[[t1]]]
      type = Terminal
      parent = left
      order = 0
      command = "ssh admin@server01; exec bash"
Enter fullscreen mode Exit fullscreen mode

The ; exec bash part matters: without it the terminal closes as soon as the command ends, for example when the SSH session drops. With it, you are left with a local shell in the same terminal.

A launcher for GNOME

The command is already a single line, so in the terminal an alias is enough:

echo "alias tgrid='terminator --layout=grid'" >> ~/.bashrc
Enter fullscreen mode Exit fullscreen mode

To open it from the GNOME menu or the dock, I create a .desktop file:

mkdir -p ~/.local/share/applications
cat > ~/.local/share/applications/terminator-grid.desktop <<'EOF'
[Desktop Entry]
Type=Application
Name=Terminator (grid)
Comment=Terminator with four terminals
Exec=terminator --layout=grid
Icon=terminator
Terminal=false
Categories=System;TerminalEmulator;
EOF
Enter fullscreen mode Exit fullscreen mode

desktop-file-validate ~/.local/share/applications/terminator-grid.desktop checks the file. The launcher then appears in the activities search under "Terminator (grid)", and from there you can pin it to the dock. For more layouts, one file per layout with a different Name and Exec.

One launcher for all layouts

With several layouts, one file each gets tedious. A .desktop file can also declare actions: GNOME shows them when you right-click the icon, in the dock or in the applications list. So one launcher is enough, with an action for each layout, and a small script can generate it from Terminator's configuration:

#!/usr/bin/env bash
# Creates a GNOME launcher for Terminator with one action per configured layout.
# The actions appear when you right-click the icon (dock or applications list).
# Run it again after adding or removing a layout.
set -euo pipefail

dest="${XDG_DATA_HOME:-$HOME/.local/share}/applications/terminator-layouts.desktop"
mkdir -p "$(dirname "$dest")"
mapfile -t layouts < <(terminator --list-layouts 2>/dev/null | grep -v '^default$')

{
  echo "[Desktop Entry]"
  echo "Type=Application"
  echo "Name=Terminator layouts"
  echo "Comment=Terminator with a saved layout"
  echo "Exec=terminator -u"
  echo "Icon=terminator"
  echo "Terminal=false"
  echo "Categories=System;TerminalEmulator;"
  # action identifiers may only contain letters, digits and hyphens
  ids=()
  for l in "${layouts[@]}"; do ids+=("$(printf '%s' "$l" | tr -c 'A-Za-z0-9-' '-')"); done
  echo "Actions=$(printf '%s;' "${ids[@]}")"
  for i in "${!layouts[@]}"; do
    l="${layouts[$i]}"
    echo
    echo "[Desktop Action ${ids[$i]}]"
    echo "Name=$l"
    echo "Exec=terminator -u --layout=$l"
  done
} > "$dest"

desktop-file-validate "$dest"
echo "Created $dest with ${#layouts[@]} layouts: ${layouts[*]}"
Enter fullscreen mode Exit fullscreen mode

I saved it as ~/bin/terminator-layouts. A normal click opens a plain Terminator, a right-click lists the layouts. Every action uses -u, so the layout opens even when another Terminator window is already running with an older configuration.

A dashboard layout

The spiral turned out to be a good shape for a monitoring dashboard: one large terminal for the overview and smaller ones for details:

  • btop, the overview of CPU, memory, disks, network and processes, in the large terminal;
  • atop, detailed system metrics, with one line per CPU core;
  • wavemon, Wi-Fi link quality and signal level;
  • nvtop, the GPU (AMD, Intel or Nvidia);
  • nload, network traffic in and out.
sudo apt install btop atop wavemon nvtop nload fonts-unifont
Enter fullscreen mode Exit fullscreen mode

Two more profiles. retro uses GNU Unifont, a pixel font that covers almost all of Unicode, so btop's graphs keep every symbol and get an old-computer look. It stays sharp at 12 points, a multiple of its pixel grid. tiny is the green Terminus profile at 7 points, for the two smallest terminals:

  [[retro]]
    font = Unifont 12
    use_system_font = False
  [[tiny]]
    font = Terminus (TTF) 7
    use_system_font = False
    background_color = "#0f2a1d"
    foreground_color = "#9fe8b0"
Enter fullscreen mode Exit fullscreen mode

The layout is the spiral, with a title for each terminal and a command:

  [[dashboard]]
    [[[window]]]
      type = Window
      parent = ""
      maximised = True
    [[[s1]]]
      type = HPaned
      parent = window
    [[[btop]]]
      type = Terminal
      parent = s1
      order = 0
      profile = retro
      title = btop
      command = "btop; exec bash"
    [[[s2]]]
      type = VPaned
      parent = s1
      order = 1
    [[[atop]]]
      type = Terminal
      parent = s2
      order = 0
      profile = small
      title = atop
      command = "atop; exec bash"
    [[[s3]]]
      type = HPaned
      parent = s2
      order = 1
    [[[wifi]]]
      type = Terminal
      parent = s3
      order = 0
      profile = small
      title = wifi
      command = "wavemon; exec bash"
    [[[s4]]]
      type = VPaned
      parent = s3
      order = 1
    [[[gpu]]]
      type = Terminal
      parent = s4
      order = 0
      profile = tiny
      title = gpu
      command = "nvtop; exec bash"
    [[[net]]]
      type = Terminal
      parent = s4
      order = 1
      profile = tiny
      title = network
      command = "nload wlp2s0; exec bash"
Enter fullscreen mode Exit fullscreen mode

A few things I learned while putting it together:

  • the font size decides whether a program starts at all: atop refuses to run in less than 60 columns by 24 lines, so it needs the larger top-right terminal or a small font;
  • nload without arguments picks the first network device, which on my laptop is an idle Docker bridge. wlp2s0 is my Wi-Fi interface: ip -br link lists yours;
  • without title, some terminals show "None" in their title bar;
  • not every console program behaves in Terminator. s-tui, which I tried first for CPU temperature and frequency, filled its terminal with control sequences instead of graphs, so I left it out.

The dashboard layout: btop, atop, wavemon, nvtop and nload

Other tools

Terminator splits a window on your desktop. If you also need splits on a remote server, or sessions that survive a disconnection, the tool is a terminal multiplexer like tmux or Zellij, which work inside any terminal, including over SSH. Zellij describes layouts in files, similar to what I did here.

Top comments (0)