In this final chapter of practical advice and code, we will
look at three applications: a simple animated clock, the game of
Concentration, and a Calendar Manager. All three of these examples
utilize a combination of the various techniques presented up to
this point.
This example creates the effect of an animated
digital clock by repeatedly generating dynamic GIF images and sending
them to the browser using server push (see the discussion in Chapter 6, Hypermedia Documents). You can use the techniques
presented in this example to create CGI programs that continuously
display such information as system load averages, stock prices,
or sports scores. However, programs like these can heavily tax the
host machine, although they may be fun and entertaining. So you
should use them only if there is an absolute need to do so.
To summarize the method used in this example: First we check
that the browser is Netscape Navigator, version 1.1 or higher. That's
because Netscape is the only browser that currently supports server
push. We then generate a new image every few seconds and send it
to the client. To create the image, we'll use the same gd
extension to Perl that we showed in Chapter 6, Hypermedia Documents.
We have to send the data as a special MIME type
called multipart/x-mixed-replace so that the
client replaces each old image with the new one. Following the MIME
standard, we send an "--End--" string at the end of each image.
Here is the code:
#!/usr/local/bin/perl5
use GD;
$| = 1;
$font_length = 8;
$font_height = 16;
$boundary_string = "\n" . "--End" . "\n";
$end_of_data = "\n" . "--End--" . "\n";
The program turns output buffering
off by setting Perl's $| variable. The boundary
strings for server push are defined.
$delay_time = 5;
$max_updates = 10;
The $delay_time variable reflects the
time between image updates. The maximum number of updates performed
by this program is set to 10. The reason for setting these variables
is so that the user does not tax the system by watching the updates
for an infinite amount of time.
print "HTTP/1.0 200 OK", "\n";
This
CGI script outputs the complete HTTP header (see
Chapter 3, Output from the Common Gateway Interface).
Server push animation appears smooth only if buffering is turned
off and a complete header is output.
$browser = $ENV{'HTTP_USER_AGENT'};
if ($browser =~ m#^Mozilla/(1\.[^0]|[2-9])#) {
print "Content-type: multipart/x-mixed-replace;boundary=End", "\n";
print $boundary_string;
This if
block runs if the browser is Netscape Navigator, version 1.1 or
higher.
for ($loop=0; $loop < $max_updates; $loop++) {
&display_time ();
print $boundary_string;
sleep ($delay_time);
}
The display_time
subroutine determines the current time, creates an image, outputs
the image/gif MIME type, and
displays the image. The boundary string is sent to the browser indicating
the end of image data. The sleep command then
waits for the specified amount of time.
&display_time ("end");
print $end_of_data;
Once the loop is terminated,
the display_time subroutine is called one final
time, with an argument. The "end" argument instructs the subroutine
to draw the clock in a different way--as we will soon see. Finally,
the last boundary string is sent to the browser.
} else {
&display_time ("end");
}
exit(0);
If the browser does not support server push, the display_time
subroutine is called just once to display a static image of the
current time.
The display_time
subroutine does most of the work for the program:
sub display_time
{
local ($status) = @_;
local ($seconds, $minutes, $hour, $ampm, $time, $time_length,
$x, $y, $image, $black, $color);
print "Content-type: image/gif", "\n\n";
($seconds, $minutes, $hour) = localtime (time);
if ($hour > 12) {
$hour -= 12;
$ampm = "pm";
} else {
$ampm = "am";
}
if ($hour == 0) {
$hour = 12;
}
$time = sprintf ("%02d:%02d:%02d %s", $hour, $minutes, $seconds, $ampm);
The
current time is formatted and stored in the variable $time.
The output of this variable will look like this: 09:27:03
pm.
$time_length = length($time);
$x = $font_length * $time_length;
$y = $font_height;
The size of the image is calculated, based on the length of
the $time string multiplied by the font dimensions.
$image = new GD::Image ($x, $y);
$black = $image->colorAllocate (0, 0, 0);
A
new image is created with black as the background color.
if ($status eq "end") {
$color = $image->colorAllocate (0, 0, 255);
$image->transparent ($black);
} else {
$color = $image->colorAllocate (255, 0, 0);
}
If the argument passed to this script
is "end", the color of the text is set to blue. In addition, black
is set as the transparent color. In other words, black will not
appear in the image, and as a result the blue text will appear without
any image border. If an argument was not passed, the text color
is set to red.
$image->string (gdLargeFont, 0, 0, $time, $color);
print $image->gif;
}
Finally,
the image is displayed to standard output.