Breaking
June 17, 2025

A Gentle Introduction to Ncurses for the Terminally Impatient Maya Posch | usagoldmines.com

Considered by many to be just a dull output for sequential text, the command-line terminal is a veritable canvas to the creative software developer. With the cursor as the brush, entire graphical user interfaces can be constructed, or even a basic text-based dashboard on which values can be updated without redrawing the entire screen over and over, or opting for a much heavier solution like a GUI.

Ncurses is one of the most well-known and rather portable Terminal User Interface (TUI) libraries using that such cursor control, and more, can be achieved in a fairly painless manner. That said, for anyone coming from a graphical user interface framework, the concepts and terminology with ncurses and similar can be confusingly different yet overlapping, so that getting started can be somewhat harrowing.

In this article we’ll take a look at ncurses’ history, how to set it up and how to use it with C and C++, and many more languages supported via bindings.

Tools And Curses

The acronym TUI is actually a so-called retronym, as TUIs were simply the way of life before the advent of bitmapped, videocard-accelerated graphics. In order to enable more than just basic, sequential character output, the terminal had to support commands that would move the cursor around the screen, along with commands that affect the way text is displayed. This basic sequence of moving the cursor and updating active attributes is what underlies TUIs, with the system’s supported character sets determining the scope of displayed characters.

Ncurses, short for “new curses“, is an evolution of the curses library by Ken Arnold as originally released in 1978 for BSD UNIX, where it saw use with a number of games like Rogue. Originally it was a freely distributable clone of System V Release 4.0 (SVr4) curses by the time of its release in 1993, based on the existing pcurses package. Later, ncurses adopted a range of new features over the course of its subsequent development by multiple authors that distinguished it from curses, and would result in it becoming the new de-facto default across a wide range of platforms.

The current version is maintained by Thomas Dickey, and the ncurses library and development files are readily available from your local package manager, or downloadable from the ncurses website. Compiling and running ncurses-based application is straightforward on Linux, BSD, and MacOS courtesy of the libncurses and related files being readily available and often already installed. On Windows you can use the MinGW port, with MSYS2 providing an appropriate terminal emulator, as well as the pacman package manager and access to the same ncurses functionality as on the other platforms.

Hello Curses

The core ncurses functionality can be accessed after including the ncurses.h header. There are two standard extensions in the panel.h and menu.h headers for panel stack management and menus, respectively. Panels are effectively wrappers around an ncurses window that automate a lot of the tedious juggling of multiple potentially overlapping windows. The menu extension is basically what it says on the tin, and makes creating and using menus easier.

For a ‘hello world’ ncurses application we’d write the following:

This application initializes ncurses before writing the Hello World! string to both the top left, at (2, 2) and the center of the terminal window, with the terminal window size being determined dynamically with getmaxyx(). The mvprintw() and mvwprintw() work like printf(), with both taking the coordinates to move the cursor to the indicated position in row (y), column (x) order. The extra ‘w’ after ‘mv’ in the function name indicates that it targets a specific window, which here is stdscr, but could be a custom window. Do note that nurses works with y/x instead of the customary x/y order.

Next, we use attributes in this example to add some color. We initialize a pair, on index 1, using predefined colors and enable this attribute with attron() and the COLOR_PAIR macro before printing the text. Attributes can also be used to render text as bold, italic, blinking, dimmed, reversed and many more styles.

Finally, we turn the color attribute back off and wait for a keypress with getch() before cleaning up with endwin(). This code is also available along with a Makefile to build it in this GitHub repository as hello_ncurses.cpp. Note that on Windows (MSYS2) the include path for the ncurses header is different, and you have to compile with the -DNCURSES_STATIC define to be able to link.

Here the background, known as the standard screen (stdscr) is used to write to, but we can also segment this surface into windows, which are effectively overlays on top of this background.

Multi-Window Application

The Usagi Electric 1 (UE1) emulator with ncurses front-end.
The Usagi Electric 1 (UE1) emulator with ncurses front-end.

There’s more to an ncurses application than just showing pretty text on the screen. There is also handling keyboard input and continuously updating on-screen values. These features are demonstrated in e.g. the emulator which I wrote recently for David Lovett’s Usagi Electric 1 (UE1) vacuum tube-based 1-bit computer. This was my first ever ncurses project, and rather educational as a result.

Using David’s QuickBasic-based version as the basis, I wrote a C++ port that differs from the QB version in that there’s no single large loop, but rather a separate CPU  (processor.cpp) thread that processes the instructions, while the front-end (ue1_emu.cpp) contains the user input processing loop as well as the ncurses-specific functionality. This helps to keep the processor core’s code as generic as possible. Handling command line flags and arguments is taken care of by another project of mine: Sarge.

This UE1 front-end creates two ncurses windows with a specific size, draws a box using the default characters and refreshes the windows to make them appear. The default text is drawn with a slight offset into the window area, except for the ‘title’ on the border, which is simply text printed with leading and trailing spaces with a column offset but on row zero.

Handling user input with getch() wouldn’t work here, as that function is specific to stdscr and would foreground that ‘window’. Ergo we need to use the following: int key = wgetch(desc). This keeps the ‘desc’ window in focus and obtains the key input from there.

During each CPU cycle the update_display() function is called, in which successive mvwprintw() calls are made to update on-screen values, making sure to blank out previous data to prevent ghosting, with clrtoeol() and kin as the nuclear option. The only use of attributes is with color and bold around the processor state, indicating a running state in bold green and halted with bold red.

Finally, an interesting and crucial part of ncurses is the beep() function, which does what it says on the tin. For UE1 it’s used to indicate success by ringing the bell of the system (inspired by the Bendix G-15), which here provides a more subtle beep but can be used to e.g. indicate a successful test run. There’s also the flash() function that unsurprisingly flashes the terminal to get the operator’s attention.

A Much Deeper Rabbit Hole

By the time that you find yourself writing an ncurses-based application on the level of, say, Vim, you will need a bit more help just keeping track of all the separate windows that you will be creating. This is where the Panel library comes into play, which are basically wrappers for windows that automate a lot of the tedious stuff such as refreshing windows and keeping track of the window stack.

Applications also love to have menus, which can either be painstakingly created and managed using core ncurses features, or simplified with the Menu library. For everyone’s favorite data-entry widget, there is the Forms library, which provides not only the widgets, but also provides field validation features. If none of this is enough for your purposes, then there’s the Curses Development Kit (CDK). For less intensive purposes, such as just popping up a dialog from a shell script, there is the dialog utility that comes standard on Linux and many other platforms and provides easy access to ncurses functionality with very little fuss.

All of which serves to state that the ground covered in this article merely scratches the surface, even if it should be enough to get one at least part-way down the ncurses rabbit hole and hopefully appreciative of the usefulness of TUIs even in today’s bitmapped GUI world.

Header image: ncurses-tetris by [Won Yong Jang].

 

This articles is written by : Nermeen Nabil Khear Abdelmalak

All rights reserved to : USAGOLDMIES . www.usagoldmines.com

You can Enjoy surfing our website categories and read more content in many fields you may like .

Why USAGoldMines ?

USAGoldMines is a comprehensive website offering the latest in financial, crypto, and technical news. With specialized sections for each category, it provides readers with up-to-date market insights, investment trends, and technological advancements, making it a valuable resource for investors and enthusiasts in the fast-paced financial world.