

\documentclass[11pt]{article}

\setlength{\oddsidemargin}{0in}
\setlength{\evensidemargin}{0in}
\setlength{\topmargin}{0.0in}
\setlength{\headheight}{0in}
\setlength{\headsep}{0in}
\setlength{\textwidth}{6.5in}
\setlength{\textheight}{9.4in}
\setlength{\parindent}{0in}
\setlength{\parskip}{0.12in}

\usepackage{times}
\usepackage{hyperref}
\usepackage{fancyvrb}
\usepackage{relsize}
\usepackage{url} 

\begin{document}

\title{Introduction to the Unix Curses Library}

\author{Norman Matloff \\
Department of Computer Science \\
University of California at Davis \\
\copyright{}1997-2011, N. Matloff}

\date{April 8, 2011} 

\maketitle 

\tableofcontents{}  
\newpage

\section{History}

\subsection{Purpose of the Curses Library}

Many widely-used programs need to make use of a terminal's
cursor-movement capabilities.  A familiar example is the \textbf{vi} (or
the {\bf vim} variation) text editor; most of its commands make use of
such capabilities.  For example, hitting the {\bf j} key while in
\textbf{vi} will make the cursor move up one line.  Typing {\bf dd} will
result in the current line being erased, the lines below it moving up
one line each, and the lines above it remaining unchanged.

A potential problem with all this is that different terminals have
different ways in which to specify a given type of cursor motion.
For example, if a program wants to make the cursor move up one line
on a VT100 terminal, the program needs to send the characters Escape,
[, and A:

\begin{Verbatim}[fontsize=\relsize{-2}]
printf("%c%c%c",27,'[','A');
\end{Verbatim}

(the ASCII character code for the Escape key is 27).  But for a
Televideo 920C terminal, the program would have to send the ctrl-K
character, which has code 11:

\begin{Verbatim}[fontsize=\relsize{-2}]
printf("%c",11);
\end{Verbatim}

Clearly, the authors of programs like \textbf{vi} would go crazy trying
to write different versions for every terminal, and worse yet, anyone
else writing a program which needed cursor movement would have to
``re-invent the wheel,'' i.e. do the same work that the \textbf{vi}-writers
did, a big waste of time.

That is why the {\tt curses} library was developed.  The goal was
to alleviate authors of cursor-oriented programs like {\bf vi} of the
need to write different code for different terminals.  The programs
would make calls to the library, and the library would sort out what to
do for the given terminal type.  

For example, if your program needs to clear the screen, it would not
(directly) use any character sequences like those above.  Instead,
it would simply make the call

\begin{Verbatim}[fontsize=\relsize{-2}]
clear();
\end{Verbatim}

and {\tt curses}  would do the work on the program's behalf, i.e. would
print the characters Escape, [, and A, causing the screen to clear.

\subsection{Evolution of the Role of Curses}

Development of the {\tt curses} library was a major step in the
evolution of Unix software.  Even though the VT100 terminal type
eventually became standard, {\tt curses} continued to play an important
role, by providing an extra level of abstraction to the programmer,
alleviating the programmer from needing to know even the VT100 cursor
movement codes.

The library is still quite important in today's GUI-oriented world,
because in many cases it is more convenient to use the keyboard for
actions than the mouse.\footnote{After all, even many Microsoft Windows
applications provide ``keyboard shortcuts.''}  Today physical terminals
are rarely used, but typical Unix work does involve some text windows
each of which is emulating a VT100 terminal.\footnote{The program
running the window is {\bf xterm} or something similar, e.g. {\bf
gnome-terminal}.} The {\bf vi} editor and other {\tt curses}-based
programs continue to be popular.\footnote{Interestingly, even some of
those classical Curses programs have also become somewhat GUI-ish.  For
instance {\bf vim}, the most popular version of {\bf vi} (it's the
version which comes with most Linux distributions, for example), can be
run in {\bf gvim} mode.  There, in addition to having the standard
keyboard-based operations, one can also use the mouse.  One can move the
cursor to another location by clicking the mouse at that point; one can
use the mouse to select blocks of text for deletion or movement; etc.
There are icons at the top of the editing window, for operations like
Find, Make, etc.}

\section{Include and Library Files}

In order to use {\tt curses}, you must include in your source code a statement

\begin{Verbatim}[fontsize=\relsize{-2}]
#include <curses.h>
\end{Verbatim}

and you must link in the {\tt curses}  library:

\begin{Verbatim}[fontsize=\relsize{-2}]
gcc -g sourcefile.c -lcurses 
\end{Verbatim}

\section{Two Examples}

It's best to learn by example!

Try running these programs!  You don't have to key in their source code;
instead, you can get it from the raw file which produced this document,
\url{http://heather.cs.ucdavis.edu/~matloff/UnixAndC/CLanguage/Curses.tex},
and then editing out the non-code.

If you are in a hurry, you may wish to go directly to the second
example, which anyway is better commented and makes use of more {\tt
curses} features.

\subsection{Simple, Quick Introductory Example}

Our first example does very little.  You can think of it as a very
primitive, uninteresting game.  But it gives us a start.

The comments at the top of the source file tell you what the ``game''
does.  Compile and run the program, inputting various characters as you
like.  Then read the code (start with {\bf main()}, as you should in
general), with the comments doing the explaining.

\begin{samepage}
\begin{Verbatim}[fontsize=\relsize{-2},numbers=left]
// simple curses example; keeps drawing the inputted characters, in columns
// downward, shifting rightward when the last row is reached, and
// wrapping around when the rightmost column is reached

#include <curses.h>  // required

int r,c,  // current row and column (upper-left is (0,0))
    nrows,  // number of rows in window
    ncols;  // number of columns in window

void draw(char dc)

{  move(r,c);  // curses call to move cursor to row r, column c
   delch();  insch(dc);  // curses calls to replace character under cursor by dc
   refresh();  // curses call to update screen
   r++;  // go to next row
   // check for need to shift right or wrap around
   if (r == nrows)  {
      r = 0;
      c++;
      if (c == ncols) c = 0;
   }
}

main()

{  int i;  char d;  
   WINDOW *wnd;

   wnd = initscr();  // curses call to initialize window
   cbreak();  // curses call to set no waiting for Enter key
   noecho();  // curses call to set no echoing
   getmaxyx(wnd,nrows,ncols);  // curses call to find size of window
   clear();  // curses call to clear screen, send cursor to position (0,0)
   refresh();  // curses call to implement all changes since last refresh

   r = 0; c = 0;
   while (1)  {
      d = getch();  // curses call to input from keyboard
      if (d == 'q') break;  // quit?
      draw(d);  // draw the character
   }

   endwin();  // curses call to restore the original window and leave

}

\end{Verbatim}
\end{samepage}

\subsection{A Second, More Useful Example}

Here's a program you can actually use.  If you need to kill a number of
processes, this program will allow you to browse through your processes,
killing the ones you want.

Again, compile and run the program, and kill some garbage processes
which you've set up for that purpose.  (For that matter, you could kill
{\bf psax} itself.) Then read the code, with the comments doing the
explaining.

\begin{Verbatim}[fontsize=\relsize{-2},numbers=left]
// psax.c; illustration of curses library

// runs the shell command 'ps ax' and dislays the last lines of its output,
// as many as the window will fit; allows the user to move up and down
// within the window, with the option to kill whichever process is 
// currently highlighted

// usage:  psax

// user commands:

//    'u':  move highlight up a line
//    'd':  move highlight down a line
//    'k':  kill process in currently highlighted line
//    'r':  re-run 'ps ax' for update
//    'q':  quit

// possible extensions:  allowing scrolling, so that the user could go
// through all the 'ps ax' output, not just the last lines; allow
// wraparound for long lines; ask user to confirm before killing a
// process

#define MAXROW 1000
#define MAXCOL 500

#include <curses.h>  // required

WINDOW *scrn; // will point to curses window object

char cmdoutlines[MAXROW][MAXCOL];  // output of 'ps ax' (better to use
                                   // malloc())
int ncmdlines,  // number of rows in cmdoutlines
    nwinlines,  // number of rows our "ps ax" output occupies in the 
                //  xterm (or equiv.) window 
    winrow,  // current row position in screen
    cmdstartrow,  // index of first row in cmdoutlines to be displayed
    cmdlastrow;  // index of last row in cmdoutlines to be displayed

// rewrites the line at winrow in bold font
highlight()
{  int clinenum;
   attron(A_BOLD);  // this curses library call says that whatever we 
                    // write from now on (until we say otherwise) 
                    // will be in bold font 
   // we'll need to rewrite the cmdoutlines line currently displayed
   // at line winrow in the screen, so as to get the bold font
   clinenum = cmdstartrow + winrow;
   mvaddstr(winrow,0,cmdoutlines[clinenum]);
   attroff(A_BOLD);  // OK, leave bold mode
   refresh();  // make the change appear on the screen
}

// runs "ps ax" and stores the output in cmdoutlines
runpsax()
{  FILE *p; char ln[MAXCOL]; int row,tmp;
   p = popen("ps ax","r");  // open Unix pipe (enables one program to read
                            // output of another as if it were a file)
   for (row = 0; row < MAXROW; row++)  {
      tmp = fgets(ln,MAXCOL,p);  // read one line from the pipe
      if (tmp == NULL) break;  // if end of pipe, break
      // don't want stored line to exceed width of screen, which the
      // curses library provides to us in the variable COLS, so truncate
      // to at most COLS characters
      strncpy(cmdoutlines[row],ln,COLS);
      // remove EOL character
      cmdoutlines[row][MAXCOL-1] = 0;
   }
   ncmdlines = row;
   close(p);  // close pipe
}

// displays last part of command output (as much as fits in screen)
showlastpart()
{  int row;
   clear();  // curses clear-screen call
   // prepare to paint the (last part of the) 'ps ax' output on the screen
   // two cases, depending on whether there is more output than screen rows;
   // first, the case in which the entire output fits in one screen:
   if (ncmdlines <= LINES)  { // LINES is an int maintained by the curses
                              // library, equal to the number of lines in
                              // the screen
      cmdstartrow = 0;
      nwinlines = ncmdlines;
   }
   else  { // now the case in which the output is bigger than one screen
      cmdstartrow = ncmdlines - LINES;
      nwinlines = LINES;
   }
   cmdlastrow = cmdstartrow + nwinlines - 1;
   // now paint the rows to the screen
   for (row = cmdstartrow, winrow = 0; row <= cmdlastrow; row++,winrow++)  
      mvaddstr(winrow,0,cmdoutlines[row]);  // curses call to move to the
                                            // specified position and
                                            // paint a string there
   refresh();  // now make the changes actually appear on the screen,
               // using this call to the curses library
   // highlight the last line
   winrow--;  
   highlight();
}

// moves up/down one line
updown(int inc)
{  int tmp = winrow + inc; 
   // ignore attempts to go off the edge of the screen
   if (tmp >= 0 && tmp < LINES)  {
      // rewrite the current line before moving; since our current font
      // is non-BOLD (actually A_NORMAL), the effect is to unhighlight
      // this line
      mvaddstr(winrow,0,cmdoutlines[cmdstartrow+winrow]);
      // highlight the line we're moving to
      winrow = tmp;
      highlight();
   }
}

// run/re-run "ps ax"
rerun()
{  runpsax();
   showlastpart();
}

// kills the highlighted process
prockill()
{  char *pid;
   // strtok() is from C library; see man page
   pid = strtok(cmdoutlines[cmdstartrow+winrow]," ");
   kill(atoi(pid),9);  // this is a Unix system call to send signal 9, 
                       // the kill signal, to the given process
   rerun();
}

main()
{  char c;
   // window setup, next 3 lines are curses library calls, a standard
   // initializing sequence for curses programs
   scrn = initscr();
   noecho();  // don't echo keystrokes
   cbreak();  // keyboard input valid immediately, not after hit Enter
   // run 'ps ax' and process the output
   runpsax();
   // display in the window
   showlastpart();
   // user command loop
   while (1)  {
      // get user command
      c = getch();
      if (c == 'u') updown(-1);
      else if (c == 'd') updown(1);
      else if (c == 'r') rerun();
      else if (c == 'k') prockill();
      else break;  // quit
   }
   // restore original settings and leave
   endwin();
}
\end{Verbatim}

\subsection{A Note on ``Cooked'' and ``Raw'' Modes in the Examples}

In most programs you write, keyboard actions are in {\bf cooked} mode.
This means that your keyboard input is not sent to the program (e.g. not
sent to the {\bf scanf()} function or to {\bf cin}) until you hit the
Enter key.  This gives you a chance to use the Backspace key to erase a
character which you typed but now wish to ``rescind.''  Again, this
means that your program never sees the character you erase, nor does it
see the Backspace character (ASCII 8) itself.  

Remember, when you type at the keyboard, the characters go to the
operating system (OS).  The OS normally passes those characters to your
application program (e.g. to its call to {\bf scanf()} or {\bf cin}),
but if the OS sees the Backspace character, the OS does NOT pass it on
to the program.  In fact, the OS doesn't pass any characters at all to
the program until the user hits the Enter key.  

The other mode is {\bf raw} mode.  Here the OS passes every character
on to the application program.  If the user hits the Backspace key, it's
just treated as any other character, with no special action taken.  The
application program then receives an ASCII 8 character, and it's up to
the programmer to decide what to do with it.

You can switch from cooked to raw mode using the {\bf cbreak()} function,
and back to cooked by calling {\bf nocbreak()}.

Similarly, the default mode is for the OS to echo characters.  If the
user types the A key (and not Shift), the OS will then write `a' to the
screen.\footnote{Note, though, that even though the `a' appears on the
screen, the application programm still has not received it, which it
won't do until the user hits the Enter key.}  There are times when we
don't want echoing, such as in the examples we've seen here, and also
other situations such as password entry.  Well, we can turn off the echo
by calling {\bf noecho()}, and turn it back on again via {\bf echo()}.

\section{Important Debugging Notes}

Don't use {\bf printf()} or {\bf cout} for debugging!  Make sure you use
debugging tool, for example GDB or better the DDD interface to GDB.  If
you are not using a debugging tool for your daily programming work, you
are causing yourself unnecessary time and frustration.  See my debugging
slide show, at \url{http://heather.cs.ucdavis.edu/~matloff/debug.html}. 

With {\tt curses} programs, you don't really have the option of using
{\bf printf()} or {\bf cout} for debugging anyway, as the output of
those actions would become mixed with that of your application.  So a
debugging tool is a necessity.  Here we will {\tt curses} debugging with
GDB and DDD, as they are the best known in the Unix world.\footnote{This
assumes prior knowledge of GDB or DDD.  See the URL listed above.}  The
problem is that you still need to do something to separate your
debugging output from your {\tt curses}  application's output.  Here I
show how.

\subsection{GDB}

Start up GDB as usual in some text window.  Then choose another window
in which your {\tt curses} application will run, and determine the
device name for that latter window (which we will call the ``execution
window'').  To do this, run the Unix {\bf tty} command in that window.
Let's suppose for example that the output of the command is
``/dev/pts/10''.  Then within GDB issue the command

\begin{Verbatim}[fontsize=\relsize{-2}]
(gdb) tty /dev/pts/10
\end{Verbatim}

We must then do one more thing before issuing the {\bf run} command to
GDB.  Go to the execution window, and type

\begin{Verbatim}[fontsize=\relsize{-2}]
sleep 10000
\end{Verbatim}

Unix's \textbf{sleep} command has the shell go inactive for the given
amount of time, in this example 10,000 seconds.  This is needed so that
any input we type in that window will be sure to go to our program,
rather than to the shell.

Now go back to GDB and execute {\bf run} as usual.  Remember, whenever
your program reaches points at which it will read from the keyboard, you
will have to go to the execution window and type there (and you will see
the output from the program there too).  When you are done, type ctrl-C
in the execution window, so as to kill the \textbf{sleep} command and
make the shell usable again.

Note that if something goes wrong and your program finishes prematurely,
that execution window may retain some of the nonstandard terminal
settings, e.g.  cbreak mode.  To fix this, go to that window and type
ctrl-j, then the word `reset', then ctrl-j again.

\subsection{DDD}

Things are much simpler in DDD.  Just click on View $|$ Execution
Window, and a new window will pop up to serve as the execution window.

\section{Some of the Major Curses APIs, Attributes and
Environment Variables}

\subsection{Environment}

\begin{itemize}

\item Row and column numbers in a window start at 0, top to bottom and
left to right.  So, the top left-hand corner position is (0,0).

\item {\bf LINES, COLS}:

The number of lines and columns in your window.

\end{itemize}

\subsection{APIs}

Here are some of the {\tt curses} functions you can call\footnote{Many
are actually macros, not functions.}  Note that the functions listed
here comprise only a small subset of what is available.  There are many
other things you can do with {\tt curses}, such as subwindowing,
forms-style input, etc.; see Section \ref{more} for resources. 

\begin{itemize}

\item {\bf WINDOW *initscr()}: 

REQUIRED.  Initializes the whole screen for {\tt curses} .  Returns a
pointer to a data structure of type WINDOW, used for some other
functions.

\item {\bf endwin()}:  

Resets the terminal, e.g. restores echo, cooked (non-cbreak)
mode, etc.  

\item {\bf cbreak()}:  

Sets the terminal so that it reads characters from keyboard immediately
as they are typed, without waiting for carriage return.  Backspace and
other control characters (including the carriage return itself) lose
their meaning.

\item {\bf nocbreak()}:  

Restores normal mode.

\item {\bf noecho()}:  

Turns off echoing of the input characters to the screen.

\item {\bf echo()}:  

Restores echo.

\item {\bf clear()}:  

Clears screen, and places cursor in upper-left corner.

\item {\bf move(int, int)}:  

Moves the cursor to the indicated row and column.

\item {\bf addch(char)}:  

Writes the given character at the current cursor position, overwriting
what was there before, and moving the cursor to the right by one
position.

\item {\bf insch(char)}:  

Same as {\bf addch()}, but inserts instead of overwrites; all characters to the
right move one space to the right.

\item {\bf mvaddstr(int, int, *char)}:  

Moves the cursor to the indicated row and column, and then writes the
string to that position.

\item {\bf refresh()}:  

Update the screen to reflect all changes we have requested since the
last call to this function.  Whatever changes we make, e.g. by calling
{\bf addch()} above, will NOT appear on the screen until we call {\bf
refresh()}.

\item {\bf delch()}:  

Delete character at the current cursor position, causing all characters
to the right moving one space to the left; cursor position does not
change.

\item {\bf int getch()}:  

Reads in one character from the keyboard.

\item {\bf char inch()}:  

Returns the character currently under the cursor.

\item {\bf getyx(WINDOW *, int, int)}:

Returns in the two {\bf int}s the row and column numbers of the current
position of the cursor for the given window.\footnote{Again, this is a
macro, so these are {\bf int}s rather than pointers to {\bf int}s.}

\item {\bf getmaxyx(WINDOW *, int, int)}:

Returns in the two {\bf int}s the number of rows and columns for the
given window.

\item {\bf scanw()}, {\bf printw()}:  

Works just like {\bf scanf()} and {\bf printf()}, but in a {\tt curses}
environment.  Avoid use of {\bf scanf()} and {\bf printf()} in such an
environment, which can lead to bizarre results.  Note that {\bf
printw()} and {\bf scanw()} will do repeated {\bf addch()} calls, so
they will overwrite, not insert.  Note too that {\bf scanw()} scans
until it encounters a newline or Enter character, which is also true for
{\bf wgetstr()}.

\item {\bf attron(const)}, {\bf attroff()const}:  

Turns the given attribute on or off.

\end{itemize}

\subsection{Attributes}

Characters can be displayed in various manners, such as normal ({\bf A\_NORMAL})
and bold ({\bf A\_BOLD}).  They can be controlled via the APIs {\bf
attron()} and {\bf attroff()}.  When the former is called, all writing
to the screen uses the attribute specified in the argument, until the
latter is clled.

\section{To Learn More}
\label{more}

The first level of help is available in the man pages.  Type

\begin{Verbatim}[fontsize=\relsize{-2}]
man curses
\end{Verbatim}

(you may have to ask for {\bf ncurses} instead of {\bf
curses}).  The individual functions have man pages too.

There are many good tutorials on the Web.  Plug ``curses tutorials'' or
``ncurses tutorials'' into your favorite search engine.  A tutorial you
might start with is at
\url{www.linux.com/howtos/NCURSES-Programming-HOWTO/index.shtml}.

Note, though, that some of these are tutorials for non-C usage, e.g. for
Perl or Python.  I don't recommend these (including my own, for Python).
Not only is there different syntax but also the APIs often have some
differences.

\end{document}


