Introduction to ncurses

New curses (ncurses) is an openly distributable library of functions that deals with an application's user interface (UI) in text mode. It creates a wrapper over terminal abilities. ncurses is a free software, it is not an open source. It provides functions to make imaginary screen windows for logical calculations, print windows, and so on. Libraries for panels, menus, and forms use the ncurses library and extend the basic functionality of ncurses as required. We can make applications that contain numerous windows, menus, panels, and forms; windows can be overseen autonomously, can give scrollability, and can even be covered up.

Menus give the client a simple order determination choice, forms permit the formation of easy−to−use information sections and show windows, and panels stretch out the abilities of ncurses to manage covering and stacked windows. These are a portion of the essential things we can do with ncurses. As we progress, we will see how to build a native library using C++.

ncurses comes with the Visual Studio Code installation and we can download ncurses-6.0.tar.gz, from http://ftp.gnu.org/pub/gnu/ncurses/. After downloading, unzip and install ncurses 6.0 (the latest version at the time of writing this book):

Open the Terminal from the folder, ncurses-6.0, and run the following commands to install ncurses:

  • ./configure: Configure the build according to your environment
  • make: Make it
  • su root: Become root
  • make install: Install it

Let's start with a simple application, "Welcome to .NET Core 2.0", to understand how to start, use, alter, and close ncurses:

#include <ncurses.h>

Void WelcomeMessage()
{
initscr(); /* Start ncurses mode */
printw("Welcome to .NET Core 2.0"); /* Print welcome message */
Move(3, 2); /* moves the cursor to 3rd row and 2nd column */
Addch('a' | A_Bold | A_UNDERLINE); /* Move() and addch() functions
can be replaced by mvaddch(row,col,ch); */
refresh(); /* Print it on to the real screen */
getch(); /* Wait for user input */
endwin(); /* End curses mode */
}
int main()
{
WelcomeMessage();
return 0;
}

Write a function called WelcomeMessage() which returns void, and call it from main, as shown in the example. Let's try to understand each function that we called inside the WelcomeMessage function:

  • Initscr(): This function initiates the Terminal in curses mode. It is typically the main curses method to call while introducing a program. A couple of uncommon methods, once in a while, should be called before it; these are slk_init(), channel(), ripoffline(), and use_env(). For different Terminal applications, newterm() might be called before initscr(). The initscr() code decides the Terminal sort and instates all curses data structures. initscr() likewise makes the main call, refresh(), to clear the screen. On the off chance that errors arise, initscr() composes an error message to standard error and exits; otherwise, a pointer comes back to stdscr. A program that yields to more than one Terminal should utilize the newterm() routine for every Terminal, rather than initscr(). The method newterm() ought to be called once for every Terminal. 
  •  printw("Welcome to .NET Core 2.0"): This prints " Welcome to .NET Core 2.0" on the screen. printw() is a class of functions:
    • printw(): This function works similar to printf() with the exception that it prints the information on a window called stdscr and with the added capability of printing at any position on the screen at the current (y, x) coordinates. If the cursor is at coordinates (0,0), the string is printed at the left corner of the window.
    • mvprintw(): This function can be used to move the cursor to a position and then print. If you want to move the cursor first and then print using the printw() function, use move() first and then use printw(). I think instead of using move() and printw(), using mvprintw() is better, because you get the flexibility to manipulate.
    • wprintw() and mvwprintw(): These two functions are similar to the preceding two functions, except that they print in the corresponding window given as an argument.
    • vwprintw(): This function is similar to vprintf(). This can be used when a variable number of arguments are to be printed.

The earlier program demonstrates how easy it is to use printw. You just feed the message to be shown on the screen, then it does what you want. When we call printw, the information is really composed for an imaginary window, which isn't refreshed on the screen yet. The activity of printw is to refresh a couple of banners and information structures and compose the information to a support as compare to stdscr(). With a specific end goal of demonstrating it on the screen, we have to call refresh(), and advise the curses framework to dump the substance on the screen. The logic behind this is to enable the software engineer to do different updates on the imaginary screen or windows, and do a revive once all his/her screen refresh is finished.

All these functions take the  y coordinate first and after that,  x in their arguments. A typical slip up by beginners is to pass x, y in a specific order. In the event that you are doing an excess amount of manipulations of ( yx) coordinates, consider isolating the screen into windows and control every one independently.
  • Move(row,col): The function move() moves the cursor to the desired position by passing row and column values.
  • Addch(ch | A_attribute): This function is utilized to print a character at the current cursor position on the stdscr window. We can pass attributes to print characters based on the attribute, for instance, addch(ch |A_Bold |A_underline) will print characters in bold and underlined. The following specified attributes characterized in <ncurses.h> can be passed to functions attron(), attroff(), and the attrset() , attron() functions turn on the attribute, and correspondingly attoff() turns off the attribute. To set the quality for a window, we should utilize attrset(), for instance, in the event that we compose attrset(A_NORMAL), it sets a typical show with no feature for a window, and it turns off all attributes.
    We can use multiple attributes at the same time using OR (|) with the characters passed to addch(). They are as shown in the following table:

    If we want to print a character in a specific location or cursor position, we can use the following functions, instead of using the move() and addch() functions:

    • mvaddch(): This function is used to print characters at the desired location by passing the cursor location, for example, mvaddch(row,col,ch)
    • waddch(): This function is useful for printing characters at the present cursor location in a specified window
    • mvwaddch(): This function is used to print characters at a definite cursor location in a specific window by passing row, column, character, and window
  • refresh(): This function checks the window and updates only the bit which has been changed. This improves execution and offers more significant flexibility. A beginners' common error is to not call refresh() after they did some refreshing through the printw() class of functions.
  • getch(): This function sits tight for the client to press a key, unless you stipulated a timeout, and when the client presses a key, the matching integer number gets returned. At that point, we can check that the value came back with the constants characterized in curses.h, to compare against the keys you need. On the off chance that it is a normal character, the integer number value will be equal to the character, otherwise it restores a number which can be coordinated with the constants characterized in curses.h. For instance, if the client presses F1, the number returned is 265. This can be checked utilizing the full scale KEY_F(), characterized in curses.h. This makes reading keys convenient and simple to oversee.
  • endwin(): This function ends the curses mode, else our Terminal may behave unusually after the program stops. endwin() frees up the memory taken by the curses subsystem and its data structures, and puts the Terminal in normal mode. This function must be called when we are finished with the curses mode.
    A program should reliably call endwin() before quickly leaving or closing curses mode. This function restores tty modes, moves the cursor to the lower left-hand corner of the screen, and resets the Terminal into the best non-visual mode. Calling revive() or doupdate() after a short escape, makes the program go to visual mode. The isendwin() routine returns TRUE if endwin() has been called with no subsequent calls to wrefresh().

To run this program in Ubuntu, ncurses should be installed on the system, otherwise during compilation, we will get an error stating ncurses.h:no such file or directory and compilation will terminate. Use the following command to install ncurses:

sudo apt-get install libncurses5-dev libncursesw5-dev

The preceding command installs the latest version of the ncurses library onto the system. Once installation is done, open Visual Studio Code, go to File, and click on New File—it opens up a new untitled page. Write code as in the earlier example and save it at your desired location. Rename the file and give it an extension of .c or whatever suits your requirements. In this example, we created a file called IntoToNcurses.c. Run the following command to execute this program.

To compile a program, we use the following command syntax:

gcc  <Program_name_with_Extension> -o <Out_FileName> -lncurses

Here is an example:

gcc IntroToNcurses.c -o ExampleOfNcurses -lncurses

The output for the preceding command can be seen in the following screenshot:

In the preceding example, we are passing our C program with the extension .c. After the -o command, we pass the output, or rather the compiled filename. Here, we can give it any name we want, as we did with ExampleOfNcurses in this example. To include the ncurses library, we have to link it during compilation using -lncurses, otherwise the compilation will fail with the error undefined reference to functions, as those functions are ncurses functions.

Once compilation is successful, it creates an output file, as shown in the following screenshot:

We can run our program after the compilation is successful and the output file is created in the same folder. On the Terminal, pass the output filename as ./hello to execute the program, as shown in the following screenshot: