[byzanz] Split recording into its own object



commit 7697bf484847210269bebe129f2bec41dbc4c376
Author: Benjamin Otte <otte gnome org>
Date:   Thu Aug 20 15:30:10 2009 +0200

    Split recording into its own object
    
    move the code that does recording from byzanzsession.c and split it into
    logical objects:
    - ByzanzRecorder
      The base object for recording. It keeps track of the layers it records
      and emits an "image" signal whenever a new image should be recorded.
    - ByzanzLayer
      Base class for the layers of a recording. Supposed to keep track of
      the region of changes to the image it is recording and doing the
      actual rendering. Two layers are implemented:
    - ByzanzLayerWindow
      The bottommost layer. It does the recording of the actual window using
      the XDamage extension.
    - ByzanzLayerCursor
      The topmost layer. Keeps track of the mouse cursor.

 .gitignore              |    2 +
 configure.ac            |    6 +-
 gifenc/gifenc.c         |   16 ++
 gifenc/gifenc.h         |    2 +
 src/Makefile.am         |   21 +++
 src/byzanzlayer.c       |  116 +++++++++++++++
 src/byzanzlayer.h       |   60 ++++++++
 src/byzanzlayercursor.c |  220 ++++++++++++++++++++++++++++
 src/byzanzlayercursor.h |   58 ++++++++
 src/byzanzlayerwindow.c |  162 +++++++++++++++++++++
 src/byzanzlayerwindow.h |   52 +++++++
 src/byzanzmarshal.list  |    1 +
 src/byzanzrecorder.c    |  367 +++++++++++++++++++++++++++++++++++++++++++++++
 src/byzanzrecorder.h    |   76 ++++++++++
 src/byzanzsession.c     |  335 +++++++------------------------------------
 15 files changed, 1208 insertions(+), 286 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 23a0718..c689136 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,4 +44,6 @@ src/ByzanzApplet.server.in
 src/byzanz-applet
 src/byzanz-record
 src/byzanz.schemas
+src/byzanzmarshal.c
+src/byzanzmarshal.h
 
diff --git a/configure.ac b/configure.ac
index 4fa576e..76fe01f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -107,10 +107,8 @@ PKG_CHECK_MODULES(GTHREAD, xdamage >= $XDAMAGE_REQ gthread-2.0 >= $GTHREAD_REQ)
 
 PKG_CHECK_MODULES(APPLET, libpanelapplet-2.0 >= $APPLET_REQ)
 
-AC_PATH_PROG(GCONFTOOL, gconftool-2, no)
-if test x"$GCONFTOOL" = xno; then
-  AC_MSG_ERROR([gconftool-2 executable not found in your path - should be installed with GConf])
-fi
+AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal)
+AC_PATH_PROG(GCONFTOOL, gconftool-2)
 
 AM_GCONF_SOURCE_2
 
diff --git a/gifenc/gifenc.c b/gifenc/gifenc.c
index 27029f5..af80014 100644
--- a/gifenc/gifenc.c
+++ b/gifenc/gifenc.c
@@ -449,6 +449,22 @@ gifenc_free (Gifenc *enc)
   return success;
 }
 
+guint
+gifenc_get_width (Gifenc *gifenc)
+{
+  g_return_val_if_fail (gifenc != NULL, 0);
+
+  return gifenc->width;
+}
+
+guint
+gifenc_get_height (Gifenc *gifenc)
+{
+  g_return_val_if_fail (gifenc != NULL, 0);
+
+  return gifenc->height;
+}
+
 /* Floyd-Steinman factors */
 #define FACTOR0 (23)
 #define FACTOR1 (79)
diff --git a/gifenc/gifenc.h b/gifenc/gifenc.h
index 0b5c602..531e1e6 100644
--- a/gifenc/gifenc.h
+++ b/gifenc/gifenc.h
@@ -88,6 +88,8 @@ gboolean        gifenc_add_image	(Gifenc *		enc,
                                          GError **		error);
 gboolean        gifenc_close            (Gifenc *       	gifenc,
                                          GError **      	error);
+guint           gifenc_get_width        (Gifenc *               gifenc);
+guint           gifenc_get_height       (Gifenc *               gifenc);
 
 void		gifenc_dither_rgb	(guint8 *		target,
 					 guint			target_rowstride,
diff --git a/src/Makefile.am b/src/Makefile.am
index 44a02e9..ba157ae 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -3,7 +3,15 @@ bin_PROGRAMS = byzanz-record
 libexec_PROGRAMS = byzanz-applet
 man_MANS = byzanz-record.1
 
+BUILT_SOURCES = \
+	byzanzmarshal.h \
+	byzanzmarshal.c
+
 noinst_HEADERS = \
+	byzanzlayer.h \
+	byzanzlayercursor.h \
+	byzanzlayerwindow.h \
+	byzanzrecorder.h \
 	byzanzsession.h \
 	byzanzselect.h \
 	paneldropdown.h \
@@ -11,6 +19,11 @@ noinst_HEADERS = \
 	screenshot-utils.h
 
 libbyzanz_la_SOURCES = \
+	byzanzlayer.c \
+	byzanzlayercursor.c \
+	byzanzlayerwindow.c \
+	byzanzmarshal.c \
+	byzanzrecorder.c \
 	byzanzsession.c \
 	byzanzselect.c
 
@@ -33,6 +46,13 @@ byzanz_applet_SOURCES = \
 byzanz_applet_CFLAGS = $(APPLET_CFLAGS) -I$(top_srcdir)/gifenc
 byzanz_applet_LDADD = $(APPLET_LIBS) ./libbyzanz.la
 
+byzanzmarshal.h: byzanzmarshal.list
+	$(GLIB_GENMARSHAL) --prefix=byzanz_marshal $(srcdir)/byzanzmarshal.list --header > byzanzmarshal.h
+
+byzanzmarshal.c: byzanzmarshal.list
+	(echo "#include \"byzanzmarshal.h\""; \
+	 $(GLIB_GENMARSHAL) --prefix=byzanz_marshal $(srcdir)/byzanzmarshal.list --body) > byzanzmarshal.c
+
 
 schemasdir   = @GCONF_SCHEMA_FILE_DIR@
 schemas_in_files = byzanz.schemas.in
@@ -58,6 +78,7 @@ ui_DATA = byzanzapplet.xml
 CLEANFILES = $(server_in_files) $(server_DATA) $(schemas_DATA)
 
 EXTRA_DIST = \
+	byzanzmarshal.list \
 	ByzanzApplet.server.in.in \
 	$(man_MANS) \
 	$(ui_DATA) \
diff --git a/src/byzanzlayer.c b/src/byzanzlayer.c
new file mode 100644
index 0000000..b99378c
--- /dev/null
+++ b/src/byzanzlayer.c
@@ -0,0 +1,116 @@
+/* desktop session recorder
+ * Copyright (C) 2009 Benjamin Otte <otte gnome org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "byzanzlayer.h"
+
+#include <gdk/gdkx.h>
+
+#include "byzanzmarshal.h"
+
+enum {
+  PROP_0,
+  PROP_RECORDER,
+};
+
+G_DEFINE_ABSTRACT_TYPE (ByzanzLayer, byzanz_layer, G_TYPE_OBJECT)
+
+static void
+byzanz_layer_set_property (GObject *object, guint param_id, const GValue *value, 
+    GParamSpec * pspec)
+{
+  ByzanzLayer *layer = BYZANZ_LAYER (object);
+
+  switch (param_id) {
+    case PROP_RECORDER:
+      layer->recorder = g_value_get_object (value);
+      g_assert (layer->recorder != NULL);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+      break;
+  }
+}
+
+static void
+byzanz_layer_get_property (GObject *object, guint param_id, GValue *value, 
+    GParamSpec * pspec)
+{
+  ByzanzLayer *layer = BYZANZ_LAYER (object);
+
+  switch (param_id) {
+    case PROP_RECORDER:
+      g_value_set_object (value, layer->recorder);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+      break;
+  }
+}
+
+static void
+byzanz_layer_constructed (GObject *object)
+{
+  ByzanzLayer *layer = BYZANZ_LAYER (object);
+
+  byzanz_layer_invalidate (layer);
+
+  if (G_OBJECT_CLASS (byzanz_layer_parent_class)->constructed)
+    G_OBJECT_CLASS (byzanz_layer_parent_class)->constructed (object);
+}
+
+static void
+byzanz_layer_finalize (GObject *object)
+{
+  //ByzanzLayer *layer = BYZANZ_LAYER (object);
+
+  G_OBJECT_CLASS (byzanz_layer_parent_class)->finalize (object);
+}
+
+static void
+byzanz_layer_class_init (ByzanzLayerClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = byzanz_layer_get_property;
+  object_class->set_property = byzanz_layer_set_property;
+  object_class->finalize = byzanz_layer_finalize;
+  object_class->constructed = byzanz_layer_constructed;
+
+  g_object_class_install_property (object_class, PROP_RECORDER,
+      g_param_spec_object ("recorder", "recorder", "recorder that manages us",
+	  BYZANZ_TYPE_RECORDER, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+byzanz_layer_init (ByzanzLayer *layer)
+{
+}
+
+void
+byzanz_layer_invalidate (ByzanzLayer *layer)
+{
+  g_return_if_fail (BYZANZ_IS_LAYER (layer));
+
+  byzanz_recorder_queue_snapshot (layer->recorder);
+}
+
diff --git a/src/byzanzlayer.h b/src/byzanzlayer.h
new file mode 100644
index 0000000..163f816
--- /dev/null
+++ b/src/byzanzlayer.h
@@ -0,0 +1,60 @@
+/* desktop session recorder
+ * Copyright (C) 2009 Benjamin Otte <otte gnome org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gdk/gdk.h>
+#include "byzanzrecorder.h"
+
+#ifndef __HAVE_BYZANZ_LAYER_H__
+#define __HAVE_BYZANZ_LAYER_H__
+
+typedef struct _ByzanzLayer ByzanzLayer;
+typedef struct _ByzanzLayerClass ByzanzLayerClass;
+
+#define BYZANZ_TYPE_LAYER                    (byzanz_layer_get_type())
+#define BYZANZ_IS_LAYER(obj)                 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BYZANZ_TYPE_LAYER))
+#define BYZANZ_IS_LAYER_CLASS(klass)         (G_TYPE_CHECK_CLASS_TYPE ((klass), BYZANZ_TYPE_LAYER))
+#define BYZANZ_LAYER(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), BYZANZ_TYPE_LAYER, ByzanzLayer))
+#define BYZANZ_LAYER_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), BYZANZ_TYPE_LAYER, ByzanzLayerClass))
+#define BYZANZ_LAYER_GET_CLASS(obj)          (G_TYPE_INSTANCE_GET_CLASS ((obj), BYZANZ_TYPE_LAYER, ByzanzLayerClass))
+
+struct _ByzanzLayer {
+  GObject		object;
+
+  /*< protected >*/
+  ByzanzRecorder *      recorder;               /* the recorder we are recording from (not keeping a reference to it) */
+};
+
+struct _ByzanzLayerClass {
+  GObjectClass		object_class;
+
+  gboolean              (* event)                       (ByzanzLayer *          layer,
+                                                         GdkXEvent *            event);
+  GdkRegion *           (* snapshot)                    (ByzanzLayer *          layer);
+  void                  (* render)                      (ByzanzLayer *          layer,
+                                                         cairo_t *              cr);
+};
+
+GType		        byzanz_layer_get_type	        (void) G_GNUC_CONST;
+
+
+/* protected API for subclasses */
+void                    byzanz_layer_invalidate         (ByzanzLayer *          layer);
+
+
+#endif /* __HAVE_BYZANZ_LAYER_H__ */
diff --git a/src/byzanzlayercursor.c b/src/byzanzlayercursor.c
new file mode 100644
index 0000000..a23abfe
--- /dev/null
+++ b/src/byzanzlayercursor.c
@@ -0,0 +1,220 @@
+/* desktop session recorder
+ * Copyright (C) 2009 Benjamin Otte <otte gnome org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "byzanzlayercursor.h"
+
+#include <gdk/gdkx.h>
+
+G_DEFINE_TYPE (ByzanzLayerCursor, byzanz_layer_cursor, BYZANZ_TYPE_LAYER)
+
+static void
+byzanz_layer_cursor_read_cursor (ByzanzLayerCursor *clayer)
+{
+  Display *dpy = gdk_x11_drawable_get_xdisplay (BYZANZ_LAYER (clayer)->recorder->window);
+
+  clayer->cursor_next = XFixesGetCursorImage (dpy);
+  if (clayer->cursor_next)
+    g_hash_table_insert (clayer->cursors, clayer->cursor_next, clayer->cursor_next);
+}
+
+static gboolean
+byzanz_layer_cursor_event (ByzanzLayer * layer,
+                           GdkXEvent *   gdkxevent)
+{
+  ByzanzLayerCursor *clayer = BYZANZ_LAYER_CURSOR (layer);
+  XFixesCursorNotifyEvent *event = gdkxevent;
+
+  if (event->type == layer->recorder->fixes_event_base + XFixesCursorNotify) {
+    XFixesCursorImage hack;
+
+    hack.cursor_serial = event->cursor_serial;
+    clayer->cursor_next = g_hash_table_lookup (clayer->cursors, &hack);
+    if (clayer->cursor_next == NULL)
+      byzanz_layer_cursor_read_cursor (clayer);
+    if (clayer->cursor_next != clayer->cursor)
+      byzanz_layer_invalidate (layer);
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+static gboolean
+byzanz_layer_cursor_poll (gpointer data)
+{
+  ByzanzLayerCursor *clayer = data;
+  int x, y;
+  
+  gdk_window_get_pointer (BYZANZ_LAYER (clayer)->recorder->window, &x, &y, NULL);
+  if (x == clayer->cursor_x &&
+      y == clayer->cursor_y)
+    return TRUE;
+
+  clayer->poll_source = 0;
+  byzanz_layer_invalidate (BYZANZ_LAYER (clayer));
+  return FALSE;
+}
+
+static void
+byzanz_layer_cursor_setup_poll (ByzanzLayerCursor *clayer)
+{
+  if (clayer->poll_source != 0)
+    return;
+
+  /* FIXME: Is 10ms ok or is it too much? */
+  clayer->poll_source = g_timeout_add (10, byzanz_layer_cursor_poll, clayer);
+}
+
+static void
+byzanz_recorder_invalidate_cursor (GdkRegion *region, XFixesCursorImage *cursor, int x, int y)
+{
+  GdkRectangle cursor_rect;
+
+  if (cursor == NULL)
+    return;
+
+  cursor_rect.x = x - cursor->xhot;
+  cursor_rect.y = y - cursor->yhot;
+  cursor_rect.width = cursor->width;
+  cursor_rect.height = cursor->height;
+
+  gdk_region_union_with_rect (region, &cursor_rect);
+}
+
+static GdkRegion *
+byzanz_layer_cursor_snapshot (ByzanzLayer *layer)
+{
+  ByzanzLayerCursor *clayer = BYZANZ_LAYER_CURSOR (layer);
+  GdkRegion *region, *area;
+  int x, y;
+  
+  gdk_window_get_pointer (layer->recorder->window, &x, &y, NULL);
+  if (x == clayer->cursor_x &&
+      y == clayer->cursor_y &&
+      clayer->cursor_next == clayer->cursor)
+    return NULL;
+
+  region = gdk_region_new ();
+  byzanz_recorder_invalidate_cursor (region, clayer->cursor, clayer->cursor_x, clayer->cursor_y);
+  byzanz_recorder_invalidate_cursor (region, clayer->cursor_next, x, y);
+  area = gdk_region_rectangle (&layer->recorder->area);
+  gdk_region_intersect (region, area);
+  gdk_region_destroy (area);
+
+  clayer->cursor = clayer->cursor_next;
+  clayer->cursor_x = x;
+  clayer->cursor_y = y;
+  byzanz_layer_cursor_setup_poll (clayer);
+
+  return region;
+}
+
+static void
+byzanz_layer_cursor_render (ByzanzLayer *layer,
+                            cairo_t *    cr)
+{
+  ByzanzLayerCursor *clayer = BYZANZ_LAYER_CURSOR (layer);
+  XFixesCursorImage *cursor = clayer->cursor;
+  cairo_surface_t *cursor_surface;
+
+  if (clayer->cursor == NULL)
+    return;
+
+  cursor_surface = cairo_image_surface_create_for_data ((guchar *) cursor->pixels,
+      CAIRO_FORMAT_ARGB32, cursor->width, cursor->height, cursor->width * 4);
+  
+  cairo_save (cr);
+  cairo_translate (cr, clayer->cursor_x, clayer->cursor_y);
+  cairo_set_source_surface (cr, cursor_surface, -(double) cursor->xhot, -(double) cursor->yhot);
+  cairo_paint (cr);
+  cairo_restore (cr);
+
+  cairo_surface_destroy (cursor_surface);
+}
+
+static void
+byzanz_layer_cursor_finalize (GObject *object)
+{
+  ByzanzLayerCursor *clayer = BYZANZ_LAYER_CURSOR (object);
+  GdkWindow *window = BYZANZ_LAYER (object)->recorder->window;
+  Display *dpy = gdk_x11_drawable_get_xdisplay (window);
+
+  XFixesSelectCursorInput (dpy, gdk_x11_drawable_get_xid (window), 0);
+
+  g_hash_table_destroy (clayer->cursors);
+
+  if (clayer->poll_source != 0) {
+    g_source_remove (clayer->poll_source);
+  }
+
+  G_OBJECT_CLASS (byzanz_layer_cursor_parent_class)->finalize (object);
+}
+
+static void
+byzanz_layer_cursor_constructed (GObject *object)
+{
+  ByzanzLayerCursor *clayer = BYZANZ_LAYER_CURSOR (object);
+  GdkWindow *window = BYZANZ_LAYER (object)->recorder->window;
+  Display *dpy = gdk_x11_drawable_get_xdisplay (window);
+
+  XFixesSelectCursorInput (dpy, gdk_x11_drawable_get_xid (window), XFixesDisplayCursorNotifyMask);
+  byzanz_layer_cursor_read_cursor (clayer);
+  byzanz_layer_cursor_setup_poll (clayer);
+
+  G_OBJECT_CLASS (byzanz_layer_cursor_parent_class)->constructed (object);
+}
+
+static void
+byzanz_layer_cursor_class_init (ByzanzLayerCursorClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  ByzanzLayerClass *layer_class = BYZANZ_LAYER_CLASS (klass);
+
+  object_class->constructed = byzanz_layer_cursor_constructed;
+  object_class->finalize = byzanz_layer_cursor_finalize;
+
+  layer_class->event = byzanz_layer_cursor_event;
+  layer_class->snapshot = byzanz_layer_cursor_snapshot;
+  layer_class->render = byzanz_layer_cursor_render;
+}
+
+static guint
+byzanz_cursor_hash (gconstpointer key)
+{
+  return (guint) ((const XFixesCursorImage *) key)->cursor_serial;
+}
+
+static gboolean
+byzanz_cursor_equal (gconstpointer c1, gconstpointer c2)
+{
+  return ((const XFixesCursorImage *) c1)->cursor_serial == 
+    ((const XFixesCursorImage *) c2)->cursor_serial;
+}
+
+static void
+byzanz_layer_cursor_init (ByzanzLayerCursor *clayer)
+{
+  clayer->cursors = g_hash_table_new_full (byzanz_cursor_hash,
+      byzanz_cursor_equal, NULL, (GDestroyNotify) XFree);
+}
+
diff --git a/src/byzanzlayercursor.h b/src/byzanzlayercursor.h
new file mode 100644
index 0000000..5873a64
--- /dev/null
+++ b/src/byzanzlayercursor.h
@@ -0,0 +1,58 @@
+/* desktop session recorder
+ * Copyright (C) 2009 Benjamin Otte <otte gnome org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "byzanzlayer.h"
+
+#include <X11/extensions/Xdamage.h>
+
+#ifndef __HAVE_BYZANZ_LAYER_CURSOR_H__
+#define __HAVE_BYZANZ_LAYER_CURSOR_H__
+
+typedef struct _ByzanzLayerCursor ByzanzLayerCursor;
+typedef struct _ByzanzLayerCursorClass ByzanzLayerCursorClass;
+
+#define BYZANZ_TYPE_LAYER_CURSOR                    (byzanz_layer_cursor_get_type())
+#define BYZANZ_IS_LAYER_CURSOR(obj)                 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BYZANZ_TYPE_LAYER_CURSOR))
+#define BYZANZ_IS_LAYER_CURSOR_CLASS(klass)         (G_TYPE_CHECK_CLASS_TYPE ((klass), BYZANZ_TYPE_LAYER_CURSOR))
+#define BYZANZ_LAYER_CURSOR(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), BYZANZ_TYPE_LAYER_CURSOR, ByzanzLayerCursor))
+#define BYZANZ_LAYER_CURSOR_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), BYZANZ_TYPE_LAYER_CURSOR, ByzanzLayerCursorClass))
+#define BYZANZ_LAYER_CURSOR_GET_CLASS(obj)          (G_TYPE_INSTANCE_GET_CLASS ((obj), BYZANZ_TYPE_LAYER_CURSOR, ByzanzLayerCursorClass))
+
+struct _ByzanzLayerCursor {
+  ByzanzLayer           layer;
+
+  XFixesCursorImage *   cursor_next;            /* current active cursor */
+
+  XFixesCursorImage *   cursor;                 /* last recorded cursor */
+  int                   cursor_x;               /* last recorded X position of cursor */
+  int                   cursor_y;               /* last recorded Y position of cursor */
+
+  GHashTable *          cursors;                /* all cursors we know about already */
+
+  guint                 poll_source;            /* source used for querying mouse position */
+};
+
+struct _ByzanzLayerCursorClass {
+  ByzanzLayerClass	layer_class;
+};
+
+GType		        byzanz_layer_cursor_get_type	        (void) G_GNUC_CONST;
+
+
+#endif /* __HAVE_BYZANZ_LAYER_CURSOR_H__ */
diff --git a/src/byzanzlayerwindow.c b/src/byzanzlayerwindow.c
new file mode 100644
index 0000000..9095eb8
--- /dev/null
+++ b/src/byzanzlayerwindow.c
@@ -0,0 +1,162 @@
+/* desktop session recorder
+ * Copyright (C) 2009 Benjamin Otte <otte gnome org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "byzanzlayerwindow.h"
+
+#include <gdk/gdkx.h>
+
+G_DEFINE_TYPE (ByzanzLayerWindow, byzanz_layer_window, BYZANZ_TYPE_LAYER)
+
+static gboolean
+byzanz_layer_window_event (ByzanzLayer * layer,
+                           GdkXEvent *   gdkxevent)
+{
+  XDamageNotifyEvent *event = (XDamageNotifyEvent *) gdkxevent;
+  ByzanzLayerWindow *wlayer = BYZANZ_LAYER_WINDOW (layer);
+
+  if (event->type == layer->recorder->damage_event_base + XDamageNotify && 
+      event->damage == wlayer->damage) {
+    GdkRectangle rect;
+
+    rect.x = event->area.x;
+    rect.y = event->area.y;
+    rect.width = event->area.width;
+    rect.height = event->area.height;
+    if (gdk_rectangle_intersect (&rect, &layer->recorder->area, &rect)) {
+      gdk_region_union_with_rect (wlayer->invalid, &rect);
+      byzanz_layer_invalidate (layer);
+    }
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+static XserverRegion
+byzanz_server_region_from_gdk (Display *dpy, GdkRegion *source)
+{
+  XserverRegion dest;
+  XRectangle *dest_rects;
+  GdkRectangle *source_rects;
+  int n_rectangles, i;
+
+  gdk_region_get_rectangles (source, &source_rects, &n_rectangles);
+  g_assert (n_rectangles);
+  dest_rects = g_new (XRectangle, n_rectangles);
+  for (i = 0; i < n_rectangles; i++) {
+    dest_rects[i].x = source_rects[i].x;
+    dest_rects[i].y = source_rects[i].y;
+    dest_rects[i].width = source_rects[i].width;
+    dest_rects[i].height = source_rects[i].height;
+  }
+  dest = XFixesCreateRegion (dpy, dest_rects, n_rectangles);
+  g_free (dest_rects);
+  g_free (source_rects);
+
+  return dest;
+}
+
+static GdkRegion *
+byzanz_layer_window_snapshot (ByzanzLayer *layer)
+{
+  Display *dpy = gdk_x11_drawable_get_xdisplay (layer->recorder->window);
+  ByzanzLayerWindow *wlayer = BYZANZ_LAYER_WINDOW (layer);
+  XserverRegion reg;
+  GdkRegion *region;
+
+  if (gdk_region_empty (wlayer->invalid))
+    return NULL;
+
+  reg = byzanz_server_region_from_gdk (dpy, wlayer->invalid);
+  XDamageSubtract (dpy, wlayer->damage, reg, reg);
+  XFixesDestroyRegion (dpy, reg);
+
+  region = wlayer->invalid;
+  wlayer->invalid = gdk_region_new ();
+  return region;
+}
+
+static void
+byzanz_cairo_set_source_window (cairo_t *cr, GdkWindow *window, double x, double y)
+{
+  cairo_t *tmp;
+
+  tmp = gdk_cairo_create (window);
+  cairo_set_source_surface (cr, cairo_get_target (tmp), x, y);
+  cairo_destroy (tmp);
+}
+
+static void
+byzanz_layer_window_render (ByzanzLayer *layer,
+                            cairo_t *    cr)
+{
+  byzanz_cairo_set_source_window (cr, layer->recorder->window, 0, 0);
+  cairo_paint (cr);
+}
+
+static void
+byzanz_layer_window_finalize (GObject *object)
+{
+  Display *dpy = gdk_x11_drawable_get_xdisplay (BYZANZ_LAYER (object)->recorder->window);
+  ByzanzLayerWindow *wlayer = BYZANZ_LAYER_WINDOW (object);
+
+  XDamageDestroy (dpy, wlayer->damage);
+  gdk_region_destroy (wlayer->invalid);
+
+  G_OBJECT_CLASS (byzanz_layer_window_parent_class)->finalize (object);
+}
+
+static void
+byzanz_layer_window_constructed (GObject *object)
+{
+  ByzanzLayer *layer = BYZANZ_LAYER (object);
+  GdkWindow *window = layer->recorder->window;
+  Display *dpy = gdk_x11_drawable_get_xdisplay (window);
+  ByzanzLayerWindow *wlayer = BYZANZ_LAYER_WINDOW (object);
+
+  wlayer->damage = XDamageCreate (dpy, gdk_x11_drawable_get_xid (window), XDamageReportDeltaRectangles);
+  gdk_region_union_with_rect (wlayer->invalid, &layer->recorder->area);
+
+  G_OBJECT_CLASS (byzanz_layer_window_parent_class)->constructed (object);
+}
+
+static void
+byzanz_layer_window_class_init (ByzanzLayerWindowClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  ByzanzLayerClass *layer_class = BYZANZ_LAYER_CLASS (klass);
+
+  object_class->finalize = byzanz_layer_window_finalize;
+  object_class->constructed = byzanz_layer_window_constructed;
+
+  layer_class->event = byzanz_layer_window_event;
+  layer_class->snapshot = byzanz_layer_window_snapshot;
+  layer_class->render = byzanz_layer_window_render;
+}
+
+static void
+byzanz_layer_window_init (ByzanzLayerWindow *wlayer)
+{
+  wlayer->invalid = gdk_region_new ();
+}
+
diff --git a/src/byzanzlayerwindow.h b/src/byzanzlayerwindow.h
new file mode 100644
index 0000000..1620434
--- /dev/null
+++ b/src/byzanzlayerwindow.h
@@ -0,0 +1,52 @@
+/* desktop session recorder
+ * Copyright (C) 2009 Benjamin Otte <otte gnome org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "byzanzlayer.h"
+
+#include <X11/extensions/Xdamage.h>
+#include <X11/extensions/Xfixes.h>
+
+#ifndef __HAVE_BYZANZ_LAYER_WINDOW_H__
+#define __HAVE_BYZANZ_LAYER_WINDOW_H__
+
+typedef struct _ByzanzLayerWindow ByzanzLayerWindow;
+typedef struct _ByzanzLayerWindowClass ByzanzLayerWindowClass;
+
+#define BYZANZ_TYPE_LAYER_WINDOW                    (byzanz_layer_window_get_type())
+#define BYZANZ_IS_LAYER_WINDOW(obj)                 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BYZANZ_TYPE_LAYER_WINDOW))
+#define BYZANZ_IS_LAYER_WINDOW_CLASS(klass)         (G_TYPE_CHECK_CLASS_TYPE ((klass), BYZANZ_TYPE_LAYER_WINDOW))
+#define BYZANZ_LAYER_WINDOW(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), BYZANZ_TYPE_LAYER_WINDOW, ByzanzLayerWindow))
+#define BYZANZ_LAYER_WINDOW_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), BYZANZ_TYPE_LAYER_WINDOW, ByzanzLayerWindowClass))
+#define BYZANZ_LAYER_WINDOW_GET_CLASS(obj)          (G_TYPE_INSTANCE_GET_CLASS ((obj), BYZANZ_TYPE_LAYER_WINDOW, ByzanzLayerWindowClass))
+
+struct _ByzanzLayerWindow {
+  ByzanzLayer           layer;
+
+  GdkRegion *           invalid;                /* TRUE if we need to repaint */
+  Damage		damage;		        /* the Damage object */
+};
+
+struct _ByzanzLayerWindowClass {
+  ByzanzLayerClass	layer_class;
+};
+
+GType		        byzanz_layer_window_get_type	        (void) G_GNUC_CONST;
+
+
+#endif /* __HAVE_BYZANZ_LAYER_WINDOW_H__ */
diff --git a/src/byzanzmarshal.list b/src/byzanzmarshal.list
new file mode 100644
index 0000000..19d9a83
--- /dev/null
+++ b/src/byzanzmarshal.list
@@ -0,0 +1 @@
+VOID:POINTER,POINTER,POINTER
diff --git a/src/byzanzrecorder.c b/src/byzanzrecorder.c
new file mode 100644
index 0000000..691898d
--- /dev/null
+++ b/src/byzanzrecorder.c
@@ -0,0 +1,367 @@
+/* desktop session recorder
+ * Copyright (C) 2009 Benjamin Otte <otte gnome org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "byzanzrecorder.h"
+
+#include <gdk/gdkx.h>
+
+#include <X11/extensions/Xdamage.h>
+#include <X11/extensions/Xfixes.h>
+
+#include "byzanzlayer.h"
+#include "byzanzlayercursor.h"
+#include "byzanzlayerwindow.h"
+#include "byzanzmarshal.h"
+
+enum {
+  PROP_0,
+  PROP_WINDOW,
+  PROP_AREA,
+  PROP_RECORDING,
+};
+
+enum {
+  IMAGE,
+  LAST_SIGNAL
+};
+
+G_DEFINE_TYPE (ByzanzRecorder, byzanz_recorder, G_TYPE_OBJECT)
+static guint signals[LAST_SIGNAL] = { 0, };
+
+static GdkRegion *
+byzanz_recorder_get_invalid_region (ByzanzRecorder *recorder)
+{
+  GdkRegion *invalid, *layer_invalid;
+  GSequenceIter *iter;
+
+  invalid = gdk_region_new ();
+  for (iter = g_sequence_get_begin_iter (recorder->layers);
+       !g_sequence_iter_is_end (iter);
+       iter = g_sequence_iter_next (iter)) {
+    ByzanzLayer *layer = g_sequence_get (iter);
+    ByzanzLayerClass *klass = BYZANZ_LAYER_GET_CLASS (layer);
+
+    layer_invalid = klass->snapshot (layer);
+    if (layer_invalid) {
+      gdk_region_union (invalid, layer_invalid);
+      gdk_region_destroy (layer_invalid);
+    }
+  }
+
+  return invalid;
+}
+
+static cairo_surface_t *
+byzanz_recorder_create_snapshot (ByzanzRecorder *recorder, const GdkRegion *invalid)
+{
+  GdkRectangle extents;
+  cairo_surface_t *surface;
+  cairo_t *cr;
+  GSequenceIter *iter;
+  
+  gdk_region_get_clipbox (invalid, &extents);
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, extents.width, extents.height);
+  cairo_surface_set_device_offset (surface, -extents.x, -extents.y);
+
+  cr = cairo_create (surface);
+  gdk_cairo_region (cr, invalid);
+  cairo_clip (cr);
+
+  for (iter = g_sequence_get_begin_iter (recorder->layers);
+       !g_sequence_iter_is_end (iter);
+       iter = g_sequence_iter_next (iter)) {
+    ByzanzLayer *layer = g_sequence_get (iter);
+    ByzanzLayerClass *klass = BYZANZ_LAYER_GET_CLASS (layer);
+
+    cairo_save (cr);
+    klass->render (layer, cr);
+    cairo_restore (cr);
+  }
+
+  cairo_destroy (cr);
+
+  /* adjust device offset here - the layers work in GdkScreen coordinates, the rest
+   * of the code works in coordinates realtive to the passed in area. */
+  cairo_surface_set_device_offset (surface,
+      recorder->area.x - extents.x, recorder->area.y - extents.y);
+  return surface;
+}
+
+static gboolean byzanz_recorder_snapshot (ByzanzRecorder *recorder);
+static gboolean
+byzanz_recorder_next_image (gpointer data)
+{
+  ByzanzRecorder *recorder = data;
+
+  recorder->next_image_source = 0;
+  byzanz_recorder_snapshot (recorder);
+  return FALSE;
+}
+
+static gboolean
+byzanz_recorder_snapshot (ByzanzRecorder *recorder)
+{
+  cairo_surface_t *surface;
+  GdkRegion *invalid;
+  GTimeVal tv;
+
+  if (!recorder->recording)
+    return FALSE;
+
+  if (recorder->next_image_source != 0)
+    return FALSE;
+
+  invalid = byzanz_recorder_get_invalid_region (recorder);
+  if (gdk_region_empty (invalid)) {
+    gdk_region_destroy (invalid);
+    return FALSE;
+  }
+
+  surface = byzanz_recorder_create_snapshot (recorder, invalid);
+  g_get_current_time (&tv);
+  gdk_region_offset (invalid, -recorder->area.x, -recorder->area.y);
+
+  g_signal_emit (recorder, signals[IMAGE], 0, surface, invalid, &tv);
+
+  cairo_surface_destroy (surface);
+  gdk_region_destroy (invalid);
+
+  recorder->next_image_source = gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE,
+      BYZANZ_RECORDER_FRAME_RATE_MS, byzanz_recorder_next_image, recorder, NULL);
+
+  return TRUE;
+}
+
+static GdkFilterReturn
+byzanz_recorder_filter_events (GdkXEvent *xevent, GdkEvent *event, gpointer data)
+{
+  ByzanzRecorder *recorder = data;
+  GSequenceIter *iter;
+  gboolean handled;
+
+  if (event->any.window != recorder->window)
+    return GDK_FILTER_CONTINUE;
+
+  handled = FALSE;
+  for (iter = g_sequence_get_begin_iter (recorder->layers);
+       !g_sequence_iter_is_end (iter);
+       iter = g_sequence_iter_next (iter)) {
+    ByzanzLayer *layer = g_sequence_get (iter);
+    ByzanzLayerClass *klass = BYZANZ_LAYER_GET_CLASS (layer);
+
+    handled |= klass->event (layer, xevent);
+  }
+
+  return handled ? GDK_FILTER_REMOVE : GDK_FILTER_CONTINUE;
+}
+
+static gboolean
+byzanz_recorder_prepare (ByzanzRecorder *recorder)
+{
+  GdkDisplay *display;
+  Display *dpy;
+
+  display = gdk_display_get_default ();
+  dpy = gdk_x11_display_get_xdisplay (display);
+
+  if (!XDamageQueryExtension (dpy, &recorder->damage_event_base, &recorder->damage_error_base) ||
+      !XFixesQueryExtension (dpy, &recorder->fixes_event_base, &recorder->fixes_error_base))
+    return FALSE;
+  gdk_x11_register_standard_event_type (display, 
+      recorder->damage_event_base + XDamageNotify, 1);
+  gdk_x11_register_standard_event_type (display,
+      recorder->fixes_event_base + XFixesCursorNotify, 1);
+
+  return TRUE;
+}
+
+static void
+byzanz_recorder_set_window (ByzanzRecorder *recorder, GdkWindow *window)
+{
+  g_assert (window != NULL);
+
+  if (!byzanz_recorder_prepare (recorder)) {
+    g_assert_not_reached ();
+  }
+
+  recorder->window = g_object_ref (window);
+  gdk_window_add_filter (window, byzanz_recorder_filter_events, recorder);
+}
+
+static void
+byzanz_recorder_set_property (GObject *object, guint param_id, const GValue *value, 
+    GParamSpec * pspec)
+{
+  ByzanzRecorder *recorder = BYZANZ_RECORDER (object);
+
+  switch (param_id) {
+    case PROP_WINDOW:
+      byzanz_recorder_set_window (recorder, g_value_get_object (value));
+      break;
+    case PROP_AREA:
+      recorder->area = *(GdkRectangle *) g_value_get_boxed (value);
+      break;
+    case PROP_RECORDING:
+      byzanz_recorder_set_recording (recorder, g_value_get_boolean (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+      break;
+  }
+}
+
+static void
+byzanz_recorder_get_property (GObject *object, guint param_id, GValue *value, 
+    GParamSpec * pspec)
+{
+  ByzanzRecorder *recorder = BYZANZ_RECORDER (object);
+
+  switch (param_id) {
+    case PROP_WINDOW:
+      g_value_set_object (value, recorder->window);
+      break;
+    case PROP_AREA:
+      g_value_set_boxed (value, &recorder->area);
+      break;
+    case PROP_RECORDING:
+      g_value_set_boolean (value, byzanz_recorder_get_recording (recorder));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+      break;
+  }
+}
+
+static void
+byzanz_recorder_constructed (GObject *object)
+{
+  ByzanzRecorder *recorder = BYZANZ_RECORDER (object);
+
+  g_sequence_append (recorder->layers,
+      g_object_new (BYZANZ_TYPE_LAYER_WINDOW, "recorder", recorder, NULL));
+  g_sequence_append (recorder->layers,
+      g_object_new (BYZANZ_TYPE_LAYER_CURSOR, "recorder", recorder, NULL));
+
+  if (G_OBJECT_CLASS (byzanz_recorder_parent_class)->constructed)
+    G_OBJECT_CLASS (byzanz_recorder_parent_class)->constructed (object);
+}
+
+static void
+byzanz_recorder_dispose (GObject *object)
+{
+  ByzanzRecorder *recorder = BYZANZ_RECORDER (object);
+
+  g_sequence_free (recorder->layers);
+
+  G_OBJECT_CLASS (byzanz_recorder_parent_class)->dispose (object);
+}
+
+static void
+byzanz_recorder_finalize (GObject *object)
+{
+  ByzanzRecorder *recorder = BYZANZ_RECORDER (object);
+
+  if (recorder->next_image_source)
+    g_source_remove (recorder->next_image_source);
+
+  gdk_window_remove_filter (recorder->window, 
+      byzanz_recorder_filter_events, recorder);
+  g_object_unref (recorder->window);
+
+  G_OBJECT_CLASS (byzanz_recorder_parent_class)->finalize (object);
+}
+
+static void
+byzanz_recorder_class_init (ByzanzRecorderClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = byzanz_recorder_get_property;
+  object_class->set_property = byzanz_recorder_set_property;
+  object_class->dispose = byzanz_recorder_dispose;
+  object_class->finalize = byzanz_recorder_finalize;
+  object_class->constructed = byzanz_recorder_constructed;
+
+  g_object_class_install_property (object_class, PROP_WINDOW,
+      g_param_spec_object ("window", "window", "window to record from",
+	  GDK_TYPE_WINDOW, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class, PROP_AREA,
+      g_param_spec_boxed ("area", "area", "recorded area",
+	  GDK_TYPE_RECTANGLE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class, PROP_RECORDING,
+      g_param_spec_boolean ("recording", "recording", "TRUE when actively recording",
+	  FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  signals[IMAGE] = g_signal_new ("image", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+      G_STRUCT_OFFSET (ByzanzRecorderClass, image), NULL, NULL, 
+      byzanz_marshal_VOID__POINTER_POINTER_POINTER, G_TYPE_NONE, 3, 
+      G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER);
+}
+
+static void
+byzanz_recorder_init (ByzanzRecorder *recorder)
+{
+  recorder->layers = g_sequence_new (g_object_unref);
+}
+
+ByzanzRecorder *
+byzanz_recorder_new (GdkWindow *window, GdkRectangle *area)
+{
+  g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
+  g_return_val_if_fail (area != NULL, NULL);
+
+  return g_object_new (BYZANZ_TYPE_RECORDER, "window", window, "area", area, NULL);
+}
+
+void
+byzanz_recorder_set_recording (ByzanzRecorder *recorder, gboolean recording)
+{
+  g_return_if_fail (BYZANZ_IS_RECORDER (recorder));
+
+  if (recorder->recording == recording)
+    return;
+
+  recorder->recording = recording;
+  if (recording)
+    byzanz_recorder_snapshot (recorder);
+}
+
+gboolean
+byzanz_recorder_get_recording (ByzanzRecorder *recorder)
+{
+  g_return_val_if_fail (BYZANZ_IS_RECORDER (recorder), FALSE);
+
+  return recorder->recording;
+}
+
+void
+byzanz_recorder_queue_snapshot (ByzanzRecorder *recorder)
+{
+  g_return_if_fail (BYZANZ_IS_RECORDER (recorder));
+
+  if (recorder->next_image_source == 0) {
+    recorder->next_image_source = gdk_threads_add_idle_full (G_PRIORITY_HIGH_IDLE,
+        byzanz_recorder_next_image, recorder, NULL);
+  }
+}
+
diff --git a/src/byzanzrecorder.h b/src/byzanzrecorder.h
new file mode 100644
index 0000000..d7e6cb7
--- /dev/null
+++ b/src/byzanzrecorder.h
@@ -0,0 +1,76 @@
+/* desktop session recorder
+ * Copyright (C) 2009 Benjamin Otte <otte gnome org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gdk/gdk.h>
+
+#ifndef __HAVE_BYZANZ_RECORDER_H__
+#define __HAVE_BYZANZ_RECORDER_H__
+
+typedef struct _ByzanzRecorder ByzanzRecorder;
+typedef struct _ByzanzRecorderClass ByzanzRecorderClass;
+
+/* 25 fps */
+#define BYZANZ_RECORDER_FRAME_RATE_MS 1000 / 25
+
+#define BYZANZ_TYPE_RECORDER                    (byzanz_recorder_get_type())
+#define BYZANZ_IS_RECORDER(obj)                 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BYZANZ_TYPE_RECORDER))
+#define BYZANZ_IS_RECORDER_CLASS(klass)         (G_TYPE_CHECK_CLASS_TYPE ((klass), BYZANZ_TYPE_RECORDER))
+#define BYZANZ_RECORDER(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), BYZANZ_TYPE_RECORDER, ByzanzRecorder))
+#define BYZANZ_RECORDER_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), BYZANZ_TYPE_RECORDER, ByzanzRecorderClass))
+#define BYZANZ_RECORDER_GET_CLASS(obj)          (G_TYPE_INSTANCE_GET_CLASS ((obj), BYZANZ_TYPE_RECORDER, ByzanzRecorderClass))
+
+struct _ByzanzRecorder {
+  GObject		object;
+
+  GdkWindow *           window;                 /* window we are recording from */
+  GdkRectangle          area;                   /* area of window that we record */
+  gboolean              recording;              /* wether we should be recording now */
+
+  int                   damage_event_base;      /* base event for Damage extension */
+  int                   damage_error_base;      /* base error for Damage extension */
+  int                   fixes_event_base;       /* base event for Fixes extension */
+  int                   fixes_error_base;       /* base error for Fixes extension */
+
+  GSequence *           layers;                 /* sequence of ByzanzLayer, ordered by layer depth */
+
+  guint                 next_image_source;      /* timer that fires when enough time after the last frame has elapsed */
+};
+
+struct _ByzanzRecorderClass {
+  GObjectClass		object_class;
+
+  void                  (* image)                       (ByzanzRecorder *       recorder,
+                                                         cairo_surface_t *      surface,
+                                                         const GdkRegion *      region,
+                                                         const GTimeVal *       tv);
+};
+
+GType		        byzanz_recorder_get_type	(void) G_GNUC_CONST;
+
+ByzanzRecorder *	byzanz_recorder_new		(GdkWindow *		window,
+							 GdkRectangle *		area);
+
+void                    byzanz_recorder_set_recording   (ByzanzRecorder *       recorder,
+                                                         gboolean               recording);
+gboolean                byzanz_recorder_get_recording   (ByzanzRecorder *       recorder);
+
+void                    byzanz_recorder_queue_snapshot  (ByzanzRecorder *       recorder);
+
+
+#endif /* __HAVE_BYZANZ_RECORDER_H__ */
diff --git a/src/byzanzsession.c b/src/byzanzsession.c
index d4de0f3..46ad6cf 100644
--- a/src/byzanzsession.c
+++ b/src/byzanzsession.c
@@ -36,6 +36,8 @@
 #include <gdk/gdkx.h>
 #include <X11/extensions/Xdamage.h>
 #include <X11/extensions/Xfixes.h>
+
+#include "byzanzrecorder.h"
 #include "gifenc.h"
 
 typedef enum {
@@ -60,22 +62,11 @@ typedef struct {
 struct _ByzanzSession {
   /*< private >*/
   /* set by user - accessed ALSO by thread */
-  GdkRectangle		area;		/* area of the screen we record */
   gboolean		loop;		/* wether the resulting gif should loop */
   guint			frame_duration;	/* minimum frame duration in msecs */
   /* state */
   SessionState		state;		/* state the session is in */
-  guint			timeout;	/* signal id for timeout */
-  GdkWindow *		window;		/* root window we record */
-  Damage		damage;		/* the Damage object */
-  XserverRegion		damaged;	/* the damaged region */
-  XserverRegion		tmp_region;	/* temporary region to construct the damaged region */
-  GHashTable *		cursors;	/* all the cursors */
-  XFixesCursorImage * 	cursor;		/* current cursor */
-  gint			cursor_x;	/* last rendered x position of cursor */
-  gint			cursor_y;	/* last rendered y position of cursor */
-  GdkRectangle		cursor_area;	/* area occupied by cursor */
-  GdkRegion *		region;		/* the region we need to record next time */
+  ByzanzRecorder *      recorder;       /* the recorder in use */
   GThread *		encoder;	/* encoding thread */
   /* accessed ALSO by thread */
   gint			encoder_running;/* TRUE while the encoder is running */
@@ -87,29 +78,10 @@ struct _ByzanzSession {
   guint8 *		data_full;    	/* palettized data of full image to compare additions to */
   GdkRectangle		relevant_data;	/* relevant area to encode */
 };
-#define IS_RECORDING_CURSOR(rec) ((rec)->cursors != NULL)
-
-/* XDamage needs these */
-static int dmg_event_base = 0;
-static int dmg_error_base = 0;
-/* XFixes needs these */
-static int fixes_event_base = 0;
-static int fixes_error_base = 0;
-    
 
 /*** JOB FUNCTIONS ***/
 
 static void
-byzanz_cairo_set_source_window (cairo_t *cr, GdkWindow *window, double x, double y)
-{
-  cairo_t *tmp;
-
-  tmp = gdk_cairo_create (window);
-  cairo_set_source_surface (cr, cairo_get_target (tmp), x, y);
-  cairo_destroy (tmp);
-}
-
-static void
 session_job_free (SessionJob *job)
 {
   if (job->image)
@@ -122,8 +94,8 @@ session_job_free (SessionJob *job)
 
 /* UGH: This function takes ownership of region, but only if a job could be created */
 static SessionJob *
-session_job_new (ByzanzSession *rec, SessionJobType type, 
-    const GTimeVal *tv, GdkRegion *region)
+session_job_new (ByzanzSession *rec, SessionJobType type, cairo_surface_t *surface,
+    const GTimeVal *tv, const GdkRegion *region)
 {
   SessionJob *job;
 
@@ -132,23 +104,11 @@ session_job_new (ByzanzSession *rec, SessionJobType type,
   if (tv)
     job->tv = *tv;
   job->type = type;
-  job->region = region;
-  if (region != NULL) {
-    cairo_t *cr;
-    job->image = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
-        rec->area.width, rec->area.height);
-    if (type == SESSION_JOB_ENCODE) {
-      Display *dpy = gdk_x11_drawable_get_xdisplay (rec->window);
-      XDamageSubtract (dpy, rec->damage, rec->damaged, rec->damaged);
-      XFixesSubtractRegion (dpy, rec->damaged, rec->damaged, rec->damaged);
-    }
-    cr = cairo_create (job->image);
-    byzanz_cairo_set_source_window (cr, rec->window, -rec->area.x, -rec->area.y);
-    gdk_region_offset (region, -rec->area.x, -rec->area.y);
-    gdk_cairo_region (cr, region);
-    cairo_paint (cr);
-    cairo_destroy (cr);
-  }
+  if (region)
+    job->region = gdk_region_copy (region);
+  if (surface)
+    job->image = cairo_surface_reference (surface);
+
   return job;
 }
 
@@ -159,24 +119,29 @@ byzanz_session_dither_region (ByzanzSession *rec, GdkRegion *region, cairo_surfa
 {
   GdkRectangle *rects;
   GdkRegion *rev;
-  int i, line, nrects;
+  int i, line, nrects, xoffset, yoffset;
   guint8 transparent;
-  guint stride;
-  gpointer mem;
+  guint width, stride;
+  guint8 *mem;
   GdkRectangle area;
+  double xod, yod;
   
+  width = gifenc_get_width (rec->gifenc);
   transparent = gifenc_palette_get_alpha_index (rec->gifenc->palette);
   gdk_region_get_clipbox (region, &rec->relevant_data);
   /* dither changed pixels */
   gdk_region_get_rectangles (region, &rects, &nrects);
   rev = gdk_region_new ();
   stride = cairo_image_surface_get_stride (surface);
+  cairo_surface_get_device_offset (surface, &xod, &yod);
+  xoffset = xod;
+  yoffset = yod;
   for (i = 0; i < nrects; i++) {
-    mem = cairo_image_surface_get_data (surface) + rects[i].x * 4
-      + rects[i].y * stride;
+    mem = cairo_image_surface_get_data (surface) + (rects[i].x + xoffset) * 4
+      + (rects[i].y + yoffset) * stride;
     if (gifenc_dither_rgb_with_full_image (
-	rec->data + rec->area.width * rects[i].y + rects[i].x, rec->area.width, 
-	rec->data_full + rec->area.width * rects[i].y + rects[i].x, rec->area.width, 
+	rec->data + width * rects[i].y + rects[i].x, width, 
+	rec->data_full + width * rects[i].y + rects[i].x, width, 
 	rec->gifenc->palette, mem, rects[i].width, rects[i].height, stride, &area)) {
       area.x += rects[i].x;
       area.y += rects[i].y;
@@ -195,7 +160,7 @@ byzanz_session_dither_region (ByzanzSession *rec, GdkRegion *region, cairo_surfa
   gdk_region_get_rectangles (rev, &rects, &nrects);
   for (i = 0; i < nrects; i++) {
     for (line = 0; line < rects[i].height; line++) {
-      memset (rec->data + rects[i].x + rec->area.width * (rects[i].y + line), 
+      memset (rec->data + rects[i].x + width * (rects[i].y + line), 
 	  transparent, rects[i].width);
     }
   }
@@ -208,8 +173,11 @@ static void
 byzanz_session_add_image (ByzanzSession *rec, const GTimeVal *tv)
 {
   glong msecs;
+  guint width;
+
+  width = gifenc_get_width (rec->gifenc);
   if (rec->data == NULL) {
-    guint count = rec->area.width * rec->area.height;
+    guint count = width * gifenc_get_height (rec->gifenc);
     rec->data = g_malloc (count);
     rec->data_full = g_malloc (count);
     memset (rec->data_full, 
@@ -226,8 +194,8 @@ byzanz_session_add_image (ByzanzSession *rec, const GTimeVal *tv)
   if (rec->relevant_data.width > 0 && rec->relevant_data.height > 0) {
     gifenc_add_image (rec->gifenc, rec->relevant_data.x, rec->relevant_data.y, 
 	rec->relevant_data.width, rec->relevant_data.height, msecs,
-	rec->data + rec->area.width * rec->relevant_data.y + rec->relevant_data.x,
-	rec->area.width, NULL);
+	rec->data + width * rec->relevant_data.y + rec->relevant_data.x,
+	width, NULL);
     rec->current = *tv;
   }
 }
@@ -238,7 +206,8 @@ byzanz_session_quantize (ByzanzSession *rec, cairo_surface_t *image)
   GifencPalette *palette;
 
   palette = gifenc_quantize_image (cairo_image_surface_get_data (image),
-      rec->area.width, rec->area.height, cairo_image_surface_get_stride (image), TRUE, 255);
+      cairo_image_surface_get_width (image), cairo_image_surface_get_height (image),
+      cairo_image_surface_get_stride (image), TRUE, 255);
   
   gifenc_initialize (rec->gifenc, palette, rec->loop, NULL);
 }
@@ -298,160 +267,6 @@ byzanz_session_run_encoder (gpointer data)
 /*** MAIN FUNCTIONS ***/
 
 static void
-render_cursor_to_image (cairo_surface_t *image, XFixesCursorImage *cursor, gint x, gint y)
-{
-  cairo_surface_t *cursor_surface;
-  cairo_t *cr;
-
-  cursor_surface = cairo_image_surface_create_for_data ((guchar *) cursor->pixels,
-      CAIRO_FORMAT_ARGB32, cursor->width, cursor->height, cursor->width * 4);
-  cr = cairo_create (image);
-  
-  cairo_translate (cr, x, y);
-  cairo_set_source_surface (cr, cursor_surface, -(double) cursor->xhot, -(double) cursor->yhot);
-  cairo_paint (cr);
-
-  cairo_destroy (cr);
-  cairo_surface_destroy (cursor_surface);
-}
-    
-static guint
-cursor_hash (gconstpointer key)
-{
-  return (guint) ((const XFixesCursorImage *) key)->cursor_serial;
-}
-
-static gboolean
-cursor_equal (gconstpointer c1, gconstpointer c2)
-{
-  return ((const XFixesCursorImage *) c1)->cursor_serial == 
-    ((const XFixesCursorImage *) c2)->cursor_serial;
-}
-
-static gboolean byzanz_session_timeout_cb (gpointer session);
-static void
-byzanz_session_queue_image (ByzanzSession *rec)
-{
-  SessionJob *job;
-  GTimeVal tv;
-  gboolean render_cursor = FALSE;
-  
-  g_get_current_time (&tv);
-  if (IS_RECORDING_CURSOR (rec)) {
-    GdkRectangle cursor_rect;
-    gdk_region_union_with_rect (rec->region, &rec->cursor_area);
-    cursor_rect.x = rec->cursor_x - rec->cursor->xhot;
-    cursor_rect.y = rec->cursor_y - rec->cursor->yhot;
-    cursor_rect.width = rec->cursor->width;
-    cursor_rect.height = rec->cursor->height;
-    render_cursor = gdk_rectangle_intersect (&cursor_rect, &rec->area, &rec->cursor_area);
-    gdk_region_union_with_rect (rec->region, &rec->cursor_area);
-  } else {
-    g_assert (!gdk_region_empty (rec->region));
-  }
-  
-  if (!gdk_region_empty (rec->region)) {
-    job = session_job_new (rec, SESSION_JOB_ENCODE, &tv, rec->region);
-    if (job) {
-      if (render_cursor) 
-	render_cursor_to_image (job->image, rec->cursor, 
-	    rec->cursor_x - rec->area.x, rec->cursor_y - rec->area.y);
-      g_async_queue_push (rec->jobs, job);
-      //g_print ("pushing ENCODE\n");
-      rec->region = gdk_region_new ();
-    }
-  }
-  
-  if (rec->timeout == 0) {
-    rec->timeout = g_timeout_add (rec->frame_duration, 
-	byzanz_session_timeout_cb, rec);
-  }
-}
-
-static gboolean
-byzanz_session_timeout_cb (gpointer session)
-{
-  ByzanzSession *rec = session;
-
-  if (IS_RECORDING_CURSOR (rec)) {
-    gint x, y;
-    gdk_window_get_pointer (rec->window, &x, &y, NULL);
-    if (x == rec->cursor_x && y == rec->cursor_y && gdk_region_empty (rec->region))
-      return TRUE;
-    rec->cursor_x = x;
-    rec->cursor_y = y;
-  } else {
-    if (gdk_region_empty (rec->region)) {
-      rec->timeout = 0;
-      return FALSE;
-    }
-  }
-  byzanz_session_queue_image (rec);
-  return TRUE;
-}
-
-static gboolean
-byzanz_session_idle_cb (gpointer session)
-{
-  ByzanzSession *rec = session;
-
-  g_assert (!gdk_region_empty (rec->region));
-
-  rec->timeout = 0;
-  byzanz_session_queue_image (rec);
-  return FALSE;
-}
-
-static GdkFilterReturn
-byzanz_session_filter_events (GdkXEvent *xevent, GdkEvent *event, gpointer data)
-{
-  ByzanzSession *rec = data;
-  XDamageNotifyEvent *dev = (XDamageNotifyEvent *) xevent;
-  Display *dpy;
-
-  if (event->any.window != rec->window)
-    return GDK_FILTER_CONTINUE;
-
-  dev = (XDamageNotifyEvent *) xevent;
-  dpy = gdk_x11_display_get_xdisplay (gdk_display_get_default ());
-
-  if (dev->type == dmg_event_base + XDamageNotify && 
-      dev->damage == rec->damage) {
-    GdkRectangle rect;
-
-    rect.x = dev->area.x;
-    rect.y = dev->area.y;
-    rect.width = dev->area.width;
-    rect.height = dev->area.height;
-    XFixesSetRegion (dpy, rec->tmp_region, &dev->area, 1);
-    XFixesUnionRegion (dpy, rec->damaged, rec->damaged, rec->tmp_region);
-    //XDamageSubtract (dpy, rec->damage, rec->damaged, None);
-    //g_print ("-> %d %d %d %d\n", rect.x, rect.y, rect.width, rect.height);
-    if (gdk_rectangle_intersect (&rect, &rec->area, &rect)) {
-      gdk_region_union_with_rect (rec->region, &rect);
-      if (rec->timeout == 0) 
-	rec->timeout = g_idle_add_full (G_PRIORITY_DEFAULT,
-	    byzanz_session_idle_cb, rec, NULL);
-    }
-    return GDK_FILTER_REMOVE;
-  } else if (dev->type == fixes_event_base + XFixesCursorNotify) {
-    XFixesCursorNotifyEvent *cevent = xevent;
-    XFixesCursorImage hack;
-
-    g_assert (IS_RECORDING_CURSOR (rec));
-    hack.cursor_serial = cevent->cursor_serial;
-    rec->cursor = g_hash_table_lookup (rec->cursors, &hack);
-    if (rec->cursor == NULL) {
-      rec->cursor = XFixesGetCursorImage (dpy);
-      if (rec->cursor)
-	g_hash_table_insert (rec->cursors, rec->cursor, rec->cursor);
-    }
-    return GDK_FILTER_REMOVE;
-  }
-  return GDK_FILTER_CONTINUE;
-}
-
-static void
 byzanz_session_state_advance (ByzanzSession *session)
 {
   switch (session->state) {
@@ -502,6 +317,17 @@ byzanz_session_new (const gchar *filename, GdkWindow *window, GdkRectangle *area
   return byzanz_session_new_fd (fd, window, area, loop, record_cursor);
 }
 
+static void
+byzanz_session_recorder_image_cb (ByzanzRecorder *  recorder,
+                                  cairo_surface_t * surface,
+                                  const GdkRegion * region,
+                                  const GTimeVal *  tv,
+                                  ByzanzSession *   session)
+{
+  SessionJob *job = session_job_new (session, SESSION_JOB_ENCODE, surface, tv, region);
+  g_async_queue_push (session->jobs, job);
+}
+
 static gboolean
 session_gifenc_write (gpointer closure, const guchar *data, gsize len, GError **error)
 {
@@ -539,7 +365,6 @@ byzanz_session_new_fd (gint fd, GdkWindow *window, GdkRectangle *area,
     gboolean loop, gboolean record_cursor)
 {
   ByzanzSession *session;
-  Display *dpy;
   GdkRectangle root_rect;
 
   g_return_val_if_fail (area->x >= 0, NULL);
@@ -547,32 +372,18 @@ byzanz_session_new_fd (gint fd, GdkWindow *window, GdkRectangle *area,
   g_return_val_if_fail (area->width > 0, NULL);
   g_return_val_if_fail (area->height > 0, NULL);
   
-  dpy = gdk_x11_display_get_xdisplay (gdk_display_get_default ());
-  if (dmg_event_base == 0) {
-    if (!XDamageQueryExtension (dpy, &dmg_event_base, &dmg_error_base) ||
-	!XFixesQueryExtension (dpy, &fixes_event_base, &fixes_error_base))
-      return NULL;
-    gdk_x11_register_standard_event_type (gdk_display_get_default (), 
-	dmg_event_base + XDamageNotify, 1);
-    gdk_x11_register_standard_event_type (gdk_display_get_default (), 
-	fixes_event_base + XFixesCursorNotify, 1);
-  }
-  
   session = g_new0 (ByzanzSession, 1);
 
   /* set user properties */
-  session->area = *area;
   session->loop = loop;
   session->frame_duration = 1000 / 25;
   
   /* prepare thread first, so we can easily error out on failure */
-  session->window = window;
-  g_object_ref (window);
   root_rect.x = root_rect.y = 0;
-  gdk_drawable_get_size (session->window,
+  gdk_drawable_get_size (window,
       &root_rect.width, &root_rect.height);
-  gdk_rectangle_intersect (&session->area, &root_rect, &session->area);
-  session->gifenc = gifenc_new (session->area.width, session->area.height, 
+  gdk_rectangle_intersect (area, &root_rect, &root_rect);
+  session->gifenc = gifenc_new (root_rect.width, root_rect.height, 
       session_gifenc_write, GINT_TO_POINTER (fd), session_gifenc_close);
   if (!session->gifenc) {
     g_free (session);
@@ -589,12 +400,9 @@ byzanz_session_new_fd (gint fd, GdkWindow *window, GdkRectangle *area,
     return NULL;
   }
 
-  /* do setup work */
-  session->damaged = XFixesCreateRegion (dpy, 0, 0);
-  session->tmp_region = XFixesCreateRegion (dpy, 0, 0);
-  if (record_cursor)
-    session->cursors = g_hash_table_new_full (cursor_hash, cursor_equal, 
-      NULL, (GDestroyNotify) XFree);
+  session->recorder = byzanz_recorder_new (window, &root_rect);
+  g_signal_connect (session->recorder, "image", 
+      G_CALLBACK (byzanz_session_recorder_image_cb), session);
 
   session->state = SESSION_STATE_CREATED;
   return session;
@@ -603,26 +411,10 @@ byzanz_session_new_fd (gint fd, GdkWindow *window, GdkRectangle *area,
 void
 byzanz_session_start (ByzanzSession *rec)
 {
-  Display *dpy;
-
   g_return_if_fail (BYZANZ_IS_SESSION (rec));
   g_return_if_fail (rec->state == SESSION_STATE_CREATED);
 
-  dpy = gdk_x11_display_get_xdisplay (gdk_display_get_default ());
-  rec->region = gdk_region_rectangle (&rec->area);
-  gdk_window_add_filter (rec->window, 
-      byzanz_session_filter_events, rec);
-  rec->damage = XDamageCreate (dpy, GDK_DRAWABLE_XID (rec->window), 
-      XDamageReportDeltaRectangles);
-  if (rec->cursors) {
-    XFixesSelectCursorInput (dpy, GDK_DRAWABLE_XID (rec->window),
-	XFixesDisplayCursorNotifyMask);
-    rec->cursor = XFixesGetCursorImage (dpy);
-    if (rec->cursor)
-      g_hash_table_insert (rec->cursors, rec->cursor, rec->cursor);
-    gdk_window_get_pointer (rec->window, &rec->cursor_x, &rec->cursor_y, NULL);
-  }
-  /* byzanz_session_queue_image (rec); - we'll get a damage event anyway */
+  byzanz_recorder_set_recording (rec->recorder, TRUE);
   
   rec->state = SESSION_STATE_RECORDING;
 }
@@ -632,37 +424,23 @@ byzanz_session_stop (ByzanzSession *rec)
 {
   GTimeVal tv;
   SessionJob *job;
-  Display *dpy;
 
   g_return_if_fail (BYZANZ_IS_SESSION (rec));
   g_return_if_fail (rec->state == SESSION_STATE_RECORDING);
 
   /* byzanz_session_queue_image (rec); - useless because last image would have a 0 time */
   g_get_current_time (&tv);
-  job = session_job_new (rec, SESSION_JOB_QUIT, &tv, NULL);
+  job = session_job_new (rec, SESSION_JOB_QUIT, NULL, &tv, NULL);
   g_async_queue_push (rec->jobs, job);
-  //g_print ("pushing QUIT\n");
-  gdk_window_remove_filter (rec->window, 
-      byzanz_session_filter_events, rec);
-  if (rec->timeout != 0) {
-    if (!g_source_remove (rec->timeout))
-      g_assert_not_reached ();
-    rec->timeout = 0;
-  }
-  dpy = gdk_x11_display_get_xdisplay (gdk_display_get_default ());
-  XDamageDestroy (dpy, rec->damage);
-  if (IS_RECORDING_CURSOR (rec))
-    XFixesSelectCursorInput (dpy, GDK_DRAWABLE_XID (rec->window),
-	0);
   
+  byzanz_recorder_set_recording (rec->recorder, FALSE);
+
   rec->state = SESSION_STATE_STOPPED;
 }
 
 void
 byzanz_session_destroy (ByzanzSession *rec)
 {
-  Display *dpy;
-
   g_return_if_fail (BYZANZ_IS_SESSION (rec));
 
   while (rec->state != SESSION_STATE_ERROR &&
@@ -672,15 +450,8 @@ byzanz_session_destroy (ByzanzSession *rec)
   if (g_thread_join (rec->encoder) != rec)
     g_assert_not_reached ();
 
-  dpy = gdk_x11_display_get_xdisplay (gdk_display_get_default ());
-  XFixesDestroyRegion (dpy, rec->damaged);
-  XFixesDestroyRegion (dpy, rec->tmp_region);
-  gdk_region_destroy (rec->region);
-  if (IS_RECORDING_CURSOR (rec))
-    g_hash_table_destroy (rec->cursors);
-
   gifenc_free (rec->gifenc);
-  g_object_unref (rec->window);
+  g_object_unref (rec->recorder);
 
   g_assert (g_async_queue_length (rec->jobs) == 0);
   g_async_queue_unref (rec->jobs);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]