Contents:
Animated Clock
Game of Concentration
Introduction to Imagemaps
Calendar Manager
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.