Solarian Programmer

My programming ramblings

C Programming - using ANSI escape codes on Windows, macOS and Linux terminals

Posted on April 8, 2019 by Paul

In this article I will show you how to use ANSI escape codes to control the colors of your Terminal, write text at arbitrary positions, erase lines of text or portions of the terminal and move the cursor. The advantage of using ANSI escape codes is that, today, these are available on most operating systems, including Windows, and you don’t need to install third party libraries. These are well suited for simple command line applications. If you need to do complex text graphics check the ncurses library.

You can find all the code examples at the GitHub repo for this article.

There is also a video version of this tutorial:

Let’s start with a simple C example that will print a green text message on your Terminal:

1 #include <stdio.h>
2 
3 int main(void) {
4     printf("\x1b[32mHello, World\n");
5 }

If you build and run the above code, on Linux or macOS, you should see the message Hello, World printed in green. A side effect of the above program is that your Terminal will switch the default text color to green. Let’s remedy the above problem by resetting the colors to the original ones before the program ends:

1 #include <stdio.h>
2 
3 int main(void) {
4     // Green text
5     printf("\x1b[32mHello, World\n");
6     // Reset colors to defaults
7     printf("\x1b[0m");
8 }

This is what I see on a macOS machine:

Green text with ANSI escape codes on macOS Terminal

Let’s analyze a bit the ANSI escape code that changes the text color to green, this is made up from a few parts:

ESC [ 32 m

We can represent the ESC key in a string in a few ways:

"\x1b"    // hex
"\033"    // octal

The 32 number from the ANSI escape code is the color code for green.

Originally the ANSI escape codes specified only 8 colors for foreground (the text) from 30 to 37: black, red, green, yellow, blue, magenta, cyan and white. Same colors can be used for the terminal background but these are defined as the numbers from 40 to 47. For example, if you want to set the background to blue and the text color to red you can use this code:

1 #include <stdio.h>
2 
3 int main(void) {
4     // Red text and blue background
5     puts("\x1b[31m\x1b[44mHello, World");
6     // Reset colors to defaults
7     printf("\x1b[0m");
8 }

This is what I see on my machine:

Red text green background with ANSI escape codes on macOS Terminal

As a side note, you can use ANSI escape codes in the Chrome Console window, if you want to change the color of the output text:

Green text with ANSI escape codes on Chrome Console

If you try one of the above C codes on a Windows 10 machine, you will be able to compile it successfully, but the output will be mangled with non printable characters. This is because the Windows console needs to be programmatically put in ANSI escape mode.

I wrote two simple helper functions to setup the console and restore it before the caller program ends. In short, you need to get the output handle for the console and set ENABLE_VIRTUAL_TERMINAL_PROCESSING. Put the next code in a file named ansi_escapes.c:

 1 #ifdef _WIN32
 2 #include <windows.h>
 3 #endif
 4 
 5 #include <stdio.h>
 6 #include "ansi_escapes.h"
 7 
 8 #ifdef _WIN32
 9 // Some old MinGW/CYGWIN distributions don't define this:
10 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
11 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING  0x0004
12 #endif
13 
14 static HANDLE stdoutHandle;
15 static DWORD outModeInit;
16 
17 void setupConsole(void) {
18 	DWORD outMode = 0;
19 	stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
20 
21 	if(stdoutHandle == INVALID_HANDLE_VALUE) {
22 		exit(GetLastError());
23 	}
24 	
25 	if(!GetConsoleMode(stdoutHandle, &outMode)) {
26 		exit(GetLastError());
27 	}
28 
29 	outModeInit = outMode;
30 	
31     // Enable ANSI escape codes
32 	outMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
33 
34 	if(!SetConsoleMode(stdoutHandle, outMode)) {
35 		exit(GetLastError());
36 	}	
37 }
38 
39 void restoreConsole(void) {
40     // Reset colors
41     printf("\x1b[0m");	
42 	
43     // Reset console mode
44 	if(!SetConsoleMode(stdoutHandle, outModeInit)) {
45 		exit(GetLastError());
46 	}
47 }
48 #else
49 void setupConsole(void) {}
50 
51 void restoreConsole(void) {
52     // Reset colors
53     printf("\x1b[0m");
54 }
55 #endif

You’ll also need the header file ansi_escapes.h:

1 #pragma once
2 
3 void setupConsole(void);
4 void restoreConsole(void);

Let’s modify the C code that writes a red text message on blue background in order to work on Linux, macOS and Windows. I’ve saved the modified code as t1b.c:

1 #include <stdio.h>
2 #include "ansi_escapes.h"
3 
4 int main(void) {
5     setupConsole();
6     puts("\x1b[31m\x1b[44mHello, World");
7     restoreConsole();
8 }

You can build the code on Linux and macOS with:

cc -std=c17 -Wall -Wextra -pedantic ansi_escapes.c t1b.c -o t1b

and on Windows, assuming that you are using the Visual Studio command line compiler:

cl /W4 ansi_escapes.c t1b.c /Fe:t1b.exe

This is what I see on a Windows console:

Red text green background with ANSI escape codes on Windows Console

Let’s also collect the color codes for the foreground and background in an enum in ansi_escapes.h:

 1 #pragma once
 2 
 3 enum Colors {
 4 	RESET_COLOR,
 5 	BLACK_TXT = 30,
 6 	RED_TXT,
 7 	GREEN_TXT,
 8 	YELLOW_TXT,
 9 	BLUE_TXT,
10 	MAGENTA_TXT,
11 	CYAN_TXT,
12 	WHITE_TXT,
13 
14 	BLACK_BKG = 40,
15 	RED_BKG,
16 	GREEN_BKG,
17 	YELLOW_BKG,
18 	BLUE_BKG,
19 	MAGENTA_BKG,
20 	CYAN_BKG,
21 	WHITE_BKG	
22 };
23 
24 // ...

If you remember from earlier, we’ve used this ANSI escape code to set the foreground or background color:

ESC [ code m

You can bright the above colors by using:

ESC [ code ;1m

for example, to have a bright green text you could use:

puts("\x1b[32;1mSome bright green text");

There are also ANSI escape codes to clear the screen:

1 printf("\x1b[2J");    // Clear the entire screen
2 printf("\x1b[1J");    // Clear the screen from cursor to the beginning
3 printf("\x1b[0J");    // Clear the screen from cursor to the end

and another ones to clear the current line of text:

1 printf("\x1b2dK");    // Clear the current line
2 printf("\x1b1dK");    // Clear the current line from cursor to left (beginning of the line)
3 printf("\x1b0dK");    // Clear the current line from cursor to right (end of the line)

We can abstract the above operations in ansi_escapes.c like this:

 1 // ...
 2 
 3 enum ClearCodes {
 4 	CLEAR_FROM_CURSOR_TO_END,
 5 	CLEAR_FROM_CURSOR_TO_BEGIN,
 6 	CLEAR_ALL
 7 };
 8 
 9 void clearScreen(void) {
10 	printf("\x1b[%dJ", CLEAR_ALL);
11 }
12 
13 void clearScreenToBottom(void) {
14 	printf("\x1b[%dJ", CLEAR_FROM_CURSOR_TO_END);
15 }
16 
17 void clearScreenToTop(void) {
18 	printf("\x1b[%dJ", CLEAR_FROM_CURSOR_TO_BEGIN);
19 }
20 
21 void clearLine(void) {
22 	printf("\x1b[%dK", CLEAR_ALL);
23 }
24 
25 void clearLineToRight(void) {
26 	printf("\x1b[%dK", CLEAR_FROM_CURSOR_TO_END);
27 }
28 
29 void clearLineToLeft(void) {
30 	printf("\x1b[%dK", CLEAR_FROM_CURSOR_TO_BEGIN);
31 }

don’t forget to copy the above functions signatures to ansi_escapes.h if you want to be able to use them.

Another useful group of ANSI escape code is the movement set. You can move relative to the current cursor position up, down, left, right or you can jump to a specific position. I’ve grouped these escape codes in a set of helper functions:

 1 // ...
 2 
 3 void moveUp(int positions) {
 4 	printf("\x1b[%dA", positions);
 5 }
 6 
 7 void moveDown(int positions) {
 8 	printf("\x1b[%dB", positions);
 9 }
10 
11 void moveRight(int positions) {
12 	printf("\x1b[%dC", positions);
13 }
14 
15 void moveLeft(int positions) {
16 	printf("\x1b[%dD", positions);
17 }
18 
19 void moveTo(int row, int col) {
20 	printf("\x1b[%d;%df", row, col);
21 }

Assuming that you’ve placed the above helper functions in ansi_escapes.h and ansi_escapes.c, let’s write a slightly more complex code. Start by clearing the screen, move the cursor position to (1, 1), set the text color to green, set the background color to yellow, write 3 lines of text, move back to second line, erase it and write another text on the second line, move down three lines, move right 5 positions, change text color to magenta, write something else, move down 1:

 1 #include <stdio.h>
 2 #include "ansi_escapes.h"
 3 
 4 int main(void) {
 5     setupConsole();
 6     
 7     clearScreen();
 8     moveTo(1,1);
 9 
10     setTextColor(GREEN_TXT);
11     setBackgroundColor(YELLOW_BKG);
12 
13     puts("Text line 1");
14     puts("Text line 2");
15     puts("Text line 3");
16 
17     moveUp(2);
18 
19     clearLine();
20 
21     puts("Replacement for second line");
22 
23     moveDown(3);
24 
25     moveRight(5);
26 
27     setTextColor(MAGENTA_TXT);
28 
29     puts("5 positions to right ...");
30 
31     moveDown(1);
32     
33     restoreConsole();
34 }

Here is what I see on my machine if I run the above code:

Terminal writing lines of various colors using ANSI escape codes

There are also ANSI escape codes to save and restore the text cursor position, these are useful you plan to go back later and do some edits:

printf("\x1b%d", 7);    // Save cursor

// ...

printf("\x1b%d", 8);    // Restore saved cursor

Let’s also modify the last code example, in order to save the cursor position after writing the second line and instead of moving up, let’s use the restore cursor position. Also, we’ll erase the line to the left instead of erasing the entire line:

#include <stdio.h>
#include "ansi_escapes.h"

int main(void) {
    setupConsole();
    
    clearScreen();
    moveTo(1,1);

    setTextColor(GREEN_TXT);
    setBackgroundColor(YELLOW_BKG);

    puts("Text line 1");
    printf("Text line 2");
    saveCursorPosition();
    printf("\n");
    puts("Text line 3");

    restoreCursorPosition();

    clearLineToLeft();

    puts("Replacement for second line");

    moveDown(3);

    moveRight(5);

    setTextColor(MAGENTA_TXT);

    puts("5 positions to right ...");

    moveDown(1);
    
    restoreConsole();
}

Please note that I’ve placed the ANSI escape codes for saving and restoring the cursor position in two new functions saveCursorPosition and restoreCursorPosition

This is what I see on my machine, if I run the above code:

Terminal writing lines of various colors using ANSI escape codes 2

You can also inquire about the current cursor position using:

printf("\x1b[6n");    // Print current cursor position

The problem with the above is that the position will be shown on the Terminal in this format:

^[[row;colR

If you want to not mess your already printed text, you will need a way to put the stdin stream in a no echo, unbuffered mode, read the cursor position from stdin and parse the string to recover the actual coordinates. Changing the stdin mode requires OS specific code, for example on Posix systems you will use functions from termios.h and on Windows functions from windows.h. The GitHub repo for this article contains a possible implementation for the above with this signature:

void getCursorPosition(int *row, int *col);

As a side note, Windows provides a function to get the cursor position, but you will get the absolute cursor position relative the console buffer and not the relative position to your running code as it is the case for when you use ANSI escape codes.

Let’s use getCursorPosition to get the current cursor position in our last example after the cursor was move 5 positions to the right:

#include <stdio.h>
#include "ansi_escapes.h"

int main(void) {
	
    // ...

    moveDown(3);
    moveRight(5);
    setTextColor(MAGENTA_TXT);

    int r, c;
    getCursorPosition(&r, &c);

    printf("Cursor position row = %d, col = %d\n", r, c);

    moveDown(1);
    
    restoreConsole();
}

This is what I see on my machine:

Terminal get cursor position using ANSI escape codes

If you want to learn more about C99/C11 I would recommend reading 21st Century C: C Tips from the New School by Ben Klemens:

or the classic C Bible, The C Programming Language by B.W. Kernighan, D.M. Ritchie:


Show Comments