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