Previous: Debugging A Running Process | Next: Other Stuff |
Activities like printing characters to a screen, moving the cursor, and changing the color of character output are collectively known as screen handling. By its nature, screen handling is very terminal dependent, however, the terminfo and termcap mechanisms were devised to provide terminal independent screen handling. The curses library (a pun on the term "cursor optimization") was created to provide a screen handling API for C programmers. The goal of curses was to provide a fast, portable, and terminal independent C API to handle device dependent terminal codes.
Curses has a very long and twisted history. However, the most commonly used modern implementation of the library is called new curses, or ncurses, for short, which is maintained by a('dickey.his.com', 'Thomas E. Dickey') ?>. Ncurses is a GNU project released under an MIT style licence and is used under nearly all modern Unixes including GNU/Linux, and Mac OS X. There are now many extensions to ncurses which includes panels, menus and even a full featured widget set: the Curses Development Kit (CDK).
To follow along, download ncurses1.
1 // ncurses1.c 2 #include<ncurses.h> 3 #include<stdlib.h> 4 #include<time.h> 5 6 unsigned int Seeder(void); 7 int Irand(int low, int high); 8 void Print_A_Character(void); 9 10 int main(void) 11 { 12 atexit( (void *)endwin ); 13 initscr(); 14 Seeder(); 15 16 for (int i = 0; i < 500000; ++i) 17 Print_A_Character(); 18 19 return 0; 20 } 21 22 23 void Print_A_Character(void) 24 { 25 int x = Irand(1, COLS); 26 int y = Irand(1, LINES); 27 unsigned ascii = Irand('A', 'z'); // ASCII dependent 28 mvaddch(y, x, ascii); 29 refresh(); 30 } 31 32 33 int Irand(int low, int high) 34 { 35 return low + (int)( (double)(high-low) * rand()/(RAND_MAX + 1.0) ); 36 } 37 38 39 unsigned int Seeder(void) 40 { 41 time_t seed; 42 time(&seed); 43 srand((unsigned)seed); 44 45 return seed; 46 }
Compile and run the program. It should fill your console (or xterm) with characters. It has a bug though: the top row and first column seem to be devoid of characters:
Since the probability of that happening is miniscule (and gets smaller with each passing second), there must be a bug in the program.
You need to do a bit more to use GDB with a program that uses ncurses. The problem is that GDB's I/O is intermixed with the program's I/O. Once you get used to it, this is not normally a problem. But when the program performs screen handling, it becomes difficult, if not impossible, to keep track of your debugging session. To see this in action, start GDB on the executable, set a breakpoint at Print_A_Character(), and run the program.
$ gdb debugging_ncurses (gdb) break Print_A_Character Breakpoint 1 at 0x80486fd: file debugging_ncurses.c, line 26. (gdb) run Starting program: code/ncurses/debugging_ncurses Breakpoint 1, Print_A_Character () at debugging_ncurses.c:26 26 int x = Irand(1, COLS);
Now issue continue 50 a few times. You should see a big mess. Here's what I see:
Quit GDB when you've had enough. Clearly, we need a way to separate GDB's I/O from the program's I/O when screen handling is done.
You'll need two terminals (either two consoles or two xterms): One for the program's I/O and another for GDB's I/O. Separating out the two I/O will resolve the problem nicely. I'll be using the word `xterm', but the same thing applies to all non-login terminals like rxvt and eterm, and login terminals like virtual consoles.
$ tty /dev/pts/1 $ who am i p pts/1 May 26 12:44 (:0.0)
$ tty /dev/pts/4
$ gdb debugging_ncurses (gdb) break Print_A_Character Breakpoint 1 at 0x80486fd: file debugging_ncurses.c, line 26. (gdb)
(gdb) tty /dev/pts/4 (gdb)
$ tty /dev/pts/4 $ sleep 100000
Let's go through a sample debugging session of debugging_ncurses.c. The problem was that the first row and column aren't being printed to. At first guess, we might suspect that the random number generator is at fault.
Under construction