17 comments

[ 2.7 ms ] story [ 57.0 ms ] thread
This Haskell package http://hackage.haskell.org/packages/archive/ansi-terminal/0.... does all of the hard work for Haskell command line tools looking to use colors, etc. I used it in my task manager, shpm, and it was awesome.
I was a little disappointed by the explanation – I was expecting this to dive deep into how ANSI code came to be, and how they worked. Not just "hey they exist and are neat."

You're probably better off reading the Wikipedia article:

https://en.wikipedia.org/wiki/ANSI_escape_code

Some of the much more interesting codes are the ones that control cursor positioning and redrawing, and are used to create ASCII UIs by libraries like ncurses & readline, and programs like vim & tmux.

I've written a rudimentary chat interface using native ANSI code, which was quite a bit trickier than it looks, but a fun exercise. The tricky part was ensuring that an interactive line of input remained at the bottom of the screen, while the chat room history keeps scrolling above[1].

https://github.com/ChartBoost/zmq-examples/blob/master/chat/...

[1]: of course, if you're actually building something for production, don't be an idiot like me and use a real library like ncurses.

Also an interesting, useful reference:

http://vt100.net/docs/vt100-ug/

Note that this doesn't include all VT100 escape codes supported by modern terminals, but it is the basis for how they all work.

Weak explanation and crummy code. It didn't answer one of my long standing ansi escape code questions: whats the deal with that 256 (really 216) color space that programs like htop use? How do I get that, and why does it exist? Also:

"cls.ESCAPE % cls.BOLD % cls.UNDERLINE % cls.COLOR['white']"?

Seriously? The "+" operator does the exact same thing in this case and is easier to read. I can forgive the typos in the article, but teaching others with poor code? That is less easy to forgive. If the original author reads this, please take the time to revise the article, the code, and resubmit.

Give him a break, he's still in high school.
> Give him a break, he's still in high school.

That was a break. Constructive criticism is useful, as opposed to lowered expectations based on ageism.

Ageism!? Supposing I take this response seriously, instead of rolling my eyes and thinking STFU (as I am wont to receive such inanity) --

Asking if this is a serious submission, calling a beginner's effort "crummy" and declaring the whole thing nearly unforgivable is not constructive criticism. The one opportunity the OP had to be constructive, he blew: he could have written an improved code sample so that cwoebker could learn something directly.

You can talk to children and beginners however you want. My experience has shown me that encouraging such people by gently correcting mistakes and offering feedback along with praise, is a very likely to encourage people to keep trying. Especially when nothing important is at stake.

Traditionally, VTxxx terminals only supported 8 colours and a 'bold' variant, while PC hardware supported 16 colours and no bold, so it became traditional to map the 'bold' variants of the basic 8 colours to the brighter half of the PC 16-colour palette. Unfortunately, VTxxx doesn't have a way to set the background colour to bold (why would it, boldface is a thing for text, not backgrounds) so, people had to resort to hacks like setting the 'bold' flag to get a bright foreground colour, then setting the 'inverse' flag to switch it to the background.

Some terminals actually properly support all 16 colours directly, without bold/inverse hacks, but the standard "xterm" and "vt100" terminal definitions don't, for compatibility reasons. If you put something like this in a shell startup script:

    TERM=xterm-16color
    export TERM
...then you'll be advertising to any programs you run that your terminal supports all 16 colours.

The 256-colour mode is a far more recent invention, and I'm not sure exactly why it came about; I suspect Thomas Dickey had a spare byte in the xterm framebuffer data structure or something and a 256-colour palette was the obvious choice. It is a 256-colour palette, by the way; by default you have the original 16 terminal colours, a 216-colour RGB cube, and a bunch of greyscale values to pad it out to 256 colours, but there's an escape sequence to set the RGB value for any of the 256 palette entries to any value.

If you want to specfically know how to pick colours from the 256-colour palette and you don't mind hard-coding control sequences, the official reference is the xterm documentation: http://invisible-island.net/xterm/ctlseqs/ctlseqs.pdf

Specifically, you want the section named "Functions using CSI, ordered by the final character(s)", then scroll down to the function ending in 'm', named "Character Attributes". To summarize, to set the foreground colour to palette entry N (0 <= N <= 255), send:

    ESC[38;5;Nm
...and to set the foreground colour to the palette entry closest to the given RGB tuple (where R, G and B are between 0 and 255), send:

    ESC[38;2;R;G;Bm
To set the background colour, it's the same as above except with "48" instead of "38".

If you'd rather use a library to talk escape codes rather than hard-coding them in your program, you'll need to set the TERM variable to a setting that describes a 256-colour terminal (such as xterm-256color), and then apps (like Vim) and libraries (like terminfo) that respect that environment variable will report the full 256 colours as available.

Modern versions of ncurses come with command-line tools that look up the current terminal definition in the terminfo database and will extract information from it; in my terminal I can find out how many colours the active terminal definition reports with:

    $ tput colors
    256
...and I can print a prettily-coloured message like this:

    $ tput setaf 2; echo "hello, world"; tput sgr0
(where "setaf" means "Set ANSI Foreground", 2 is a palette entry number, and "sgr0" is a historical phrase meaning "reset all graphics attributes to defaults"). For a list of names you can give to tput, see terminfo(5).
Why make a class with class methods? Using a plain old module would do the same thing, easier.
Indeed, I made my own one of these a few years ago, it was really just a module defining a bunch of constants. Well, not entirely constants. Rather than defining, say, FG_RED, BG_RED, FG_GREEN, BG_GREEN, etc. I had functions FG and BG that took color codes, like FG(RED) + BG(GREEN) + BOLD + "Garish horrible colors!!" + RESET.

Actually I prefer working in inline escapes like that to curses overly complex method of defining color pairs and only ever specifying which to use at write-time. I wish curses had some way to inline attributes in a piece of text, that I can then use as normal data, pass around, etc.

Hey guys, I am the one who wrote this little post. My friend suggested to submit it to HN. I didn't expect to land on the front page. I'll make sure to fix all the bugs and spelling mistakes and improve the examples etc. Thanks a lot for the feedback.
I made a bunch of fixes. Thanks for the feedback again. Let me know if you find any other issues with it.