Rewrite techtalk to use Webkit instead of (unmaintained) Mozembed.
[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::Gdk::Keysyms;
33 use Gtk2::WebKit;
34 use Gnome2::Vte;
35
36 =encoding utf8
37
38 =head1 NAME
39
40 techtalk-pse - superior technical demonstration software
41
42 =head1 SYNOPSIS
43
44  cd /path/to/talk/; techtalk-pse
45
46  techtalk-pse /path/to/talk/
47
48 =head1 DESCRIPTION
49
50 Tech Talk "Platinum Supreme Edition" (PSE) is Linux Presentation
51 Software designed by technical people to give technical software
52 demonstrations to other technical people.  It is designed to be simple
53 to use (for people who know how to use an editor and the command line)
54 and powerful, so that you can create informative, technically accurate
55 and entertaining talks and demonstrations.
56
57 Tech Talk PSE is good at opening editors at the right place, opening
58 shell prompts with preloaded history, compiling and running things
59 during the demonstration, displaying text, photos, figures and video.
60
61 Tech Talk PSE is I<bad> at slide effects, chart junk and bullet
62 points.
63
64 This manual page covers all the documentation you will need to use
65 Tech Talk PSE.  The next section covers running the tool from the
66 command line.  After that there is a L</TUTORIAL> section to get you
67 started.  Then there is a detailed L</REFERENCE> section.  Finally
68 there is a discussion on L<WHAT MAKES A GOOD TALK>.
69
70 =head1 RUNNING THE TOOL FROM THE COMMAND LINE
71
72 A Tech Talk PSE talk is not a single file, but a directory full of
73 files.  (If you want to start a new talk, see the L</TUTORIAL> section
74 below).  To display or run the talk, change into the directory
75 containing all those files and run the C<techtalk-pse> command:
76
77  cd /path/to/talk/; techtalk-pse
78
79 You can also run C<techtalk-pse> without changing directory, instead
80 specifying the path to the talk:
81
82  techtalk-pse /path/to/talk/
83
84 =head2 OPTIONS
85
86 =over 4
87
88 =cut
89
90 my $help;
91
92 =item B<--help>
93
94 Display brief help and exit.
95
96 =cut
97
98 my $last;
99
100 =item B<--last>
101
102 Start at the last slide.
103
104 You cannot use this with the B<-n> / B<--start> option.
105
106 =cut
107
108 my $start;
109
110 =item B<-n SLIDE> | B<--start SLIDE>
111
112 Start at the named slide.  I<SLIDE> is the shortest unique prefix of
113 the slide name, so to start at a slide named
114 I<00010-introduction.html>, you could use I<-n 00010> or I<-n 00010-intro>,
115 or give the full filename I<-n 00010-introduction.html>.
116
117 The default is to start at the first slide in the talk.
118
119 =cut
120
121 my $verbose;
122
123 =item B<--verbose>
124
125 Display verbose messages, useful for debugging or tracing
126 what the program is doing.
127
128 =cut
129
130 my $version;
131
132 =item B<--version>
133
134 Display version number and exit.
135
136 =cut
137
138 GetOptions ("help|?" => \$help,
139             "last" => \$last,
140             "n=s" => \$start,
141             "start=s" => \$start,
142             "verbose" => \$verbose,
143             "version" => \$version,
144     ) or pod2usage (2);
145
146 =back
147
148 =cut
149
150 pod2usage (1) if $help;
151 if ($version) {
152     print "@PACKAGE@ @VERSION@\n";
153     exit
154 }
155 die "techtalk-pse: cannot use --start and --last options together\n"
156     if defined $last && defined $start;
157
158 die "techtalk-pse: too many arguments\n" if @ARGV >= 2;
159
160 # Get the true name of the program.
161 $0 = abs_path ($0);
162
163 # Locate the talk.
164 if (@ARGV > 0) {
165     my $d = $ARGV[0];
166     if (-d $d) {
167         chdir $d or die "techtalk-pse: chdir: $d: $!";
168     } else {
169         # XXX In future allow people to specify an archive and unpack
170         # it for them.
171         die "techtalk-pse: argument is not a directory"
172     }
173 }
174
175 # Get the talk directory and set environment variable $talkdir
176 # which is inherited by all the scripts.
177 my $talkdir = getcwd;
178 $ENV{talkdir} = $talkdir;
179
180 # Get the files.
181 my @files;
182 my %files;
183 my $current;
184 my $pid;
185 my $pipeline;
186
187 &reread_directory ();
188
189 print STDERR "read ", 0+@files, " files\n" if $verbose;
190 if (@files == 0) {
191     warn "techtalk-pse: no files found, continuing anyway ...\n"
192 }
193
194 my $w = Gtk2::Window->new ();
195 my $vbox = Gtk2::VBox->new ();
196 my $webkit = Gtk2::WebKit::WebView->new ();
197 my $vte = Gnome2::Vte::Terminal->new ();
198 my $notebook = Gtk2::Notebook->new ();
199 my $splash = make_splash_page ();
200
201 my $webkitscroll = Gtk2::ScrolledWindow->new ();
202 $webkitscroll->add ($webkit);
203 $webkitscroll->set_policy('automatic', 'automatic');
204
205 my $webkitpage = $notebook->append_page ($webkitscroll);
206 my $vtepage = $notebook->append_page ($vte);
207 my $splashpage = $notebook->append_page ($splash);
208
209 my ($bbox, $bquit, $breload, $bnext, $bback, $brestart) = make_button_bar ();
210
211 $vbox->pack_start($bbox, 0, 0, 0);
212 $vbox->pack_start($notebook, 1, 1, 0);
213
214 $notebook->set_show_tabs(0);
215 $notebook->set_show_border(0);
216
217 # Default font size is almost certainly too small
218 # for audience to see.
219 # XXX we should make font size configurable via
220 # @ARGV.
221 # XXX any way we can scale WebKit programmatically
222 # to set base size which CSS is relative to ?
223 # NB careful setting it too big, because it will
224 # force a min size on the terminal. Scaling 1.3
225 # is biggest we can do while fitting 1024x768
226 my $font = $vte->get_font;
227 $font->set_size($font->get_size * 1.3);
228
229 # When an external command exits, automatically
230 # go to the next slide
231 $vte->signal_connect (
232     'child-exited' => sub {
233         if ($pid) {
234             $pid = 0;
235             &switch_slide("NEXT");
236         }
237     });
238
239 # Exit if the window is closed
240 $w->signal_connect (
241     destroy => sub {
242         Gtk2->main_quit;
243         return FALSE;
244     });
245
246 # Handle left/right arrows, page up/down & home/end
247 # as slide navigation commands. But not when there
248 # is a shell running
249 $w->signal_connect (
250     'key-press-event' => sub {
251         my $src = shift;
252         my $ev = shift;
253
254         # If a shell is running, don't trap keys
255         if ($pid) {
256             return 0;
257         }
258
259         if ($ev->keyval == $Gtk2::Gdk::Keysyms{Right} ||
260             $ev->keyval == $Gtk2::Gdk::Keysyms{Page_Down}) {
261             &switch_slide("NEXT");
262             return 1;
263         } elsif ($ev->keyval == $Gtk2::Gdk::Keysyms{Left} ||
264                  $ev->keyval == $Gtk2::Gdk::Keysyms{Page_Up}) {
265             &switch_slide("PREV");
266             return 1;
267         } elsif ($ev->keyval == $Gtk2::Gdk::Keysyms{Home}) {
268             &switch_slide("FIRST");
269             return 1;
270         } elsif ($ev->keyval == $Gtk2::Gdk::Keysyms{End}) {
271             &switch_slide("LAST");
272             return 1;
273         } elsif ($ev->keyval == $Gtk2::Gdk::Keysyms{q} ||
274                  $ev->keyval == $Gtk2::Gdk::Keysyms{Escape}) {
275             Gtk2->main_quit;
276             return 1;
277         }
278         return 0;
279     });
280
281
282 $w->add ($vbox);
283 $w->show_all ();
284
285 $w->set_decorated (0);
286 $w->fullscreen ();
287 $w->move (0,0);
288
289 my $scr = $w->get_screen();
290
291 &update_slide();
292
293 Gtk2->main();
294
295 exit 0;
296
297 sub reread_directory
298 {
299     @files = ();
300
301     my $i = 0;
302     foreach (glob ("*")) {
303         if (/^(\d+)(?:-.*)\.(html|sh)$/) {
304             print STDERR "reading $_\n" if $verbose;
305
306             my $seq = $1;
307             my $ext = $2;
308             warn "techtalk-pse: $_: command file is not executable (+x)\n"
309                 if $ext eq "sh" && ! -x $_;
310
311             my $h = { name => $_, seq => $1, ext => $2, i => $i };
312             push @files, $h;
313             $files{$_} = $h;
314             $i++;
315         } else {
316             print STDERR "ignoring $_\n" if $verbose;
317         }
318     }
319
320     if (@files > 0) {
321         $files[0]->{first} = 1;
322         $files[$#files]->{last} = 1;
323     }
324
325     # Work out what slide we're starting on.
326     if (@files && !$current) {
327         if ($start) {
328             foreach my $file (@files) {
329                 if ($file->{name} =~ /^$start/) {
330                     $current = $file;
331                     last;
332                 }
333             }
334         } elsif ($last) {
335             $current = $files[$#files];
336         }
337         if (!$current) {
338             $current = $files[0];
339         }
340     }
341 }
342
343 sub run_process
344 {
345     $pid = $vte->fork_command("./" . $current->{name}, [], [], undef, 0, 0, 0);
346 }
347
348 sub kill_process
349 {
350     print STDERR "sending TERM signal to process group $pid\n"
351         if $verbose;
352     kill "TERM", -$pid;
353
354     # Clears out any current displayed text
355     $vte->reset(1, 1);
356     $vte->set_default_colors();
357     $pid = 0;
358 }
359
360 sub switch_slide
361 {
362     my $action = shift;
363
364     if ($pid) {
365         kill_process ();
366     }
367     if ($pipeline) {
368         $pipeline->set_state('ready');
369         $pipeline = undef;
370     }
371     print STDERR "action = $action\n" if $verbose;
372
373     my $i = defined $current ? $current->{i} : 0;
374
375     print STDERR "i = $i\n" if $verbose;
376     if ($action eq "PREV") {
377         if (defined $current) {
378             $i--;
379         } else {
380             $i = $#files;
381         }
382     } elsif ($action eq "NEXT") {
383         $i++;
384     } elsif ($action eq "FIRST") {
385         $i = 0;
386     } elsif ($action eq "LAST") {
387         $i = $#files;
388     } elsif ($action =~ /^I_(\d+)$/) {
389         $i = $1;
390     }
391
392     $i = 0 if $i < 0;
393     if ($i > $#files) {
394         $current = undef;
395     } else {
396         $current = $files[$i];
397     }
398
399     &update_slide ();
400
401 }
402
403 sub update_slide
404 {
405     if ($current) {
406         # Display an HTML page.
407         if ($current->{ext} eq "html") {
408             $notebook->set_current_page ($webkitpage);
409             my $name = $current->{name};
410             my $url = "file://$talkdir/$name";
411
412             $webkit->load_uri ($url);
413             $webkit->grab_focus ();
414         }
415         # Run a shell command.
416         elsif ($current->{ext} eq "sh") {
417             $notebook->set_current_page ($vtepage);
418             $vte->grab_focus ();
419             run_process ();
420         }
421     } else {
422         $notebook->set_current_page ($splashpage);
423     }
424
425     if ($pid) {
426         $brestart->show ();
427     } else {
428         $brestart->hide ();
429     }
430
431     if (defined $current) {
432         $bquit->hide ();
433         $breload->hide ();
434         $bnext->set_sensitive (1);
435         $bback->set_sensitive (!exists $current->{first});
436     } else {
437         $bquit->show ();
438         if (@files) {
439             $breload->hide ();
440         } else {
441             $breload->show ();
442         }
443         $bnext->set_sensitive (0);
444         $bback->set_sensitive (int(@files));
445     }
446 }
447
448
449 sub make_splash_page {
450     my $box = Gtk2::VBox->new();
451
452     my $title = Gtk2::Label->new ("<b><span size='x-large'>Tech Talk Platinum Supreme Edition (PSE)</span></b>");
453     $title->set_use_markup (1);
454
455     $box->pack_start ($title, 0, 1, 0);
456
457     my $vers = Gtk2::Label->new ("<b><span size='large'>@VERSION@</span></b>");
458     $vers->set_use_markup (1);
459     $box->pack_start ($vers, 0, 1, 0);
460
461     my $tagline = Gtk2::Label->new ("<i><span size='large'>Superior technical demonstration software</span></i>");
462     $tagline->set_use_markup (1);
463
464     $box->pack_start ($tagline, 0, 1, 0);
465     $box->pack_start (Gtk2::Label->new (""), 0, 1, 0);
466     $box->pack_start (Gtk2::Label->new ("Author: Richard W.M. Jones"), 0, 1, 0);
467
468     my $url = Gtk2::Label->new ("<a href='http://people.redhat.com/~rjones'>http;//people.redhat.com/~rjones/</a>");
469     $url->set_use_markup (1);
470     $box->pack_start ($url, 0, 1, 0);
471     $box->pack_start (Gtk2::Label->new ("GNU General Public License v2 or above"), 0, 1, 0);
472
473     return $box;
474 }
475
476 # Make the standard button bar across the top of the page.
477 sub make_button_bar
478 {
479     my $bbox = Gtk2::Toolbar->new ();
480     $bbox->set_style ("GTK_TOOLBAR_TEXT");
481
482     my $i = 0;
483
484     my $bquit = Gtk2::ToolButton->new (undef, "Quit");
485     $bquit->signal_connect (clicked => sub { Gtk2->main_quit });
486     $bbox->insert ($bquit, $i++);
487
488     my $breload = Gtk2::ToolButton->new (undef, "Reload");
489     $breload->signal_connect (clicked => sub { reread_directory () });
490     $bbox->insert ($breload, $i++);
491
492     my $bnext = Gtk2::ToolButton->new (undef, "Next slide");
493     $bnext->signal_connect (clicked => sub { &switch_slide ("NEXT") });
494     $bbox->insert ($bnext, $i++);
495
496     my $bback = Gtk2::ToolButton->new (undef, "Back");
497     $bback->signal_connect (clicked => sub { &switch_slide ("PREV") });
498     $bbox->insert ($bback, $i++);
499
500     $bbox->insert (Gtk2::SeparatorToolItem->new (), $i++);
501
502     my $brestart = Gtk2::ToolButton->new (undef, "Kill & restart");
503     $brestart->signal_connect (clicked =>
504                                sub {
505                                    kill_process ();
506                                    run_process ();
507                                });
508     $bbox->insert ($brestart, $i++);
509
510     my $sep = Gtk2::SeparatorToolItem->new ();
511     $sep->set_expand (TRUE);
512     $sep->set_draw (FALSE);
513     $bbox->insert ($sep, $i++);
514
515     my $optsmenu = Gtk2::Menu->new ();
516
517     my $mfirst = Gtk2::MenuItem->new ("First slide");
518     $mfirst->signal_connect (activate => sub { &switch_slide ("FIRST") });
519     $mfirst->show ();
520     $optsmenu->append ($mfirst);
521
522     my $mlast = Gtk2::MenuItem->new ("Last slide");
523     $mlast->signal_connect (activate => sub { &switch_slide ("LAST") });
524     $mlast->show ();
525     $optsmenu->append ($mlast);
526
527     my $slidesmenu = Gtk2::Menu->new ();
528     foreach (@files) {
529         my $item = Gtk2::MenuItem->new ($_->{name});
530         my $index = $_->{i};
531         $item->signal_connect (activate => sub { &switch_slide ("I_$index") });
532         $item->set_sensitive ($current->{i} != $index);
533         $item->show ();
534         $slidesmenu->append ($item);
535     }
536
537     my $mslides = Gtk2::MenuItem->new ("Slides");
538     $mslides->set_submenu ($slidesmenu);
539     $mslides->show ();
540     $optsmenu->append ($mslides);
541
542     my $sep2 = Gtk2::SeparatorMenuItem->new ();
543     $sep2->show ();
544     $optsmenu->append ($sep2);
545
546     my $mscreenshot = Gtk2::MenuItem->new ("Take a screenshot");
547     $mscreenshot->signal_connect (activate => sub { screenshot () });
548     $mscreenshot->show ();
549     $optsmenu->append ($mscreenshot);
550
551     my $sep3 = Gtk2::SeparatorMenuItem->new ();
552     $sep3->show ();
553     $optsmenu->append ($sep3);
554
555     my $mquit = Gtk2::MenuItem->new ("Quit");
556     $mquit->signal_connect (activate => sub { Gtk2->main_quit });
557     $mquit->show ();
558     $optsmenu->append ($mquit);
559
560     my $moptions = Gtk2::MenuToolButton->new (undef, "Options");
561     #$boptions->signal_connect (clicked =>
562     #  sub { $optsmenu->popup (undef, undef, undef, undef, ?, ?) } );
563     $bbox->insert ($moptions, $i++);
564     $moptions->set_menu ($optsmenu);
565
566     return ($bbox, $bquit, $breload, $bnext, $bback, $brestart);
567 }
568
569 # Try running the external "gnome-screenshot" program, if it's
570 # available, else take a screenshot using gdk routines.
571 sub screenshot
572 {
573     system ("gnome-screenshot");
574
575     if ($? == -1) {
576         # We are going to save the entire screen.
577         my $root = Gtk2::Gdk->get_default_root_window ();
578         my ($width, $height) = $root->get_size;
579
580         # Create blank pixbuf to hold the image.
581         my $gdkpixbuf = Gtk2::Gdk::Pixbuf->new ('rgb',
582                                                 0, 8, $width, $height);
583
584         $gdkpixbuf->get_from_drawable ($root, $root->get_colormap (),
585                                        0, 0, 0, 0, $width, $height);
586
587         my $i = 0;
588         $i++ while -f "screenshot$i.png";
589         $gdkpixbuf->save ("screenshot$i.png", 'png');
590     }
591
592     return FALSE;
593 }
594
595 1;
596
597 __END__
598
599 =head1 TUTORIAL
600
601 =head2 START WRITING A TALK
602
603 [Before you start writing your real talk, I urge you to read
604 L</WHAT MAKES A GOOD TALK> below].
605
606 To start your talk, all you have to do is to make a new directory
607 somewhere:
608
609  mkdir talk
610  cd talk
611
612 A tech talk consists of HTML files ("slides") and shell scripts.  The
613 filenames must start with a number, followed optionally by a
614 description, followed by the extension (C<.html> or C<.sh>).  So to
615 start our talk with two slides:
616
617  echo "This is the introduction" > 0010-introduction.html
618  echo "This is the second slide" > 0020-second.html
619
620 To run it, run the command from within the talk directory:
621
622  techtalk-pse
623
624 Any other file in the directory is ignored, so if you want to add
625 Makefiles, version control files etc, just go ahead.
626
627 =head2 TIPS FOR WRITING HTML
628
629 You may have your own techniques and tools for writing HTML, so
630 this section is just to share my ideas.  I start every
631 HTML file with a standard stylesheet and Javascript header:
632
633  <link rel="stylesheet" href="style.css" type="text/css"/>
634  <script src="code.js" type="text/javascript"></script>
635
636 That just ensures that I can put common styling instructions for all
637 my slides in a single file (C<style.css>), and I have one place where
638 I can add all Javascript, if I need to use any (C<code.js>).
639
640 =head3 BACKGROUNDS, FONTS AND LOGOS
641
642 To add a common background and font size to all slides, put this in
643 C<style.css>:
644
645  body {
646      font-size: 24pt;
647      background: url(background-image.jpg) no-repeat;
648  }
649
650 To add a logo in one corner:
651
652  body {
653      background: url(logo.jpg) top right no-repeat;
654  }
655
656 =head3 SCALING AND CENTERING
657
658 Scaling slide text and images so that they appear at the same
659 proportionate size for any screen resolution can be done using
660 Javascript.  (See
661 L<https://developer.mozilla.org/En/DOM/window.innerHeight>).
662
663 If you want to center text horizontally, use CSS, eg:
664
665  p.center {
666      text-align: center;
667  }
668
669 To center text vertically, CSS3 is supposed to offer a solution some
670 time, but while you're waiting for that try
671 L<http://www.w3.org/Style/Examples/007/center#vertical>.
672
673 =head3 PREVIEWING HTML
674
675 I find it helpful to have Firefox open to display the HTML files and
676 styles as I edit them.  Just start firefox in the talk directory:
677
678  firefox file://$(pwd) &
679
680 When you edit an HTML file, click the Firefox reload button to
681 immediately see your changes.
682
683 Tech Talk PSE uses Mozilla embedding to display HTML, which uses the
684 same Mozilla engine as Firefox, so what you should see in Firefox
685 should be identical to what Tech Talk PSE displays.
686
687 =head2 CREATING FIGURES
688
689 Use your favorite tool to draw the figure, convert it to an image (in
690 any format that the Mozilla engine can display) and include it using
691 an C<E<lt>imgE<gt>> tag, eg:
692
693  <img src="fig1.gif">
694
695 Suitable tools include: XFig, GnuPlot, GraphViz, and many TeX tools
696 such as PicTex and in particular TikZ.
697
698 =head2 EMBEDDING VIDEOS, ANIMATIONS, ETC.
699
700 Using HTML 5, embedding videos in the browser is easy.  See:
701 L<https://developer.mozilla.org/En/Using_audio_and_video_in_Firefox>
702
703 For animations, you could try L<Haxe|http://haxe.org/> which has a
704 Javascript back-end.  There are many other possibilities.
705
706 If you are B<sure> that the venue will have an internet connection,
707 why not embed a YouTube video.
708
709 =head2 DISPLAYING EXISTING WEB PAGES
710
711 Obviously you could just have an HTML file that contains a redirect to
712 the public web page:
713
714  <meta http-equiv="Refresh" content="0; url=http://www.example.com/">
715
716 However if you want your talk to work offline, then it's better to
717 download the web page in advance, eg. using Firefox's "Save Page As
718 -E<gt> Web Page, complete" feature, into the talk directory, then
719 either rename or make a symbolic link to the slide name:
720
721  ln -s "haXe - Welcome to haXe.html" 0010-haxe-homepage.html
722
723 =head2 TIPS FOR WRITING SHELL SCRIPTS
724
725 Make sure each C<*.sh> file you write is executable, otherwise Tech
726 Talk PSE won't be able to run it.  (The program gives a warning if you
727 forget this).
728
729 A good idea is to start each script by sourcing some common functions.
730 All my scripts start with:
731
732  #!/bin/bash -
733  source functions
734
735 where C<functions> is another file (ignored by Tech Talk PSE) which
736 contains common functions for setting shell history and starting a
737 terminal.
738
739 In C<functions>, I have:
740
741  # -*- shell-script -*-
742  
743  # Place any local environment variables required in 'local'.
744  if [ -f local ]; then source local; fi
745  
746  export PS1="$ "
747  
748  export HISTFILE=$talkdir/history
749  
750  rm -f $HISTFILE
751  touch $HISTFILE
752  
753  add_history ()
754  {
755      echo "$@" >> $HISTFILE
756  }
757  
758  terminal ()
759  {
760      # Make $HISTFILE unwritable so the shell won't update it
761      # when it exits.
762      chmod -w $HISTFILE
763  
764      # Run gnome-terminal.
765      exec \
766          gnome-terminal \
767          --window \
768          --geometry=+100+100 \
769          --hide-menubar \
770          --disable-factory \
771          -e '/bin/bash --norc' \
772          "$@"
773  }
774
775 By initializing the shell history, during your talk you can rapidly
776 recall commands to start parts of the demonstration just by hitting
777 the Up arrow.  A complete shell script from one of my talks would look
778 like this:
779
780  #!/bin/bash -
781  source functions
782  add_history guestfish -i debian.img
783  terminal --title="Examining a Debian guest image in guestfish"
784
785 This is just a starting point for your own scripts.  You may want to
786 use a different terminal, such as xterm, and you may want to adjust
787 terminal fonts.
788
789 =head1 REFERENCE
790
791 =head2 ORDER OF FILES
792
793 Tech Talk PSE displays the slides in the directory in lexicographic
794 order (the same order as C<LANG=C ls -1>).  Only files matching the
795 following regexp are considered:
796
797  ^(\d+)(?:-.*)\.(html|sh)$
798
799 For future compatibility, you should ensure that every slide has a
800 unique numeric part (ie. I<don't> have C<0010-aaa.html> and
801 C<0010-bbb.html>).  This is because in future we want to have the
802 ability to display multiple files side by side.
803
804 Also for future compatibility, I<don't> use file names that have an
805 uppercase letter immediately after the numeric part.  This is because
806 in future we want to allow placement hints using filenames like
807 C<0010L-on-the-left.html> and C<0010R-on-the-right.html>.
808
809 =head2 BASE URL AND CURRENT DIRECTORY
810
811 The base URL is set to the be the directory containing the talk files.
812 Thus you should use relative paths, eg:
813
814  <img src="fig1.gif">
815
816 You can also place assets into subdirectories, because subdirectories
817 are ignored by Tech Talk PSE, eg:
818
819  <img src="images/fig1.gif">
820
821 When running shell scripts, the current directory is also set to be
822 the directory containing the talk files, so the same rules about using
823 relative paths apply there too.
824
825 The environment variable C<$talkdir> is exported to scripts and it
826 contains the absolute path of the directory containing the talk files.
827 When a script is run, the current directory is the same as
828 C<$talkdir>, but if your script changes directory (eg. into a
829 subdirectory containing supporting files) then it can be useful to use
830 C<$talkdir> to refer back to the original directory.
831
832 =head1 WHAT MAKES A GOOD TALK
833
834 I like what Edward Tufte writes, for example his evisceration of
835 PowerPoint use at NASA here:
836 L<http://www.edwardtufte.com/bboard/q-and-a-fetch-msg?msg_id=0001yB>
837
838 However it is sometimes hard to translate his ideas into clear
839 presentations, and not all of that is the fault of the tools.  Here
840 are my thoughts and rules on how to deliver a good talk.
841
842 B<First, most important rule:> Before you start drawing any slides at
843 all, write your talk as a short essay.
844
845 This is the number one mistake that presenters make, and it is partly
846 a tool fault, because PowerPoint, OpenOffice, even Tech Talk PSE, all
847 open up on an initial blank slide, inviting you to write a title and
848 some bullet points.  If you start that way, you will end up using the
849 program as a kind of clumsy outlining tool, and then reading that
850 outline to your audience.  That's boring and a waste of time for you
851 and your audience.  (It would be quicker for them just to download the
852 talk and read it at home).
853
854 B<Secondly:> How long do you want to spend preparing the talk?  A good
855 talk, with a sound essay behind it, well thought out diagrams and
856 figures, and interesting demonstrations, takes many hours to prepare.
857 How many hours?  I would suggest thinking about how many hours of
858 effort your audience are putting in.  Even just 20 people sitting
859 there for half an hour is 10 man-hours of attention, and that is a
860 very small talk, and doesn't include all the extra time and hassle
861 that it took to get them all in one place.
862
863 I don't think you can get away with spending less than two full days
864 preparing a talk, if you want to master the topic and draw up accurate
865 slides.  Steve Jobs is reputed to spend weeks preparing his annual
866 sales talk to the Apple faithful.
867
868 B<Thirdly:> Now that you're going to write your talk as an essay, what
869 should go in the slides?  I would say that you should consider
870 delivering the essay, I<not> the slides, to people who don't make the
871 talk.  An essay can be turned into an article or blog posting, whereas
872 even "read-out-the-bullet-point" slides have a low information
873 density, large size, and end-user compatibility problems (*.pptx
874 anyone?).
875
876 What, then, goes on the slides?  Anything you cannot just say:
877 diagrams, graphs, videos, animations, and of course (only with Tech
878 Talk PSE!) demonstrations.
879
880 B<Lastly:> Once you've got your talk as an essay and slides, practice,
881 practice and practice again.  Deliver the talk to yourself in the
882 mirror, to your colleagues.  Practice going backwards and forwards
883 through the slides, using your actual laptop and the software so you
884 know what to click and what keys to press.  Partly memorize what you
885 are going to say (but use short notes written on paper if you need
886 to).
887
888 =head1 SEE ALSO
889
890 The Cognitive Style of PowerPoint, Tufte, Edward R.
891
892 =head1 AUTHOR
893
894 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
895
896 =head1 COPYRIGHT
897
898 Copyright (C) 2010 Red Hat Inc.
899
900 This program is free software; you can redistribute it and/or modify
901 it under the terms of the GNU General Public License as published by
902 the Free Software Foundation; either version 2 of the License, or
903 (at your option) any later version.
904
905 This program is distributed in the hope that it will be useful,
906 but WITHOUT ANY WARRANTY; without even the implied warranty of
907 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
908 GNU General Public License for more details.
909
910 You should have received a copy of the GNU General Public License
911 along with this program; if not, write to the Free Software
912 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.