Howdy Perl/Tk Widgets!

This document is an outgrowth of the ptkPORT.html page that I wrote. The concentration here is on stepping through the build of the Howdy extension to Perl/Tk.

Lesson 0: hiworld.c
In an effort to test your ability to program for Xlib consider writing and compiling a very simple C program that uses Xlib. For illustrative purposes I will use the (not too exciting) hiworld.c example. (You Xlib and widget experts out there will please bear with me on this ;-)

On a typical UNIX computer I can compile and link that program with a command like:

    cc -o hiworld.exe hiworld.c -lX11
whereas if I used the GNU C compiler that would simply be:
    gcc -o hiworld.exe hiworld.c -lX11
(and you may very well need other -l and/or -L arguments depending on the details of your system, these will be discussed in the next lesson.)

hiworld.c has a very simple interface: it pops up with the 'Hello!' string in place, mouse <Button-1> clicks put 'Hi.' strings in place, and any keyboard keystroke stops the primitive event handler loop and exits the program.

It is amusing to note that the equivalent functionality of that roughly 73 line C program can, with Perl/Tk to the rescue, be written in about 10 lines:

    #!/usr/bin/perl -w
    use Tk;
    my $m = MainWindow->new;
    my $c = $m -> Canvas(-height => 200, -width => 300,);
    $c -> pack;
    $c -> create('text', 40, 50, '-text' => "Hello World!");
    $m -> bind('<Any-KeyPress>' => sub{exit});
    $m -> bind('<Button-1>' => sub {
        $c -> create('text',$c->XEvent->x,$c->XEvent->y, -text => "Hi.") });
    MainLoop;
(Note that a tcl/tk version can be as few as 6 lines! Neither the perl/tk nor tcl/tk versions are really fair to the C version since it cannot even refresh the additional 'Hi.' strings on expose events the way the perl and wish scripts do automatically.)
[optional] Lesson 1: Makefile
Assuming you are able to figure out how to compile and link hiworld.c, an additional step to take would be to try coming up with a Makefile for it (although this would not be necessary if you have MakeMaker and can follow the steps in Lesson 2 below - so this lesson is strictly optional). The crude way to create a Makefile is to simply use a text editor to write something like this:
    hiworld : hiworld.c
            cc -o hiworld hiworld.c -lX11
which would be invoked with "make hiworld" for example. It is considered much more fashionable to throw in a bunch of macros into one's Makefile's as in the following:
    CC = cc
    X11 = -lX11
    all : hiworld
    hiworld : hiworld.c
            $(CC) -o hiworld hiworld.c $(X11)
which is still invoked via "make hiworld" even though it looks a little more complicated. Perl's ExtUtils::MakeMaker facility is much more adept at making Makefiles though, its use is discussed in the next lesson.
Lesson 2: h2xs, hiworld.c->Howdy.xs, Makefile.PL, xsubpp
Following the perlxstut(1) man page we go to an otherwise unused directory and type:
    h2xs -A -n Howdy
and note the creation of a new Howdy/ directory with several new files. With version 1.16 of h2xs the directory appears as follows:
    -rw-r--r--   user     118 Changes
    -rw-r--r--   user     982 Howdy.pm
    -rw-r--r--   user     158 Howdy.xs
    -rw-r--r--   user      54 MANIFEST
    -rw-r--r--   user     371 Makefile.PL
    -rw-r--r--   user     650 test.pl
The remainder of this lesson concerns itself with modifying (at least) four of the new files that were automatically generated: Howdy.xs, Howdy.pm, Makefile.PL, and test.pl.

For those of you curious about the above options passed to h2xs try taking a look through perldoc h2xs or tkpod h2xs. Note that -A specifies omission of the AutoLoader, and the -n option is given the package name we will be creating: Howdy.

One way to get our C Xlib code into Howdy would be to append (and slightly modify) the contents of hiworld.c to the (initially) blank stub of Howdy.xs (on Unix that would be something like cat hiworld.c >> Howdy.xs.) We edit Howdy.xs to add the required CODE: and OUTPUT: RETVAL statements, as well as to change function main(argv argc) to a funtion hiworld(argv argc) (clever name eh?). So that now the head of Howdy.xs now appears like:

    #include "EXTERN.h"
    #include "perl.h"
    #include "XSUB.h"
    #include 
    #include 


    MODULE = Howdy          PACKAGE = Howdy

    int 
    hiworld(argc, argv)
            int argc
            char argv
            CODE:
 
(note the function type of int, although void would have been just as appropriate.)

The tail of Howdy.xs now appears something like this:

            XCloseDisplay(mydisplay);
            OUTPUT:
            RETVAL
Note the absence of a call to exit(0); that we had in hiworld.c, and an absence of the braces {} for a main since there is no longer a main (we are turning out a hiworld() subroutine call for perl after all and perl.c already has a main{}).

We turn our attention to the Howdy/Makefile.PL file and change the line that initially says:

    'LIBS'      => [''],   # e.g., '-lm' 
to read something like what we needed to use to compile hiworld.c from the command line, like:
    'LIBS'      => ['-lX11'],   # e.g., '-lm' 
better still would be something like what is in the Makfile.PL for Tk, as of Tk-b11.02 that would be:
    'LIBS'    => ["$xlib -lX11 -lpt -lsocket -lnsl -lm"],
Hmm... we don't know what "$xlib" interpolates to, so let's keep it simple:
    'LIBS'      => ['-lX11 -lpt -lsocket -lnsl -lm -ldnet'],   
(The very observant will note that I also added a line to Howdy's Howdy/Makefile.PL that reads:
    'dist'      => { COMPRESS => "gzip -9f" },
which is for make dist to make a nice compact distribution file.)

Before attempting to build our Howdy extension lets put in some non-trivial code into the test.pl file so that make test will be interesting. Since test # 1 is the loading via a use Howdy; statement let us add a second test so that we set last_test_to_print to 2 and add a couple of careful lines to test.pl:

    eval '$retval = &Howdy::hiworld($foo,$bar);';
    if ($@) {print "not ok 2: $@\n";} else {print "ok 2\n";}
We can now type the following standard commands to build and install our Howdy::hiworld extension to perl:
    perl Makefile.PL
    make
    make test
    make install
(Note also, statically linked Howdy perl binaries can be made by running something like this:
    make -f Makefile.aperl inst_perl MAP_TARGET=howdyperl
after make and before make test. Scripts written with a statically linked howdyperl will need to start with #!/path/to/howdyperl.)

After make install it is possible to run perl scripts that call the hiworld() subroutine in as little as three simple lines of perl:

    #!/usr/bin/perl
    use Howdy;
    $return = &Howdy::hiworld($a, $b);
For illustrative and testing purposes I have made a distribution copy of the Howdy "module" for perl, as it appears up to this point in the discussion.
Lesson 3: @ISA & Tk::Howdy
Hmm... the 3 line test script from lesson 2 uses a fully qualified subroutine name. What happens if we try:
    #!/usr/bin/perl
    use Howdy;
    $return = &hiworld($a, $b);
we get error messages like:
Lesson 4: Typemaps
Hmm... the 3 line test script above in lesson 2 provides for
    $return = &Howdy::hiworld($a, $b);
which will complain under -w about $a and $b not being initialized. Let's re-write that sample use script as:
    #!/usr/bin/perl -w
    use Howdy;
    my ($a, $b) = (0, 0);
    $return = &Howdy::hiworld($a, $b);
    print "\$return = $return, \$a = $a, \$b = $b\n";


I am:
Peter Prymmer
Wilson Synchrotron Laboratory
Cornell University
Ithaca, NY 14853

pvhp@lns62.lns.cornell.edu

comp.lang.perl.tk | perl/Tk FAQ | FAQ Table of Contents