Guide To Ncurses Programming
Guide To Ncurses Programming
Programming
Dan Gookin’s Guide to Ncurses Programming
Written by Dan Gookin
Published by Quantum Particle Bottling Co.,
Coeur d’Alene, ID, 83814
USA
Copyright ©2017 by Quantum Particle Bottling Co., Inc. All Rights
Reserved.
For additional information on this or other publications from Dan Gookin
and Quantum Particle Bottling Co., please visit http://www.wambooli.com/
Edition 1.0
September 2017
Table of Contents
Introduction
Assumptions
Curses or Ncurses?
Conventions
Mise en place
Contacting the Author
1. The Set Up
Ncurses is a Unix Thing
The Terminal
Know the Shell
The Ncurses Development Cycle
2. Basic Input/Output
The Ncurses Skeleton
Text Output
Text Input
3. Formatting Text
Text Abuse with Text Attributes
Can It Do Color?
Color a Window
Noise, too!
4. More Than a Character
Attributes and chtype Characters
The Alternative Character Set
The chtype Array
Unicode Output
5. Cursor Here or There
The Screen’s Size
Move the Cursor
Where is the Cursor?
6. Text Manipulation
Insert Text Functions
Delete Text Functions
A Dual Insert and Delete Function
7. Clear and Zap
Erase the Window
Clrto means "Clear to"
8. Keyboard Input
Single Character Input
Read a String
9. Ncurses Windows
Windows Prefix Functions
New Windows
Window Input Functions
Remove a Window
Window Borders
10. Subwindows
The Thing with Subwindows
Create a Subwindow
Sub-Subwindows
Removing a Subwindow
Subwindow Roundup
11. Window Tricks: Copying and Moving
Copy Window Contents
The Moving Experience
12. Window Tricks: Wrapping and Scrolling
Text at the End of a Line
Scroll Around
13. Pads
The Pad Window Data Structure
Subpads
Pad Miscellany
14. Mouse Input
Hello, Mouse
Read the Mouse
15. Miscellany
Hide the Cursor
Line Drawing
Save the Screen
Also by Dan Gookin
Introduction
Nurses is a C (and Python) library that provides direct control over a
terminal window. You use the library to control the cursor, place text
in specific spots on the screen, and interactively read the keyboard.
This book presents an up-and-running tutorial that covers the basics
of the Ncurses library, including input/output, text attributes, special
characters, windows, pads, and mouse input.
Assumptions
Ncurses runs in a terminal environment, text mode. This
environment can be found on most operating systems, specifically
Linux, Mac OS X, and other POSIX systems. Windows features a
terminal environment, though to make Ncurses happen you need a
POSIX shell like Cygwin.
The terminal window doesn’t need to be full-screen. Any terminal
window in a GUI works.
Further, you must install the Ncurses library. Use the operating
system’s package manager to perform the setup, though the binaries
and source can be found at http://invisible-
island.net/ncurses/ncurses.html
Curses or Ncurses?
Curses has been around since the early 1980s. It was part of older
versions of Unix, many of which came with complex licensing. An
update to Curses was dubbed New Curses, or Ncurses, to avoid
various copyright and legal issues.
In this book, I use the convention Ncurses, but internally the
program is still Curses and the header and library files are named
accordingly. Aliases for ncurses -> curses exist, so you can use
either one.
The book is current with Ncurses version 6.0.
Conventions
This eBook uses the following conventions: C language keywords
and function names are shown in italic text, such as newwin() or int.
Variables and filenames are shown in blue monospaced text, such as
a and ncurses.h.
Mise en place
This book was written on an iMac running OS X 10.12.5 and
Ncurses version 6.0.20160910. Programs were tested on a Windows
7 PC running Cygwin, as well as an Ubuntu Linux system. Despite
the different environments, Ncurses behaves consistently, especially
when compiling and running the short sample code presented in this
eBook.
The Terminal
As a text-mode toolkit, Ncurses operates in a terminal window or
full-screen if you dare to start your computer in text mode. (It’s
possible.) Nearly every Unix implementation features a Terminal
program, which displays text in a window. This is the environment
in which Ncurses code runs.
Terminals come in different types, and the type of terminal used can
affect how Ncurses behaves. I recommend using the standard xterm-
color terminal to get the most from Ncurses. Type echo $TERM at the
prompt to determine which terminal type is currently set. Refer to
the terminal program window’s settings or preferences to select a
different type.
The terminal type cygwin is okay for that environment, though
reading mouse input may be problematic.
2
3 int main() 4 {
5 initscr(); /* Initialize Ncurses */
6
7 /* other programming */
8
9 endwin(); /* close Ncurses */
10 return(0); 11 }
The ncurses.h header file defines the functions and constants you
use to access the Ncurses library.
The bookend functions are initscr() and endwin(). One activates
Ncurses, the other closes the session.
Once activated, you can use Ncurses functions to control the screen.
The screen clears and your program does whatever wondrous thing it
does.
The endwin() function terminates the Ncurses session, but it does not
quit your program. You must do that yourself, such as the simple
return shown in the skeleton sample.
2
3 int main() 4 {
5 initscr(); 6 addstr(“Goodbye, cruel world!”); 7
refresh(); 8 getch();
9
10 endwin(); 11 return(0); 12 }
In Line 5 the initscr() function configures Ncurses and creates both
the standard screen and current screen. It also clears the current
terminal window. If the terminal uses the rmcup feature, then the
screen is saved and then restored after the Ncurses program exits.
(rmcup is a terminal attribute.) In Line 6, the addstr() function writes
a string to the standard screen. The stdscr variable need not be
specified, as the addstr() function (actually, a macro) outputs to the
standard screen by default.
At this point in the code, nothing appears on the screen.
The refresh() function at Line 7 updates the virtual screen, which
immediately updates the current screen. For the user, the text appears
in the terminal window .
In Line 8, the program pauses while the getch() function awaits
keyboard input. Unlike the getchar() function (standard C I/O), the
user doesn’t have to press the Enter key; getch() immediately reads
any non-shift key.
The endwin() function shuts down Ncurses in Line 10. If the
terminal supports rmcup, the original screen is restored at this point.
Line 11 returns control back to the shell.
¨ It’s very important that you use the endwin() function to finish your
Ncurses code!
If you neglect to use endwin(), the terminal’s behavior becomes
unpredictable.
Text Output
Three common Ncurses text-output functions are addch(), addstr(),
and printw(). These functions are similar to the standard C library
functions, putchar(), puts(), and printf(): addch(ch) addstr(*string)
printw(format,arg...) The addch() function places (adds) a single
character to the screen, similar to the putchar() function.
The addstr() function adds an entire string to the screen, similar to
calling addch() over and over. Unlike the puts() function, a newline
(\n) isn't appended to output .
The printw() function is the Ncurses version of printf(). It uses the
standard printf() placeholders and variables to output formatted text.
Additional text output functions, as well as variations on these three,
are available in the Ncurses library.
2
3 int main() 4 {
5 char text[] = “Greetings from Ncurses!”; 6 char
*t;
7
8 initscr(); /* initialize Ncurses */
9 t = text; /* initialize the pointer */
10
11 while(*t) /* loop through the string */
12 {
13 addch(*t); /* one char output */
14 t++; /* increment the pointer */
15 refresh(); /* update the screen */
16 napms(100); /* delay .1 sec */
17 }
18 getch(); /* wait here */
19
20 endwin(); /* clean up Ncurses */
21 return(0); 22 }
This code uses a pointer t to inch through a string of text. Along the
way, the addch() function outputs the character (Line 13).
At Line 16, the napms() function pauses output one tenth of a second
between each character displayed. Think “nap for milliseconds,”
with the millisecond value as the napms() function’s argument. So
napms(1000) pauses output for one second.
The position of the refresh() function is important in this code! It
appears at Line 15, which causes each character output to be
displayed one at a time. If you edit the code and place the refresh()
after the while loop, you see the text all at once, which isn’t the
intended outcome.
2
3 int main() 4 {
5 char t1[] = "Shall I compare thee"; 6 char t2[]
= " to a summer's day?";
7
8 initscr(); 9 addstr(t1); /* add the first string
*/
10 addstr(t2); /* add the second string */
11 refresh(); /* display the result */
12 getch(); /* wait */
13
14 endwin(); 15 return(0); 16 }
The addstr() function’s argument doesn’t appear until the next
refresh() function updates the screen.
The effect of the two addstr() functions is to display the text on a
single line: Shall I compare thee to a summer’s day?
Afterwards, the cursor rests at the end of the line. If string t2 had a
newline (\n) at the end, the cursor would be at the start of the next
line, as would also be the case with standard output.
2
3 int main() 4 {
5 char t1[] = "Shall I compare thee"; 6 char t2[]
= " to a summer's day?";
7
8 initscr(); 9 addstr(t1); /* add the first string
*/
10 addstr(t2); /* add the second string */
11 move(2,0); /* row 3, column 1 */
12 addstr("Though art more lovely..."); 13
refresh(); /* display the result */
14 getch(); /* wait */
15
16 endwin(); 17 return(0); 18 }
The move() function in Line 11 relocates the cursor to the third row,
first column. As with array elements and other C counting, the first
column and row on the screen are numbered zero; position 2,0 is the
third row, first column. The output looks like this: Shall I compare
thee to a summer's day?
After the cursor has moved, Line 12 uses the addstr() function to
place another line of text at the new position.
Here is the format of the move() function: move(y,x) y is a row
value, with zero as the top (first) row on the standard screen up to as
many rows as are available.
x is a column value, starting with zero at the far-left column and
incrementing until the far-right position for the current screen size.
¨ It's important to remember that the move() function’s arguments have
the row first, or Y, X (if you're familiar with Cartesian coordinates).
Think "row, column" as opposed to X, Y.
2
3 int main() 4 {
5 int yoda = 874; 6 int ss = 65;
7
8 initscr(); 9 printw("Yoda is %d years
old\n",yoda); 10 printw("He has collected %d
years",yoda-ss); 11 printw(" of Social
Security."); 12 refresh(); 13 getch();
14
15 endwin(); 16 return(0); 17 }
This code runs the same as you would expect had printf() been used,
though the output is full-screen.
Text Input
Ncurses doesn’t use an input stream. Characters can be checked,
fetched as they come in, linger in a queue, and so on. This type of
behavior is natural if you’ve used text mode programs, but it differs
greatly from the standard C library input functions.
Popular Ncurses console input functions include getch(), which
you’ve seen in most of the samples so far, and getstr(), getnstr(), and
scanw().
getch() getstr(*buf) getnstr(*buf,n) scanw(format,*var) The
getch() function returns a single character from the console.
The user doesn’t need to press the Enter key, the character
is fetched immediately. If no character is available,
execution waits.
The getstr() and getnstr() functions read a string of text. Of the two,
use getnstr(), which measures input and is more secure .
The scanw() function works just like the standard I/O function
scanf(). It allows for formatted input.
Ncurses input functions aren’t limited to these four. Additional
functions let you massage input and read special keys.
2
3 int main() 4 {
5 int ch;
6
7 initscr(); 8 addstr(“Type a few lines of
text\n”); 9 addstr(“Press ~ to quit\n”); 10
refresh();
11
12 while( (ch = getch()) != ‘~’) 13 ;
14
15 endwin(); 16 return(0); 17 }
The meat of the code is the while loop at Line 12. It uses the getch()
function to read input and store it in variable ch. If that character
isn’t the ~ (tilde), the loop repeats.
Variable ch can be an int or a char. The getch() function is defined as
generating integer output, though char variables work in most
circumstances.
A single-character output function isn’t required because getch()
echoes text input by default. You can disable this effect, which is
covered later in this eBook.
¨ In this code’s program, you might see that pressing the Enter key
generates a carriage return but no linefeed. This is a function of the
terminal, not of Ncurses.
2
3 int main() 4 {
5 char first[24]; 6 char last[32];
7
8 initscr(); 9 addstr("What is your first name?
"); 10 refresh(); 11 getnstr(first,23);
12
13 addstr("What is your last name? "); 14
refresh(); 15 getnstr(last,31);
16
17 printw("Pleased to meet you, %s
%s!",first,last); 18 refresh(); 19 getch();
20
21 endwin(); 22 return(0); 23 }
Only 24 characters are allocated for char buffer first (Line 5). And
32 characters are allocated for char buffer last (Line 6).
The getnstring() function at Line 11 reads up to 23 characters from
the keyboard. If the user types more, a beep sounds. The 23
characters doesn’t include the null character (\0) at the end of the
string, which is why the value read is one less than the buffer size.
The refresh() functions after the addstr() functions (Lines 10 and 14)
ensure that the prompts appear on the screen. Likewise, a refresh() is
required at line 18 to ensure that the printw() function’s output
appears.
Here is a sample run: What is your first name? Clark What is your
last name? Kent Pleased to meet you, Clark Kent!
Users can backspace and erase after hitting the maximum number of
characters that getnstr() allows; they cannot type more. So unlike
stream-input functions, such as fgets(), any text that flows in after
the maximum is lost.
2
3 int main() 4 {
5 int pieces; 6 const float uni = 4.5;
7
8 initscr();
9
10 addstr("SUSHI BAR"); 11 move(2,0); 12
printw("We have Uni today for $%.2f.\n",uni); 13
addstr("How many pieces would you like? "); 14
refresh();
15
16 scanw("%d",&pieces); 17 printw("You want %d
pieces?\n",pieces); 18 printw("That will be
$%.2f!",uni*(float)pieces); 19 refresh(); 20
getch();
21
22 endwin(); 23 return(0); 25 }
The scanw() function works just like scanf(). In Line 16, it reads an
integer value from input and stores it in variable pieces.
¨ As with scanf(), don’t forget the address-of operator (&) when using a
non-pointer variable with scanw().
3. Formatting Text
Ncurses text styles may not be as elaborate as the styles offered in a
GUI word processor, but they’re enough to add emphasis, fun, and
perhaps a wee bit o' color to what would otherwise be boring
terminal text.
¨ Text attributes affect only text, not the whitespace between words.
The COLORS and COLOR_PAIRS constants are set when the start_color()
function determines how many colors are available for the terminal.
Also coming into play is how much space is left for storing color
information in the Ncurses attr_t variable type .
1 4 Red COLOR_RED
2 2 Green COLOR_GREEN
3 6 Brown COLOR_YELLOW
4 1 Blue COLOR_BLUE
5 5 Magenta COLOR_MAGENTA
COLOR_CYAN
6 3 Cyan
7 7 White COLOR_WHITE
It's important to remember that the COLORS value tells you how many
colors are available, yet the color numbers start with zero. So, the
range is 0 through COLORS-1.
A color pair is a combination of foreground and background color
attributes. Each color pair is assigned a number from 1 through the
value of COLOR_PAIRS.
For example, the color pair COLOR_YELLOW, COLOR_RED indicates
yellow text on a red background. To assign those colors as a text
attribute you first use the init_pair() function:
init_pair(n,foreground,background) Argument n is the color pair
number; foreground and background are the Ncurses names for the
colors.
init_pair(1,COLOR_YELLOW,COLOR_RED); The above statement
defines color pair number 1 as yellow text on a red
background.
3 int main() 4 {
5 initscr(); 6 start_color(); 7
init_pair(1,COLOR_BLACK,COLOR_RED); 8
init_pair(2,COLOR_YELLOW,COLOR_BLACK); 9
attrset(COLOR_PAIR(1)); /* black on red */
10 addstr("I am Mr. Black!\n"); 11
attrset(COLOR_PAIR(2)); /* yellow on black */
12 addstr("I am Mr. Yellow!\n"); 13
attrset(COLOR_PAIR(1) | A_BOLD); /* +bold */
14 addstr("I'm feeling bold!\n"); 15
attrset(COLOR_PAIR(2) | A_BOLD);; /* +bold */
16 addstr("Me too!"); 17 refresh(); 18 getch(); 19
20 endwin(); 21 return(0); 22 }
Each time the attrset() function is used (Lines 9, 11, 13, and 15), a
new attribute is applied (not replaced). In Lines 13 and 15, the
A_BOLD attribute is mixed in with the original color pairs. The effect
may be profound on some terminals, or just bold text.
¨ Color pair attributes affect only the text put to the screen. The red
background doesn’t fill the rest of the line. The foreground color, like
other text attributes, affects only characters and not the blanks or
white space between text .
Color a Window
Colors in Ncurses can be both text attributes as well as screen or
window attributes. To set the standard screen background color, use
the bkgd() function: bkgd(attrs) This function works similarly to the
attrset() function for text: You first create a color pair, then assign
that color pair to the standard screen (or window). For example:
bkgd(COLOR_PAIR(1)); The foreground and background colors
assigned to COLOR_PAIR(1) are applied to the standard screen – the
entire window. All text displayed inherits the screen background
color, though you can use attrset() and attorn() functions to spot-
change text color or other attributes.
Noise, too!
Computer terminals traditionally sport some method of getting
attention. The early teletypes featured a bell, which is still ASCII
code 7 (^G). Terminal windows in GUI operating systems also
feature a beep or some alert technique. To implement these features,
Ncurses offers the beep() and flash() functions.
beep() flash() The beep() function, as you can guess, beeps
the terminal's speaker or generates whatever tone is assigned
to the terminal window program.
The first several attributes listed in Table 4-1 are what I refer to as
traditional text attributes, modifying the way characters appear.
Using the A_ALTCHARSET attribute is covered in the next section.
The A_INVIS attribute allows text to be present on the screen, yet
invisible. The A_PROTECT prevents text from being altered.
Implementation of these attributes isn’t available on every terminal.
The final attributes deal with text orientation and direction. As you
may suspect, not every terminal supports these attributes.
The following code demonstrates each attribute, though I use the
attrset() command and not chtype variables directly. Even so,
because attributes and characters are combined on the screen, result
is the same as if chtype characters were punched to various positions
on the screen.
04-03_attrtest.c 1 #include <ncurses.h> 2
3 int main() 4 {
5 initscr(); 6
7 attrset(A_STANDOUT); 8 addstr("This is
A_STANDOUT\n"); 9 attrset(A_UNDERLINE); 10
addstr("This is A_UNDERLINE\n"); 11
attrset(A_REVERSE); 12 addstr("This is
A_REVERSE\n"); 13 attrset(A_BLINK); 14
addstr("This is A_BLINK\n"); 15 attrset(A_DIM); 16
addstr("This is A_DIM\n"); 17 attrset(A_BOLD); 18
addstr("This is A_BOLD\n"); 19
attrset(A_ALTCHARSET); 20 addstr("This is
A_ALTCHARSET\n"); 21 attrset(A_INVIS); 22
addstr("This is A_INVIS\n"); 23
attrset(A_PROTECT); 42 addstr("This is
A_PROTECT\n"); 25 attrset(A_HORIZONTAL); 26
addstr("This is A_HORIZONTAL\n"); 27
attrset(A_LEFT); 28 addstr("This is A_LEFT\n"); 29
attrset(A_LOW); 30 addstr("This is A_LOW\n"); 31
attrset(A_RIGHT); 32 addstr("This is A_RIGHT\n");
33 attrset(A_TOP); 34 addstr("This is A_TOP\n");
35 attrset(A_VERTICAL); 36 addstr("This is
A_VERTICAL\n"); 37
38 refresh(); 39 getch(); 40
41 endwin(); 42 return(0); 43 }
The output you see on the terminal depends on which attributes it
supports.
You can apply a masking constant to further manipulate chtype
variables. These constants are defined in the ncurses.h header file
and shown in Table 4-2.
ATTRIBUTE MASKS VALUE (HEX)
NAME
A_NORMAL Nothing 0x00000000
A_ATTRIBUTES Attributes only 0xFFFFFF00
A_CHARTEXT Character only 0x000000FF
A_COLOR Color info only 0x0000FF00
Table 4-2. Text attribute masking values.
¨ I recommend using the attribute name and not its value .
Another set of attributes is available, one that adheres to the XSI
standard. These attributes use the same suffix as those shown in
Tables 4-1 and 4-2, though the prefix WA_ is used instead of A_.
Unicode Output
Ncurses can output Unicode text the same as any C program sends
Unicode to standard output. It’s not a straightforward operation, but
it can be done.
CORNERED!
Each window on the text screen features four corners: upper left,
upper right, lower left and lower right. Using data from the
getmaxyx() function, along with the mvaddch() function, you can
plot characters in each of the corners.
05-03_corners.c 1 #include <ncurses.h> 2
3 int main() 4 {
5 int lines,cols; 6
7 initscr();
8 getmaxyx(stdscr,lines,cols);
9 lines--; 10 cols--; 11
12 mvaddch(0,0,'*'); /* UL corner */
13 refresh(); 14 napms(500); /* pause 1/2 sec */
15
16 mvaddch(0,cols,'*'); /* UR corner */
17 refresh(); 18 napms(500); 19
20 mvaddch(lines,0,'*'); /* LL corner */
21 refresh(); 22 napms(500); 23
24 mvaddch(lines,cols,'*'); /* LR corner */
25 refresh(); 26 getch(); 27
28 endwin(); 29 return(0); 30 }
The program uses the mvaddch() function to slap down an asterisk in
every corner of the standard screen.
Line 12 could have been re-written as just: mvaddch('*'); /* UL
corner */
That’s because Ncurses initializes the cursor at the home location of
0,0 for each of its windows.
EXERCISE 06-04_TEXT4.C
Complete the code by inserting two blank lines at the second step. So, the
user presses a key and blank rows 1 and 3 open. Then strings text2[] and
text4[] are inserted.
EXERCISE 06-10_CAT.C
Write code to display the following line: Where did that silly cat go?
After the user presses any key, they see the word silly erased one character
at a time, then it’s replaced by the word fat one character at a time.
EXERCISE 08-03_KEYWAIT2.C
Modify the code from Example 08-02_keywait1.c so that the while loop
stops only when the spacebar is pressed.
For a function key, replace the n in Table 7-1 with the key number.
For example, to check for Function key 5, you would use: KEY_F(5).
Read a String
To read text longer than a single keypress, use the getstr() function.
Better, use the getnstr() function instead, which caps input at n
characters. Here’s the format: getnstr(buf,c) Variable buf is a storage
buffer, such as a char array, or pointer to a char buffer. Value c is the
maximum number of characters to type; input halts after c count
keystrokes, though you can backspace to edit input. (Unlike stream
input, backspace doesn’t count as an input character.) The input
count for getnstr() should be one less than the size of the storage
buffer. So, if the buffer holds 32 characters, use value 31 in the
getnstr() function.
Press the Enter key to terminate input. The Enter keystroke isn’t
stored in the buffer.
EXERCISE 09-03_SWITCHBACK.C
Modify the source code from 09-02_switch.c so that after the second
window is displayed, the user can press a key to again to view the standard
screen. Keep reading in the next section to help you understand what’s
going on when you fail.
EXERCISE 09-06_QUAD.C
Write a program that places four windows on the screen, each a quarter size
of the standard screen and positioned so that you can see all four windows
at once. Color each window with a different background, such as red, green,
blue, and cyan.
Remove a Window
When you’re done using a window, you use the delwin() function to
release its WINDOW pointer variable: delwin(win) The delwin()
function removes the window referenced by WINDOW pointer win. The
window’s contents remain on the screen after the call, but as the
window no longer exists, you can’t clear it or add new text.
09-07_twowin.c 1 #include <ncurses.h> 2
3 int main() 4 {
5 WINDOW *one,*two; 6
7 initscr(); 8
9 /* update the standard screen regardless */
10 refresh(); 11 /* create two half-size windows
*/
12 one = newwin(LINES,COLS/2,0,0); 13 two =
newwin(LINES,COLS/2,0,COLS/2); 14 if( one==NULL ||
two==NULL ) 15 {
16 endwin(); 17 puts("Unable to create windows");
18 return(1); 19 }
20
21 /* set backgrounds and text */
22 wbkgd(one,'|'); 23 waddstr(one,"Window One\n");
24 wbkgd(two,'-'); 25 waddstr(two,"Window Two\n");
26
27 /* update screen */
28 wrefresh(one); 29 getch(); 30 wrefresh(two); 31
getch(); 32
33 /* remove window 1 */
34 delwin(one); 35 waddstr(two,"Window one
deleted\n"); 36 wrefresh(two); 37 getch(); 38
39 endwin(); 40 return(0); 41 }
Two WINDOW pointers are created at Line 5, one and two.
Line 10 is important: The standard screen isn’t used in this code, but
it must be refreshed anyway. If not, the first window won’t show up.
Lines 11 through 19 create the two windows, each of which are half
the width of the standard screen.
Text and background are applied to each window in Lines 21
through 25.
In Lines 27 through 18, the windows are displayed, one at a time.
Finally, Line 34 removes window one and displays a message on
window two. On the screen, the contents of window two remain.
¨ The delwin() function cannot remove the standard screen, stdscr. To
remove that window, use the endwin() function, which also
terminates Ncurses.
Window Borders
Ncurses features two functions to apply a border to a window:
border() box() See the descriptions elsewhere in this chapter for the
argument lists, which can be quit long.
Both functions place text around the standard screen. The border()
function also features a w prefix variation, wborder(), to apply the
border to a named window. ACS line-drawing characters are used to
create the border, or you can choose your own box-drawing
characters .
The box() function is a simplified version of the border() function,
having fewer arguments. It lacks a w prefix variation as a window
argument is always required.
The border’s characters aren’t protected; they appear on the outside
rows and columns of the window. To avoid overwriting the box, you
must calculate a one-character offset for window-writing functions
(or use a subwindow, covered in Chapter 10.) The standard screen
border The format for the border() function is rather complex. It
features eight arguments, one for each side and corner character used
to draw the box. In its simplest format, the border() function is
called with all zeros: border(0,0,0,0,0,0,0,0); The above statement
places a single-line border around the standard screen.
If you prefer to specify your own characters to draw the border, they
are in order: · Left side character · Right side character · Top side
character · Bottom side character · Top left corner character · Top
right corner character · Bottom left corner character · Bottom right
corner character When the terminal lacks access to ACS characters,
ASCII characters approximating lines and corners are substituted.
09-08_border.c 1 #include <ncurses.h> 2
3 int main() 4 {
5 initscr(); 6
7 border(0,0,0,0, 0,0,0,0); 8 move(1,1);
9 addstr("Now that's a swell border!\n");
10 refresh(); 11 getch(); 12
13 endwin(); 14 return(0); 15 }
A single-line border is drawn around the edges of the standard
screen, thanks to the eight zeros as arguments to the border()
function in Line7.
A move() function at Line 8 ensures that the text displayed at line 9
appears within the window’s border.
The newline (‘\n’), added by addstr() in Line 9, wraps text to the
next line, which erases to the end of the line. To fix the problem
remove the newline character in the code.
¨ Newlines (‘\n’) displayed in Ncurses erase to the end of the line,
clobbering any text from the cursor to the right edge of the window.
EXERCISE 09-09_ABORDER.C
Modify the code in 09-8_border.c so that ASCII characters are used to
create the border.
EXERCISE 09-11_QUADBORDERS.C
Take your solution for Exercise 09-06_quad.c and modify the code so that
each window has a single-line border.
10. Subwindows
Much of the Ncurses’ documentation on the Internet claims that
subwindows are buggy and should be avoided. Yet, I believe they do
have a useful place in your code. In my experience, I find a
subwindow useful and a time-saver.
Create a Subwindow
Two functions create a subwindow, subwin() or derwin():
subwin(win,rows,cols,y,x) derwin(win,rows,cols,y,x) The win
argument represents the parent window, already created in your
code.
The rows and cols arguments are the subwindow’s height and width,
respectively. These values much be less than the parent window’s
height and width .
The y and x arguments represent an offset, which is where the two
functions differ: In the subwin() function, y and x, are coordinates
relative to the standard screen; in the derwin() function, y and x are
relative to the parent window.
After creation, the subwindow is addressed by using standard
Ncurses output functions.
EXERCISE 10-02_SUB2.C
Crate a subwindow that’s one quarter size the standard screen and place that
subwindow in the center of its parent. Output text to both windows.
Removing a Subwindow
The delwin() function removes the named window, whether it’s a
primary window or a subwindow: delwin(win); The win argument is
the name of a WINDOW pointer variable, a subwindow.
As with a primary window, removing a subwindow doesn’t remove
its contents from the screen. Any text was written to the subwindow
is inherited by the parent window, so nothing is truly lost.
10-05_delsub.c 1 #include <ncurses.h> 2
3 int main() 4 {
5 WINDOW *sub; 6 int x; 7
8 initscr(); 9 start_color(); 10
init_pair(1,COLOR_BLACK,COLOR_BLUE); 11
12 /* create subwindow */
13 sub=subwin(stdscr,LINES-10,COLS-10,4,5); 14 if(
sub==NULL) 15 {
16 endwin(); 17 puts("Unable to create
subwindow"); 18 return(1); 19 }
20
21 /* fill windows */
22 for(x=0;x<120;x++) 23 addstr("standard screen
"); 24 wbkgd(sub,COLOR_PAIR(1)); 25
for(x=0;x<200;x++) 26 waddstr(sub," sub "); 27
refresh(); 28 wrefresh(sub); 29 getch(); 30
31 /* delete subwindow */
32 delwin(sub); 33 mvaddstr(0,0,"Subwindow deleted
"); 34 refresh(); 35 getch(); 36
37 endwin(); 38 return(0); 39 }
By the time you see things (Line 28), the standard screen is filled
with text and a blue-background subwindow appears in the center.
Press the Enter key (Line 29), and then the delwin() function
removes the subwindow.
The result is that the parent window (stdscr) inherits the
subwindow’s text. The refresh() at Line 24 doesn’t remove the
subwindow’s contents.
¨ Never remove a parent window that has active subwindows. Doing so
results in ugly and unpredictable behavior.
Subwindow Roundup
Subwindows work like real windows in many ways: * Subwindows
use a separate WINDOW data structure in memory * Subwindows sport
their own cursor, separate from the parent window's cursor.
* Subwindows can have their own color and text attributes.
* Subwindows are manipulated like regular windows, with a few
exceptions.
A subwindow knows its parent, but parent windows are unaware of
any subwindows. No error is triggered should you remove a window
and not first remove its subwindows.
Finally, remember that subwindows share memory with their parent.
Text written to a subwindow is also written to the parent. Likewise,
the parent has no respect for its subwindow, and can effortlessly
write over the subwindow's text. In fact, text written over the
subwindow's text becomes part of the subwindow, thanks to the
shared memory.
11. Window Tricks: Copying and Moving
Windows aren’t only containers for text and formatting. They’re data
structures. As such, you can copy text and attributes from one
structure to another. You can duplicate windows. You can move
windows.
EXERCISE 11-02_OVERWRITE2.C
To prove that the contents of window red are copied to window blue,
modify the source code 11-01_overwrite1.c so that the background on
window red is changed to solid red after the overwrite() function (Line 33).
Update both windows to prove that window red is clear but window blue
still holds the copied text.
EXERCISE 11-05_CLOBBER.C
Modify Line 39 in 11-04_copywin.c so that the final argument is FALSE. Run
the program to see a destructive copy from one window to the other.
This is window fred again; the text put to barney is covered up.
Press Enter.
This is Fred.
This is Barney.
You too!
Scroll Around
Scrolling is a window attribute, along with size, position, and
background color. You can activate or deactivate scrolling, as well as
manually scroll a window up or down by a given number of lines.
The sminrow and smincol arguments set the upper left corner for the
rectangle on the standard screen (think s for standard screen). Then
the smaxrow and smaxcol define the size of the rectangle as measured
from the standard screen’s original, coordinates 0, 0.
The arguments smaxrow and smaxcol must be greater than sminrow
and smincol, respectively, and they must be located on the standard
screen. Though the pad can be larger than the standard screen, the
chunk displayed must fit within the screen’s dimensions.
13-02_newpad2.c 1 #include <ncurses.h> 2
3 int main() 4 {
5 WINDOW *p;
6 int x;
7
8 initscr(); 9
10 /* create a new pad */
11 p = newpad(50,100); 12 if( p==NULL ) 13 {
14 endwin(); 15 puts("Unable to create pad"); 16
return(1); 17 }
18
19 /* fill the pad */
20 for(x=0;x<500;x++)
21 wprintw(p,"%4d",x);
22
23 addstr("Press Enter to update");
24 refresh(); 25 getch(); 26
27 prefresh(p,0,0,5,5,16,45);
28 getch();
29
30 endwin(); 31 return(0); 32 }
This code is based on 13-01_newpad1.c, with new and changed lines
highlighted in green.
Lines 19 through 21 fill the pad with numbers, just some text.
The prefersh() function in Line 27 copies a chunk of text from the
pad to a rectangle on the standard screen. This function
automatically updates (refreshes) the standard screen, so a refresh()
statement is redundant.
Subpads
Just as windows can have subwindows, pads can sport subpads. The
subpad shares memory with the parent; changing the contents of a
subpad changes the text on a pad.
As with my subwindow philosophy, the subpad works best as an
offset reference to the pad. So, if you’re working on a certain part of
the pad, you can quickly create a subpad to avoid tedious math
calculations.
To create a subpad, you must first create a pad. Then use the
subpad() function: subpad(p,rows,cols,y,x) p is the parent pad. rows
and cols set the subpad's size, which (logically) cannot be greater
than the parent’s size. y and x set the subpad's position relative to the
parent, where 0,0 is the upper left corner.
When the subpad() call is successful, a subpad is created in memory
and a pointer to a WINDOW structure is returned, otherwise NULL is
returned.
13-03_sonofpad.c 1 #include <ncurses.h> 2
3 int main() 4 {
5 WINDOW *pop,*son; 6 int x; 7
8 initscr(); 9
10 /* create a new pad */
11 pop = newpad(50,50); 12 if( pop==NULL ) 13 {
14 endwin(); 15 puts("Unable to create pad"); 16
return(1); 17 }
18
19 /* fill the pad */
20 for(x=0;x<50;x++) 21 waddstr(pop,"Hello "); 22
23 /* create the subpad */
24 son = subpad(pop,10,10,0,0); 25 if( son==NULL)
26 {
27 endwin(); 28 puts("Unable to create subpad");
29 return(1); 30 }
31
32 addstr("Press Enter to update"); 33 refresh();
34 getch(); 35
36 prefresh(son,0,0,5,5,15,15); 37 getch(); 38
39 endwin(); 40 return(0); 41 }
The subpad is created at Line 24. It’s set at a size 10 lines by 10
columns at offset 0, 0 of the pad pop.
The prefresh() function at Line 36 splashes the son pad to the
standard screen.
In this example, nothing spectacular happens. Again, a subpad (like
a subwindow) is best for accessing text at specific offsets within its
parent pad.
Pad Miscellany
You probably won’t use pads that much. I’ve used them as a buffer.
For example, I’ve loaded a long text file into a pad. Then I prefresh()
chunks of that text to the standard screen to display help text.
Beyond that, I’ve not used pads. I’ve never used a subpad.
REMOVE A PAD PADS ARE BLOWN TO
SMITHEREENS JUST LIKE WINDOWS.
THE SAME FUNCTION IS USED,
DELWIN(): DELWIN(PAD) THE ABOVE
FUNCTION RETURNS OK WHEN THE
NAMED PAD IS SUCCESSFULLY
REMOVED OR IT RETURNS ERR WHEN
SOMETHING UNTOWARD HAPPENS.
The delwin() function also removes subpads. As with subwindows,
ensure that you remove the subpad before you remove its parent pad
.
Hello, Mouse
Two tests must be made before you can code a program that reads
mouse input: 1. You must confirm that Ncurses has the smarts to
read the mouse.
2. You must determine whether the terminal window can
communicate mouse information.
For the first test, check constant value NCURSES_MOUSE_VERSION. If it’s
greater than zero, Ncurses can read mouse input.
For the second text, use the mousemask() function:
mousemask(newmask,*oldmask) The newmask argument tells
Ncurses which mouse events to trap. It’s a variable of the mmask_t
type, though constants are used that represent mouse events. These
constants are defined in ncurses.h, and listed in Table 14-1.
MOUSE ACTION CONSTANT VALUE
BUTTON1_RELEASED 0x00000001
BUTTON1_PRESSED 0x00000002
BUTTON1_CLICKED 0x00000004
BUTTON1_DOUBLE_CLICKED 0x00000008
BUTTON1_TRIPLE_CLICKED 0x00000010
BUTTON1_RESERVED_EVENT 0x00000020
BUTTON2_RELEASED 0x00000040
BUTTON2_PRESSED 0x00000080
BUTTON2_CLICKED 0x00000100
BUTTON2_DOUBLE_CLICKED 0x00000200
BUTTON2_TRIPLE_CLICKED 0x00000400
BUTTON2_RESERVED_EVENT 0x00000800
BUTTON3_RELEASED 0x00001000
BUTTON3_PRESSED 0x00002000
BUTTON3_CLICKED 0x00004000
BUTTON3_DOUBLE_CLICKED 0x00008000
BUTTON3_TRIPLE_CLICKED 0x00010000
BUTTON3_RESERVED_EVENT 0x00020000
BUTTON4_RELEASED 0x00040000
BUTTON4_PRESSED 0x00080000
BUTTON4_CLICKED 0x00100000
BUTTON4_DOUBLE_CLICKED 0x00200000
BUTTON4_TRIPLE_CLICKED 0x00400000
BUTTON4_RESERVED_EVENT 0x00800000
BUTTON_CTRL 0x01000000
BUTTON_SHIFT 0x02000000
BUTTON_ALT 0x04000000
ALL_MOUSE_EVENTS 0x07FFFFF
REPORT_MOUSE_POSITION 0x08000000
Table 14-1: Mouse action constants and values.
The best constant to use in the mousemask() function is
ALL_MOUSE_EVENTS, which tells Ncurses to keep an eye on every
mouse button, up or down, clicked, double- or triple-clicked, or used
in combination with Shift, Alt, or Ctrl keys.
The second argument in mousemask() is *oldmask. It’s also a
mmask_t variable representing the value returned from a previous
mousemask() function, though NULL is specified most of the time.
14-01_mousetest.c 1 #include <ncurses.h> 2
3 int main() 4 {
5 initscr(); 6
7 if( NCURSES_MOUSE_VERSION>0) 8 {
9 addstr("Mouse functions available.\n"); 10
mousemask(ALL_MOUSE_EVENTS,NULL); 11 addstr("Mouse
Active"); 12 }
13 else 14 {
15 addstr("Mouse functions unavailable.\n"); 16 }
17 refresh(); 18 getch(); 19
20 endwin(); 21 return(0); 22 }
This code determines whether Ncurses has mouse functions
available at Line 7. If so, the mouse is enabled at Line 10, though no
code reads the mouse’s position or status.
Appropriate text is displayed depending on the value of the
NCURSES_MOUSE_VERSION constant.
¨ One of the reasons your terminal might not pass on mouse functions is
that the operating system intercepts them first. If possible, disable
mouse selection functions for the terminal window.
EXERCISE 14-03_CLICKPUT.C
Write code that places an asterisk at the mouse click location on the screen.
WHAT CLICKED?
The following code uses the predefined button constants to display
information about which mouse button was pressed.
14-04_bclick.c 1 #include <ncurses.h> 2
3 int main(void) 4 {
5 MEVENT mort; 6 int ch; 7
8 initscr(); 9 noecho(); 10 keypad(stdscr,TRUE);
11
12 mousemask(ALL_MOUSE_EVENTS,NULL); 13
14 while(1) 15 {
16 ch = getch(); 17 if( ch == KEY_MOUSE ) 18 {
19 clear(); 20 getmouse(&mort); 21
switch(mort.bstate) 22 {
23 case BUTTON1_PRESSED: 24 mvaddstr(0,0,"B1
Press"); 25 break; 26 case BUTTON1_RELEASED: 27
mvaddstr(1,0,"B1 Release"); 28 break; 29 case
BUTTON1_CLICKED: 30 mvaddstr(2,0,"B1 Click"); 31
break; 32 case BUTTON1_DOUBLE_CLICKED: 33
mvaddstr(3,0,"B1 2xClick"); 34 break; 35 case
BUTTON2_PRESSED: 36 mvaddstr(0,20,"B2 Press"); 37
break; 38 case BUTTON2_RELEASED: 39
mvaddstr(1,20,"B2 Release"); 40 break; 41 case
BUTTON2_CLICKED: 42 mvaddstr(2,20,"B2 Click"); 43
break; 44 case BUTTON2_DOUBLE_CLICKED: 45
mvaddstr(3,40,"B2 2xClick"); 46 break; 47 case
BUTTON3_PRESSED: 48 mvaddstr(0,40,"B3 Press"); 49
break; 50 case BUTTON3_RELEASED: 51
mvaddstr(1,40,"B3 Release"); 52 break; 53 case
BUTTON3_CLICKED: 54 mvaddstr(2,40,"B3 Click"); 55
break; 56 case BUTTON3_DOUBLE_CLICKED: 57
mvaddstr(3,40,"B3 2xClick"); 58 break; 59 default:
60 break; 61 }
62 refresh(); 63 continue; 64 }
65 if( ch == '\n' ) 66 break; 67 }
68
69 endwin(); 70 return 0; 71 }
When you run the code, remember to press and hold the mouse
button, then release. You’ll see the Press and Release text appear on
the screen. Also, click and double click to see that output.
It surprised me that the right mouse button was read as B3 by the
code. The wheel button was read as B2.
Refer to Table 14-1 for the full list of mouse events.
15. Miscellany
This eBook covers many of the basic Ncurses functions, yet it only
scratches the surface. To wrap things up, I’ve collected a few
additional functions and tossed them into this final chapter.
Line Drawing
Chapter 9 covered the box() and border() functions, which are
specific to a window. To draw other lines, you can access alternative
character set characters, use Unicode, or employ these functions:
hline(ch,n) vline(ch,n) The hline() and vline() functions draw a
horizontal or vertical line from the cursor's current position right or
down, respectively. The line is drawn by using character ch. When
ch isn’t specified, one of the standard line drawing character is used
instead. Argument n sets the length of the line in characters.
Both functions draw the line from the cursor's current position.
Neither function alters the cursor's position.
15-02_steps.c 1 #include <ncurses.h> 2
3 int main() 4 {
5 int y,x,maxy,maxx; 6
7 initscr(); 8
9 getmaxyx(stdscr,maxy,maxx); 10
11 for(y=x=0;y<maxy;y++,x+=2) 12 {
13 move(y,x); 14 hline(0,maxx-x); 15 vline(0,maxy-
y); 16 }
17 refresh(); 18 getchar(); 19
20 endwin(); 21 return(0); 22 }
This program’s output shows a cascade of vertical and horizontal
lines with an origin in the upper left corner of the screen.
You can save time by combining the cursor’s location (Y and X
positions) with the hline() and vline() functions. The variations are
mvhline() and mvvlin(). A w prefix can also be added to send output
to a specific window.
EXERCISE 15-03_PLUS.C
Code a program that uses the hline() and vline() functions to display a plus
(+) on the standard screen. You get bonus points if it’s a hollow plus and not
just two lines that intersect.
Save the Screen
As a data structure, it’s possible to save an Ncurses window to a file.
Further, you can load a saved Ncurses data structure into a window.
The quartet of functions that handle the task are:
scr_dump(*filename) scr_restore(*filename) putwin(win,*FILE)
getwin(win,*FILE) The scr_dump() and putwin() functions write
information from the current screen or a specific window to the
named file, respectively.
The scr_restore() and getwin() functions read information from a file
back into the current screen or a named window, respectively For the
scr_dump() and scr_restore() functions, the filename argument is a
string, a filename. It is not a FILE handle. These functions open,
read, and close the named file; you do need to fopen() or fclose() the
file.
The putwin() and getwin() function sport a window argument, win,
which is the name of the WINDOW pointer referencing the window to
be saved or restored. The FILE argument is a file handle pointer
returned from an fopen() function .
The scr functions are the “screen shot” functions; they work with the
current screen, what you see in the terminal window. The contents
saved or restored could be a collection of visible windows or a single
window. The scr_dump() function saves the current screen’s
contents to the named file; the scr_restore() function retrieves
previously-saved screen contents, placing the data back on the
current screen.
The putwin() and getwin() functions work with a window, not the
current screen. That window could also be the standard screen.
The scr_dump() and putwin() functions overwrite any existing file.
When the dump or restore is successful, the functions return OK,
otherwise ERR is returned.
TAKE A SCREEN DUMP THE
FOLLOWING CODE DISPLAYS THE
STANDARD SCREEN AS WELL AS A
WINDOW, W. THE SCR_DUMP()
FUNCTION CAPTURES THE CURRENT
SCREEN, SAVING IT TO A FILE NAMED
DUMP.WIN.
The scr_dump() function performs what old timers refer to as a
"screen dump." The inelegant term "dump" simply means to transfer
a chunk of (often raw) data from one device to another. In the case
of a screen dump, the data from the screen is saved to a file.
15-04_dumpwin.c 1 #include <ncurses.h> 2 #include <stdlib.h>
3 #include <time.h> 4
5 int main() 6 {
7 char word[7]; 8 int x,w,r; 9
10 srandom((unsigned)time(NULL)); 11 word[6] =
'\0'; 12 initscr(); 13
14 /* add some random 6-char words */
15 for(x=0;x<200;x++) 16 {
17 for(w=0;w<6;w++) 18 word[w] = (random() % 26) +
'a'; 19 printw("%s\t",word); 20 }
21 addstr("\n Press Enter to dump the screen ");
22 refresh(); 23 getch(); 24
25 /* write the window */
26 r = scr_dump(“dump.win”); 27 if( r == ERR) 28
addstr("Error writing window"); 29 else 30
addstr("File written; press Enter"); 31 refresh();
32 getch(); 33
34 endwin(); 35 return(0); 36 }
Lines 14 through 23 work to write 200 random words to the screen.
The result is something unique on the screen. The scr_dump()
function is issued at Line 26, with its result tested at Line 27. If the
function fails, ERR is returned, otherwise the file is written.