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