Using VxWorks Curses

This document details how to use the curses library included in VxWorks 6.x in a simple application. It is designed to be used as a primer to using curses, rather than trying to explain everything fully. More information regarding curses can be found on the Internet.

This document uses these abbreviations for paths and commands to describe the VxWorks installation: $WIND_BASE is the Directory of VxWorks install, for example C:/WindRiver/vxworks-6.6; $WIND_CURSES relates to $WIND_BASE/vxworks-6.2/target/usr/apps/samples/UNIX/vi; xxx is the architecture and; yyy the compiler used

Libraries

The version of curses shipped with VxWorks 6.2 is compiled into 2 libraries:

  • libcurses.a
  • libtermlib.a

They are found by default at $WIND_CURSES/lib/xxxyyy

Rebuilding the Libraries

It is possible to rebuild the libraries following a change (such as adding a path to a termcap file in termcap.c) by calling make CPU=xxx TOOL=yyy as applicable to your project in both the following locations from the command line:

  • $WIND_CURSES/vw_curses/curses
  • $WIND_CURSES/vw_curses/termlib

Creating a Workbench Project

To use the curses libraries within a workbench RTP project the following properties should be changed (note the forward slashes):

  • Build Properties > Build Tools > Command > add L$WIND_CURSES /lib/xxxyyy into the box before any "-l" statements.
  • Build Properties > Build Macros > Build spec specific settings > LIBS should add -lcurses -ltermlib
  • Build Properties > Build Paths > Build spec specific settings > Add both: -I$WIND_CURSES/vw_curses/curses and -I$WIND_CURSES/vw_curses/h

Every source file that wishes to use curses must include curses.h and curses.ext.

Usable API

Unfortunately, the VxWorks curses doesn’t have the complete API that might be expected. However, it’s perfectly usable, and possibly more suitable for a lighter embedded system that way. This table details of the available commands:

Command Details
initscr () Initializes the terminal in curses mode. To do any screen manipulation using curses package this has to be called first.
endwin () Frees the memory taken by curses sub-system and its data structures and puts the terminal back to normal mode. This function must be called after you are done with the curses mode. Otherwise your terminal might behave strangely after the program quits.
printw () This function is analogous to normal printf in all respects except that it prints the data on the standard window (called stdscr) at the current (y,x) co-ordinates.
refresh ()
Dump the contents of stdscr to the screen.
mvprintw (y, x, string) Move to (y, x) then print the string.
move (y, x) Move the cursor to (y, x).
addch (ch)
Add a single character ch.
mvaddch (row,col,ch) Move to (y, x) then add a single character ch.
getch () Get a single character from standard input.
getyx (stdscr, y, x) Get the current curser position within stdscr.
newwin (nlines, ncols, begin_y, begin_x) Create new window of specified size and initial cursor placement. Return value of
type WINDOW * to identify it.
delwin (WINDOW *win) Delete the specified window.
clearok (stdscr, TRUE) When called with TRUE as argument, the next call to refresh will clear the screen completely and redraw the entire screen from scratch.
cbreak () Get input as soon as it is typed, don't wait for an EOL.
nocbreak () Wait for an EOL to get input.
noecho () Don't echo characters.
echo () Do echo characters.
leaveok (stdscr, TRUE | FALSE) Leave (TRUE) or don’t leave (FALSE) the cursor wherever the update happens.
Scrollok (stdscr, TRUE | FALSE) Controls what happens when the cursor of a window is moved off the edge of the window or scrolling region.
clear ()
Clear the screen and send cursor to position (0,0).
Refresh () Implement all changes since last refresh.


Thankfully there is an example of using the curses libraries in an application called twinkle located at $WIND_CURSES\vw_curses\test. This should be able to be compiled easily for anyone new to curses and shows some of the things that are possible. Also included is a vi clone called stevie. This builds into an RTP named vi.vxe in $WIND_BASE\vxworks-6.2\target\usr\root\PENTIUM4diab\bin.

Example Code

This example uses curses to view an incrementing number and allows the user to quit by pressing 'q' at any time. It is also available as a workbench archive (for registered users) Curses Example Code.

   1 
   2 
   3 
   4 
   5 
   6 
   7 
   8 
   9 
  10 
  11 
  12 
  13 
  14 
  15 
  16 
  17 
  18 
  19 
  20 
  21 
  22 
  23 
  24 
  25 
  26 
  27 
  28 
  29 
  30 
  31 
  32 
  33 
  34 
  35 
  36 
  37 
  38 
  39 
  40 
  41 
  42 
  43 
  44 
  45 
  46 
  47 
  48 
  49 
  50 
  51 
  52 
  53 
  54 
  55 
  56 
  57 
  58 
  59 
  60 
  61 
  62 
  63 
  64 
  65 
  66 
  67 
  68 
  69 
  70 
  71 
  72 
  73 
  74 
  75 
  76 
  77 
  78 
  79 
  80 
  81 
  82 
  83 
  84 
  85 
  86 
  87 
  88 
  89 
  90 
  91 
  92 
  93 
  94 
  95 
  96 
  97 
  98 
  99 
 100 
 101 
 102 
 103 
 104 
 105 
 106 
 107 
 108 
 109 
 110 
 111 
 112 
 113 
 114 
 115 
 116 
 117 
 118 
 119 
 120 
 121 
 122 
 123 
 124 
 125 
 126 
 127 
 128 
 129 
 130 
 131 
 132 
 133 
 134 
 135 
 136 
 137 
 138 
 139 
 140 
 141 
 142 
 143 
 144 
 145 
 146 
 147 
 148 
 149 
 150 
 151 
 152 
 153 
 154 
 155 
 156 
 157 
 158 
 159 
 160 
 161 
 162 
 163 
 164 
 165 
 166 
 167 
 168 
 169 
 170 
 171 
 172 
 173 
 174 
 175 
 176 
 177 
 178 
 179 
 180 
 181 
 182 
 183 
 184 
/* cursesRtpExample.c - example code to show the curses API */

/* Copyright (c) 2007 Harmonic Software Systems Ltd. */

/*
modification history
--------------------
01a,26nov07,hss  Created
*/

/*
DESCRIPTION

This example uses curses to view an incrementing number and allows the user
to quit by presing 'q' at any time.  This code will form an RTP, and therefore
the entry point is the main function.
*/

#ifndef VxWorks
#define VxWorks
#endif

#include <stdio.h>
#include <stdlib.h>
#include <curses.h>
#include <taskLib.h>
#include <string.h>
#include "sysLib.h"
#include "curses.ext"

/* defines */

#define NCOLS               80
#define TASK_PRIORITY 100
#define TASK_OPTIONS 0
#define TASK_STACK 0x10000
#define WELCOME_START_ROW 0
#define WELCOME_START_COL 2
#define WELCOME_END_ROW 4
#define NUMBER_ROW 10
#define NUMBER_COL 5

#define TERMINAL "nansi"

/* global variables */

int run = 1; /* controls the exit */
int number = 0;

/* functions */

void printWelcome (void);
void printNumber (void);
void controlTask (void);


/*****************************************************************************
*
* main - run the RTP
*
* This function will run the RTP
*
* RETURNS: N/A
*
*/

int main
    (
    int argc, /* number of arguments */
    char * argv[], /* array of arguments */
    char * envp[], /* array of environment strings */
    void * auxp /* implementation specific auxiliary vector */
    )
    {
    /* Initialise the curses */

    strcpy(Def_term,TERMINAL);
    initscr ();
    clearok (stdscr, TRUE); /* get input as soon as it's typed */
    cbreak (); /* Don't wait for EOL to get input */
    noecho (); /* Don't echo characters */
    leaveok (stdscr, TRUE); /* Leave the cursor where it is */
    clear(); /* clear screen */
    refresh(); /* refresh screen */

    /* Spawn the input task */

    if ((taskSpawn ("tControl", TASK_PRIORITY, TASK_OPTIONS,
                    TASK_STACK, (FUNCPTR)controlTask,
                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) == ERROR)
        {
        mvprintw (21, 2, "Unable to spawn control task\n");
        return;
        }

    /* Print a welcome message */

    printWelcome ();
    refresh();

    while (run)
        {
        printNumber ();
        taskDelay (sysClkRateGet ());
        }

    endwin(); /* End curses mode */
    return 0;
}

/*****************************************************************************
*
* printWelcome - display a welcome message
*
* This function will display some info on the screen.
*
* RETURNS: N/A
*
*/

void printWelcome (void)
    {
    int i;

    /* Display some welcome info */

    mvprintw (WELCOME_START_ROW, WELCOME_START_COL,
              "Harmonic Software Systems");
    mvprintw ((WELCOME_START_ROW + 2), WELCOME_START_COL,
              "Press 'q' to exit");

    move (WELCOME_END_ROW, 0);

    for (i = 0; i < NCOLS; i++)
        {
        addch ('-');
        }
    }

/*****************************************************************************
*
* printNumber - display a message
*
* This function will display some info on the screen.
*
* RETURNS: N/A
*
*/

void printNumber (void)
    {
    mvprintw (NUMBER_ROW, NUMBER_COL, "Number is now %d", number);
    refresh ();
    number++;
    }

/*****************************************************************************
*
* controlTask - input to control curses
*
* This function will take input to allow the user to exit.
*
* RETURNS: N/A
*
*/

void controlTask (void)
    {
    int ch;

    while (1)
        {
        ch = getch();

        if (ch == 'q')
            {
            run = 0;
            clear ();
            refresh();
            break;
            }
        }
    }

Running a curses RTP

There are multiple ways to run an RTP that uses curses, including using workbench. Using the target shell is possible, and easier using the VxWorks Command shell (entered by typing cmd). From there, rtp exec /path-to/rtpName.vxe will run the RTP. This is a handy way to start any RTP that’s not used within a production environment. Since the RTP must be started form a filesystem, a standalone application may want to include it in ROMFS.