#!/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 $!; }