+sub show_slide
+{
+ my $slide = shift;
+
+ # 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";
+ }
+ }
+ }
+ # Run a shell command.
+ elsif ($slide->{ext} eq "sh") {
+ my $pid;
+ # http://docstore.mik.ua/orelly/perl/cookbook/ch10_17.htm
+ local *run_process = sub {
+ $pid = fork ();
+ die "fork: $!" unless defined $pid;
+ unless ($pid) {
+ # Child.
+ POSIX::setsid ();
+ $ENV{PATH} = ".:$ENV{PATH}";
+ exec ($slide->{name});
+ die "failed to execute command: ", $slide->{name}, ": $!";
+ }
+ # Parent returns.
+ };
+ local *kill_process = sub {
+ print STDERR "sending TERM signal to process group $pid\n"
+ if $verbose;
+ kill "TERM", -$pid;
+ };
+ run_process ();
+
+ my $r = "NEXT";
+
+ my $w = Gtk2::Window->new ();
+
+ my $s = $w->get_screen;
+ $w->set_default_size ($s->get_width, -1);
+ $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);
+
+ $w->add ($bbox);
+
+ $w->signal_connect (destroy => sub {
+ Gtk2->main_quit;
+ return FALSE;
+ });
+ $w->show_all ();
+
+ Gtk2->main;
+
+ kill_process ();
+ print STDERR "returning r=$r\n" if $verbose;
+ return $r;
+ }
+}