use strict;
use utf8;
+use POSIX qw(setsid);
use Pod::Usage;
use Getopt::Long;
+use Cwd qw(getcwd abs_path);
use Glib qw(TRUE FALSE);
use Gtk2 -init;
use Gtk2::MozEmbed;
=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,
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;
+# Get the true name of the program.
+$0 = abs_path ($0);
+
# Locate the talk.
if (@ARGV > 0) {
my $d = $ARGV[0];
}
}
-# MozEmbed initialization.
-Gtk2::MozEmbed->set_profile_path ("$ENV{HOME}/.@PACKAGE@", "Tech Talk PSE");
-
# Get the files.
my @files;
-my %groups;
+my %files;
sub reread_directory
{
@files = ();
- %groups = ();
+ my $i = 0;
foreach (glob ("*")) {
- if (/^(\d+)([A-Z])?(?:-.*)\.(html|sh|txt)$/) {
+ if (/^(\d+)(?:-.*)\.(html|sh)$/) {
print STDERR "reading $_\n" if $verbose;
my $seq = $1;
- my $pos = $2 || "A";
- my $ext = $3;
+ my $ext = $2;
warn "techtalk-pse: $_: command file is not executable (+x)\n"
if $ext eq "sh" && ! -x $_;
- my $h = { name => $_, seq => $1, pos => $2, ext => $3 };
+ my $h = { name => $_, seq => $1, ext => $2, i => $i };
push @files, $h;
-
- $groups{$seq} = [] unless exists $groups{$seq};
- push @{$groups{$seq}}, $h;
+ $files{$_} = $h;
+ $i++;
} else {
print STDERR "ignoring $_\n" if $verbose;
}
}
+
+ if (@files > 0) {
+ $files[0]->{first} = 1;
+ $files[$#files]->{last} = 1;
+ }
}
reread_directory ();
print STDERR "read ", 0+@files, " files\n" if $verbose;
$w->set_version ("@VERSION@");
$w->set_website ("http://people.redhat.com/~rjones");
$w->set_license ("GNU General Public License v2 or above");
- $w->signal_connect (destroy => sub { Gtk2->main_quit });
- $w->show_all;
- Gtk2->main;
+ $w->run;
+ print STDERR "calling \$w->destroy on about dialog\n" if $verbose;
+ $w->destroy;
}
+MAIN: while (1) {
+ if (defined $current) {
+ my $go = show_slide ($current);
+ if (defined $go) {
+ print STDERR "go = $go\n" if $verbose;
+ last MAIN if $go eq "QUIT";
+
+ my $i = $current->{i};
+ print STDERR "i = $i\n" if $verbose;
+ $i-- if $go eq "PREV" && $i > 0;
+ $i++ if $go eq "NEXT" && $i+1 < @files;
+ $current = $files[$i];
+ }
+ } else {
+ print "No slides found. Press any key to reload directory ...\n";
+ $_ = <STDIN>;
+ }
+ # Reread directory between slides.
+ reread_directory ();
+ if (defined $current && !exists $files{$current->{name}}) {
+ # Current slide was deleted.
+ undef $current;
+ $current = $files[0] if @files;
+ }
+}
+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;
+ }
+}
+# If invoked with the --mozembed parameter then we just display a
+# single page. This is just to prevent crashes in MozEmbed from
+# 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');
+
+ $vbox->pack_start ($bbox, 0, 0, 0);
+ $vbox->add ($moz);
+ $w->fullscreen ();
+ #$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]);
+ Gtk2->main;
-
-
+ exit $r;
+}
1;