• This repository has been archived on 26/Apr/2024
  • Stars
    star
    499
  • Rank 85,270 (Top 2 %)
  • Language
  • License
    MIT License
  • Created over 5 years ago
  • Updated over 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

How to write a TUI in BASH

Writing a TUI in BASH [WIP]

Through my travels I've discovered it's possible to write a fully functional Terminal User Interface in BASH. The object of this guide is to document and teach the concepts in a simple way. To my knowledge they aren't documented anywhere so this is essential.

The benefit of using BASH is the lack of needed dependencies. If the system has BASH available, the program will run. Now there are cases against using BASH and they're most of the time valid. However, there are cases where BASH is the only thing available and that's where this guide comes in.

This guide covers BASH 3.2+ which covers pretty much every OS you'll come across. One of the major reasons for covering this version is macOS which will be forever stuck on BASH 3.

To date I have written 3 different programs using this method. The best example of a TUI that covers most vital features is fff which is a Terminal File manager.

Table of Contents

Operating Systems

Identify the Operating System.

The quickest way to determine the current Operating System is the $OSTYPE variable. This variable is set at compile time in bash and typically stores the name of the running kernel or the name of the OS itself.

You can also use the command uname to identify which OS is running. The uname command is POSIX and should be available everywhere. The output from uname does differ from $OSTYPE but there's a vast amount of documented information about it. [1] [2]

get_os() {
    # Figure out the current operating system to handle some
    # OS specific behavior.
    # '$OSTYPE' typically stores the name of the OS kernel.
    case "$OSTYPE" in
        linux*)
            # ...
        ;;

        # Mac OS X / macOS.
        darwin*)
            # ...
        ;;

        openbsd*)
            # ...
        ;;

        # Everything else.
        *)
            #...
        ;;
    esac
}

Documented $OSTYPE values.

The table below was populated by users submitting the value of the $OSTYPE variable using the following command. If you're running an OS not mentioned below or the output differs, please open an issue with the correct value.

bash -c "echo $OSTYPE"
OS $OSTYPE
Linux with glibc linux-gnu
Linux with musl linux-musl
Cygwin cygwin
Bash on Windows 10 linux-gnu
Msys msys
Mingw64 msys
Mingw32 msys
OpenBSD openbsd*
FreeBSD freebsd*
NetBSD netbsd
macOS darwin*
iOS darwin9
Solaris solaris*
Android (Termux) linux-android
Android linux-gnu
Haiku haiku

Getting the window size.

Using cursor position

This function figures out the terminal window size by moving the cursor to the bottom right corner and then querying the terminal for the cursor's position. As the terminal is made up of cells the bottom right corner is equal to the terminal's size.

get_term_size() {
    # '\e7':           Save the current cursor position.
    # '\e[9999;9999H': Move the cursor to the bottom right corner.
    # '\e[6n':         Get the cursor position (window size).
    # '\e8':           Restore the cursor to its previous position.
    IFS='[;' read -sp $'\e7\e[9999;9999H\e[6n\e8' -d R -rs _ LINES COLUMNS
}

Using checkwinsize

Note: This only works in bash 4+.

When checkwinsize is enabled and bash receives a command, the LINES and COLUMNS variables are populated with the terminal window size. The (:;:) snippet works as a pseudo command, populating the variables without running anything external.

get_term_size() {
    shopt -s checkwinsize; (:;:)
}

Using stty

This function calls stty size to query the terminal for its size. The stty command is POSIX and should be available everywhere which makes it a viable alternative to the pure bash solutions.

get_term_size() {
    # Get terminal size ('stty' is POSIX and always available).
    # This can't be done reliably across all bash versions in pure bash.
    read -r LINES COLUMNS < <(stty size)
}

Reacting to window size changes.

Using trap allows us to capture and react to specific signals sent to the running program. In this case we're trapping the SIGWINCH signal which is sent to the terminal and the running shell on window resize.

We're reacting to the signal by running the above get_term_size() function. The variables $LINES and $COLUMNS will be updated with the new terminal size ready to use elsewhere in the program.

# Trap the window resize signal (handle window resize events).
# See: 'man trap' and 'trap -l'
trap 'get_term_size' WINCH

Escape Sequences

For the purposes of this resource we won't be using tput. The tput command has a lot of overhead (10-15 ms per invocation) and won't make the program any more portable than sticking to standard VT100 escape sequences. Using tput also adds a dependency on ncurses which defeats the whole purpose of doing this in bash.

Hiding and Showing the cursor

See:

# Hiding the cursor.
printf '\e[?25l'

# Showing the cursor.
printf '\e[?25h'

Line wrapping

See:

# Disabling line wrapping.
printf '\e[?7l'

# Enabling line wrapping.
printf '\e[?7h'

Moving the cursor to specific coordinates

See:

# Move the cursor to 0,0.
printf '\e[H'

# Move the cursor to line 3, column 10.
printf '\e[3;10H'

# Move the cursor to line 5.
printf '\e[5H'

Moving the cursor to the bottom of the terminal.

See:

# Using terminal size, move cursor to bottom.
printf '\e[%sH' "$LINES"

Moving the cursor relatively

When using these escape sequences and the cursor hits the edge of the window it stops.

Cursor Up

See:

# Move the cursor up a line.
printf '\e[A'

# Move the cursor up 10 lines.
printf '\e[10A'

Cursor Down

See:

# Move the cursor down a line.
printf '\e[B'

# Move the cursor down 10 lines.
printf '\e[10B'

Cursor Left

See:

# Move the cursor back a column.
printf '\e[D'

# Move the cursor back 10 columns.
printf '\e[10D'

Cursor Right

See:

# Move the cursor forward a column.
printf '\e[C'

# Move the cursor forward 10 columns.
printf '\e[10C'

Clearing the screen

See:

# Clear the screen.
printf '\e[2J'

# Clear the screen and move cursor to (0,0).
# This mimics the 'clear' command.
printf '\e[2J\e[H'

Setting the scroll area.

This sequence allow you to limit the terminal's vertical scrolling area between two points. This comes in handy when you need to reserve portions of the screen for a top or bottom status-line (you don't want them to scroll).

This sequence also has the side-effect of moving the cursor to the top-left of the boundaries. This means you can use it directly after a screen clear instead of \e[H (\e[2J\e[0;10r).

See:

# Limit scrolling from line 0 to line 10.
printf '\e[0;10r'

# Set scrolling margins back to default.
printf '\e[;r'

Saving and Restoring the user's terminal screen.

This is the only non VT100 sequences I'll be covering. This sequence allows you to save and restore the user's terminal screen when running your program. When the user exits the program, their command-line will be restored as it was before running the program.

While this sequence is XTerm specific, it is covered by almost all modern terminal emulators and simply ignored in older ones. There is also DECCRA which may or may not be more widely supported than the XTerm sequence but I haven't done much testing.

# Save the user's terminal screen.
printf '\e[?1049h'

# Restore the user's terminal screen.
printf '\e[?1049l'

References

More Repositories

1

pure-bash-bible

๐Ÿ“– A collection of pure bash alternatives to external processes.
Shell
35,948
star
2

neofetch

๐Ÿ–ผ๏ธ A command-line system information tool written in bash 3.2+
Shell
20,984
star
3

pywal

๐ŸŽจ Generate and change color-schemes on the fly.
Python
8,114
star
4

pure-sh-bible

๐Ÿ“– A collection of pure POSIX sh alternatives to external processes.
Shell
6,293
star
5

fff

๐Ÿ“ A simple file manager written in bash.
Shell
4,028
star
6

pfetch

๐Ÿง A pretty system information tool written in POSIX sh.
Shell
2,018
star
7

sowm

An itsy bitsy floating window manager (220~ sloc!).
C
894
star
8

wal

๐ŸŽจ Generate and change colorschemes on the fly. Deprecated, use pywal instead. -->
Shell
724
star
9

pxltrm

๐Ÿ–Œ๏ธ pxltrm - [WIP] A pixel art editor inside the terminal
Shell
642
star
10

dotfiles

๐Ÿ™ dotfiles
Vim Script
541
star
11

pash

๐Ÿ”’ A simple password manager using GPG written in POSIX sh.
Shell
336
star
12

birch

An IRC client written in bash
Shell
312
star
13

torque

๐Ÿš‚ A TUI client for transmission written in pure bash.
Shell
222
star
14

shfm

file manager written in posix shell
Shell
215
star
15

wal.vim

๐ŸŽจ A vim colorscheme for use with wal
Vim Script
214
star
16

promptless

๐Ÿš€ A super fast and extremely minimal shell prompt.
Shell
186
star
17

paleta

Change terminal colors on-the-fly independent of terminal emulator.
C
182
star
18

bin

๐Ÿ—‘๏ธ scripts
Shell
145
star
19

bum

๐ŸŽต Download and display album art for mpd/mopidy tracks.
Python
134
star
20

openbox-patched

PKGBUILD and patches for Openbox with Rounded Corners
111
star
21

fff.vim

A plugin for vim/neovim which allows you to use fff as a file opener.
Vim Script
103
star
22

bareutils

A coreutils written in pure bash.
Shell
88
star
23

k

kiss pkg man written in c
C
55
star
24

clutter-home

clutter your home directory!
50
star
25

eiwd

iwd without dbus
C
46
star
26

barsh

Use your terminal as a bar
Shell
38
star
27

nosj

a json parser written in pure bash
Shell
36
star
28

bush

This is an experiment to see how many standard tools and functions we can re-implement in pure bash.
Shell
33
star
29

crayon

๐ŸŽจ A dark 16 color colorscheme for Vim, Gvim, and Nvim
Vim Script
29
star
30

startpage

๐Ÿ”— Simple start page written in HTML/SCSS
CSS
29
star
31

ryuuko

๐ŸŽจ A colorscheme~
Vim Script
28
star
32

wiki

KISS Linux - Wiki (The wiki is now a part of the website)
27
star
33

hello-world.rs

๐Ÿš€Memory safe, blazing fast, configurable, minimal hello world written in rust(๐Ÿš€) under 1 line of code with few(774๐Ÿš€) dependencies๐Ÿš€
Rust
25
star
34

pow

Shell
24
star
35

root.vim

๐ŸŒด Automatically set directory to your project's root based on a known dir/file.
Vim Script
22
star
36

nfu

Neofetch Utils - A set of C programs to print system information.
C
22
star
37

wm

xcb wm
C
20
star
38

pkg

Package Manager for Kiss Linux
Shell
18
star
39

dylanaraps

17
star
40

okpal

okpal - Swap on the fly between a bunch of palettes
Shell
15
star
41

neofetch-branding

Logos for Neofetch
15
star
42

codegolf

my bash code golfs
15
star
43

eiwd_old

SEE: https://github.com/dylanaraps/eiwd
C
14
star
44

taskrunner.nvim

๐Ÿƒ Runs Gulp/Gruntfiles in terminal splits
Vim Script
14
star
45

discord-selena

Log all Discord messages for transparency
Python
12
star
46

libdbus-stub

stub libdbus to appease
C
12
star
47

str

C
11
star
48

dylan-kiss

Dylan's KISS repository
Objective-C
11
star
49

kiss-flatpak

flatpak for kiss
Shell
11
star
50

blog

Shell
10
star
51

kiss-initramfs

[WIP] initramfs tool for KISS (help wanted!)
Shell
9
star
52

sowm-patches

READ: https://github.com/dylanaraps/sowm/pull/57
8
star
53

golfetch

simple fetch script for Linux.
Shell
8
star
54

reddit-sidebar-toggle

๐Ÿ‘ฝ Toggle the sidebar on reddit.com
JavaScript
8
star
55

coal

๐Ÿš‚ A bash script that takes a list of colors and outputs them in various formats for use in other programs.
Shell
8
star
56

dylan.k1ss.org

HTML
7
star
57

wayland-experiment

Shell
6
star
58

uncompress

6
star
59

xyz-redirect

simply a cheeky way to 301 redirect https to another domain leveraging netlify to handle the SSL cert.
HTML
5
star
60

repo

๐Ÿ“ฆ Dylan's Cydia Repo
Shell
5
star
61

dlink-ssid-bypass

๐Ÿ“ก Bypass SSID validation on D-Link DSL-2750B
4
star
62

languages

Shell
4
star
63

pywal-branding

Logos for pywal
Shell
4
star
64

blag

blag
HTML
3
star
65

oldyiayias

Old website for Yiayias Greek Cafe
CSS
3
star
66

kisslinux-irc-logs

Freenode #kisslinux IRC logs (2019-2021)
3
star
67

pascal_lint.nvim

๐Ÿ† Show fpc compiler output in a neovim split.
Vim Script
3
star
68

dylanaraps.github.io-old

๐Ÿ”— My personal website.
HTML
2
star
69

2211

mnml trmnl using vte
C
2
star
70

kiss-submodule-links

Shell
2
star
71

eww-static-test

Rust
1
star
72

yiayias

Recreating Yiayia's website 6 months later
HyPhy
1
star
73

google

Remake of Google.com for an assignment
HTML
1
star