An Introduction to Ncurses

My previous post was on the Galton Board which required the ability to print at a specified location in a specified colour within the terminal. Both these can be done without any additional libraries which is what I did for the first version of the Galton Board. However, a better solution is to use the Ncurses Posix library which provides the necessary functionality and much more.

In this post I will introduce the two abilities I initially required, namely moving around and printing in colour. If you want to look into Ncurses further I recommend Dan Gookin's book on the subject.

Installing Ncurses

Ncurses is a Posix library and can be used with Linux, OS/X or, if you use Windows, with Cygwin. There are various ways of actually installing the library so I recommend you search for "installing ncurses on . . ." to find the best way for your particular OS. Once installed you can just #include the header file and you are ready to go.

The Project

For this project I will write three simple functions to demonstrate the following features of Ncurses.

  • Printing characters, strings and numbers
  • Moving around the terminal
  • Printing in colour

Create a new folder and within it create a single empty file called ncursesdemo.c. You can download the source code from the Downloads page or clone/download from Github if you prefer.

Now type or paste the following into the file.

ncursesdemo.c part 1

#include<ncurses.h>
#include<stdlib.h>

//--------------------------------------------------------
// FUNCTION PROTOTYPES
//--------------------------------------------------------
void printing();
void moving_and_sleeping();
void colouring();

//--------------------------------------------------------
// FUNCTION main
//--------------------------------------------------------
int main(void)
{
    initscr();

    addstr("-----------------\n| code-in-c.com |\n| Ncurses Demo  |\n-----------------\n\n");
    refresh();

    //printing();

    //moving_and_sleeping();

    //colouring();

    addstr("\npress any key to exit...");
    refresh();

    getch();

    endwin();

    return EXIT_SUCCESS;
}

The first line in main is initscr(); which starts Ncurses. The next line just writes a heading, but note that it uses addstr, one of a few Ncurses functions for printing. Ncurses actually outputs to a buffer so to flush the buffer to the screen we then need to call refresh.

Leave the three function calls commented out for the moment, after which there is a message to hit any key and a call to getch to pick up a key press. Without this the program would terminate immediately and we probably wouldn't see a thing!

Finally comes the all-important endwin to end Ncurses before the program exits.

The program doesn't do much so far, just displays a couple of bits of text and waits for you to bash the keyboard. However, let's compile and run it anyway just to ensure Ncurses is working properly. You'll need to include -lncurses in the compiler command to link in the Ncurses library.

Compile and Run

gcc ncursesdemo.c -std=c99 -lncurses -o ncursesdemo
./ncursesdemo

Program Output

-----------------
| code-in-c.com |
| Ncurses Demo  |
-----------------


press any key to exit...

Not very impressive so far as we haven't done anything you can't do more easily without Ncurses. But before we get to the more interesting stuff let's look at three functions for printing stuff in Ncurses.

ncursesdemo.c part 2

//--------------------------------------------------------
// FUNCTION printing
//--------------------------------------------------------
void printing()
{
    addstr("This was printed using addstr\n\n");
    refresh();

    addstr("The following letter was printed using addch:- ");
    addch('a');
    refresh();

    printw("\n\nThese numbers were printed using printw\n%d\n%f\n", 123, 456.789);
    refresh();
}

The three functions are

  • addstr - prints the specified string
  • addch - prints a single char
  • printw - the Ncurses equivalent of printf

These work in the same way as the stdio.h puts, putchar and printf except that you need to call refresh after to copy from the buffer to the screen.

Uncomment printing(); in main, then compile and run again: you should see this.

Program Output

-----------------
| code-in-c.com |
| Ncurses Demo  |
-----------------

This was printed using addstr

The following letter was printed using addch:- a

These numbers were printed using printw
123
456.789000

press any key to exit...

Now for the moving_and_sleeping function.

ncursesdemo.c part 3

//--------------------------------------------------------
// FUNCTION moving_and_sleeping
//--------------------------------------------------------
void moving_and_sleeping()
{
    int row = 5;
    int col = 0;

    curs_set(0);

    for(char c = 65; c <= 90; c++)
    {
        move(row++, col++);
        addch(c);
        refresh();
        napms(100);
    }

    row = 5;
    col = 3;

    for(char c = 97; c <= 122; c++)
    {
        mvaddch(row++, col++, c);
        refresh();
        napms(100);
    }

    curs_set(1);

    addch('\n');
}

This prints the alphabet in upper and lower case, moving one space down and across for each letter. The row and column variables are initialised to 5 (to allow for the heading) and 0, and the Ncurses curs_set function is used to hide the cursor.

The for loop iterates the upper case letters, moving to the current position with move, printing the character with addch, calling refresh and then using the napms ("nap for milliseconds") function to pause for a tenth of a second.

Moving and then printing is so common that the three printing functions shown in the printing function have equivalents that move and print in one function call. They have the same names prefixed with mv, and I have used mvaddch for the second loop which prints lower case letters.

Finally, don't forget to show the cursor again. Uncomment moving_and_sleeping, then compile and run.

Program Output

-----------------
| code-in-c.com |
| Ncurses Demo  |
-----------------

A  a
 B  b
  C  c
   D  d
    E  e
     F  f
      G  g
       H  h
        I  i
         J  j
          K  k
           L  l
            M  m
             N  n
              O  o
               P  p
                Q  q
                 R  r
                  S  s
                   T  t
                    U  u
                     V  v
                      W  w
                       X  x
                        Y  y
                         Z  z

press any key to exit...

This is the output after the program has run, but what you don't see here is that each character appeared one at a time with a tenth of a second delay.

Now we get to the last part of the program, printing in Glorious Technicolor.

ncursesdemo.c part 4

//--------------------------------------------------------
// FUNCTION colouring
//--------------------------------------------------------
void colouring()
{
    if(has_colors())
    {
        if(start_color() == OK)
        {
            init_pair(1, COLOR_YELLOW, COLOR_RED);
            init_pair(2, COLOR_GREEN, COLOR_GREEN);
            init_pair(3, COLOR_MAGENTA, COLOR_CYAN);

            attrset(COLOR_PAIR(1));
            addstr("Yellow and red\n\n");
            refresh();
            attroff(COLOR_PAIR(1));

            attrset(COLOR_PAIR(2) | A_BOLD);
            addstr("Green and green A_BOLD\n\n");
            refresh();
            attroff(COLOR_PAIR(2));
            attroff(A_BOLD);

            attrset(COLOR_PAIR(3));
            addstr("Magenta and cyan\n");
            refresh();
            attroff(COLOR_PAIR(3));
        }
        else
        {
            addstr("Cannot start colours\n");
            refresh();
        }
    }
    else
    {
        addstr("Not colour capable\n");
        refresh();
    }
}

Not all terminals can print in colour so first we need to check using the has_colors function. If so we then need to call the start_color(). Unlike has_colors this returns OK rather than true so we need to check for that.

(In this function if colour is not available a message is printed and the function ends. For production code it might be better to provide a non-coloured fallback.)

Assuming you have 8 colours available then there are 8 * 8 = 64 possible combinations or pairs which can be used for backgrounds and foregrounds. Before using a pair it has to be set using the init_pair function which takes a number between 1 and 64 (not 0 to 63), the foreground colour and the background colour. Here I have set three pairs - no, the one with both set to green isn't a mistake!

This is a full list of Ncurses colour constants:

  • COLOR_BLACK
  • COLOR_RED
  • COLOR_GREEN
  • COLOR_YELLOW
  • COLOR_BLUE
  • COLOR_MAGENTA
  • COLOR_CYAN
  • COLOR_WHITE

Now that we have a few colour pairs we can use them to print with. Firstly call attrset to set the printing attributes to the required pair, then addstr/refresh. The attribute can then be "switched off" with attroff. In the second output I have or'ed the colour pair with A_BOLD which makes the foreground brighter; this is why we can use the same colour constant for both background and foreground. Finally, just to make the screen more garish than it already is, I have printed a bit more text, this time with a tasteful magenta-on-cyan scheme.

Uncomment colouring(); in main and compile/run again. You should see something like this.

Program Output

-----------------
| code-in-c.com |
| Ncurses Demo  |
-----------------

Yellow and red

Green and green A_BOLD

Magenta and cyan

press any key to exit...

This has been a very quick introduction to what I feel are the most useful features of Ncurses but as I mentioned above the library is much more comprehensive than this and deserves further investigation.

Please let me have your comments and suggestions, and follow Code in C on Twitter for news of future posts.

Leave a Reply

Your email address will not be published. Required fields are marked *