11. Key management. How to read function keys, arrow keys etc..

11.1. The Basics

No GUI is complete without a strong user interface and to interact with the user, a curses program should be sensitive to key presses or the mouse actions done by the user. Let's deal with the keys first.

As you have seen in almost all of the above examples, it's very easy to get key input from the user. A simple way of getting key presses is to use getch() function. The cbreak mode should be enabled to read keys when you are interested in reading individual key hits rather than complete lines of text (which usually end with a carriage return). keypad should be enabled to get the Functions keys, arrow keys etc. See the initialization section for details.

getch() returns an integer corresponding to the key pressed. If it is a normal character, the integer value will be equivalent to the character. Otherwise it returns a number which can be matched with the constants defined in curses.h. For example if the user presses F1, the integer returned is 265. This can be checked using the macro KEY_F() defined in curses.h. This makes reading keys portable and easy to manage.

For example, if you call getch() like this

    int ch;

    ch = getch();

getch() will wait for the user to press a key, (unless you specified a timeout) and when user presses a key, the corresponding integer is returned. Then you can check the value returned with the constants defined in curses.h to match against the keys you want.

The following code piece will do that job.

    if(ch == KEY_LEFT)
        printw("Left arrow is pressed\n");

Let's write a small program which creates a menu which can be navigated by up and down arrows.

11.2. A Simple Key Usage example

Example 10. A Simple Key Usage example

#include <stdio.h>
#include <ncurses.h>

#define WIDTH 30
#define HEIGHT 10 

int startx = 0;
int starty = 0;

char *choices[] = { 
			"Choice 1",
			"Choice 2",
			"Choice 3",
			"Choice 4",
			"Exit",
		  };
int n_choices = sizeof(choices) / sizeof(char *);
void print_menu(WINDOW *menu_win, int highlight);

int main()
{	WINDOW *menu_win;
	int highlight = 1;
	int choice = 0;
	int c;

	initscr();
	clear();
	noecho();
	cbreak();	/* Line buffering disabled. pass on everything */
	startx = (80 - WIDTH) / 2;
	starty = (24 - HEIGHT) / 2;
		
	menu_win = newwin(HEIGHT, WIDTH, starty, startx);
	keypad(menu_win, TRUE);
	mvprintw(0, 0, "Use arrow keys to go up and down, Press enter to select a choice");
	refresh();
	print_menu(menu_win, highlight);
	while(1)
	{	c = wgetch(menu_win);
		switch(c)
		{	case KEY_UP:
				if(highlight == 1)
					highlight = n_choices;
				else
					--highlight;
				break;
			case KEY_DOWN:
				if(highlight == n_choices)
					highlight = 1;
				else 
					++highlight;
				break;
			case 10:
				choice = highlight;
				break;
			default:
				mvprintw(24, 0, "Charcter pressed is = %3d Hopefully it can be printed as '%c'", c, c);
				refresh();
				break;
		}
		print_menu(menu_win, highlight);
		if(choice != 0)	/* User did a choice come out of the infinite loop */
			break;
	}	
	mvprintw(23, 0, "You chose choice %d with choice string %s\n", choice, choices[choice - 1]);
	clrtoeol();
	refresh();
	endwin();
	return 0;
}


void print_menu(WINDOW *menu_win, int highlight)
{
	int x, y, i;	

	x = 2;
	y = 2;
	box(menu_win, 0, 0);
	for(i = 0; i < n_choices; ++i)
	{	if(highlight == i + 1) /* High light the present choice */
		{	wattron(menu_win, A_REVERSE); 
			mvwprintw(menu_win, y, x, "%s", choices[i]);
			wattroff(menu_win, A_REVERSE);
		}
		else
			mvwprintw(menu_win, y, x, "%s", choices[i]);
		++y;
	}
	wrefresh(menu_win);
}