A programming library, is a external collection of code that can be used by other programs, but is not normally standard to the language. Libraries are usually an example of what is called an Application Programmer Interface (API) which is a set of data structures and functions that compose an interface to that libraries functionality, and an important aspect of programming is learning to work with various libraries and their API's so that you don't have to make everything yourself.
There are hundreds of libraries for C, including the standard C library (libc) which is included in your program by default, the C math library (libm) which you need if you want to do more than the most basic of floating point math, such as the sin(),cos(),pow() functions, etc. And there is the curses (or ncurses) library (libncurses) which we'll use to "draw" (with characters) on the terminal window.
sin()
cos()
pow()
To include a library in your program you need to usually include it's header file (#include <math.h> for libm, #include <curses.h> for libncurses, etc.) and then link the library to your program with -llib-name (minus the lib prefix, so -lm for the math library, -lncurses for the curses library) appended to your gcc command, so to make a program that needs both the math and ncurses library you'd type:
#include <math.h>
#include <curses.h>
-l
lib
-lm
-lncurses
gcc -o prog prog.c -lm -lncurses
Curses is a library that allows for optimal updating of character based terminal screens and allows for dynamic placing of text on the screen.
Your terminal screen is a 2 dimensional matrix of characters formatted into lines and columns. A typical 80x25 terminal window might be formatted like:
┌──────────────────────────── COLS ───────────────────────────┐ 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 7 7 7 7 7 7 7 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 3 4 5 6 7 8 9 ┌ ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬ ┬─┬─┬─┬─┬─┬─┬─┐ │ 00│H│e│l│l│o│,│ │w│o│r│l│d│!│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼ ┼─┼─┼─┼─┼─┼─┼─┤ │ 01│█│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼ ... ┼─┼─┼─┼─┼─┼─┼─┤ │ 02│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼ ┼─┼─┼─┼─┼─┼─┼─┤ │ 03│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ LINES ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼ ┼─┼─┼─┼─┼─┼─┼─┤ │ . . . │ . . . │ . . . │ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼ ┼─┼─┼─┼─┼─┼─┼─┤ │ 23│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼ ... ┼─┼─┼─┼─┼─┼─┼─┤ │ 24│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └ └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴ ┴─┴─┴─┴─┴─┴─┴─┘
The origin of the screen is the upper left corner which in curses is at position 0,0. Unlike in geometry curses uses the line # followed by the column number, so (0,10) would be the 'l' character in the above example screen or row 0, line 10.
l
The cursor on your terminal represents where the next character will be printed. Curses provides means of moving the cursor to wherever you want prior to actually printing anything. In the above example it is currently sitting at line 1, column 0 and anything that is printed will start printing at that location.
Curses also maintains two global variables called:
LINES -- the total number of lines on the screen, and COLS -- the total number of columns
LINES
COLS
which inform you about the size of your screen, which can be resized, even as the program runs (in which case LINES and COLS will change.)
Because curses controls everything (or at least attempts to,) that is seen on the terminal screen. To that end curses replaces all normal C library input and output functions with its own equivalents which you should always use when programming in curses. The equivalent functions are:
putchar
addch
mvaddch
printf
printw
mvprintw
getchar
getch
mvgetch
scanf
scanw
mvscanw
fgets
getnstr
mvgetnstr
mv
In curses there are two "screens" that curses maintains. The actual screen that you see and a virtual screen that all drawing operations are performed to. When a refresh() call is made the real screen is made to look like the virtual screen in as optimal a way as possible.
refresh()
┌────────────────────┐ │ Virtual Screen │ ┌──┴────────────────┐ │ │ │ │ │ Real screen │ │ │ ├───┘ │ │ └───────────────────┘
In graphics programming the virtual screen is a common programming practice, the computer draws the next scene on the virtual screen which then is made the real screen all at once. This is often called double buffering.
Use the following include in your source: #include <curses.h>
To compile your C program with curses, you must link it with the ncurses library: gcc -o program program.c -lncurses
gcc -o program program.c -lncurses
To initialize the curses library in your program you should use the following functions:
initscr();
cbreak();
nodelay(stdscr, TRUE);
noecho();
keypad(stdscr, TRUE);
When the program is done, the following should be called:
echo();
noecho()
nocbreak();
cbreak()
endwin();
When curses has been initialized, you may then begin "drawing" on the screen. Again, the global integers LINES and COLS are set to the height and width (in characters) of the terminal screen, but not before initscr() is called.
initscr()
#include <curses.h> #include <string.h> // Compile with: // gcc -o curses_hello curses_hello.c -lncurses int main(void) { // Define our message and it's "width" in characters: char *message = "Hello, world!"; int mesglen = strlen(message); // Initialize curses: initscr(); // Clear the virtual screen: clear(); // Print the message in the middle of the virtual screen: // The message is centered on the line by subtracting half the width of the // message from the middle column of the screen: mvprintw(LINES/2, (COLS/2)-(mesglen/2), "%s", message); // Updates the screen to look like the virtual screen: refresh(); // Wait for a key-press (get a character from the user.) getch(); // Shutdown curses: endwin(); return 0; }
Enter the programs found here, compile them and run them, particularly the etch-a-sketch.c found below.
Change some things and see what happens, for example:
int refresh(void);
int clear(void);
int move(int y, int x);
int addch(const chtype ch); int mvaddch(int y, int x, const chtype ch);
int addch(const chtype ch);
int mvaddch(int y, int x, const chtype ch);
int printw(const char *fmt, ...); int mvprintw(int y, int x, const char *fmt, ...);
int printw(const char *fmt, ...);
int mvprintw(int y, int x, const char *fmt, ...);
Notes:
Arrow keys: KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT read: man getch for a complete list.
KEY_UP
KEY_DOWN
KEY_LEFT
KEY_RIGHT
man getch
int getch(void);
int getnstr(char *str, int n); int mvgetnstr(int y, int x, char *str, int n);
int getnstr(char *str, int n);
int mvgetnstr(int y, int x, char *str, int n);
Example: char str[31]; mvgetstr(str,30); // Reads a string into s up to 30 characters in length
char str[31];
mvgetstr(str,30); // Reads a string into s up to 30 characters in length
int scanw(const char *fmt, ...); int mvscanw(int y, int x, const char *fmt, ...);
int scanw(const char *fmt, ...);
int mvscanw(int y, int x, const char *fmt, ...);
Example: int n; mvscanf(10,5,"%d", &n); Move to line 10, column 5 and read a number into n.
Example: int n; mvscanf(10,5,"%d", &n);
int n;
mvscanf(10,5,"%d", &n);
#include <curses.h> #include <string.h> // Compile with: // gcc -o form form.c -lncurses // Prints the string in 'mesg' in the center of the given 'line': void center(int line, char mesg[]) { mvprintw(line, COLS/2-strlen(mesg)/2, mesg); } int main(void) { int y, x; initscr(); clear(); // Reads the X and Y coordinates from the user: mvprintw(10,10, "Input X: "); scanw("%d", &x); mvprintw(11,10, "Input Y: "); scanw("%d", &y); clear(); // Note that the y coordinate (the line) is always specified before the x // coordinate (the column) in curses functions that take coordinates: mvprintw(y, x, " <- %d,%d is there", x, y); center(LINES-1, "Press any key to exit"); refresh(); // Wait for the user to type a character: getch(); endwin(); return 0; }
#include <stdio.h> #include <curses.h> // Compile with: // gcc -o etch-a-sketch etch-a-sketch.c -lncurses int main(void) { initscr(); // Disables echo of keys typed: noecho(); // Turns off line buffering (so we can get key-presses immediately): cbreak(); // Enable arrow keys: keypad(stdscr, TRUE); // Define our software cursor position: int x = 1, y = 1; // Clears the screen: clear(); // Draws a nice box around the entire screen: border(0,0,0,0,0,0,0,0); // moves the cursor to (1,1): move(y,x); refresh(); // Loops forever (a non-zero number is always "true"): while (1) { // Read a character from curses: int ch = getch(); // Exit the loop on a 'q' or 'Q': if (ch == 'q' || ch == 'Q') break; // Clear the screen when a 'c' or 'C' is typed: if (ch == 'c' || ch == 'C') { // Clearing the screen, moves the cursor back to 0,0: clear(); // Restore the cursor to where it should be: move(y,x); refresh(); continue; } // Check for movement keys and update y,x, making sure the coordinates stay // inside of the screen if (ch == KEY_UP || ch == 'w') { if (y > 0) y--; } if (ch == KEY_DOWN || ch == 's') { if (y < LINES-1) y++; } if (ch == KEY_LEFT || ch == 'a') { if (x > 0) x--; } if (ch == KEY_RIGHT || ch == 'd') { if (x < COLS-1) x++; } // Adds "reversed" space character at the cursor position, we'll talk about // "attributes" like A_REVERSE next time. addch(' '|A_REVERSE); // addch moves the cursor forward, so put the cursor back: move(y,x); refresh(); } // Shutdown: echo(); nocbreak(); endwin(); return 0; }