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