Using Ncurses in PHP

January 29, 2003

Tutorials

Intended audience

Overview

Learning Objectives

Definitions

Background Information

Prerequisites

How it Works

Putting it all together

The Script

Conclusion

Links

About the author


Intended audience


This tutorial is intended for expert PHP
programmers interested in extending command-line PHP applications to generate
terminal-based user interfaces. You need SSH access to a UNIX server with PHP 4
compiled with ncurses.

Overview


As a programmer you are often assigned to write programs for
system administration tasks. By default, command line applications in PHP lack
aesthetics. By taking advantage of the
ncurses functions you can create a
friendly user interface for the application that will be generally uniform
across the various terminal types.



PHP is a highly flexible and extensible language and can be used to augment your
existing shell/Perl scripting solutions or replace them; so ncurses
functionality is a natural progression for PHP.



This tutorial will give you a kick-start towards writing decent looking command-line
applications that benefit the user as well as the writer. I will focus on the
‘meat’ of what is needed and hopefully give you a framework for adding
ncurses functionality into your PHP application.

Learning Objectives


In this tutorial you will:

  • Learn how to create basic
    windows using ncurses and PHP, and write data to
    them.
  • Learn how to
    create ncurses window sets in PHP, and dynamically size the windows to fit the
    terminal.
  • Learn the
    specifics of window handles, and how to capture function
    keys.
  • See an example
    of using the ncurses functions by creating a small application that extends the
    Linux traceroute program while using all the functions discussed
    herein.

Definitions


  • Ncurses : The
    Ncurses (new curses) library is a free software emulation of curses in System V
    Release 4.0, and more. It uses Terminfo format, supports pads and color and
    multiple highlights and forms characters and function-key mapping, and has all
    the other SYSV-curses enhancements over BSD Curses. If you have used Linux you
    have probably seen ncurses in action. Programs such as Midnight commander,
    ncftp, Iptraf, trafshow and many others use ncurses for the visual aspect of the
    application.
  • Window:
    The term ‘window’ as used in this document refers to a section
    of a terminal that is created via ncurses in
    PHP.

Background Information


This tutorial is written for the advanced programmer.


It has been said that the Curses library of functions is appropriately named.
The documentation for the PHP ncurses functions is very limited and several of
the functions used in this tutorial do not even appear on the php.net website
as of this writing. Ncurses applications follow a c-style format, as they are
written to be true applications and not simply scripts with a defined start and
finish.



If you have experience programming in C or C++ then you are probably already familiar
with what ncurses can do, and can probably imagine how useful ncurses can be when
used in conjunction with a scripting language like PHP.

Prerequisites


You will need to have access to PHP compiled with –with-ncurses on a Unix-based
system. Not all terminals will support colorized ncurses output so this tutorial
will not use any coloring functions.



Keep in mind that you might have to reset your terminal often while working with
ncurses. If you have an error ncurses_end() is not called, so your
terminal will be fried and you will have to issue the ‘reset’
command to fix it.



Order is also very important for these functions, you can segfault PHP by attempting
to refresh a window that does not have an appropriate resource handle.



How it Works


Using ncurses we will create a functional window system and increase interface
usability.



Lets start with a very basic ncurses introduction before moving into anything
more advanced:



First let’s create an ncurses main window that uses the entire screen region
it has available. Then let’s add in a smaller window into the middle of
this screen and write a string to it. It will look like this.







Start ncurses with
ncurses_init()
which lets PHP know that you wish to enter into ncurses mode. Without making a
call to
ncurses_init()
before calling ncurses functions, PHP will error.



The code to produce the above
window:


<?

// we begin by initializing ncurses

$ncurse ncurses_init();

// let ncurses know we wish to use the whole screen

$fullscreen ncurses_newwin 0000); 

// draw a border around the whole thing.

ncurses_border(0,00,00,00,0);

// now lets create a small window

$small ncurses_newwin(1030725);

// border our small window.

ncurses_wborder($small,0,00,00,00,0);

ncurses_refresh();// paint both windows

// move into the small window and write a string

ncurses_mvwaddstr($small55"   Test  String   ");

// show our handiwork and refresh our small window

ncurses_wrefresh($small);

$pressed ncurses_getch();// wait for a user keypress

ncurses_end();// clean up our screen

?>

Try resizing the terminal and running
the code again. Notice that the main window will border itself using the maximum
values for the height and width.



Now you may want to see something
actually happen when you press a key, or you may not want to exit the
application unless a certain key is pressed. I personally prefer using the
escape key (char(27)) for this. To do this, at the top of the script add
in:


define("ESCAPE_KEY"27);

while(
1){

    ... #code

}

Change
ncurses_end()
to read


If($pressed == ESCAPE_KEY){

      ncurses_end();

  exit;

}else{

       
ncurses_mvwaddstr($small55$pressed);

}

Now if you press any key except
Escape, its integer equivalent will be placed within the window resource named
“small”. This creates a little application that responds to key
presses and exits only when you press Escape.



We can make this look better by adding a title-bar.


ncurses_attron(NCURSES_A_REVERSE);

ncurses_mvaddstr(0,1,"My first ncurses application");

ncurses_attroff(NCURSES_A_REVERSE);

You are not just limited to using
REVERSE
either; you can use DIM,
UNDERLINE
as well as several
others.



To add intuitive usability to a program
you will want to add a menu system.



Most programs have user interaction and the ability to select menu items. In the
past we used number or alpha systems where the user would select which menu item
he wanted by typing a number or letter on the keyboard and the item would be selected.
Selection menus have been replaced by interactive menu systems. You want to have
a menu that visually signals the user as to which option is currently selected.



The following code will replace
“$pressed =
ncurses_getch();”
in the previous
example


$menu = array("one","two","three","four");

    for($a=0;$a<count($menu);$a++){

        
$out $menu[$a];

        if($currently_selected == intval($a)){ 

            
ncurses_wattron($small,NCURSES_A_REVERSE);

            
ncurses_mvwaddstr ($small1+$a1$out);

            ncurses_wattroff($small,NCURSES_A_REVERSE);

        }else{

            
ncurses_mvwaddstr ($small1+$a1$out);

        }

    }

ncurses_wrefresh($small); // otherwise we will not see

  $pressed ncurses_getch($lower_main_window); // wait for user to press key

  
if($pressed == NCURSES_KEY_UP){

        $currently_selected--; 

        if(
$currently_selected 0){ $currently_selected 0; }

  }elseif( 
$pressed == NCURSES_KEY_DOWN){

        $currently_selected++;

        if(
$currently_selected >= count($menu)){ $currently_selected count($menu)-1; }

  }elseif( $pressed == ESCAPE_KEY){

    
ncurses_end();

    exit;

  }else{

    
ncurses_mvwaddstr($small55$pressed);

  }



When run you will have an
application that looks like the following illustration. It has a menu that you
can navigate through using the arrow keys and exits when you press
escape.







Now that you have this knowledge of
ncurses you are probably thinking of all kinds of applications that you can
create that utilize these functions. So the next portion of this tutorial will
focus on using ncurses to create a real world application that is both useful
and an excellent starting point for further development.

Putting it all together


The online documentation for PHP currently has 119 listed functions for ncurses;
the following list contains the functions used in the script below with links
to the online documentation where available. The four functions listed below which
do not appear in the online documentation are explained.

























































ncurses_init


Initialize ncurses.


ncurses_newwin


Create a new window.


ncurses_getmaxyx( resource window, int
return Y, int return X);


Sets Y and X to maximum size of current
terminal.

ncurses_border


Draw a border around the main
window.


ncurses_refresh


Refresh the main window; use
ncurses_wrefresh for refreshing individual windows.


ncurses_attron


Turn output attribute
on.


ncurses_mvaddstr

Move and insert a
string.


ncurses_attroff


Turn output attribute
off.


ncurses_wborder (resource window, int
left, int right, int top, int bottom, int tl_corner, int tr_corner, int
bl_corner, int br_corner);




Identical to ncurses_border except that you pass the window resource as an additional first parameter.


ncurses_wattron(resource window, int
attribute)


Identical to ncurses_attron except that you pass the window resource as an additional first parameter


ncurses_mvwaddstr


Select a window and place a string
within it a coordinates.


ncurses_wattroff (resource window, int
attribute)


Identical to ncurses_attroff except that you pass the window resource as an additional first parameter.


ncurses_wrefresh


Refresh named window
resource.

ncurses_getch


Wait for user keyboard or mouse
input.


The Script


The following code will produce a window which looks like this:






This script runs a traceroute to
zend.com, limits the hops to only ten, and displays
the results within a window. The items in the upper window are scrollable by
using your arrow keys and when you press Enter on each one a reverse
whois is
done and the results are placed in the lower window. If you press the escape key
the program will exit.


<?

// define some key constants.

define("ESCAPE_KEY"27);

define("ENTER_KEY"13);

// get our initial data

$tr_return traceroute("www.zend.com");

$ncurses_session ncurses_init();

$main ncurses_newwin(0000); // main window

ncurses_getmaxyx(&$main$lines$columns);

ncurses_border(0,00,00,00,0);

while(1){

// border the main window

ncurses_attron(NCURSES_A_REVERSE);

ncurses_mvaddstr(0,1,"Traceroute example");

ncurses_attroff(NCURSES_A_REVERSE);

 // create a lower window which is dynamically sized...

$lower_frame_window ncurses_newwin (10$columns-3$lines-111);

        ncurses_wborder($lower_frame_window0,00,00,00,0); // border it

        $lower_main_window ncurses_newwin (8$columns-5$lines-102);

        $main_list_window ncurses_newwin ($lines-12$columns-311);

        
ncurses_wborder($main_list_window0,00,00,00,0); // border it

  if($currently_selected == ""){$currently_selected 0;}

        for(
$a=0;$a<count($tr_return);$a++){

        $out $tr_return[$a];

        if(
$currently_selected == intval($a)){ 

            
ncurses_wattron($main_list_window,NCURSES_A_REVERSE);

            ncurses_mvwaddstr ($main_list_window1+$a1$out);

                
ncurses_wattroff($main_list_window,NCURSES_A_REVERSE);

        }else{

            
ncurses_mvwaddstr ($main_list_window1+$a1$out);

        }

    }

  if(
$y == ENTER_KEY){    

        $newout explode(" "$check_me);

        
$rwhois_return rwhois(trim($newout[3]));

        $a=0;

        while(list(
$key,$val)=each($rwhois_return)){

            
ncurses_mvwaddstr($lower_main_window1+$a1$key " - " $val);

            $a++;

        }

  }elseif(
$y == ESCAPE_KEY){

        
ncurses_end();

        exit;

  }

  
ncurses_move(-1,1); // toss cursor out of sight.

  ncurses_wrefresh($lower_frame_window); 

  
ncurses_wrefresh($lower_main_window); // show it

  
ncurses_wrefresh($main_list_window); 

  // wait for user to press a key

  $y ncurses_getch($lower_main_window);

  // check for the presence of up-arrow and down-arrow keys

  
if($y == NCURSES_KEY_UP){

        
$currently_selected--; 

        if(
$currently_selected 0){ $currently_selected 0; }

  }elseif( $y == NCURSES_KEY_DOWN){

        
$currently_selected++;

        if(
$currently_selected >= count($tr_return)){ $currently_selected count($tr_return)-1; }

  }

}//end main while

// the following are two helper functions for

// this ncurses example.

function traceroute($address){

    
exec("/usr/sbin/traceroute -x -n -m 10 $address",

$trreturn);

    return 
$trreturn;

}
//end function

// reverse whois

function rwhois($query){

    
$fp fsockopen ("rwhois.arin.net"4321$errno$errstr30);

    if (!$fp) {

            
$ret[] = "$errstr ($errno)\n";

    }else{

            
fputs ($fp"$query\r\n\r\n");

        $count=1;

            while (!
feof($fp) && $count 17) {

            
$back fgets ($fp,128);

            $data explode(":",$back);

                
$ret[$data[1]] = $data[2];

            $count++;

            }
//wend

          
fclose ($fp);

    }
//fi

   
return $ret;

}
//end function

?>

Conclusion


The code above is obviously a very simple framework for an ncurses application.
With so many functions at your disposal there is quite a world of interfaces that
can be developed.



This tutorial has not touched upon using the mouse in terminals, using colors
or many of the output control functions and their defined constants.



You will want to create a library of functions for using ncurses for doing things
like starting up ncurses and returning a handle to the main window or for drawing
windows.


The following are two functions which
show in more depth how to use the
ncurses_newwin()
function and how to create dynamically sized
windows within terminal screens. These will be a good start for much more
advanced functions. These can easily be extended to add in title bars, panes or
anything else you can think up.


#

# left_window(int size) creates an ncurses window

# that is write-safe on the left-hand side of the

# screen $size is how wide it will be

# returns window handle of inner window.

#

function left_window($size=15){

    global $fullscreen;

    
ncurses_getmaxyx($fullscreen$MAX_Y$MAX_X);

    $c ncurses_newwin ($MAX_Y-,$size11);

    ncurses_wborder($c,0,00,00,00,0); // border it

// now create window overtop the other just

// slightly smaller so that we won't write over

// the border.

    
$d ncurses_newwin ($MAX_Y-,$size-21+12);

    ncurses_wrefresh($c); // show it

    
ncurses_wrefresh($d); 

    return 
$d;

}

#

# creates an upper-right window

#

function upperr_window($size=15){

    global 
$fullscreen;

    
ncurses_getmaxyx($fullscreen$MAX_Y$MAX_X);

    $c ncurses_newwin ($size ,$size1$MAX_X-($size+1));

    ncurses_wborder($c,0,00,00,00,0); // border it

    ncurses_wrefresh($c); // show it

    
return $c;

}


Happy programming!

Links


http://www.opengroup.org/onlinepubs/007908799/xcurses/curses.h.html

This is the curses.h documentation, which will be an excellent reference while
working on any PHP application that uses the ncurses functions.



http://dickey.his.com/ncurses/ncurses-intro.html


A great write-up on programming with ncurses in C, much of which translates directly
into PHP.


And as always, use
‘man ncurses’ as each function in C has a man page for it. (i.e. ´man wborder’´).



About the author


Joel De Gan is a seasoned developer currently working for a high-profile domain
name registrar. He is obsessed with PHP and despite working with it on a daily
basis he often continues late into the night to sit up and program.



Joel additionally is the CTO for Tenshimedia LLC ( http://tenshimedia.com

) a multi-faceted media company that among other things runs and maintains JOIhost
( http://www.joihost.com ).



Joel is currently, in his spare time, working on a PHP/ncurses interface for the
Internet file sharing application eDonkey, and a PHP/ncurses packet logger, which
logs from tcpdump in addition to a small side project (http://listbid.com/affil/)
called Listbid.


About Cal Evans

Many moons ago, at the tender age of 14, Cal touched his first computer. (We're using the term "computer" loosely here, it was a TRS-80 Model 1) Since then his life has never been the same. He graduated from TRS-80s to Commodores and eventually to IBM PC's. For the past 10 years Cal has worked with PHP and MySQL on Linux OSX, and when necessary, Windows. He has built on a variety of projects ranging in size from simple web pages to multi-million dollar web applications. When not banging his head on his monitor, attempting a blood sacrifice to get a particular piece of code working, he enjoys building and managing development teams using his widely imitated but never patented management style of "management by wandering around". Cal is currently based in Nashville, TN and is gainfully unemployed as the Chief Marketing Officer of Blue Parabola, LLC. Cal is happily married to wife 1.28, the lovely and talented Kathy. Together they have 2 kids who were both bright enough not to pursue a career in IT. Cal blogs at http://blog.calevans.com and is the founder and host of Day Camp 4 Developers

View all posts by Cal Evans

2 Responses to “Using Ncurses in PHP”

  1. _____anonymous_____ Says:

    The article on ncurses is GREAT!!!!
    Just what I was looking for.

    But don’t bother looking at the links for
    tenshimedia.com and joihost.com

    apparently they expired long ago and have been taken over by the usual domain squatter scum.