Add --new flag which creates a skeleton talk.
[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 =head2 CREATING A NEW TALK
71
72 Tech Talk PSE talks are just directories containing C<*.html> and
73 C<*.sh> (shell script) files:
74
75  0010-introduction.html
76  0500-demonstration.sh
77  9900-conclusion.html
78
79 The filenames that Tech Talk PSE considers to be slides have to match
80 the regular expression:
81
82  ^(\d+)(?:-.*)\.(html|sh)$
83
84 (any other file or subdirectory is ignored).  Shell scripts I<must>
85 be executable.
86
87 You can create a new talk just by creating an empty directory and
88 adding files as above, but you can also create a useful skeleton talk
89 like this:
90
91  mkdir talk
92  cd talk
93  techtalk-pse --new
94
95 The C<--new> flag will refuse to overwrite any existing files, so you
96 should run it in an empty directory.
97
98 =head2 DISPLAYING AN EXISTING TALK
99
100 To display or run a talk, change into the directory containing all
101 those files and run the C<techtalk-pse> command:
102
103  cd /path/to/talk/; techtalk-pse
104
105 You can also run C<techtalk-pse> without changing directory, instead
106 specifying the path to the talk:
107
108  techtalk-pse /path/to/talk/
109
110 =head2 OPTIONS
111
112 =over 4
113
114 =cut
115
116 my $help;
117
118 =item B<--help>
119
120 Display brief help and exit.
121
122 =cut
123
124 my $last;
125
126 =item B<--last>
127
128 Start at the last slide.
129
130 You cannot use this with the B<-n> / B<--start> option.
131
132 =cut
133
134 my $new;
135
136 =item B<--new>
137
138 Create a new outline talk in an empty directory.
139
140 This refuses to overwrite existing files.
141
142 =cut
143
144 my $start;
145
146 =item B<-n SLIDE> | B<--start SLIDE>
147
148 Start at the named slide.  I<SLIDE> is the shortest unique prefix of
149 the slide name, so to start at a slide named
150 I<00010-introduction.html>, you could use I<-n 00010> or I<-n 00010-intro>,
151 or give the full filename I<-n 00010-introduction.html>.
152
153 The default is to start at the first slide in the talk.
154
155 =cut
156
157 my $splash = 1;
158
159 =item B<--no-splash>
160
161 Don't display the initial "splash" screen which advertises Tech Talk
162 PSE to your audience.  Just go straight into the talk.
163
164 =cut
165
166 my $verbose;
167
168 =item B<--verbose>
169
170 Display verbose messages, useful for debugging or tracing
171 what the program is doing.
172
173 =cut
174
175 my $version;
176
177 =item B<--version>
178
179 Display version number and exit.
180
181 =cut
182
183 my $mozembed;
184
185 GetOptions ("help|?" => \$help,
186             "last" => \$last,
187             "mozembed" => \$mozembed,
188             "n=s" => \$start,
189             "new" => \$new,
190             "splash!" => \$splash,
191             "start=s" => \$start,
192             "verbose" => \$verbose,
193             "version" => \$version,
194     ) or pod2usage (2);
195
196 =back
197
198 =cut
199
200 pod2usage (1) if $help;
201 if ($version) {
202     print "@PACKAGE@ @VERSION@\n";
203     exit
204 }
205 die "techtalk-pse: cannot use --start and --last options together\n"
206     if defined $last && defined $start;
207
208 die "techtalk-pse: too many arguments\n" if !$mozembed && @ARGV >= 2;
209
210 # Get the true name of the program.
211 $0 = abs_path ($0);
212
213 # Locate the talk.
214 if (@ARGV > 0) {
215     my $d = $ARGV[0];
216     if (-d $d) {
217         chdir $d or die "techtalk-pse: chdir: $d: $!";
218     } else {
219         # XXX In future allow people to specify an archive and unpack
220         # it for them.
221         die "techtalk-pse: argument is not a directory"
222     }
223 }
224
225 # Get the talk directory and set environment variable $talkdir
226 # which is inherited by all the scripts.
227 my $talkdir = getcwd;
228 $ENV{talkdir} = $talkdir;
229
230 # Create a new talk (--new flag).
231 if ($new) {
232     my %files = (
233         "essay.txt" => {
234             mode => 0644,
235             desc => "essay and background notes",
236             c => 'Start by writing your thoughts in this file as an essay.
237
238 You can then provide this as extra background reading material
239 for your audience after the talk.'
240         },
241
242         "0010-introduction.html" => {
243             mode => 0644,
244             desc => "title slide",
245             c => '<link rel="stylesheet" href="style.css" type="text/css"/>
246 <script src="code.js" type="text/javascript"></script>
247
248 <div class="titlepage">
249 <p>A Technical Talk</p>
250 <author>by John Smith (jsmith@example.com)</author>
251 </div>'
252         },
253
254         "0500-demonstration.html" => {
255             mode => 0644,
256             desc => "intro to demonstration slide",
257             c => '<link rel="stylesheet" href="style.css" type="text/css"/>
258 <script src="code.js" type="text/javascript"></script>
259
260 <h1>Demonstration</h1>
261
262 <p>The next slide demonstrates a gnome terminal.</p>
263
264 <p>Hit the UP arrow to access the preloaded history.</p>'
265         },
266
267         "0510-demonstration.sh" => {
268             mode => 0755,
269             desc => "shell demonstration slide",
270             c => '#!/bin/bash -
271 source functions
272 add_history ls -l
273 add_history virt-df -a
274 terminal --title="Demonstration terminal"'
275         },
276
277         "9900-conclusion.html" => {
278             mode => 0644,
279             desc => "last slide",
280             c => '<link rel="stylesheet" href="style.css" type="text/css"/>
281 <script src="code.js" type="text/javascript"></script>
282
283 <h1>Conclusions</h1>
284
285 <p>The conclusion page</p>'
286         },
287
288         "functions" => {
289             mode => 0644,
290             desc => "shell script helper functions",
291             c => '# -*- shell-script -*-
292
293 # Place any local environment variables and settings in "local".
294 if [ -f local ]; then source local; fi
295
296 export PS1=\'\\$ \'
297
298 export HISTFILE=$talkdir/history
299
300 rm -f $HISTFILE
301 touch $HISTFILE
302
303 add_history ()
304 {
305     echo "$@" >> $HISTFILE
306 }
307
308 # Note: If you hand-configure gnome-terminal by adding a
309 # new profile (eg. with larger fonts) then you can use that
310 # profile here by replacing the --window flag with
311 # --window-with-profile=ProfileName
312
313 terminal ()
314 {
315     chmod -w $HISTFILE
316     exec \\
317         gnome-terminal \\
318         --window \\
319         --geometry=+140+64 \\
320         --hide-menubar \\
321         --disable-factory \\
322         -e \'/bin/bash --norc\' \\
323         "$@"
324 }
325 '
326         },
327
328         "style.css" => {
329             mode => 0644,
330             desc => "HTML stylesheet",
331             c => 'body {
332     font-size: 28pt;
333     font-family: liberation, helvetica;
334 }
335
336 h1 {
337     font-size: 48px;
338     top: 8;
339     left: 0;
340     border-bottom: 2px solid #ccc;
341 }
342
343 div.titlepage {
344     margin-top: 100px;
345     text-align: center;
346 }
347 '
348         },
349     );
350
351     # Refuse to overwrite existing files.
352     foreach (sort keys %files) {
353         die "techtalk-pse: refusing to overwrite '$_'\n" if -f $_;
354     }
355
356     # Write the files.
357     foreach (sort keys %files) {
358         print "writing $_ ($files{$_}{desc}) ...\n";
359         open FILE, ">$_" or die "$_: open: $!";
360         print FILE $files{$_}{c} or die "$_: print: $!";
361         close FILE or die "$_: close: $!";
362         chmod $files{$_}{mode}, $_ or die "$_: chmod: $!";
363     }
364
365     exit 0;
366 }
367
368 # Get the files.
369 my @files;
370 my %files;
371 sub reread_directory
372 {
373     @files = ();
374
375     my $i = 0;
376     foreach (glob ("*")) {
377         if (/^(\d+)(?:-.*)\.(html|sh)$/) {
378             print STDERR "reading $_\n" if $verbose;
379
380             my $seq = $1;
381             my $ext = $2;
382             warn "techtalk-pse: $_: command file is not executable (+x)\n"
383                 if $ext eq "sh" && ! -x $_;
384
385             my $h = { name => $_, seq => $1, ext => $2, i => $i };
386             push @files, $h;
387             $files{$_} = $h;
388             $i++;
389         } else {
390             print STDERR "ignoring $_\n" if $verbose;
391         }
392     }
393
394     if (@files > 0) {
395         $files[0]->{first} = 1;
396         $files[$#files]->{last} = 1;
397     }
398 }
399 reread_directory ();
400 print STDERR "read ", 0+@files, " files\n" if $verbose;
401 if (@files == 0) {
402     warn "techtalk-pse: no files found, continuing anyway ...\n"
403 }
404
405 # Run with --mozembed: see below.
406 run_mozembed () if $mozembed;
407
408 # Else, normal run of the program ...
409
410 # Work out what slide we're starting on.
411 my $current;
412 if (defined $current) {
413     die "start slide not implemented yet XXX"
414 }
415 elsif (@files) {
416     $current = $files[0];
417 }
418 # else $current is undefined
419
420 if ($splash) {
421     my $w = Gtk2::AboutDialog->new;
422     $w->set_authors ("Richard W.M. Jones");
423     $w->set_comments (
424         "Superior technical demonstration software\n"
425         );
426     $w->set_program_name ("Tech Talk Platinum Supreme Edition (PSE)");
427     $w->set_version ("@VERSION@");
428     $w->set_website ("http://people.redhat.com/~rjones");
429     $w->set_license ("GNU General Public License v2 or above");
430     $w->run;
431     print STDERR "calling \$w->destroy on about dialog\n" if $verbose;
432     $w->destroy;
433
434     # The dialog doesn't really get destroyed here.  We have
435     # to add this hack to really destroy it.
436     Glib::Idle->add (sub { Gtk2->main_quit; return FALSE; });
437     Gtk2->main;
438 }
439
440 MAIN: while (1) {
441     if (defined $current) {
442         my $go = show_slide ($current);
443         if (defined $go) {
444             print STDERR "go = $go\n" if $verbose;
445             last MAIN if $go eq "QUIT";
446
447             my $i = $current->{i};
448             print STDERR "i = $i\n" if $verbose;
449             $i-- if $go eq "PREV" && $i > 0;
450             $i++ if $go eq "NEXT" && $i+1 < @files;
451             $i = 0 if $go eq "FIRST";
452             $i = $#files if $go eq "LAST";
453             $i = $1 if $go =~ /^I_(\d+)$/;
454             $current = $files[$i];
455         }
456     } else {
457         print "No slides found.  Press any key to reload directory ...\n";
458         $_ = <STDIN>;
459     }
460
461     # Reread directory between slides.
462     reread_directory ();
463
464     if (defined $current && !exists $files{$current->{name}}) {
465         # Current slide was deleted.
466         undef $current;
467         $current = $files[0] if @files;
468     }
469 }
470
471 sub show_slide
472 {
473     my $slide = shift;
474
475     # Display an HTML page.
476     if ($slide->{ext} eq "html") {
477         # MozEmbed is incredibly crashy, so we run ourself as a
478         # subprocess, so when it segfaults we don't care.  If all goes
479         # well and it doesn't crash, it should print a line 'RESULT FOO'
480         # where 'FOO' is the instruction (eg. 'NEXT', 'PREV', 'QUIT' etc).
481         my @cmd = ($0, "--mozembed", $talkdir, $slide->{name});
482         print STDERR "running subcommand: ", join (" ", @cmd), "\n"
483             if $verbose;
484         open CMD, "-|", @cmd
485             or die "failed to execute subcommand: ", join(" ", @cmd), ": $!\n";
486         my $r;
487         while (<CMD>) {
488             if (/^RESULT ([A-Z]+.*)/) {
489                 $r = $1;
490                 print STDERR "subcommand result: $r\n" if $verbose;
491                 last;
492             }
493         }
494         # No RESULT line?  Subcommand probably segfaulted, just
495         # continue to next slide.
496         $r ||= "NEXT";
497         return $r;
498     }
499     # Run a shell command.
500     elsif ($slide->{ext} eq "sh") {
501         my $pid;
502         # http://docstore.mik.ua/orelly/perl/cookbook/ch10_17.htm
503         local *run_process = sub {
504             $pid = fork ();
505             die "fork: $!" unless defined $pid;
506             unless ($pid) {
507                 # Child.
508                 POSIX::setsid ();
509                 exec ("./".$slide->{name});
510                 die "failed to execute command: ", $slide->{name}, ": $!";
511             }
512             # Parent returns.
513         };
514         local *kill_process = sub {
515             print STDERR "sending TERM signal to process group $pid\n"
516                 if $verbose;
517             kill "TERM", -$pid;
518         };
519         run_process ();
520
521         my $r = "NEXT";
522
523         my $w = Gtk2::Window->new ();
524
525         my $s = $w->get_screen;
526         $w->set_default_size ($s->get_width, -1);
527         $w->move (0, 0);
528         $w->set_decorated (0);
529
530         my $bbox =
531             make_button_bar ((exists $slide->{first}),
532                              (exists $slide->{last}),
533                              sub { $r = $_[0]; $w->destroy },
534                              restart => sub {
535                                  kill_process ();
536                                  run_process ();
537                              },
538             );
539
540         $w->add ($bbox);
541
542         $w->signal_connect (destroy => sub {
543             Gtk2->main_quit;
544             return FALSE;
545         });
546         $w->show_all ();
547
548         Gtk2->main;
549
550         kill_process ();
551         print STDERR "returning r=$r\n" if $verbose;
552         return $r;
553     }
554 }
555
556 # If invoked with the --mozembed parameter then we just display a
557 # single page.  This is just to prevent crashes in MozEmbed from
558 # killing the whole program.
559 sub run_mozembed
560 {
561     my $w = Gtk2::Window->new ();
562     my $vbox = Gtk2::VBox->new ();
563     my $moz = Gtk2::MozEmbed->new ();
564
565     reread_directory ();
566
567     my $name = $ARGV[1];
568     $current = $files{$name};
569     my $url = "file://$talkdir/$name";
570
571     my $bbox =
572         make_button_bar ($current->{first}, $current->{last},
573                          sub { print "RESULT ", $_[0], "\n"; $w->destroy }
574         );
575
576     $vbox->pack_start ($bbox, 0, 0, 0);
577     $vbox->add ($moz);
578     $w->fullscreen ();
579     #$w->set_default_size (640, 480);
580     $w->add ($vbox);
581
582     $w->signal_connect (destroy => sub {
583         Gtk2->main_quit;
584         return FALSE;
585     });
586     $w->show_all ();
587
588     $moz->load_url ($url);
589
590     Gtk2->main;
591
592     exit 0;
593 }
594
595 # Make the standard button bar across the top of the page.
596 sub make_button_bar
597 {
598     my $first = shift;
599     my $last = shift;
600     my $cb = shift;
601     my %params = @_;
602
603     my $bbox = Gtk2::Toolbar->new ();
604     $bbox->set_style ("GTK_TOOLBAR_TEXT");
605
606     my $i = 0;
607
608     my $bnext = Gtk2::ToolButton->new (undef, "Next slide");
609     $bnext->signal_connect (clicked => sub { &$cb ("NEXT") });
610     $bnext->set_sensitive (!$last);
611     $bbox->insert ($bnext, $i++);
612
613     my $bback = Gtk2::ToolButton->new (undef, "Back");
614     $bback->signal_connect (clicked => sub { &$cb ("PREV") });
615     $bback->set_sensitive (!$first);
616     $bbox->insert ($bback, $i++);
617
618     if (exists $params{restart}) {
619         $bbox->insert (Gtk2::SeparatorToolItem->new (), $i++);
620
621         my $brestart = Gtk2::ToolButton->new (undef, "Kill & restart");
622         $brestart->signal_connect (clicked => $params{restart});
623         $bbox->insert ($brestart, $i++);
624     }
625
626     my $sep = Gtk2::SeparatorToolItem->new ();
627     $sep->set_expand (TRUE);
628     $sep->set_draw (FALSE);
629     $bbox->insert ($sep, $i++);
630
631     my $optsmenu = Gtk2::Menu->new ();
632
633     my $bfirst = Gtk2::MenuItem->new ("First slide");
634     $bfirst->signal_connect (activate => sub { \&$cb ("FIRST") });
635     $bfirst->show ();
636     $optsmenu->append ($bfirst);
637
638     my $blast = Gtk2::MenuItem->new ("Last slide");
639     $blast->signal_connect (activate => sub { \&$cb ("LAST") });
640     $blast->show ();
641     $optsmenu->append ($blast);
642
643     my $slidesmenu = Gtk2::Menu->new ();
644     foreach (@files) {
645         my $item = Gtk2::MenuItem->new ($_->{name});
646         my $index = $_->{i};
647         $item->signal_connect (activate => sub { \&$cb ("I_$index") });
648         $item->set_sensitive ($current->{i} != $index);
649         $item->show ();
650         $slidesmenu->append ($item);
651     }
652
653     my $bslides = Gtk2::MenuItem->new ("Slides");
654     $bslides->set_submenu ($slidesmenu);
655     $bslides->show ();
656     $optsmenu->append ($bslides);
657
658     my $sep2 = Gtk2::SeparatorMenuItem->new ();
659     $sep2->show ();
660     $optsmenu->append ($sep2);
661
662     my $bscreenshot = Gtk2::MenuItem->new ("Take a screenshot");
663     $bscreenshot->signal_connect (activate => sub { screenshot () });
664     $bscreenshot->show ();
665     $optsmenu->append ($bscreenshot);
666
667     my $sep3 = Gtk2::SeparatorMenuItem->new ();
668     $sep3->show ();
669     $optsmenu->append ($sep3);
670
671     my $bquit = Gtk2::MenuItem->new ("Quit");
672     $bquit->signal_connect (activate => sub { \&$cb ("QUIT") });
673     $bquit->show ();
674     $optsmenu->append ($bquit);
675
676     my $boptions = Gtk2::MenuToolButton->new (undef, "Options");
677     #$boptions->signal_connect (clicked =>
678     #  sub { $optsmenu->popup (undef, undef, undef, undef, ?, ?) } );
679     $bbox->insert ($boptions, $i++);
680     $boptions->set_menu ($optsmenu);
681
682     return $bbox;
683 }
684
685 # Try running the external "gnome-screenshot" program, if it's
686 # available, else take a screenshot using gdk routines.
687 sub screenshot
688 {
689     system ("gnome-screenshot");
690
691     if ($? == -1) {
692         # We are going to save the entire screen.
693         my $root = Gtk2::Gdk->get_default_root_window ();
694         my ($width, $height) = $root->get_size;
695
696         # Create blank pixbuf to hold the image.
697         my $gdkpixbuf = Gtk2::Gdk::Pixbuf->new ('rgb',
698                                                 0, 8, $width, $height);
699
700         $gdkpixbuf->get_from_drawable ($root, $root->get_colormap (),
701                                        0, 0, 0, 0, $width, $height);
702
703         my $i = 0;
704         $i++ while -f "screenshot$i.png";
705         $gdkpixbuf->save ("screenshot$i.png", 'png');
706     }
707
708     return FALSE;
709 }
710
711 1;
712
713 __END__
714
715 =head1 TUTORIAL
716
717 =head2 START WRITING A TALK
718
719 [Before you start writing your real talk, I urge you to read
720 L</WHAT MAKES A GOOD TALK> below].
721
722 To start your talk, all you have to do is to make a new directory
723 somewhere and run Tech Talk PSE with the C<--new> flag to create an
724 outline:
725
726  mkdir talk
727  cd talk
728  techtalk-pse --new
729
730 A tech talk consists of HTML files ("slides") and shell scripts.  The
731 filenames must start with a number, followed optionally by a
732 description, followed by the extension (C<.html> or C<.sh>).
733
734 To run it, run the command from within the talk directory:
735
736  techtalk-pse
737
738 Any other file in the directory is ignored, so if you want to add
739 Makefiles, version control files etc, just go ahead.
740
741 =head2 TIPS FOR WRITING HTML
742
743 You may have your own techniques and tools for writing HTML, so
744 this section is just to share my ideas.  I start every
745 HTML file with a standard stylesheet and Javascript header:
746
747  <link rel="stylesheet" href="style.css" type="text/css"/>
748  <script src="code.js" type="text/javascript"></script>
749
750 That just ensures that I can put common styling instructions for all
751 my slides in a single file (C<style.css>), and I have one place where
752 I can add all Javascript, if I need to use any (C<code.js>).
753
754 =head3 BACKGROUNDS, FONTS AND LOGOS
755
756 To add a common background and font size to all slides, put this in
757 C<style.css>:
758
759  body {
760      font-size: 24pt;
761      background: url(background-image.jpg) no-repeat;
762  }
763
764 To add a logo in one corner:
765
766  body {
767      background: url(logo.jpg) top right no-repeat;
768  }
769
770 =head3 SCALING AND CENTERING
771
772 Scaling slide text and images so that they appear at the same
773 proportionate size for any screen resolution can be done using
774 Javascript.  (See
775 L<https://developer.mozilla.org/En/DOM/window.innerHeight>).
776
777 If you want to center text horizontally, use CSS, eg:
778
779  p.center {
780      text-align: center;
781  }
782
783 To center text vertically, CSS3 is supposed to offer a solution some
784 time, but while you're waiting for that try
785 L<http://www.w3.org/Style/Examples/007/center#vertical>.
786
787 =head3 PREVIEWING HTML
788
789 I find it helpful to have Firefox open to display the HTML files and
790 styles as I edit them.  Just start firefox in the talk directory:
791
792  firefox file://$(pwd) &
793
794 When you edit an HTML file, click the Firefox reload button to
795 immediately see your changes.
796
797 Tech Talk PSE uses Mozilla embedding to display HTML, which uses the
798 same Mozilla engine as Firefox, so what you should see in Firefox
799 should be identical to what Tech Talk PSE displays.
800
801 =head2 CREATING FIGURES
802
803 Use your favorite tool to draw the figure, convert it to an image (in
804 any format that the Mozilla engine can display) and include it using
805 an C<E<lt>imgE<gt>> tag, eg:
806
807  <img src="fig1.gif">
808
809 Suitable tools include: XFig, GnuPlot, GraphViz, and many TeX tools
810 such as PicTex and in particular TikZ.
811
812 =head2 EMBEDDING VIDEOS, ANIMATIONS, ETC.
813
814 Using HTML 5, embedding videos in the browser is easy.  See:
815 L<https://developer.mozilla.org/En/Using_audio_and_video_in_Firefox>
816
817 For animations, you could try L<Haxe|http://haxe.org/> which has a
818 Javascript back-end.  There are many other possibilities.
819
820 If you are B<sure> that the venue will have an internet connection,
821 why not embed a YouTube video.
822
823 =head2 DISPLAYING EXISTING WEB PAGES
824
825 Obviously you could just have an HTML file that contains a redirect to
826 the public web page:
827
828  <meta http-equiv="Refresh" content="0; url=http://www.example.com/">
829
830 However if you want your talk to work offline, then it's better to
831 download the web page in advance, eg. using Firefox's "Save Page As
832 -E<gt> Web Page, complete" feature, into the talk directory, then
833 either rename or make a symbolic link to the slide name:
834
835  ln -s "haXe - Welcome to haXe.html" 0010-haxe-homepage.html
836
837 =head2 TIPS FOR WRITING SHELL SCRIPTS
838
839 Make sure each C<*.sh> file you write is executable, otherwise Tech
840 Talk PSE won't be able to run it.  (The program gives a warning if you
841 forget this).
842
843 A good idea is to start each script by sourcing some common functions.
844 All my scripts start with:
845
846  #!/bin/bash -
847  source functions
848
849 where C<functions> is another file (ignored by Tech Talk PSE) which
850 contains common functions for setting shell history and starting a
851 terminal.
852
853 In C<functions>, I have:
854
855  # -*- shell-script -*-
856  
857  # Place any local environment variables required in 'local'.
858  if [ -f local ]; then source local; fi
859  
860  export PS1="$ "
861  
862  export HISTFILE=$talkdir/history
863  
864  rm -f $HISTFILE
865  touch $HISTFILE
866  
867  add_history ()
868  {
869      echo "$@" >> $HISTFILE
870  }
871  
872  terminal ()
873  {
874      # Make $HISTFILE unwritable so the shell won't update it
875      # when it exits.
876      chmod -w $HISTFILE
877  
878      # Run gnome-terminal.
879      exec \
880          gnome-terminal \
881          --window \
882          --geometry=+100+100 \
883          --hide-menubar \
884          --disable-factory \
885          -e '/bin/bash --norc' \
886          "$@"
887  }
888
889 By initializing the shell history, during your talk you can rapidly
890 recall commands to start parts of the demonstration just by hitting
891 the Up arrow.  A complete shell script from one of my talks would look
892 like this:
893
894  #!/bin/bash -
895  source functions
896  add_history guestfish -i debian.img
897  terminal --title="Examining a Debian guest image in guestfish"
898
899 This is just a starting point for your own scripts.  You may want to
900 use a different terminal, such as xterm, and you may want to adjust
901 terminal fonts.
902
903 =head1 REFERENCE
904
905 =head2 ORDER OF FILES
906
907 Tech Talk PSE displays the slides in the directory in lexicographic
908 order (the same order as C<LANG=C ls -1>).  Only files matching the
909 following regexp are considered:
910
911  ^(\d+)(?:-.*)\.(html|sh)$
912
913 For future compatibility, you should ensure that every slide has a
914 unique numeric part (ie. I<don't> have C<0010-aaa.html> and
915 C<0010-bbb.html>).  This is because in future we want to have the
916 ability to display multiple files side by side.
917
918 Also for future compatibility, I<don't> use file names that have an
919 uppercase letter immediately after the numeric part.  This is because
920 in future we want to allow placement hints using filenames like
921 C<0010L-on-the-left.html> and C<0010R-on-the-right.html>.
922
923 =head2 BASE URL AND CURRENT DIRECTORY
924
925 The base URL is set to the be the directory containing the talk files.
926 Thus you should use relative paths, eg:
927
928  <img src="fig1.gif">
929
930 You can also place assets into subdirectories, because subdirectories
931 are ignored by Tech Talk PSE, eg:
932
933  <img src="images/fig1.gif">
934
935 When running shell scripts, the current directory is also set to be
936 the directory containing the talk files, so the same rules about using
937 relative paths apply there too.
938
939 The environment variable C<$talkdir> is exported to scripts and it
940 contains the absolute path of the directory containing the talk files.
941 When a script is run, the current directory is the same as
942 C<$talkdir>, but if your script changes directory (eg. into a
943 subdirectory containing supporting files) then it can be useful to use
944 C<$talkdir> to refer back to the original directory.
945
946 =head1 WHAT MAKES A GOOD TALK
947
948 I like what Edward Tufte writes, for example his evisceration of
949 PowerPoint use at NASA here:
950 L<http://www.edwardtufte.com/bboard/q-and-a-fetch-msg?msg_id=0001yB>
951
952 However it is sometimes hard to translate his ideas into clear
953 presentations, and not all of that is the fault of the tools.  Here
954 are my thoughts and rules on how to deliver a good talk.
955
956 B<First, most important rule:> Before you start drawing any slides at
957 all, write your talk as a short essay.
958
959 This is the number one mistake that presenters make, and it is partly
960 a tool fault, because PowerPoint, OpenOffice, even Tech Talk PSE, all
961 open up on an initial blank slide, inviting you to write a title and
962 some bullet points.  If you start that way, you will end up using the
963 program as a kind of clumsy outlining tool, and then reading that
964 outline to your audience.  That's boring and a waste of time for you
965 and your audience.  (It would be quicker for them just to download the
966 talk and read it at home).
967
968 B<Secondly:> How long do you want to spend preparing the talk?  A good
969 talk, with a sound essay behind it, well thought out diagrams and
970 figures, and interesting demonstrations, takes many hours to prepare.
971 How many hours?  I would suggest thinking about how many hours of
972 effort your audience are putting in.  Even just 20 people sitting
973 there for half an hour is 10 man-hours of attention, and that is a
974 very small talk, and doesn't include all the extra time and hassle
975 that it took to get them all in one place.
976
977 I don't think you can get away with spending less than two full days
978 preparing a talk, if you want to master the topic and draw up accurate
979 slides.  Steve Jobs is reputed to spend weeks preparing his annual
980 sales talk to the Apple faithful.
981
982 B<Thirdly:> Now that you're going to write your talk as an essay, what
983 should go in the slides?  I would say that you should consider
984 delivering the essay, I<not> the slides, to people who don't make the
985 talk.  An essay can be turned into an article or blog posting, whereas
986 even "read-out-the-bullet-point" slides have a low information
987 density, large size, and end-user compatibility problems (*.pptx
988 anyone?).
989
990 What, then, goes on the slides?  Anything you cannot just say:
991 diagrams, graphs, videos, animations, and of course (only with Tech
992 Talk PSE!) demonstrations.
993
994 B<Lastly:> Once you've got your talk as an essay and slides, practice,
995 practice and practice again.  Deliver the talk to yourself in the
996 mirror, to your colleagues.  Practice going backwards and forwards
997 through the slides, using your actual laptop and the software so you
998 know what to click and what keys to press.  Partly memorize what you
999 are going to say (but use short notes written on paper if you need
1000 to).
1001
1002 =head1 SEE ALSO
1003
1004 The Cognitive Style of PowerPoint, Tufte, Edward R.
1005
1006 =head1 AUTHOR
1007
1008 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
1009
1010 =head1 COPYRIGHT
1011
1012 Copyright (C) 2010 Red Hat Inc.
1013
1014 This program is free software; you can redistribute it and/or modify
1015 it under the terms of the GNU General Public License as published by
1016 the Free Software Foundation; either version 2 of the License, or
1017 (at your option) any later version.
1018
1019 This program is distributed in the hope that it will be useful,
1020 but WITHOUT ANY WARRANTY; without even the implied warranty of
1021 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1022 GNU General Public License for more details.
1023
1024 You should have received a copy of the GNU General Public License
1025 along with this program; if not, write to the Free Software
1026 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.