Curses Applications on Interix

Microsoft Services for UNIX 3.0

On This Page

Overview: Curses Applications on INTERIX Overview: Curses Applications on INTERIX
Curses Overview Curses Overview
Elements of Curses Elements of Curses
Elements of Curses 1 of 5 Elements of Curses 1 of 5
Elements of Curses 2 of 5 Elements of Curses 2 of 5
Elements of Curses 3 of 5 Elements of Curses 3 of 5
Elements of Curses 4 of 5 Elements of Curses 4 of 5
Elements of Curses 5 of 5 Elements of Curses 5 of 5
Environment for Curses on INTERIX Environment for Curses on INTERIX
Curses Functionality on INTERIX Curses Functionality on INTERIX
Curses on INTERIX Curses on INTERIX
Demo 1: Basic Window Handling Demo 1: Basic Window Handling
Demo 1: Example Code Demo 1: Example Code
Building and Running Demo 1 Building and Running Demo 1
Color Support on INTERIX Color Support on INTERIX
Panels on INTERIX Panels on INTERIX
Menus on INTERIX Menus on INTERIX
Support for Multiple Screen Highlights Support for Multiple Screen Highlights
Mouse Interfacing on INTERIX Mouse Interfacing on INTERIX
Debugging on INTERIX Debugging on INTERIX
Prerequisites for Porting Prerequisites for Porting
Curses-Specific Porting Issues Curses-Specific Porting Issues
Functions and Macros Missing in INTERIX Functions and Macros Missing in INTERIX
Demo 2 : A Comprehensive Demo 1/2 Demo 2 : A Comprehensive Demo 1/2
Demo 2 : A Comprehensive Demo 2/2 Demo 2 : A Comprehensive Demo 2/2

Overview: Curses Applications on INTERIX

  • Overview of Curses environment

    • What is curses?

    • Understanding the elements of curses

  • Curses on INTERIX

    • Physical environment

    • Curses functionality on INTERIX

  • Strategy for porting Curses code on INTERIX

    • Pre-requisites for porting curses application
  • Porting a sample Curses program

    • Compiling and Building the application

    • Guidelines for porting

    • Running Curses applications on MS Windows

Objectives

This section provides a brief introduction to curses and guidelines for porting curses application code to the INTERIX development environment.

What you will learn

Fundamentals of Curses-based GUI applications

  • Requirements for migrating a curses application to run on Windows

  • Guidelines and issues in migration

Prerequisites

It is essential to have a basic understanding of INTERIX development environment before going through this section.

The following references are useful in understanding the Curses programming environment:

Programming with Curses

John Strang, 1986. ISBN :0-937175-02-1

The following references are useful in understanding INTERIX:

Services For Unix (INTERIX) Overview

Microsoft Consulting Services

Curses Overview

  • What is Curses?

    • UNIX library of functions for controlling a terminals video display screen from a 'C program'

    • Provides screen driver for a program. For example, visual editor (vi)

    • curses provides terminal independent features, that is the curses behavior is same across different type of terminals

    • Most commonly used functions in curses are:

      • Insert text anywhere on the screen

      • Divide the screen into regular areas called windows

      • Manage each window independently, that is, scroll in one window while erasing a line in another

      • Move the cursor to any point in the screen

      • Drawing box around the window using choice of characters

Curses is the UNIX library of functions for controlling a terminal's video display screen from a C program. Curses can be used to provide a screen driver for a program, such as a visual editor, or to improve a program's user interface. This library supplies a C programmer with optimized cursor motion, physical terminal independence, multiple windows, and video attributes.

Curses are an easy way to write programs that use the whole screen. It is a library of compiled procedures that do the work of managing the whole screen. Curses reads the terminal information at runtime and provides terminal-independent behavior. Some of the most commonly used function categories are listed on the above slide.

Elements of Curses

Elements of Curses 1 of 5

  • Windows and Screens

    • Window is an independent rectangular area of characters displayed on the screen

    • A screen is also represented as WINDOW.

    • A WINDOW is a character array that maintains an image of the terminal screen, known as screen image

  • A window is C data structure, called WINDOW, that holds all the information about a window. It is an internal representation of window in curses. For example, a sample WINDOW structure is as shown:

struct_win_st { short _cury, curx; /* current coordinates / short _maxy, maxx; / max coordinates / short _begy, _begx; / (0,0) screen coorinates / bool _clear, / clearok() info */ _leave, /*leaveok() info */

 char    _waitc[CSMAX];     /* array to hold partial m-width char */
 bool    _insmode;   /* TRUE for inserting, */
                       /* FALSE for adding */

};

Windows, Screens, and Images

Conceptually, a window is an independent rectangular area of characters displayed on the screen. Physically, a WINDOW structure holds all the information about a window and WINDOW is the only structure used by Curses.

The WINDOW structure will help you learn and remember the Curses commands. In principle, a complete description of the WINDOW structure is impossible because Curses is defined by the command set, not the implementation.

The structure described on the slide is almost universally used. A few extra fields might be added, but the basics remains the same. To see an actual WINDOW structure used in INTERIX, open the curses.h header file under /usr/include.

Elements of Curses 2 of 5

  • The Standard Screen - stdscr

    • Is a standard screen for all the curses application that refers to a physical screen. Stdscr is the window representation in curses of the standard screen.

    • Is a window that files the entire screen of video terminal

    • The screen image array is stdscr is automatically made the length and width of the terminal screen. Initially, its filled with blanks

    • Is created in the process of initialization. It is created when initscr() call is made.

    • There are curses function for putting the character in the image array, some common ones are:

ins ch(), addch(), addstr(), printw()

  - There are a set of functions and macros specifically for the stdscr. They do not have "w" in the beginning of their name

  - Moving the logical cursor
    
      - It's a (y,x) marker in each window
    
      - move (y, x) is command to specify the position for the cursor

stdscr The standard screen

stdscr refers to the "standard screen". It is a window or a set of windows that fills the entire screen of the video display terminal. The structure that describes stdscr is the WINDOW structure. A WINDOW is a character array that maintains an image of terminal screen, known as the screen image. The screen image array in stdscr is automatically made the length and width of the terminal screen. Therefore, there is one character in the array for every place on the screen. A useful analogy is that of a bit mapped terminal, which contains the area of the memory in which each bit corresponds to a pixel on the screen. The bit mapped memory and the character array in stdscr are images of the physical screen.

Initially, the screen image is filled with blanks. As you use Curses, you will put characters in that array. There are curses functions for putting characters into the array. Some of them are listed on the slides.

Normal I/O terminal is sequential. A cursor marks the location where the next character will be printed. The purpose of the windows is to provide a non-sequential I/O. To do this, Curses employs a logical cursor rather than the physical cursor. A logical cursor is a (y, x) marker in the window.

"Logical" describes the concept applied to a window, while "physical" applies to the terminal itself. The co-ordinates itself mark the position of the logical cursor anywhere inside the window screen image. For example, the move (y, x) function is used to move the logical cursor.

There are some functions and macros that are specific to stdscr and windows. An easy way to distinguish them is, stdscr specific function names are not prefixed with w. For example, some of the functions that take WINDOW as the one of the parameters are wrefresh(), waddch(), wgetch(), and wmove(). Some stdscr specific functions are refresh(), addch(), getch(), and move().

Refreshing the screen

Once the characters are inserted into the character array, the characters are displayed on the screen. This is called refreshing the screen, and this is done by the refresh () function.

The screen is refreshed only when the refresh () function is used. You can make a number of changes to the window and have them displayed at once. refresh () calculates the optimal way to change the physical screen, so it looks like the current window*.* A summary of the procedures to follow when using windows is described on the above slide.

The refresh () function tries to minimize the number of characters output on the screen. It sends only the characters that were modified since the last refresh ().

Elements of Curses 3 of 5

  • A normal flow for working with the windows are:

    • Initialize WINDOW to represent window on the screen

    • Insert and move characters in the WINDOW structure

    • Refresh to display the updated window on the screen

    • Make more changes to the window

    • And refresh the screen again

The above slide mentions the normal procedure for working with windows and screens.

Elements of Curses 4 of 5

  • The Current Screen - curscr

    • Like stdscr, curscr is created when initializing curses with initscr()

    • Contains the image of the screen as curses thinks it was made to look by the last refresh()

    • For the purpose of optimization, refresh() is the only function used

  • Multiple windows

    • Multiple windows can be created and maintained using curses

    • In addition to stdscr additional windows can be created by:

      • Dividing an existing window into sub-windows with subwin() or

      • Defining one or more new windows with newwin()

curscr The current screen

curscr is not directly aware of what the terminal is displaying. Also, it would be slow to query the terminal to find out the characters that are being displayed at each position. So what Curses does is, it keeps an image of what it thinks the screen looks like, in a window called curscr.

curscr, like stdscr, is created automatically when you initialize Curses with initscr (). curscr is a WINDOW, and has a screen image of the size of the physical screen. When refresh () is called, refresh () writes the character it is sending to the terminal into the corresponding location in the screen image of the curscr.

The refresh () function uses the screen image of the curscr to minimize its work. While refreshing a window, it compares the contents of that window to curscr. The refresh () function assumes that the physical screen looks like curscr. It does not output characters that are the same in curscr and the window that is being refreshed. In this way, refresh () minimizes the number of characters that it sends to the screen. This saves a lot of time.

Multiple windows

Usually most of the tasks are done in a single window - stdscr. In case you need to create additional windows, you will need to define windows in addition to stdscr.

The new windows can be created either by dividing the existing window into smaller sub-windows using the subwin () function, or by defining one or more new windows using the newwin () function.

Elements of Curses 5 of 5

  • Terminal Independence

    • Means that a curses program can control the display screen of vt100 or vt220 or hundreds of other types of terminals. It handles the terminals internally and provides a terminal independent interface.

    • UNIX contains a database that describes the control codes used by variety of terminals

      • The first database facility developed for dealing with the differences among the terminals is called termcap

      • The second database utility introduced is terminfo

    • Curses reads the terminal capabilities from /etc/termcap or /etc/terminfo into the external variables declared in <cursesh>

    • Programmer does not have to be concerned with the termcap database as curses reads the database while initializing the screen. I.e, using function initscr()

Terminal independence is the foundation of Curses. Terminal independence allows the programmer to write programs that make use of the advanced features of the intelligent terminals. It means a program can control the display screen on hundreds of different old and new types of terminals.

UNIX contains a database that describes the control code used by hundreds of terminals. Each make of the terminal has an entry in the database and it is identified by the manufacture's name and the product line number.

termcap is the first UNIX database utility developed for dealing with the differences in terminals. A second database utility introduced is called terminfo.

termcap

/usr/share/termcap (on a conventional UNIX system this is frequently /etc/termcap) is a text file that contains the termcap database. Each entry in termcap is made up of:

  • Comment lines that begin with a sharp sign (#), describes the manufacturer terminal or the creator of the termcap entry.

  • Alias names that are used to identify the entry in the database. In UNIX, at the beginning of a session, the user specifies or sets the variable "TERM".

  • List of capabilities that describe the specific control codes for accessing the various features of a terminal.

terminfo

The terminfo facility performs the same basic functions as the termcap database. The main difference is that after a terminfo entry is written, it is compiled. The text file terminfo.src contains the entries in source format similar to /usr/share/termcap.

Environment for Curses on INTERIX

  • To use the curses library, you must include <curese.h> in your program

    And it must be complied with

gcc [flags] -lcurses file

Or load with

<pre IsFakePre="true" xmlns="https://www.w3.org/1999/xhtml">

ld [flags] -lcurses file

If the curses is setup to use *termcap* then add **-ltermlib** option
  • For example, to compile a sample program windowtest.c to display windows using curses type:

    gcc -owin dowtest windowtest.c lcurses =ltermlib

    This will create an executable windowtest

The Curses Library

Curses is made up of a library of procedures in /usr/lib and a header file /usr/include/curses.h. The library contains the compiled procedures while the header file contains definitions of the data types you will need.

To use the Curses library, you must include the header file in your program so that you can refer to the Curses data types, and you must provide the linker with information on where to find the Curses functions.

The following slides explain the compilation and linking procedures, to compile a simple Curses program.

Curses Functionality on INTERIX

  • The curses functionality covers the following

    • Support for Windows

    • Character inputs

    • Color support

    • Panels

    • Multiple window highlights

    • Menus

    • Mouse interfacing

Curses on INTERIX

  • Curses functionality on INTERIX

    • There are two demos, which covers the curses functionalities

      • Demo 1 Covers basic window handling and small demo program to demonstrate,

        Support for Windows

        Character inputs

      • Demo 2 It demonstrates following additional functionalities along with the ones already mentioned for Demo 1

        Color support

        Panels

        Multiple window highlights

        Menus

        Mouse interfacing

The Curses functionality on INTERIX is demonstrated through two Demos:

  • Demonstration 1 - covers basic window handling

  • Demonstration 2 shows additional functionalities such as Color support, Panels, Multiple window highlights and Mouse interfacing.

Demo 1: Basic Window Handling

  • Application cover basic window handling

    • The application uses initialization, moving the cursor, text manipulation and cleanup related library functions

    • The program refers to curses hunder/us/include

    • Demo applications use following basic functions:

intsc()

    \-creates two windows: *stdscr* and *curscr,* also calls *setterm()* internally to read the terminal capability information from either *termcap* or *terminfo*
    
    <pre IsFakePre="true" xmlns="https://www.w3.org/1999/xhtml">

wrefresh()

    it causes the screen to be changed so that it displays the current screen image
    
    <pre IsFakePre="true" xmlns="https://www.w3.org/1999/xhtml">

wgetch()

    causes, the character to be echoed in the window. Also it is not affected by the location of the physical cursor on the screen

The application used in this demonstration provides a basic understanding of how windows are created using Curses and navigating between the windows. The application uses important functions that are most commonly used in Curses application. Porting such applications gives a basic insight into handling Curses functions on INTERIX.

The functions that are used by the demo application are listed on the following slides. This list is not exhaustive, but it mentions the most common ones.

The application, basically, creates two windows. One of the windows is highlighted using borders with characters "=" and "|". And the other window is highlighted using borders with characters "-" and "|". You can type anything on the window, use the TAB key to move between the windows, and ESC key to exit out of the application.

Demo 1: Basic Window Handling

box()

draw box at the edge of the window, defined by the *WINDOW parameter

waddc h()

adds a single character to the stdscr. It takes *WINDOW as the first argument in addition to the regular arugments

newwin()

creates a new window, returns *WINDOW to the new window structure. The screen image in the new window is filled with blanks.

endwin()

called when the program is finished. The function does all the changes curses has done the terminal. The space taken by the i nitscr and cursor will be allocated

The demo application provides a basic insight into the following areas:

  • Initializing and terminating the Curses Before you can use a window, you have to initialize it. When you create a window, your program must close the window before exiting to restore the terminal settings.

  • Adding characters to the screen image After creating the window, displaying the characters on the window is essential as to interact with the user. Addch (), addstr (), box (), and refresh () are some of the functions that are used to add characters into the window.

  • Getting characters from the terminal - getch (), getstr () and scanw () are some of the functions that are used to get characters from the terminal.

  • Creating multiple windows newwin () and subwin () function calls are used to create multiple windows.

Demo 1: Example Code

Following is the example code highlighting the common functions used in Demo 1

     #include<curses.h>
     
     
     #include<stdio.h>
     
     
     main (){
     
     
          WINDOW *win[5]; 
          
          initscr ();
     
          win[2] = newwin (12, 22, 4, 4); 
          
          box (win[2], '|', '+'); 
          
          wrefresh (win[2]);
          
          if ( ( win [1] = newwin (10, 20, 5, 5)) == 0) 
          
          
               addstr ("new window creation erro \n"); 
           
           wmove (win[1], 0, 0);
          
          for(; ch != 27;)
          
          {
     
              ch=wgetch (win[j]); 
                    
              waddch (win [j], ch);
          
          } 
          
          endwin ();
     
          system ("dear");
     
     }

Demo application Demo 1

The complete source code of the demo application is given below.

/* windowtest.c */
#include<curses.h>
#include<stdio.h>
main ()
{
     int i,j=1;
     char c[30];
     char ch;
     WINDOW *win[5];
     initscr ();
     noecho ();
     cbreak ();
           /* Create a new window with the specified co-ordinates */
     win[2] = newwin (12, 22, 4, 4);
           /* Draw a visible box around the window, with top and bottom lines  */
     /* Comprising of the "=" character                              */
     box ( win[2], '|' , '=' );
     /* refresh the window, so that the latest changes are visible          */
     wrefresh (win[2]) ;
     if ( ( win[1] = newwin(10,20,5,5)) == ( char * ) 0 )
          addstr ( "new window creation error \n" );
     /* Create one more window next to the one already exists           */
     win[4] = newwin (12, 22, 4, 35);
     /* Draw a box around the newly created window                */
box ( win[4], '|' , '-' );
     wrefresh (win[4]) ;
     if ( ( win[3] = newwin(10,20,5,36)) == ( char * ) 0 )
          addstr ( "new window creation error \n" );
     wmove (win[1], 0, 0);
     wrefresh (win[1]) ;
     for(; ch != 27 ; )
     {
          ch=wgetch (win[j]);
          if ( ch == 9 )
          {
               j = (j == 1) ? 3 : 1;
          }
          else
          {
               waddch (win[j], ch);
          }
          wrefresh (win[j] );
     }
     /*Close all the windows created along with the WINDOW structure      */
     endwin ();
     system ("clear");
     clear ();
}

The file wintest.exe contains the sample source code.

Building and Running Demo 1

  • Building Demo 1

    • windowtest.c file is complied using the 'gcc compiler' as explained before

    • On the INTERIX shell, type in 'gcc -o windowtest windowtest.c -lcurses'. This will create an executable by name windowtest

  • Running Demo 1

    • On the INTERIX command prompt type in './windowtest'

    • Two new windows are created in the terminal. Use 'TAB' key to navigate between the windows and 'ESC' key to terminate

The next step is to compile the source code and resolve issues as they appear. Typical issues are related to path differences, potentially non-existent device files, environment differences, etc.

To compile the provided sample, follow these steps:

  1. Unzip the windowtest.zip file.

  2. Open an INTERIX shell.

  3. To compile the windowtest.c file, type:

    gcc ****** o windowtest windowtest.c lcurses and press Enter.

    This generates an executable, 'windowtest'.

  4. To run the executable, type,

    ./windowtest and press Enter.

This runs the application on the current INTERIX terminal.

Color Support on INTERIX

  • curses support color attributes on terminals with that capability.

  • Start_color(), init_pair(), init_color(), has_color(), can_change_color(), color_content(), pair_content are some of the curses color manipulation routines

  • To use the above routines start_color() must be called, usually right after initscr(). Colors are always used in pairs (referred to as color-pairs).

  • The package actually thinks in terms of color pairs, combinations of foreground and background colors. For example:

    init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK), function sets up guaranteed-RED color on black.

  • Curses also assumes that COLOR_BLACK is the default background color for all terminals.

Curses support color attributes on terminals. To use color functions, the start_color () function/routine must be called, usually immediately after initscr (). Colors are always used in pairs (referred to as color-pairs). A color-pair consists of a foreground color (for characters) and a background color (for the blank fields on which characters are displayed). A programmer initializes a color-pair with the routine init_pair (). After a color-pair is initialized, COLOR_PAIR (n), a macro defined in <curses.h>, is used as a new video attribute.

If a terminal is capable of redefining colors, a programmer can use the routine init_color () to change the definition of a color. The routines has_colors () and can_change_color () return a TRUE or FALSE, depending on whether the terminal has color capabilities and whether the programmer can change the colors. The routine color_content () allows a programmer to extract the amounts of red, green, and blue components in an initialized color. The routine pair_content() allows a programmer to find out how a given color-pair is currently defined.

The init_color () routine changes the definition of a color. It takes four arguments, the number of the color to be changed followed by the three RGB values (for the amounts of the red, green, and blue components).

The has_colors () routine requires no arguments. It returns TRUE if the terminal can manipulate colors, otherwise, it returns FALSE.

The can_change_color () routine requires no arguments. It returns TRUE if the terminal supports colors and can change their definitions, otherwise, it returns FALSE. These routines facilitate writing terminal-independent programs.

In <curses.h>, the following macros are defined:

  • COLOR_BLACK

  • COLOR_RED

  • COLOR_GREEN

  • COLOR_YELLOW

  • COLOR_BLUE

  • COLOR_MAGENTA

  • COLOR_CYAN

  • COLOR_WHITE

These are the default colors. Curses also assumes that COLOR_BLACK is the default background color for all terminals.

The sample application for the second demonstration, ncurses.c, included at the end of this document provides the option for testing the color patterns. To do this:

  1. Make the sample application.

  2. Run the application using - ./ncurses.

  3. Type the option 'c' for color patterns.

The application displays the different color patterns available, both background and foreground colors.

Panels on INTERIX

  • Panels are curses windows with the added feature of depth.

  • Panel functions allow the use of stacked windows and ensure the proper portions of each window and the curses stdscr window are hidden or displayed when panels are added, moved, modified or removed.

  • The panels library includes an update function (analogous to refresh ()) that displays all panels in the deck in the proper order to resolve overlaps.

  • The standard window, stdscr, is considered below all panels.

  • All the panels-using modules must import the panels library declarations with #include <panel.h> and must be linked explicitly with the panels library using an lpanel argument.

  • There is presently no way to display changes to one obscured panel without repainting all panels

The Curses library, by itself, provides good support for screen displays in which the windows are tiled (non-overlapping). In a more general case, where windows might overlap, you have to use a series of wnoutrefresh() calls followed by a doupdate(), and be careful about the order in which you refresh the windows. It has to be bottom-to-top, otherwise parts of windows that should be hidden will be visible. A panel object is a window that is implicitly treated as a part of a deck including all other panel objects. The deck has an implicit bottom-to-top visibility order. The panels library includes an update function (analogous to refresh()) that displays all panels in the deck in the correct order to resolve overlaps. The standard window, stdscr, is considered below all panels.

Here are some of the highlights:

You can create a panel from a window by calling new_panel() on a window pointer. It then becomes the top of the deck. The panel's window is available as the value of panel_window() called with the panel pointer as argument. You can delete a panel (removing it from the deck) with del_panel(). You can replace a panel's window with a different window by calling replace_window().To move a panel's window, use move_panel(). Typically, you will want to call update_panels() and doupdate() just before accepting a command input, once in each cycle of interaction with the user.

Note: There is currently no way of displaying changes to one hidden panel without repainting all panels.

Compiling with the Panels Library

All panels-using modules must import the panels library declarations with #include <panel.h> and must be linked explicitly with the panels library using an lpanel argument. Note that they must also link the ncurses library with lncurses.

The sample application (demo 2), ncurses.c, included at the end of this document provides the option for testing the panels. To do this:

  1. Make the sample application.

  2. Run the application using - ./ncurses..

  3. Type the option 'o' for panel operation.

The application provides options for creating, moving, and refreshing the panels.

  • The menu library is a curses extension that supports easy programming of menu hierarchies with a uniform but flexible interface.

  • All menu-using modules must import the menu library declarations with #include <menu.h> and must be linked explicitly with the menus library using an -lmenu argument.

  • The general flow of control of a menu program looks like this:

    • Initializes curses.

    • Create the menu items, using new_item()

    • Create the menu using new_menu()

    • Post the menu using menu_post()

    • Refresh the screen.

    • Process user requests via an input loop

    • Unpost the menu using menu_unpost()

    • Free the menu, using free_menu()

    • Free the items using free_item()

    • Terminate curses

The menus created by the menu library consist of collections of items, which includes a name string part and a description string part. To make menus, you create groups of these items and connect them with menu frame objects.

The menu can then be posted, that is written to an associated window. Actually, each menu has two associated windows: a containing window in which the programmer can scribble titles or borders, and a subwindow in which the menu items are displayed. If this subwindow is too small to display all the items, it will be a scrollable viewport on the collection of items.

A menu might also be unposted (that is, undisplayed), and finally freed to make the storage associated with it and its items available for reuse. The general flow of control of a menu program resembles the following:

  1. Initialize Curses.

  2. Create the menu items, using new_item().

  3. Create the menu using new_menu().

  4. Post the menu using menu_post().

  5. Refresh the screen.

  6. Process user requests via an input loop.

  7. Unpost the menu using menu_unpost().

  8. Free the menu using free_menu().

  9. Free the items using free_item().

  10. Terminate curses.

Menus may be multi-valued or single-valued, which is the default (see the manual page mitem_opts to see how to change the default). Both types always have a current item. From a single-valued menu, you can read the selected value simply by looking at the current item. From a multi-valued menu, you get the selected set by looping through the items applying the item_value() predicate function. Your menu-processing code can use the function set_item_value()to flag the items in the select set.

The menu library calculates a minimum display size for your window, based on the following variables:

  • The number and maximum length of the menu items

  • Whether the O_ROWMAJOR option is enabled

  • Whether display of descriptions is enabled

  • Whatever menu format may have been set by the programmer

  • The length of the menu mark string used for highlighting selected items

The function set_menu_format() allows you to set the maximum size of the viewport or menu page that will be used to display menu items. You can retrieve any format associated with a menu with menu_format(). The default format is rows=16, columns=1.

As mentioned earlier, each menu has, a pair of associated windows. Both these windows are painted when the menu is posted, and erased when the menu is unposted. The outer or frame window is not otherwise touched by the menu routines. It exists so that the programmer can associate a title, a border, or perhaps help text with the menu and have it correctly refreshed or erased at post/unpost time. The inner window or subwindow is where the current menu page is displayed. By default, both windows are stdscr.

The sample application (demo 2), ncurses.c, included at the end of this document provides the option for testing the menu options. To do this:

  1. Make the sample application.

  2. Run the application using - ./ncurses

  3. Type the option 'm' for menu options.

The sample application displays the list of menus, and you can use the cursor for selecting the menus. Select a menu option using the <ENTER> key and the application displays the menu you have selected.

Support for Multiple Screen Highlights

Curses on INTERIX

Curses functionality on INTERIX

  • Support for multiple screen highlights

    • supports screen highlights including standout, reverse-video, underline, and blink.

    • It also supports color, which is treated as another kind of highlight.

    • There are two ways to make highlights.

      • One is to logical-or the value of the highlights you want into the character argument of an addch( ) call, or any other output call that takes a chtype argument.

      • The other is to set the current-highlight value. This is logical-or'ed with any highlight you specify the first way. You do this with the functions attron(),attroff ( ), and attrset(); see the manual pages for details.Curses supports screen highlights including standout, reverse-video, underline, and blink. It also supports color, which is treated as another kind of highlight. Highlights are encoded, internally, as high bits of the pseudo-character type (chtype) that <curses.h> uses to represent the contents of a screen cell. See the <curses.h> header file for a complete list of highlight mask values (look for the prefix A_).

There are two ways to make highlights:

  • The first way is to logical-or the value of the highlights you want into the character argument of an addch() call, or any other output call that takes a chtype argument.

  • The second way is to set the current-highlight value. This is logical-or'ed with any highlight you specify the first way. You can do this with the functions attron(), attroff (), and attrset().

Color is a special kind of highlight. The package actually thinks in terms of color pairs, that is, combinations of foreground and background colors. The sample code sets up different color pairs, with all of the guaranteed-available colors on black.

The sample application (demo 2), ncurses.c, included at the end of this document provides the option for testing the overlapping and window scrolling. To do this:

  1. Make the sample application.

  2. Run the application using - ./ncurses.

  3. Type the option 's' for multiple screen and 'g' for window scrolling.

The application provides options for creating new windows and navigating between them.

Mouse Interfacing on INTERIX

  • Mouse interfacing is not part of either the XSI Curses standard, nor of System V Release 4, nor BSD curses.

  • The ncurses library provides a mouse interface. While compiling on different systems rap mouse-related code in an #ifdef using the feature macro NCURSES_MOUSE_VERSION so it will not be compiled and linked on non-ncurses systems.

  • Presently, mouse event reporting works only under xterm and does not work at all with INTERIX.

  • The mouse interface is very simple. To activate it, you use the function mousemask(), passing it as first argument a bit-mask that specifies what kinds of events you want your program to be able to see.

  • Events that can be reported include presses, releases, single-, double- and triple clicks

Mouse Interfaces are not a part of either the XSI Curses standard, System V Release 4, or BSD Curses. Currently, mouse event reporting works only under xterm and does not work at all with INTERIX.

Therefore, it is better to wrap mouse-related code in an #ifdef using the feature macro NCURSES_MOUSE_VERSION, so it will not be compiled and linked on non-ncurses systems.

The mouse interface is very simple. To activate it, you use the function mousemask(), and the first argument that you pass to it is a bit-mask that specifies the kinds of events you want your program to be able to see. It will return the bit-mask of events that actually become visible, which may differ from the argument if the mouse device is not capable of reporting some of the event types you specify.

Once the mouse is active, your application's command loop should watch for a return value of KEY_MOUSE from wgetch(). When you see this value, a mouse event report has been queued. To pick it off the queue, use the function getmouse() (You must do this before the next wgetch(), otherwise another mouse event might come in and make the first one inaccessible). Each call to getmouse() fills a structure (the address of which you will pass it with mouse event data. The event data includes zero-origin, screen-relative character-cell coordinates of the mouse pointer. It also includes an event mask. Bits in this mask will be set, corresponding to the event type being reported.

The mouse structure contains two additional fields, which might be significant in the future as ncurses keeps interfacing to new kinds of pointing devices. In addition to the x and y coordinates, there is a slot for a z coordinate. This might be useful with touch screens that can return a pressure or duration parameter. There is also a device ID field, which could be used to distinguish between multiple pointing devices.

For example, some of the mouse handling functions used in the ncurses.c sample application are getmouse(), ungetmouse(), and mousemask(). All routines return the integer ERR upon failure or OK upon a successful completion.

The sample application (demo 2), ncurses.c, included at the end of this document provides the option for testing the keyboard and mouse inputs. To do this:

  1. Make the sample application.

  2. Run the application using - ./ncurses

  3. Type the option 'a' for keyboard and mouse input testing.

The application provides options for getting inputs from the keyboard and displaying the same as an echoed output.

Debugging on INTERIX

Typically most of the Curses programs are 'C' language oriented. One of the debuggers that can be used to debug a Curses program is gdb. INTERIX provides few debugging options for Curses applications. Some of the useful debugging functions are described below.

trace()

This function can be used to explicitly set a trace level. If the trace level is nonzero, on executing the program, a file called 'trace' is generated in the current working directory. This file contains a report on the library's actions. Higher trace levels enable more detailed (and verbose) reporting. See comments attached to TRACE_ in the <curses.h> file for details. (It is also possible to set a trace level by assigning a trace level value to the environment variable NCURSES_TRACE).

_tracef()

This function can be used to output your own debugging information. It is available only if you link with lncurses_g (not shipped with INTERIX). It can be used the same way as printf(), except that it outputs a newline character after the end of arguments. The output is generated in a file called trace in the current directory.

Trace logs can be difficult to interpret due to the sheer volume of data dumped in them.

Prerequisites for Porting

  • INTERIX installed on host machine

    • Curses libraries are provided by INTERIX

    • Provides termcap and terminfo directories

      /usr/include/curses.h

      /usr/share/termcap

  • Understanding of writing and porting POSIX code on INTERIX

    • The application will very likely make use of various system calls, signal handling using signal.h header files

      • For examples, handling interrupt signal handling

        Signal(SIGINT, closeall);

        //where closeall is a function called upon receiving an interrupt

Because there are not many changes involved in the data structure and the function definition used in UNIX and INTERIX, porting any Curses application from UNIX to INTERIX on Microsoft Windows does not involve major code changes. Some of the basic requirements that need to be met are as follows:

  1. INTERIX should be installed on the Windows host machine. As described earlier, INTERIX provides access to the curses.h header file. INTERIX also provides access to the termcap and terminfo databases.

    INTERIX supports various terminal types, one of them being interix. The interix terminal is one of the terminals that support color.

  2. Because most of the Curses applications use the signal handling functions for trapping various control signals, it is necessary to go through Section 4: "Writing POSIX standard code".

Curses-Specific Porting Issues

  • Porting issues

    • INTERIX provides compilers and make utilities

    • If there are any special tools are utilities used on UNIX environment, the equivalent should be installed on INTERIX

    • Include and library paths for curses.h

      • curses.h should be present under /usr/include/directory
    • Setting DISPLAY environment variable

      • Running the application from an INTERIX shell, DISPLAY would be set in the profile of the shell
    • INTERIX does not support <sgtty.h>

      • There is no support available for stty and gtty

      • function calls to getbkgd() needs to be replaced by wgetbkgd()

To port any application, one of the important steps is to set up the build environment. Setting up the build environment basically involves making sure of compilers and the make utility for building the application.

INTERIX provides the gcc compiler to compile applications. As shown on the slide, for compiling smaller applications, enter a simple command in the INTERIX command line. For larger applications, for example, the one included at the end of this document, it is better to have a Makefile, which specifies all include paths and libraries used by the application to the compiler. An example Makefile is provided with sample application for the second demonstration, for compiling ncurses.c.

After porting, successfully compiling, and building, the sample application is run from the INTERIX command prompt. The application refers to the environment variable TERM, which defines the terminal capability set to use. By default, the term used is interix under INTERIX environment, unless it is set to some other type.

One of the most common issues in porting applications is specifying the library paths and the include path. Make sure that the curses.h file is present in the path /usr/include. While building the application, specify the libraries to be included for building the application. This can be set in the Makefile, if you are using one, or specified as a part of compiler-specific options.

As of now, INTERIX, ,does not support the header file called <sgtty.h>. This header file mainly defines the stty() and gtty() system calls.

The stty() command sets certain terminal I/O options for the device that is the current standard input. Without arguments, it reports the settings of certain options.

The system call is not used very frequently, and moreover the standard settings are set using the environment variables.

The lists of functions implemented and missing are identified in the curses.h header file under the /usr/include directory.

Functions and Macros Missing in INTERIX

  • Curses.h provides function and macro definition

    • The curses.h defines function definitions, that are implemented in INTERIX and also the functions that are missing

    • Following are some of the functions that are marked missing in curses.h under INTERIX

    addnwstr(const wchar_t *, int);
     addwstr(const wchar_t *);
     add_wch(const cchar_t *);
     add_wchnstr(const cchar_t *, int);
     bkgrndset(const cchar_t *);
     bkgrnd(const cchar_t *);
     killwchar(wchar_t *);
     mvaddnwstr(int, int, const wchar_t *, int);
     mvaddwstr(int, int, const wchar_t *);

INTERIX supports the Curses data types and libraries. The header function defining various Curses data types can be found in curses.h under the /usr/include directory.

By viewing the curses.h file, you can see the various library functions supported and not supported under INTERIX. INTERIX also supports various terminal types that are defined by the terminfo or termcap database under /usr/share. The curses.h header file describes the WINDOW data structure used by the INTERIX while using Curses. Most of the window attributes are the same as the ones used by the Curses on UNIX.

The following list provides some of the function definitions missing in the curses.h file.

addnwstr(const wchar_t *, int);
addwstr(const wchar_t *);
add_wch(const cchar_t *);
add_wchnstr(const cchar_t *, int);
add_wchstr(const cchar_t *);
bkgrndset(const cchar_t *);
bkgrnd(const cchar_t *);
border_set(cchar_t,cchar_t,cchar_t,cchar_t,cchar_t,cchar_t,cchar_t,cchar_t);
box_set(WINDOW *, cchar_t, cchar_t);
echo_wchar(const cchar_t *);
erase_wchar(wchar_t *);
pe getbkgd(WINDOW *);
getbkgrnd(cchar_t *);
getcchar(const cchar_t *, wchar_t*, attr_t*, short*, void*);
getn_wstr(wint_t *, int n);
get_wch(wint_t *);
get_wstr(wint_t *);
hline_set(const cchar_t *, int);
innwstr(wchar_t *, int);
ins_nwstr(const wchar_t *, int);
ins_wch(const cchar_t *);
ins_wstr(const wchar_t *);
inwstr(wchar_t *);
in_wch(const cchar_t *);
in_wchstr(const cchar_t *);
in_wchntr(const cchar_t *, int);
key_name(wchar_t *);
killwchar(wchar_t *);
mvaddnwstr(int, int, const wchar_t *, int);
mvaddwstr(int, int, const wchar_t *);
mvadd_wch(int, int, const cchar_t *);
mvadd_wchnstr(int, int, const cchar_t *, int);
mvadd_wchstr(int, int, const cchar_t *);
mvgetn_wstr(int, int, wint_t *, int n);
mvget_wch(int, int, wint_t *);
mvget_wstr(int, int, wint_t *);
mvhline_set(int, int, const cchar_t *, int);
mvinnwstr(int, int, wchar_t *, int);
mvins_nwstr(int, int, const wchar_t *, int);
mvins_wch(int, int, const cchar_t *);
mvins_wstr(int, int, const wchar_t *);
mvinwstr(int, int, wchar_t *);
mvin_wch(int, int, const cchar_t *);
mvin_wchstr(int, int, const cchar_t *);
mvin_wchntr(int, int, const cchar_t *, int);
mvvline_set(int, int, const cchar_t *, int);
mvwaddnwstr(WINDOW *, int, int, const wchar_t *, int);
mvwaddwstr(WINDOW *, int, int, const wchar_t *);
mvwadd_wch(WINDOW *, int, int, const cchar_t *);
mvwadd_wchnstr(WINDOW *, int, int, const cchar_t *, int);
mvwadd_wchstr(WINDOW *, int, int, const cchar_t *);
mvwgetn_wstr(WINDOW *, int, int, wint_t *, int n);
mvwget_wch(WINDOW *, int, int, wint_t *);
mvwget_wstr(WINDOW *, int, int, wint_t *);
mvwhline_set(WINDOW *, int, int, const cchar_t *, int);
mvwinnwstr(WINDOW *, int, int, wchar_t *, int);
mvwins_nwstr(WINDOW *, int,int, const wchar_t *,int);
mvwins_wch(WINDOW *, int, int, const cchar_t *);
mvwins_wstr(WINDOW *, int, int, const wchar_t *);
mvwinwstr(WINDOW *, int, int, wchar_t *);
mvwin_wch(WINDOW *, int, int, const cchar_t *);
mvwin_wchnstr(WINDOW *, int,int,const cchar_t *,int);
mvwin_wchstr(WINDOW *, int, int, const cchar_t *);
mvwvline_set(WINDOW *, int,int, const cchar_t *,int);
pecho_wchar(WINDOW *, const cchar_t *);
setcchar(cchar_t *, wchar_t *, attr_t, short, const void *);
slk_attr_off(attr_t);
slk_attr_on(attr_t);
slk_attr_set(attr_t);
slk_wset(int, wchar_t *, int);
unget_wch(const wchar_t *);
vid_attr(attr_t);
vid_puts(attr_t, int (*)(int));
vline_set(const cchar_t *, int);
waddwstr(WINDOW *,const wchar_t *);
wadd_wch(WINDOW *,const cchar_t *);
wadd_wchnstr(WINDOW *,const cchar_t *,int);
wadd_wchstr(WINDOW *,const cchar_t *);
wbkgrndset(WINDOW *,const cchar_t *);
wbkgrnd(WINDOW *,const cchar_t *);
wecho_wchar(WINDOW *, const cchar_t *);
wgetbkgrnd(WINDOW *, cchar_t *);
wgetn_wstr(WINDOW *,wint_t *, int);
wget_wch(WINDOW *, wint_t *);
wget_wstr(WINDOW *, wint_t *);
whline_set(WINDOW *, const cchar_t *, int);
winnwstr(WINDOW *, wchar_t *, int);
wins_nwstr(WINDOW *, const wchar_t *, int);
wins_wch(WINDOW *, const cchar_t *);
wins_wstr(WINDOW *, const wchar_t *);
winwstr(WINDOW *, wchar_t *);
win_wch(WINDOW *, const cchar_t *);
win_wchnstr(WINDOW *, const cchar_t *, int);
win_wchstr(WINDOW *, const cchar_t *);
wunctrl(cchar_t *);
wvline_set(WINDOW *, const cchar_t *, int);

Demo 2 : A Comprehensive Demo 1/2

  • A Demo application for exhaustive coverage of curses

    • The application uses most of the widely used functions in curses. It covers

      • Color support, panels, menus and window highlights
    • Building Demo 2

      • Get ncurses.c, test.priv.h and Makefile files

      • No code changes are made in the ncurses.c file

      • Run command 'make ncurses' on INTERIX shell, this creates the executable called 'ncurses'

      • With no code changes made, the above command generates the following error

linking ncurses

gcc -o ncurses ./ncurses.o -lpanel -lmenu -lform -lcurses

./ncurses.o: In function 'show_attr':

/samples/ncurses/ncurses.c:414 undefined reference to 'wgetbkgd'

***Error code 1

Another sample application is included in this document for an exhaustive coverage of the Curses library. Follow the instructions provided on the slides to compile and run the application.

The following are the series of test that are executed in the application to test all possible functionality provided by the Curses:

  • Keyboard and mouse input

  • Color pattern

  • Display windows and scrolling

  • Menus

  • Panels

Sample Application demo 2The file sampapp.exe contains the sample application files.

Demo 2 : A Comprehensive Demo 2/2

  • Now edit the file ncurses.c, by using 'vi ncurses.c'

  • Go to the line where wgetbkgd is referenced

    • This function is not defined in curses.h in INTERIX
  • Replace the function name with getbkgd

    • This function has been defined in curses.h in INTERIX
  • After these code changes are done in the ncurses.c. Run command 'make ncurses' on INTERIX shell, this creates an executable called 'ncurses'

  • No error messages are generated

  • Some warning messages may be generated. Ignore.

  • Run ./ncurses from the INTERIX command prompt

  • A list of options are displayed for exercising color support, multiple window highlights, menus and panels. Enter your option for covering the curses functionalities.

As mentioned in the above slide, one of the code changes suggested is necessary to run the application, that is, replace wgetbkgd() to getbkgd(). To compile the new code:

  1. Type make ncurses and press Enter.

    This creates an executable by the name ncurses.

  2. Type ./ncurses and press Enter.

    This runs the application on the current screen.

    The application is displayed with the previously mentioned options. Test all the options to verify the application under the INTERIX environment.