hexatlas

Terminals, colors, formatting, and you

Terminals are strange, arcane beasts. Many programmers start out writing programs for a terminal or terminal-inspired text environment. Even many IDEs just run terminal programs behind the scenes. One way or another, you're bound to write code for a terminal eventually — maybe you're running some unit tests, automating a process, or doing development work on a remote server.

Terminals used to be big physical boxes that served as the keyboard and screen for some larger computer. Early terminals had a variety of features and protocols, many incompatible and proprietary. Eventually, standards emerged, such as the ANSI escape sequences standard, which serves as the foundation for many control sequences in modern terminals (or, more frequently, terminal emulators, like OSX's Terminal.app or X Windows' xterm).

Even terminal emulators vary on which ANSI codes they recognize; however, there is a reasonably safe subset of ANSI that works on most terminals I use. Here's how they work.

ANSI code basics

When you read about ANSI codes, many documents talk about how they all start with a magic CSI (Control Sequence Introducer). These days, that's the escape character (ASCII 27, which most languages let you reach with "\e" or at the very least \x1b) followed by a left square bracket ([). So, whenever someone says CSI around ANSI codes, they mean "\e[".

When your terminal sees "\e[" written out to it, it does not print it. Instead, it continues reading until it gets to a letter; the letter it eventually finds dictates which ANSI code is being invoked; any characters before then are arguments for that code. The terminal then does whatever it's supposed to do in response to that code and continues processing output as normal. For example, A moves the cursor up, and so writing "\e[A" to the terminal tells it to move the cursor up one line.

Colors

The ANSI code I use most frequently is called SGR (Select Graphic Rendition). Its ANSI code is m. Between the CSI and the m, it takes a semicolon-delimited list of format codes (all of which are numbers) to apply. These formats apply until the next SGR. Most terminal emulators support only a few format codes.

Format zero ("\e[0m") resets all formats. All future output will appear in the default styles. In general, end your formatted text with a reset sequence.

Two-digit codes beginning with 3 set the text color. The second digit is the sum of whether you want the output to be red (1), green (2), blue (4), or some combination: 31 makes red text, 30 makes black text, 33 (red + green) makes yellow text, 37 makes white text, etc. So, you could create a sentence with appropriately-colored fruits by printing "I like \e[31mcherries\e[0m, \e[33mbananas\e[0m, and \e[34mblueberries\e[0m.". This should produce I like cherries, bananas, and blueberries. in your terminal window.

You can set the text background color much like the foreground: begin the code with a 4 instead of a 3. Terminals track the foreground and background color separately, so you can change one without affecting the other. I like to configure my shell's prompt to include "\e41mroot\e[0m" when I'm root so it's more obvious.

Other formats and combining

Using the above, you have access to eight whole colors! (Plus the default text color, if it's not one of those eight.) If your terminal is configured so that "bold" (ANSI code 1) makes the text brighter without making the characters actually bold, you can access a whole second set of text colors. (If your terminal is not configured like this, reconfigure it. Having 16 colors is way cooler than having a bolded monospace font.)

To combine codes in one escape sequence, separate them with semicolons. So, you can make blue text with "\e[34m", but you can make it bright blue by adding 1 like this: "\e[1;34m". This gives you sixteen foreground colors and eight background colors. Want to set all three at once? Send your terminal "\e[1;36;44mBright-cyan-on-blue!\e[0m" and you should see Bright-cyan-on-blue! if it's working correctly.

Some terminal emulators also support formats like dim, italic, underline, blinking, strikethrough, and a bunch of even more ridiculous formats. In my experience, they aren't supported consistently, so I don't use them.

Maneuvers

You can control more than just text styles with ANSI codes. Codes ending in A, B, C, or D move the cursor up, down, left, or right, respectively. If you put a number after the CSI, the cursor moves that many times in the given direction, so "\e[10D" will move the cursor ten columns to the left. You can also absolutely-position the cursor with H; it takes two semicolon-delimited arguments which specify the new row and column for the cursor: "\e[5;20H" moves the cursor to row 5, column 20. There are also codes to do things like erase parts of the current line, erase parts of the screen, or scroll.

Even more colors

Most terminal emulators aren't stuck in eight-color 1983 — many of them are stuck in 256-color 1999 instead! To acess all the glory of 256 colors in your terminal, you might have to adjust your configuration. Sometimes, you just need to set your TERM variable to xterm-256color or screen-256color (or whatever your termcap hides your 256-color settings under); other times, you have to enable options in your emulator, or sometimes it just works out of the box. Doing a search with your terminal emulator name and "256 color support" usually turns up useful results.

Anyway, once you have 256-color support, what can you do? Well, write "\e[38;5;<color>m" to set the foreground color to <color>, or switch the 38 to a 48 to adjust the background color instead. In these sequences, <color> is a number from 0 to 255. 0–7 are the same colors as before; 8–15 are the bright versions. Then, you get a 216-color RGB cube for any R, G, or B in the range 0–5: use 16 + 36*R + 6*G + B, which produces values in the range 16–231. For example, orange is 208 (R=5, G=2, B=0: 16 + 365 + 62 + 0). Finally, the remaining values 232–255 are a grayscale ramp from almost-black to almost-white (use 0 for black and 15 for white).

Color keys

Terminal color key

The top half of the key shows regular sixteen-color mode in every combination of foreground and background color. Along the left, 0: and 1: indicate whether the foreground color is bold (bright); in each cell, the pair of numbers represents the foreground/background codes. For example, the style in a cell marked 6/4 on a line marked 1: would be obtained by writing "\e[0m1;36;44m".

The bottom half of the key shows the colors available in 256-color mode terminals, each labled with their color index. Creating a table of all 256-color foreground/background pairs is left as an exercise for the reader.