#!/usr/bin/perl # # ivybanner, a banner to launch demos using ivy # # Author: Daniel Etienne # # ivybanner is derived from the toccata-banner application # written by Johnny Accot and Michelle Jacomi and maintained # by Christophe Mertz # # $Id$ # use strict; use Tk; use Getopt::Long; use File::Basename; use vars qw(%opt); # where you may find image files use lib "/usr/share/ivybanner"; # variables definition #------------------------ my $logdir = "/tmp"; my $logfile = "ivybanner-"; my $conffile = "/etc/ivybanner.conf"; my $base_width = 1024; my $base_height = 768; my $coef = 1; # the following variables can be overloaded in configuration file my $ivylaunch_command = "/usr/bin/ivylaunch"; my $default_domain; my @ivyports = (2010, 3009, 3110, 3945); my $default_port = 3110; my $demossortingpolicy = 'mtime'; my $testssortingpolicy = 'mtime'; my %SIZES = ( "small" => { "width" => 640, "height" => 480, "fontspec_large" => '-b&h-lucida-bold-i-normal-sans-15-150-100-100-p-216-iso8859-1', "fontspec_medium" => '-adobe-helvetica-medium-r-normal--11-80-100-100-p-60-iso10646-1', "logo_x" => -120, "logo_y" => -144, "logo_small_x" => 225, "logo_small_y" => -16, }, "medium" => { "width" => 800, "height" => 600, "fontspec_large" => '-b&h-lucida-bold-i-normal-sans-17-120-100-100-p-108-iso10646-1', "fontspec_medium" => '-adobe-helvetica-medium-r-normal-*-12-120-75-75-p-67-iso10646-1', "logo_x" => -40, "logo_y" => -80, "logo_small_x" => 290, "logo_small_y" => 0, }, "normal" => { "width" => $base_width, "height" => $base_height, "fontspec_large" => '-b&h-lucida-bold-i-normal-sans-24-240-100-100-p-216-iso8859-1', "fontspec_medium" => '-adobe-helvetica-bold-r-normal--17-120-100-100-p-92-iso10646-1', "logo_x" => 0, "logo_y" => 0, "logo_small_x" => 390, "logo_small_y" => 26, }, ); # colors spec my $darkbg = '#1E161B'; my $bg = '#5C5655'; my $hlbg = '#7CC452'; my $hlbg2 = 'green'; my $fg = '#FCFAFC'; my $selcolor = 'yellow'; # misc my $demosdir; my $testsdir; my $selected_button; my $selected_demo; my $view_button; my $launch_button; my %apps; my $currentlogfile; my @subdirbtn; my $level = 1; # parse options #------------------------ Getopt::Long::Configure('pass_through'); &usage unless Getopt::Long::GetOptions(\%opt, 'conf=s', 'testdir=s', '-dir=s', 'nocursor', 'help', 'override', 'size=s', 'smallsize' ); &usage if $opt{help}; $conffile = $ENV{"HOME"} . "/.ivybanner" if ( -f $ENV{"HOME"} . "/.ivybanner"); if ($opt{conf}) { if (not -f $opt{conf}) { die "No such file $opt{conf}\n"; } elsif (not -r $opt{conf}) { die "File $opt{conf} is unreadable\n"; } else { $conffile = $opt{conf}; } } if ($opt{dir}) { $demosdir = $opt{dir}; } elsif ($ENV{'IVYCONFIGSDIR'}) { $demosdir = $ENV{'IVYCONFIGSDIR'}; } else { $demosdir = "."; } if ($opt{testdir}) { $testsdir = $opt{testdir}; } elsif ($ENV{'IVYTESTSDIR'}) { $testsdir = $ENV{'IVYTESTSDIR'}; } else { $testsdir = "."; } $opt{size} = "normal" if not $SIZES{$opt{size}}; $opt{size} = "small" if $opt{smallsize}; $coef = $SIZES{$opt{size}}->{width} / $base_width; # test directories #---------------------------- for ($demosdir, $testsdir) { if (not -d $_) { die "No such directory $_\n"; } elsif (not -r $_) { die "Directory $_ is unreadable\n"; } } # parse configuration file #-------------------------- if (open S, $conffile) { while () { if (/^ivylaunch_command\s*:\s*([\w\/-]+)/) { $ivylaunch_command = $1; } elsif (/^default_domain\s*:\s*(.*)/) { $default_domain = $1; } elsif (/^4ports\s*:\s*(.*)/) { my $ports = $1; @ivyports = split(/\s*,\s*/, $ports); } elsif (/^default_port\s*:\s*([\w\/-]+)/) { $default_port = $1; } elsif (/^demos_sorting_policy\s*:\s*(\w+)/) { $demossortingpolicy = $1 if $1 eq 'mtime' or $1 eq 'alphabetical'; } elsif (/^tests_sorting_policy\s*:\s*(\w+)/) { $testssortingpolicy = $1 if $1 eq 'mtime' or $1 eq 'alphabetical'; } } close S; } $ivylaunch_command .= " -override" if $opt{override}; # build the presentation frame #------------------------------ my $mw = MainWindow->new (-bg => 'black'); if ($opt{nocursor}) { open(CURSOR, ">/tmp/cursor-ivybanner"); print CURSOR "#define cursor_width 1\n"; print CURSOR "#define cursor_height 1\n"; print CURSOR "#define cursor_x_hot 0\n"; print CURSOR "#define cursor_y_hot 0 \n"; print CURSOR "static char cursor_bits[] = { 0x20};\n"; close(CURSOR); $mw->configure(-cursor => ['@'.'/tmp/cursor-ivybanner', '/tmp/cursor-ivybanner', 'black', 'black']); unlink("/tmp/cursor-ivybanner"); } else { $mw->configure(-cursor => 'circle'); } $mw->geometry ($SIZES{$opt{size}}->{width}."x".$SIZES{$opt{size}}->{height}); $mw->title ('ivybanner'); $mw->client ('ivybanner'); $mw->resizable(0,0); #$mw->scale($maingroup, $winW/1280, $winH/1024); if $smallsized; my $logo_small =$mw->Photo ('logo-small', -file => Tk::findINC('logo.gif')); my $logo_bg = $mw->Photo ('logo_bg', -file => Tk::findINC('background.gif')); my ($fm1, $fm2, $fm3, $tlog, $tlog2); $fm1 = $mw->Frame->pack(-expand => 1, -fill => 'both'); my @buttonattr = (-width => 8*$coef, -height => 3, -borderwidth => 1, -font => $SIZES{$opt{size}}->{fontspec_large}, -relief => 'flat', -highlightthickness => 3, -highlightbackground => $hlbg, -activebackground => $bg, -activeforeground => $fg, -background => $bg, -foreground => $fg); $fm1->Label(-image => $logo_bg)->place(-x => $SIZES{$opt{size}}->{logo_x}, '-y' => $SIZES{$opt{size}}->{logo_y}); $fm1->Button(-text => 'demos', -command => [\&demosframe, 'demos', $demosdir], @buttonattr)->place(-x => 800*$coef, '-y' => 100*$coef); $fm1->Button(-text => 'tests', -command => [\&demosframe, 'tests', $testsdir], @buttonattr)->place(-x => 800*$coef, '-y' => 330*$coef); $fm1->Button(-text => 'quit', @buttonattr, -command => [sub {exit(0)}])->place(-x => 800*$coef, '-y' => 560*$coef); print "--- Environment variables\n"; print "\$IVYCONFIGSDIR=",$ENV{'IVYCONFIGSDIR'},"\n"; print "\$IVYTESTSDIR=",$ENV{'IVYTESTSDIR'},"\n"; print "--- Parameters\n"; print "configuration file : $conffile\n"; print "declared ports : ",join(',', @ivyports),"\n"; print "default domain : $default_domain\n"; print "default port : $default_port \n"; print "demos directory : $demosdir \n"; print "tests directory : $testsdir \n"; print "demos sorting policy : $demossortingpolicy \n"; print "tests sorting policy : $testssortingpolicy \n"; print "ivylaunch command : $ivylaunch_command \n"; print "======================================================\n"; MainLoop; sub execute { my ($demo, $port, $domain) = @_; my $bdemo = basename($demo); unless (defined $demo) { $mw->bell; return; } $mw->withdraw; my $command = $ivylaunch_command; $command .= " -b $domain:$port" if $port; my ($sec,$min,$hour,$mday,$mon,$year) = localtime(); my $now_string = sprintf ("%02d%02d%02d_%02d:%02d:%02d", $year-100, 1+$mon, $mday,$hour,$min,$sec); my $tmp_file_prefix = $logdir."/".$logfile ; print "===========================================================\n"; print "Ivy demos launched at ".localtime()."\n"; print "Selected config is $bdemo on bus $domain:$port\n"; print "Traces are in $tmp_file_prefix$now_string\n"; print "Run $command $demo\n"; system ("$command $demo 2>&1 | tee $tmp_file_prefix$now_string"); $mw->deiconify; $mw->raise; } # end execute sub demosframe { my $title = shift; my $path = shift; my @packinfo = $fm1->packInfo; $fm1->packForget(); $fm2 = $mw->Frame(-background => $darkbg)->pack(-expand => 1, -fill => 'both'); my $lb_logo; $fm2->Frame(-background => $darkbg, -relief => 'groove', -borderwidth => 5, -width => $SIZES{$opt{size}}->{width}+20*$coef, -height => 160*$coef) ->place(-x => -10*$coef, '-y' => -10*$coef); $lb_logo = $fm2->Label(-image => $logo_small, -borderwidth => 0)->place(-x => $SIZES{$opt{size}}->{logo_small_x}, '-y' => $SIZES{$opt{size}}->{logo_small_y}); # Note that we use a decorative frame for each widget because frame size can be # set in pixel unit whereas buttons family manages size in characters number # (so, the size depends on used font) # ivy network parameters frames #-------------------------------------------------- my $y = 25*$coef; my $x = 70*$coef; my $h = 50*$coef; my $w = 130*$coef; my $dx = 15*$coef; my $dy = 10*$coef; my @placeattr = (-x => 2*$coef, '-y' => 2*$coef); my @lab_placeattr = (-x => $w - 8*$coef, '-y' => 10*$coef ); my @frameattr = (-relief => 'flat', -highlightthickness => 3*$coef*$coef, -highlightbackground => $hlbg, -highlightcolor => $hlbg, -height => $h, -width => $w, -background => $bg); my @labelattr = (-width => 9, -height => 2, -borderwidth => 1, -relief => 'flat', -highlightthickness => 0, -background => $bg, -foreground => $fg); my @cbuttonattr = (@labelattr, -font => $SIZES{$opt{size}}->{fontspec_medium}, -activeforeground => $fg, -activebackground => $bg, -selectcolor => $selcolor); my @entryattr = (-width => 15*$coef, -state => 'disable', -font => $SIZES{$opt{size}}->{fontspec_medium}, -background => $bg); my $fm_localhost = $fm2->Frame(@frameattr)->place(-x => $x, '-y' => $y); my $fm_default = $fm2->Frame(@frameattr)->place(-x => $x+$w+$dx, '-y' => $y); my ($dom_default, $dom_local, $dom_own); my $fm_ownnet = $fm2->Frame(@frameattr, -width => 2*$w+$dx)->place(-x => $x, '-y' => $y+$h+$dy); my $cb_local = $fm_localhost->Checkbutton(@cbuttonattr, -variable => \$dom_local, -text => 'localhost')->place(@placeattr); my $cb_default = $fm_default->Checkbutton(@cbuttonattr, -variable => \$dom_default, -text => "default\ndomain")->place(@placeattr); if (defined($default_domain)) { $cb_default->select; } else { $cb_default->configure(-state => 'disabled'); } my $cb_ownnet = $fm_ownnet->Checkbutton(-text => "other\ndomain", -variable => \$dom_own, @cbuttonattr)->place(@placeattr); my $en_ownnet = $fm_ownnet->Entry(@entryattr)->place(@lab_placeattr); $cb_ownnet->configure(-command => sub { my $v = $cb_ownnet->cget(-variable); if ($$v == 1) { $en_ownnet->configure(-state => 'normal', -background => $fg); } else { $en_ownnet->configure(-state => 'disable', -background => $bg); } }); # ivy port parameters frames #-------------------------------------------------- $x = 540*$coef; my @ports = (@ivyports); my $port = $default_port; my @fm_port; $fm_port[0] = $fm2->Frame(@frameattr)->place(-x => $x, '-y' => $y); $fm_port[1] = $fm2->Frame(@frameattr)->place(-x => $x+$w+$dx, '-y' => $y); $fm_port[2] = $fm2->Frame(@frameattr)->place(-x => $x+2*$w+2*$dx, '-y' => $y); $fm_port[3] = $fm2->Frame(@frameattr)->place(-x => $x, '-y' => $y+$dy+$h); my $fm_ownport = $fm2->Frame(@frameattr, -width => 2*$w+$dx)->place(-x => $x+$w+$dx, '-y' => $y+$h+$dy); my $en_ownport = $fm_ownport->Entry(@entryattr)->place(@lab_placeattr); for (my $i=0; $i < @fm_port; $i++) { $fm_port[$i]->Radiobutton(@cbuttonattr, -variable => \$port, -value => $ports[$i], -command => sub { $en_ownport->configure(-state => 'disable', -background => $bg)}, -text => $ports[$i])->place(@placeattr); } $fm_ownport->Radiobutton(@cbuttonattr, -value => 'own', -variable => \$port, -command => sub { $en_ownport->configure(-state => 'normal', -background => $fg)}, -text => "other\nport")->place(@placeattr); my $policy = ($title eq 'tests') ? $testssortingpolicy : $demossortingpolicy; &subdirframe($policy, $fm2, $path, $x, $y, $dx, $dy); # control buttons bottom frame #-------------------------------------------------- $y = 680*$coef; my @labelattr = (-width => 6, -height => 2, -borderwidth => 1, -font => $SIZES{$opt{size}}->{fontspec_large}, -relief => 'flat', -background => $darkbg, -foreground => $fg); my @buttonattr = (-width => 6, -height => 2, -borderwidth => 1, -font => $SIZES{$opt{size}}->{fontspec_large}, -relief => 'flat', -highlightthickness => 3*$coef*$coef, -highlightbackground => $hlbg, -activebackground => $bg, -activeforeground => $fg, -background => $bg, -foreground => $fg); $fm2->Frame(-background => $darkbg, -relief => 'groove', -borderwidth => 5*$coef, -width => $SIZES{$opt{size}}->{width}+20*$coef, -height => $SIZES{$opt{size}}->{height})->place(-x => -10*$coef, '-y' => $y - 30*$coef); $fm2->Button(@buttonattr, -text => "top", -command => sub { $level = 1; &off(); $fm2->destroy; $fm1->pack(@packinfo); })->place(-x => 70*$coef, '-y' => $y); # $fm2->Button(@buttonattr, # -command => sub {}, # -text => "kill")->place(-x => 290, '-y' => $y); $fm2->Label(@labelattr, -text => $title)->place(-x => 290*$coef, '-y' => $y); $fm2->Button(@buttonattr, -command => \&viewLog, -text => "view\nlog")->place(-x => 510*$coef, '-y' => $y); $view_button = $fm2->Button(@buttonattr, -state => 'disabled', -command => sub {&viewConfig($selected_demo)}, -text => "view\nconfig")->place(-x => 670*$coef, '-y' => $y); $launch_button = $fm2->Button(@buttonattr, -state => 'disabled', -command => sub { my $p = $port; my $d; if ($port eq 'own') { $p = $en_ownport->get(); } $d .= "127, " if $dom_local; $d .= $default_domain."," if $dom_default; $d .= $en_ownnet->get() if $dom_own; $d =~ s/,\s*$// if $d; $d =~ s/\s+// if $d; #print "port=$p domain=$d\n"; &execute($selected_demo, $p, $d); }, -text => "launch")->place(-x => 830*$coef, '-y' => $y); } # end demosframe sub subdirframe { my $filesortingpolicy = shift; my $fm2 = shift; my $path = shift; my $x = shift; my $y = shift; my $dx = shift; my $dy = shift; for (@subdirbtn) { $_->destroy if Tk::Exists($_); } @subdirbtn = (); # demos frame #-------------------------------------------------- my @bbox = (40*$coef, 160*$coef, $SIZES{$opt{size}}->{width}-40*$coef, 640*$coef); my $fmh = 90*$coef; my $fmw = 180*$coef; my $butw = 14*$coef; my $max_columns = int(($bbox[2]-$bbox[0])/$fmw); my $max_rows = int(($bbox[3]-$bbox[1])/$fmh); my $maxnb = $max_columns*$max_rows; my @placeattr = (-x => 10*$coef, '-y' => 5*$coef); my @frameattr = (-relief => 'flat', -highlightthickness => 3*$coef*$coef, -highlightbackground => $hlbg, -height => $fmh, -width => $fmw, -background => $bg); my @rbuttonattr = (-width => $butw, -height => 4*$coef, -borderwidth => 1, -font => $SIZES{$opt{size}}->{fontspec_medium}, -relief => 'flat', -highlightthickness => 0, -background => $bg, -foreground => $fg, -activeforeground => $fg, -activebackground => $bg, -selectcolor => $selcolor, -indicatoron => 0, ); my @buttonattr = (-width => $butw, -height => 4*$coef, -borderwidth => 1, -font => $SIZES{$opt{size}}->{fontspec_medium}, -relief => 'flat', -highlightthickness => 3*$coef*$coef, -highlightbackground => $hlbg, -activebackground => $bg, -activeforeground => $fg, -background => $bg, -foreground => $fg); my $c = 1; my $r = 1; # sélection du contenu du répertoire # => création de la liste d'éléments [file, type, label] #-------------------------------------------------- opendir PREFIX, $path; #my @file_liste = grep /:/, readdir PREFIX; my @file_liste = readdir PREFIX; closedir PREFIX; my @cleanfilelist = (); foreach my $file (@file_liste) { my $type; if (-f $path."/".$file) { $type = 'f'; } elsif (-d $path."/".$file) { $type = 'd'; } next unless defined $type; next if ($type eq 'f' and $file !~ /:/); next if ($type eq 'f' and $file =~ /\~$/); next if ($type eq 'f' and $file =~ /^\./); next if ($type eq 'f' and $file =~ /\%$/); next if ($type eq 'd' and $file =~ /^\./); next if ($type eq 'd' and $file eq 'CVS'); my $label; if ($type eq 'd') { $label = $file; # cas des fichiers : seuls les 2 derniers champs séparés par des ':' # seront affichés } elsif ($type eq 'f') { my @fields = split(/:/, $file); my ($category, $extinfo); if (@fields == 1) { $category = $fields[0]; } elsif (@fields > 1) { ($category, $extinfo) = ($fields[-2], $fields[-1]); } my $len = length($category); if ($len >= $butw) { $category = substr($category, 0, $butw).":".substr($category, $butw); } $len = length($extinfo); if ($len > $butw) { $extinfo = substr($extinfo, 0, $butw).":".substr($extinfo, $butw); } $label = $category.":".$extinfo; } push(@cleanfilelist, [$file, $type, $label]); } # règle de tri des fichiers #-------------------------------------------------- if ($filesortingpolicy eq 'mtime') { @cleanfilelist = sort { (stat("$path/$a->[0]"))[9] <=> (stat("$path/$b->[0]"))[9] } @cleanfilelist; } else { @cleanfilelist = sort { uc($a->[2]) cmp uc($b->[2]) } @cleanfilelist; } @cleanfilelist = (['..', 'd', 'Up'], @cleanfilelist) if $level > 1; # calcul des limites #-------------------------------------------------- my $demosnb = @cleanfilelist; if ($demosnb > $maxnb) { splice(@cleanfilelist, $maxnb-1); } my $maxc = $max_columns; for my $i (1, 4, 9, 16, 25) { if ($demosnb <= $i) { $maxc = sqrt($i); last; } } # affichage #-------------------------------------------------- $dx = ($bbox[2] - $bbox[0] - $maxc*$fmw)/($maxc+1); $dy = ($bbox[3] - $bbox[1] - $maxc*$fmh)/($maxc+1); $x = $bbox[0] + $dx; $y = $bbox[1] + $dy; #print "maxc=$maxc x=$x y=$y dx=$dx dy=$dy\n"; my ($xi, $yi); for my $fileref (@cleanfilelist) { my ($file, $type, $label) = @$fileref; #print "file=$file label=$label mtime=",(stat("$path/$file"))[9],"\n"; ($xi, $yi) = ($x + ($c-1)*($dx+$fmw), $y + ($r-1)*($dy+$fmh)); my $b = $fm2->Button(@buttonattr)->place(-x => $xi, '-y' => $yi); if ($type eq 'f') { $label =~ s/:/\n/; $b->configure(-text => $label, -command => sub { &off(); &on($b, $path."/".$file); }); } else { $b->configure(-bg => $darkbg, -activebackground => $darkbg, -text => $label, -command => sub { &off; if ($file eq '..') { $level--; } else { $level++; } &subdirframe($filesortingpolicy, $fm2, $path."/".$file, $x, $y, $dx, $dy); }); } push(@subdirbtn, $b); if ($c >= $maxc) { $c=1; $r++; } else { $c++; } } if ($demosnb > $maxnb) { #print "maxnb=$maxnb\n"; my $many = $demosnb - $maxnb + 1; ($xi, $yi) = ($x + ($c-1)*($dx+$fmw), $y + ($r-1)*($dy+$fmh)); my $disa = $fm2->Button(@buttonattr, -state => "disabled", -text => "and $many\nmore...", )->place(-x => $xi, '-y' => $yi); push(@subdirbtn, $disa); } } # end subdirframe sub viewConfig { my ($file) = @_; my $dir = dirname($file); my $bfile = basename($file); unless ($file) { $mw->bell; return; } my @packinfo = $fm2->packInfo; $fm2->packForget(); $fm3 = $mw->Frame(-background => $darkbg)->pack(-expand => 1, -fill => 'both'); my $t = $fm3->Scrolled('Text', -background => $bg, -relief => 'sunken', -foreground => $fg, -borderwidth => 2, -highlightthickness => 3*$coef, -highlightbackground => $hlbg, -font => $SIZES{$opt{size}}->{fontspec_medium}, -height => 28, -width => 102, -scrollbars => 'oe'); $t->Subwidget('yscrollbar')->configure(-width => 40*$coef, -borderwidth => 3*$coef, -relief => 'flat', -highlightthickness => 3*$coef, -troughcolor => $darkbg, -highlightbackground => $hlbg, -activebackground => $hlbg, -background => $bg); $t->place(-x => 50*$coef*$coef*$coef, '-y' => 120*$coef); $t->tagConfigure('comment', -foreground => 'gray70'); open(C_FILE, "cpp -traditional $file|"); while () { next if /^\s*$/; if (/^\s*\#/) { $t->insert('end',$_."\n", 'comment'); } else { $t->insert('end',$_."\n"); } } close C_FILE; $t->configure(-state => 'disabled'); my @labelattr = (-width => 40*$coef, -height => 2, -borderwidth => 1, -font => $SIZES{$opt{size}}->{fontspec_large}, -relief => 'flat', -highlightthickness => 3*$coef, -highlightbackground => $hlbg, -background => $bg, -foreground => $fg); my @buttonattr = (@labelattr, -width => 8, -activebackground => $bg, -activeforeground => $fg); my $text1 = "content of $bfile"; my $text2 = "in $dir"; my $len1 = length($text1); my $len2 = length($text2); $fm3->Label(-text => "$text1\n$text2", @labelattr, -width => ($len1 > $len2) ? $len1 : $len2, )->place(-x => 50*$coef*$coef*$coef, '-y' => 30*$coef); $fm3->Button(@buttonattr, -text => "back", -command => sub { $fm3->destroy; $fm2->pack(@packinfo); })->place(-x => 70*$coef, '-y' => 680*$coef); } # end viewConfig sub viewLog { my $file; my $dir; my @packinfo = $fm2->packInfo; $fm2->packForget(); $fm3 = $mw->Frame(-background => $darkbg)->pack(-expand => 1, -fill => 'both'); opendir PREFIX, $logdir; my @files = readdir PREFIX; closedir PREFIX; my %logfiles; my %dates; my @logfiles; for (@files) { next unless /^$logfile(\d\d)(\d\d)(\d\d)_(\d\d):(\d\d):(\d\d)/; push(@logfiles, $_); $logfiles{$_} = $1.$2.$3.$4.$5.$6; $dates{$_} = "$3/$2/$1 $4:$5:$6"; } my @logfiles = sort {$logfiles{$a} <=> $logfiles{$b}} (@logfiles); my $cnt = $#logfiles; my @labelattr = (-width => 40*$coef, -height => 2, -borderwidth => 1, -font => $SIZES{$opt{size}}->{fontspec_large}, -relief => 'flat', -highlightthickness => 3*$coef, -highlightbackground => $hlbg, -background => $bg, -foreground => $fg); my @buttonattr = (@labelattr, -width => 8, -activebackground => $bg, -activeforeground => $fg); my $l = $fm3->Label(@labelattr, )->place(-x => 50*$coef*$coef*$coef, '-y' => 30*$coef); $fm3->Button(@buttonattr, -text => "back", -command => sub { $fm3->destroy; $fm2->pack(@packinfo); })->place(-x => 70*$coef, '-y' => 680*$coef); my $select_button = $fm3->Button(@buttonattr, -text => "select...", -command => \&selectappname, )->place(-x => 400*$coef, '-y' => 680*$coef); my $prev_button = $fm3->Button(@buttonattr, -text => "previous\nlog", )->place(-x => 600*$coef, '-y' => 680*$coef); my $next_button = $fm3->Button(@buttonattr, -text => "next\nlog", -state => 'disabled', )->place(-x => 800*$coef, '-y' => 680*$coef); if (@logfiles <= 1) { $prev_button->configure(-state => 'disabled'); $next_button->configure(-state => 'disabled'); } $prev_button->configure(-command => sub { $tlog2->destroy if Tk::Exists($tlog2); $cnt--; &readlog($logfiles[$cnt], $dates{$logfiles[$cnt]}, $l) if $cnt >= 0; $prev_button->configure(-state => 'disabled') if $cnt <= 0; $next_button->configure(-state => 'normal'); }); $next_button->configure(-command => sub { $tlog2->destroy if Tk::Exists($tlog2); $cnt++; &readlog($logfiles[$cnt], $dates{$logfiles[$cnt]}, $l) if $cnt <= $#logfiles; $next_button->configure(-state => 'disabled') if $cnt >= $#logfiles; $prev_button->configure(-state => 'normal'); }); &readlog($logfiles[$cnt], $dates{$logfiles[$cnt]}, $l) if @logfiles > 0; } # end viewLog sub selectappname { my $w = shift; my $tl = $mw->Toplevel(-background => $darkbg); $tl->title('messages selection'); $tl->resizable(0, 0); my $fm = $tl->Frame(-background => $darkbg, -highlightthickness => 3, -highlightbackground => $hlbg, )->pack(-side => 'top', -padx => 0, -pady => 0); my $fm1 = $fm->Frame(-background => $darkbg)->pack(-side => 'top', -padx => 10, -pady => 10); my $fm2 = $fm->Frame(-background => $darkbg)->pack(-side => 'bottom'); my @labelattr = (-relief => 'flat', -background => $darkbg, -foreground => $fg, -borderwidth => 0, -highlightthickness => 0, -height => 2, -font => $SIZES{$opt{size}}->{fontspec_large}); my @buttonattr = (-relief => 'flat', -background => $bg, -foreground => $fg, -borderwidth => 1, -highlightbackground => $hlbg, -highlightthickness => 3*$coef, -height => 2, -activebackground => $bg, -activeforeground => $fg, -font => $SIZES{$opt{size}}->{fontspec_large}); my @cbuttonattr = (@buttonattr, -font => $SIZES{$opt{size}}->{fontspec_medium}); my $i = 0; my $selapp = 'all'; for my $app (keys(%apps), 'all') { my $col = $i % 3; my $row = int($i/3); $fm1->Radiobutton(@cbuttonattr, -selectcolor => $hlbg, -text => $app, -variable => \$selapp, -value => $app, )->grid(-row => $row, -column => $col, -padx => 5, -pady => 5, -sticky => 'nsew'); $i++; } $fm2->Button(@buttonattr, -text => 'yes', -command => sub { if ($selapp eq 'all') { $tlog2->destroy if Tk::Exists($tlog2); } else { &readpartiallog($selapp); } $tl->destroy; }, )->pack(-side => 'left', -padx => 20, -pady => 20, -expand => 1, -fill => 'both'); $fm2->Button(@buttonattr, -text => 'cancel', -command => sub { $tl->destroy }, )->pack(-side => 'left', -padx => 20, -pady => 20, -expand => 1, -fill => 'both'); # placement at window center $tl->update; my ($x, $y) = ($mw->rootx, $mw->rooty); $x += int($SIZES{$opt{size}}->{width} / 2 - $tl->width/2); $y += int($SIZES{$opt{size}}->{height} / 2 - $tl->height/2); $tl->geometry('+'.$x.'+'.$y); $tl->grab; } # end selectappname sub readlog { # il est plus rapide de recréer un widget Text que de le vider de son # contenu et le remettre à jour !! my ($file, $date, $l) = @_; $file = $logdir."/".$file; $currentlogfile = $file; $tlog2->destroy if Tk::Exists($tlog2); $fm3->update; $tlog = $fm3->Scrolled('Text', -background => $bg, -relief => 'sunken', -foreground => $fg, -font => $SIZES{$opt{size}}->{fontspec_medium}, -height => 28, -width => 102, -borderwidth => 2, -highlightthickness => 3*$coef, -highlightbackground => $hlbg, -state => 'disabled', -scrollbars => 'oe'); $tlog->Subwidget('yscrollbar')->configure(-width => 40*$coef, -borderwidth => 3*$coef, -relief => 'flat', -highlightthickness => 3*$coef, -troughcolor => $darkbg, -highlightbackground => $hlbg, -activebackground => $hlbg, -background => $bg); $tlog->tagConfigure('bold', -foreground => $hlbg); $tlog->place(-x => 50*$coef*$coef*$coef, '-y' => 120*$coef); $l->configure(-text => "loading..."); $l->update; %apps = (); open(FILE, $file) or warn "cannot read file '$file'\n"; $tlog->configure(-state => 'normal'); $tlog->update; while () { my @msg; my $i = 0; while (/\c[\[1m(.*?)\c[\[m([^\c[]*)/g) { my $app = $1; my $msg = $2; #print "app=[$app] [$msg]\n"; if ($app =~ /^\S+\s*$/) { $apps{$app} = 1 if $i == 0; $tlog->insert('end', "$app", 'bold', "$msg"); } else { $tlog->insert('end', "$app $msg"); } $i++; } } $tlog->see('end'); $tlog->configure(-state => 'disabled'); $l->configure(-text => $date); close FILE; } # end readlog sub readpartiallog { my ($selapp) = @_; $tlog2->destroy if Tk::Exists($tlog2); $fm3->update; $tlog2 = $fm3->Scrolled('Text', -background => $bg, -relief => 'sunken', -foreground => $fg, -font => $SIZES{$opt{size}}->{fontspec_medium}, -height => 28, -width => 102, -borderwidth => 2, -highlightthickness => 3*$coef, -highlightbackground => $hlbg, -state => 'disabled', -scrollbars => 'oe'); $tlog2->Subwidget('yscrollbar')->configure(-width => 40*$coef, -borderwidth => 3*$coef, -relief => 'flat', -highlightthickness => 3*$coef, -troughcolor => $darkbg, -highlightbackground => $hlbg, -activebackground => $hlbg, -background => $bg); $tlog2->tagConfigure('bold', -foreground => $hlbg); $tlog2->place(-x => 50*$coef*$coef*$coef, '-y' => 120*$coef); $tlog2->configure(-state => 'normal'); open(FILE, $currentlogfile) or warn "cannot read file '$currentlogfile'\n"; while () { my @msg; my $i = 0; while (/\c[\[1m(.*?)\c[\[m([^\c[]*)/g) { my $app = $1; last if $i == 0 and $app ne $selapp; my $msg = $2; #print "app=[$app] [$msg]\n"; if ($app =~ /^\S+\s*$/) { $tlog2->insert('end', "$app", 'bold', "$msg"); } else { $tlog2->insert('end', "$app $msg"); } $i++; } } $tlog2->see('end'); $tlog2->configure(-state => 'disabled'); $tlog2->update; } # end readpartiallog sub on { my ($b, $file) = @_; &off(); $selected_button = $b; $selected_demo = $file; $b->configure(-foreground => $selcolor, -highlightbackground => $selcolor, -activeforeground => $selcolor); $view_button->configure(-state => 'normal'); $launch_button->configure(-state => 'normal'); } # end on sub off { return unless defined $selected_button and Tk::Exists($selected_button); $selected_button->configure(-foreground => $fg, -highlightbackground => $hlbg, -activeforeground => $fg); $view_button->configure(-state => 'disabled'); $launch_button->configure(-state => 'disabled'); $selected_demo = undef; $selected_button = undef; } # end off sub usage { print "Usage : ivybanner [-help] [-nocursor] [-override] [-conf configfile]\n"; print " [-dir fugueconfdir] [-testdir testsdir]\n"; print "\n"; print "Options :\n"; print " -override option passed to ivylaunch. if true, global agents\n"; print " which are already connected will be overriden\n"; print " -nocursor hide mouse cursor\n"; print " -conf configuration file; default=/etc/ivybanner.conf\n"; print " -dir fugue configs directory\n"; print " -testdir tests directory\n"; print " -smallsize display IvyBanner in 640x480 instead of 1024x768 (deprecated)\n"; print " -size=[small|medium|normal] display IvyBanner in 640x480 (small), 800x600 (medium) or normal (1024x768)\n"; print "\n"; exit 0; } __END__ =head1 NAME ivybanner - an interface for ivy launcher =head1 SYNOPSIS ivybanner [-help] [-nocursor] [-override] [-conf configfile] [-dir fugueconfdir] [-testdir testsdir] [-smallsize] =head1 DESCRIPTION ivybanner provides an interface for selecting ivy parameters (domain and port number) and selecting the configuration file with the applications to be launched on ivy. The aim of ivybanner is to automate the launching of Ivy demonstrations. The main page of ivybanner allows to activate the "Demos" or "Tests" launching page or quit. =head1 OPTIONS =over =item B<-nocursor> Hide mouse cursor (for touchscreen usage). =item B<-conf> file Specify configuration file. Default is $HOME/.ivybanner if available or /etc/ivybanner.conf otherwise. =item B<-dir> directory Specify fugue configurations directory. Default is current directory unless the $IVYCONFIGSDIR variable is set. =item B<-testdir> file Specify tests directory. Default is current directory unless the $IVYTESTSDIR variable is set. =item B<-override> Infer ivylaunch behavior when a B agent is detected on the bus. See ivylaunch(1) man page. =item B<-smallsize> display IvyBanner in 640x480 instead of 1024x768 (deprecated) =item B<-size=[small|medium|normal]> display IvyBanner in 640x480 (small), 800x600 (medium) or normal (1024x768) =back =head1 FILENAME FORMAT To be detected by ivybanner, the files must be located under the demos or tests directories and must respect the following name format : [string0:]string1:string2 The label of ivybanner demos buttons will contain string1 and string2. Note the sub-directories whose name starts with letter '.' will not be explored. =head1 FILE FORMAT The format of fugue configuration files is described in ivylaunch(1) man page. =head1 LOG FILES /tmp/ivybanner-yymmdd_hh:mm:ss =head1 SEE ALSO ivylaunch(1), ivycontrolpanel(1) =head1 AUTHOR Daniel Etienne =head1 HISTORY ivybanner is derived from the toccata-banner application written by Johnny Accot and Michelle Jacomi.