CS330 NCurses


Highlights of this lab:

For an excellent resource on NCURSES go to"The Linux Documentation Project" web site


Background behind Ncurses

The following indented section is copied from "The Linux Documentation Project" section on NCURSES.

In the olden days of teletype terminals, terminals were away from computers and were connected to them through serial cables. The terminals could be configured by sending a series of bytes to each of them. All the capabilities (such as moving the cursor to a new location, erasing part of the screen, scrolling the screen, changing modes, changing appearance, colors, brightness, blinking, underlining, reverse video etc.) of terminals could be accessed through these series of bytes which are usually called escape sequences because they start with an escape(0x1B) character. Even today, with proper emulation, we can send escape sequences to the emulator and achieve the same effect on the terminal window.

Suppose you wanted to print a line in color. Try typing this on your console.

echo "^[[0;31;40mIn Color"

The first character is an escape character, which looks like two characters ^ and [. To be able to print that you have to press CTRL+V and then the ESC key. All the others are normal printable characters. You should be able to see the string "In Color" in red. It stays that way and to revert back to the original mode type this.

echo "^[[0;37;40m"

Now, what do those magic characters mean? Difficult to comprehend? They might even be different for different terminals. So the designers of UNIX invented a mechanism named termcap. It is a file that lists all the capabilities of a particular terminal, along with the escape sequences needed to achieve a particular effect. In the later years, this was replaced by terminfo. Without delving too much into details, the concept of the mechanism is to allow application programs query the terminfo database and obtain the control characters to be sent to the terminal or terminal emulator.

1.1. What is NCURSES?

You might be wondering, what the import of all this technical gibberish is. In the above scenario, every application program is supposed to query the terminfo and do the necessary stuff(sending control characters etc.). It soon became difficult to manage this complexity and this gave birth to 'CURSES'. Curses is a pun on the name "cursor optimization". The Curses library forms a wrapper over working with raw terminal codes, and provides highly flexible and efficient API (Application Programming Interface). It provides functions to move the cursor, create windows, produce colors, play with mouse etc. The Application programs need not worry about the underlying terminal capabilities.

So what is NCURSES? NCURSES is a clone of the original System V Release 4.0 (SVr4) curses. It is a freely distributable library, fully compatible with older version of curses. In short, it is a library of functions that manages an application's display on character-cell terminals. In the remainder of the document, the terms curses and ncurses are used interchangeably.

Note: On Tera terms, the font is shown as white with the background black. The above commands return it to that state. On Linux, the font is shown in black with the background white. To return to the Linux xterm colors try:

echo "^[[30;49m"

Numbers 30 to 39 affect the foreground (the text) and numbers 40 to 49 affect the background. For more on these, see: http://networking.ringofsaturn.com/Unix/Bash-prompts.php


The Basics

To get you started with Ncurses, you first need to know how to write a basic program and compile it.

You are using the ncurses library. This means two things:

  1. you must: #include <ncurses.h>
  2. to compile and link, you use: g++ myprog.cpp -lncurses

A basic program to print "Hello World" to the top of the screen might look like this:

#include <ncurses.h>

int main()
{
		initscr();                //Start curses mode
		printw("Hello World");    //Print Hello World
		refresh();                //Print it to the screen (otherwise buffered)
		getch();                  //Wait for user input (before quitting)
		endwin();                 //End curses mode
		
		return 0;
}

A few more details about these functions:

Every program should have at least these four functions.


Output

There are three major ways that you can output to the screen:

  1. addch()--Prints single characters with attributes
  2. printw()--Prints formatted output similar to the C programming language printf()
  3. addstr()--Print strings

When we are talking about outputting, it is worth mentioning, that we can output anywhere on the screen. If we want to print to the fourth row down and second column, we could say:

move(4,2);
printw("Hello World");

Better yet, we could use mvprintw() to move the cursor to a given point, and then print. For instance, this does the same thing as the above two lines:

mvprintw(4,2,"Hello World");

In addition, we can specify different windows and print at specified locations within the windows. The following provides a table summarizing all of these printing options:

Output to stdscr Output to a specific row, column of stdscr Output to specified window ("mywin") Output to a specific row, column in "mywin"
addch(ch); mvaddch(row,col,ch); waddch(mywin,ch); mvwaddch(mywin,row,col,ch);
printw("hello"); mvprintw(row,col,"hello"); wprintw(mywin,"hello"); mvwprintw(mywin,row,col,"hello");
addstr(str); mvaddstr(row,col,str); waddstr(mywin,str); mvwaddstr(mywin,row,col,str);

The following summarizes the types used in the above table: char ch; char *str; int row, col; WINDOW *mywin

A couple of things to note about this are:


Input

There are three major ways that you can input from the user:

  1. getch()--gets a character
  2. scanw()--gets a formatted input
  3. getstr()--gets strings
Input from stdscr Input from specified location on stdscr Input from specified window Input from a specified location of a specific window
ch=getch(); ch=mvgetch(row,col); ch=wgetch(mywin); ch=mvwgetch(mywin,row,col);
scanw("%d%s",&ch,str) mvscanw(row,col,"%d%s",&ch,str); wscanw(mywin,"%d%s",&ch,str); mvwscanw(mywin,row,col,"%d%s",&ch,str);
getstr(str); mvgetstr(row,col) wgetstr(mywin,str); mvwgetstr(mywin,row,col,str);

The following summarizes the types used in the above table: int ch; char str[90]; int row, col; WINDOW *mywin

Notes:

The basic format for user input (using the getch()) might be the following:

int ch;
...
initscr();

cbreak();                      //Line buffering disabled, Pass on everything to me

keypad(stdscr,TRUE);           //accept non-typewriter keys

noecho();                      //Don't echo what the user types

while ((ch=getch())!= 'q')     //while the user hasn't pressed q 
{

		switch(ch)
		{
				case KEY_LEFT:		//if left arrow key is pressed
					//do something
					break;
				case KEY_RIGHT:		//if right arrow key is pressed
					//do something
					break;
				case KEY_UP:		//if up arrow key is pressed
					//do something
					break;
				case KEY_DOWN:		//if down arrow key is pressed
					//do something
					break;
		}

}
A few things to note about this are the following:

Creating Multiple Windows

By default, when you call initscr(), you get a window called stdscr. Any prints or refreshes (without specifying another window) act on stdscr. To create additional windows, you will use a function called newwin() it returns a pointer to a structure WINDOW. To delete a window you use delwin();

The following sample places a 10x3 window at the center of the screen:

#include <ncurses.h>


int main()
{	
		WINDOW *mywin;
		int startx, starty, width, height;
		int ch;


		initscr();            //Start curses mode
		cbreak();             //Line buffering disabled, Pass on everything to me
		
		keypad(stdscr, TRUE); //Set this so that you can get the arrow keys
	
		height=3;
		width=10;
		starty=(LINES - height)/2;  //Next two lines calculate center placement
		startx=(COLS - width)/2;
		printw("Press q to quit");  //print to stdscr
		refresh();
		mywin=newwin(height,width,starty,startx);
		
		box(mywin, 0,0);            //draw a box around the window
                                            //0,0 gives default characters
		mvwprintw(mywin,1,1,"Hi");
		wrefresh(mywin);            //show the message and the box

	
		while ( (ch=getch()) != 'q')
		{
			//cycle around until q is pressed
		}

		endwin();
		return 0;

}

A couple of things to note:


References