X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;ds=sidebyside;f=techtalk-pse.pl;h=843f6c774064c87d9cdfccb825fd840561c30d41;hb=3fa7fe016fabd3414388a08981ee89e12cd51d81;hp=ae8a9c9a1230113a245d7f3441f2c08d6955363a;hpb=ccd8fffc4ae378f86dc399ea6eefec2f7f9ad8ed;p=techtalk-pse.git diff --git a/techtalk-pse.pl b/techtalk-pse.pl index ae8a9c9..843f6c7 100755 --- a/techtalk-pse.pl +++ b/techtalk-pse.pl @@ -143,14 +143,10 @@ Display version number and exit. =cut my $mozembed; -my $mozembed_first; -my $mozembed_last; GetOptions ("help|?" => \$help, "last" => \$last, "mozembed" => \$mozembed, - "mozembed-first" => \$mozembed_first, - "mozembed-last" => \$mozembed_last, "n=s" => \$start, "splash!" => \$splash, "start=s" => \$start, @@ -170,11 +166,7 @@ if ($version) { die "techtalk-pse: cannot use --start and --last options together\n" if defined $last && defined $start; -# Run with --mozembed: see below. -run_mozembed () if $mozembed; - -# Normal run of the program. -die "techtalk-pse: too many arguments\n" if @ARGV >= 2; +die "techtalk-pse: too many arguments\n" if !$mozembed && @ARGV >= 2; # Get the true name of the program. $0 = abs_path ($0); @@ -191,6 +183,11 @@ if (@ARGV > 0) { } } +# Get the talk directory and set environment variable $talkdir +# which is inherited by all the scripts. +my $talkdir = getcwd; +$ENV{talkdir} = $talkdir; + # Get the files. my @files; my %files; @@ -228,6 +225,11 @@ if (@files == 0) { warn "techtalk-pse: no files found, continuing anyway ...\n" } +# Run with --mozembed: see below. +run_mozembed () if $mozembed; + +# Else, normal run of the program ... + # Work out what slide we're starting on. my $current; if (defined $current) { @@ -242,11 +244,7 @@ if ($splash) { my $w = Gtk2::AboutDialog->new; $w->set_authors ("Richard W.M. Jones"); $w->set_comments ( - "Superior technical demonstration software\n". - "\n". - "Keys\n". - "↑ — Go back one slide\n". - "↓ — Go forward one slide\n" + "Superior technical demonstration software\n" ); $w->set_program_name ("Tech Talk Platinum Supreme Edition (PSE)"); $w->set_version ("@VERSION@"); @@ -255,6 +253,11 @@ if ($splash) { $w->run; print STDERR "calling \$w->destroy on about dialog\n" if $verbose; $w->destroy; + + # The dialog doesn't really get destroyed here. We have + # to add this hack to really destroy it. + Glib::Idle->add (sub { Gtk2->main_quit; return FALSE; }); + Gtk2->main; } MAIN: while (1) { @@ -268,6 +271,9 @@ MAIN: while (1) { print STDERR "i = $i\n" if $verbose; $i-- if $go eq "PREV" && $i > 0; $i++ if $go eq "NEXT" && $i+1 < @files; + $i = 0 if $go eq "FIRST"; + $i = $#files if $go eq "LAST"; + $i = $1 if $go =~ /^I_(\d+)$/; $current = $files[$i]; } } else { @@ -292,29 +298,26 @@ sub show_slide # Display an HTML page. if ($slide->{ext} eq "html") { # MozEmbed is incredibly crashy, so we run ourself as a - # subprocess, so when it segfaults we don't care. - my @cmd = ($0, "--mozembed"); - push @cmd, "--mozembed-first" if exists $slide->{first}; - push @cmd, "--mozembed-last" if exists $slide->{last}; - my $cwd = getcwd; - my $url = "file://" . $cwd . "/" . $slide->{name}; - push @cmd, $url; - system (@cmd); - die "failed to execute subcommand: ", join(" ", @cmd), ": $!\n" - if $? == -1; - if ($? & 127) { - # Subcommand probably segfaulted, just continue to next slide. - return "NEXT"; - } else { - my $r = $? >> 8; - if ($r == 0) { - return "NEXT"; - } elsif ($r == 1) { - return "PREV"; - } elsif ($r == 2) { - return "QUIT"; + # subprocess, so when it segfaults we don't care. If all goes + # well and it doesn't crash, it should print a line 'RESULT FOO' + # where 'FOO' is the instruction (eg. 'NEXT', 'PREV', 'QUIT' etc). + my @cmd = ($0, "--mozembed", $talkdir, $slide->{name}); + print STDERR "running subcommand: ", join (" ", @cmd), "\n" + if $verbose; + open CMD, "-|", @cmd + or die "failed to execute subcommand: ", join(" ", @cmd), ": $!\n"; + my $r; + while () { + if (/^RESULT ([A-Z]+.*)/) { + $r = $1; + print STDERR "subcommand result: $r\n" if $verbose; + last; } } + # No RESULT line? Subcommand probably segfaulted, just + # continue to next slide. + $r ||= "NEXT"; + return $r; } # Run a shell command. elsif ($slide->{ext} eq "sh") { @@ -347,30 +350,15 @@ sub show_slide $w->move (0, 0); $w->set_decorated (0); - my $bbox = Gtk2::HButtonBox->new (); - $bbox->set_layout ('start'); - - my $bnext = Gtk2::Button->new ("Next slide"); - $bnext->signal_connect (clicked => sub { $r = "NEXT"; $w->destroy }); - $bnext->set_sensitive (!(exists $slide->{last})); - $bbox->add ($bnext); - - my $bback = Gtk2::Button->new ("Back"); - $bback->signal_connect (clicked => sub { $r = "PREV"; $w->destroy }); - $bback->set_sensitive (!(exists $slide->{first})); - $bbox->add ($bback); - - my $brestart = Gtk2::Button->new ("Kill & restart"); - $brestart->signal_connect (clicked => sub { - kill_process (); - run_process (); - }); - $bbox->add ($brestart); - - my $bquit = Gtk2::Button->new ("Quit"); - $bquit->signal_connect (clicked => sub { $r = "QUIT"; $w->destroy }); - $bbox->add ($bquit); - $bbox->set_child_secondary ($bquit, 1); + my $bbox = + make_button_bar ((exists $slide->{first}), + (exists $slide->{last}), + sub { $r = $_[0]; $w->destroy }, + restart => sub { + kill_process (); + run_process (); + }, + ); $w->add ($bbox); @@ -393,14 +381,20 @@ sub show_slide # killing the whole program. sub run_mozembed { - my $r = 0; - my $w = Gtk2::Window->new (); my $vbox = Gtk2::VBox->new (); my $moz = Gtk2::MozEmbed->new (); - my $bbox = Gtk2::HButtonBox->new (); - $bbox->set_layout ('start'); + reread_directory (); + + my $name = $ARGV[1]; + $current = $files{$name}; + my $url = "file://$talkdir/$name"; + + my $bbox = + make_button_bar ($current->{first}, $current->{last}, + sub { print "RESULT ", $_[0], "\n"; $w->destroy } + ); $vbox->pack_start ($bbox, 0, 0, 0); $vbox->add ($moz); @@ -408,35 +402,139 @@ sub run_mozembed #$w->set_default_size (640, 480); $w->add ($vbox); - my $bnext = Gtk2::Button->new ("Next slide"); - $bnext->signal_connect (clicked => sub { $r = 0; $w->destroy }); - $bnext->set_sensitive (!$mozembed_last); - $bbox->add ($bnext); - - my $bback = Gtk2::Button->new ("Back"); - $bback->signal_connect (clicked => sub { $r = 1; $w->destroy }); - $bback->set_sensitive (!$mozembed_first); - $bbox->add ($bback); - - my $bquit = Gtk2::Button->new ("Quit"); - $bquit->signal_connect (clicked => sub { $r = 2; $w->destroy }); - $bbox->add ($bquit); - $bbox->set_child_secondary ($bquit, 1); - $w->signal_connect (destroy => sub { Gtk2->main_quit; return FALSE; }); $w->show_all (); - $moz->load_url ($ARGV[0]); + $moz->load_url ($url); + Gtk2->main; - exit $r; + exit 0; +} + +# Make the standard button bar across the top of the page. +sub make_button_bar +{ + my $first = shift; + my $last = shift; + my $cb = shift; + my %params = @_; + + my $bbox = Gtk2::Toolbar->new (); + $bbox->set_style ("GTK_TOOLBAR_TEXT"); + + my $i = 0; + + my $bnext = Gtk2::ToolButton->new (undef, "Next slide"); + $bnext->signal_connect (clicked => sub { &$cb ("NEXT") }); + $bnext->set_sensitive (!$last); + $bbox->insert ($bnext, $i++); + + my $bback = Gtk2::ToolButton->new (undef, "Back"); + $bback->signal_connect (clicked => sub { &$cb ("PREV") }); + $bback->set_sensitive (!$first); + $bbox->insert ($bback, $i++); + + if (exists $params{restart}) { + $bbox->insert (Gtk2::SeparatorToolItem->new (), $i++); + + my $brestart = Gtk2::ToolButton->new (undef, "Kill & restart"); + $brestart->signal_connect (clicked => $params{restart}); + $bbox->insert ($brestart, $i++); + } + + my $sep = Gtk2::SeparatorToolItem->new (); + $sep->set_expand (TRUE); + $sep->set_draw (FALSE); + $bbox->insert ($sep, $i++); + + my $optsmenu = Gtk2::Menu->new (); + + my $bfirst = Gtk2::MenuItem->new ("First slide"); + $bfirst->signal_connect (activate => sub { \&$cb ("FIRST") }); + $bfirst->show (); + $optsmenu->append ($bfirst); + + my $blast = Gtk2::MenuItem->new ("Last slide"); + $blast->signal_connect (activate => sub { \&$cb ("LAST") }); + $blast->show (); + $optsmenu->append ($blast); + + my $slidesmenu = Gtk2::Menu->new (); + foreach (@files) { + my $item = Gtk2::MenuItem->new ($_->{name}); + my $index = $_->{i}; + $item->signal_connect (activate => sub { \&$cb ("I_$index") }); + $item->set_sensitive ($current->{i} != $index); + $item->show (); + $slidesmenu->append ($item); + } + + my $bslides = Gtk2::MenuItem->new ("Slides"); + $bslides->set_submenu ($slidesmenu); + $bslides->show (); + $optsmenu->append ($bslides); + + my $sep2 = Gtk2::SeparatorMenuItem->new (); + $sep2->show (); + $optsmenu->append ($sep2); + + my $bscreenshot = Gtk2::MenuItem->new ("Take a screenshot"); + $bscreenshot->signal_connect (activate => sub { screenshot () }); + $bscreenshot->show (); + $optsmenu->append ($bscreenshot); + + my $sep3 = Gtk2::SeparatorMenuItem->new (); + $sep3->show (); + $optsmenu->append ($sep3); + + my $bquit = Gtk2::MenuItem->new ("Quit"); + $bquit->signal_connect (activate => sub { \&$cb ("QUIT") }); + $bquit->show (); + $optsmenu->append ($bquit); + + my $boptions = Gtk2::MenuToolButton->new (undef, "Options"); + #$boptions->signal_connect (clicked => + # sub { $optsmenu->popup (undef, undef, undef, undef, ?, ?) } ); + $bbox->insert ($boptions, $i++); + $boptions->set_menu ($optsmenu); + + return $bbox; +} + +# Try running the external "gnome-screenshot" program, if it's +# available, else take a screenshot using gdk routines. +sub screenshot +{ + system ("gnome-screenshot"); + + if ($? == -1) { + # We are going to save the entire screen. + my $root = Gtk2::Gdk->get_default_root_window (); + my ($width, $height) = $root->get_size; + + # Create blank pixbuf to hold the image. + my $gdkpixbuf = Gtk2::Gdk::Pixbuf->new ('rgb', + 0, 8, $width, $height); + + $gdkpixbuf->get_from_drawable ($root, $root->get_colormap (), + 0, 0, 0, 0, $width, $height); + + my $i = 0; + $i++ while -f "screenshot$i.png"; + $gdkpixbuf->save ("screenshot$i.png", 'png'); + } + + return FALSE; } 1; +__END__ + =head1 TUTORIAL =head2 START WRITING A TALK @@ -580,9 +678,16 @@ terminal. In C, I have: # -*- shell-script -*- + + # Place any local environment variables required in 'local'. + if [ -f local ]; then source local; fi + export PS1="$ " - export HISTFILE=/tmp/history + + export HISTFILE=$talkdir/history + rm -f $HISTFILE + touch $HISTFILE add_history () { @@ -591,6 +696,11 @@ In C, I have: terminal () { + # Make $HISTFILE unwritable so the shell won't update it + # when it exits. + chmod -w $HISTFILE + + # Run gnome-terminal. exec \ gnome-terminal \ --window \ @@ -651,6 +761,13 @@ When running shell scripts, the current directory is also set to be the directory containing the talk files, so the same rules about using relative paths apply there too. +The environment variable C<$talkdir> is exported to scripts and it +contains the absolute path of the directory containing the talk files. +When a script is run, the current directory is the same as +C<$talkdir>, but if your script changes directory (eg. into a +subdirectory containing supporting files) then it can be useful to use +C<$talkdir> to refer back to the original directory. + =head1 WHAT MAKES A GOOD TALK I like what Edward Tufte writes, for example his evisceration of