ANSI escape codes are a way to do more than just plain text in the terminal (be it Windows cmd.exe or UNIX xterm). A picture is worth a thousand words so here’s what I was able to do with them:
All of the text appearance manipulation and coloring was done using a tiny library I wrote yesterday and a very intuitive C++ syntax. Here’s the code responsible for the screenshot above:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#include <iostream> #include "ansi_escape_codes.hpp" using namespace std; using namespace ansi_escape_codes; int main(int argc, char** argv) { cout << bold << "BOLD" << reset << endl; cout << faint << "FAINT" << reset << endl; cout << italic << "ITALIC" << reset << endl; cout << underline << "UNDERLINE" << reset << endl; cout << slow_blink << "SLOW BLINK" << reset << endl; cout << inverse << "REVERSE" << reset << endl; cout << endl; for(int n = 0; n <= 255; ++n) cout << color_n(n) << "X"; cout << reset << endl << endl; for(int n = 0; n <= 255; ++n) cout << color_bg_n(n) << "X"; cout << reset << endl << endl; for(int r = 0; r <= 255; r+=32) for(int g = 0; g <= 255; g+=32) for(int b = 0; b <= 255; b+=32) cout << color_rgb(r, g, b) << "X"; cout << reset <<endl << endl; for(int r = 0; r <= 255; r+=32) for(int g = 0; g <= 255; g+=32) for(int b = 0; b <= 255; b+=32) cout << color_bg_rgb(r, g, b) << "X"; cout << reset <<endl << endl; return 1; } |
Pretty easy to follow I hope 🙂 The library defines a bunch of stream manipulators that inject the appropriate escape sequences. For example, cout << bold << "BOLD"; will print out, you guessed it, bolded text. color_n picks from the 8 bit color table. color_rgb let’s you define a 24 bit truecolor. The _bg_ version is for selecting the background color. Here’s the complete source code for the library, hope you will find it useful!
P.S. The color_rgb does not appear to work on Mac OS terminal. So far all the codes work only on Linux; haven’t tested on Windows 🙂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
#pragma once #include <ostream> namespace ansi_escape_codes { #define ENTRY(name, code) \ static inline struct name##_opcode {} name; \ inline std::ostream& operator << (std::ostream& os, const name##_opcode&) \ { os << "\x1b[" #code "m"; return os; } ENTRY(reset, 0) ENTRY(bold, 1) ENTRY(faint, 2) ENTRY(italic, 3) ENTRY(underline, 4) ENTRY(slow_blink, 5) ENTRY(rapid_blink, 6) ENTRY(inverse, 7) ENTRY(conceal, 8) ENTRY(crossed_out, 9) ENTRY(default_font, 10) ENTRY(alt_font_1, 11) ENTRY(alt_font_2, 12) ENTRY(alt_font_3, 13) ENTRY(alt_font_4, 14) ENTRY(alt_font_5, 15) ENTRY(alt_font_6, 16) ENTRY(alt_font_7, 17) ENTRY(alt_font_8, 18) ENTRY(alt_font_9, 19) ENTRY(fraktur, 20) ENTRY(doubly_underline, 21) ENTRY(normal, 22) ENTRY(not_italic_not_raktur, 23) ENTRY(underline_off, 24) ENTRY(blink_off, 25) ENTRY(inverse_off, 27) ENTRY(reveal, 28) ENTRY(not_crossed_out, 29) ENTRY(default_foreground_color, 39) ENTRY(default_background_color, 49) ENTRY(framed, 51) ENTRY(encircled, 52) ENTRY(overlined, 53) ENTRY(not_framed_or_encircled, 54) ENTRY(not_overlined, 55) #define COLOR_ENTRY(name, code) \ static inline struct name##_color {} name; \ inline std::ostream& operator << (std::ostream& os, const name##_color&) \ { os << "\x1b[" #code "m"; return os; } COLOR_ENTRY(black, 30) COLOR_ENTRY(red, 31) COLOR_ENTRY(green, 32) COLOR_ENTRY(yellow, 33) COLOR_ENTRY(blue, 34) COLOR_ENTRY(magenta, 35) COLOR_ENTRY(cyan, 36) COLOR_ENTRY(white, 37) COLOR_ENTRY(black_bg, 40) COLOR_ENTRY(red_bg, 41) COLOR_ENTRY(green_bg, 42) COLOR_ENTRY(yellow_bg, 43) COLOR_ENTRY(blue_bg, 44) COLOR_ENTRY(magenta_bg, 45) COLOR_ENTRY(cyan_bg, 46) COLOR_ENTRY(white_bg, 47) COLOR_ENTRY(bright_black, 90) COLOR_ENTRY(bright_red, 91) COLOR_ENTRY(bright_green, 92) COLOR_ENTRY(bright_yellow, 93) COLOR_ENTRY(bright_blue, 94) COLOR_ENTRY(bright_magenta, 95) COLOR_ENTRY(bright_cyan, 96) COLOR_ENTRY(bright_white, 97) COLOR_ENTRY(bright_black_bg, 100) COLOR_ENTRY(bright_red_bg, 101) COLOR_ENTRY(bright_green_bg, 102) COLOR_ENTRY(bright_yellow_bg, 103) COLOR_ENTRY(bright_blue_bg, 104) COLOR_ENTRY(bright_magenta_bg, 105) COLOR_ENTRY(bright_cyan_bg, 106) COLOR_ENTRY(bright_white_bg, 107) struct color_n { color_n(unsigned char n) : m_n(n) {} friend std::ostream& operator << (std::ostream& os, const color_n& n); private: unsigned char m_n; }; struct color_bg_n { color_bg_n(unsigned char n) : m_n(n) {} friend std::ostream& operator << (std::ostream& os, const color_bg_n& n); private: unsigned char m_n; }; inline std::ostream& operator << (std::ostream& os, const color_n& n) { os << "\x1b[38;5;" << (int)n.m_n << "m"; return os; } inline std::ostream& operator << (std::ostream& os, const color_bg_n& n) { os << "\x1b[48;5;" << (int)n.m_n << "m"; return os; } struct color_rgb { color_rgb(unsigned char r, unsigned char g, unsigned char b) : m_r(r), m_g(g), m_b(b) {} friend std::ostream& operator << (std::ostream& os, const color_rgb& c); private: unsigned char m_r, m_g, m_b; }; struct color_bg_rgb { color_bg_rgb(unsigned char r, unsigned char g, unsigned char b) : m_r(r), m_g(g), m_b(b) {} friend std::ostream& operator << (std::ostream& os, const color_bg_rgb& c); private: unsigned char m_r, m_g, m_b; }; inline std::ostream& operator << (std::ostream& os, const color_rgb& c) { os << "\x1b[38;2;" << (int)c.m_r << ";" << (int)c.m_g << ";" << (int)c.m_b << "m"; return os; } inline std::ostream& operator << (std::ostream& os, const color_bg_rgb& c) { os << "\x1b[48;2;" << (int)c.m_r << ";" << (int)c.m_g << ";" << (int)c.m_b << "m"; return os; } } |
Cool! I get errors like this with gcc5.4:
../src/ascii_escape_codes.hpp:13:7: error: ‘ascii_escape_codes::reset’ declared as an ‘inline’ variable
ENTRY(reset, 0)
^
../src/ascii_escape_codes.hpp:9:40: note: in definition of macro ‘ENTRY’
static inline struct name##_opcode {} name; \
^
If I remove the “inline” I now get warnings such as:
../src/ascii_escape_codes.hpp:19:7: warning: ‘ascii_escape_codes::rapid_blink’ defined but not used [-Wunused-variable]
ENTRY(rapid_blink, 6)
^
../src/ascii_escape_codes.hpp:9:33: note: in definition of macro ‘ENTRY’
static struct name##_opcode {} name; \
^
It works if I ignore these warnings, but your example makes my terminal super laggy. Do you know how I can get rid of the warnings?
Compile like this: g++ -std=c++17 colors.cpp -o colors
Linux?
My output looks different? https://pasteimg.com/image/image.wu7AC
Looks similar enough to what I see on my Mac. What OS did you use?
Not every terminal (Windows CMD, Linux) supports RGB/24bit colors.
It’s probably best to use the few colors spelled out by name if one wants to be consistent across many platforms.
P.S. Thanks for reading my blog 🙂