#!/usr/bin/perl
use warnings;
use strict;
use MIME::Base64;
use threads;
use threads::shared;
# apparently the &delete_event sub needs this to exit without GLib errors
# it depends where you create the thread
# ...i'm creating it well down in the Gtk2 code
Glib::Object->set_threadsafe (1);
use Gtk2 qw/-init -threads-init/;
# usage: ./scriptname number_horses
# the animated gif support in Gtk2 is a bit limited, and
# it will only load from a disk file..... so i include
# the base64 encoded image at the bottom, and write it to
# disk, if not already done
if( ! -e './horse.gif' ){
open (FH, '>', 'horse.gif') or die "$!\n";
print FH get_horsey();
close FH;
}
my $numworkers = shift || 6;
my %threads;
share my $finish;
$finish = 0;
share my $go;
$go = 0;
share my $die;
$die = 0;
share my $restart;
$restart = 0;
my $window = Gtk2::Window->new;
$window->signal_connect('destroy', \&delete_event);
$window->set( border_width => 15 );
$window->set_size_request(800,600);
my $vbox0 = Gtk2::VBox->new(0,0);
$window->add($vbox0);
my $hbox = Gtk2::HBox->new( 0, 6 );
$hbox->set_size_request(5,50);
$vbox0->pack_start($hbox,0,0,0);
my $sbutton = Gtk2::Button->new(' Start ');
$sbutton->signal_connect( "clicked" => \&start );
$hbox->pack_start($sbutton,0,0,0);
my $rbutton = Gtk2::Button->new(' Reset ');
$rbutton->signal_connect( "clicked" => \&re_set );
$hbox->pack_start($rbutton,0,0,0);
my $lab = Gtk2::Label->new();
$lab->set_markup("Race to 500");
$hbox->pack_start($lab,0,0,0);
my $ebutton = Gtk2::Button->new('Exit');
$ebutton->signal_connect( "clicked" => \&delete_event );
$hbox->pack_end($ebutton,0,0,0);
my $winlab = Gtk2::Label->new();
$winlab->set_markup(":Winner");
$hbox->pack_end($winlab,0,0,0);
my $wins = Gtk2::Label->new();
$wins->set_markup(" ? ");
$hbox->pack_end($wins,0,0,0);
my $vbox = Gtk2::VBox->new();
my $sw = Gtk2::ScrolledWindow->new (undef, undef);
$sw->set_shadow_type ('etched-out');
$sw->set_policy ('automatic', 'automatic');
$sw->set_size_request (500, 500);
$sw->set_border_width(5);
$vbox0->pack_start($sw,1,1,0);
$sw->add_with_viewport($vbox);
my $pba = Gtk2::Gdk::PixbufAnimation->new_from_file ('horse.gif');
foreach my $tr (1..$numworkers){
my $trackframe = Gtk2::Frame->new($tr);
$vbox->pack_start( $trackframe, 0, 0, 0 );
$trackframe->set_border_width(3);
my $trackhbox = Gtk2::HBox->new( 0, 6 );
$trackhbox->set_size_request(5,50);
$trackframe->add( $trackhbox );
my $anim = Gtk2::Image->new_from_animation ($pba);
$trackhbox->pack_start( $anim, 0, 0, 0 );
my $scorelab = Gtk2::Label->new();
$scorelab->set_markup("0");
$trackhbox->pack_end( $scorelab, 0, 0, 0 );
$threads{$tr} = threads->new( \&run, $tr, $winlab, $wins, $trackhbox, $anim, $scorelab );
}
$window->show_all();
$winlab->hide;
$wins->hide;
Gtk2->main;
sub start{
$sbutton->set_sensitive(0);
{
lock $finish;
$finish = 0;
lock $restart;
$restart = 0;
lock $go;
$go = 1;
}
}
sub re_set{
$wins->hide;
$winlab->hide;
{
lock $finish;
$finish = 1;
lock $restart;
$restart = 1;
lock $go;
$go = 0;
}
$wins->hide;
$winlab->hide;
$sbutton->set_sensitive(1);
}
sub delete_event{
foreach my $tr (1..$numworkers){
$go = 0;
$die = 1;
$threads{$tr}->join;
}
Gtk2->main_quit;
return 0;
}
sub run{
my ($tr,$winlab, $wins, $trackhbox, $anim, $scorelab ) = @_;
$|++;
while(1){
if($die == 1){ return };
if ( $go == 1 ){
my $tot = 0;
while(1) {
my $whip = rand(5) - rand (1);
$tot += 5 + (rand 10) + $whip;
if ( $restart == 1){
Glib::Idle->add(
sub{
$wins->hide;
$winlab->hide;
$trackhbox->set_child_packing ( $anim, 0, 0, 0 , 'start');
$scorelab->set_markup("0");
}
);
goto RESET;
}
if ( $finish == 0){
if ($restart == 1){goto RESET}
Glib::Idle->add(
sub{
$tot ||= 0;
my $total = sprintf('%.2f', $tot );
$total ||= 0.00;
if ($restart == 0){
$trackhbox->set_child_packing ( $anim, 0, 0,$total , 'start');
$scorelab->set_markup("$total");
}
#check for winner
if($total > 500){
lock($finish); # prevent thread races :-)
$finish = 1;
$scorelab->set_markup("$total");
$wins->set_markup("$tr");
$wins->show;
$winlab->show;
}
});
}
RESET:
if($die == 1){ return };
if ( $go == 0 ){last }
select(undef,undef,undef,.1);
}
# end of go loop
}else{
select (undef,undef,undef,.1)
#sleep while loop
}
} #end of main while loop
}
sub get_horsey{
return decode_base64(
'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'
) or die $!;
}