ANSI Fun

I just installed Ubuntu 12.04 (Precise) on both my netbook and desktop. I haven't used a Linux OS for a while and I wanted to see how different it would be when coding. I knew some things that Window's can do, so I wanted to try to code them on Linux as well.

I've been fond of the conio.h header file from back when I first learned programming and thought it was just awesome. The more and more I learn, the more I try to get away from it. I have doing research on clearing consoles on Linux and ran across a small piece of code: cout << "\x1B[2J";. I decided to do some digging and found out it was actually and ANSI escape code. That got me thinking.

Nowadays, the odds that a compiler isn't ANSI compliant is slim to none (I know of none) so I figured, "What better way to make something more standard?" I don't believe compilers are required to be ANSI compliant (I could be wrong) but with very few not being compliant, I figure it would cover more OS's than even doing OS specific libraries (windows.h, termios.h, etc.). (Am I wrong that ANSI is compiler dependent and not C++ dependent?)

For the last few hours, I was having a good bit of fun with the ANSI codes so I started typing up my header file again.

Here is the beta, if you will:
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
namespace vp {
   void consolecursor(bool show = true) {
      if (show)
         std::cout << "\x1B[25h";
      else
         std::cout << "\x1B[25l";
   }
   bool gotoxy(unsigned short x = 1, unsigned short y = 1) {
      if ((x == 0) || (y == 0))
         return false;
      std::cout << "\x1B[" << y << ";" << x << "H";
   }

   void clearscreen(bool moveToStart = true) {
      std::cout << "\x1B[2J";
      if (moveToStart)
         gotoxy(1,1);
   }
   enum colors { BLACK = 0, RED, GREEN, YELLOW, BLUE, PURPLE, CYAN, GREY,
                 LIGHTGREY, LIGHTRED, LIGHTGREEN, LIGHTYELLOW, LIGHTBLUE,
                 LIGHTPURPLE, LIGHTCYAN, WHITE, DEFAULT };

   void consolecolor(colors textColor = DEFAULT, colors backgroundColor = DEFAULT) {

      // Set default foreground color
      if (textColor == DEFAULT)
         std::cout << "\x1B[39m";

      // Set bright foreground color
      else if (textColor > GREY) {
         // Set bright mode
         std::cout << "\x1B[1m";
         // Set color
         std::cout << "\x1B[3" << textColor - LIGHTGREY << "m";
      }

      // Set normal foreground color
      else {
         // Set normal mode
         std::cout << "\x1B[22m";
         // Set color
         std::cout << "\x1B[3" << textColor << "m";
      }

      // Set default background color
      if (backgroundColor == DEFAULT)
         std::cout << "\x1B[49m";

      // Set bright background color
      else if (backgroundColor > GREY) {
         // Set bright mode
         std::cout << "\x1B[1m";
         // Set color
         std::cout << "\x1B[4" << backgroundColor - LIGHTGREY << "m";
      }

      // Set normal background color
      else {
         // Set normal mode
         std::cout << "\x1B[22m";
         // Set color
         std::cout << "\x1B[4" << backgroundColor << "m";
      }
   }

   void setconsoletextcolor(colors textColor) {

      // Set bright foreground color
      if (textColor > GREY) {
         // Set bright mode
         std::cout << "\x1B[1m";
         // Set color
         std::cout << "\x1B[3" << textColor - LIGHTGREY << "m";
      }

      // Set normal foreground color
      else {
         // Set normal mode
         std::cout << "\x1B[22m";
         // Set color
         std::cout << "\x1B[3" << textColor << "m";
      }
   }

   // Numerical color setter
   /*bool setconsoletextcolor(int textcolor = -1) {
      if (((textcolor < 0) || (textcolor > 7)) && (textcolor != -1))
         return false;
      std::cout << "\x1B[3" << textcolor << "m";
      return true;
   }*/
}


Essentially, I'm pasting this to show some people (who never knew about ANSI, like myself) what you can do with ANSI. I also wanted to see what opinions you guys have of the code, things that can be added, removed, etc. I did try to error test it, but it's getting late so I decided to turn in.

Tomorrow, I'm going to look at Duoas' code again for Linux PressAnyKey Code to see if I can figure out how to get it to work like the Window's OS version. In the meantime, I welcome Criticism.

~Volatile Pulse
I decided to do some digging and found out it was actually and ANSI escape code.
[...]
Nowadays, the odds that a compiler isn't ANSI compliant is slim to none
You're confusing two different meanings of the word "ANSI".
The former refers to a standard for teletype interfaces. In this case, to the special case of how the terminal should behave when it encounters certain strings of characters.
The latter is an informal way of saying that a compiler complies with the standard to an acceptable degree. If you want to get technical, you'd have to say that the compiler is "ISO/IEC compliant" or maybe even "ISO/IEC 14882:1998 compliant".
There's still other completely unrelated standards also typically known just as "ANSI". The "ANSI codepage", for example, is an incorrect name for ISO/IEC 8859-1.

Just because a compiler is said to be "ANSI compliant" doesn't mean it has to comply with every standard by the ANSI plus every standard that's known as "ANSI".

In the particular case of control codes, the compiler has nothing to do with how the device will react, and it can't make any guarantees about it. In fact, the device might not be a console at all, but a file, or something much more exotic like a LED dot matrix.
Last edited on
Alright, so aside from the standard output being piped into something other than a console (which is known to happen a lot) what restrictions do I specifically have to look at? Is it really a compiler limitation, or is it a limitation on the console itself? Also, aside from using a third party library, what is the best way to make sure this is handled correctly (ensure that it's on a console and not a file/LED matrix, ensure that the escape codes are going to be handled correctly)? Do I still need to use OS specific libraries? Is there anyway to make sure something like this is portable?

I appreciate the information, and I have more to look into now. Is there a specific compiler/standard output console/etc. that you know of that wouldn't be able to handle the escape codes correctly?
ensure that it's on a console and not a file/LED matrix, ensure that the escape codes are going to be handled correctly


1. Don't use escape codes
2. Explicitly create a console instead of assuming you write to one. That way you can also ensure you really get the kind of console you want, and you know what you can do with it.

There are some somewhat portable console API's like http://www.gnu.org/software/ncurses/ , but generally the only real reason you'd ever want to do this nowadays is for esoteric reasons.
Aside from using an API, what options do I have?

I know system is bad, so I can't use that, and it also has limited functionality depending on the OS anyways.

I don't want to use curses since it has to be installed on each machine just to compile the code anyways. And if I'm not mistaken, it creates (in a sense) it's own version of the terminal (the code doesn't look like the standard iostream at all to me).

I know that using the OS API is a possibility, but that only covers a small range of OS's anyways. I know that the escape codes cover a much broader range of systems, but as pointed out, it's still limited to console output (which is what I am aiming for). The OS's APIs ensure that it is infact a console (unless I'm mistaken as to how the code works), so that should guarantee that the program isn't writing to a file (or is there absolutely no guarantee).

I have used several Libraries before that allow for console manipulation, but this goes along with curses. I was looking for something more along the lines of a way to ensure that the code will behave the same on each system (or at least as many as possible).

Obviously without using a C++ standard feature, there is no 100% works on everything, unless you ensure you cover everything.

With all of that, escape codes still seem to be the best option here unless I have completely overlooked something. It has the widest range without requiring a third party API, should work on a large range of consoles, and has a standardized definition of how it should act.

As I was researching the escape codes, it seems that Linux (or at least a large portion of them) returns the escape codes when using the termios. I haven't looked into Duoas's function yet, but that is my next step.
To use curses you just need to supply a .dll when you distribute it on Windows (UNIX systems will either have it installed or have a package for it). Windows will look for the DLL automatically according to certain rules (see: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682586.aspx ).

As for OS APIs ensuring its a console, that isn't true of the POSIX API. Anything you open with open() could be a stream, a file, a device or a socket. The OS treats them all the same by design because it simplifies a lot of things. You can check if it is a console by using the isatty ("is a teletype terminal") function.
Last edited on
I was referring to publishing code. I hate being forced to download dependencies (curses DLL in this case) so I wouldn't want to force someone else to do it unless absolutely necessary. As for using curses on UNIX systems, I tried to compile a simple program with the #include <curses.h> header file and got an error saying it wasn't found. I thought curses came with Linux. Again, it resorts to a dependency that isn't distributed and back to what I said about earlier, curses essentially creates it's own console to manipulate using it's defined functions. This isn't what I have in mind since it would be just like using a toolkit in this case then.

As for the OS APIs, I don't know anything really about the POSIX API since I just got around to it. It seems that the Window's API is massive (which I think was incredibly dumb to put everything in one header file) but I'm concerned that the POSIX API is going to either be hindered or have reduced functionality. I know the console isn't the place to be doing much, if any of these things, but you'll always have those people that want/need these features and have a mentality like me (obviously I was/am one of those people).

My ultimate goal is to be able to provide a header file that enables beginners (or anyone else that wants them) and option to be able to use the commonly used things that are frowned upon (which I'm beginning to think the path I'm taking is also frowned upon). I want to be able to point someone to what they need and allow them to use whatever they want. In all reality, it's going to end up mainly being just for my personal use, but I thought it would be a good idea to show people that using the Escape Codes is a possibility when resorting to a non standard feature.

What direction should I be looking towards to make sure that my code is only used the way it was intended (console applications)? Just implement the OS API? I want to eventually use the OS API's anyways to limit the numbers passed for the gotoxy function, but I need to break apart the windows header file and figure out what's possible with POSIX.
I hate being forced to download dependencies
There's various ways to bundle dependencies. Static linking is one.

And if I'm not mistaken, it creates (in a sense) it's own version of the terminal (the code doesn't look like the standard iostream at all to me).
If your problem is with the syntax, then just write a wrapper for it. It's a C library, so of course it's going to look different than iostream.

Don't reinvent the wheel just because the wheels we have now don't exactly fit your car. Tweak them until they do, instead.
You don't have to download curses if the developer puts the dll along with the executable. On Linux/BSD it's different; the runtime file (libncurses.so - which reminds me, don't link with -lcurses but -lncurses) will normally be pre-installed. You couldn't find curses.h because you didn't have the development files installed (look for a package like libncurses-dev or libncurses-devel). The user wouldn't have to install the development files unless they wanted to modify or re-compile your code.

Curses doesn't "create its own console", it just uses the OS API to manage an already-existent one. The point of it is to abstract the details of the OS API so that you can write the same code on every system that curses supports. If you don't like the API it provides (I don't think anyone does) then just take helios' advice and wrap it up in a class.

The Windows API isn't wholly contained in Windows.h. Windows.h includes several other header files.

@helios
You can't do that with real wheels though.
Last edited on
Heh heh heh...
http://www.cplusplus.com/forum/general/18200/#msg92479

The so-called "ANSI" terminal control codes are essentially VT-100 codes. Not all terminals support them -- not even modern software terminal emulators.

They are fun to play with though. :-)


If you are serious about playing with the terminal, you need to use either the terminfo library directly (like I do here http://cplusplus.com/articles/4z18T05o/#POSIX ) or the higher-level NCurses library.

Good luck!

[edit] Sorry, I started to respond earlier, before the last few responses.
Last edited on
That's awesome Duoas. I have some questions though. I want to get started handling POSIX along side with windows API since I just started with Ubuntu Precise. Is there anyway to tell at runtime if the console accepts Escape Codes or not? I was also wondering if there is a breakdown of the windows.h header that shows specifically what header each function is in. I think it's dumb to have to include the entire windows header when you only need a handful of functions.

Essentially, I want to create a gotoxy, clearscreen, console colors and console cursor, as well as a getch function. I reread your articles about once a month because I believe the information in them is great and haven't gotten to play with any of the POSIX functions yet, but I believe you wouldn't have posted them if they weren't complete. Is there anything I should know about when dealing with the console and POSIX functions? Like, is it possible that it works one way on Linux, but differently on Unix or Mac or even on different Linux versions?

The world of C++ is just so massive -.-
If you really want to do cross-platform console stuff, look into NCurses (for POSIX) and PDCurses (for Windows). They are essentially the same library.

If you really want to do it low-level, start perusing the ncurses (3) and terminfo (5) man pages. Also, this is "the" place to start learning more: http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/

The terminfo (and older termcap) libraries exist simply because the non-Windows PC world uses hundreds of different keyboard hardwares, all with different control codes. Some are smarter than others.

In any case, you should also know that printf() and other standard output streams may actually modify the output in ways that prevent or booger kbd control sequences. Hence the putp() function...

It is actually a pretty steep learning curve, so good luck!
Topic archived. No new replies allowed.