Ncurses Programming Howto
Ncurses Programming Howto
Pradeep Padala
<[email protected]> v1.9, 20050620 Revision History Revision 1.9 20050620 Revised by: ppadala The license has been changed to the MITstyle license used by NCURSES. Note that the programs are also relicensed under this. Revision 1.8 20050617 Revised by: ppadala Lots of updates. Added references and perl examples. Changes to examples. Many grammatical and stylistic changes to the content. Changes to NCURSES history. Revision 1.7.1 20020625 Revised by: ppadala Added a README file for building and instructions for building from source. Revision 1.7 20020625 Revised by: ppadala Added "Other formats" section and made a lot of fancy changes to the programs. Inlining of programs is gone. Revision 1.6.1 20020224 Revised by: ppadala Removed the old Changelog section, cleaned the makefiles Revision 1.6 20020216 Revised by: ppadala Corrected a lot of spelling mistakes, added ACS variables section Revision 1.5 20020105 Revised by: ppadala Changed structure to present proper TOC Revision 1.3.1 20010726 Revised by: ppadala Corrected maintainers paragraph, Corrected stable release number Revision 1.3 20010724 Revised by: ppadala Added copyright notices to main document (LDP license) and programs (GPL), Corrected printw_example. Revision 1.2 20010605 Revised by: ppadala Incorporated ravi's changes. Mainly to introduction, menu, form, justforfun sections Revision 1.1 20010522 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. Send comments to this address
Table of Contents
1. Introduction.....................................................................................................................................................1 1.1. What is NCURSES?.........................................................................................................................1 1.2. What we can do with NCURSES ......................................................................................................1 1.3. Where to get it..................................................................................................................................2 . 1.4. Purpose/Scope of the document........................................................................................................2 1.5. About the Programs..........................................................................................................................2 1.6. Other Formats of the document........................................................................................................4 1.6.1. Readily available formats from tldp.org ..................................................................................4 1.6.2. Building from source...............................................................................................................4 1.7. Credits...............................................................................................................................................5 1.8. Wish List...........................................................................................................................................5 1.9. Copyright..........................................................................................................................................5 2. Hello World !!!................................................................................................................................................7 2.1. Compiling With the NCURSES Library..........................................................................................7 2.2. Dissection ..........................................................................................................................................7 2.2.1. About initscr().........................................................................................................................7 2.2.2. The mysterious refresh().........................................................................................................8 2.2.3. About endwin().......................................................................................................................8 . 3. The Gory Details.............................................................................................................................................9 4. Initialization ...................................................................................................................................................10 4.1. Initialization functions....................................................................................................................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 5. A Word about Windows...............................................................................................................................12 6. Output functions...........................................................................................................................................13 6.1. addch() class of functions...............................................................................................................13 6.2. mvaddch(), waddch() and mvwaddch()..........................................................................................13 6.3. printw() class of functions..............................................................................................................14 6.3.1. printw() and mvprintw ...........................................................................................................14 6.3.2. wprintw() and mvwprintw.....................................................................................................14 6.3.3. vwprintw().............................................................................................................................14 6.3.4. A Simple printw example......................................................................................................14 6.4. addstr() class of functions...............................................................................................................15 6.5. A word of caution...........................................................................................................................15 7. Input functions..............................................................................................................................................16 7.1. getch() class of functions................................................................................................................16 7.2. scanw() class of functions...............................................................................................................16 7.2.1. scanw() and mvscanw...........................................................................................................16 i
Table of Contents
7. Input functions 7.2.2. wscanw() and mvwscanw()...................................................................................................16 7.2.3. vwscanw().............................................................................................................................16 7.3. getstr() class of functions................................................................................................................16 7.4. Some examples...............................................................................................................................17 8. Attributes.......................................................................................................................................................18 8.1. The details.......................................................................................................................................19 8.2. attron() vs attrset()..........................................................................................................................19 . 8.3. attr_get() ..........................................................................................................................................20 8.4. attr_ functions.................................................................................................................................20 8.5. wattr functions................................................................................................................................20 8.6. chgat() functions.............................................................................................................................20 9. Windows .........................................................................................................................................................22 9.1. The basics ........................................................................................................................................22 9.2. Let there be a Window !!!...............................................................................................................22 9.3. Explanation.....................................................................................................................................24 9.4. The other stuff in the example........................................................................................................24 9.5. Other Border functions...................................................................................................................24 10. Colors...........................................................................................................................................................27 10.1. The basics ......................................................................................................................................27 10.2. Changing Color Definitions..........................................................................................................28 10.3. Color Content................................................................................................................................28 11. Interfacing with the key board..................................................................................................................29 11.1. The Basics.....................................................................................................................................29 11.2. A Simple Key Usage example......................................................................................................29 12. Interfacing with the mouse.........................................................................................................................32 12.1. The Basics.....................................................................................................................................32 12.2. Getting the events.........................................................................................................................32 12.3. Putting it all Together...................................................................................................................33 12.4. Miscellaneous Functions ...............................................................................................................35 13. Screen Manipulation ...................................................................................................................................36 13.1. getyx() functions...........................................................................................................................36 13.2. Screen Dumping ............................................................................................................................36 13.3. Window Dumping .........................................................................................................................36 14. Miscellaneous features................................................................................................................................37 14.1. curs_set().......................................................................................................................................37 14.2. Temporarily Leaving Curses mode ...............................................................................................37 14.3. ACS_ variables.............................................................................................................................37
ii
Table of Contents
15. Other libraries.............................................................................................................................................39 16. Panel Library..............................................................................................................................................40 16.1. The Basics.....................................................................................................................................40 16.2. Compiling With the Panels Library..............................................................................................40 16.3. Panel Window Browsing..............................................................................................................41 16.4. Using User Pointers......................................................................................................................43 16.5. Moving and Resizing Panels .........................................................................................................44 16.6. Hiding and Showing Panels..........................................................................................................48 16.7. panel_above() and panel_below() Functions................................................................................51 17. Menus Library............................................................................................................................................52 17.1. The Basics.....................................................................................................................................52 17.2. Compiling With the Menu Library...............................................................................................52 17.3. Menu Driver: The work horse of the menu system......................................................................54 17.4. Menu Windows.............................................................................................................................55 17.5. Scrolling Menus............................................................................................................................57 17.6. Multi Columnar Menus.................................................................................................................60 17.7. Multi Valued Menus.....................................................................................................................62 17.8. Menu Options...............................................................................................................................64 17.9. The useful User Pointer................................................................................................................65 18. Forms Library.............................................................................................................................................68 18.1. The Basics.....................................................................................................................................68 18.2. Compiling With the Forms Library..............................................................................................68 18.3. Playing with Fields.......................................................................................................................70 18.3.1. Fetching Size and Location of Field ....................................................................................70 18.3.2. Moving the field..................................................................................................................70 18.3.3. Field Justification................................................................................................................70 18.3.4. Field Display Attributes......................................................................................................71 18.3.5. Field Option Bits.................................................................................................................73 18.3.6. Field Status..........................................................................................................................75 18.3.7. Field User Pointer ................................................................................................................76 18.3.8. VariableSized Fields.........................................................................................................76 18.4. Form Windows.............................................................................................................................77 18.5. Field Validation............................................................................................................................79 TYPE_ALPHA..................................................................................................................................................81 TYPE_ALNUM.................................................................................................................................................82 TYPE_ENUM....................................................................................................................................................83 TYPE_INTEGER.............................................................................................................................................84 TYPE_NUMERIC............................................................................................................................................85
iii
Table of Contents
TYPE_REGEXP...............................................................................................................................................86 18.6. Form Driver: The work horse of the forms system .......................................................................86 18.6.1. Page Navigation Requests...................................................................................................86 18.6.2. InterField Navigation Requests.........................................................................................86 18.6.3. IntraField Navigation Requests.........................................................................................87 18.6.4. Scrolling Requests...............................................................................................................88 18.6.5. Editing Requests..................................................................................................................88 18.6.6. Order Requests....................................................................................................................89 18.6.7. Application Commands.......................................................................................................89 19. Tools and Widget Libraries.......................................................................................................................90 19.1. CDK (Curses Development Kit)...................................................................................................90 19.1.1. Widget List..........................................................................................................................90 19.1.2. Some Attractive Features....................................................................................................91 19.1.3. Conclusion...........................................................................................................................91 19.2. The dialog.....................................................................................................................................91 19.3. Perl Curses Modules CURSES::FORM and CURSES::WIDGETS............................................92 20. Just For Fun !!!...........................................................................................................................................93 20.1. The Game of Life..........................................................................................................................93 20.2. Magic Square................................................................................................................................93 20.3. Towers of Hanoi...........................................................................................................................93 20.4. Queens Puzzle...............................................................................................................................93 20.5. Shuffle...........................................................................................................................................94 20.6. Typing Tutor.................................................................................................................................94 21. References....................................................................................................................................................95
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. All the capabilities (such as moving the cursor to a new location, erasing part of the screen, scrolling the screen, changing modes etc.) of terminals could be accessed through these series of bytes. These control seeuqnces 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 a terminal window. Suppose you wanted to print a line in color. Try typing this on your console.
echo "^[[0;31;40mIn Color"
The first character is an escape character, which looks like two characters ^ and [. To be able to print it, 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 these 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, this mechanism allows application programs to query the terminfo database and obtain the control characters to be sent to a terminal or terminal emulator.
NCURSES Programming HOWTO can be managed independently, can provide 'scrollability' and even can be hidden. Menus provide the user with an easy command selection option. Forms allow the creation of easytouse 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.
Using the RPM NCURSES RPM can be found and downloaded from http://rpmfind.net . The RPM can be installed with the following command after becoming root.
rpm i <downloaded rpm>
1. Introduction
acs_vars.c hello_world.c init_func_example.c key_code.c mouse_menu.c other_border.c printw_example.c scanw_example.c simple_attr.c simple_color.c simple_key.c temp_leave.c win_border.c with_chgat.c
ACS_ variables example Simple "Hello World" Program Initialization functions example Shows the scan code of the key pressed A menu accessible by mouse Shows usage of other border functions apa rt from box() A very simple printw() example A very simple getstr() example A program that can print a c file with comments in attribute A simple example demonstrating colors A menu accessible with keyboard UP, DOWN arrows Demonstrates temporarily leaving curses mode Shows Creation of windows and borders chgat() usage example
Usage of field attributes Usage of field options A simple form example Demo of windows associated with forms
Usage of menu attributes Usage of item_name() etc.. functions Creates multi columnar menus Demonstrates scrolling capability of menus A simple menu accessed by arrow keys Creates multi valued menus and explains
1. Introduction
Panel browsing through tab. Usage of user pointer Hiding and Un hiding of panels Moving and resizing of panels A simple panel example
There is a top level Makefile included in the main directory. It builds all the files and puts the readytouse 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 included path name for the file relative to the examples directory. If you prefer browsing individual programs, point your browser to http://tldp.org/HOWTO/NCURSESProgrammingHOWTO/ncurses_programs/ All the programs are released under the same license that is used by ncurses (MITstyle). This gives you the ability to do pretty much anything other than claiming them as yours. Feel free to use them in your programs as appropriate.
1. Introduction
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. Perl equivalents of the example programs are contributed by Anuradha Ratnaweera. 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.
1.9. Copyright
Copyright 2001 by Pradeep Padala. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, distribute with modifications, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 1. Introduction 5
NCURSES Programming HOWTO THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name(s) of the above copyright holders shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization.
1. Introduction
/* /* /* /* /*
Start curses mode */ Print Hello World */ Print it on to the real screen */ Wait for user input */ End curses mode */
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.
4. Initialization
We now know that to initialize curses system the function initscr() has to be called. There are functions which can be called after this initialization to customize our curses session. We may ask the curses system to set the terminal in raw mode or initialize color or initialize the mouse etc.. Let's discuss some of the functions that are normally called immediately after initscr();
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 halfdelay 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 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. Initialization
10
4.7. An Example
Let's write a program which will clarify the usage of these functions.
printw("Type any character to see it in bold\n"); ch = getch(); /* If raw() hadn't been called * we have to press enter before it * gets to the program */ if(ch == KEY_F(1)) /* Without keypad enabled this will */ printw("F1 Key pressed");/* not get to us either */ /* Without noecho() some ugly escape * charachters might have been printed * on screen */ else { printw("The pressed key is "); attron(A_BOLD); printw("%c", ch); attroff(A_BOLD); } refresh(); /* Print it on to the real screen */ getch(); /* Wait for user input */ endwin(); /* End curses mode */ return 0; }
This program is selfexplanatory. 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.
4. Initialization
11
It prints the string on stdscr at the present cursor position. Similarly the call to refresh(), works on stdscr only. Say you have created windows then you have to call a function with a 'w' added to the usual function.
wprintw(win, "Hi There !!!"); wrefresh(win);
As you will see in the rest of the document, naming of functions follow the same convention. For each function there usually are three more functions.
printw(string); /* Print on stdscr at present cursor position */ mvprintw(y, x, string);/* Move to (y, x) then print string */ wprintw(win, string); /* Print on window win at present cursor position */ /* in the window */ mvwprintw(win, y, x, string); /* Move to (y, x) relative to window */ /* coordinates and then print */
Usually the wless functions are macros which expand to corresponding wfunction with stdscr as the window parameter.
12
6. Output functions
I guess you can't wait any more to see some action. Back to our odyssey of curses functions. Now that curses is initialized, let's interact with world. There are three classes of functions which you can use to do output on screen. 1. addch() class: Print single character with attributes 2. printw() class: Print formatted output similar to printf() 3. addstr() class: Print strings 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.
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 characterbased 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.
can be replaced by
mvaddch(row,col,ch);
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.
6. Output functions
13
NCURSES Programming HOWTO 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 printflike or putslike functions.
6.3.3. vwprintw()
This function is similar to vprintf(). This can be used when variable number of arguments are to be printed.
/* message to be appeared on the screen */ /* 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,(colstrlen(mesg))/2,"%s",mesg); /* print the message at the center of the screen */ mvprintw(row2,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. 6. Output functions 14
NCURSES Programming HOWTO 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.
6. Output functions
15
7. Input functions
Well, printing without taking input, is boring. Let's see functions which allow us to get input from user. These functions also can be divided into three categories. 1. getch() class: Get a character 2. scanw() class: Get formatted input 3. getstr() class: Get strings
7.2.3. vwscanw()
This function is similar to vscanf(). This can be used when a variable number of arguments are to be scanned.
/* 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,(colstrlen(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; }
7. Input functions
17
8. Attributes
We have seen an example of how attributes can be used to print characters with some special effects. Attributes, when set prudently, can present information in an easy, understandable manner. The following program takes a C file as input and prints the file with comments in bold. Scan through the code.
8. Attributes
18
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 coordinates 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 coordinates 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 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);
NCURSES Programming HOWTO menus with colors and highlighting. So decide on a consistent policy and stick to it. You can always use standend() which is equivalent to attrset(A_NORMAL) which turns off all attributes and brings you to normal mode.
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.
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 "wless" functions are macros.
*/ */
8. Attributes
20
This example also introduces us to the color world of curses. Colors will be explained in detail later. Use 0 for no color.
8. Attributes
21
9. Windows
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 easytomanage design in your programs. If you are writing big, complex GUIs this is of pivotal importance before you start doing anything.
WINDOW *create_newwin(int height, int width, int starty, int startx); void destroy_win(WINDOW *local_win); int main(int argc, char *argv[]) { WINDOW *my_win; int startx, starty, width, height; int ch; initscr(); cbreak(); keypad(stdscr, TRUE); /* /* * /* Start curses mode */ Line buffering disabled, Pass on everty thing to me */ I need that nifty F1 */
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);
9. Windows
22
width, starty,startx);
width, starty,++startx);
width, starty,startx);
width, ++starty,startx);
*/
9. Windows
23
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:
wborder(win, '|', '|', '', '', '+', '+', '+', '+');
9. Windows
24
keypad(stdscr, TRUE); noecho(); init_pair(1, COLOR_CYAN, COLOR_BLACK); /* Initialize the window parameters */ init_win_params(&win); print_win_params(&win); 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; } } endwin(); /* End curses mode return 0;
*/
9. Windows
25
} 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, bool flag) { int i, j; int x, y, w, h; x y w h = = = = p_win>startx; p_win>starty; p_win>width; p_win>height;
if(flag == 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. Windows
26
10. Colors
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.
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 using various functions. To find out whether a 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. 10. Colors 27
NCURSES Programming HOWTO 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 COLOR_RED COLOR_GREEN COLOR_YELLOW COLOR_BLUE COLOR_MAGENTA COLOR_CYAN COLOR_WHITE 0 1 2 3 4 5 6 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).
10. Colors
28
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.
29
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 pr 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; }
30
31
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. The following are all the event masks:
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
32
getmouse() returns the event into the pointer given to it. It's a structure which contains
typedef struct { short id; int x, y, z; mmask_t bstate; }
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.
if(event.bstate & BUTTON1_PRESSED) printw("Left Button Pressed");
}; int n_choices = sizeof(choices) / sizeof(char *); void print_menu(WINDOW *menu_win, int highlight); void report_choice(int mouse_x, int mouse_y, int *p_choice); int main() { int c, choice = 0; WINDOW *menu_win; MEVENT event; /* Initialize curses */ initscr(); clear(); noecho();
33
/* Try to put the window in the middle of screen */ startx = (80 WIDTH) / 2; starty = (24 HEIGHT) / 2; attron(A_REVERSE); mvprintw(23, 1, "Click on Exit to quit (Works best in a virtual console)"); refresh(); attroff(A_REVERSE); /* Print the menu for the first time */ menu_win = newwin(HEIGHT, WIDTH, starty, startx); print_menu(menu_win, 1); /* Get all the mouse events */ mousemask(ALL_MOUSE_EVENTS, NULL);
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; }
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) { 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); } /* Report the choice according to mouse position */
34
for(choice = 0; choice < n_choices; ++choice) if(mouse_y == j + choice && mouse_x >= i && mouse_x <= i + strlen(choices[choice] { if(choice == n_choices 1) *p_choice = 1; else *p_choice = choice + 1; break; } }
35
The function getparyx() gets the beginning coordinates 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 coordinates etc. A simple solution to this problem, is to create menus in sub windows and later find the starting coordinates of the menus by using getparyx(). The functions getbegyx() and getmaxyx() store current window's beginning and maximum coordinates. These functions are useful in the same way as above in managing the windows and sub windows effectively.
36
14.1. curs_set()
This function can be used to make the cursor invisible. The parameter to this function should be
0 : invisible 1 : normal or 2 : very visible. or
/* /* /* /* /* /* /* /* /* /* /* /* /*
Start curses mode */ Print Hello World */ Print it on to the real screen */ Save the tty modes */ End curses mode temporarily */ Do whatever you like in cooked mode */ Return to the previous tty mode*/ stored by def_prog_mode() */ Do refresh() to restore the */ Screen contents */ Back to curses use the full */ capabilities of curses */ End curses mode */
38
39
40
/* * 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); /* Attach a panel to each window */ my_panels[0] = new_panel(my_wins[0]); my_panels[1] = new_panel(my_wins[1]); my_panels[2] = new_panel(my_wins[2]); /* /* /* /* Order is bottom up */ Push 0, order: stdscr0 */ Push 1, order: stdscr01 */ Push 2, order: stdscr012 */
/* Update the stacking order. 2nd panel will be on top */ update_panels(); /* Show it on the screen */ doupdate(); 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.
41
void init_wins(WINDOW **wins, int n); void win_show(WINDOW *win, char *label, int label_color); void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color); int main() { WINDOW *my_wins[3]; PANEL *my_panels[3]; PANEL *top; int ch; /* Initialize curses */ initscr(); start_color(); cbreak(); noecho(); keypad(stdscr, TRUE); /* Initialize all the colors */ init_pair(1, COLOR_RED, COLOR_BLACK); init_pair(2, COLOR_GREEN, COLOR_BLACK); init_pair(3, COLOR_BLUE, COLOR_BLACK); init_pair(4, COLOR_CYAN, COLOR_BLACK); init_wins(my_wins, 3); /* Attach a panel to each window */ my_panels[0] = new_panel(my_wins[0]); my_panels[1] = new_panel(my_wins[1]); my_panels[2] = new_panel(my_wins[2]); /* /* /* /* Order is bottom up */ Push 0, order: stdscr0 */ Push 1, order: stdscr01 */ Push 2, order: stdscr012 */
/* Set up the user pointers to the next panel */ set_panel_userptr(my_panels[0], my_panels[1]); set_panel_userptr(my_panels[1], my_panels[2]); set_panel_userptr(my_panels[2], my_panels[0]); /* Update the stacking order. 2nd panel will be on top */ update_panels(); /* Show it on the screen */ attron(COLOR_PAIR(4)); mvprintw(LINES 2, 0, "Use tab to browse through the windows (F1 to Exit)"); attroff(COLOR_PAIR(4)); doupdate(); 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; }
42
NCURSES Programming HOWTO set_panel_userptr(). It can be accessed using the function panel_userptr() which will return the user pointer for the panel given as argument. After finding the next panel in the cycle It's brought to the top by the function top_panel(). This function brings the panel given as argument to the top of the panel stack.
init_wins(WINDOW **wins, int n); win_show(WINDOW *win, char *label, int label_color); print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color); set_user_ptrs(PANEL **panels, int n);
int main() { WINDOW *my_wins[3]; PANEL *my_panels[3]; PANEL_DATA *top; PANEL *stack_top; WINDOW *temp_win, *old_win; int ch; int newx, newy, neww, newh; int size = FALSE, move = FALSE; /* Initialize curses */ initscr(); start_color(); cbreak(); noecho(); keypad(stdscr, TRUE);
44
set_user_ptrs(my_panels, 3); /* Update the stacking order. 2nd panel will be on top */ update_panels(); /* Show it on the screen */ 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)); doupdate();
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': /* ReSize*/ 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; }
45
case
case
case
case
} 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; } /* Put all the windows */ void init_wins(WINDOW **wins, int n) { int x, y, i; char label[80];
46
47
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.
void init_wins(WINDOW **wins, int n); void win_show(WINDOW *win, char *label, int label_color); void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color);
48
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]); /* Update the stacking order. 2nd panel will be on top */ update_panels(); /* Show it on the screen */ attron(COLOR_PAIR(4)); mvprintw(LINES 3, 0, "Show or Hide a window with 'a'(first window) mvprintw(LINES 2, 0, "F1 to Exit"); attroff(COLOR_PAIR(4)); doupdate(); while((ch = getch()) != KEY_F(1)) { switch(ch) { 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':
'b'(Second Window)
49
50
51
52
initscr(); cbreak(); noecho(); keypad(stdscr, TRUE); 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; my_menu = new_menu((ITEM **)my_items); mvprintw(LINES 2, 0, "F1 to Exit"); post_menu(my_menu); refresh(); while((c = getch()) != KEY_F(1)) { switch(c) { case KEY_DOWN: menu_driver(my_menu, REQ_DOWN_ITEM); break; case KEY_UP: menu_driver(my_menu, REQ_UP_ITEM); break; } } 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 KEY_MOUSE special key associated with a mouse event. The menu_driver accepts following navigational requests. 17. Menus Library 53
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.
54
NCURSES Programming HOWTO 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,
If the second argument is the KEY_MOUSE special key, the associated mouse event is translated into one of the above predefined requests. Currently only clicks in the user window (e.g. inside the menu display area or the decoration window) are handled. If you click above the display region of the menu, a REQ_SCR_ULINE is generated, if you doubleclick a REQ_SCR_UPAGE is generated and if you tripleclick a REQ_FIRST_ITEM is generated. If you click below the display region of the menu, a REQ_SCR_DLINE is generated, if you doubleclick a REQ_SCR_DPAGE is generated and if you tripleclick a REQ_LAST_ITEM is generated. If you click at an item inside the display area of the menu, the menu cursor is positioned to that item.
Each of the above requests will be explained in the following lines with several examples whenever appropriate.
55
}; 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); /* Create the window to be associated with the menu */ my_menu_win = newwin(10, 40, 4, 4); keypad(my_menu_win, TRUE); /* Set main window and sub window */ set_menu_win(my_menu, my_menu_win); set_menu_sub(my_menu, derwin(my_menu_win, 6, 38, 3, 1)); /* Set menu mark to the string " * " */ set_menu_mark(my_menu, " * "); /* Print a border around the main window and print a title */ box(my_menu_win, 0, 0); print_in_middle(my_menu_win, 1, 0, 40, "My Menu", COLOR_PAIR(1)); mvwaddch(my_menu_win, 2, 0, ACS_LTEE); mvwhline(my_menu_win, 2, 1, ACS_HLINE, 38); mvwaddch(my_menu_win, 2, 39, ACS_RTEE); mvprintw(LINES 2, 0, "F1 to exit"); refresh(); /* Post the menu */ post_menu(my_menu); wrefresh(my_menu_win); while((c = wgetch(my_menu_win)) != KEY_F(1)) { switch(c) { case KEY_DOWN: menu_driver(my_menu, REQ_DOWN_ITEM); break; case KEY_UP: menu_driver(my_menu, REQ_UP_ITEM); break; } wrefresh(my_menu_win); }
56
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().
57
}; 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); /* Create the window to be associated with the menu */ my_menu_win = newwin(10, 40, 4, 4); keypad(my_menu_win, TRUE); /* Set main window and sub window */ set_menu_win(my_menu, my_menu_win); set_menu_sub(my_menu, derwin(my_menu_win, 6, 38, 3, 1)); set_menu_format(my_menu, 5, 1); /* Set menu mark to the string " * " */ set_menu_mark(my_menu, " * "); /* Print a border around the main window and print a title */ box(my_menu_win, 0, 0); print_in_middle(my_menu_win, 1, 0, 40, "My Menu", COLOR_PAIR(1)); mvwaddch(my_menu_win, 2, 0, ACS_LTEE); mvwhline(my_menu_win, 2, 1, ACS_HLINE, 38); mvwaddch(my_menu_win, 2, 39, ACS_RTEE); /* Post the menu */ post_menu(my_menu); wrefresh(my_menu_win); 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));
58
REQ_DOWN_ITEM);
REQ_UP_ITEM);
REQ_SCR_DPAGE);
REQ_SCR_UPAGE);
This program is selfexplanatory. 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().
59
60
REQ_DOWN_ITEM);
REQ_UP_ITEM);
REQ_LEFT_ITEM);
REQ_RIGHT_ITEM);
REQ_SCR_DPAGE);
REQ_SCR_UPAGE);
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 posted. O_ROWMAJOR
the
item
descriptions
when
the
menu
is
61
name
while
pat-
O_NONCYCLIC Don't wrap around nextitem and previousitem, 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.
62
Whew, A lot of new functions. Let's take them one after another. Firstly, the REQ_TOGGLE_ITEM. In a multivalued 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 17. Menus Library 63
NCURSES Programming HOWTO 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().
64
/* Post the menu */ mvprintw(LINES 3, 0, "Press <ENTER> to see the option selected"); mvprintw(LINES 2, 0, "Up and Down arrow keys to naviage (F1 to Exit)"); post_menu(my_menu); refresh(); while((c = getch()) != KEY_F(1)) { switch(c) { case KEY_DOWN: menu_driver(my_menu, REQ_DOWN_ITEM); break; case KEY_UP: menu_driver(my_menu, REQ_UP_ITEM); break; case 10: /* Enter */ move(20, 0); clrtoeol(); mvprintw(20, 0, "Item selected is : %s", item_name(current_item(my_menu))); pos_menu_cursor(my_menu); break; } } unpost_menu(my_menu); for(i = 0; i < n_choices; ++i) free_item(my_items[i]); free_menu(my_menu); endwin(); }
65
66
67
68
/* Print a line for the option */ /* Don't go to next field when this */ /* Field is filled up */
69
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.
70
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.
71
72
NCURSES Programming HOWTO 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 readonly fields may be useful for help messages. O_WRAP Controls wordwrapping in multiline fields. Normally, when any character of a (blankseparated) word reaches the end of the current line, the entire word is wrapped to the next line (assuming there is one). When this option is off, the word will be split across the line break. 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 justentered 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 18. Forms Library 73
NCURSES Programming HOWTO 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 bitmasks and can be composed with logicalor 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.
*/
field_opts_off(field[0], O_ACTIVE); /* This field is a static label */ field_opts_off(field[1], O_PUBLIC); /* This filed is like a password field*/ field_opts_off(field[1], O_AUTOSKIP); /* To avoid entering the same field */ /* after last character is entered */ /* Create the form and post it */ my_form = new_form(field); post_form(my_form); refresh();
74
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
75
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
int set_max_field(FIELD *field, /* Field on which to operate */ int max_growth); /* maximum growth allowed for the field */
Though field_info work as usual, it is advisable to use this function to get the proper attributes of a dynamically growable field. 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 18. Forms Library 76
NCURSES Programming HOWTO 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 O_NL_OVERLOAD form option is on, REQ_NEW_LINE will only implicitly generate REQ_NEXT_FIELD if the field has grown to its maximum size and the user is on the last line. 4. The library call dup_field will work as usual; it will duplicate the field, including the current buffer size and contents of the field being duplicated. Any specified maximum growth will also be duplicated. 5. The library call link_field will work as usual; it will duplicate all field attributes and share buffers with the field being linked. If the O_STATIC field option is subsequently changed by a field sharing buffers, how the system reacts to an attempt to enter more data into the field than the buffer will currently hold will depend on the setting of the option in the current field. 6. The library call field_info will work as usual; the variable nrow will contain the value of the original call to new_field. The user should use dynamic_field_info, described above, to query the current size of the buffer. Some of the above points make sense only after explaining form driver. We will be looking into that in next few sections.
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;
77
78
Once set, the validation type for a field can be queried with The form driver validates the data in a field only when data is entered by the enduser. Validation does not occur when
79
NCURSES Programming HOWTO the application program changes the field value by calling set_field_buffer. linked field values are changed indirectly by changing the field to which they are linked The following are the predefined validation types. You can also specify custom validation, though it's a bit tricky and cumbersome.
80
TYPE_ALPHA
This field type accepts alphabetic data; no blanks, no digits, no special characters (this is checked at characterentry time). It is set up with:
int set_field_type(FIELD *field, TYPE_ALPHA, int width); /* field to alter */ /* type to associate */ /* maximum width of field */
The width argument sets a minimum width of data. The user has to enter atleast 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
81
TYPE_ALNUM
This field type accepts alphabetic data and digits; no blanks, no special characters (this is checked at characterentry time). It is set up with:
int set_field_type(FIELD *field, TYPE_ALNUM, int width); /* field to alter */ /* type to associate */ /* maximum width of field */
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
82
TYPE_ENUM
This type allows you to restrict a field's values to be among a specified set of string values (for example, the twoletter postal codes for U.S. states). It is set up with:
int set_field_type(FIELD *field, TYPE_ENUM, char **valuelist; int checkcase; int checkunique); /* /* /* /* /* field to alter */ type to associate */ list of possible values */ casesensitive? */ must specify uniquely? */
The valuelist parameter must point at a NULLterminated list of valid strings. The checkcase argument, if true, makes comparison with the string casesensitive. 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
83
TYPE_INTEGER
This field type accepts an integer. It is set up as follows:
int set_field_type(FIELD *field, TYPE_INTEGER, int padding, int vmin, int vmax); /* /* /* /* field to alter */ type to associate */ # places to zeropad to */ valid range */
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
84
TYPE_NUMERIC
This field type accepts a decimal number. It is set up as follows:
int set_field_type(FIELD *field, TYPE_NUMERIC, int padding, int vmin, int vmax); /* /* /* /* field to alter */ type to associate */ # places of precision */ valid range */
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
85
TYPE_REGEXP
This field type accepts data matching a regular expression. It is set up as follows:
int set_field_type(FIELD *field, TYPE_REGEXP, char *regexp); /* field to alter */ /* type to associate */ /* expression to match */
The syntax for regular expressions is that of regcomp(3). The check for regularexpression 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:
The following requests allow you to move to different pages REQ_NEXT_PAGE Move to the next form page. REQ_PREV_PAGE Move to the previous form page. REQ_FIRST_PAGE Move to the first form page. REQ_LAST_PAGE Move to the last form page. 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.
NCURSES Programming HOWTO REQ_SNEXT_FIELD Move to sorted next field. REQ_SPREV_FIELD Move to sorted previous field. REQ_SFIRST_FIELD Move to the sorted first field. REQ_SLAST_FIELD Move to the sorted last field. REQ_LEFT_FIELD Move left to field. REQ_RIGHT_FIELD Move right to field. REQ_UP_FIELD Move up to field. REQ_DOWN_FIELD Move down to field. 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 screenposition order, so the sequence goes lefttoright and toptobottom. To do this, use the second group of four sortedmovement 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 upperleft corner. For example, suppose you have a multiline field B, and two singleline 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.
TYPE_REGEXP
87
TYPE_REGEXP
88
NCURSES Programming HOWTO 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. Now, let us consider REQ_DEL_PREV: 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). However, REQ_DEL_PREV at the beginning of a field is instead treated as a REQ_PREV_FIELD. If the O_BS_OVERLOAD option is off, this special action is disabled and the forms driver just returns E_REQUEST_DENIED.
TYPE_REGEXP
89
90
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 wellwritten package of widgets, which if used properly can form a strong frame work for developing complex GUI.
NCURSES Programming HOWTO 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.
92
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. File Path: JustForFun/life.c
93
20.5. Shuffle
A fun game, if you have time to kill. File Path: JustForFun/shuffle.c
94
21. References
NCURSES man pages NCURSES FAQ at http://invisibleisland.net/ncurses/ncurses.faq.html Writing programs with NCURSES by Eric Raymond and Zeyd M. BenHalim at http://invisibleisland.net/ncurses/ncursesintro.html somewhat obsolete. I was inspired by this document and the structure of this HOWTO follows from the original document
21. References
95