Using ncurses library with C

From Notes_Wiki

Home > C programming language > Using ncurses library with C

Steps for using ncurses library in C program

  1. #include <ncurses.h>
  2. Compile using -lncurses
  3. Call initscr(). It allocates memory for present window which is called stdscr.
  4. Use printw() in place of printf().
  5. Don't forget to use refresh() after every or series of printw() so that changes reflect on window. Changes are made only in memory by printw() to see those changes we must refresh() the screen.
  6. Always use endwin() when you end the program. Forgetting to use it may result in crashing the terminal window.

You now know sufficient to initialize the window, print something and close the window. Try using it before proceeding.


Example(01)

#include <ncurses.h>

int main()
{	
	initscr();			/* Start curses mode 		  */
	printw("Hello World !!!");	/* Print Hello World		  */
	refresh();			/* Print it on to the real screen */
	getch();			/* Wait for user input */
	endwin();			/* End curses mode		  */

	return 0;
}


echo() and noecho()
These functions control the echoing of characters typed by the user to the terminal. noecho() switches off echoing. The reason you might want to do this is to gain more control over echoing or to suppress unnecessary echoing while taking input from the user through the getch() etc. functions.
keypad()
It enables the reading of function keys like F1, F2, arrow keys etc. Almost every interactive program enables this, as arrow keys are a major part of any User Interface. Do keypad(stdscr, TRUE) to enable this feature for the regular screen (stdscr).
raw() and cbreak()
Normally the terminal driver buffers the characters a user types until a new line or carriage return is encountered. But most programs require that the characters be available as soon as the user types them. The above two functions are used to disable line buffering. The difference between these two functions is in the way control characters like suspend (CTRL-Z), interrupt and quit (CTRL-C) are passed to the program. In the raw() mode these characters are directly passed to the program without generating a signal. In the cbreak() mode these control characters are interpreted as any other character by the terminal driver.


Try the aove commands with the following example and some code of your own.


Example(02)

#include <ncurses.h>

int main()
{	int ch;

	initscr();			/* Start curses mode 		*/
	raw();				/* Line buffering disabled	*/
	keypad(stdscr, TRUE);		/* We get F1, F2 etc..		*/
	noecho();			/* Don't echo() while we do getch */

    	printw("Type any character to see it in bold\n");
	ch = getch();			/* If raw() hadn't been called
					 * we have to press enter before it
					 * gets to the program 		*/
	if(ch == KEY_F(2))		/* Without keypad enabled this will */
		printw("F2 Key pressed");/*  not get to us either	*/
					/* Without noecho() some ugly escape
					 * charachters might have been printed
					 * on screen			*/
	else
	{	
		printw("The pressed key is ");
		attron(A_BOLD);
		printw("%c", ch);
		attroff(A_BOLD);
	}

	refresh();			/* Print it on to the real screen */
    	getch();			/* Wait for user input */
	endwin();			/* End curses mode		  */

	return 0;
}


What is a window?
A Window is an imaginary screen defined by curses system. A window does not mean a bordered window which you usually see on Win9X platforms. When

curses is initialized, it creates a default window named stdscr which represents your 80x25 (or the size of window in which you are running) screen. If you are doing simple tasks like printing few strings, reading input etc., you can safely use this single window for all of your purposes. You can also create windows and call functions which explicitly work on the specified window.

For example, if you call
printw("Hi There !!!");
refresh();
It prints the string on stdscr at the present cursor position. Similarly the call to refresh(), works on stdscr only.
Say you have created windows then you have to call a function with a 'w' added to the usual function.
wprintw(win, "Hi There !!!");
wrefresh(win);
As you will see in the rest of the document, naming of functions follow the same convention. For each function there usually are three more functions.
    printw(string);        /* Print on stdscr at present cursor position */
    mvprintw(y, x, string);/* Move to (y, x) then print string     */
    wprintw(win, string);  /* Print on window win at present cursor position*/
                           /* in the window */
    mvwprintw(win, y, x, string);   /* Move to (y, x) relative to window */
                                    /* co-ordinates and then print         */

Usually the w-less functions are macros which expand to corresponding w-function with stdscr as the window parameter.


move(row, column)
Moves the cursor to row,column.


addch()
These functions put a single character into the current cursor location and advance the position of the cursor. You can give the character to be printed but they usually are used to print a character with some attributes. Example addch(ch | A_BOLD | A_UNDERLINE)


Example(03)

#include <ncurses.h>                    /* ncurses.h includes stdio.h */
#include <string.h>
                                                                                                 
int main()
{
        char mesg[]="Just a string";            /* message to be appeared on the screen */
        int row,col;                            /* to store the number of rows and *
                                                 * the number of colums of the screen */
        initscr();                              /* start the curses mode */
        getmaxyx(stdscr,row,col);               /* get the number of rows and columns */
        mvprintw(row/2,(col-strlen(mesg))/2,"%s",mesg);         /* print the message at the
                                                                   center of the screen */
        mvprintw(row-2,0,"This screen has %d rows and %d columns\n",row,col);
        printw("Try resizing your window(if possible) and then run this program again");
        refresh();
        getch();
        endwin();
                                                                                                 
        return 0;
}


getmaxyx()
The above program introduces us to a new function getmaxyx(), a macro defined in ncurses.h. It gives the number of columns and the number of rows in a given window. getmaxyx() does this by updating the variables given to it. Since getmaxyx() is not a function we don't pass pointers to it, we just give two integer variables.


addstr()
addstr() is used to put a character string into a given window. This function is similar to calling addch() once for each character in a given string. This is true for all output functions. There are other functions from this family such as mvaddstr(),mvwaddstr() and waddstr(), which obey the naming convention of curses.(e.g. mvaddstr() is similar to the respective calls move() and then addstr().) Another function of this family is addnstr(), which takes an integer parameter(say n) additionally. This function puts at most n characters into the screen. If n is negative, then the entire string will be added.

All these functions take y co-ordinate first and then x in their arguments.


getch()
These functions read a single character from the terminal. But there are several subtle facts to consider. For example if you don't use the function cbreak(), curses will not read your input characters contiguously but will begin read them only after a new line or an EOF is encountered. In order to avoid this, the cbreak() function must used so that characters are immediately available to your program. Another widely used function is noecho(). As the name suggests, when this function is set (used), the characters that are keyed in by the user will not show up on the screen.


scanw()
These functions are similar to scanf() with the added capability of getting the input from any location on the screen.


getstr()
These functions are used to get strings from the terminal. In essence, this function performs the same task as would be achieved by a series of calls to getch() until a newline, carriage return, or end-of-file is received. The resulting string of characters are pointed to by str, which is a character pointer provided by the user.


Example(04)

#include <ncurses.h>                    /* ncurses.h includes stdio.h */
#include <string.h>
                                                                                                 
int main()
{
        char mesg[]="Enter a string: ";         /* message to be appeared on the screen */
        char str[80];
        int row,col;                            /* to store the number of rows and *
                                                 * the number of colums of the screen */
        initscr();                              /* start the curses mode */
        getmaxyx(stdscr,row,col);               /* get the number of rows and columns */
        mvprintw(row/2,(col-strlen(mesg))/2,"%s",mesg);
                                        /* print the message at the center of the screen */
        getstr(str);
        mvprintw(LINES - 2, 0, "You Entered: %s", str);
        getch();
        endwin();
                                                                                                 
        return 0;
}


Using Attributes
We have seen an example of how attributes can be used to print characters with some special effects. Attributes, when set prudently, can present information in an easy, understandable manner. The following program takes a C file as input and prints the file with comments in bold. Scan through the code.


Example(05)

#include <ncurses.h>

int main(int argc, char *argv[])
{ 
    int ch, prev;
    FILE *fp;
    int goto_prev = FALSE, y, x;

    if(argc != 2)
    {   printf("Usage: %s <a c file name>\n", argv[0]);
        exit(1);
    }
    fp = fopen(argv[1], "r");
    if(fp == NULL)
    {   perror("Cannot open input file");
        exit(1);
    }

    initscr();                      /* Start curses mode            */

    prev = EOF;
    while((ch = fgetc(fp)) != EOF)
    {   if(prev == '/' && ch == '*')    /* If it is / and * then olny
                                         * switch bold on */    
        {   attron(A_BOLD);
            goto_prev = TRUE;       /* Go to previous char / and
                                     * print it in BOLD */
        }
        if(goto_prev == TRUE)
        {   getyx(stdscr, y, x);
            move(y, x - 1);
            printw("%c%c", '/', ch); /* The actual printing is done
                                      * here */
            ch = 'a';                /* 'a' is just a dummy
                                      * character to prevent */
                                     // "/*/" comments.
            goto_prev = FALSE;      /* Set it to FALSE or every 
                                     * thing from here will be / */
        } else
            printw("%c", ch);
        refresh();
        if(prev == '*' && ch == '/')
                attroff(A_BOLD);        /* Switch it off once we got *
                                           and then / */
        prev = ch;
    }
    getch();
    endwin();                       /* End curses mode                */
    return 0;
}

Don't worry about all those initialization and other crap. Concentrate on the while loop. It reads each character in the file and searches for the pattern /*. Once it spots the pattern, it switches the BOLD attribute on with * attron() . When we get the pattern */ it is switched off by attroff() .

The above program also introduces us to two useful functions getyx() and move(). The first function gets the co-ordinates of the present cursor into the variables y, x. Since getyx() is a macro we don't have to pass pointers to variables. The function move() moves the cursor to the co-ordinates given to it.

The above program is really a simple one which doesn't do much. On these lines one could write a more useful program which reads a C file, parses it and prints it in different colors. One could even extend it to other languages as well.



The details of various attributes available and their functions
Let's get into more details of attributes. The functions attron(), attroff(), attrset() , and their sister functions attr_get() etc.. can be used to switch attributes on/off , get attributes and produce a colorful display.
The functions attron and attroff take a bit-mask of attributes and switch them on or off, respectively. The following video attributes, which are defined in <curses.h> can be passed to these functions.
    A_NORMAL        Normal display (no highlight)
    A_STANDOUT      Best highlighting mode of the terminal.
    A_UNDERLINE     Underlining
    A_REVERSE       Reverse video
    A_BLINK         Blinking
    A_DIM           Half bright
    A_BOLD          Extra bright or bold
    A_PROTECT       Protected mode
    A_INVIS         Invisible or blank mode
    A_ALTCHARSET    Alternate character set
    A_CHARTEXT      Bit-mask to extract a character
    COLOR_PAIR(n)   Color-pair number n 

We can OR(|) any number of above attributes to get a combined effect. If you wanted reverse video with blinking characters you can use

    attron(A_REVERSE | A_BLINK);


attron() vs. attrset()
Then what is the difference between attron() and attrset()? attrset sets the attributes of window whereas attron just switches on the attribute given to it. So attrset() fully overrides whatever attributes the window previously had and sets it to the new attribute(s). Similarly attroff() just switches off the attribute(s) given to it as an argument. This gives us the flexibility of managing attributes easily.But if you use them carelessly you may loose track of what attributes the window has and garble the display. This is especially true while managing menus with colors and highlighting. So decide on a consistent policy and stick to it. You can always use standend() which is equivalent to attrset(A_NORMAL) which turns off all attributes and brings you to normal mode.


attrget()
The function attr_get() gets the current attributes and color pair of the window. Though we might not use this as often as the above functions, this is useful in scanning areas of screen. Say we wanted to do some complex update on screen and we are not sure what attribute each character is associated with. Then this function can be used with either attrset or attron to produce the desired effect.


chgat() // **gat not get**
It actually is a useful one. This function can be used to set attributes for a group of characters without moving. I mean it !!! without moving the cursor :-) It changes the attributes of a given number of characters starting at the current cursor location.
We can give -1 as the character count to update till end of line. If you want to change attributes of characters from current position to end of line, just use this.
chgat(-1, A_REVERSE, 0, NULL);
This function is useful when changing attributes for characters that are already on the screen. Move to the character from which you want to change and change the attribute.

Other functions wchgat(), mvchgat(), wchgat() behave similarly except that the w functions operate on the particular window. The mv functions first move the cursor then perform the work given to them. Actually chgat is a macro which is replaced by a wchgat() with stdscr as the window. Most of the "w-less" functions are macros.


Example(06)

#include <ncurses.h>

int main(int argc, char *argv[])
{	initscr();			/* Start curses mode 		*/
	start_color();			/* Start color functionality	*/
	
	init_pair(1, COLOR_CYAN, COLOR_BLACK);
	printw("A Big string which i didn't care to type fully ");
	mvchgat(0, 0, -1, A_BLINK, 1, NULL);	
	/* 
	 * First two parameters specify the position at which to start 
	 * Third parameter number of characters to update. -1 means till 
	 * end of line
	 * Forth parameter is the normal attribute you wanted to give 
	 * to the charcter
	 * Fifth is the color index. It is the index given during init_pair()
	 * use 0 if you didn't want color
	 * Sixth one is always NULL 
	 */
	refresh();
    	getch();
	endwin();			/* End curses mode		  */
	return 0;
}


Windows creating and deleting

Example(07)

#include <ncurses.h>


WINDOW *create_newwin(int height, int width, int starty, int startx);
void destroy_win(WINDOW *local_win);

int main(int argc, char *argv[])
{	
    WINDOW *my_win;
    int startx, starty, width, height;
    int ch;
    initscr();			/* Start curses mode 		*/
    raw();			/* Line buffering disabled, Pass on
				 * everty thing to me 		*/
    noecho();

    keypad(stdscr, TRUE);		/* I need that nifty F2 	*/
    height = 3;
    width = 10;
    starty = (LINES - height) / 2;	/* Calculating for a center placement */
    startx = (COLS - width) / 2;	/* of the window		*/
    printw("Press F2 to exit");
    refresh();
     
    my_win = create_newwin(height, width, starty, startx);

    while((ch = getch()) != KEY_F(2))
    {
	switch(ch)
	{	
	    case KEY_LEFT: 				
		destroy_win(my_win);				
		my_win = create_newwin(height, width, starty,--startx);	
		break;
	
	    case KEY_RIGHT:
		destroy_win(my_win);
		my_win = create_newwin(height, width, starty,++startx);
		break;

	    case KEY_UP:
		destroy_win(my_win);
		my_win = create_newwin(height, width, --starty,startx);
		break;

	    case KEY_DOWN:
		destroy_win(my_win);
		my_win = create_newwin(height, width, ++starty,startx);
		break;	
	}
    }
    endwin();			/* End curses mode		  */
    return 0;
}

WINDOW *create_newwin(int height, int width, int starty, int startx)
{	
    WINDOW *local_win;
    local_win = newwin(height, width, starty, startx);
    box(local_win, 0 , 0);		/* 0, 0 gives default characters 
					   for the vertical and horizontal
					    lines			*/
    wrefresh(local_win);		/* Show that box 		*/
    return local_win;
}

void destroy_win(WINDOW *local_win)
{
    	/* box(local_win, ' ', ' '); : This won't produce the desired
  	 * result of erasing the window. It will leave it's four corners 
	 * and so an ugly remnant of window.  */
    
    wborder(local_win, ' ', ' ', ' ',' ',' ',' ',' ',' ');

	/* The parameters taken are 
	 * 1. win: the window on which to operate
	 * 2. ls: character to be used for the left side of the window 
	 * 3. rs: character to be used for the right side of the window 
	 * 4. ts: character to be used for the top side of the window 
	 * 5. bs: character to be used for the bottom side of the window 
 	 * 6. tl: character to be used for the top left corner of the window 
	 * 7. tr: character to be used for the top right corner of the window 
	 * 8. bl: character to be used for the bottom left corner of the window 
	 * 9. br: character to be used for the bottom right corner of the window */
    
    wrefresh(local_win);
    delwin(local_win);
}


Home > C programming language > Using ncurses library with C