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