ncurses

Introducing ncurses

ncurses is a library for interfacing with a computer terminal like the terminal emulator we have been using in class.

Here is a “Hello Curses!” program in a file named hello_curses.cpp:

#include <ncurses.h>
using namespace std;

int main() {
    initscr();
    move(11, 26);
    printw("Hello CSC 222 from ncurses!");
    refresh();
    getch();
    endwin();

    return 0;
}

Notice that we are not including iostream, but are instead including ncurses.h. This is because we are using ncurses for our user interface instead of the standard input / output command line.

To compile this program we will need to link it to the ncurses library:

$ g++ hello_curses.cpp -o hello_curses -lncurses

The program calls initscr to create a screen, move to move the curser to row 11 and column 26, printw to print the string "Hello CSC 222 from ncurses!" beginning at the curser location onto the screen, refresh to draw the screen to the terminal, getch to read a character of user input, and endwin to close the screen.

We are using getch in this first example just to pause the screen to allow for viewing. In our next example, read_chars.cpp, we will use it to read the character entered and do something “useful” with it:

#include <ncurses.h>
using namespace std;

int main() {
    char ch;

    initscr();
    noecho();
    move(9, 25);
    printw("Enter a character or '!' to quit: ");
    refresh();
    ch = getch();

    while (ch != '!') {
        clear();
        mvprintw(9, 25, "Enter a character or '!' to quit: ");
        mvprintw(
           12, 18,
           "You entered '%c', which has a numeric value of %d.",
           ch, ch
        );
        move(9, 59);
        refresh();
        ch = getch();
    }
    endwin();

    return 0;
}

The noecho function is called to turn off echoing of keyboard input. After prompting the user to enter a character or '!' to quit, we enter a loop which calls clear to erase everything in the screen. We then call mvprintw, which combines move and printw into a single function, to redraw the prompt that was just erased.

Another call to mvprintw displays the character typed along with its numeric value. The move(9, 59) on the next line is used only for cosmetic reasons. It looks nicer to have the cursor appear at the end of the prompt.

Finally refresh draws the screen and ch = getch() waits for the user to enter another character.

Exploring the window environment

This next example, interrogate_env.cpp, uses several of the available ncurses functions to get information about the window environment:

#include <ncurses.h>
#define COLOR_ORANGE 8
using namespace std;

int main() {
    int rows, cols;

    initscr();
    noecho();
    refresh();

    WINDOW* win1 = newwin(5, 76, 1, 2);
    WINDOW* win2 = newwin(15, 46, 6, 18);

    wattron(win1, A_STANDOUT);
    wmove(win1, 2, 22);
    wprintw(win1, "ncurses"); 
    wattroff(win1, A_STANDOUT);
    wprintw(win1, " Environment Interrogator"); 
    box(win1, 0, 0);
    wrefresh(win1);

    const char* colors = has_colors() ? "YES" : "NO";
    wmove(win2, 2, 5);
    wprintw(win2, "Supports color: "); 
    if (colors == "YES") {
        start_color();
        init_pair(1, COLOR_CYAN, COLOR_BLACK);
        wattron(win2, COLOR_PAIR(1));
    };
    wprintw(win2, colors); 
    if (colors == "YES") {
        wattroff(win2, COLOR_PAIR(1));
    };

    const char* change_color = can_change_color() ? "YES" : "NO";
    wmove(win2, 4, 5);
    wprintw(win2, "Supports change color: "); 
    if (change_color == "YES") {
        init_color(COLOR_ORANGE, 1000, 500, 0);
        init_pair(2, COLOR_ORANGE, COLOR_BLACK);
        wattron(win2, COLOR_PAIR(2));
    };
    wprintw(win2, change_color); 
    if (change_color == "YES") {
        wattroff(win2, COLOR_PAIR(2));
    };

    wmove(win2, 6, 5);
    getmaxyx(stdscr, rows, cols);
    wprintw(win2, "Main window size: %d by %d", rows, cols); 
    wmove(win2, 8, 5);
    getmaxyx(win2, rows, cols);
    wprintw(win2, "Current window size: %d by %d", rows, cols); 
    wmove(win2, 10, 5);
    getmaxyx(win1, rows, cols);
    wprintw(win2, "Top window size: %d by %d", rows, cols); 
    wmove(win2, 12, 5);
    getbegyx(win2, rows, cols);
    wprintw(win2, "Current window top left pos: %d, %d", rows, cols); 

    box(win2, 0, 0);
    wrefresh(win2);

    move(22, 25);
    printw("Enter a character quit: ");
    getch();
    endwin();

    return 0;
}

Running this will on a terminal that supports colors and color changes will produce a screen something like this:

interrogate_env screenshot

Resources