NCURSES-Programming-HOWTO 20050606 210528
NCURSES-Programming-HOWTO 20050606 210528
Pradeep Padala
[email protected]
v1.7.1, 2002−06−25
Revision History
Revision 1.7.1 2002−06−25 Revised by: ppadala
Added a README file for building and instructions for building from source.
Revision 1.7 2002−06−25 Revised by: ppadala
Added "Other formats" section and made lot of fancy changes to the programs. Inlining of programs is gone.
Revision 1.6.1 2002−02−24 Revised by: ppadala
Removed the old Changelog section, cleaned the makefiles
Revision 1.6 2002−02−16 Revised by: ppadala
Corrected lot of spelling mistakes, added ACS variables section
Revision 1.5 2002−01−05 Revised by: ppadala
Changed structure to present proper TOC
Revision 1.3.1 2001−07−26 Revised by: ppadala
Corrected maintainers paragraph, Corrected stable release number
Revision 1.3 2001−07−24 Revised by: ppadala
Added copyright notice(LDP license) to main document, Put copyright notice (GPL) for programs as well,
Corrected printw_example.
Revision 1.2 2001−06−05 Revised by: ppadala
Incorporated ravi's changes. Mainly to introduction, menu, form, justforfun sections
Revision 1.1 2001−05−22 Revised by: ppadala
Added "a word about window" section, Added scanw_example.
This document is intended to be an "All in One" guide for programming with ncurses and its sister libraries.
We graduate from a simple "Hello World" program to more complex form manipulation. No prior experience
in ncurses is assumed. Latest version of the document can always be found at my web site. Send comments to
this address
NCURSES Programming HOWTO
Table of Contents
1. Introduction.....................................................................................................................................................1
1.1. What is NCURSES?.........................................................................................................................1
1.2. What we can do with NCURSES......................................................................................................2
1.3. Where to get it...................................................................................................................................2
1.4. Purpose/Scope of the document........................................................................................................3
1.5. About the Programs..........................................................................................................................3
1.6. Other Formats of the document........................................................................................................4
1.6.1. Readily available formats from tldp.org...........................................................................4
1.6.2. Building from source........................................................................................................5
1.7. Credits...............................................................................................................................................5
1.8. Wish List...........................................................................................................................................5
1.9. Copyright..........................................................................................................................................6
4. Initialization...................................................................................................................................................10
4.1. About Initialization functions like raw() etc...................................................................................10
4.2. raw() and cbreak()...........................................................................................................................10
4.3. echo() and noecho()........................................................................................................................10
4.4. keypad()..........................................................................................................................................10
4.5. halfdelay().......................................................................................................................................10
4.6. Miscellaneous Initialization functions............................................................................................11
4.7. An Example....................................................................................................................................11
i
NCURSES Programming HOWTO
Table of Contents
7.2.2. wscanw() and mvwscanw()............................................................................................17
7.2.3. vwscanw().......................................................................................................................17
7.3. getstr() class of functions................................................................................................................18
7.4. Some examples...............................................................................................................................18
8. Attributes.......................................................................................................................................................19
8.1. The details.......................................................................................................................................20
8.2. attron() vs attrset()...........................................................................................................................20
8.3. attr_get()..........................................................................................................................................21
8.4. attr_ functions.................................................................................................................................21
8.5. wattr functions................................................................................................................................21
8.6. chgat() functions.............................................................................................................................21
11. Key management. How to read function keys, arrow keys etc...............................................................30
11.1. The Basics.....................................................................................................................................30
11.2. A Simple Key Usage example......................................................................................................30
ii
NCURSES Programming HOWTO
Table of Contents
16.1. The Basics.....................................................................................................................................41
16.2. Compiling With the Panels Library..............................................................................................41
16.3. Panel Window Browsing..............................................................................................................42
16.4. Using User Pointers......................................................................................................................45
16.5. Moving and Resizing Panels.........................................................................................................45
16.6. Hiding and Showing Panels..........................................................................................................49
16.7. panel_above() and panel_below() Functions................................................................................52
TYPE_ALPHA..................................................................................................................................................82
TYPE_ALNUM.................................................................................................................................................83
TYPE_ENUM....................................................................................................................................................84
TYPE_INTEGER.............................................................................................................................................85
TYPE_NUMERIC............................................................................................................................................86
TYPE_REGEXP...............................................................................................................................................87
18.6. Form Driver: The work horse of the forms system.......................................................................87
18.6.1. Page Navigation Requests............................................................................................87
18.6.2. Inter−Field Navigation Requests..................................................................................88
iii
NCURSES Programming HOWTO
Table of Contents
18.6.3. Intra−Field Navigation Requests..................................................................................88
18.6.4. Scrolling Requests........................................................................................................89
18.6.5. Editing Requests...........................................................................................................89
18.6.6. Order Requests..............................................................................................................90
18.6.7. Application Commands................................................................................................90
iv
1. Introduction
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.
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.
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.
The ncurses package was originated by Pavel Curtis. The original maintainer of this package is Zeyd
Ben−Halim <[email protected]>. Eric S. Raymond <[email protected]> wrote
many of the new features in versions after 1.8.1. Jürgen Pfeifer wrote all of the menu and forms code as well
as the Ada95 binding. Ongoing work is being done by Thomas Dickey and Jürgen Pfeifer. Florian La
Roche acts as the maintainer for the Free Software Foundation, which holds the copyright on ncurses.
Contact the current maintainers at bug−[email protected].
1. Introduction 1
NCURSES Programming HOWTO
Menus provide the user with an easy command selection option. Forms allow the creation of easy−to−use
data entry and display windows. Panels extend the capabilities of ncurses to deal with overlapping and
stacked windows.
These are just some of the basic things we can do with ncurses. As we move along, We will see all the
capabilities of these libraries.
Read the README and INSTALL files for details on to how to install it. It usually involves the following
operations.
Ncurses RPM can be found and downloaded from http://rpmfind.net . The RPM can be installed with the
following command after becoming root.
ncurses
|
|−−−−> JustForFun −− just for fun programs
|−−−−> basics −− basic programs
|−−−−> demo −− output files go into this directory after make
| |
| |−−−−> exe −− exe files of all example programs
|−−−−> forms −− programs related to form library
|−−−−> menus −− programs related to menus library
|−−−−> panels −− programs related to panels library
|−−−−> Makefile −− the top level Makefile
|−−−−> README −− the top level README file. contains instructions
|−−−−> COPYING −− copyright notice
basics
|
|−−−−> acs_vars.c −− ACS_ variables example
|−−−−> hello_world.c −− Simple "Hello World" Program
|−−−−> init_func_example.c −− Initialization functions example
|−−−−> key_code.c −− Shows the scan code of the key pressed
|−−−−> mouse_menu.c −− A menu accessible by mouse
|−−−−> other_border.c −− Shows usage of other border functions apa
| −− rt from box()
|−−−−> printw_example.c −− A very simple printw() example
|−−−−> scanw_example.c −− A very simple getstr() example
|−−−−> simple_attr.c −− A program that can print a c file with
| −− comments in attribute
|−−−−> simple_color.c −− A simple example demonstrating colors
|−−−−> simple_key.c −− A menu accessible with keyboard UP, DOWN
| −− arrows
|−−−−> temp_leave.c −− Demonstrates temporarily leaving curses mode
|−−−−> win_border.c −− Shows Creation of windows and borders
forms
|
|−−−−> form_attrib.c −− Usage of field attributes
|−−−−> form_options.c −− Usage of field options
|−−−−> form_simple.c −− A simple form example
|−−−−> form_win.c −− Demo of windows associated with forms
menus
|
|−−−−> menu_attrib.c −− Usage of menu attributes
|−−−−> menu_item_data.c −− Usage of item_name() etc.. functions
|−−−−> menu_multi_column.c −− Creates multi columnar menus
|−−−−> menu_scroll.c −− Demonstrates scrolling capability of menus
|−−−−> menu_simple.c −− A simple menu accessed by arrow keys
|−−−−> menu_toggle.c −− Creates multi valued menus and explains
| −− REQ_TOGGLE_ITEM
|−−−−> menu_userptr.c −− Usage of user pointer
|−−−−> menu_win.c −− Demo of windows associated with menus
panels
|
|−−−−> panel_browse.c −− Panel browsing through tab. Usage of user
| −− pointer
|−−−−> panel_hide.c −− Hiding and Un hiding of panels
|−−−−> panel_resize.c −− Moving and resizing of panels
|−−−−> panel_simple.c −− A simple panel example
There is a top level Makefile included in the main directory. It builds all the files and puts the ready−to−use
exes in demo/exe directory. You can also do selective make by going into the corresponding directory. Each
directory contains a README file explaining the purpose of each c file in the directory.
For every example I have given the path name for the file relative to the ncurses directory.
All the programs are released under GPL and you can use them for any thing you like.
Use jade to create various formats. For example if you just want to create
the multiple html files, you would use
jade −t sgml −i html −d <path to docbook html stylesheet>
NCURSES−Programming−HOWTO.sgml
to get pdf, first create a single html file of the HOWTO with
jade −t sgml −i html −d <path to docbook html stylesheet> −V nochunks
NCURSES−Programming−HOWTO.sgml > NCURSES−ONE−BIG−FILE.html
then use htmldoc to get pdf file with
htmldoc −−size universal −t pdf −−firstpage p1 −f <output file name.pdf>
NCURSES−ONE−BIG−FILE.html
for ps, you would use
htmldoc −−size universal −t ps −−firstpage p1 −f <output file name.ps>
NCURSES−ONE−BIG−FILE.html
See LDP Author guide for more details. If all else failes, mail me at [email protected]
1.7. Credits
I thank Sharath and Emre Akbas for helping me with few sections. The introduction was initially written by
sharath. I rewrote it with few excerpts taken from his initial work. Emre helped in writing printw and scanw
sections.
Then comes Ravi Parimi, my dearest friend, who has been on this project before even one line was written.
He constantly bombarded me with suggestions and patiently reviewed the whole text. He also checked each
program on Linux and Solaris. See his notes to check on your problems.
1.9. Copyright
Copyright (c) 2001 by Pradeep Padala. This document may be distributed under the terms set forth in the
LDP license at linuxdoc.org/COPYRIGHT.html.
This HOWTO is free documentation; you can redistribute it and/or modify it under the terms of the LDP
license. This document is distributed in the hope that it will be useful, but without any warranty; without even
the implied warranty of merchantability or fitness for a particular purpose. See the LDP license for more
details.
1.9. Copyright 6
2. The Hello World Program
Welcome to the world of curses. Before we plunge into the library and look into its various features, bells and
whistles, let's write a simple program and say hello to the world.
#include <ncurses.h>
.
.
.
#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;
}
2.2. Dissection
The above program prints "Hello World !!!" to the screen and exits. This program shows how to initialize
curses and do screen manipulation and end curses mode. Let's dissect it line by line.
After this is done we can do a variety of initializations to customize our curses settings. These details will be
explained later .
This brings us to that mysterious refresh(). Well, when we did printw actually the data is written to an
imaginary window called stdscr, which is not updated on the screen yet. The job of printw is to update a few
flags and data structures and write the data to a buffer corresponding to stdscr. In order to bring it to the
screen we need to call refresh() and tell the curses system to dump the contents on the screen.
The philosophy behind all this is to allow the programmer to do multiple updates on the imaginary screen or
windows and do a refresh once all his screen update is done. refresh() checks the window and updates only
the portion which has been changed. This gives good response and offers greater flexibility too. But it is
sometimes frustrating to beginners. A common mistake committed by beginners is to forget to call refresh()
after they did some update through printw() class of functions. I still forget to add it sometimes :−)
Here we go...
4.4. keypad()
This is my favorite initialization function. 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). You will learn more
about key management in later sections of this document.
4.5. halfdelay()
This function, though not used very often, is a useful one at times. halfdelay()is called to enable the
half−delay mode, which is similar to the cbreak() mode in that characters typed are immediately available to
program. However, it waits for 'X' tenths of a second for input and then returns ERR, if no input is available.
'X' is the timeout value passed to the function halfdelay(). This function is useful when you want to ask the
4. Initialization 10
NCURSES Programming HOWTO
user for input, and if he doesn't respond with in certain time, we can do some thing else. One possible
example is a timeout at the password prompt.
4.7. An Example
Let's write a program which will clarify the usage of these functions.
#include <ncurses.h>
int main()
{ int ch;
return 0;
}
This program is self−explanatory. But I used functions which aren't explained yet. The function getch() is
used to get a character from user. It is equivalent to normal getchar() except that we can disable the line
buffering to avoid <enter> after input. Look for more about getch()and reading keys in the key
management section . The functions attron and attroff are used to switch some attributes on and off
respectively. In the example I used them to print the character in bold. These functions are explained in detail
later.
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.
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.
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.
Usually the w−less functions are macros which expand to corresponding w−function with stdscr as the
window parameter.
There are three classes of functions which you can use to do output on screen.
These functions can be used interchangeably and it's a matter of style as to which class is used. Let's see each
one in detail.
In order to combine a character with some attributes, you have two options:
• By OR'ing a single character with the desired attribute macros. These attribute macros could be found
in the header file ncurses.h. For example, you want to print a character ch(of type char) bold and
underlined, you would call addch() as below.
addch(ch | A_BOLD | A_UNDERLINE);
• By using functions like attrset(),attron(),attroff(). These functions are explained in
the Attributes section. Briefly, they manipulate the current attributes of the given window. Once set,
the character printed in the window are associated with the attributes until it is turned off.
Additionally, curses provides some special characters for character−based graphics. You can draw tables,
horizontal or vertical lines, etc. You can find all avaliable characters in the header file ncurses.h. Try
looking for macros beginning with ACS_ in this file.
waddch() is similar to addch(), except that it adds a character into the given window. (Note that
addch() adds a character into the window stdscr.)
In a similar fashion mvwaddch() function is used to add a character into the given window at the given
coordinates.
Now, we are familiar with the basic output function addch(). But, if we want to print a string, it would be
very annoying to print it character by character. Fortunately, ncurses provides printf−like or
puts−like functions.
6.3.3. vwprintw()
This function is similar to vprintf(). This can be used when variable number of arguments are to be
printed.
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;
}
Above program demonstrates how easy it is to use printw. You just feed the coordinates and the message
to be appeared on the screen, then it does what you want.
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.
7.2.3. vwscanw()
This function is similar to vscanf(). This can be used when a variable number of arguments are to be
scanned.
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;
}
#include <ncurses.h>
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);
}
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;
}
8. Attributes 19
NCURSES Programming HOWTO
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 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.
The last one is the most colorful one :−) Colors are explained in the next sections.
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);
always use standend() which is equivalent to attrset(A_NORMAL) which turns off all attributes and
brings you to normal mode.
8.3. attr_get()
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.
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.
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.
#include <ncurses.h>
8.3. attr_get() 21
NCURSES Programming HOWTO
This example also introduces us to the color world of curses. Colors will be explained in detail later. Use 0
for no color.
8.3. attr_get() 22
9. All about window functions
Windows form the most important concept in curses. You have seen the standard window stdscr above where
all the functions implicitly operated on this window. Now to make design even a simplest GUI, you need to
resort to windows. The main reason you may want to use windows is to manipulate parts of the screen
separately, for better efficiency, by updating only the windows that need to be changed and for a better
design. I would say the last reason is the most important in going for windows. You should always strive for
a better and easy−to−manage design in your programs. If you are writing big, complex GUIs this is of pivotal
importance before you start doing anything.
#include <ncurses.h>
height = 3;
width = 10;
starty = (LINES − height) / 2; /* Calculating for a center placement */
startx = (COLS − width) / 2; /* of the window */
printw("Press F1 to exit");
refresh();
my_win = create_newwin(height, width, starty, startx);
return local_win;
}
9.3. Explanation
Don't scream. I know it's a big example. But I have to explain some important things here :−). This program
creates a rectangular window that can be moved with left, right, up, down arrow keys. It repeatedly creates
and destroys windows as user press a key. Don't go beyond the screen limits. Checking for those limits is left
as an exercise for the reader. Let's dissect it by line by line.
The create_newwin() function creates a window with newwin() and displays a border around it with
box. The function destroy_win() first erases the window from screen by painting a border with ' '
character and then calling delwin() to deallocate memory related to it. Depending on the key the user
presses, starty or startx is changed and a new window is created.
In the destroy_win, as you can see, I used wborder instead of box. The reason is written in the comments
(You missed it. I know. Read the code :−)). wborder draws a border around the window with the characters
given to it as the 4 corner points and the 4 lines. To put it clearly, if you have called wborder as below:
+−−−−−−−−−−−−−−−−−−−−−−−+
| |
| |
| |
| |
| |
| |
+−−−−−−−−−−−−−−−−−−−−−−−+
The following program uses mvhline() and mvvline() to achieve similar effect. These two functions
are simple. They create a horizontal or vertical line of the specified length at the specified position.
9.3. Explanation 25
NCURSES Programming HOWTO
#include <ncurses.h>
attron(COLOR_PAIR(1));
printw("Press F1 to exit");
refresh();
attroff(COLOR_PAIR(1));
create_box(&win, TRUE);
while((ch = getch()) != KEY_F(1))
{ switch(ch)
{ case KEY_LEFT:
create_box(&win, FALSE);
−−win.startx;
create_box(&win, TRUE);
break;
case KEY_RIGHT:
create_box(&win, FALSE);
++win.startx;
create_box(&win, TRUE);
break;
case KEY_UP:
create_box(&win, FALSE);
−−win.starty;
create_box(&win, TRUE);
break;
case KEY_DOWN:
create_box(&win, FALSE);
++win.starty;
create_box(&win, TRUE);
break;
9.3. Explanation 26
NCURSES Programming HOWTO
}
}
endwin(); /* End curses mode */
return 0;
}
void init_win_params(WIN *p_win)
{
p_win−>height = 3;
p_win−>width = 10;
p_win−>starty = (LINES − p_win−>height)/2;
p_win−>startx = (COLS − p_win−>width)/2;
p_win−>border.ls = '|';
p_win−>border.rs = '|';
p_win−>border.ts = '−';
p_win−>border.bs = '−';
p_win−>border.tl = '+';
p_win−>border.tr = '+';
p_win−>border.bl = '+';
p_win−>border.br = '+';
}
void print_win_params(WIN *p_win)
{
#ifdef _DEBUG
mvprintw(25, 0, "%d %d %d %d", p_win−>startx, p_win−>starty,
p_win−>width, p_win−>height);
refresh();
#endif
}
void create_box(WIN *p_win, int bool)
{ int i, j;
int x, y, w, h;
x = p_win−>startx;
y = p_win−>starty;
w = p_win−>width;
h = p_win−>height;
if(bool == TRUE)
{ mvaddch(y, x, p_win−>border.tl);
mvaddch(y, x + w, p_win−>border.tr);
mvaddch(y + h, x, p_win−>border.bl);
mvaddch(y + h, x + w, p_win−>border.br);
mvhline(y, x + 1, p_win−>border.ts, w − 1);
mvhline(y + h, x + 1, p_win−>border.bs, w − 1);
mvvline(y + 1, x, p_win−>border.ls, h − 1);
mvvline(y + 1, x + w, p_win−>border.rs, h − 1);
}
else
for(j = y; j <= y + h; ++j)
for(i = x; i <= x + w; ++i)
mvaddch(j, i, ' ');
refresh();
9.3. Explanation 27
10. All about color
10.1. The basics
Life seems dull with no colors. Curses has a nice mechanism to handle colors. Let's get into the thick of the
things with a small program.
#include <ncurses.h>
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string);
int main(int argc, char *argv[])
{ initscr(); /* Start curses mode */
if(has_colors() == FALSE)
{ endwin();
printf("You terminal does not support color\n");
exit(1);
}
start_color(); /* Start color */
init_pair(1, COLOR_RED, COLOR_BLACK);
attron(COLOR_PAIR(1));
print_in_middle(stdscr, LINES / 2, 0, 0, "Viola !!! In color ...");
attroff(COLOR_PAIR(1));
getch();
endwin();
}
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string)
{ int length, x, y;
float temp;
if(win == NULL)
win = stdscr;
getyx(win, y, x);
if(startx != 0)
x = startx;
if(starty != 0)
y = starty;
if(width == 0)
width = 80;
length = strlen(string);
temp = (width − length)/ 2;
x = startx + (int)temp;
mvwprintw(win, y, x, "%s", string);
refresh();
}
As you can see, to start using color, you should first call the function start_color(). After that you can
use color capabilities of your terminals by various functions. To find out whether terminal has color
capabilities or not, you can use has_colors() function, which returns FALSE if the terminal does not
support color.
Curses initializes all the colors supported by terminal when start_color() is called. These can be accessed by
the define constants like COLOR_BLACK etc. Now to actually start using colors, you have to define pairs.
Colors are always used in pairs. That means you have to use the function init_pair() to define the
foreground and background for the pair number you give. After that that pair number can be used as a normal
attribute with COLOR_PAIR()function. This may seem to be cumbersome at first. But this elegant solution
allows us to manage color pairs very easily. To appreciate it, you have to look into the the source code of
"dialog", a utility for displaying dialog boxes from shell scripts. The developers have defined foreground and
background combinations for all the colors they might need and initialized at the beginning. This makes it
very easy to set attributes just by accessing a pair which we already have defined as a constant.
The following colors are defined in curses.h. You can use these as parameters for various color functions.
COLOR_BLACK 0
COLOR_RED 1
COLOR_GREEN 2
COLOR_YELLOW 3
COLOR_BLUE 4
COLOR_MAGENTA 5
COLOR_CYAN 6
COLOR_WHITE 7
If your terminal cannot change the color definitions, the function returns ERR. The function
can_change_color() can be used to find out whether the terminal has the capability of changing color
content or not. The rgb content is scaled from 0 to 1000. Initially RED color is defined with content 1000(r),
0(g), 0(b).
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.
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.
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.
#include <stdio.h>
#include <ncurses.h>
#define WIDTH 30
#define HEIGHT 10
int startx = 0;
int starty = 0;
11. Key management. How to read function keys, arrow keys etc.. 30
NCURSES Programming HOWTO
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;
11. Key management. How to read function keys, arrow keys etc.. 31
NCURSES Programming HOWTO
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);
}
11. Key management. How to read function keys, arrow keys etc.. 32
12. Interfacing with the mouse
Now that you have seen how to get keys, lets do the same thing from mouse. Usually each UI allows the user
to interact with both keyboard and mouse.
The first parameter to above function is a bit mask of events you would like to listen. By default, all the
events are turned off. The bit mask ALL_MOUSE_EVENTS can be used to get all the events.
Name Description
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
BUTTON1_PRESSED mouse button 1 down
BUTTON1_RELEASED mouse button 1 up
BUTTON1_CLICKED mouse button 1 clicked
BUTTON1_DOUBLE_CLICKED mouse button 1 double clicked
BUTTON1_TRIPLE_CLICKED mouse button 1 triple clicked
BUTTON2_PRESSED mouse button 2 down
BUTTON2_RELEASED mouse button 2 up
BUTTON2_CLICKED mouse button 2 clicked
BUTTON2_DOUBLE_CLICKED mouse button 2 double clicked
BUTTON2_TRIPLE_CLICKED mouse button 2 triple clicked
BUTTON3_PRESSED mouse button 3 down
BUTTON3_RELEASED mouse button 3 up
BUTTON3_CLICKED mouse button 3 clicked
BUTTON3_DOUBLE_CLICKED mouse button 3 double clicked
BUTTON3_TRIPLE_CLICKED mouse button 3 triple clicked
BUTTON4_PRESSED mouse button 4 down
BUTTON4_RELEASED mouse button 4 up
BUTTON4_CLICKED mouse button 4 clicked
BUTTON4_DOUBLE_CLICKED mouse button 4 double clicked
BUTTON4_TRIPLE_CLICKED mouse button 4 triple clicked
BUTTON_SHIFT shift was down during button state change
BUTTON_CTRL control was down during button state change
BUTTON_ALT alt was down during button state change
ALL_MOUSE_EVENTS report all button state changes
REPORT_MOUSE_POSITION report mouse movement
MEVENT event;
ch = getch();
if(ch == KEY_MOUSE)
if(getmouse(&event) == OK)
. /* Do some thing with the event */
.
.
getmouse() returns the event into the pointer given to it. It's a structure which contains
typedef struct
{
short id; /* ID to distinguish multiple devices */
int x, y, z; /* event coordinates */
mmask_t bstate; /* button state bits */
}
The bstate is the main variable we are interested in. It tells the button state of the mouse.
Then with a code snippet like the following, we can find out what happened.
#include <ncurses.h>
#define WIDTH 30
#define HEIGHT 10
int startx = 0;
int starty = 0;
int main()
{ int c, choice = 0;
WINDOW *menu_win;
MEVENT event;
/* Initialize curses */
initscr();
clear();
noecho();
cbreak(); //Line buffering disabled. pass on everything
attron(A_REVERSE);
mvprintw(23, 1, "Click on Exit to quit (Works best in a virtual console)");
refresh();
attroff(A_REVERSE);
while(1)
{ c = wgetch(menu_win);
switch(c)
{ case KEY_MOUSE:
if(getmouse(&event) == OK)
{ /* When the user clicks left mouse button */
if(event.bstate & BUTTON1_PRESSED)
{ report_choice(event.x + 1, event.y + 1, &choice);
if(choice == −1) //Exit chosen
goto end;
mvprintw(22, 1, "Choice made is : %d String Chosen is \"%
refresh();
}
}
print_menu(menu_win, choice);
break;
}
}
end:
endwin();
return 0;
}
x = 2;
y = 2;
box(menu_win, 0, 0);
for(i = 0; i < n_choices; ++i)
{ if(highlight == i + 1)
{ 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);
}
i = startx + 2;
j = starty + 3;
The mouseinterval function sets the maximum time (in thousands of a second) that can elapse between press
and release events in order for them to be recognized as a click. This function returns the previous interval
value. The default is one fifth of a second.
getyx(win, y, x);
/* win: window pointer
* y, x: y, x co−ordinates will be put into this variables
*/
The function getparyx() gets the beginning co−ordinates of the sub window relative to the main window. This
is some times useful to update a sub window. When designing fancy stuff like writing multiple menus, it
becomes difficult to store the menu positions, their first option co−ordinates etc. A simple solution to this
problem, is to create menus in sub windows and later find the starting co−ordinates of the menus by using
getparyx().
The functions getbegyx() and getmaxyx() store current window's beginning and maximum co−ordinates.
These functions are useful in the same way as above in managing the windows and sub windows effectively.
The function copywin() can be used to copy a window completely onto another window. It takes the
source and destination windows as parameters and according to the rectangle specified, it copies the
rectangular region from source to destination window. It's last parameter specifies whether to overwrite or
just overlay the contents on to the destination window. If this argument is true, then the copying is
non−destructive.
14.1. curs_set()
This function can be used to make the cursor invisible. The parameter to this function should be
0 : invisible or
1 : normal or
2 : very visible.
#include <ncurses.h>
int main()
{
initscr(); /* Start curses mode */
printw("Hello World !!!\n"); /* Print Hello World */
refresh(); /* Print it on to the real screen */
def_prog_mode(); /* Save the tty modes */
endwin(); /* End curses mode temporarily */
system("/bin/sh"); /* Do whatever you like in cooked mode */
reset_prog_mode(); /* Return to the previous tty mode*/
/* stored by def_prog_mode() */
refresh(); /* Do refresh() to restore the */
/* Screen contents */
printw("Another String\n"); /* Back to curses use the full */
refresh(); /* capabilities of curses */
endwin(); /* End curses mode */
return 0;
}
#include <ncurses.h>
int main()
{
initscr();
refresh();
getch();
endwin();
return 0;
}
Don't despair. There's an elegant solution provided in panels library. In the words of developers of ncurses
When your interface design is such that windows may dive deeper into the visibility stack or pop to the top at
runtime, the resulting book−keeping can be tedious and difficult to get right. Hence the panels library.
If you have lot of overlapping windows, then panels library is the way to go. It obviates the need of doing
series of wnoutrefresh(), doupdate() and relieves the burden of doing it correctly(bottom up). The library
maintains information about the order of windows, their overlapping and update the screen properly. So why
wait? Let's take a close peek into panels.
Let's make the concepts clear, with some programs. The following is a simple program which creates 3
overlapping panels and shows them on the screen.
#include <panel.h>
.
.
.
#include <panel.h>
int main()
{ WINDOW *my_wins[3];
PANEL *my_panels[3];
int lines = 10, cols = 40, y = 2, x = 4, i;
initscr();
cbreak();
noecho();
/*
* Create borders around the windows so that you can see the effect
* of panels
*/
for(i = 0; i < 3; +++i)
box(my_wins[i], 0, 0);
getch();
endwin();
}
As you can see, above program follows a simple flow as explained. The windows are created with newwin()
and then they are attached to panels with new_panel(). As we attach one panel after another, the stack of
panels gets updated. To put them on screen update_panels() and doupdate() are called.
#include <panel.h>
#define NLINES 10
#define NCOLS 40
int main()
{ WINDOW *my_wins[3];
PANEL *my_panels[3];
PANEL *top;
int ch;
/* Initialize curses */
initscr();
start_color();
cbreak();
noecho();
keypad(stdscr, TRUE);
init_wins(my_wins, 3);
top = my_panels[2];
while((ch = getch()) != KEY_F(1))
{ switch(ch)
{ case 9:
top = (PANEL *)panel_userptr(top);
top_panel(top);
break;
}
update_panels();
doupdate();
}
endwin();
return 0;
}
y = 2;
x = 10;
for(i = 0; i < n; ++i)
{ wins[i] = newwin(NLINES, NCOLS, y, x);
sprintf(label, "Window Number %d", i + 1);
win_show(wins[i], label, i + 1);
y += 3;
x += 7;
}
}
box(win, 0, 0);
mvwaddch(win, 2, 0, ACS_LTEE);
mvwhline(win, 2, 1, ACS_HLINE, width − 2);
mvwaddch(win, 2, width − 1, ACS_RTEE);
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)
{ int length, x, y;
float temp;
if(win == NULL)
win = stdscr;
getyx(win, y, x);
if(startx != 0)
x = startx;
if(starty != 0)
y = starty;
if(width == 0)
width = 80;
length = strlen(string);
temp = (width − length)/ 2;
x = startx + (int)temp;
wattron(win, color);
mvwprintw(win, y, x, "%s", string);
wattroff(win, color);
refresh();
}
Resizing a panel is slightly complex. There is no straight forward function just to resize the window
associated with a panel. A solution to resize a panel is to create a new window with the desired sizes, change
the window associated with the panel using replace_panel(). Don't forget to delete the old window. The
window associated with a panel can be found by using the function panel_window().
The following program shows these concepts, in supposedly simple program. You can cycle through the
window with <TAB> as usual. To resize or move the active panel press 'r' for resize 'm' for moving. Then use
arrow keys to resize or move it to the desired way and press enter to end your resizing or moving. This
example makes use of user data to get the required data to do the operations.
#include <panel.h>
#define NLINES 10
#define NCOLS 40
int main()
{ WINDOW *my_wins[3];
PANEL *my_panels[3];
PANEL_DATA *top;
PANEL *stack_top;
WINDOW *temp_win, *old_win;
int ch;
/* Initialize curses */
initscr();
start_color();
cbreak();
noecho();
keypad(stdscr, TRUE);
init_wins(my_wins, 3);
set_user_ptrs(my_panels, 3);
/* Update the stacking order. 2nd panel will be on top */
update_panels();
stack_top = my_panels[2];
top = (PANEL_DATA *)panel_userptr(stack_top);
newx = top−>x;
newy = top−>y;
neww = top−>w;
newh = top−>h;
while((ch = getch()) != KEY_F(1))
{ switch(ch)
{ case 9: /* Tab */
top = (PANEL_DATA *)panel_userptr(stack_top);
top_panel(top−>next);
stack_top = top−>next;
top = (PANEL_DATA *)panel_userptr(stack_top);
newx = top−>x;
newy = top−>y;
neww = top−>w;
newh = top−>h;
break;
case 'r': /* Re−Size*/
size = TRUE;
attron(COLOR_PAIR(4));
mvprintw(LINES − 4, 0, "Entered Resizing :Use Arrow Keys to resiz
refresh();
attroff(COLOR_PAIR(4));
break;
case 'm': /* Move */
attron(COLOR_PAIR(4));
mvprintw(LINES − 4, 0, "Entered Moving: Use Arrow Keys to Move an
refresh();
attroff(COLOR_PAIR(4));
move = TRUE;
break;
case KEY_LEFT:
if(size == TRUE)
{ −−newx;
++neww;
}
if(move == TRUE)
−−newx;
break;
case KEY_RIGHT:
if(size == TRUE)
{ ++newx;
−−neww;
}
if(move == TRUE)
++newx;
break;
case KEY_UP:
if(size == TRUE)
{ −−newy;
++newh;
}
if(move == TRUE)
−−newy;
break;
case KEY_DOWN:
if(size == TRUE)
{ ++newy;
−−newh;
}
if(move == TRUE)
++newy;
break;
case 10: /* Enter */
move(LINES − 4, 0);
clrtoeol();
refresh();
if(size == TRUE)
{ old_win = panel_window(stack_top);
temp_win = newwin(newh, neww, newy, newx);
replace_panel(stack_top, temp_win);
win_show(temp_win, top−>label, top−>label_color);
delwin(old_win);
size = FALSE;
}
if(move == TRUE)
{ move_panel(stack_top, newy, newx);
move = FALSE;
}
break;
}
attron(COLOR_PAIR(4));
mvprintw(LINES − 3, 0, "Use 'm' for moving, 'r' for resizing");
mvprintw(LINES − 2, 0, "Use tab to browse through the windows (F1 to Exit)");
attroff(COLOR_PAIR(4));
refresh();
update_panels();
doupdate();
}
endwin();
return 0;
}
y = 2;
x = 10;
for(i = 0; i < n; ++i)
{ wins[i] = newwin(NLINES, NCOLS, y, x);
sprintf(label, "Window Number %d", i + 1);
win_show(wins[i], label, i + 1);
y += 3;
x += 7;
}
}
box(win, 0, 0);
mvwaddch(win, 2, 0, ACS_LTEE);
mvwhline(win, 2, 1, ACS_HLINE, width − 2);
mvwaddch(win, 2, width − 1, ACS_RTEE);
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)
{ int length, x, y;
float temp;
if(win == NULL)
win = stdscr;
getyx(win, y, x);
if(startx != 0)
x = startx;
if(starty != 0)
y = starty;
if(width == 0)
width = 80;
length = strlen(string);
temp = (width − length)/ 2;
x = startx + (int)temp;
wattron(win, color);
mvwprintw(win, y, x, "%s", string);
wattroff(win, color);
refresh();
}
Concentrate on the main while loop. Once it finds out the type of key pressed, it takes appropriate action. If 'r'
is pressed resizing mode is started. After this the new sizes are updated as the user presses the arrow keys.
When the user presses <ENTER> present selection ends and panel is resized by using the concept explained.
While in resizing mode the program doesn't show how the window is getting resized. It's left as an exercise to
the reader to print a dotted border while it gets resized to a new position.
When the user presses 'm' the move mode starts. This is a bit simpler than resizing. As the arrow keys are
pressed the new position is updated and pressing of <ENTER> causes the panel to be moved by calling the
function move_panel().
In this program the user data which is represented as PANEL_DATA, plays very important role in finding the
associated information with a panel. As written in the comments, the PANEL_DATA stores the panel sizes,
label, label color and a pointer to the next panel in the cycle.
The following program shows the hiding of panels. Press 'a' or 'b' or 'c' to show or hide first, second and third
windows respectively. It uses a user data with a small variable hide, which keeps track of whether the
window is hidden or not. For some reason the function panel_hidden() which tells whether a panel is
hidden or not is not working. A bug report was also presented by Michael Andres here
#include <panel.h>
#define NLINES 10
#define NCOLS 40
int main()
{ WINDOW *my_wins[3];
PANEL *my_panels[3];
PANEL_DATA panel_datas[3];
PANEL_DATA *temp;
int ch;
/* Initialize curses */
initscr();
start_color();
cbreak();
noecho();
keypad(stdscr, TRUE);
init_wins(my_wins, 3);
set_panel_userptr(my_panels[0], &panel_datas[0]);
set_panel_userptr(my_panels[1], &panel_datas[1]);
set_panel_userptr(my_panels[2], &panel_datas[2]);
attroff(COLOR_PAIR(4));
doupdate();
{ case 'a':
temp = (PANEL_DATA *)panel_userptr(my_panels[0]);
if(temp−>hide == FALSE)
{ hide_panel(my_panels[0]);
temp−>hide = TRUE;
}
else
{ show_panel(my_panels[0]);
temp−>hide = FALSE;
}
break;
case 'b':
temp = (PANEL_DATA *)panel_userptr(my_panels[1]);
if(temp−>hide == FALSE)
{ hide_panel(my_panels[1]);
temp−>hide = TRUE;
}
else
{ show_panel(my_panels[1]);
temp−>hide = FALSE;
}
break;
case 'c':
temp = (PANEL_DATA *)panel_userptr(my_panels[2]);
if(temp−>hide == FALSE)
{ hide_panel(my_panels[2]);
temp−>hide = TRUE;
}
else
{ show_panel(my_panels[2]);
temp−>hide = FALSE;
}
break;
}
update_panels();
doupdate();
}
endwin();
return 0;
}
y = 2;
x = 10;
for(i = 0; i < n; ++i)
{ wins[i] = newwin(NLINES, NCOLS, y, x);
sprintf(label, "Window Number %d", i + 1);
win_show(wins[i], label, i + 1);
y += 3;
x += 7;
}
}
box(win, 0, 0);
mvwaddch(win, 2, 0, ACS_LTEE);
mvwhline(win, 2, 1, ACS_HLINE, width − 2);
mvwaddch(win, 2, width − 1, ACS_RTEE);
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)
{ int length, x, y;
float temp;
if(win == NULL)
win = stdscr;
getyx(win, y, x);
if(startx != 0)
x = startx;
if(starty != 0)
y = starty;
if(width == 0)
width = 80;
length = strlen(string);
temp = (width − length)/ 2;
x = startx + (int)temp;
wattron(win, color);
mvwprintw(win, y, x, "%s", string);
wattroff(win, color);
refresh();
}
A menu is a screen display that assists the user to choose some subset of a given set of items. To put it
simple, a menu is a collection of items from which one or more items can be chosen. Some readers might not
be aware of multiple item selection capability. Menu library provides functionality to write menus from
which the user can chose more than one item as the preferred choice. This is dealt with in a later section. Now
it is time for some rudiments.
1. Initialize curses
2. Create items using new_item(). You can specify a name and description for the items.
3. Create the menu with new_menu() by specifying the items to be attached with.
4. Post the menu with menu_post() and refresh the screen.
5. Process the user requests with a loop and do necessary updates to menu with menu_driver.
6. Unpost the menu with menu_unpost()
7. Free the memory allocated to menu by free_menu()
8. Free the memory allocated to the items with free_item()
9. End curses
Let's see a program which prints a simple menu and updates the current selection with up, down arrows.
#include <menu.h>
.
.
.
#include <curses.h>
#include <menu.h>
char *choices[] = {
"Choice 1",
"Choice 2",
"Choice 3",
"Choice 4",
"Exit",
};
int main()
{ ITEM **my_items;
int c;
MENU *my_menu;
int n_choices, i;
ITEM *cur_item;
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
n_choices = ARRAY_SIZE(choices);
my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *));
free_item(my_items[0]);
free_item(my_items[1]);
free_menu(my_menu);
endwin();
}
This program demonstrates the basic concepts involved in creating a menu using menus library. First we
create the items using new_item() and then attach them to the menu with new_menu() function. After posting
the menu and refreshing the screen, the main processing loop starts. It reads user input and takes
corresponding action. The function menu_driver() is the main work horse of the menu system. The second
parameter to this function tells what's to be done with the menu. According to the parameter, menu_driver()
does the corresponding task. The value can be either a menu navigational request, an ascii character, or a
Don't get overwhelmed by the number of options. We will see them slowly one after another. The options of
interest in this example are REQ_UP_ITEM and REQ_DOWN_ITEM. These two options when passed to
menu_driver, menu driver updates the current item to one item up or down respectively.
A Menu can be displayed with multiple columns for more than one item. This can be done by using
the menu_format()function. When a multi columnar menu is displayed these requests cause the
menu driver to move the current selection to left or right.
These two options you have seen in the above example. These options when given, makes the
menu_driver to move the current selection to an item up or down.
• REQ_SCR_* options
• REQ_TOGGLE_ITEM
This request when given, toggles the present selection. This option is to be used only in a multi
valued menu. So to use this request the option O_ONEVALUE must be off. This option can be made
off or on with set_menu_opts().
• Pattern Requests
Every menu has an associated pattern buffer, which is used to find the nearest match to the ascii
characters entered by the user. Whenever ascii characters are given to menu_driver, it puts in to the
pattern buffer. It also tries to find the nearest match to the pattern in the items list and moves current
selection to that item. The request REQ_CLEAR_PATTERN clears the pattern buffer. The request
REQ_BACK_PATTERN deletes the previous character in the pattern buffer. In case the pattern
matches more than one item then the matched items can be cycled through REQ_NEXT_MATCH
and REQ_PREV_MATCH which move the current selection to the next and previous matches
respectively.
• Mouse Requests
In case of KEY_MOUSE requests, according to the mouse position an action is taken accordingly.
The action to be taken is explained in the man page as,
Each of the above requests will be explained in the following lines with several examples whenever
appropriate.
#include <menu.h>
char *choices[] = {
"Choice 1",
"Choice 2",
"Choice 3",
"Choice 4",
"Exit",
(char *)NULL,
};
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color);
int main()
{ ITEM **my_items;
int c;
MENU *my_menu;
WINDOW *my_menu_win;
int n_choices, i;
/* Initialize curses */
initscr();
start_color();
cbreak();
noecho();
keypad(stdscr, TRUE);
init_pair(1, COLOR_RED, COLOR_BLACK);
/* Create items */
n_choices = ARRAY_SIZE(choices);
my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *));
for(i = 0; i < n_choices; ++i)
my_items[i] = new_item(choices[i], choices[i]);
/* Crate menu */
my_menu = new_menu((ITEM **)my_items);
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)
{ int length, x, y;
float temp;
if(win == NULL)
win = stdscr;
getyx(win, y, x);
if(startx != 0)
x = startx;
if(starty != 0)
y = starty;
if(width == 0)
width = 80;
length = strlen(string);
temp = (width − length)/ 2;
x = startx + (int)temp;
wattron(win, color);
mvwprintw(win, y, x, "%s", string);
wattroff(win, color);
refresh();
}
This example creates a menu with a title, border, a fancy line separating title and the items. As you can see, in
order to attach a window to a menu the function set_menu_win() has to be used. Then we attach the sub
window also. This displays the items in the sub window. You can also set the mark string which gets
displayed to the left of the selected item with set_menu_mark().
#include <curses.h>
#include <menu.h>
char *choices[] = {
"Choice 1",
"Choice 2",
"Choice 3",
"Choice 4",
"Choice 5",
"Choice 6",
"Choice 7",
"Choice 8",
"Choice 9",
"Choice 10",
"Exit",
(char *)NULL,
};
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color);
int main()
{ ITEM **my_items;
int c;
MENU *my_menu;
WINDOW *my_menu_win;
int n_choices, i;
/* Initialize curses */
initscr();
start_color();
cbreak();
noecho();
keypad(stdscr, TRUE);
init_pair(1, COLOR_RED, COLOR_BLACK);
init_pair(2, COLOR_CYAN, COLOR_BLACK);
/* Create items */
n_choices = ARRAY_SIZE(choices);
my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *));
for(i = 0; i < n_choices; ++i)
my_items[i] = new_item(choices[i], choices[i]);
/* Crate menu */
my_menu = new_menu((ITEM **)my_items);
attron(COLOR_PAIR(2));
mvprintw(LINES − 2, 0, "Use PageUp and PageDown to scoll down or up a page of items");
mvprintw(LINES − 1, 0, "Arrow Keys to navigate (F1 to Exit)");
attroff(COLOR_PAIR(2));
refresh();
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)
{ int length, x, y;
float temp;
if(win == NULL)
win = stdscr;
getyx(win, y, x);
if(startx != 0)
x = startx;
if(starty != 0)
y = starty;
if(width == 0)
width = 80;
length = strlen(string);
temp = (width − length)/ 2;
x = startx + (int)temp;
wattron(win, color);
mvwprintw(win, y, x, "%s", string);
wattroff(win, color);
refresh();
}
This program is self−explanatory. In this example the number of choices has been increased to ten, which is
larger than our sub window size which can hold 6 items. This message has to be explicitly conveyed to the
menu system with the function set_menu_format(). In here we specify the number of rows and columns we
want to be displayed for a single page. We can specify any number of items to be shown, in the rows
variables, if it is less than the height of the sub window. If the key pressed by the user is a PAGE UP or
PAGE DOWN, the menu is scrolled a page due to the requests (REQ_SCR_DPAGE and
REQ_SCR_UPAGE) given to menu_driver().
#include <curses.h>
#include <menu.h>
char *choices[] = {
"Choice 1", "Choice 2", "Choice 3", "Choice 4", "Choice 5",
"Choice 6", "Choice 7", "Choice 8", "Choice 9", "Choice 10",
"Choice 11", "Choice 12", "Choice 13", "Choice 14", "Choice 15",
"Choice 16", "Choice 17", "Choice 18", "Choice 19", "Choice 20",
"Exit",
(char *)NULL,
};
int main()
{ ITEM **my_items;
int c;
MENU *my_menu;
WINDOW *my_menu_win;
int n_choices, i;
/* Initialize curses */
initscr();
start_color();
cbreak();
noecho();
keypad(stdscr, TRUE);
init_pair(1, COLOR_RED, COLOR_BLACK);
init_pair(2, COLOR_CYAN, COLOR_BLACK);
/* Create items */
n_choices = ARRAY_SIZE(choices);
my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *));
for(i = 0; i < n_choices; ++i)
my_items[i] = new_item(choices[i], choices[i]);
/* Crate menu */
my_menu = new_menu((ITEM **)my_items);
attron(COLOR_PAIR(2));
mvprintw(LINES − 3, 0, "Use PageUp and PageDown to scroll");
mvprintw(LINES − 2, 0, "Use Arrow Keys to navigate (F1 to Exit)");
attroff(COLOR_PAIR(2));
refresh();
free_menu(my_menu);
for(i = 0; i < n_choices; ++i)
free_item(my_items[i]);
endwin();
}
Watch the function call to set_menu_format(). It specifies the number of columns to be 3, thus displaying 3
items per row. We have also switched off the showing descriptions with the function menu_opts_off(). There
are couple of functions set_menu_opts(), menu_opts_on() and menu_opts() which can be used to manipulate
menu options. The following menu options can be specified.
O_ONEVALUE
Only one item can be selected for this menu.
O_SHOWDESC
Display the item descriptions when the menu is
posted.
O_ROWMAJOR
Display the menu in row−major order.
O_IGNORECASE
Ignore the case when pattern−matching.
O_SHOWMATCH
Move the cursor to within the item name while pat-
tern−matching.
O_NONCYCLIC
Don't wrap around next−item and previous−item,
requests to the other end of the menu.
All options are on by default. You can switch specific attributes on or off with menu_opts_on() and
menu_opts_off() functions. You can also use set_menu_opts() to directly specify the options. The argument
to this function should be a OR ed value of some of those above constants. The function menu_opts() can be
used to find out a menu's present options.
#include <curses.h>
#include <menu.h>
char *choices[] = {
"Choice 1",
"Choice 2",
"Choice 3",
"Choice 4",
"Choice 5",
"Choice 6",
"Choice 7",
"Exit",
};
int main()
{ ITEM **my_items;
int c;
MENU *my_menu;
int n_choices, i;
ITEM *cur_item;
/* Initialize curses */
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
/* Initialize items */
n_choices = ARRAY_SIZE(choices);
my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *));
for(i = 0; i < n_choices; ++i)
my_items[i] = new_item(choices[i], choices[i]);
my_items[n_choices] = (ITEM *)NULL;
items = menu_items(my_menu);
temp[0] = '\0';
for(i = 0; i < item_count(my_menu); ++i)
if(item_value(items[i]) == TRUE)
{ strcat(temp, item_name(items[i]));
strcat(temp, " ");
}
move(20, 0);
clrtoeol();
mvprintw(20, 0, temp);
refresh();
}
break;
}
}
free_item(my_items[0]);
free_item(my_items[1]);
free_menu(my_menu);
endwin();
}
Whew, A lot of new functions. Let's take them one after another. Firstly, the REQ_TOGGLE_ITEM. In a
multi−valued menu, the user should be allowed to select or un select more than one item. The request
REQ_TOGGLE_ITEM toggles the present selection. In this case when space is pressed
REQ_TOGGLE_ITEM request is sent to menu_driver to achieve the result.
Now when the user presses <ENTER> we show the items he presently selected. First we find out the items
associated with the menu using the function menu_items(). Then we loop through the items to find out if the
item is selected or not. The function item_value() returns TRUE if an item is selected. The function
item_count() returns the number of items in the menu. The item name can be found with item_name(). You
can also find the description associated with an item using item_description().
The function set_menu_grey() can be used to set the display attribute for the non−selectable items in the
menu. This brings us to the interesting option for an item the one and only O_SELECTABLE. We can turn it
off by the function item_opts_off() and after that that item is not selectable. It's like a grayed item in those
fancy windows menus. Let's put these concepts in practice with this example
#include <menu.h>
char *choices[] = {
"Choice 1",
"Choice 2",
"Choice 3",
"Choice 4",
"Choice 5",
"Choice 6",
"Choice 7",
"Exit",
};
int main()
{ ITEM **my_items;
int c;
MENU *my_menu;
int n_choices, i;
ITEM *cur_item;
/* Initialize curses */
initscr();
start_color();
cbreak();
noecho();
keypad(stdscr, TRUE);
init_pair(1, COLOR_RED, COLOR_BLACK);
init_pair(2, COLOR_GREEN, COLOR_BLACK);
init_pair(3, COLOR_MAGENTA, COLOR_BLACK);
/* Initialize items */
n_choices = ARRAY_SIZE(choices);
my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *));
for(i = 0; i < n_choices; ++i)
my_items[i] = new_item(choices[i], choices[i]);
my_items[n_choices] = (ITEM *)NULL;
item_opts_off(my_items[3], O_SELECTABLE);
item_opts_off(my_items[6], O_SELECTABLE);
/* Create menu */
my_menu = new_menu((ITEM **)my_items);
#include <curses.h>
#include <menu.h>
char *choices[] = {
"Choice 1",
"Choice 2",
"Choice 3",
"Choice 4",
"Choice 5",
"Choice 6",
"Choice 7",
"Exit",
};
void func(char *name);
int main()
{ ITEM **my_items;
int c;
MENU *my_menu;
int n_choices, i;
ITEM *cur_item;
/* Initialize curses */
initscr();
start_color();
cbreak();
noecho();
keypad(stdscr, TRUE);
init_pair(1, COLOR_RED, COLOR_BLACK);
init_pair(2, COLOR_GREEN, COLOR_BLACK);
init_pair(3, COLOR_MAGENTA, COLOR_BLACK);
/* Initialize items */
n_choices = ARRAY_SIZE(choices);
my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *));
for(i = 0; i < n_choices; ++i)
{ my_items[i] = new_item(choices[i], choices[i]);
/* Set the user pointer */
set_item_userptr(my_items[i], func);
}
my_items[n_choices] = (ITEM *)NULL;
/* Create menu */
my_menu = new_menu((ITEM **)my_items);
cur = current_item(my_menu);
p = item_userptr(cur);
p((char *)item_name(cur));
pos_menu_cursor(my_menu);
break;
}
break;
}
}
unpost_menu(my_menu);
for(i = 0; i < n_choices; ++i)
free_item(my_items[i]);
free_menu(my_menu);
endwin();
}
A form is a collection of fields; each field can be either a label(static text) or a data−entry location. The forms
also library provides functions to divide forms into multiple pages.
1. Initialize curses
2. Create fields using new_field(). You can specify the height and width of the field, and its position on
the form.
3. Create the forms with new_form() by specifying the fields to be attached with.
4. Post the form with form_post() and refresh the screen.
5. Process the user requests with a loop and do necessary updates to form with form_driver.
6. Unpost the menu with form_unpost()
7. Free the memory allocated to menu by free_form()
8. Free the memory allocated to the items with free_field()
9. End curses
As you can see, working with forms library is much similar to handling menu library. The following
examples will explore various aspects of form processing. Let's start the journey with a simple example. first.
#include <form.h>
.
.
.
#include <form.h>
int main()
{ FIELD *field[3];
FORM *my_form;
int ch;
/* Initialize curses */
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
endwin();
return 0;
}
Above example is pretty straight forward. It creates two fields with new_field(). new_field() takes
height, width, starty, startx, number of offscreen rows and number of additional working buffers. The fifth
argument number of offscreen rows specifies how much of the field to be shown. If it is zero, the entire field
is always displayed otherwise the form will be scrollable when the user accesses not displayed parts of the
field. The forms library allocates one buffer per field to store the data user enters. Using the last parameter to
new_field() we can specify it to allocate some additional buffers. These can be used for any purpose you like.
After creating the fields, back ground attribute of both of them is set to an underscore with set_field_back().
The AUTOSKIP option is turned off using field_opts_off(). If this option is turned on, focus will move to the
next field in the form once the active field is filled up completely.
After attaching the fields to the form, it is posted. Here on, user inputs are processed in the while loop, by
making corresponding requests to form_driver. The details of all the requests to the form_driver() are
explained later.
The justification mode valued accepted and returned by these functions are NO_JUSTIFICATION,
JUSTIFY_RIGHT, JUSTIFY_LEFT, or JUSTIFY_CENTER.
Though above functions seem quite simple, using colors with set_field_fore() may be frustrating in the
beginning. Let me first explain about foreground and background attributes of a field. The foreground
attribute is associated with the character. That means a character in the field is printed with the attribute you
have set with set_field_fore(). Background attribute is the attribute used to fill background of field, whether
any character is there or not. So what about colors? Since colors are always defined in pairs, what is the right
way to display colored fields? Here's an example clarifying color attributes.
#include <form.h>
int main()
{ FIELD *field[3];
FORM *my_form;
int ch;
/* Initialize curses */
initscr();
start_color();
cbreak();
noecho();
keypad(stdscr, TRUE);
endwin();
return 0;
}
Play with the color pairs and try to understand the foreground and background attributes. In my programs
using color attributes, I usually set only the background with set_field_back(). Curses simply doesn't allow
defining individual color attributes.
The function set_field_opts() can be used to directly set attributes of a field or you can choose to switch a few
attributes on and off with field_opts_on() and field_opts_off() selectively. Anytime you can query the
attributes of a field with field_opts(). The following is the list of available options. By default, all options are
on.
O_VISIBLE
Controls whether the field is visible on the screen. Can be used during form processing to hide or pop
up fields depending on the value of parent fields.
O_ACTIVE
Controls whether the field is active during forms processing (i.e. visited by form navigation keys).
Can be used to make labels or derived fields with buffer values alterable by the forms application, not
the user.
O_PUBLIC
Controls whether data is displayed during field entry. If this option is turned off on a field, the library
will accept and edit data in that field, but it will not be displayed and the visible field cursor will not
move. You can turn off the O_PUBLIC bit to define password fields.
O_EDIT
Controls whether the field's data can be modified. When this option is off, all editing requests except
REQ_PREV_CHOICE and REQ_NEXT_CHOICEwill fail. Such read−only fields may be useful for
help messages.
O_WRAP
O_BLANK
Controls field blanking. When this option is on, entering a character at the first field position erases
the entire field (except for the just−entered character).
O_AUTOSKIP
Controls automatic skip to next field when this one fills. Normally, when the forms user tries to type
more data into a field than will fit, the editing location jumps to next field. When this option is off,
the user's cursor will hang at the end of the field. This option is ignored in dynamic fields that have
not reached their size limit.
O_NULLOK
Controls whether validation is applied to blank fields. Normally, it is not; the user can leave a field
blank without invoking the usual validation check on exit. If this option is off on a field, exit from it
will invoke a validation check.
O_PASSOK
Controls whether validation occurs on every exit, or only after the field is modified. Normally the
latter is true. Setting O_PASSOK may be useful if your field's validation function may change during
forms processing.
O_STATIC
Controls whether the field is fixed to its initial dimensions. If you turn this off, the field becomes
dynamic and will stretch to fit entered data.
A field's options cannot be changed while the field is currently selected. However, options may be changed
on posted fields that are not current.
The option values are bit−masks and can be composed with logical−or in the obvious way. You have seen the
usage of switching off O_AUTOSKIP option. The following example clarifies usage of some more options.
Other options are explained where appropriate.
#include <form.h>
#define STARTX 15
#define STARTY 4
#define WIDTH 25
#define N_FIELDS 3
int main()
{ FIELD *field[N_FIELDS];
FORM *my_form;
int ch, i;
/* Initialize curses */
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
form_driver(my_form, ch);
break;
}
}
endwin();
return 0;
}
This example, though useless, shows the usage of options. If used properly, they can present information very
effectively in a form. The second field being not O_PUBLIC, does not show the characters you are typing.
It's better to check the field's status only after after leaving the field, as data buffer might not have been
updated yet as the validation is still due. To guarantee that right status is returned, call field_status() either (1)
in the field's exit validation check routine, (2) from the field's or form's initialization or termination hooks, or
(3) just after a REQ_VALIDATION request has been processed by the forms driver
to the field orientation it will scroll horizontally or vertically to incorporate the new data.
To make a field dynamically growable, the option O_STATIC should be turned off. This can be done with a
field_opts_off(field_pointer, O_STATIC);
But it's usually not advisable to allow a field to grow infinitely. You can set a maximum limit to the growth
of the field with
Recall the library routine new_field; a new field created with height set to one will be defined to be a one line
field. A new field created with height greater than one will be defined to be a multi line field.
A one line field with O_STATIC turned off (dynamically growable field) will contain a single fixed row, but
the number of columns can increase if the user enters more data than the initial field will hold. The number of
columns displayed will remain fixed and the additional data will scroll horizontally.
A multi line field with O_STATIC turned off (dynamically growable field) will contain a fixed number of
columns, but the number of rows can increase if the user enters more data than the initial field will hold. The
number of rows displayed will remain fixed and the additional data will scroll vertically.
The above two paragraphs pretty much describe a dynamically growable field's behavior. The way other parts
of forms library behaves is described below:
1. The field option O_AUTOSKIP will be ignored if the option O_STATIC is off and there is no
maximum growth specified for the field. Currently, O_AUTOSKIP generates an automatic
REQ_NEXT_FIELD form driver request when the user types in the last character position of a field.
On a growable field with no maximum growth specified, there is no last character position. If a
maximum growth is specified, the O_AUTOSKIP option will work as normal if the field has grown
to its maximum size.
2. The field justification will be ignored if the option O_STATIC is off. Currently, set_field_just can be
used to JUSTIFY_LEFT, JUSTIFY_RIGHT, JUSTIFY_CENTER the contents of a one line field. A
growable one line field will, by definition, grow and scroll horizontally and may contain more data
than can be justified. The return from field_just will be unchanged.
3. The overloaded form driver request REQ_NEW_LINE will operate the same way regardless of the
O_NL_OVERLOAD form option if the field option O_STATIC is off and there is no maximum
growth specified for the field. Currently, if the form option O_NL_OVERLOAD is on,
REQ_NEW_LINE implicitly generates a REQ_NEXT_FIELD if called from the last line of a field. If
a field can grow without bound, there is no last line, so REQ_NEW_LINE will never implicitly
generate a REQ_NEXT_FIELD. If a maximum growth limit is specified and the
Some of the above points make sense only after explaining form driver. We will be looking into that in next
few sections.
Since this is pretty much similar to menu windows, I am providing an example with out much explanation.
The functions are similar and they work the same way.
#include <form.h>
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color);
int main()
{
FIELD *field[3];
FORM *my_form;
WINDOW *my_form_win;
int ch, rows, cols;
/* Initialize curses */
initscr();
start_color();
cbreak();
noecho();
keypad(stdscr, TRUE);
field[2] = NULL;
post_form(my_form);
wrefresh(my_form_win);
endwin();
return 0;
}
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)
{ int length, x, y;
float temp;
if(win == NULL)
win = stdscr;
getyx(win, y, x);
if(startx != 0)
x = startx;
if(starty != 0)
y = starty;
if(width == 0)
width = 80;
length = strlen(string);
temp = (width − length)/ 2;
x = startx + (int)temp;
wattron(win, color);
mvwprintw(win, y, x, "%s", string);
wattroff(win, color);
refresh();
}
The form driver validates the data in a field only when data is entered by the end−user. Validation does not
occur when
The following are the pre−defined validation types. You can also specify custom validation, though it's a bit
tricky and cumbersome.
The width argument sets a minimum width of data. The user has to enter at−least width number of characters
before he can leave the field. Typically you'll want to set this to the field width; if it's greater than the field
width, the validation check will always fail. A minimum width of zero makes field completion optional.
TYPE_ALPHA 82
TYPE_ALNUM
This field type accepts alphabetic data and digits; no blanks, no special characters (this is checked at
character−entry time). It is set up with:
The width argument sets a minimum width of data. As with TYPE_ALPHA, typically you'll want to set this
to the field width; if it's greater than the field width, the validation check will always fail. A minimum width
of zero makes field completion optional.
TYPE_ALNUM 83
TYPE_ENUM
This type allows you to restrict a field's values to be among a specified set of string values (for example, the
two−letter postal codes for U.S. states). It is set up with:
The valuelist parameter must point at a NULL−terminated list of valid strings. The checkcase argument, if
true, makes comparison with the string case−sensitive.
When the user exits a TYPE_ENUM field, the validation procedure tries to complete the data in the buffer to
a valid entry. If a complete choice string has been entered, it is of course valid. But it is also possible to enter
a prefix of a valid string and have it completed for you.
By default, if you enter such a prefix and it matches more than one value in the string list, the prefix will be
completed to the first matching value. But the checkunique argument, if true, requires prefix matches to be
unique in order to be valid.
The REQ_NEXT_CHOICE and REQ_PREV_CHOICE input requests can be particularly useful with these
fields.
TYPE_ENUM 84
TYPE_INTEGER
This field type accepts an integer. It is set up as follows:
Valid characters consist of an optional leading minus and digits. The range check is performed on exit. If the
range maximum is less than or equal to the minimum, the range is ignored.
If the value passes its range check, it is padded with as many leading zero digits as necessary to meet the
padding argument.
A TYPE_INTEGER value buffer can conveniently be interpreted with the C library function atoi(3).
TYPE_INTEGER 85
TYPE_NUMERIC
This field type accepts a decimal number. It is set up as follows:
Valid characters consist of an optional leading minus and digits. possibly including a decimal point. The
range check is performed on exit. If the range maximum is less than or equal to the minimum, the range is
ignored.
If the value passes its range check, it is padded with as many trailing zero digits as necessary to meet the
padding argument.
A TYPE_NUMERIC value buffer can conveniently be interpreted with the C library function atof(3).
TYPE_NUMERIC 86
TYPE_REGEXP
This field type accepts data matching a regular expression. It is set up as follows:
The syntax for regular expressions is that of regcomp(3). The check for regular−expression match is
performed on exit.
As you have seen some of the examples above, you have to be in a loop looking for user input and then
decide whether it's a field data or a form request. The form requests are then passed to form_driver() to do the
work.
The requests roughly can be divided into following categories. Different requests and their usage is explained
below:
These requests treat the list as cyclic; that is, REQ_NEXT_PAGE from the last page goes to the first, and
REQ_PREV_PAGE from the first page goes to the last.
TYPE_REGEXP 87
NCURSES Programming HOWTO
These requests treat the list of fields on a page as cyclic; that is, REQ_NEXT_FIELD from the last field goes
to the first, and REQ_PREV_FIELD from the first field goes to the last. The order of the fields for these (and
the REQ_FIRST_FIELD and REQ_LAST_FIELD requests) is simply the order of the field pointers in the
form array (as set up by new_form() or set_form_fields()
It is also possible to traverse the fields as if they had been sorted in screen−position order, so the sequence
goes left−to−right and top−to−bottom. To do this, use the second group of four sorted−movement requests.
Finally, it is possible to move between fields using visual directions up, down, right, and left. To accomplish
this, use the third group of four requests. Note, however, that the position of a form for purposes of these
requests is its upper−left corner.
For example, suppose you have a multi−line field B, and two single−line fields A and C on the same line with
B, with A to the left of B and C to the right of B. A REQ_MOVE_RIGHT from A will go to B only if A, B,
and C all share the same first line; otherwise it will skip over B to C.
Each word is separated from the previous and next characters by whitespace. The commands to move to
beginning and end of line or field look for the first or last non−pad character in their ranges.
For scrolling purposes, a page of a field is the height of its visible part.
The following requests support editing the field and changing the edit mode:
The behavior of the REQ_NEW_LINE and REQ_DEL_PREV requests is complicated and partly controlled
by a pair of forms options. The special cases are triggered when the cursor is at the beginning of a field, or on
the last line of the field.
The normal behavior of REQ_NEW_LINE in insert mode is to break the current line at the position of the
edit cursor, inserting the portion of the current line after the cursor as a new line following the current and
moving the cursor to the beginning of that new line (you may think of this as inserting a newline in the field
buffer).
The normal behavior of REQ_NEW_LINE in overlay mode is to clear the current line from the position of
the edit cursor to end of line. The cursor is then moved to the beginning of the next line.
However, REQ_NEW_LINE at the beginning of a field, or on the last line of a field, instead does a
REQ_NEXT_FIELD. O_NL_OVERLOAD option is off, this special action is disabled.
The normal behavior of REQ_DEL_PREV is to delete the previous character. If insert mode is on, and the
cursor is at the start of a line, and the text on that line will fit on the previous one, it instead appends the
contents of the current line to the previous one and deletes the current line (you may think of this as deleting a
newline from the field buffer).
If the O_BS_OVERLOAD option is off, this special action is disabled and the forms driver just returns
E_REQUEST_DENIED.
• REQ_NEXT_CHOICE Place the successor value of the current value in the buffer.
• REQ_PREV_CHOICE Place the predecessor value of the current value in the buffer.
Of the built−in field types, only TYPE_ENUM has built−in successor and predecessor functions. When you
define a field type of your own (see Custom Validation Types), you can associate our own ordering functions.
CDK stands for 'Curses Development Kit' and it currently contains 21 ready to use widgets which facilitate
the speedy development of full screen curses programs.
The kit provides some useful widgets, which can be used in your programs directly. It's pretty well written
and the documentation is very good. The examples in the examples directory can be a good place to start for
beginners. The CDK can be downloaded from http://www.vexus.ca/release/cdk.tar.gz . Follow the
instructions in README file to install it.
If the string
given as a parameter to newCDKLabel(), it prints the line with yellow foreground and blue background.
There are other tags available for justifying string, embedding special drawing characters etc.. Please refer to
the man page cdk_display(3X) for details. The man page explains the usage with nice examples.
19.1.3. Conclusion
All in all, CDK is a well−written package of widgets, which if used properly can form a strong frame work
for developing complex GUI.
Linux is based on the Unix operating system, but also features a number of unique and useful kernel features
and application programs that often go beyond what is available under Unix. One little−known gem is
"dialog", a utility for creating professional−looking dialog boxes from within shell scripts. This article
presents a tutorial introduction to the dialog utility, and shows examples of how and where it can be used
As he explains, dialog is a real gem in making professional−looking dialog boxes with ease. It creates a
variety of dialog boxes, menus, check lists etc.. It is usually installed by default. If not, you can find it at
The above−mentioned article gives a very good overview of its uses and capabilites. The man page has more
details. It can be used in variety of situations. One good example is building of linux kernel in text mode.
Linux kernel uses a modified version of dialog tailored for its needs.
dialog was initially designed to be used with shell scripts. If you want to use its functionality in a c program,
then you can use libdialog. The documentation regarding this is sparse. Definitive reference is the dialog.h
header file which comes with the library. You may need to hack here and there to get the required output. The
source is easily customizable. I have used it on a number of occasions by modifying the code.
For more information see man pages Curses(3) , Curses::Form(3) and Curses::Widgets(3). These pages are
installed only when the above modules are acquired and installed.
The Game of Life (or simply Life) is not a game in the conventional sense. There
are no players, and no winning or losing. Once the "pieces" are placed in the
starting position, the rules determine everything that happens later.
Nevertheless, Life is full of surprises! In most cases, it is impossible to look
at a starting position (or pattern) and see what will happen in the future. The
only way to find out is to follow the rules of the game.
This program starts with a simple inverted U pattern and shows how wonderful life works. There is a lot of
room for improvement in the program. You can let the user enter pattern of his choice or even take input from
a file. You can also change rules and play with a lot of variations. Search on google for interesting
information on game of life.
20.5. Shuffle
A fun game, if you have time to kill.