File pinpoint-speakerview.patch of Package pinpoint

Subject: pinpoint speakerview patch

This is a diff from the upstream git tree v0.1.2 to commit
719837eb3150062bc8d82e466218348cfd467f18 to provide speakerview support
and lots of other fixes that haven't made it into a release yet.

Note, pdf output seems to be broken at the moment.

Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>


diff --git a/.vimrc b/.vimrc
new file mode 100644
index 0000000..899811a
--- /dev/null
+++ b/.vimrc
@@ -0,0 +1,5 @@
+set tabstop=8
+set softtabstop=2
+set shiftwidth=2
+set expandtab
+set cinoptions=>4,n-2,{2,^-2,:0,=2,g0,h2,t0,+2,(0,u0,w1,m1
diff --git a/Makefile.am b/Makefile.am
index d100ac1..41442a7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,8 +1,8 @@
 SUBDIRS=transitions
 bin_PROGRAMS=pinpoint
 
-AM_CFLAGS = $(DEPS_CFLAGS) $(MAINTAINER_FLAGS) -D_GNU_SOURCE -DPKGDATADIR=\"$(pkgdatadir)/\"
-ACLOCAL_AMFLAGS = -I m4
+AM_CFLAGS = $(DEPS_CFLAGS) $(MAINTAINER_CFLAGS) -D_GNU_SOURCE -DPKGDATADIR=\"$(pkgdatadir)/\"
+ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
 
 if USE_DAX
 DAX_SOURCES = pp-super-aa.c pp-super-aa.h
diff --git a/README b/README
index 941d95e..435bba5 100644
--- a/README
+++ b/README
@@ -5,7 +5,7 @@ Pinpoint a simple presentation tool that hopes to avoid audience death by
 bullet point and instead encourage presentations containing beautiful images
 and small amounts of concise text in slides.
 
-Pinpoints main web-presence is a page in the GNOME wiki:
+Pinpoint’s main web presence is a page on the GNOME wiki:
 
  http://live.gnome.org/Pinpoint
 
diff --git a/configure.ac b/configure.ac
index 641b9d7..0ca3092 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
 AC_PREREQ(2.53)
-AC_INIT(pinpoint, [0.1.2], [])
+AC_INIT(pinpoint, [0.1.3], [])
 AC_CONFIG_SRCDIR(pinpoint.c)
 AC_CONFIG_AUX_DIR([build])
 AM_INIT_AUTOMAKE([foreign -Wno-portability no-define])
@@ -13,6 +13,12 @@ AC_HEADER_STDC
 PINPOINT_DEPS="clutter-1.0 >= 1.4 gio-2.0 cairo-pdf pangocairo gdk-pixbuf-2.0"
 
 AS_COMPILER_FLAGS([MAINTAINER_CFLAGS], [-Wall])
+AC_SUBST(MAINTAINER_CFLAGS)
+
+# Do we have clutter compiled for x11?
+PKG_CHECK_EXISTS([clutter-x11-1.0],
+		 [AC_DEFINE([HAVE_CLUTTER_X11], [1],
+			    [Whether clutter is compiled for x11])])
 
 # PDF output support
 AC_ARG_ENABLE([pdf],
diff --git a/gst-video-thumbnailer.c b/gst-video-thumbnailer.c
index fa2fbe2..ee0a340 100644
--- a/gst-video-thumbnailer.c
+++ b/gst-video-thumbnailer.c
@@ -17,6 +17,7 @@
  */
 
 #include <config.h>
+
 #ifdef USE_CLUTTER_GST
 
 #include <string.h>
@@ -60,9 +61,9 @@ convert_buffer_to_pixbuf (GstBuffer    *buffer,
     GstElement *src, *sink, *colorspace, *scale, *filter;
     GstBus *bus;
     GstMessage *msg;
-    GstStateChangeReturn state;
+    GstStateChangeReturn state G_GNUC_UNUSED;
     gboolean ret;
-    int width, height, dw, dh, i;
+    int dw, dh, i;
     GstStructure *s;
 
     s = gst_caps_get_structure (GST_BUFFER_CAPS (buffer), 0);
@@ -259,63 +260,4 @@ gst_video_thumbnailer_get_shot (const gchar  *location,
 
     return shot;
 }
-
-static gboolean
-is_interesting (GdkPixbuf *pixbuf)
-{
-    int width, height, r, rowstride;
-    gboolean has_alpha;
-    guint32 histogram[4][4][4] = {{{0,},},};
-    guchar *pixels;
-    int pxl_count = 0, count, i;
-
-    width = gdk_pixbuf_get_width (pixbuf);
-    height = gdk_pixbuf_get_height (pixbuf);
-    rowstride = gdk_pixbuf_get_rowstride (pixbuf);
-    has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
-
-    pixels = gdk_pixbuf_get_pixels (pixbuf);
-    for (r = 0; r < height; r++) {
-        guchar *row = pixels + (r * rowstride);
-        int c;
-
-        for (c = 0; c < width; c++) {
-            guchar r, g, b;
-
-            r = row[0];
-            g = row[1];
-            b = row[2];
-
-            histogram[r / 64][g / 64][b / 64]++;
-
-            if (has_alpha) {
-                row += 4;
-            } else {
-                row += 3;
-            }
-
-            pxl_count++;
-        }
-    }
-
-    count = 0;
-    for (i = 0; i < 4; i++) {
-        int j;
-        for (j = 0; j < 4; j++) {
-            int k;
-
-            for (k = 0; k < 4; k++) {
-                /* Count how many bins have more than
-                   1% of the pixels in the histogram */
-                if (histogram[i][j][k] > pxl_count / 100) {
-                    count++;
-                }
-            }
-        }
-    }
-
-    /* Image is boring if there is only 1 bin with > 1% of pixels */
-    return count > 1;
-}
-
-#endif
+#endif /* USE_CLUTTER_GST */
diff --git a/introduction.pin b/introduction.pin
old mode 100755
new mode 100644
index a11c49e..bdf04b5
--- a/introduction.pin
+++ b/introduction.pin
@@ -1,18 +1,21 @@
 #!/usr/bin/env pinpoint
-[font=Sans 50px]
-[bg.jpg]
 
-- The bits above this point are defaults for every slide in the presentation
+[bg.jpg]
+[font=Sans 50px]
+[duration=5.000000]
+-- [duration=4.448699]
 Welcome to pinpoint
 
-- lines starting with - are slide separators.
+-- [duration=3.393199]
 A tool for making excellent presentations
 
-- [bowls.jpg] You can override the defaults with with per-slide properties
+-- [bowls.jpg] [duration=4.322308]
 It has a simple text source
-# The source can include witty comment lines
-
-- [font=monospace 18px] [shading-opacity=1.0]
+#The source can include witty comment lines!
+#These comments should fit on screen
+#The comments wrap
+#Using up/down should overstrike the speaker points?
+-- [font=monospace 18px] [shading-opacity=1.000000] [duration=23.560078]
 The current presentation thus far:
  #!/usr/bin/env pinpoint
  [font=Sans 50px]
@@ -30,8 +33,8 @@ The current presentation thus far:
 
  - [font=monospace 18px] [shading-opacity=1.0]
  The current presentation thus far:
-
-- [text-align=center]
+#Should break this into smaller bits/slides
+-- [text-align=center] [duration=12.553503]
 You start by writing down the core concepts that you will be talking about. Then sort and format
 them as a pinpoint presentation in a text editor of your choice. 
 
@@ -42,98 +45,97 @@ Pinpoint reloads the presentation when it changes on disk, very useful when you'
 so keep both pinpoint and your text editor open. And remember, the less text you have, the happier
 your audience will be ;-)
 
-
-- You can add '\n' before and after the slide text to keep the file tidy and slides seperated
+-- [duration=3.136431]
 Let's use some of the more 
 esoteric features of pinpoint
 
-- [bowls.jpg] [text-align=center]
-\- [filename.jpg]
+-- [bowls.jpg] [text-align=center] [duration=5.757168]
+- [filename.jpg]
 set a custom background for an individual slide
 
-- [bunny.ogg] [fill]
+-- [bunny.ogv] [fill] [duration=24.458597]
 You can put <b>anything</b> in the background
 
-- [linus.jpg] [fill] [bottom-left]
+-- [linus.jpg] [fill] [bottom-left] [duration=3.724967]
 But, slides with cat pictures are best
 
--[top-right] [text-align=right]
+-- [text-align=right] [top-right] [duration=1.879800]
 position text in the
 [top-right]
 
-- [right]
+-- [right] [duration=1.031598]
 [right]
 
-- [bottom-right]
+-- [bottom-right] [duration=1.064696]
 [bottom-right]
 
-- [bottom]
+-- [bottom] [duration=1.047646]
 [bottom]
 
-- [bottom-left]
+-- [bottom-left] [duration=1.132064]
 [bottom-left]
 
-- [left]
+-- [left] [duration=1.014935]
 [left]
 
-- [top-left]
+-- [top-left] [duration=1.214729]
 [top-left]
 
-- [top]
+-- [top] [duration=1.747087]
 [top]
 
-- [center] [text-align=center]
+-- [text-align=center] [duration=4.109942]
 and even in the
 [center]
 (which is the default)
 
-- [font=Monospace 100px][white][text-color=black][shading-opacity=0.0][bottom]
+-- [white] [bottom] [font=Monospace 100px] [text-color=black] [shading-opacity=0.000000] [duration=7.491852]
 Make text big!
 [font=Monospace 100px]
 [white][text-color=black]
 [shading-opacity=0.0][bottom]
 
--
+-- [duration=2.912952]
 <s>use</s> <b>pango</b> <i
 ><span foreground='red'>m<u
 >ar</u>k<sup>up</sup></span></i> for style
 
-- [no-markup]
+-- [duration=4.263503] [no-markup]
 [no-markup]
 <s>don't use</s> <b>pango</b> <i
 ><span foreground='red'>m<u
 >ar</u>k<sup>up</sup></span></i>
 
-- [transition=page-curl-both][bowls.jpg]
+-- [bowls.jpg] [transition=page-curl-both] [duration=4.935558]
 need bullets?
  ❥ use the power of unicode
 • ✽ ✩ ✔ ☃ ◦
 
-- [transition=text-slide-up]
+-- [transition=text-slide-up] [duration=6.881204]
 $ pinpoint presentation.txt -o output.pdf
 Export to PDF. Handy.
 
-- [fill]
+-- [fill] [duration=3.903439]
 [fill]
 Fill the slide with your background
 (the default is [fit])
 
-- [transition=swing]
+-- [transition=swing] [duration=2.679214]
 Keybindings:
 
-- [transition=sheet]
+-- [transition=sheet] [duration=2.913154]
 right, down or space - next slide
 
-- [transition=slide-left]
+-- [transition=slide-left] [duration=4.702688]
 left or up - previous slide
 
-- [transition=slide-up]
-F11 - toggle fullscreen
+-- [transition=slide-up] [duration=3.435221]
+F11 - toggle fullscreen!
 
-- [transition=text-slide-down]
+-- [transition=text-slide-down] [duration=2.167284]
 Escape - quit pinpoint
 
-- [transition=slide-in-left] [command=killall xeyes ; xeyes] [fill]
+-- [fill] [transition=slide-in-left] [command=killall xeyes ; xeyes] [duration=16.965691]
 Enter - Run command
 Tab - Edit command
 [command=killall xeyes ; xeyes]
@@ -142,9 +144,9 @@ N.B. run pinpoint with the -m option
 and hidden or auto-hiding panels, to
 allow window management in fullscreen.
 
-- [linus.jpg] [fill] [bottom-right]
+-- [linus.jpg] [fill] [bottom-right] [duration=7.079270]
 That's pretty much everything. 
 Told you it was simple.
 
-- [black] [font=Sans 100px] [transition=spin-text]
+-- [black] [font=Sans 100px] [transition=spin-text]
 FIN
diff --git a/pinpoint.c b/pinpoint.c
index 9e732eb..fa98b9b 100644
--- a/pinpoint.c
+++ b/pinpoint.c
@@ -31,13 +31,20 @@
 
 #include "pinpoint.h"
 
+#ifdef USE_CLUTTER_GST
+#include <clutter-gst/clutter-gst.h>
+#endif
+
+/* Probably time to create a PinPointPresentation type */
+
 GList *pp_slides      = NULL; /* list of slide text */
 GList *pp_slidep      = NULL; /* current slide */
+GFile *pp_basedir     = NULL; /* basedir to resolve relative paths against */
 
 typedef struct
 {
   const char *name;
-  int value;
+  int         value;
 } EnumDescription;
 
 static EnumDescription PPTextAlign_desc[] =
@@ -50,7 +57,8 @@ static EnumDescription PPTextAlign_desc[] =
 
 #define PINPOINT_RENDERER(renderer) ((PinPointRenderer *) renderer)
 
-static PinPointPoint default_point = {
+/* pinpoint defaults */
+static PinPointPoint pin_default_point = {
   .stage_color = "black",
 
   .bg = "NULL",
@@ -64,17 +72,32 @@ static PinPointPoint default_point = {
   .text_align = PP_TEXT_LEFT,
   .use_markup = TRUE,
 
+  .duration = 30,
+
+  .speaker_notes = NULL,
+
   .shading_color = "black",
   .shading_opacity = 0.66,
-  .transition = NULL,
+  .transition = "fade",
 
   .command = NULL,
+
+  .camera_framerate = 0,                    /* auto */
+  .camera_resolution = {0, 0},              /* auto */
+
   .data = NULL,
 };
 
-char      *pp_output_filename;
-gboolean   pp_fullscreen = FALSE;
-gboolean   pp_maximized = FALSE;
+static PinPointPoint default_point;
+
+PinPointPoint *point_defaults = &default_point;
+
+char     *pp_output_filename = NULL;
+gboolean  pp_fullscreen      = FALSE;
+gboolean  pp_maximized       = FALSE;
+gboolean  pp_speakermode     = FALSE;
+gboolean  pp_rehearse        = FALSE;
+char     *pp_camera_device   = NULL;
 
 static GOptionEntry entries[] =
 {
@@ -85,9 +108,15 @@ static GOptionEntry entries[] =
 "                                         [command=] spawned apps.", NULL},
     { "fullscreen", 'f', 0, G_OPTION_ARG_NONE, &pp_fullscreen,
     "Start in fullscreen mode", NULL},
+    { "speakermode", 's', 0, G_OPTION_ARG_NONE, &pp_speakermode,
+    "Show speakermode window", NULL},
+    { "rehearse", 'r', 0, G_OPTION_ARG_NONE, &pp_rehearse,
+    "Rehearse timings", NULL},
     { "output", 'o', 0, G_OPTION_ARG_STRING, &pp_output_filename,
       "Output presentation to FILE\n"
 "                                         (formats supported: pdf)", "FILE" },
+    { "camera", 'c', 0, G_OPTION_ARG_STRING, &pp_camera_device,
+      "Device to use for [camera] background", "DEVICE" },
     { NULL }
 };
 
@@ -95,16 +124,58 @@ PinPointRenderer *pp_clutter_renderer (void);
 #ifdef HAVE_PDF
 PinPointRenderer *pp_cairo_renderer   (void);
 #endif
+static char * pp_serialize (void);
+
+void pp_rehearse_init (void)
+{
+  GList *iter;
+  for (iter = pp_slides; iter; iter=iter->next)
+    {
+      PinPointPoint *point = iter->data;
+      point->new_duration = 0.0;
+    }
+}
+
+
+static char *pinfile = NULL;
+
+static void pp_rehearse_save (void)
+{
+  GError *error = NULL;
+  char *content = pp_serialize ();
+  if (!g_file_set_contents (pinfile, content, -1, &error))
+    {
+      printf ("Failed to save to %s %s\n", pinfile, error->message);
+    }
+  else
+    {
+      printf ("saved to %s\n", pinfile);
+    }
+  g_free (content);
+}
+
+
+void pp_rehearse_done (void)
+{
+  GList *iter;
+  for (iter = pp_slides; iter; iter=iter->next)
+    {
+      PinPointPoint *point = iter->data;
+      point->duration = point->new_duration;
+    }
+  pp_rehearse_save ();
+}
 
 int
 main (int    argc,
       char **argv)
 {
   PinPointRenderer *renderer;
-  GOptionContext *context;
+  GOptionContext   *context;
   GError *error = NULL;
-  char   *text = NULL;
+  char   *text  = NULL;
 
+  memcpy (&default_point, &pin_default_point, sizeof (default_point));
   renderer = pp_clutter_renderer ();
 
   context = g_option_context_new ("- Presentations made easy");
@@ -117,16 +188,20 @@ main (int    argc,
       return EXIT_FAILURE;
     }
 
-  if (!argv[1])
+  pinfile = argv[1];
+
+  if (!pinfile)
     {
       g_print ("usage: %s [options] <presentation>\n", argv[0]);
-      text = g_strdup ("[no-markup][transition=sheet][red]\n--\nusage: pinpoint [options] <presentation.txt>\n");
+      text = g_strdup ("[no-markup][transition=sheet][red]\n"
+                       "--\n"
+                       "usage: pinpoint [options] <presentation.txt>\n");
     }
   else
     {
-      if (!g_file_get_contents (argv[1], &text, NULL, NULL))
+      if (!g_file_get_contents (pinfile, &text, NULL, NULL))
         {
-          g_print ("failed to load presentation from %s\n", argv[1]);
+          g_print ("failed to load presentation from %s\n", pinfile);
           return -1;
         }
     }
@@ -153,15 +228,35 @@ main (int    argc,
 #endif
     }
 
-  renderer->init (renderer, argv[1]);
+  if (!pinfile)
+    pp_rehearse = FALSE;
+
+  if (pinfile)
+    {
+      GFile *file;
+
+      file = g_file_new_for_commandline_arg (pinfile);
+      pp_basedir = g_file_get_parent (file);
+      g_object_unref (file);
+    }
 
+  renderer->init (renderer, pinfile);
   pp_parse_slides (renderer, text);
   g_free (text);
 
+  if (pp_rehearse)
+    {
+      pp_rehearse_init ();
+      printf ("Running in rehearsal mode, press ctrl+C to abort without saving timings back to %s\n", pinfile);
+    }
   renderer->run (renderer);
   renderer->finalize (renderer);
   if (renderer->source)
     g_free (renderer->source);
+#if 0
+  if (pp_rehearse)
+    pp_rehearse_save ();
+#endif
 
   g_list_free (pp_slides);
 
@@ -265,6 +360,7 @@ pp_get_text_position_scale (PinPointPoint *point,
         x = (stage_width - w * sx) / 2;
         break;
     }
+
   switch (point->position)
     {
       case CLUTTER_GRAVITY_SOUTH:
@@ -312,13 +408,22 @@ pp_get_shading_position_size (float stage_width,
 
 void     pp_parse_slides  (PinPointRenderer *renderer,
                            const char       *slide_src);
+
 /*
  * Parsing
  */
 
 static void
+parse_resolution (PPResolution *r,
+                  const gchar  *str)
+{
+  if (sscanf (str, "%dx%d", &r->width, &r->height) != 2)
+    r->width = r->height = 0;
+}
+
+static void
 parse_setting (PinPointPoint *point,
-               const gchar   *setting)
+               const char    *setting)
 {
 /* C Preprocessor macros implemeting a mini language for interpreting
  * pinpoint key=value pairs
@@ -329,9 +434,11 @@ parse_setting (PinPointPoint *point,
 #define END_PARSER   }
 #define IF_PREFIX(prefix) } else if (g_str_has_prefix (setting, prefix)) {
 #define IF_EQUAL(string) } else if (g_str_equal (setting, string)) {
-#define char g_intern_string (strchrnul (setting, '=') + 1)
-#define float g_ascii_strtod (strchrnul (setting, '=') + 1, NULL);
-#define enum(r,t,s) \
+#define STRING  g_intern_string (strchr (setting, '=') + 1)
+#define INT     atoi (strchr (setting, '=') + 1)
+#define FLOAT   g_ascii_strtod (strchr (setting, '=') + 1, NULL)
+#define RESOLUTION(r) parse_resolution (&r, strchr (setting, '=') + 1)
+#define ENUM(r,t,s) \
   do { \
       int _i; \
       EnumDescription *_d = t##_desc; \
@@ -342,14 +449,17 @@ parse_setting (PinPointPoint *point,
   } while (0)
 
   START_PARSER
-  IF_PREFIX("stage-color=") point->stage_color = char;
-  IF_PREFIX("font=")        point->font = char;
-  IF_PREFIX("text-color=")  point->text_color = char;
-  IF_PREFIX("text-align=")  enum(point->text_align, PPTextAlign, char);
-  IF_PREFIX("shading-color=") point->shading_color = char;
-  IF_PREFIX("shading-opacity=") point->shading_opacity = float;
-  IF_PREFIX("command=")    point->command = char;
-  IF_PREFIX("transition=") point->transition = char;
+  IF_PREFIX("stage-color=") point->stage_color = STRING;
+  IF_PREFIX("font=")        point->font = STRING;
+  IF_PREFIX("text-color=")  point->text_color = STRING;
+  IF_PREFIX("text-align=")  ENUM(point->text_align, PPTextAlign, STRING);
+  IF_PREFIX("shading-color=") point->shading_color = STRING;
+  IF_PREFIX("shading-opacity=") point->shading_opacity = FLOAT;
+  IF_PREFIX("duration=")   point->duration = FLOAT;
+  IF_PREFIX("command=")    point->command = STRING;
+  IF_PREFIX("transition=") point->transition = STRING;
+  IF_PREFIX("camera-framerate=")  point->camera_framerate = INT;
+  IF_PREFIX("camera-resolution=") RESOLUTION (point->camera_resolution);
   IF_EQUAL("fill")         point->bg_scale = PP_BG_FILL;
   IF_EQUAL("fit")          point->bg_scale = PP_BG_FIT;
   IF_EQUAL("stretch")      point->bg_scale = PP_BG_STRETCH;
@@ -368,22 +478,24 @@ parse_setting (PinPointPoint *point,
   DEFAULT                  point->bg = g_intern_string (setting);
   END_PARSER
 
-/* undefine all the overrides, returning us to regular C */
+/* undefine the overrides, returning us to regular C */
 #undef START_PARSER
 #undef END_PARSER
 #undef DEFAULT
 #undef IF_PREFIX
 #undef IF_EQUAL
-#undef float
-#undef char
-#undef enum
+#undef FLOAT
+#undef STRING
+#undef INT
+#undef ENUM
+#undef RESOLUTION
 }
 
 static void
 parse_config (PinPointPoint *point,
               const char    *config)
 {
-  GString *str = g_string_new ("");
+  GString    *str = g_string_new ("");
   const char *p;
 
   for (p = config; *p; p++)
@@ -411,6 +523,10 @@ pin_point_free (PinPointRenderer *renderer,
 {
   if (renderer->free_data)
     renderer->free_data (renderer, point->data);
+  if (point->speaker_notes)
+    {
+      g_free (point->speaker_notes);
+    }
   g_free (point);
 }
 
@@ -435,17 +551,184 @@ pp_is_color (const char *string)
   return clutter_color_from_string (&color, string);
 }
 
+static gboolean
+str_has_video_suffix (const char *string)
+{
+  char *video_extensions[] =
+    {".avi", ".ogg", ".ogv", ".mpg",  ".flv", ".mpeg",
+     ".mov", ".mp4", ".wmv", ".webm", ".mkv", NULL};
+  char **ext;
+
+  for (ext = video_extensions; *ext; ext ++)
+    if (g_str_has_suffix (string, *ext))
+      {
+        return TRUE;
+      }
+  return FALSE;
+}
+
+static void serialize_slide_config (GString       *str,
+                                    PinPointPoint *point,
+                                    PinPointPoint *reference,
+                                    const char    *separator)
+{
+#define STRING(v,n) \
+  if (point->v != reference->v) \
+    g_string_append_printf (str, "%s[" n "%s]", separator, point->v)
+#define INT(v,n) \
+  if (point->v != reference->v) \
+    g_string_append_printf (str, "%s[" n "%d]", separator, point->v)
+#define FLOAT(v,n) \
+  if (point->v != reference->v) \
+    g_string_append_printf (str, "%s[" n "%f]", separator, point->v)
+
+  STRING(stage_color, "stage-color=");
+  STRING(bg, "");
+
+  if (point->bg_scale != reference->bg_scale)
+    {
+      g_string_append (str, separator);
+      switch (point->bg_scale)
+        {
+          case PP_BG_FILL:     g_string_append (str, "[fill]");     break;
+          case PP_BG_FIT:      g_string_append (str, "[fit]");      break;
+          case PP_BG_STRETCH:  g_string_append (str, "[stretch]");  break;
+          case PP_BG_UNSCALED: g_string_append (str, "[unscaled]"); break;
+        }
+    }
+
+  if (point->text_align != reference->text_align)
+    {
+      g_string_append (str, separator);
+      switch (point->text_align)
+        {
+          case PP_TEXT_LEFT:  g_string_append (str, "[text-align=left]");break;
+          case PP_TEXT_CENTER:g_string_append (str, "[text-align=center]");break;
+          case PP_TEXT_RIGHT: g_string_append (str, "[text-align=right]");break;
+        }
+    }
+
+  if (point->position != reference->position)
+    {
+      g_string_append (str, separator);
+      switch (point->position)
+        {
+          case CLUTTER_GRAVITY_NONE:
+            break;
+          case CLUTTER_GRAVITY_CENTER:
+            g_string_append (str, "[center]");break;
+          case CLUTTER_GRAVITY_NORTH:
+            g_string_append (str, "[top]");break;
+          case CLUTTER_GRAVITY_SOUTH:
+            g_string_append (str, "[bottom]");break;
+          case CLUTTER_GRAVITY_WEST:
+            g_string_append (str, "[left]");break;
+          case CLUTTER_GRAVITY_EAST:
+            g_string_append (str, "[right]");break;
+          case CLUTTER_GRAVITY_NORTH_WEST:
+            g_string_append (str, "[top-left]");break;
+          case CLUTTER_GRAVITY_NORTH_EAST:
+            g_string_append (str, "[top-right]");break;
+          case CLUTTER_GRAVITY_SOUTH_WEST:
+            g_string_append (str, "[bottom-left]");break;
+          case CLUTTER_GRAVITY_SOUTH_EAST:
+            g_string_append (str, "[bottom-right]");break;
+        }
+    }
+
+  STRING(font,"font=");
+  STRING(text_color,"text-color=");
+  STRING(shading_color,"shading-color=");
+  FLOAT(shading_opacity, "shading-opacity=");
+
+  STRING(transition,"transition=");
+  STRING(command,"command=");
+  if (point->duration != 0.0)
+    FLOAT(duration, "duration="); /* XXX: probably needs special treatment */
+
+  INT(camera_framerate, "camera-framerate=");
+  if (point->camera_resolution.width != reference->camera_resolution.width &&
+      point->camera_resolution.height != reference->camera_resolution.height)
+    {
+        g_string_append_printf (str, "[camera-resolution=%dx%d]",
+                                point->camera_resolution.width,
+                                point->camera_resolution.height);
+    }
+
+  if (point->use_markup != reference->use_markup)
+    {
+      g_string_append (str, separator);
+      if (point->use_markup)
+        g_string_append (str, "[markup]");
+      else
+        g_string_append (str, "[no-markup]");
+    }
+
+#undef FLOAT
+#undef INT
+#undef STRING
+}
+
+
+static void serialize_slide (GString *str,
+                             PinPointPoint *point)
+{
+  g_string_append_c (str, '\n');
+  g_string_append (str, "--");
+  serialize_slide_config (str, point, &default_point, " ");
+  g_string_append (str, "\n");
+
+  g_string_append_printf (str, "%s\n", point->text);
+
+  if (point->speaker_notes)
+    {
+      char *p;
+      g_string_append_c (str, '#');
+      for (p = point->speaker_notes; *p; p++)
+        {
+          if (*p == '\n')
+            {
+              g_string_append_c (str, '\n');
+              if (*(p+1))
+                g_string_append_c (str, '#');
+            }
+          else
+            {
+              g_string_append_c (str, *p);
+            }
+        }
+    }
+}
+
+static char * pp_serialize (void)
+{
+  GString *str = g_string_new ("#!/usr/bin/env pinpoint\n");
+  char *ret;
+  GList *iter;
+
+  serialize_slide_config (str, &default_point, &pin_default_point, "\n");
+
+  for (iter = pp_slides; iter; iter = iter->next)
+    {
+      serialize_slide (str, iter->data);
+    }
+  ret = str->str;
+  g_string_free (str, FALSE);
+  return ret;
+}
+
 void
 pp_parse_slides (PinPointRenderer *renderer,
                  const char       *slide_src)
 {
   const char *p;
-  int         slideno = 0;
-  gboolean    done = FALSE;
+  int         slideno     = 0;
+  gboolean    done        = FALSE;
   gboolean    startofline = TRUE;
-  gboolean    gotconfig = FALSE;
-  GString    *slide_str = g_string_new ("");
+  gboolean    gotconfig   = FALSE;
+  GString    *slide_str   = g_string_new ("");
   GString    *setting_str = g_string_new ("");
+  GString    *notes_str   = g_string_new ("");
   GList      *s;
   PinPointPoint *point, *next_point;
 
@@ -456,7 +739,9 @@ pp_parse_slides (PinPointRenderer *renderer,
       int lineno=0;
       /* compute slide no that has changed */
       for (pos = 0, slideno = 0;
-           slide_src[pos] && renderer->source[pos] && slide_src[pos]==renderer->source[pos]
+           slide_src[pos] &&
+           renderer->source[pos] &&
+           slide_src[pos]==renderer->source[pos]
            ; pos ++)
         {
           switch (slide_src[pos])
@@ -472,7 +757,7 @@ pp_parse_slides (PinPointRenderer *renderer,
                 start_of_line = FALSE;
             }
         }
-      slideno--;
+      slideno-=1;
       g_free (renderer->source);
     }
   renderer->source = g_strdup (slide_src);
@@ -484,127 +769,130 @@ pp_parse_slides (PinPointRenderer *renderer,
   pp_slides = NULL;
   point = pin_point_new (renderer);
 
-  /* parse the slides, constructing lists of objects, adding all generated
-   * actors to the stage
+  /* parse the slides, constructing lists of slide/point objects
    */
   for (p = slide_src; *p; p++)
     {
       switch (*p)
-      {
-        case '\\': /* escape the next char */
-          p++;
-          startofline = FALSE;
-          if (*p)
+        {
+          case '\\': /* escape the next char */
+            p++;
+            startofline = FALSE;
+            if (*p)
+              g_string_append_c (slide_str, *p);
+            break;
+          case '\n':
+            startofline = TRUE;
             g_string_append_c (slide_str, *p);
-          break;
-        case '\n':
-          startofline = TRUE;
-          g_string_append_c (slide_str, *p);
-          break;
-        case '-': /* slide seperator */
-          close_last_slide:
-          if (startofline)
-            {
-              startofline = FALSE;
-              next_point = pin_point_new (renderer);
-
-              g_string_assign (setting_str, "");
-              while (*p && *p!='\n')  /* until newline */
-                {
-                  g_string_append_c (setting_str, *p);
-                  p++;
-                }
-              parse_config (next_point, setting_str->str);
+            break;
+          case '-': /* slide seperator */
+            close_last_slide:
+            if (startofline)
+              {
+                startofline = FALSE;
+                next_point = pin_point_new (renderer);
 
-              if (!gotconfig)
-                {
-                  parse_config (&default_point, slide_str->str);
-                  /* copy the default point except the per-slide allocated
-                   * data (void *) */
-                  memcpy (point, &default_point,
-                          sizeof (PinPointPoint) - sizeof (void *));
-                  parse_config (point, setting_str->str);
-                  gotconfig = TRUE;
-                  g_string_assign (slide_str, "");
-                  g_string_assign (setting_str, "");
-                }
-              else
-                {
-                  if (point->bg && point->bg[0])
-                    {
-                      gchar *filename = g_strdup (point->bg);
-                      int i = 0;
-
-                      while (filename[i])
-                        {
-                          filename[i] = tolower(filename[i]);
-                          i++;
-                        }
-                      if (g_str_has_suffix (filename, ".avi")
-                       || g_str_has_suffix (filename, ".ogg")
-                       || g_str_has_suffix (filename, ".ogv")
-                       || g_str_has_suffix (filename, ".mpg")
-                       || g_str_has_suffix (filename, ".flv")
-                       || g_str_has_suffix (filename, ".mpeg")
-                       || g_str_has_suffix (filename, ".mov")
-                       || g_str_has_suffix (filename, ".mp4")
-                       || g_str_has_suffix (filename, ".wmv")
-                       || g_str_has_suffix (filename, ".webm")
-                       || g_str_has_suffix (filename, ".mkv"))
-                        point->bg_type = PP_BG_VIDEO;
-                      else if (g_str_has_suffix (filename, ".svg"))
-                        point->bg_type = PP_BG_SVG;
-                      else if (pp_is_color (point->bg))
-                        point->bg_type = PP_BG_COLOR;
-                      else
-                        point->bg_type = PP_BG_IMAGE;
-                      g_free (filename);
-                    }
+                g_string_assign (setting_str, "");
+                while (*p && *p!='\n')  /* until newline */
+                  {
+                    g_string_append_c (setting_str, *p);
+                    p++;
+                  }
+                parse_config (next_point, setting_str->str);
 
+                if (!gotconfig)
+                  {
+                    parse_config (&default_point, slide_str->str);
+                    /* copy the default point except the per-slide allocated
+                     * data (void *) */
+                    memcpy (point, &default_point,
+                            sizeof (PinPointPoint) - sizeof (void *));
+                    parse_config (point, setting_str->str);
+                    gotconfig = TRUE;
+                    g_string_assign (slide_str, "");
+                    g_string_assign (setting_str, "");
+                    g_string_assign (notes_str, "");
+                  }
+                else
                   {
-                    char *str = slide_str->str;
+                    if (point->bg && point->bg[0])
+                      {
+                        char *filename = g_strdup (point->bg);
+                        int i = 0;
+
+                        while (filename[i])
+                          {
+                            filename[i] = tolower(filename[i]);
+                            i++;
+                          }
+
+                        if (strcmp (filename, "camera") == 0)
+                          point->bg_type = PP_BG_CAMERA;
+                        else if (str_has_video_suffix (filename))
+                          point->bg_type = PP_BG_VIDEO;
+                        else if (g_str_has_suffix (filename, ".svg"))
+                          point->bg_type = PP_BG_SVG;
+                        else if (pp_is_color (point->bg))
+                          point->bg_type = PP_BG_COLOR;
+                        else
+                          point->bg_type = PP_BG_IMAGE;
+                        g_free (filename);
+                      }
 
-                  /* trim newlines from start and end. ' ' can be used in the
-                   * insane case that you actually want blank lines before or after
-                   * the text of a slide */
-                    while (*str == '\n') str++;
-                    while ( slide_str->str[strlen(slide_str->str)-1]=='\n')
-                      slide_str->str[strlen(slide_str->str)-1]='\0';
+                    {
+                      char *str = slide_str->str;
 
-                    point->text = g_intern_string (str);
-                  }
+                    /* trim newlines from start and end. ' ' can be used in the
+                     * insane case that you actually want blank lines before or
+                     * after the text of a slide */
+                      while (*str == '\n') str++;
+                      while ( slide_str->str[strlen(slide_str->str)-1]=='\n')
+                        slide_str->str[strlen(slide_str->str)-1]='\0';
 
-                  renderer->make_point (renderer, point);
+                      point->text = g_intern_string (str);
+                    }
+                    if (notes_str->str[0])
+                      point->speaker_notes = g_strdup (notes_str->str);
 
-                  g_string_assign (slide_str, "");
-                  g_string_assign (setting_str, "");
+                    renderer->make_point (renderer, point);
 
-                  pp_slides = g_list_append (pp_slides, point);
-                  point = next_point;
-                }
-            }
-          else
-            {
-              g_string_append_c (slide_str, *p);
-            }
-          break;
-      case '#': /* comment */
-        if (startofline)
-          {
-            char *end = strchr (p, '\n');
-            if (end)
+                    g_string_assign (slide_str, "");
+                    g_string_assign (setting_str, "");
+                    g_string_assign (notes_str, "");
+
+                    pp_slides = g_list_append (pp_slides, point);
+                    point = next_point;
+                  }
+              }
+            else
               {
-                p = end;
-                break;
+                g_string_append_c (slide_str, *p);
               }
-          }
-        /* flow through */
-        default:
-          startofline = FALSE;
-          g_string_append_c (slide_str, *p);
-          break;
-      }
+            break;
+        case '#': /* comment */
+          if (startofline)
+            {
+              const char *end = p + 1;
+              while (*end != '\n' && *end != '\0')
+                {
+                  g_string_append_c (notes_str, *end);
+                  end++;
+                }
+              if (end)
+                {
+                  g_string_append_c (notes_str, '\n');
+                  p = end;
+                  break;
+                }
+            }
+          /* flow through */
+          default:
+            startofline = FALSE;
+            g_string_append_c (slide_str, *p);
+            break;
+        }
     }
+
   if (!done)
     {
       done = TRUE;
@@ -613,6 +901,7 @@ pp_parse_slides (PinPointRenderer *renderer,
 
   g_string_free (slide_str, TRUE);
   g_string_free (setting_str, TRUE);
+  g_string_free (notes_str, TRUE);
 
   if (g_list_nth (pp_slides, slideno))
     pp_slidep = g_list_nth (pp_slides, slideno);
diff --git a/pinpoint.h b/pinpoint.h
index cb02d39..3f26630 100644
--- a/pinpoint.h
+++ b/pinpoint.h
@@ -35,9 +35,9 @@ typedef struct _PinPointRenderer PinPointRenderer;
 
 typedef enum
 {
-  PP_TEXT_LEFT = PANGO_ALIGN_LEFT,
+  PP_TEXT_LEFT   = PANGO_ALIGN_LEFT,
   PP_TEXT_CENTER = PANGO_ALIGN_CENTER,
-  PP_TEXT_RIGHT = PANGO_ALIGN_RIGHT
+  PP_TEXT_RIGHT  = PANGO_ALIGN_RIGHT
 } PPTextAlign;
 
 typedef enum
@@ -46,6 +46,7 @@ typedef enum
   PP_BG_COLOR,
   PP_BG_IMAGE,
   PP_BG_VIDEO,
+  PP_BG_CAMERA,
   PP_BG_SVG
 } PPBackgroundType;
 
@@ -57,6 +58,11 @@ typedef enum
   PP_BG_STRETCH
 } PPBackgroundScale;
 
+typedef struct
+{
+  gint width, height;
+} PPResolution;
+
 #define PINPOINT_RENDERER(renderer) ((PinPointRenderer *) renderer)
 
 struct _PinPointRenderer
@@ -88,21 +94,37 @@ struct _PinPointPoint
   const char        *text_color;
   gboolean           use_markup;
 
+  gfloat             duration;       /* on slide 0 the duration is the duration of the
+                                        presentation, other slides have a representation
+                                        of their portion of the presentation  */
+
+  gfloat             new_duration;  /* new duration,. used during reharsal */
+
+  char              *speaker_notes; /* speaker comments for the slide */
+
   const char        *shading_color;
   float              shading_opacity;
   const char        *transition;      /* transition template to use, if any */
 
   const char        *command;
 
+  gint              camera_framerate;
+  PPResolution      camera_resolution;
+
   void              *data;            /* the renderer can attach data here */
 };
 
-extern char      *pp_output_filename;
-extern gboolean   pp_fullscreen;
-extern gboolean   pp_maximized;
+extern char     *pp_output_filename;
+extern gboolean  pp_fullscreen;
+extern gboolean  pp_maximized;
+extern gboolean  pp_speakermode;
+extern gboolean  pp_rehearse;
+extern char     *pp_camera_device;
 
-extern GList *pp_slides;
-extern GList *pp_slidep;
+extern GList         *pp_slides;  /* list of slide text */
+extern GList         *pp_slidep;  /* current slide */
+extern GFile         *pp_basedir;
+extern PinPointPoint *point_defaults;
 
 void     pp_parse_slides  (PinPointRenderer *renderer,
                            const char       *slide_src);
@@ -112,6 +134,9 @@ pp_get_padding (float  stage_width,
                 float  stage_height,
                 float *padding);
 
+void pp_rehearse_init (void);
+void pp_rehearse_done (void);
+
 void
 pp_get_background_position_scale (PinPointPoint *point,
                                   float          stage_width,
diff --git a/pp-cairo.c b/pp-cairo.c
index 3c91094..a673674 100644
--- a/pp-cairo.c
+++ b/pp-cairo.c
@@ -41,17 +41,19 @@
 typedef struct _CairoRenderer
 {
   PinPointRenderer renderer;
-  gchar      *path;
-  GHashTable *surfaces;         /* keep cairo_surface_t around for source
-                                   images as we wantt to only include one
+  char            *path;
+  GHashTable      *surfaces;    /* keep cairo_surface_t around for source
+                                   images as we want to only include one
                                    instance of the image when using it in
                                    several slides */
-  GHashTable *svgs;             /* keep RsvgHandles around for source
+  GHashTable      *svgs;        /* keep RsvgHandles around for source
                                    svg backgrounds as we want to only
                                    include one instance of the image
                                    when using it in several slides */
-  cairo_surface_t  *surface;
-  cairo_t *ctx;
+  cairo_surface_t *surface;
+  cairo_t         *ctx;
+  double           width;
+  double           height;
 } CairoRenderer;
 
 typedef struct
@@ -76,8 +78,10 @@ cairo_renderer_init (PinPointRenderer *pp_renderer,
   CairoRenderer *renderer = CAIRO_RENDERER (pp_renderer);
 
   /* A4, landscape */
+  renderer->width = A4_LS_WIDTH;
+  renderer->height = A4_LS_HEIGHT;
   renderer->surface = cairo_pdf_surface_create (pp_output_filename,
-                                                A4_LS_WIDTH, A4_LS_HEIGHT);
+                                                renderer->width, renderer->height);
   renderer->path = g_strdup (pinpoint_file);
 
   renderer->ctx = cairo_create (renderer->surface);
@@ -95,17 +99,18 @@ cairo_renderer_init (PinPointRenderer *pp_renderer,
 static cairo_surface_t *
 _cairo_new_surface_from_pixbuf (const GdkPixbuf *pixbuf)
 {
-  gint width = gdk_pixbuf_get_width (pixbuf);
-  gint height = gdk_pixbuf_get_height (pixbuf);
-  guchar *gdk_pixels = gdk_pixbuf_get_pixels (pixbuf);
-  int gdk_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
-  int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
-  int cairo_stride;
-  guchar *cairo_pixels;
-  cairo_format_t format;
+  int              width         = gdk_pixbuf_get_width (pixbuf);
+  int              height        = gdk_pixbuf_get_height (pixbuf);
+  guchar          *gdk_pixels    = gdk_pixbuf_get_pixels (pixbuf);
+  int              gdk_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+  int              n_channels    = gdk_pixbuf_get_n_channels (pixbuf);
+  int              cairo_stride;
+  guchar          *cairo_pixels;
+
+  cairo_format_t   format;
   cairo_surface_t *surface;
-  static const cairo_user_data_key_t key;
-  int j;
+  static const     cairo_user_data_key_t key;
+  int              j;
 
   if (n_channels == 3)
     format = CAIRO_FORMAT_RGB24;
@@ -127,56 +132,55 @@ _cairo_new_surface_from_pixbuf (const GdkPixbuf *pixbuf)
       guchar *q = cairo_pixels;
 
       if (n_channels == 3)
-	{
-	  guchar *end = p + 3 * width;
+	      {
+          guchar *end = p + 3 * width;
 
-	  while (p < end)
-	    {
+          while (p < end)
+            {
 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
-	      q[0] = p[2];
-	      q[1] = p[1];
-	      q[2] = p[0];
+              q[0] = p[2];
+              q[1] = p[1];
+              q[2] = p[0];
 #else
-	      q[1] = p[0];
-	      q[2] = p[1];
-	      q[3] = p[2];
+              q[1] = p[0];
+              q[2] = p[1];
+              q[3] = p[2];
 #endif
-	      p += 3;
-	      q += 4;
-	    }
-	}
+              p += 3;
+              q += 4;
+            }
+	      }
       else
-	{
-	  guchar *end = p + 4 * width;
-	  guint t1,t2,t3;
+        {
+          guchar *end = p + 4 * width;
+          guint t1,t2,t3;
 
 #define MULT(d,c,a,t) G_STMT_START { t = c * a + 0x7f; d = ((t >> 8) + t) >> 8; } G_STMT_END
 
-	  while (p < end)
-	    {
+          while (p < end)
+            {
 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
-	      MULT(q[0], p[2], p[3], t1);
-	      MULT(q[1], p[1], p[3], t2);
-	      MULT(q[2], p[0], p[3], t3);
-	      q[3] = p[3];
+              MULT(q[0], p[2], p[3], t1);
+              MULT(q[1], p[1], p[3], t2);
+              MULT(q[2], p[0], p[3], t3);
+              q[3] = p[3];
 #else
-	      q[0] = p[3];
-	      MULT(q[1], p[0], p[3], t1);
-	      MULT(q[2], p[1], p[3], t2);
-	      MULT(q[3], p[2], p[3], t3);
+              q[0] = p[3];
+              MULT(q[1], p[0], p[3], t1);
+              MULT(q[2], p[1], p[3], t2);
+              MULT(q[3], p[2], p[3], t3);
 #endif
 
-	      p += 4;
-	      q += 4;
-	    }
+              p += 4;
+              q += 4;
+            }
 
 #undef MULT
-	}
+        }
 
       gdk_pixels += gdk_rowstride;
       cairo_pixels += cairo_stride;
     }
-
   return surface;
 }
 
@@ -208,8 +212,8 @@ _cairo_get_surface (CairoRenderer *renderer,
                     const char    *file)
 {
   cairo_surface_t *surface;
-  GdkPixbuf *pixbuf;
-  GError *error = NULL;
+  GdkPixbuf       *pixbuf;
+  GError          *error = NULL;
 
   surface = g_hash_table_lookup (renderer->surfaces, file);
   if (surface)
@@ -234,7 +238,7 @@ _cairo_get_surface (CairoRenderer *renderer,
   if (g_str_has_suffix (file, ".jpg") || g_str_has_suffix (file, ".jpeg"))
       {
         unsigned char *data = NULL;
-        guint len;
+        guint len = 0;
 
         _cairo_read_file (file, &data, &len);
         cairo_surface_set_mime_data (surface, CAIRO_MIME_TYPE_JPEG,
@@ -252,7 +256,7 @@ _cairo_get_svg (CairoRenderer *renderer,
                 const char    *file)
 {
   RsvgHandle *svg;
-  GError *error = NULL;
+  GError     *error = NULL;
 
   svg = g_hash_table_lookup (renderer->svgs, file);
   if (svg)
@@ -281,12 +285,21 @@ static void
 _cairo_render_background (CairoRenderer *renderer,
                           PinPointPoint *point)
 {
-  gchar *full_path = NULL;
-  const gchar *file = point->bg;
+  char       *full_path = NULL;
+  const char *file;
+
+  if (point == NULL)
+    {
+      cairo_set_source_rgb (renderer->ctx, 0,0,0);
+      cairo_paint (renderer->ctx);
+      return;
+    }
+
+  file = point->bg;
 
   if (point->bg_type != PP_BG_COLOR && renderer->path && file)
     {
-      gchar *dir = g_path_get_dirname (renderer->path);
+      char *dir = g_path_get_dirname (renderer->path);
       full_path = g_build_filename (dir, file, NULL);
       g_free (dir);
 
@@ -335,7 +348,7 @@ _cairo_render_background (CairoRenderer *renderer,
         bg_height = cairo_image_surface_get_height (surface);
 
         pp_get_background_position_scale (point,
-                                          A4_LS_WIDTH, A4_LS_HEIGHT,
+                                          renderer->width, renderer->height,
                                           bg_width, bg_height,
                                           &bg_x, &bg_y,
                                           &bg_scale_x, &bg_scale_y);
@@ -351,14 +364,24 @@ _cairo_render_background (CairoRenderer *renderer,
     case PP_BG_VIDEO:
       {
 #ifdef USE_CLUTTER_GST
-        GdkPixbuf *pixbuf;
+        GdkPixbuf       *pixbuf;
         cairo_surface_t *surface;
         float bg_x, bg_y, bg_width, bg_height, bg_scale_x, bg_scale_y;
         GCancellable* cancellable = g_cancellable_new ();
+        GFile *abs_file;
+        gchar *abs_path;
 
-        pixbuf = gst_video_thumbnailer_get_shot (file, cancellable);
+        abs_file = g_file_resolve_relative_path (pp_basedir, point->bg);
+        abs_path = g_file_get_path (abs_file);
+        g_object_unref (abs_file);
+
+        pixbuf = gst_video_thumbnailer_get_shot (abs_path, cancellable);
+        g_free (abs_path);
         if (pixbuf == NULL)
-          break;
+          {
+            g_warning ("Could not create video thumbmail for %s", point->bg);
+            break;
+          }
 
         surface = _cairo_new_surface_from_pixbuf (pixbuf);
         g_hash_table_insert (renderer->surfaces, g_strdup (file), surface);
@@ -367,7 +390,7 @@ _cairo_render_background (CairoRenderer *renderer,
         bg_height = cairo_image_surface_get_height (surface);
 
         pp_get_background_position_scale (point,
-                                          A4_LS_WIDTH, A4_LS_HEIGHT,
+                                          renderer->width, A4_LS_HEIGHT,
                                           bg_width, bg_height,
                                           &bg_x, &bg_y,
                                           &bg_scale_x, &bg_scale_y);
@@ -394,7 +417,7 @@ _cairo_render_background (CairoRenderer *renderer,
         rsvg_handle_get_dimensions (svg, &dim);
 
         pp_get_background_position_scale (point,
-                                          A4_LS_WIDTH, A4_LS_HEIGHT,
+                                          renderer->width, renderer->height,
                                           dim.width, dim.height,
                                           &bg_x, &bg_y,
                                           &bg_scale_x, &bg_scale_y);
@@ -408,6 +431,9 @@ _cairo_render_background (CairoRenderer *renderer,
       }
 #endif
       break;
+    case PP_BG_CAMERA:
+      /* silently ignore camera backgrounds */
+      break;
     default:
       g_assert_not_reached();
     }
@@ -419,13 +445,16 @@ static void
 _cairo_render_text (CairoRenderer *renderer,
                     PinPointPoint *point)
 {
-  PangoLayout *layout;
+  PangoLayout          *layout;
   PangoFontDescription *desc;
-  PangoRectangle logical_rect = { 0, };
-  ClutterColor text_color, shading_color;
+  PangoRectangle        logical_rect = { 0, };
+  ClutterColor          text_color,
+                        shading_color;
 
-  float text_width, text_height, text_x, text_y, text_scale;
+  float text_x,    text_y,    text_width,    text_height,   text_scale;
   float shading_x, shading_y, shading_width, shading_height;
+  if (point == NULL)
+    return;
 
   layout = pango_cairo_create_layout (renderer->ctx);
   desc = pango_font_description_from_string (point->font);
@@ -443,12 +472,12 @@ _cairo_render_text (CairoRenderer *renderer,
     goto out;
 
   pp_get_text_position_scale (point,
-                              A4_LS_WIDTH, A4_LS_HEIGHT,
+                              renderer->width, renderer->height,
                               text_width, text_height,
                               &text_x, &text_y,
                               &text_scale);
 
-  pp_get_shading_position_size (A4_LS_HEIGHT, A4_LS_WIDTH,
+  pp_get_shading_position_size (renderer->height, renderer->width, /* XXX: is this right order?? */
                                 text_x, text_y,
                                 text_width, text_height,
                                 text_scale,
@@ -483,9 +512,9 @@ out:
   g_object_unref (layout);
 }
 
-static void
-_cairo_render_page (CairoRenderer *renderer,
-                    PinPointPoint *point)
+void
+cairo_renderer_render_page (CairoRenderer *renderer,
+                            PinPointPoint *point)
 {
   _cairo_render_background (renderer, point);
   _cairo_render_text (renderer, point);
@@ -496,10 +525,10 @@ static void
 cairo_renderer_run (PinPointRenderer *pp_renderer)
 {
   CairoRenderer *renderer = CAIRO_RENDERER (pp_renderer);
-  GList *cur;
+  GList         *cur;
 
   for (cur = pp_slides; cur; cur = g_list_next (cur))
-    _cairo_render_page (renderer, cur->data);
+    cairo_renderer_render_page (renderer, cur->data);
 }
 
 static void
@@ -508,12 +537,15 @@ cairo_renderer_finalize (PinPointRenderer *pp_renderer)
   CairoRenderer *renderer = CAIRO_RENDERER (pp_renderer);
 
   g_free (renderer->path);
-  cairo_surface_destroy (renderer->surface);
+  if (renderer->surface)
+    cairo_surface_destroy (renderer->surface);
   g_hash_table_unref (renderer->surfaces);
   g_hash_table_unref (renderer->svgs);
-  cairo_destroy (renderer->ctx);
+  if (renderer->ctx)
+    cairo_destroy (renderer->ctx);
 }
 
+
 static gboolean
 cairo_renderer_make_point (PinPointRenderer *pp_renderer,
                            PinPointPoint    *point)
@@ -524,12 +556,41 @@ cairo_renderer_make_point (PinPointRenderer *pp_renderer,
     {
       ClutterColor color;
 
-      ret =  clutter_color_from_string (&color, point->bg);
+      ret = clutter_color_from_string (&color, point->bg); /* this roughly checks that the color is valid? */
     }
 
   return ret;
 }
 
+void
+cairo_renderer_unset_cr (PinPointRenderer *pp_renderer)
+{
+  CairoRenderer *renderer = CAIRO_RENDERER (pp_renderer);
+  renderer->ctx = NULL;
+}
+
+void
+cairo_renderer_set_cr (PinPointRenderer *pp_renderer,
+                       cairo_t          *ctx,
+                       float             width,
+                       float             height)
+{
+  CairoRenderer *renderer = CAIRO_RENDERER (pp_renderer);
+  if (renderer->ctx)
+    {
+      if (renderer->surface)
+        {
+          cairo_surface_destroy (renderer->surface);
+          renderer->surface = NULL;
+        }
+      cairo_destroy (renderer->ctx);
+      renderer->ctx = NULL;
+    }
+  renderer->ctx = ctx;
+  renderer->width = width;
+  renderer->height = height;
+}
+
 static void *
 cairo_renderer_allocate_data (PinPointRenderer *renderer)
 {
diff --git a/pp-clutter.c b/pp-clutter.c
index 7051d96..08ed901 100644
--- a/pp-clutter.c
+++ b/pp-clutter.c
@@ -21,8 +21,15 @@
  *             Emmanuele Bassi <ebassi@linux.intel.com>
  */
 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
 #include "pinpoint.h"
+
+#if HAVE_CLUTTER_X11
 #include <clutter/x11/clutter-x11.h>
+#endif
 #include <gio/gio.h>
 #ifdef USE_CLUTTER_GST
 #include <clutter-gst/clutter-gst.h>
@@ -33,6 +40,17 @@
 #endif
 #include <stdlib.h>
 
+
+void cairo_renderer_unset_cr (PinPointRenderer *pp_renderer);
+
+void cairo_renderer_set_cr (PinPointRenderer *pp_renderer,
+                            cairo_t          *ctx,
+                            float             width,
+                            float             height);
+
+void cairo_renderer_render_page (void          *renderer,
+                                 PinPointPoint *point);
+
 /* #define QUICK_ACCESS_LEFT - uncomment to move speed access from top to left,
  *                             useful on meego netbook
  */
@@ -41,13 +59,37 @@
 #define RESTX        4600.0
 #define STARTPOS    -3000.0
 
+/* The maximum of these is used, either 80% of the slide time
+   or 20seconds left of the slide
+ */
+#define SLIDE_WARN_TIME          2.0
+#define SLIDE_WARN_THRESHOLD     0.33
+#define OPACITY_OK               0
+#define OPACITY_PAST_THRESHOLD   96
+#define OPACITY_OVER_TIME        140
+
+#define PREVIEW_WIDTH  640
+#define PREVIEW_HEIGHT 480
+
+static ClutterColor c_prog_bg =    {0x11,0x11,0x11,0xff};
+static ClutterColor c_prog_slide = {0xff,0xff,0xff,0x77};
+static ClutterColor c_prog_time =  {0xff,0xff,0xff,0x55};
+
 static ClutterColor black = {0x00,0x00,0x00,0xff};
+static ClutterColor white = {0xff,0xff,0xff,0xff};
+static ClutterColor red   = {0xff,0x00,0x00,0xff};
+
+#ifdef HAVE_PDF
+PinPointRenderer *pp_cairo_renderer   (void);
+#endif
 
 typedef struct _ClutterRenderer
 {
   PinPointRenderer renderer;
   GHashTable      *bg_cache;    /* only load the same backgrounds once */
   ClutterActor    *stage;
+  ClutterActor    *root;
+
   ClutterActor    *background;
   ClutterActor    *midground;
   ClutterActor    *shading;
@@ -55,10 +97,48 @@ typedef struct _ClutterRenderer
 
   ClutterActor    *json_layer;
 
-  ClutterActor    *commandline; 
+  ClutterActor    *commandline;
   ClutterActor    *commandline_shading;
+
+  GTimer          *timer;
+  gboolean         timer_paused;
+  int              total_seconds;
+  gboolean         autoadvance;
+
+  gboolean         speaker_mode;
+  ClutterActor    *speaker_screen;
+
+  gdouble          slide_start_time;
+
+  ClutterActor    *speaker_buttons_group;
+  ClutterActor    *speaker_speakerscreen;
+  ClutterActor    *speaker_rehearse;
+  ClutterActor    *speaker_autoadvance;
+  ClutterActor    *speaker_start;
+  ClutterActor    *speaker_pause;
+  ClutterActor    *speaker_fullscreen;
+
+  ClutterActor    *speaker_notes;
+  ClutterActor    *speaker_prev;
+  ClutterActor    *speaker_current;
+  ClutterActor    *speaker_next;
+
+  ClutterActor    *speaker_slide_prog_warning;
+
+  ClutterActor    *speaker_prog_bg;
+  ClutterActor    *speaker_prog_slide;
+  ClutterActor    *speaker_prog_time;
+
+  ClutterActor    *speaker_time_remaining;
+
   char *path;               /* path of the file of the GFileMonitor callback */
   float rest_y;             /* where the text can rest */
+
+  gboolean reset;           /* tells the speaker screen update function to
+                               reset all state
+                             */
+
+  PinPointRenderer *cairo_renderer;
 } ClutterRenderer;
 
 typedef struct
@@ -75,6 +155,10 @@ typedef struct
   ClutterActor     *midground;
   ClutterActor     *foreground;
   ClutterActor     *shading;
+
+#ifdef USE_CLUTTER_GST
+  GstElement       *pipeline; /* used for the custom camera pipeline */
+#endif
 } ClutterPointData;
 
 #define CLUTTER_RENDERER(renderer)  ((ClutterRenderer *) renderer)
@@ -94,16 +178,46 @@ static void     file_changed  (GFileMonitor     *monitor,
 static void     stage_resized (ClutterActor     *actor,
                                GParamSpec       *pspec,
                                ClutterRenderer  *renderer);
-static gboolean key_pressed   (ClutterActor     *actor,
-                               ClutterEvent     *event,
-                               ClutterRenderer  *renderer);
+static gboolean key_pressed (ClutterActor    *actor,
+                             ClutterEvent    *event,
+                             ClutterRenderer *renderer);
+
+static void
+pp_clutter_render_adjust_background (ClutterRenderer *renderer,
+                                     PinPointPoint   *point)
+{
+  float bg_x, bg_y, bg_width, bg_height, bg_scale_x, bg_scale_y;
+  ClutterPointData *data = point->data;
+
+  if (!data)
+    return;
+
+  if (CLUTTER_IS_RECTANGLE (data->background))
+    {
+      clutter_actor_get_size (renderer->stage, &bg_width, &bg_height);
+      clutter_actor_set_size (data->background, bg_width, bg_height);
+    }
+  else
+    {
+      clutter_actor_get_size (data->background, &bg_width, &bg_height);
+    }
+
+  pp_get_background_position_scale (point,
+                                    clutter_actor_get_width (renderer->stage),
+                                    clutter_actor_get_height (renderer->stage),
+                                    bg_width, bg_height,
+                                    &bg_x, &bg_y, &bg_scale_x, &bg_scale_y);
 
+  clutter_actor_set_scale (data->background, bg_scale_x, bg_scale_y);
+  clutter_actor_set_position (data->background, bg_x, bg_y);
+}
 
+#ifdef HAVE_CLUTTER_X11
 static void pp_set_fullscreen (ClutterStage  *stage,
                                gboolean       fullscreen)
 {
   static gboolean is_fullscreen = FALSE;
-  static gfloat old_width=640, old_height=480;
+  static float old_width=640, old_height=480;
 
   struct {
     unsigned long flags;
@@ -129,7 +243,9 @@ static void pp_set_fullscreen (ClutterStage  *stage,
   if (fullscreen)
     {
       int full_width = DisplayWidth (xdisplay, xscreen);
-      int full_height = DisplayHeight (xdisplay, xscreen)+5; /* avoid being detected as fullscreen */
+      int full_height = DisplayHeight (xdisplay, xscreen)+5;
+        /* avoid being detected as fullscreen, workaround for some
+           windowmanagers  */
       clutter_actor_get_size (CLUTTER_ACTOR (stage), &old_width, &old_height);
 
       if (wm_hints != None)
@@ -158,6 +274,21 @@ static gboolean pp_get_fullscreen (ClutterStage *stage)
     return clutter_stage_get_fullscreen (stage);
   return pp_fullscreen;
 }
+#else
+
+static void
+pp_set_fullscreen (ClutterStage  *stage,
+                   gboolean       fullscreen)
+{
+  return clutter_stage_set_fullscreen (stage, fullscreen);
+}
+
+static gboolean
+pp_get_fullscreen (ClutterStage *stage)
+{
+  return clutter_stage_get_fullscreen (stage);
+}
+#endif
 
 static void
 _destroy_surface (gpointer data)
@@ -167,7 +298,7 @@ _destroy_surface (gpointer data)
    */
 }
 
-static uint hide_cursor = 0;
+static guint hide_cursor = 0;
 static gboolean hide_cursor_cb (gpointer stage)
 {
   hide_cursor = 0;
@@ -181,23 +312,27 @@ static void
 activate_commandline (ClutterRenderer *renderer)
 {
   PinPointPoint *point;
-  ClutterPointData *data;
 
   if (!pp_slidep)
     return;
- 
+
   point = pp_slidep->data;
-  data = point->data;
 
   clutter_actor_animate (renderer->commandline,
                          CLUTTER_LINEAR, 500,
-                         "opacity", 0xff, NULL);
+                         "opacity",      0xff,
+                         NULL);
+
   clutter_actor_animate (renderer->commandline_shading,
                          CLUTTER_LINEAR, 100,
-                         "opacity", (int)(point->shading_opacity*0xff*0.33), NULL);
+                         "opacity",     (int)(point->shading_opacity*0xff*0.33),
+                         NULL);
 
-  g_object_set (renderer->commandline, "editable", TRUE,
-                "single-line-mode", TRUE, "activatable", TRUE, NULL);
+  g_object_set (renderer->commandline,
+                "editable", TRUE,
+                "single-line-mode", TRUE,
+                "activatable", TRUE,
+                NULL);
   clutter_actor_grab_key_focus (renderer->commandline);
 }
 
@@ -250,7 +385,8 @@ static void commandline_notify_cb (ClutterActor *actor,
                                    gpointer      data)
 {
   ClutterRenderer *renderer = CLUTTER_RENDERER (data);
-  gfloat scale;
+  float            scale;
+
   scale = clutter_actor_get_width (renderer->stage) /
           (clutter_actor_get_width (actor) / 0.9);
   if (scale > 1.0)
@@ -262,7 +398,7 @@ static gboolean stage_motion (ClutterActor *actor,
                               ClutterEvent *event,
                               gpointer      renderer)
 {
-  gfloat stage_width, stage_height;
+  float stage_width, stage_height;
 
   if (hide_cursor)
     g_source_remove (hide_cursor);
@@ -272,7 +408,8 @@ static gboolean stage_motion (ClutterActor *actor,
   if (!pp_get_fullscreen (CLUTTER_STAGE (actor)))
     return FALSE;
 
-  clutter_actor_get_size (CLUTTER_RENDERER (renderer)->stage, &stage_width, &stage_height);
+  clutter_actor_get_size (CLUTTER_RENDERER (renderer)->stage,
+                          &stage_width, &stage_height);
 #ifdef QUICK_ACCESS_LEFT
   if (event->motion.x < 8)
     {
@@ -293,6 +430,345 @@ static gboolean stage_motion (ClutterActor *actor,
   return FALSE;
 }
 
+#define NORMAL_OPACITY 100
+#define HOVER_OPACITY  255
+
+static gboolean
+opacity_hover_enter (ClutterActor *actor,
+                     ClutterEvent *event,
+                     gpointer      data)
+{
+  clutter_actor_animate (actor, CLUTTER_LINEAR, 200,
+                         "opacity", HOVER_OPACITY,
+                         NULL);
+  return FALSE;
+}
+
+
+static gboolean
+opacity_hover_leave (ClutterActor *actor,
+                     ClutterEvent *event,
+                     gpointer      data)
+{
+  clutter_actor_animate (actor, CLUTTER_LINEAR, 200,
+                         "opacity", NORMAL_OPACITY,
+                         NULL);
+  return FALSE;
+}
+
+#define opacity_hover(o)                             \
+      clutter_actor_set_opacity (o, NORMAL_OPACITY); \
+      clutter_actor_set_reactive (o, TRUE);          \
+      g_signal_connect (o, "enter-event",            \
+                        G_CALLBACK (opacity_hover_enter), NULL); \
+      g_signal_connect (o, "leave-event",            \
+                        G_CALLBACK (opacity_hover_leave), NULL); \
+
+static gboolean
+play_pause (ClutterActor *actor,
+            ClutterEvent *event,
+            gpointer      data)
+{
+  ClutterRenderer *renderer = CLUTTER_RENDERER (data);
+  if (renderer->timer_paused)
+    {
+      g_timer_continue (renderer->timer);
+      renderer->timer_paused = FALSE;
+      clutter_text_set_text (CLUTTER_TEXT (renderer->speaker_pause), "pause");
+    }
+  else
+    {
+      g_timer_stop (renderer->timer);
+      renderer->timer_paused = TRUE;
+      clutter_text_set_text (CLUTTER_TEXT (renderer->speaker_pause), "go");
+    }
+  return TRUE;
+}
+
+static gboolean
+toggle_autoadvance (ClutterActor *actor,
+                    ClutterEvent *event,
+                    gpointer      data)
+{
+  ClutterRenderer *renderer = CLUTTER_RENDERER (data);
+  if (renderer->autoadvance)
+    {
+      renderer->autoadvance = FALSE;
+      clutter_text_set_text (CLUTTER_TEXT (renderer->speaker_autoadvance),
+                             "enable autoadvance");
+    }
+  else
+    {
+      renderer->autoadvance = TRUE;
+      clutter_text_set_text (CLUTTER_TEXT (renderer->speaker_autoadvance),
+                             "disable autoadvance");
+    }
+  return TRUE;
+}
+
+static void end_of_presentation (ClutterRenderer *renderer);
+
+static void
+next_slide (ClutterRenderer *renderer)
+{
+  if (pp_slidep && pp_slidep->next)
+    {
+      leave_slide (renderer, FALSE);
+      pp_slidep = pp_slidep->next;
+      show_slide (renderer, FALSE);
+    }
+  else
+    {
+      end_of_presentation (renderer);
+    }
+}
+
+static void
+prev_slide (ClutterRenderer *renderer)
+{
+  if (pp_slidep && pp_slidep->prev)
+    {
+      leave_slide (renderer, TRUE);
+      pp_slidep = pp_slidep->prev;
+      show_slide (renderer, TRUE);
+    }
+}
+
+static gboolean
+go_prev (ClutterActor *actor,
+         ClutterEvent *event,
+         gpointer      data)
+{
+  ClutterRenderer *renderer = CLUTTER_RENDERER (data);
+  prev_slide (renderer);
+  return TRUE;
+}
+
+static gboolean
+go_next (ClutterActor *actor,
+         ClutterEvent *event,
+         gpointer      data)
+{
+  ClutterRenderer *renderer = CLUTTER_RENDERER (data);
+  next_slide (renderer);
+  return TRUE;
+}
+
+static gboolean
+start (ClutterActor *actor,
+       ClutterEvent *event,
+       gpointer      data)
+{
+  ClutterRenderer *renderer = CLUTTER_RENDERER (data);
+  g_timer_stop (renderer->timer);
+  g_timer_start (renderer->timer);
+  renderer->timer_paused = FALSE;
+  leave_slide (renderer, TRUE);
+  pp_slidep = pp_slides;
+  play_pause (NULL, NULL, data);
+  play_pause (NULL, NULL, data);
+  if (pp_rehearse)
+    {
+      pp_rehearse = FALSE;
+    }
+  pp_rehearse_init (); /* zeroes out the new-time */
+  show_slide (renderer, TRUE);
+  renderer->reset = TRUE;
+  return TRUE;
+}
+
+static gboolean
+start_rehearse (ClutterActor *actor,
+                ClutterEvent *event,
+                gpointer      data)
+{
+  start (actor, event, data);
+  pp_rehearse = TRUE;
+  pp_rehearse_init ();
+
+  return FALSE;
+}
+
+
+static void toggle_speaker_screen (ClutterRenderer *renderer);
+
+static void
+clutter_renderer_init_speaker_screen (ClutterRenderer *renderer)
+{
+  renderer->speaker_screen = clutter_stage_new ();
+
+  renderer->speaker_notes = g_object_new (CLUTTER_TYPE_TEXT,
+                                "x", 10.0,
+                                "y", 20.0,
+                                "font-name",      "Sans 20px",
+                                "color",          &white,
+                                NULL);
+
+  renderer->speaker_time_remaining = g_object_new (CLUTTER_TYPE_TEXT,
+                                "x",           300.0,
+                                "y",           0.0,
+                                "opacity",     NORMAL_OPACITY,
+                                "font-name",   "Sans 24px",
+                                "text",        "-",
+                                "color",       &white,
+                                NULL);
+
+  renderer->speaker_buttons_group = clutter_group_new ();
+
+#define BUTTON_FONT "Sans 20px"
+
+  renderer->speaker_speakerscreen = g_object_new (CLUTTER_TYPE_TEXT,
+                                "x",           0.0,
+                                "y",           0.0,
+                                "opacity",     NORMAL_OPACITY,
+                                "font-name",   BUTTON_FONT,
+                                "text",        "speaker",
+                                "reactive",    TRUE,
+                                "color",       &white,
+                                NULL);
+
+  renderer->speaker_rehearse = g_object_new (CLUTTER_TYPE_TEXT,
+                                "x",           0.0,
+                                "y",           0.0,
+                                "opacity",     NORMAL_OPACITY,
+                                "font-name",   BUTTON_FONT,
+                                "text",        "rehearse",
+                                "reactive",    TRUE,
+                                "color",       &white,
+                                NULL);
+  renderer->speaker_autoadvance = g_object_new (CLUTTER_TYPE_TEXT,
+                                "y",           0.0,
+                                "opacity",     NORMAL_OPACITY,
+                                "font-name",   BUTTON_FONT,
+                                "text",        "enable autoadvance",
+                                "reactive",    TRUE,
+                                "color",       &white,
+                                NULL);
+  renderer->speaker_start = g_object_new (CLUTTER_TYPE_TEXT,
+                                "y",           0.0,
+                                "opacity",     NORMAL_OPACITY,
+                                "font-name",   BUTTON_FONT,
+                                "text",        "(re)start",
+                                "reactive",    TRUE,
+                                "color",       &white,
+                                NULL);
+  renderer->speaker_pause = g_object_new (CLUTTER_TYPE_TEXT,
+                                "y",           0.0,
+                                "opacity",     NORMAL_OPACITY,
+                                "font-name",   BUTTON_FONT,
+                                "text",        "",
+                                "reactive",    TRUE,
+                                "color",       &white,
+                                NULL);
+
+  renderer->speaker_fullscreen = g_object_new (CLUTTER_TYPE_TEXT,
+                                "x",           0.0,
+                                "y",           0.0,
+                                "opacity",     NORMAL_OPACITY,
+                                "font-name",   BUTTON_FONT,
+                                "text",        "fullscreen",
+                                "reactive",    TRUE,
+                                "color",       &white,
+                                NULL);
+
+  opacity_hover(renderer->speaker_speakerscreen);
+  opacity_hover(renderer->speaker_rehearse);
+
+  g_signal_connect (renderer->speaker_rehearse, "button-press-event",
+                    G_CALLBACK (start_rehearse), renderer);
+
+  opacity_hover(renderer->speaker_autoadvance);
+
+  g_signal_connect (renderer->speaker_autoadvance, "button-press-event",
+                    G_CALLBACK (toggle_autoadvance), renderer);
+
+  opacity_hover(renderer->speaker_start);
+
+  g_signal_connect (renderer->speaker_start, "button-press-event",
+                    G_CALLBACK (start), renderer);
+
+  opacity_hover(renderer->speaker_pause);
+
+  g_signal_connect (renderer->speaker_pause, "button-press-event",
+                    G_CALLBACK (play_pause), renderer);
+
+  opacity_hover(renderer->speaker_fullscreen);
+
+  clutter_container_add (CLUTTER_CONTAINER (renderer->speaker_buttons_group),
+                         renderer->speaker_speakerscreen,
+                         renderer->speaker_start,
+                         renderer->speaker_pause,
+                         renderer->speaker_autoadvance,
+                         renderer->speaker_rehearse,
+                         renderer->speaker_fullscreen,
+                         NULL);
+
+  g_signal_connect (renderer->speaker_screen, "key-press-event",
+                    G_CALLBACK (key_pressed), renderer);
+
+
+
+  renderer->timer_paused = TRUE;
+  renderer->timer = g_timer_new ();
+  g_timer_stop (renderer->timer);
+
+  renderer->speaker_prog_bg = clutter_rectangle_new_with_color (&c_prog_bg);
+  renderer->speaker_prog_time = clutter_rectangle_new_with_color (&c_prog_time);
+  renderer->speaker_prog_slide = clutter_rectangle_new_with_color (&c_prog_slide);
+  renderer->speaker_slide_prog_warning = clutter_rectangle_new_with_color (&red);
+
+  clutter_stage_set_color (CLUTTER_STAGE (renderer->speaker_screen), &black);
+  clutter_stage_set_color (CLUTTER_STAGE (renderer->speaker_screen), &black);
+  clutter_stage_set_user_resizable (CLUTTER_STAGE (renderer->speaker_screen), TRUE);
+
+  renderer->speaker_prev = clutter_cairo_texture_new (PREVIEW_WIDTH, PREVIEW_HEIGHT);
+  renderer->speaker_current = clutter_cairo_texture_new (PREVIEW_WIDTH, PREVIEW_HEIGHT);
+  renderer->speaker_next = clutter_cairo_texture_new (PREVIEW_WIDTH, PREVIEW_HEIGHT);
+
+  clutter_container_add (CLUTTER_CONTAINER (renderer->speaker_screen),
+
+                         renderer->speaker_prev,
+                         renderer->speaker_current,
+                         renderer->speaker_next,
+                         renderer->speaker_notes,
+                         renderer->speaker_prog_bg,
+                         renderer->speaker_slide_prog_warning,
+                         renderer->speaker_prog_time,
+
+
+                         renderer->speaker_prog_slide,
+
+
+                         renderer->speaker_buttons_group,
+                         renderer->speaker_time_remaining,
+                         NULL);
+
+
+
+  clutter_actor_set_opacity (renderer->speaker_slide_prog_warning, 0);
+
+
+
+  opacity_hover(renderer->speaker_prev);
+  opacity_hover(renderer->speaker_next);
+
+  g_signal_connect (renderer->speaker_prev, "button-press-event",
+                    G_CALLBACK (go_prev), renderer);
+  g_signal_connect (renderer->speaker_next, "button-press-event",
+                    G_CALLBACK (go_next), renderer);
+
+}
+
+static gboolean
+stage_deleted (ClutterStage *stage,
+               ClutterEvent *event,
+               gpointer      user_data)
+{
+  clutter_main_quit ();
+
+  return TRUE;
+}
+
 static void
 clutter_renderer_init (PinPointRenderer   *pp_renderer,
                        char               *pinpoint_file)
@@ -301,10 +777,9 @@ clutter_renderer_init (PinPointRenderer   *pp_renderer,
   GFileMonitor *monitor;
   ClutterActor *stage;
 
+  renderer->stage = stage = clutter_stage_new ();
+  renderer->root = clutter_group_new ();
   renderer->rest_y = STARTPOS;
-
-  renderer->stage = stage = clutter_stage_get_default ();
-
   renderer->background = clutter_group_new ();
   renderer->midground = clutter_group_new ();
   renderer->foreground = clutter_group_new ();
@@ -312,12 +787,18 @@ clutter_renderer_init (PinPointRenderer   *pp_renderer,
   renderer->shading = clutter_rectangle_new_with_color (&black);
   renderer->commandline_shading = clutter_rectangle_new_with_color (&black);
   renderer->commandline = clutter_text_new ();
+
   clutter_actor_set_opacity (renderer->shading, 0x77);
   clutter_actor_set_opacity (renderer->commandline_shading, 0x77);
 
   clutter_container_add_actor (CLUTTER_CONTAINER (renderer->midground),
                                renderer->shading);
-  clutter_container_add (CLUTTER_CONTAINER (stage),
+
+
+  clutter_container_add (CLUTTER_CONTAINER (renderer->stage),
+                         renderer->root,
+                         NULL);
+  clutter_container_add (CLUTTER_CONTAINER (renderer->root),
                          renderer->background,
                          renderer->midground,
                          renderer->foreground,
@@ -326,12 +807,32 @@ clutter_renderer_init (PinPointRenderer   *pp_renderer,
                          renderer->commandline,
                          NULL);
 
+  renderer->timer_paused = FALSE;
+  renderer->timer = g_timer_new ();
+
+
+  if (pp_speakermode)
+    toggle_speaker_screen (renderer);
+
   clutter_actor_show (stage);
+
+
   clutter_stage_set_color (CLUTTER_STAGE (stage), &black);
+  g_signal_connect (stage, "delete-event",
+                    G_CALLBACK (stage_deleted), renderer);
   g_signal_connect (stage, "key-press-event",
                     G_CALLBACK (key_pressed), renderer);
   g_signal_connect (stage, "notify::width",
                     G_CALLBACK (stage_resized), renderer);
+
+  if (renderer->speaker_screen)
+    {
+      g_signal_connect (renderer->speaker_screen, "notify::width",
+                    G_CALLBACK (stage_resized), renderer);
+      g_signal_connect (renderer->speaker_screen, "notify::height",
+                    G_CALLBACK (stage_resized), renderer);
+    }
+
   g_signal_connect (stage, "notify::height",
                     G_CALLBACK (stage_resized), renderer);
   g_signal_connect (stage, "motion-event",
@@ -359,12 +860,24 @@ clutter_renderer_init (PinPointRenderer   *pp_renderer,
 
   renderer->bg_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
                                               NULL, _destroy_surface);
+
+  renderer->cairo_renderer = pp_cairo_renderer ();
+  renderer->cairo_renderer->init (renderer->cairo_renderer, "");
 }
 
+static gboolean update_speaker_screen (ClutterRenderer *renderer);
+
 static void
-clutter_renderer_run (PinPointRenderer *renderer)
+clutter_renderer_run (PinPointRenderer *pp_renderer)
 {
-  show_slide (CLUTTER_RENDERER (renderer), FALSE);
+  ClutterRenderer *renderer = CLUTTER_RENDERER (pp_renderer);
+
+  show_slide (renderer, FALSE);
+
+  /* the presentaiton is not parsed at first initialization,.. */
+  renderer->total_seconds = point_defaults->duration * 60;
+
+  g_timeout_add (15, (GSourceFunc)update_speaker_screen, renderer);
   clutter_main ();
 }
 
@@ -405,21 +918,136 @@ _clutter_get_texture (ClutterRenderer *renderer,
   return clutter_clone_new (source);
 }
 
+#if USE_CLUTTER_GST
+static void
+on_size_changed (ClutterActor *texture,
+                 gint          width,
+                 gint          height,
+                 gpointer      user_data)
+{
+  PinPointRenderer *pp_renderer = user_data;
+  ClutterRenderer *renderer = CLUTTER_RENDERER (pp_renderer);
+  PinPointPoint *point;
+  ClutterPointData *data;
+
+  point = pp_slidep->data;
+  if (!point)
+    return;
+
+  data = point->data;
+
+  if (data->background != texture)
+    {
+      g_warning ("size changed but not current background ?!");
+      return;
+    }
+
+  clutter_actor_set_size (texture, width, height);
+  pp_clutter_render_adjust_background (renderer, point);
+}
+
+static gboolean
+setup_camera (PinPointRenderer *renderer,
+              PinPointPoint    *point)
+{
+  /* These are static to be able to share the texture and the pipeline between
+   * all the slides using the camera */
+  static ClutterActor *texture = NULL;
+  static GstElement   *pipeline = NULL;
+
+  ClutterPointData *data = point->data;
+  GstElement       *src;
+  GstElement       *capsfilter;
+  GstElement       *sink;
+  GstCaps          *caps;
+  gboolean          result;
+
+  if (texture)
+    {
+      data->background = clutter_clone_new (texture);
+      data->pipeline = pipeline;
+
+      return TRUE;
+    }
+
+  texture = g_object_new (CLUTTER_TYPE_TEXTURE, "disable-slicing", TRUE, NULL);
+
+  g_signal_connect (CLUTTER_TEXTURE (texture),
+                    "size-change",
+                    G_CALLBACK (on_size_changed), renderer);
+
+  /* Set up pipeline */
+  pipeline = gst_pipeline_new (NULL);
+
+  src = gst_element_factory_make ("v4l2src", NULL);
+  if (src == NULL)
+    {
+      g_critical ("Failed to create v4l2src element");
+      g_object_unref (pipeline);
+      return FALSE;
+    }
+
+  capsfilter = gst_element_factory_make ("capsfilter", NULL);
+  sink = clutter_gst_video_sink_new (CLUTTER_TEXTURE (texture));
+
+  /* make videotestsrc spit the format we want */
+  caps = gst_caps_new_simple ("video/x-raw-yuv", NULL);
+
+  if (point->camera_framerate)
+    {
+      gst_caps_set_simple (caps,
+                           "framerate", GST_TYPE_FRACTION,
+                           point->camera_framerate, 1,
+                           NULL);
+    }
+
+  if (pp_camera_device)
+    g_object_set (src, "device", pp_camera_device, NULL);
+
+#define W (point->camera_resolution.width)
+#define H (point->camera_resolution.height)
+  if (W != 0 && H != 0)
+    {
+      gst_caps_set_simple (caps,
+                           "width", G_TYPE_INT, W,
+                           "height", G_TYPE_INT, H,
+                           NULL);
+    }
+#undef W
+#undef H
+
+  g_object_set (capsfilter, "caps", caps, NULL);
+
+  gst_bin_add_many (GST_BIN (pipeline), src, capsfilter, sink, NULL);
+  result = gst_element_link_many (src, capsfilter, sink, NULL);
+  if (result == FALSE)
+    {
+      g_critical("Could not link elements");
+      gst_object_unref (pipeline);
+      return FALSE;
+    }
+
+  data->background = texture;
+  data->pipeline = pipeline;
+
+  return TRUE;
+}
+#endif
 
 static gboolean
 clutter_renderer_make_point (PinPointRenderer *pp_renderer,
                              PinPointPoint    *point)
 {
-  ClutterRenderer *renderer = CLUTTER_RENDERER (pp_renderer);
-  ClutterPointData *data = point->data;
-  const gchar *file = point->bg;
-  gchar *full_path = NULL;
+  ClutterRenderer  *renderer  = CLUTTER_RENDERER (pp_renderer);
+  ClutterPointData *data      = point->data;
+  const char       *file      = point->bg;
+        char       *full_path = NULL;
   ClutterColor color;
-  gboolean ret;
+  gboolean ret = FALSE;
 
   if (point->bg_type != PP_BG_COLOR && renderer->path && file)
     {
-      gchar *dir = g_path_get_dirname (renderer->path);
+      char *dir = g_path_get_dirname (renderer->path);
       full_path = g_build_filename (dir, file, NULL);
       g_free (dir);
 
@@ -433,8 +1061,8 @@ clutter_renderer_make_point (PinPointRenderer *pp_renderer,
         ret = clutter_color_from_string (&color, point->bg);
         if (ret)
           data->background = g_object_new (CLUTTER_TYPE_RECTANGLE,
-                                           "color", &color,
-                                           "width", 100.0,
+                                           "color",  &color,
+                                           "width",  100.0,
                                            "height", 100.0,
                                            NULL);
       }
@@ -447,11 +1075,17 @@ clutter_renderer_make_point (PinPointRenderer *pp_renderer,
 #ifdef USE_CLUTTER_GST
       data->background = clutter_gst_video_texture_new ();
       clutter_media_set_filename (CLUTTER_MEDIA (data->background), file);
-      /* should pre-roll the video and set the size */
-      clutter_actor_set_size (data->background, 400, 300);
+      g_signal_connect (CLUTTER_TEXTURE (data->background),
+                        "size-change",
+                        G_CALLBACK (on_size_changed), renderer);
       ret = TRUE;
 #endif
       break;
+    case PP_BG_CAMERA:
+#ifdef USE_CLUTTER_GST
+      ret = setup_camera (pp_renderer, point);
+#endif
+      break;
     case PP_BG_SVG:
 #ifdef USE_DAX
       {
@@ -495,20 +1129,20 @@ clutter_renderer_make_point (PinPointRenderer *pp_renderer,
   if (point->use_markup)
     {
       data->text = g_object_new (CLUTTER_TYPE_TEXT,
-                                 "font-name", point->font,
-                                 "text", point->text,
+                                 "font-name",      point->font,
+                                 "text",           point->text,
                                  "line-alignment", point->text_align,
-                                 "color", &color,
-                                 "use-markup", TRUE,
+                                 "color",          &color,
+                                 "use-markup",     TRUE,
                                  NULL);
     }
   else
     {
       data->text = g_object_new (CLUTTER_TYPE_TEXT,
-                                 "font-name", point->font,
-                                 "text", point->text,
+                                 "font-name",      point->font,
+                                 "text",           point->text,
                                  "line-alignment", point->text_align,
-                                 "color", &color,
+                                 "color",          &color,
                                  NULL);
     }
 
@@ -548,7 +1182,34 @@ clutter_renderer_free_data (PinPointRenderer *renderer,
   g_slice_free (ClutterPointData, data);
 }
 
+static void end_of_presentation (ClutterRenderer *renderer)
+{
+  if (pp_rehearse)
+    {
+      pp_rehearse_done ();
+    }
+  pp_rehearse = FALSE;
+  if (renderer->autoadvance)
+    toggle_autoadvance (NULL, NULL, renderer);
+}
+
 
+static void
+toggle_speaker_screen (ClutterRenderer *renderer)
+{
+  if (!renderer->speaker_screen)
+    clutter_renderer_init_speaker_screen (renderer);
+  if (renderer->speaker_mode)
+    {
+      renderer->speaker_mode = FALSE;
+      clutter_actor_hide (renderer->speaker_screen);
+    }
+  else
+    {
+      renderer->speaker_mode = TRUE;
+      clutter_actor_show (renderer->speaker_screen);
+    }
+}
 
 static gboolean
 key_pressed (ClutterActor    *actor,
@@ -560,39 +1221,46 @@ key_pressed (ClutterActor    *actor,
     {
       case CLUTTER_Left:
       case CLUTTER_Up:
-        if (pp_slidep && pp_slidep->prev)
-          {
-            leave_slide (renderer, TRUE);
-            pp_slidep = pp_slidep->prev;
-            show_slide (renderer, TRUE);
-          }
-        break;
       case CLUTTER_BackSpace:
       case CLUTTER_Prior:
-        if (pp_slidep && pp_slidep->prev)
-          {
-            leave_slide (renderer, TRUE);
-            pp_slidep = pp_slidep->prev;
-            show_slide (renderer, TRUE);
-          }
+        prev_slide (renderer);
         break;
       case CLUTTER_Right:
       case CLUTTER_space:
       case CLUTTER_Next:
       case CLUTTER_Down:
-        if (pp_slidep && pp_slidep->next)
-          {
-            leave_slide (renderer, FALSE);
-            pp_slidep = pp_slidep->next;
-            show_slide (renderer, FALSE);
-          }
+        next_slide (renderer);
         break;
       case CLUTTER_Escape:
         clutter_main_quit ();
         break;
+      case CLUTTER_F1:
+        {
+          gboolean was_fullscreen = pp_get_fullscreen (
+                                              CLUTTER_STAGE (renderer->stage));
+          toggle_speaker_screen (renderer);
+          if (renderer->speaker_mode && renderer->speaker_screen)
+            pp_set_fullscreen (CLUTTER_STAGE (renderer->speaker_screen),
+                               was_fullscreen);
+        }
+        break;
+      case CLUTTER_F2:
+        if (renderer->autoadvance)
+          renderer->autoadvance = FALSE;
+        else
+          renderer->autoadvance = TRUE;
+        break;
       case CLUTTER_F11:
-        pp_set_fullscreen (CLUTTER_STAGE (renderer->stage),
-                           !pp_get_fullscreen (CLUTTER_STAGE (renderer->stage)));
+        case CLUTTER_F:
+        case CLUTTER_f:
+        {
+          gboolean was_fullscreen = pp_get_fullscreen (
+                                              CLUTTER_STAGE (renderer->stage));
+          pp_set_fullscreen (CLUTTER_STAGE (renderer->stage), !was_fullscreen);
+          if (renderer->speaker_mode && renderer->speaker_screen)
+            pp_set_fullscreen (CLUTTER_STAGE (renderer->speaker_screen),
+                               !was_fullscreen);
+        }
         break;
       case CLUTTER_Return:
         action_slide (renderer);
@@ -604,48 +1272,31 @@ key_pressed (ClutterActor    *actor,
   return TRUE;
 }
 
-
 static void leave_slide (ClutterRenderer *renderer,
                          gboolean         backwards)
 {
   PinPointPoint *point = pp_slidep->data;
   ClutterPointData *data = point->data;
 
+  point->new_duration += g_timer_elapsed (renderer->timer, NULL) -
+                                          renderer->slide_start_time;
+
   if (!point->transition)
     {
       clutter_actor_animate (data->text,
                              CLUTTER_LINEAR, 2000,
-                             "depth", RESTDEPTH,
-                             "scale-x", 1.0,
-                             "scale-y", 1.0,
-                             "x", RESTX,
-                             "y", data->rest_y,
+                             "depth",        RESTDEPTH,
+                             "scale-x",      1.0,
+                             "scale-y",      1.0,
+                             "x",            RESTX,
+                             "y",            data->rest_y,
                              NULL);
       if (data->background)
         {
           clutter_actor_animate (data->background,
                                  CLUTTER_LINEAR, 1000,
-                                 "opacity", 0x0,
+                                 "opacity",      0x0,
                                  NULL);
-#ifdef USE_CLUTTER_GST
-          if (CLUTTER_GST_IS_VIDEO_TEXTURE (data->background))
-            {
-              clutter_media_set_playing (CLUTTER_MEDIA (data->background), FALSE);
-            }
-#endif
-#ifdef USE_DAX
-          if (DAX_IS_ACTOR (data->background))
-            {
-              dax_actor_set_playing (DAX_ACTOR (data->background), FALSE);
-            }
-          else if (PP_IS_SUPER_AA (data->background))
-            {
-              ClutterActor *actor;
-
-              actor = mx_offscreen_get_child (MX_OFFSCREEN (data->background));
-              dax_actor_set_playing (DAX_ACTOR (actor), FALSE);
-            }
-#endif
         }
     }
   else
@@ -654,17 +1305,45 @@ static void leave_slide (ClutterRenderer *renderer,
         {
           if (backwards)
             clutter_state_set_state (data->state, "pre");
-          else 
+          else
             clutter_state_set_state (data->state, "post");
         }
     }
+
+  if (data->background)
+    {
+#ifdef USE_CLUTTER_GST
+      if (point->bg_type == PP_BG_CAMERA)
+        {
+          gst_element_set_state (data->pipeline, GST_STATE_PAUSED);
+        }
+      if (CLUTTER_GST_IS_VIDEO_TEXTURE (data->background))
+        {
+          clutter_media_set_playing (CLUTTER_MEDIA (data->background),
+                                     FALSE);
+        }
+#endif
+#ifdef USE_DAX
+      if (DAX_IS_ACTOR (data->background))
+        {
+          dax_actor_set_playing (DAX_ACTOR (data->background), FALSE);
+        }
+      else if (PP_IS_SUPER_AA (data->background))
+        {
+          ClutterActor *actor;
+
+          actor = mx_offscreen_get_child (MX_OFFSCREEN (data->background));
+          dax_actor_set_playing (DAX_ACTOR (actor), FALSE);
+        }
+#endif
+    }
 }
 
 static void state_completed (ClutterState *state, gpointer user_data)
 {
-  PinPointPoint    *point = user_data;
-  ClutterPointData *data = point->data;
-  const gchar *new_state = clutter_state_get_state (state);
+  PinPointPoint    *point     = user_data;
+  ClutterPointData *data      = point->data;
+  const char       *new_state = clutter_state_get_state (state);
 
   if (new_state == g_intern_static_string ("post") ||
       new_state == g_intern_static_string ("pre"))
@@ -675,11 +1354,11 @@ static void state_completed (ClutterState *state, gpointer user_data)
           clutter_actor_reparent (data->text, CLUTTER_RENDERER (data->renderer)->foreground);
 
           g_object_set (data->text,
-                        "depth", RESTDEPTH,
+                        "depth",   RESTDEPTH,
                         "scale-x", 1.0,
                         "scale-y", 1.0,
-                        "x", RESTX,
-                        "y", data->rest_y,
+                        "x",       RESTX,
+                        "y",       data->rest_y,
                         NULL);
           clutter_actor_set_opacity (data->background, 0);
         }
@@ -689,13 +1368,13 @@ static void state_completed (ClutterState *state, gpointer user_data)
 static void
 action_slide (ClutterRenderer *renderer)
 {
-  PinPointPoint *point;
+  PinPointPoint    *point;
   ClutterPointData *data;
-  const char *command = NULL;
+  const char       *command = NULL;
 
   if (!pp_slidep)
     return;
- 
+
   point = pp_slidep->data;
   data = point->data;
 
@@ -712,13 +1391,14 @@ action_slide (ClutterRenderer *renderer)
     }
 }
 
-static gchar *pp_lookup_transition (const gchar *transition)
+static char *pp_lookup_transition (const char *transition)
 {
-  int i;
-  gchar *dirs[] ={ "", "./transitions/", PKGDATADIR, NULL};
+  int   i;
+  char *dirs[] ={ "", "./transitions/", PKGDATADIR, NULL};
+
   for (i = 0; dirs[i]; i++)
     {
-      gchar *path = g_strdup_printf ("%s%s.json", dirs[i], transition);
+      char *path = g_strdup_printf ("%s%s.json", dirs[i], transition);
       if (g_file_test (path, G_FILE_TEST_EXISTS))
         return path;
       g_free (path);
@@ -726,50 +1406,374 @@ static gchar *pp_lookup_transition (const gchar *transition)
   return NULL;
 }
 
-
 static void update_commandline_shading (ClutterRenderer *renderer)
 {
-   ClutterColor color;
-   float text_x, text_y, text_width, text_height;
-   float shading_x, shading_y, shading_width, shading_height;
-   const char *command;
-   PinPointPoint *point;
-   point = pp_slidep->data;
-   clutter_actor_get_size (renderer->commandline, &text_width, &text_height);
-   clutter_actor_get_position (renderer->commandline, &text_x, &text_y);
-   pp_get_shading_position_size (clutter_actor_get_width (renderer->stage),
-                                 clutter_actor_get_height (renderer->stage),
-                                 text_x, text_y,
-                                 text_width, text_height,
-                                 1.0,
-                                 &shading_x, &shading_y,
-                                 &shading_width, &shading_height);
-   
-   clutter_color_from_string (&color, point->shading_color);
-   g_object_set (renderer->commandline_shading,
-          "x", shading_x,
-          "y", shading_y,
-          NULL);
-   command = clutter_text_get_text (CLUTTER_TEXT (renderer->commandline));
-   clutter_actor_animate (renderer->commandline_shading,
-          CLUTTER_EASE_OUT_QUINT, 1000,
-          "opacity", command && *command?(int)(point->shading_opacity*255*0.33):0,
-          "color", &color,
-          "width", shading_width,
-          "height", shading_height,
-          NULL);
+  PinPointPoint *point;
+  ClutterColor   color;
+  const char    *command;
+
+  float text_x,    text_y,    text_width,    text_height;
+  float shading_x, shading_y, shading_width, shading_height;
+
+  point = pp_slidep->data;
+  clutter_actor_get_size (renderer->commandline, &text_width, &text_height);
+  clutter_actor_get_position (renderer->commandline, &text_x, &text_y);
+  pp_get_shading_position_size (clutter_actor_get_width (renderer->stage),
+                                clutter_actor_get_height (renderer->stage),
+                                text_x, text_y,
+                                text_width, text_height,
+                                1.0,
+                                &shading_x, &shading_y,
+                                &shading_width, &shading_height);
+
+  clutter_color_from_string (&color, point->shading_color);
+  g_object_set (renderer->commandline_shading,
+         "x", shading_x,
+         "y", shading_y,
+         NULL);
+  command = clutter_text_get_text (CLUTTER_TEXT (renderer->commandline));
+
+  clutter_actor_animate (renderer->commandline_shading,
+       CLUTTER_EASE_OUT_QUINT, 1000,
+
+       /* the opacity of the commandline shading depends on whether we
+          have a command or not */
+       "opacity", command && *command?(int)(point->shading_opacity*255*0.33):0,
+       "color",   &color,
+       "width",   shading_width,
+       "height",  shading_height,
+       NULL);
+}
+
+static gfloat point_time (ClutterRenderer *renderer,
+                          PinPointPoint   *point)
+{
+  GList *iter;
+  float time;
+  gboolean after_current = FALSE;
+
+  for (iter = pp_slides; iter && iter->data != point; iter = iter->next)
+    {
+      if (iter == pp_slidep)
+        after_current = TRUE;
+    }
+
+  time = point->duration != 0.0 ? point->duration : 2.0;
+  /* if before current point, use new time.. if at or after current point
+     use historic time
+   */
+  if (!after_current)
+    if (point->new_duration != 0.0)
+      time = point->new_duration;
+  return time;
+}
+
+static gfloat total_time (ClutterRenderer *renderer,
+                          GList *start)
+{
+  GList *iter;
+  gfloat total = 0;
+  if (!start)
+    start = pp_slides;
+  for (iter = start; iter; iter = iter->next)
+    {
+      total += point_time (renderer, iter->data);
+    }
+  return total;
+}
+
+static gfloat slide_rel_duration (ClutterRenderer *renderer,
+                                  GList *slide)
+{
+  return point_time (renderer, slide->data) / total_time (renderer, pp_slides);
+}
+
+static gfloat slide_rel_start (ClutterRenderer *renderer,
+                               GList *slide)
+{
+  GList *iter;
+  float time = 0;
+
+  for (iter = slide ->prev; iter; iter = iter->prev)
+    {
+      time += point_time (renderer, iter->data);
+    }
+
+  time = time / total_time (renderer, pp_slides);
+  return time;
+}
+
+static gfloat slide_time (ClutterRenderer *renderer,
+                          GList *slide)
+{
+  float time = point_time (renderer, slide->data) /
+                     total_time (renderer, slide);
+  float remaining_time = renderer->total_seconds -
+                           g_timer_elapsed (renderer->timer, NULL);
+  time *= remaining_time;
+  return time;
+}
+
+
+static gboolean update_speaker_screen (ClutterRenderer *renderer)
+{
+  PinPointPoint *point;
+
+  if (!pp_slidep)
+    return FALSE;
+
+  point = pp_slidep->data;
+  static float current_slide_time = 0.0;
+  static float current_slide_duration = 0.0;
+  static GList *current_slide = NULL;
+  float nh, nw;
+
+  if (renderer->reset)
+    {
+      current_slide = NULL;
+      current_slide_duration = 0.0;
+      current_slide_time = 0.0;
+      renderer->reset = FALSE;
+    }
+
+    {
+      static float current_slide_prev_time = 0.0;
+      float warn_time = SLIDE_WARN_TIME;
+      float diff = g_timer_elapsed (renderer->timer, NULL) - current_slide_prev_time;
+
+      if (current_slide != pp_slidep)
+        {
+          current_slide_time = 0;
+          current_slide = pp_slidep;
+          current_slide_duration = slide_time (renderer, pp_slidep);
+        }
+
+      /* if 33% of the slide is longer than the seconds based threshold, use
+         the percentage
+       */
+      if ((warn_time <= current_slide_duration * SLIDE_WARN_THRESHOLD))
+        warn_time =     current_slide_duration * SLIDE_WARN_THRESHOLD;
+
+      current_slide_time += diff;
+      if (current_slide_time >= current_slide_duration)
+        {
+          if (renderer->autoadvance)
+            {
+              current_slide_time = 0;
+              next_slide (renderer);
+            }
+
+
+          if (renderer->speaker_slide_prog_warning)
+            clutter_actor_animate (renderer->speaker_slide_prog_warning,
+                                   CLUTTER_LINEAR, 500,
+                                   "opacity", OPACITY_OVER_TIME,
+                                   NULL);
+        }
+
+
+      else if ((current_slide_duration - current_slide_time < warn_time))
+        {
+          if (renderer->speaker_slide_prog_warning)
+            clutter_actor_animate (renderer->speaker_slide_prog_warning,
+                                   CLUTTER_LINEAR, 500,
+                                   "opacity", OPACITY_PAST_THRESHOLD,
+                                   NULL);
+        }
+      else
+        {
+          if (renderer->speaker_slide_prog_warning)
+            clutter_actor_animate (renderer->speaker_slide_prog_warning,
+                                   CLUTTER_LINEAR, 50,
+                                   "opacity", OPACITY_OK,
+                                   NULL);
+        }
+
+      current_slide_prev_time = g_timer_elapsed (renderer->timer, NULL);
+    }
+
+  if (!renderer->speaker_mode)
+    return TRUE;
+
+  if (point->speaker_notes)
+    clutter_text_set_text (CLUTTER_TEXT (renderer->speaker_notes),
+                           point->speaker_notes);
+  else
+    clutter_text_set_text (CLUTTER_TEXT (renderer->speaker_notes), "");
+
+  nw = clutter_actor_get_width (renderer->speaker_screen) + 1;
+  nh = clutter_actor_get_height (renderer->speaker_screen);
+
+  { /* should draw rectangles representing progress instead... */
+    GString *str = g_string_new ("");
+    int i;
+    GList *iter;
+    for (iter = pp_slides, i=0; iter && iter != pp_slidep;
+         iter = iter->next, i++);
+
+    {
+      int time;
+      time = renderer->total_seconds -
+                      g_timer_elapsed (renderer->timer, NULL) + 0.5;
+
+      if (time <= -60)
+        g_string_printf (str, "%imin", time/60);
+      else if (time <= 10)
+        g_string_printf (str, "%is", time);
+      else if (time <= 60)
+        {
+          time = ((time + 4)/ 5) * 5;
+          g_string_printf (str, "%is", time);
+        }
+      else
+        g_string_printf (str, "%i%smin", time / 60, (time % 60 > 30) ? "½":"");
+
+      clutter_text_set_text (CLUTTER_TEXT (renderer->speaker_time_remaining),
+                             str->str);
+    }
+    g_string_free (str, TRUE);
+  }
+
+  {
+#define append_ltr(a,b) \
+  clutter_actor_set_x (b, clutter_actor_get_x (a) + clutter_actor_get_width (a) + 20)
+
+    /* should be replace with constraints */
+    append_ltr (renderer->speaker_speakerscreen, renderer->speaker_start);
+    append_ltr (renderer->speaker_start, renderer->speaker_pause);
+    append_ltr (renderer->speaker_pause, renderer->speaker_autoadvance);
+    append_ltr (renderer->speaker_autoadvance, renderer->speaker_rehearse);
+    append_ltr (renderer->speaker_rehearse, renderer->speaker_fullscreen);
+  }
+
+  {
+    float height = 32;
+    float y = clutter_actor_get_height (renderer->speaker_screen) - height;
+    float elapsed_part = g_timer_elapsed (renderer->timer, NULL) / renderer->total_seconds;
+
+    clutter_actor_set_height (renderer->speaker_prog_bg, height);
+    clutter_actor_set_height (renderer->speaker_prog_slide, height * 0.7);
+    clutter_actor_set_height (renderer->speaker_prog_time, height * 0.84);
+    clutter_actor_set_size   (renderer->speaker_slide_prog_warning,
+                              nw, height * 1.0);
+    clutter_actor_set_y (renderer->speaker_prog_bg, y);
+    clutter_actor_set_y (renderer->speaker_prog_slide, y + height * 0.05);
+    clutter_actor_set_y (renderer->speaker_prog_time, y);
+
+    clutter_actor_set_y (renderer->speaker_slide_prog_warning, y - height * 0.1);
+    /*
+    clutter_actor_set_size   (renderer->speaker_slide_prog_warning,
+                              nw, nh);
+    clutter_actor_set_y (renderer->speaker_slide_prog_warning, 0);*/
+
+    clutter_actor_set_x (renderer->speaker_buttons_group, nw * 0.4);
+    clutter_actor_set_y (renderer->speaker_buttons_group, 5);
+
+    clutter_actor_set_width (renderer->speaker_prog_bg, nw);
+
+
+    clutter_actor_set_position (renderer->speaker_time_remaining,
+       nw - clutter_actor_get_width (renderer->speaker_time_remaining),
+       nh - clutter_actor_get_height (renderer->speaker_time_remaining) - 4);
+
+    clutter_actor_set_width (renderer->speaker_prog_slide,
+                             nw * slide_rel_duration (renderer, pp_slidep));
+    clutter_actor_set_x (renderer->speaker_prog_slide,
+                         nw * slide_rel_start (renderer, pp_slidep));
+
+    clutter_actor_set_x (renderer->speaker_prog_time, nw * elapsed_part);
+
+    clutter_actor_set_width (renderer->speaker_prog_time, nw * (1.0-elapsed_part));
+
+
+    elapsed_part = current_slide_time / current_slide_duration;
+  }
+
+  {
+    float scale;
+    scale = nh / clutter_actor_get_height (renderer->speaker_next) * 0.4;
+    clutter_actor_set_scale (renderer->speaker_prev, scale, scale);
+    clutter_actor_set_scale (renderer->speaker_current, scale, scale);
+    clutter_actor_set_scale (renderer->speaker_next, scale, scale);
   }
 
+  {
+    static GList *current_slide = NULL;
+    if (current_slide != pp_slidep)
+      {
+        cairo_t *cr;
+
+        /*************/
+        cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (renderer->speaker_prev));
+        cairo_renderer_set_cr (renderer->cairo_renderer,
+                               cr, clutter_actor_get_width (renderer->speaker_prev),
+                               clutter_actor_get_height (renderer->speaker_prev));
+        if (pp_slidep->prev)
+          cairo_renderer_render_page (renderer->cairo_renderer,
+                                      pp_slidep->prev->data);
+        else
+          cairo_renderer_render_page (renderer->cairo_renderer,
+                                      NULL);
+        cairo_renderer_unset_cr (renderer->cairo_renderer);
+        cairo_destroy (cr);
+
+        /*************/
+        cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (renderer->speaker_current));
+        cairo_renderer_set_cr (renderer->cairo_renderer,
+                               cr, clutter_actor_get_width (renderer->speaker_current),
+                               clutter_actor_get_height (renderer->speaker_current));
+        cairo_renderer_render_page (renderer->cairo_renderer,
+                                    pp_slidep->data);
+        cairo_renderer_unset_cr (renderer->cairo_renderer);
+        cairo_destroy (cr);
+
+        /*************/
+        cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (renderer->speaker_next));
+        cairo_renderer_set_cr (renderer->cairo_renderer,
+                               cr, clutter_actor_get_width (renderer->speaker_next),
+                               clutter_actor_get_height (renderer->speaker_next));
+        if (pp_slidep->next)
+          cairo_renderer_render_page (renderer->cairo_renderer,
+                                      pp_slidep->next->data);
+        else
+          cairo_renderer_render_page (renderer->cairo_renderer,
+                                      NULL);
+        cairo_renderer_unset_cr (renderer->cairo_renderer);
+        cairo_destroy (cr);
+        /*************/
+        current_slide = pp_slidep;
+    }
+  }
+
+  clutter_actor_set_position (renderer->speaker_prev,
+                              nw * 0.0,
+                              nh * -0.1);
+  clutter_actor_set_position (renderer->speaker_current,
+                              nw * 0.0,
+                              nh * 0.3);
+  clutter_actor_set_position (renderer->speaker_next,
+                              nw * 0.0,
+                              nh * 0.7);
+  clutter_actor_set_position (renderer->speaker_notes,
+                              nw * 0.46,
+                              nh * 0.35);
+  clutter_actor_set_width    (renderer->speaker_notes,
+                              nw * 0.5);
+  return TRUE;
+}
+
 static void
 show_slide (ClutterRenderer *renderer, gboolean backwards)
 {
-  PinPointPoint *point;
+  PinPointPoint    *point;
   ClutterPointData *data;
-  ClutterColor color;
+  ClutterColor      color;
 
   if (!pp_slidep)
     return;
- 
+
+  renderer->slide_start_time = g_timer_elapsed (renderer->timer, NULL);
+
   point = pp_slidep->data;
   data = point->data;
 
@@ -781,28 +1785,14 @@ show_slide (ClutterRenderer *renderer, gboolean backwards)
 
   if (data->background)
     {
-      float bg_x, bg_y, bg_width, bg_height, bg_scale_x, bg_scale_y;
-      if (CLUTTER_IS_RECTANGLE (data->background))
-        {
-          clutter_actor_get_size (renderer->stage, &bg_width, &bg_height);
-          clutter_actor_set_size (data->background, bg_width, bg_height);
-        }
-      else
-        {
-          clutter_actor_get_size (data->background, &bg_width, &bg_height);
-        }
-
-      pp_get_background_position_scale (point,
-                                        clutter_actor_get_width (renderer->stage),
-                                        clutter_actor_get_height (renderer->stage),
-                                        bg_width, bg_height,
-                                        &bg_x, &bg_y, &bg_scale_x, &bg_scale_y);
-
-      clutter_actor_set_scale (data->background, bg_scale_x, bg_scale_y);
-      clutter_actor_set_position (data->background, bg_x, bg_y);
+      pp_clutter_render_adjust_background (renderer, point);
 
 #ifdef USE_CLUTTER_GST
-      if (CLUTTER_GST_IS_VIDEO_TEXTURE (data->background))
+      if (point->bg_type == PP_BG_CAMERA)
+        {
+          gst_element_set_state (data->pipeline, GST_STATE_PLAYING);
+        }
+      else if (CLUTTER_GST_IS_VIDEO_TEXTURE (data->background))
         {
           clutter_media_set_progress (CLUTTER_MEDIA (data->background), 0.0);
           clutter_media_set_playing (CLUTTER_MEDIA (data->background), TRUE);
@@ -831,66 +1821,70 @@ show_slide (ClutterRenderer *renderer, gboolean backwards)
     {
       clutter_actor_animate (renderer->foreground,
                              CLUTTER_LINEAR, 500,
-                             "opacity", 255,
+                             "opacity",      255,
                              NULL);
       clutter_actor_animate (renderer->midground,
                              CLUTTER_LINEAR, 500,
-                             "opacity", 255,
+                             "opacity",      255,
                              NULL);
       clutter_actor_animate (renderer->background,
                              CLUTTER_LINEAR, 500,
-                             "opacity", 255,
+                             "opacity",      255,
                              NULL);
 
-      
       if (point->text && *point->text)
         {
          float text_x, text_y, text_width, text_height, text_scale;
          float shading_x, shading_y, shading_width, shading_height;
 
          clutter_actor_get_size (data->text, &text_width, &text_height);
-         pp_get_text_position_scale (point,
-                                     clutter_actor_get_width (renderer->stage),
-                                     clutter_actor_get_height (renderer->stage),
-                                     text_width, text_height,
-                                     &text_x, &text_y,
-                                     &text_scale);
-         pp_get_shading_position_size (clutter_actor_get_width (renderer->stage),
-                                       clutter_actor_get_height (renderer->stage),
-                                       text_x, text_y,
-                                       text_width, text_height,
-                                       text_scale,
-                                       &shading_x, &shading_y,
-                                       &shading_width, &shading_height);
+
+         pp_get_text_position_scale (
+             point,
+             clutter_actor_get_width (renderer->stage),
+             clutter_actor_get_height (renderer->stage),
+             text_width, text_height,
+             &text_x, &text_y,
+             &text_scale);
+
+         pp_get_shading_position_size (
+             clutter_actor_get_width (renderer->stage),
+             clutter_actor_get_height (renderer->stage),
+             text_x, text_y,
+             text_width, text_height,
+             text_scale,
+             &shading_x, &shading_y,
+             &shading_width, &shading_height);
+
          clutter_color_from_string (&color, point->shading_color);
 
          clutter_actor_animate (data->text,
                                 CLUTTER_EASE_OUT_QUINT, 1000,
-                                "depth", 0.0,
+                                "depth",   0.0,
                                 "scale-x", text_scale,
                                 "scale-y", text_scale,
-                                "x", text_x,
-                                "y", text_y,
+                                "x",       text_x,
+                                "y",       text_y,
                                 NULL);
 
          clutter_actor_animate (renderer->shading,
                 CLUTTER_EASE_OUT_QUINT, 1000,
-                "x", shading_x,
-                "y", shading_y,
+                "x",       shading_x,
+                "y",       shading_y,
                 "opacity", (int)(point->shading_opacity*255),
-                "color", &color,
-                "width", shading_width,
-                "height", shading_height,
+                "color",   &color,
+                "width",   shading_width,
+                "height",  shading_height,
                 NULL);
         }
       else
         {
-           clutter_actor_animate (renderer->shading,
-                  CLUTTER_LINEAR, 500,
-                  "opacity", 0,
-                  "width", 0.0,
-                  "height", 0.0,
-                  NULL);
+          clutter_actor_animate (renderer->shading,
+                 CLUTTER_LINEAR, 500,
+                 "opacity", 0,
+                 "width",   0.0,
+                 "height",  0.0,
+                 NULL);
         }
 
 
@@ -906,29 +1900,37 @@ show_slide (ClutterRenderer *renderer, gboolean backwards)
       /* fade out global group of texts when using a custom .json template */
       clutter_actor_animate (renderer->foreground,
                              CLUTTER_LINEAR, 500,
-                             "opacity", 0,
+                             "opacity",      0,
                              NULL);
       clutter_actor_animate (renderer->midground,
                              CLUTTER_LINEAR, 500,
-                             "opacity", 0,
+                             "opacity",      0,
                              NULL);
       clutter_actor_animate (renderer->background,
                              CLUTTER_LINEAR, 500,
-                             "opacity", 0,
+                             "opacity",      0,
                              NULL);
       if (!data->script)
         {
-          gchar *path = pp_lookup_transition (point->transition);
+          char *path = pp_lookup_transition (point->transition);
           data->script = clutter_script_new ();
           clutter_script_load_from_file (data->script, path, &error);
           g_free (path);
-          data->foreground = CLUTTER_ACTOR (clutter_script_get_object (data->script, "foreground"));
-          data->midground = CLUTTER_ACTOR (clutter_script_get_object (data->script, "midground"));
-          data->background2 = CLUTTER_ACTOR (clutter_script_get_object (data->script, "background"));
-          data->state = CLUTTER_STATE (clutter_script_get_object (data->script, "state"));
-          data->json_slide = CLUTTER_ACTOR (clutter_script_get_object (data->script, "actor"));
-          clutter_container_add_actor (CLUTTER_CONTAINER (renderer->json_layer), data->json_slide);
-          g_signal_connect (data->state, "completed", G_CALLBACK (state_completed), point);
+          data->foreground = CLUTTER_ACTOR (
+              clutter_script_get_object (data->script, "foreground"));
+          data->midground = CLUTTER_ACTOR (
+              clutter_script_get_object (data->script, "midground"));
+          data->background2 = CLUTTER_ACTOR (
+              clutter_script_get_object (data->script, "background"));
+          data->state = CLUTTER_STATE (
+              clutter_script_get_object (data->script, "state"));
+          data->json_slide = CLUTTER_ACTOR (
+              clutter_script_get_object (data->script, "actor"));
+
+          clutter_container_add_actor (CLUTTER_CONTAINER (renderer->json_layer),
+                                       data->json_slide);
+          g_signal_connect (data->state, "completed",
+                            G_CALLBACK (state_completed), point);
           clutter_state_warp_to_state (data->state, "pre");
 
           if (data->background2) /* parmanently steal background */
@@ -936,24 +1938,32 @@ show_slide (ClutterRenderer *renderer, gboolean backwards)
               clutter_actor_reparent (data->background, data->background2);
             }
         }
-          clutter_actor_set_size (data->json_slide, clutter_actor_get_width (renderer->stage),
-                                                    clutter_actor_get_height (renderer->stage));
 
-          clutter_actor_set_size (data->foreground, clutter_actor_get_width (renderer->stage),
-                                                    clutter_actor_get_height (renderer->stage));
+      clutter_actor_set_size (data->json_slide,
+                              clutter_actor_get_width (renderer->stage),
+                              clutter_actor_get_height (renderer->stage));
+
+      clutter_actor_set_size (data->foreground,
+                              clutter_actor_get_width (renderer->stage),
+                              clutter_actor_get_height (renderer->stage));
+
+      clutter_actor_set_size (data->background2,
+                              clutter_actor_get_width (renderer->stage),
+                              clutter_actor_get_height (renderer->stage));
 
-          clutter_actor_set_size (data->background2, clutter_actor_get_width (renderer->stage),
-                                                    clutter_actor_get_height (renderer->stage));
       if (!data->json_slide)
         {
-          g_warning ("failed to load transition %s %s\n", point->transition, error?error->message:"");
+          g_warning ("failed to load transition %s %s\n",
+                     point->transition, error?error->message:"");
           return;
         }
+
       if (data->foreground)
         {
           clutter_actor_reparent (data->text, data->foreground);
         }
-              clutter_actor_set_opacity (data->background, 255);
+
+      clutter_actor_set_opacity (data->background, 255);
 
       {
        float text_x, text_y, text_width, text_height, text_scale;
@@ -966,42 +1976,46 @@ show_slide (ClutterRenderer *renderer, gboolean backwards)
                                    &text_x, &text_y,
                                    &text_scale);
        g_object_set (data->text,
-                     "depth", 0.0,
+                     "depth",   0.0,
                      "scale-x", text_scale,
                      "scale-y", text_scale,
-                     "x", text_x,
-                     "y", text_y,
+                     "x",       text_x,
+                     "y",       text_y,
                      NULL);
 
-
        if (clutter_actor_get_width (data->text) > 1.0)
          {
            ClutterColor color;
            float shading_x, shading_y, shading_width, shading_height;
            clutter_color_from_string (&color, point->shading_color);
 
+           pp_get_shading_position_size (
+                clutter_actor_get_width (renderer->stage),
+                clutter_actor_get_height (renderer->stage),
+                text_x, text_y,
+                text_width, text_height,
+                text_scale,
+                &shading_x, &shading_y,
+                &shading_width, &shading_height);
 
-           pp_get_shading_position_size (clutter_actor_get_width (renderer->stage),
-                                         clutter_actor_get_height (renderer->stage),
-                                         text_x, text_y,
-                                         text_width, text_height,
-                                         text_scale,
-                                         &shading_x, &shading_y,
-                                         &shading_width, &shading_height);
            if (!data->shading)
              {
                data->shading = clutter_rectangle_new_with_color (&black);
-               clutter_container_add_actor (CLUTTER_CONTAINER (data->midground), data->shading);
-               clutter_actor_set_size (data->midground, clutter_actor_get_width (renderer->stage),
-                                                        clutter_actor_get_height (renderer->stage));
+
+               clutter_container_add_actor (
+                   CLUTTER_CONTAINER (data->midground), data->shading);
+               clutter_actor_set_size (data->midground,
+                                    clutter_actor_get_width (renderer->stage),
+                                    clutter_actor_get_height (renderer->stage));
              }
+
            g_object_set (data->shading,
-                  "depth", -0.01,
-                  "x", shading_x,
-                  "y", shading_y,
+                  "depth",  -0.01,
+                  "x",      shading_x,
+                  "y",      shading_y,
                   "opacity", (int)(point->shading_opacity*255),
-                  "color", &color,
-                  "width", shading_width,
+                  "color",  &color,
+                  "width",  shading_width,
                   "height", shading_height,
                   NULL);
          }
@@ -1016,11 +2030,11 @@ show_slide (ClutterRenderer *renderer, gboolean backwards)
 
       if (!backwards)
         clutter_actor_raise_top (data->json_slide);
+
       clutter_actor_show (data->json_slide);
       clutter_state_set_state (data->state, "show");
     }
 
-
   /* render potentially executed commands */
   {
    float text_x, text_y, text_width, text_height;
@@ -1028,27 +2042,29 @@ show_slide (ClutterRenderer *renderer, gboolean backwards)
    clutter_color_from_string (&color, point->text_color);
    g_object_set (renderer->commandline,
                  "font-name", point->font,
-                 "text", point->command?point->command:"",
-                 "color", &color,
+                 "text",      point->command?point->command:"",
+                 "color",     &color,
                  NULL);
+
    color.alpha *= 0.33;
    g_object_set (renderer->commandline,
                  "selection-color", &color,
                  NULL);
 
    clutter_actor_get_size (renderer->commandline, &text_width, &text_height);
+
    if (point->position == CLUTTER_GRAVITY_SOUTH ||
        point->position == CLUTTER_GRAVITY_SOUTH_WEST)
      text_y = clutter_actor_get_height (renderer->stage) * 0.05;
    else
      text_y = clutter_actor_get_height (renderer->stage) * 0.95 - text_height;
-   text_x = clutter_actor_get_width (renderer->stage) * 0.05;
 
+   text_x = clutter_actor_get_width (renderer->stage) * 0.05;
 
    g_object_set (renderer->commandline,
-          "x", text_x,
-          "y", text_y,
-          NULL);
+                 "x", text_x,
+                 "y", text_y,
+                 NULL);
    clutter_actor_animate (renderer->commandline,
           CLUTTER_EASE_OUT_QUINT, 1000,
           "opacity", point->command?(gint)(0xff*0.33):0,
@@ -1056,8 +2072,12 @@ show_slide (ClutterRenderer *renderer, gboolean backwards)
 
    update_commandline_shading (renderer);
   }
-}
 
+  if (renderer->speaker_mode)
+    {
+      update_speaker_screen (renderer);
+    }
+}
 
 static void
 stage_resized (ClutterActor    *actor,
@@ -1065,6 +2085,7 @@ stage_resized (ClutterActor    *actor,
                ClutterRenderer *renderer)
 {
   show_slide (renderer, FALSE); /* redisplay the current slide */
+  update_speaker_screen (renderer);
 }
 
 static guint reload_tag = 0;
@@ -1073,9 +2094,11 @@ static gboolean
 reload (gpointer data)
 {
   ClutterRenderer *renderer = data;
-  char   *text = NULL;
+  char            *text     = NULL;
+
   if (!g_file_get_contents (renderer->path, &text, NULL, NULL))
     g_error ("failed to load slides from %s\n", renderer->path);
+
   renderer->rest_y = STARTPOS;
   pp_parse_slides (PINPOINT_RENDERER (renderer), text);
   g_free (text);
@@ -1093,6 +2116,7 @@ file_changed (GFileMonitor      *monitor,
 {
   if (reload_tag)
     g_source_remove (reload_tag);
+
   reload_tag = g_timeout_add (200, reload, renderer);
 }
openSUSE Build Service is sponsored by