Documentation.
[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 POSIX qw(setsid);
27 use Pod::Usage;
28 use Getopt::Long;
29 use Cwd qw(getcwd abs_path);
30 use Glib qw(TRUE FALSE);
31 use Gtk2 -init;
32 use Gtk2::MozEmbed;
33
34 =encoding utf8
35
36 =head1 NAME
37
38 techtalk-pse - superior technical demonstration software
39
40 =head1 SYNOPSIS
41
42  cd /path/to/talk/; techtalk-pse
43
44  techtalk-pse /path/to/talk/
45
46 =head1 DESCRIPTION
47
48 Tech Talk "Platinum Supreme Edition" (PSE) is Linux Presentation
49 Software designed by technical people to give technical software
50 demonstrations to other technical people.  It is designed to be simple
51 to use (for people who know how to use an editor and the command line)
52 and powerful, so that you can create informative, technically accurate
53 and entertaining talks and demonstrations.
54
55 Tech Talk PSE is good at opening editors at the right place, opening
56 shell prompts with preloaded history, compiling and running things
57 during the demonstration, displaying text, photos, figures and video.
58
59 Tech Talk PSE is I<bad> at slide effects, chart junk and bullet
60 points.
61
62 This manual page covers all the documentation you will need to use
63 Tech Talk PSE.  The next section covers running the tool from the
64 command line.  After that there is a L</TUTORIAL> section to get you
65 started.  Then there is a detailed L</REFERENCE> section.  Finally
66 there is a discussion on L<WHAT MAKES A GOOD TALK>.
67
68 =head1 RUNNING THE TOOL FROM THE COMMAND LINE
69
70 A Tech Talk PSE talk is not a single file, but a directory full of
71 files.  (If you want to start a new talk, see the L</TUTORIAL> section
72 below).  To display or run the talk, change into the directory
73 containing all those files and run the C<techtalk-pse> command:
74
75  cd /path/to/talk/; techtalk-pse
76
77 You can also run C<techtalk-pse> without changing directory, instead
78 specifying the path to the talk:
79
80  techtalk-pse /path/to/talk/
81
82 =head2 OPTIONS
83
84 =over 4
85
86 =cut
87
88 my $help;
89
90 =item B<--help>
91
92 Display brief help and exit.
93
94 =cut
95
96 my $last;
97
98 =item B<--last>
99
100 Start at the last slide.
101
102 You cannot use this with the B<-n> / B<--start> option.
103
104 =cut
105
106 my $start;
107
108 =item B<-n SLIDE> | B<--start SLIDE>
109
110 Start at the named slide.  I<SLIDE> is the shortest unique prefix of
111 the slide name, so to start at a slide named
112 I<00010-introduction.html>, you could use I<-n 00010> or I<-n 00010-intro>,
113 or give the full filename I<-n 00010-introduction.html>.
114
115 The default is to start at the first slide in the talk.
116
117 =cut
118
119 my $splash = 1;
120
121 =item B<--no-splash>
122
123 Don't display the initial "splash" screen which advertises Tech Talk
124 PSE to your audience.  Just go straight into the talk.
125
126 =cut
127
128 my $verbose;
129
130 =item B<--verbose>
131
132 Display verbose messages, useful for debugging or tracing
133 what the program is doing.
134
135 =cut
136
137 my $version;
138
139 =item B<--version>
140
141 Display version number and exit.
142
143 =cut
144
145 my $mozembed;
146 my $mozembed_first;
147 my $mozembed_last;
148
149 GetOptions ("help|?" => \$help,
150             "last" => \$last,
151             "mozembed" => \$mozembed,
152             "mozembed-first" => \$mozembed_first,
153             "mozembed-last" => \$mozembed_last,
154             "n=s" => \$start,
155             "splash!" => \$splash,
156             "start=s" => \$start,
157             "verbose" => \$verbose,
158             "version" => \$version,
159     ) or pod2usage (2);
160
161 =back
162
163 =cut
164
165 pod2usage (1) if $help;
166 if ($version) {
167     print "@PACKAGE@ @VERSION@\n";
168     exit
169 }
170 die "techtalk-pse: cannot use --start and --last options together\n"
171     if defined $last && defined $start;
172
173 # Run with --mozembed: see below.
174 run_mozembed () if $mozembed;
175
176 # Normal run of the program.
177 die "techtalk-pse: too many arguments\n" if @ARGV >= 2;
178
179 # Get the true name of the program.
180 $0 = abs_path ($0);
181
182 # Locate the talk.
183 if (@ARGV > 0) {
184     my $d = $ARGV[0];
185     if (-d $d) {
186         chdir $d or die "techtalk-pse: chdir: $d: $!";
187     } else {
188         # XXX In future allow people to specify an archive and unpack
189         # it for them.
190         die "techtalk-pse: argument is not a directory"
191     }
192 }
193
194 # Get the files.
195 my @files;
196 my %files;
197 sub reread_directory
198 {
199     @files = ();
200
201     my $i = 0;
202     foreach (glob ("*")) {
203         if (/^(\d+)(?:-.*)\.(html|sh)$/) {
204             print STDERR "reading $_\n" if $verbose;
205
206             my $seq = $1;
207             my $ext = $2;
208             warn "techtalk-pse: $_: command file is not executable (+x)\n"
209                 if $ext eq "sh" && ! -x $_;
210
211             my $h = { name => $_, seq => $1, ext => $2, i => $i };
212             push @files, $h;
213             $files{$_} = $h;
214             $i++;
215         } else {
216             print STDERR "ignoring $_\n" if $verbose;
217         }
218     }
219
220     if (@files > 0) {
221         $files[0]->{first} = 1;
222         $files[$#files]->{last} = 1;
223     }
224 }
225 reread_directory ();
226 print STDERR "read ", 0+@files, " files\n" if $verbose;
227 if (@files == 0) {
228     warn "techtalk-pse: no files found, continuing anyway ...\n"
229 }
230
231 # Work out what slide we're starting on.
232 my $current;
233 if (defined $current) {
234     die "start slide not implemented yet XXX"
235 }
236 elsif (@files) {
237     $current = $files[0];
238 }
239 # else $current is undefined
240
241 if ($splash) {
242     my $w = Gtk2::AboutDialog->new;
243     $w->set_authors ("Richard W.M. Jones");
244     $w->set_comments (
245         "Superior technical demonstration software\n".
246         "\n".
247         "Keys\n".
248         "↑ — Go back one slide\n".
249         "↓ — Go forward one slide\n"
250         );
251     $w->set_program_name ("Tech Talk Platinum Supreme Edition (PSE)");
252     $w->set_version ("@VERSION@");
253     $w->set_website ("http://people.redhat.com/~rjones");
254     $w->set_license ("GNU General Public License v2 or above");
255     $w->run;
256     print STDERR "calling \$w->destroy on about dialog\n" if $verbose;
257     $w->destroy;
258 }
259
260 MAIN: while (1) {
261     if (defined $current) {
262         my $go = show_slide ($current);
263         if (defined $go) {
264             print STDERR "go = $go\n" if $verbose;
265             last MAIN if $go eq "QUIT";
266
267             my $i = $current->{i};
268             print STDERR "i = $i\n" if $verbose;
269             $i-- if $go eq "PREV" && $i > 0;
270             $i++ if $go eq "NEXT" && $i+1 < @files;
271             $current = $files[$i];
272         }
273     } else {
274         print "No slides found.  Press any key to reload directory ...\n";
275         $_ = <STDIN>;
276     }
277
278     # Reread directory between slides.
279     reread_directory ();
280
281     if (defined $current && !exists $files{$current->{name}}) {
282         # Current slide was deleted.
283         undef $current;
284         $current = $files[0] if @files;
285     }
286 }
287
288 sub show_slide
289 {
290     my $slide = shift;
291
292     # Display an HTML page.
293     if ($slide->{ext} eq "html") {
294         # MozEmbed is incredibly crashy, so we run ourself as a
295         # subprocess, so when it segfaults we don't care.
296         my @cmd = ($0, "--mozembed");
297         push @cmd, "--mozembed-first" if exists $slide->{first};
298         push @cmd, "--mozembed-last" if exists $slide->{last};
299         my $cwd = getcwd;
300         my $url = "file://" . $cwd . "/" . $slide->{name};
301         push @cmd, $url;
302         system (@cmd);
303         die "failed to execute subcommand: ", join(" ", @cmd), ": $!\n"
304             if $? == -1;
305         if ($? & 127) {
306             # Subcommand probably segfaulted, just continue to next slide.
307             return "NEXT";
308         } else {
309             my $r = $? >> 8;
310             if ($r == 0) {
311                 return "NEXT";
312             } elsif ($r == 1) {
313                 return "PREV";
314             } elsif ($r == 2) {
315                 return "QUIT";
316             }
317         }
318     }
319     # Run a shell command.
320     elsif ($slide->{ext} eq "sh") {
321         my $pid;
322         # http://docstore.mik.ua/orelly/perl/cookbook/ch10_17.htm
323         local *run_process = sub {
324             $pid = fork ();
325             die "fork: $!" unless defined $pid;
326             unless ($pid) {
327                 # Child.
328                 POSIX::setsid ();
329                 exec ("./".$slide->{name});
330                 die "failed to execute command: ", $slide->{name}, ": $!";
331             }
332             # Parent returns.
333         };
334         local *kill_process = sub {
335             print STDERR "sending TERM signal to process group $pid\n"
336                 if $verbose;
337             kill "TERM", -$pid;
338         };
339         run_process ();
340
341         my $r = "NEXT";
342
343         my $w = Gtk2::Window->new ();
344
345         my $s = $w->get_screen;
346         $w->set_default_size ($s->get_width, -1);
347         $w->move (0, 0);
348         $w->set_decorated (0);
349
350         my $bbox = Gtk2::HButtonBox->new ();
351         $bbox->set_layout ('start');
352
353         my $bnext = Gtk2::Button->new ("Next slide");
354         $bnext->signal_connect (clicked => sub { $r = "NEXT"; $w->destroy });
355         $bnext->set_sensitive (!(exists $slide->{last}));
356         $bbox->add ($bnext);
357
358         my $bback = Gtk2::Button->new ("Back");
359         $bback->signal_connect (clicked => sub { $r = "PREV"; $w->destroy });
360         $bback->set_sensitive (!(exists $slide->{first}));
361         $bbox->add ($bback);
362
363         my $brestart = Gtk2::Button->new ("Kill & restart");
364         $brestart->signal_connect (clicked => sub {
365             kill_process ();
366             run_process ();
367         });
368         $bbox->add ($brestart);
369
370         my $bquit = Gtk2::Button->new ("Quit");
371         $bquit->signal_connect (clicked => sub { $r = "QUIT"; $w->destroy });
372         $bbox->add ($bquit);
373         $bbox->set_child_secondary ($bquit, 1);
374
375         $w->add ($bbox);
376
377         $w->signal_connect (destroy => sub {
378             Gtk2->main_quit;
379             return FALSE;
380         });
381         $w->show_all ();
382
383         Gtk2->main;
384
385         kill_process ();
386         print STDERR "returning r=$r\n" if $verbose;
387         return $r;
388     }
389 }
390
391 # If invoked with the --mozembed parameter then we just display a
392 # single page.  This is just to prevent crashes in MozEmbed from
393 # killing the whole program.
394 sub run_mozembed
395 {
396     my $r = 0;
397
398     my $w = Gtk2::Window->new ();
399     my $vbox = Gtk2::VBox->new ();
400     my $moz = Gtk2::MozEmbed->new ();
401
402     my $bbox = Gtk2::HButtonBox->new ();
403     $bbox->set_layout ('start');
404
405     $vbox->pack_start ($bbox, 0, 0, 0);
406     $vbox->add ($moz);
407     $w->fullscreen ();
408     #$w->set_default_size (640, 480);
409     $w->add ($vbox);
410
411     my $bnext = Gtk2::Button->new ("Next slide");
412     $bnext->signal_connect (clicked => sub { $r = 0; $w->destroy });
413     $bnext->set_sensitive (!$mozembed_last);
414     $bbox->add ($bnext);
415
416     my $bback = Gtk2::Button->new ("Back");
417     $bback->signal_connect (clicked => sub { $r = 1; $w->destroy });
418     $bback->set_sensitive (!$mozembed_first);
419     $bbox->add ($bback);
420
421     my $bquit = Gtk2::Button->new ("Quit");
422     $bquit->signal_connect (clicked => sub { $r = 2; $w->destroy });
423     $bbox->add ($bquit);
424     $bbox->set_child_secondary ($bquit, 1);
425
426     $w->signal_connect (destroy => sub {
427         Gtk2->main_quit;
428         return FALSE;
429     });
430     $w->show_all ();
431
432     $moz->load_url ($ARGV[0]);
433     Gtk2->main;
434
435     exit $r;
436 }
437
438 1;
439
440 =head1 TUTORIAL
441
442 =head2 START WRITING A TALK
443
444 [Before you start writing your real talk, I urge you to read
445 L</WHAT MAKES A GOOD TALK> below].
446
447 To start your talk, all you have to do is to make a new directory
448 somewhere:
449
450  mkdir talk
451  cd talk
452
453 A tech talk consists of HTML files ("slides") and shell scripts.  The
454 filenames must start with a number, followed optionally by a
455 description, followed by the extension (C<.html> or C<.sh>).  So to
456 start our talk with two slides:
457
458  echo "This is the introduction" > 0010-introduction.html
459  echo "This is the second slide" > 0020-second.html
460
461 To run it, run the command from within the talk directory:
462
463  techtalk-pse
464
465 Any other file in the directory is ignored, so if you want to add
466 Makefiles, version control files etc, just go ahead.
467
468 =head2 TIPS FOR WRITING HTML
469
470 You may have your own techniques and tools for writing HTML, so
471 this section is just to share my ideas.  I start every
472 HTML file with a standard stylesheet and Javascript header:
473
474  <link rel="stylesheet" href="style.css" type="text/css"/>
475  <script src="code.js" type="text/javascript"></script>
476
477 That just ensures that I can put common styling instructions for all
478 my slides in a single file (C<style.css>), and I have one place where
479 I can add all Javascript, if I need to use any (C<code.js>).
480
481 =head3 BACKGROUNDS, FONTS AND LOGOS
482
483 To add a common background and font size to all slides, put this in
484 C<style.css>:
485
486  body {
487      font-size: 24pt;
488      background: url(background-image.jpg) no-repeat;
489  }
490
491 To add a logo in one corner:
492
493  body {
494      background: url(logo.jpg) top right no-repeat;
495  }
496
497 =head3 SCALING AND CENTERING
498
499 Scaling slide text and images so that they appear at the same
500 proportionate size for any screen resolution can be done using
501 Javascript.  (See
502 L<https://developer.mozilla.org/En/DOM/window.innerHeight>).
503
504 If you want to center text horizontally, use CSS, eg:
505
506  p.center {
507      text-align: center;
508  }
509
510 To center text vertically, CSS3 is supposed to offer a solution some
511 time, but while you're waiting for that try
512 L<http://www.w3.org/Style/Examples/007/center#vertical>.
513
514 =head3 PREVIEWING HTML
515
516 I find it helpful to have Firefox open to display the HTML files and
517 styles as I edit them.  Just start firefox in the talk directory:
518
519  firefox file://$(pwd) &
520
521 When you edit an HTML file, click the Firefox reload button to
522 immediately see your changes.
523
524 Tech Talk PSE uses Mozilla embedding to display HTML, which uses the
525 same Mozilla engine as Firefox, so what you should see in Firefox
526 should be identical to what Tech Talk PSE displays.
527
528 =head2 CREATING FIGURES
529
530 Use your favorite tool to draw the figure, convert it to an image (in
531 any format that the Mozilla engine can display) and include it using
532 an C<E<lt>imgE<gt>> tag, eg:
533
534  <img src="fig1.gif">
535
536 Suitable tools include: XFig, GnuPlot, GraphViz, and many TeX tools
537 such as PicTex and in particular TikZ.
538
539 =head2 EMBEDDING VIDEOS, ANIMATIONS, ETC.
540
541 Using HTML 5, embedding videos in the browser is easy.  See:
542 L<https://developer.mozilla.org/En/Using_audio_and_video_in_Firefox>
543
544 For animations, you could try L<Haxe|http://haxe.org/> which has a
545 Javascript back-end.  There are many other possibilities.
546
547 If you are B<sure> that the venue will have an internet connection,
548 why not embed a YouTube video.
549
550 =head2 DISPLAYING EXISTING WEB PAGES
551
552 Obviously you could just have an HTML file that contains a redirect to
553 the public web page:
554
555  <meta http-equiv="Refresh" content="0; url=http://www.example.com/">
556
557 However if you want your talk to work offline, then it's better to
558 download the web page in advance, eg. using Firefox's "Save Page As
559 -E<gt> Web Page, complete" feature, into the talk directory, then
560 either rename or make a symbolic link to the slide name:
561
562  ln -s "haXe - Welcome to haXe.html" 0010-haxe-homepage.html
563
564 =head2 TIPS FOR WRITING SHELL SCRIPTS
565
566 Make sure each C<*.sh> file you write is executable, otherwise Tech
567 Talk PSE won't be able to run it.  (The program gives a warning if you
568 forget this).
569
570 A good idea is to start each script by sourcing some common functions.
571 All my scripts start with:
572
573  #!/bin/bash -
574  source functions
575
576 where C<functions> is another file (ignored by Tech Talk PSE) which
577 contains common functions for setting shell history and starting a
578 terminal.
579
580 In C<functions>, I have:
581
582  # -*- shell-script -*-
583  export PS1="$ "
584  export HISTFILE=/tmp/history
585  rm -f $HISTFILE
586  
587  add_history ()
588  {
589      echo "$@" >> $HISTFILE
590  }
591  
592  terminal ()
593  {
594      exec \
595          gnome-terminal \
596          --window \
597          --geometry=+100+100 \
598          --hide-menubar \
599          --disable-factory \
600          -e '/bin/bash --norc' \
601          "$@"
602  }
603
604 By initializing the shell history, during your talk you can rapidly
605 recall commands to start parts of the demonstration just by hitting
606 the Up arrow.  A complete shell script from one of my talks would look
607 like this:
608
609  #!/bin/bash -
610  source functions
611  add_history guestfish -i debian.img
612  terminal --title="Examining a Debian guest image in guestfish"
613
614 This is just a starting point for your own scripts.  You may want to
615 use a different terminal, such as xterm, and you may want to adjust
616 terminal fonts.
617
618 =head1 REFERENCE
619
620 =head2 ORDER OF FILES
621
622 Tech Talk PSE displays the slides in the directory in lexicographic
623 order (the same order as C<LANG=C ls -1>).  Only files matching the
624 following regexp are considered:
625
626  ^(\d+)(?:-.*)\.(html|sh)$
627
628 For future compatibility, you should ensure that every slide has a
629 unique numeric part (ie. I<don't> have C<0010-aaa.html> and
630 C<0010-bbb.html>).  This is because in future we want to have the
631 ability to display multiple files side by side.
632
633 Also for future compatibility, I<don't> use file names that have an
634 uppercase letter immediately after the numeric part.  This is because
635 in future we want to allow placement hints using filenames like
636 C<0010L-on-the-left.html> and C<0010R-on-the-right.html>.
637
638 =head2 BASE URL AND CURRENT DIRECTORY
639
640 The base URL is set to the be the directory containing the talk files.
641 Thus you should use relative paths, eg:
642
643  <img src="fig1.gif">
644
645 You can also place assets into subdirectories, because subdirectories
646 are ignored by Tech Talk PSE, eg:
647
648  <img src="images/fig1.gif">
649
650 When running shell scripts, the current directory is also set to be
651 the directory containing the talk files, so the same rules about using
652 relative paths apply there too.
653
654 =head1 WHAT MAKES A GOOD TALK
655
656 I like what Edward Tufte writes, for example his evisceration of
657 PowerPoint use at NASA here:
658 L<http://www.edwardtufte.com/bboard/q-and-a-fetch-msg?msg_id=0001yB>
659
660 However it is sometimes hard to translate his ideas into clear
661 presentations, and not all of that is the fault of the tools.  Here
662 are my thoughts and rules on how to deliver a good talk.
663
664 B<First, most important rule:> Before you start drawing any slides at
665 all, write your talk as a short essay.
666
667 This is the number one mistake that presenters make, and it is partly
668 a tool fault, because PowerPoint, OpenOffice, even Tech Talk PSE, all
669 open up on an initial blank slide, inviting you to write a title and
670 some bullet points.  If you start that way, you will end up using the
671 program as a kind of clumsy outlining tool, and then reading that
672 outline to your audience.  That's boring and a waste of time for you
673 and your audience.  (It would be quicker for them just to download the
674 talk and read it at home).
675
676 B<Secondly:> How long do you want to spend preparing the talk?  A good
677 talk, with a sound essay behind it, well thought out diagrams and
678 figures, and interesting demonstrations, takes many hours to prepare.
679 How many hours?  I would suggest thinking about how many hours of
680 effort your audience are putting in.  Even just 20 people sitting
681 there for half an hour is 10 man-hours of attention, and that is a
682 very small talk, and doesn't include all the extra time and hassle
683 that it took to get them all in one place.
684
685 I don't think you can get away with spending less than two full days
686 preparing a talk, if you want to master the topic and draw up accurate
687 slides.  Steve Jobs is reputed to spend weeks preparing his annual
688 sales talk to the Apple faithful.
689
690 B<Thirdly:> Now that you're going to write your talk as an essay, what
691 should go in the slides?  I would say that you should consider
692 delivering the essay, I<not> the slides, to people who don't make the
693 talk.  An essay can be turned into an article or blog posting, whereas
694 even "read-out-the-bullet-point" slides have a low information
695 density, large size, and end-user compatibility problems (*.pptx
696 anyone?).
697
698 What, then, goes on the slides?  Anything you cannot just say:
699 diagrams, graphs, videos, animations, and of course (only with Tech
700 Talk PSE!) demonstrations.
701
702 B<Lastly:> Once you've got your talk as an essay and slides, practice,
703 practice and practice again.  Deliver the talk to yourself in the
704 mirror, to your colleagues.  Practice going backwards and forwards
705 through the slides, using your actual laptop and the software so you
706 know what to click and what keys to press.  Partly memorize what you
707 are going to say (but use short notes written on paper if you need
708 to).
709
710 =head1 SEE ALSO
711
712 The Cognitive Style of PowerPoint, Tufte, Edward R.
713
714 =head1 AUTHOR
715
716 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
717
718 =head1 COPYRIGHT
719
720 Copyright (C) 2010 Red Hat Inc.
721
722 This program is free software; you can redistribute it and/or modify
723 it under the terms of the GNU General Public License as published by
724 the Free Software Foundation; either version 2 of the License, or
725 (at your option) any later version.
726
727 This program is distributed in the hope that it will be useful,
728 but WITHOUT ANY WARRANTY; without even the implied warranty of
729 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
730 GNU General Public License for more details.
731
732 You should have received a copy of the GNU General Public License
733 along with this program; if not, write to the Free Software
734 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.