Adam Richardson's Site

ncurses Notes

Table of Contents

<2022-05-01 Sun>

What is ncurses

"new curses" is a library that allows you to manipulate the character display of your terminal. Curses originally was pun on "cursor optimization". It is primarily used to create nice UIs in text mode terminals.

Resources

Basic ncurses program

Setup

To initialize ncurses you should call initscr

Tear down

Make sure to call endwin at the end of your program to clean up ncurses

Hiding user key presses

Use noecho and echo to enable or disable the keys the user types from showing up in the terminal.

Configuring the cursor visibility

Use curs_set to set the visibility level of the cursor. It can be invisible <0>, visible <1>, or very visible <2>. Not all terminals support this feature (for instance term.el in Emacs always shows the cursor no matter what this is set to).

Refreshing

You will need to call refresh to flush the back buffer to the terminal

Printing to the screen

You can use printw or mvprintw to print a string to the screen. printw uses the current cursor position. mvprintw allows you to specify where to print the string in the terminal. Caution the ncurses functions that accept a coordinate pair want the y coordinate before the x, always double check the manpage. The ncurses print functions support formats the same way printf does.

Moving the cursor

Use the move function to set the cursor position in the terminal. The y coordinate is the first argument and represents the number of characters down from the top. The second argument is x and represents the number of characters from the left.

Waiting for user input

If you want to wait for the user to press a key before continuing use the getch function. This function will also return the char that the user typed on the keyboard.

Clearing the screen

Use the clear function to clear the screen.

Code

#include <ncurses.h>

int main (int argc, char **argv) {
  char c;
  int x, y;

  x = 2;
  y = 3;

  initscr();
  noecho();
  curs_set(0);

  move(y, x);
  printw("Hello");
  mvprintw(y + 1, x + 5, "ncurses!");

  refresh();

  c = getch();
  clear();
  mvprintw(0, 0, "You pressed char code: %d", c);
  mvprintw(2, 0, "Press any key to exit");
  refresh();

  getch();

  endwin();

  return 0;
}

Compiling the code

#!/bin/sh
set -e

../../config/tangle.sh ncurses.org
cd ~/tmp
gcc -o basic_ncurses basic_ncurses.c -lncurses

Using Windows

Windows

In ncurses bounding rectangles are known as windows. A window can be created with the newwin function. Like most ncurses functions y comes before x and height comes before width when specifying the rectangle. The newwin function should be called after initscr.

Drawing a box border around a window

Use the box function to draw a box border around a window. This will clear any characters that might be on the screen when you call it so make sure to draw the box before putting any content into the window. The box function accepts the window as well as the vertical and horizontal characters to use when drawing the border. Use zeroes as the arguments if you just want to use the terminal default border.

Refreshing the window

An individual window can be refreshed with wrefresh. This function takes the window as an argument. Refreshing the window is needed anytime you draw to the window. This includes box or any of the w* functions.

Drawing in the window

There are window equivalents of all the normal ncurses draw functions. The functions that accept coordinates will be relative to the window.

Code

#include <ncurses.h>

int main (int argc, char **argv) {
  int c;

  WINDOW *char_win;
  WINDOW *code_win;

  initscr();
  noecho();
  curs_set(0);

  char_win = newwin(5, 10, 2, 2);
  code_win = newwin(5, 10, 2, 14);

  refresh();

  box(char_win, 0, 0);
  mvwprintw(char_win, 0, 2, "char");
  wrefresh(char_win);

  box(code_win, 0, 0);
  mvwprintw(code_win, 0, 2, "code");
  wrefresh(code_win);

  printw("Press q to exit");
  refresh();

  do {
    c = getch();

    wclear(char_win);
    box(char_win, 0, 0);
    mvwprintw(char_win, 0, 2, "char");
    mvwprintw(char_win, 2, 4, "%c", c);
    wrefresh(char_win);

    wclear(code_win);
    box(code_win, 0, 0);
    mvwprintw(code_win, 0, 2, "code");
    mvwprintw(code_win, 2, 4, "%d", c);
    wrefresh(code_win);
  } while (c != 'q');

  endwin();

  return 0;
}

Compiling the code

#!/bin/sh
set -e

../../config/tangle.sh ncurses.org
cd ~/tmp
gcc -o win_ncurses win_ncurses.c -lncurses

Attributes

Use the attron and attroff function to set the desired attributes. Use bit-wise or to combine multiple attributes. Some examples of the attributes you can can are underline, bold, reverse (swap foreground and background colors), etc. You can see a complete table of the attributes in the manpage for attron.

Colors

If a terminal supports colors you can change the color of various characters. The has_colors function is a good check to run to see if a terminal supports various colors. If you want to use colors and your terminal supports it use start_color function to initialize color support. From there color pairs (foreground, background) need to be defined with the init_pair. When defining a color pair you will specify and id. This id will be used in the attribute COLOR_PAIR to specify which color pair you are using. Some terminals also support redefining colors. You can use can_change_color to see if you can redefine the color.

Example

....
if (!has_colors()) {
  printw("Colors not supported");
  getch();
  endwin();
  return -1;
}

start_color();

init_pair(1, COLOR_CYAN, COLOR_WHITE);

attron(COLOR_PAIR(1));
printw("Light blue");
attroff(COLOR_PAIR(1));

mvprintw(2, 0, "Press any key to continue");
getch();
...

Querying Cursor position

You can query the size, cursor position, and top left offset of a window using getmaxyx, getxy and getbegxy. Each of these functions accepts a window as the argument but if you want to query the default window use stdscr. The getmaxyx using the stdscr is a good way to get the size of the current terminal window. These functions are actually macros and they accept as arguments the x and y variables to hold the return values. Since they are macros you do not need to pass by reference to them. So don't do this getmaxy(stdscr, &x, &y) but rather do this getmaxy(stdscr, x, y).

Reading non char keys

When you run getch normally it will not return usefule values for the arrow or function keys. In order to get this information you need to call the keypad function. Once enabled you can use wgetch to compare the returned key with KEY_UP or KEY_LEFT. All the available keys are listed in curses.h.