8a3abb6fd1a97c229bafbcaf74875a31ba0e9385
[techtalk-pse.git] / techtalk-pse.pl
1 #!/usr/bin/perl -w
2 # -*- perl -*-
3 # @configure_input@
4 #
5 # Tech Talk PSE
6 # Copyright (C) 2010 Red Hat Inc.
7 #
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 use warnings;
23 use strict;
24 use utf8;
25
26 use Pod::Usage;
27 use Getopt::Long;
28 use Cwd qw(getcwd abs_path);
29 use Glib qw(TRUE FALSE);
30 use Gtk2 -init;
31 use Gtk2::MozEmbed;
32
33 =encoding utf8
34
35 =head1 NAME
36
37 techtalk-pse - superior technical demonstration software
38
39 =head1 SYNOPSIS
40
41  cd /path/to/talk/; techtalk-pse
42
43  techtalk-pse /path/to/talk/
44
45 =head1 DESCRIPTION
46
47 Tech Talk "Platinum Supreme Edition" (PSE) is Linux Presentation
48 Software designed by technical people to give technical software
49 demonstrations to other technical people.  It is designed to be simple
50 to use (for people who know how to use an editor and the command line)
51 and powerful, so that you can create informative, technically accurate
52 and entertaining talks and demonstrations.
53
54 Tech Talk PSE is good at opening editors at the right place, opening
55 shell prompts with preloaded history, compiling and running things
56 during the demonstration, displaying text, photos, figures and video.
57
58 Tech Talk PSE is I<bad> at slide effects, chart junk and bullet
59 points.
60
61 This manual page covers all the documentation you will need to use
62 Tech Talk PSE.  The next section covers running the tool from the
63 command line.  After that there is a L</TUTORIAL> section to get you
64 started.  Then there is a detailed L</REFERENCE> section.  Finally
65 there is a discussion on L<WHAT MAKES A GOOD TALK>.
66
67 =head1 RUNNING THE TOOL FROM THE COMMAND LINE
68
69 A Tech Talk PSE talk is not a single file, but a directory full of
70 files.  (If you want to start a new talk, see the L</TUTORIAL> section
71 below).  To display or run the talk, change into the directory
72 containing all those files and run the C<techtalk-pse> command:
73
74  cd /path/to/talk/; techtalk-pse
75
76 You can also run C<techtalk-pse> without changing directory, instead
77 specifying the path to the talk:
78
79  techtalk-pse /path/to/talk/
80
81 =head2 OPTIONS
82
83 =over 4
84
85 =cut
86
87 my $help;
88
89 =item B<--help>
90
91 Display brief help and exit.
92
93 =cut
94
95 my $last;
96
97 =item B<--last>
98
99 Start at the last slide.
100
101 You cannot use this with the B<-n> / B<--start> option.
102
103 =cut
104
105 my $start;
106
107 =item B<-n SLIDE> | B<--start SLIDE>
108
109 Start at the named slide.  I<SLIDE> is the shortest unique prefix of
110 the slide name, so to start at a slide named
111 I<00010-introduction.html>, you could use I<-n 00010> or I<-n 00010-intro>,
112 or give the full filename I<-n 00010-introduction.html>.
113
114 The default is to start at the first slide in the talk.
115
116 =cut
117
118 my $splash = 1;
119
120 =item B<--no-splash>
121
122 Don't display the initial "splash" screen which advertises Tech Talk
123 PSE to your audience.  Just go straight into the talk.
124
125 =cut
126
127 my $verbose;
128
129 =item B<--verbose>
130
131 Display verbose messages, useful for debugging or tracing
132 what the program is doing.
133
134 =cut
135
136 my $version;
137
138 =item B<--version>
139
140 Display version number and exit.
141
142 =cut
143
144 my $mozembed;
145
146 GetOptions ("help|?" => \$help,
147             "last" => \$last,
148             "mozembed" => \$mozembed,
149             "n=s" => \$start,
150             "splash!" => \$splash,
151             "start=s" => \$start,
152             "verbose" => \$verbose,
153             "version" => \$version,
154     ) or pod2usage (2);
155
156 =back
157
158 =cut
159
160 pod2usage (1) if $help;
161 if ($version) {
162     print "@PACKAGE@ @VERSION@\n";
163     exit
164 }
165 die "techtalk-pse: cannot use --start and --last options together\n"
166     if defined $last && defined $start;
167
168 # --mozembed runs Gtk2::MozEmbed as a subprocess, because MozEmbed
169 # is very crashy.
170 if ($mozembed) {
171     my $r = 0;
172
173     my $w = Gtk2::Window->new ();
174     my $vbox = Gtk2::VBox->new ();
175     my $moz = Gtk2::MozEmbed->new ();
176     my $bbox = Gtk2::HButtonBox->new ();
177
178     $vbox->pack_start ($bbox, 0, 0, 0);
179     $vbox->add ($moz);
180     $w->fullscreen ();
181     #$w->set_default_size (640, 480);
182     $w->add ($vbox);
183
184     $bbox->set_layout ('start');
185     my $bnext = Gtk2::Button->new ("Next slide");
186     $bnext->signal_connect (clicked => sub { $r = 0; Gtk2->main_quit });
187     $bbox->add ($bnext);
188
189     my $bback = Gtk2::Button->new ("Back");
190     $bback->signal_connect (clicked => sub { $r = 1; Gtk2->main_quit });
191     $bbox->add ($bback);
192
193     my $bquit = Gtk2::Button->new ("Quit");
194     $bquit->signal_connect (clicked => sub { $r = 2; Gtk2->main_quit });
195     $bbox->add ($bquit);
196     $bbox->set_child_secondary ($bquit, 1);
197
198     $w->signal_connect (delete_event => sub {
199         Gtk2->main_quit;
200         return FALSE;
201     });
202     $w->show_all ();
203
204     $moz->load_url ($ARGV[0]);
205     Gtk2->main;
206
207     exit $r;
208 }
209
210 die "techtalk-pse: too many arguments\n" if @ARGV >= 2;
211
212 # Get the true name of the program.
213 $0 = abs_path ($0);
214
215 # Locate the talk.
216 if (@ARGV > 0) {
217     my $d = $ARGV[0];
218     if (-d $d) {
219         chdir $d or die "techtalk-pse: chdir: $d: $!";
220     } else {
221         # XXX In future allow people to specify an archive and unpack
222         # it for them.
223         die "techtalk-pse: argument is not a directory"
224     }
225 }
226
227 # Get the files.
228 my @files;
229 my %files;
230 my %groups;
231 sub reread_directory
232 {
233     @files = ();
234     %groups = ();
235
236     foreach (glob ("*")) {
237         if (/^(\d+)([A-Z])?(?:-.*)\.(html|sh)$/) {
238             print STDERR "reading $_\n" if $verbose;
239
240             my $seq = $1;
241             my $pos = $2 || "A";
242             my $ext = $3;
243             warn "techtalk-pse: $_: command file is not executable (+x)\n"
244                 if $ext eq "sh" && ! -x $_;
245
246             my $h = { name => $_, seq => $1, pos => $2, ext => $3 };
247             push @files, $h;
248             $files{$_} = $h;
249
250             $groups{$seq} = [] unless exists $groups{$seq};
251             push @{$groups{$seq}}, $h;
252         } else {
253             print STDERR "ignoring $_\n" if $verbose;
254         }
255     }
256 }
257 reread_directory ();
258 print STDERR "read ", 0+@files, " files\n" if $verbose;
259 if (@files == 0) {
260     warn "techtalk-pse: no files found, continuing anyway ...\n"
261 }
262
263 # Work out what slide we're starting on.
264 my $current;
265 if (defined $current) {
266     die "start slide not implemented yet XXX"
267 }
268 elsif (@files) {
269     $current = $files[0];
270 }
271 # else $current is undefined
272
273 if ($splash) {
274     my $w = Gtk2::AboutDialog->new;
275     $w->set_authors ("Richard W.M. Jones");
276     $w->set_comments (
277         "Superior technical demonstration software\n".
278         "\n".
279         "Keys\n".
280         "↑ — Go back one slide\n".
281         "↓ — Go forward one slide\n"
282         );
283     $w->set_program_name ("Tech Talk Platinum Supreme Edition (PSE)");
284     $w->set_version ("@VERSION@");
285     $w->set_website ("http://people.redhat.com/~rjones");
286     $w->set_license ("GNU General Public License v2 or above");
287     $w->signal_connect (destroy => sub { Gtk2->main_quit });
288     $w->show_all;
289     Gtk2->main;
290 }
291
292 MAIN: while (1) {
293     if (defined $current) {
294         my $go = show_slide ($current);
295         if (defined $go) {
296             print STDERR "go = $go\n" if $verbose;
297             last MAIN if $go eq "QUIT";
298
299             my $i = 0;
300           FOUND: {
301               foreach (@files) {
302                   last FOUND if $files[$i]->{name} eq $current->{name};
303                   $i++;
304               }
305               die "internal error: cannot find \$current in \@files"
306             }
307             print STDERR "found current entry at i = $i\n" if $verbose;
308             $i-- if $go eq "PREV" && $i > 0;
309             $i++ if $go eq "NEXT" && $i+1 < @files;
310             $current = $files[$i];
311         }
312     } else {
313         print "No slides found.  Press any key to reload directory ...\n";
314         $_ = <STDIN>;
315     }
316
317     reread_directory ();
318
319     if (defined $current && !exists $files{$current->{name}}) {
320         # Current slide was deleted.
321         undef $current;
322         $current = $files[0] if @files;
323     }
324 }
325
326 sub show_slide {
327     my $slide = shift;
328
329     if ($slide->{ext} eq "html") {
330         # MozEmbed is incredibly crashy, so we run ourself as a
331         # subprocess, so when it segfaults we don't care.
332         my $cwd = getcwd;
333         my $url = "file://" . $cwd . "/" . $slide->{name};
334         my @cmd = ($0, "--mozembed", $url);
335         system (@cmd);
336         die "failed to execute subcommand: $!\n" if $? == -1;
337         if ($? & 127) {
338             # Subcommand probably segfaulted, just continue to next slide.
339             return "NEXT";
340         } else {
341             my $r = $? >> 8;
342             if ($r == 0) {
343                 return "NEXT";
344             } elsif ($r == 1) {
345                 return "PREV";
346             } elsif ($r == 2) {
347                 return "QUIT";
348             }
349         }
350     }
351     elsif ($slide->{ext} eq "sh") {
352         system ("PATH=.:\$PATH " . $slide->{name});
353         return "NEXT";
354     }
355 }
356
357 1;
358
359 =head1 TUTORIAL
360
361 =head1 REFERENCE
362
363 =head1 WHAT MAKES A GOOD TALK
364
365 =head1 SEE ALSO
366
367 The Cognitive Style of PowerPoint, Tufte, Edward R.
368
369 =head1 AUTHOR
370
371 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
372
373 =head1 COPYRIGHT
374
375 Copyright (C) 2010 Red Hat Inc.
376
377 This program is free software; you can redistribute it and/or modify
378 it under the terms of the GNU General Public License as published by
379 the Free Software Foundation; either version 2 of the License, or
380 (at your option) any later version.
381
382 This program is distributed in the hope that it will be useful,
383 but WITHOUT ANY WARRANTY; without even the implied warranty of
384 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
385 GNU General Public License for more details.
386
387 You should have received a copy of the GNU General Public License
388 along with this program; if not, write to the Free Software
389 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.