Patch: Xdamage support for Vino



Hi, Mark,

Here is a little patch to add Xdamage support to Vino.  With this, and
if your X server supports the DAMAGE extension, Vino won't have to poll
the screen periodically.  Instead, it will just get XDamageNotify events
and update acoordingly.

I'm pretty confident that the code is working correctly; on my machine
it consumes a lot less CPU than the polling version.  The only part that
needs love is the autoconf-fu.  It is a total hack right now; my auto-fu
is rusty and so I just slapped some pkg-config goodness in there, rather
than doing the right thing.

BTW, the Vino source code is really nice!  I thought it would be a lot
harder to add support for Xdamage, but it turned out to be easy.

Love,

  Federico
? fautogenera
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/vino/ChangeLog,v
retrieving revision 1.7
diff -u -r1.7 ChangeLog
--- ChangeLog	16 Apr 2004 11:36:41 -0000	1.7
+++ ChangeLog	26 May 2004 19:12:20 -0000
@@ -1,3 +1,29 @@
+2004-05-26  Federico Mena Quintero  <federico ximian com>
+
+	* configure.in: Test for the DAMAGE extension.
+
+	* acconfig.h: #undef HAVE_DAMAGE.
+
+	* server/Makefile.am (vino_server_LDADD): Add the XDAMAGE_LIBS.
+
+	* server/vino-fb.c (struct _VinoFBPrivate): Added these fields:
+	xdamage, xdamage_event_base, xdamage_error_base, use_xdamage.
+	(vino_fb_init_polling): New function; moved the polling
+	initialization code over from vino_fb_init_from_screen().
+	(vino_fb_init_from_screen): Call vino_fb_init_xdamage(), or
+	vino_fb_init_polling() if the former fails.
+	(vino_fb_finalize_polling): New function, code moved over from
+	vino_fb_finalize_screen_data().  Also, destroy the timeout here.
+	(vino_fb_finalize_xdamage): New function to get rid of the Damage
+	object.
+	(vino_fb_finalize_screen_data): Call vino_fb_finalize_xdamage() or
+	vino_fb_finalize_polling() as appropriate.
+	(vino_fb_finalize): Don't destroy the timeout here.
+	(copy_image_to_fb_image): New helper function, code moved over
+	from vino_fb_copy_tile().
+	(vino_fb_copy_tile): Use copy_image_to_fb_image().  Removed the
+	bytes_per_pixel argument.
+
 2004-04-16  Mark McLoughlin  <mark skynet ie>
 
 	* configure.in: don't rebuild the jar file by default even when
Index: acconfig.h
===================================================================
RCS file: /cvs/gnome/vino/acconfig.h,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 acconfig.h
--- acconfig.h	5 Feb 2004 08:54:24 -0000	1.1.1.1
+++ acconfig.h	26 May 2004 19:12:20 -0000
@@ -4,3 +4,4 @@
 #undef GETTEXT_PACKAGE
 #undef HAVE_XSHM
 #undef HAVE_XTEST
+#undef HAVE_DAMAGE
Index: configure.in
===================================================================
RCS file: /cvs/gnome/vino/configure.in,v
retrieving revision 1.4
diff -u -r1.4 configure.in
--- configure.in	16 Apr 2004 11:36:41 -0000	1.4
+++ configure.in	26 May 2004 19:12:20 -0000
@@ -49,7 +49,7 @@
    AC_SUBST(DISABLE_DEPRECATED_CFLAGS)
 fi
 
-PKG_CHECK_MODULES(VINO_SERVER, gtk+-x11-2.0 gconf-2.0 libglade-2.0 libgnomeui-2.0)
+PKG_CHECK_MODULES(VINO_SERVER, gtk+-x11-2.0 gconf-2.0 libglade-2.0 libgnomeui-2.0 xext xdamage)
 AC_SUBST(VINO_SERVER_LIBS)
 AC_SUBST(VINO_SERVER_CFLAGS)
 	
@@ -140,13 +140,24 @@
 #
 # Check for XTest extension
 #
-XTEST_LIBS=
-AC_CHECK_HEADER(X11/extensions/XTest.h, [
-    AC_CHECK_LIB(Xtst, XTestQueryExtension, [
-      AC_DEFINE(HAVE_XTEST)
-      XTEST_LIBS="-lXtst"],, $X_LIBS)
-  ])
-AC_SUBST(XTEST_LIBS)
+#XTEST_LIBS=
+#AC_CHECK_HEADER(X11/extensions/XTest.h, [
+#    AC_CHECK_LIB(Xtst, XTestQueryExtension, [
+#      AC_DEFINE(HAVE_XTEST)
+#      XTEST_LIBS="-lXtst"],, $X_LIBS)
+#  ])
+#AC_SUBST(XTEST_LIBS)
+
+#
+# Check for XDAMAGE extension
+#
+#XDAMAGE_LIBS=
+#AC_CHECK_HEADER(X11/extensions/Xdamage.h, [
+#    AC_CHECK_LIB(Xdamage, XDamageQueryExtension, [
+#      AC_DEFINE(HAVE_DAMAGE)
+#      XDAMAGE_LIBS="-lXdamage"],, $X_LIBS)
+#  ],, [#include <X11/Xlib.h>])
+#AC_SUBST(XDAMAGE_LIBS)
 
 
 dnl
Index: server/Makefile.am
===================================================================
RCS file: /cvs/gnome/vino/server/Makefile.am,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 Makefile.am
--- server/Makefile.am	5 Feb 2004 08:54:54 -0000	1.1.1.1
+++ server/Makefile.am	26 May 2004 19:12:20 -0000
@@ -21,9 +21,10 @@
 	$(VINO_SERVER_LIBS) \
 	$(LIBGNUTLS_LIBS) \
 	$(LIBGCRYPT_LIBS) \
-	$(X_LIBS) $(XTEST_LIBS) $(XSHM_LIBS) \
+	$(X_LIBS) $(XSHM_LIBS) \
 	$(top_builddir)/server/libvncserver/libvncserver.la \
 	$(NULL)
+#	$(X_LIBS) $(XTEST_LIBS) $(XSHM_LIBS) $(XDAMAGE_LIBS) \
 
 vino_server_SOURCES = \
 	vino-cursor.c \
Index: server/vino-fb.c
===================================================================
RCS file: /cvs/gnome/vino/server/vino-fb.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 vino-fb.c
--- server/vino-fb.c	5 Feb 2004 08:54:56 -0000	1.1.1.1
+++ server/vino-fb.c	26 May 2004 19:12:20 -0000
@@ -26,7 +26,7 @@
  *     Copyright (C) 2000 heXoNet Support GmbH, D-66424 Homburg.
  *
  */
-
+#define HAVE_DAMAGE
 #include <config.h>
 
 #include "vino-fb.h"
@@ -40,6 +40,9 @@
 #ifdef HAVE_XSHM
 #include <X11/extensions/XShm.h>
 #endif
+#ifdef HAVE_DAMAGE
+#include <X11/extensions/Xdamage.h>
+#endif
 
 #include "vino-util.h"
 
@@ -67,9 +70,17 @@
 
   guint            update_timeout;
 
+#ifdef HAVE_DAMAGE
+  Damage           xdamage;
+  int              xdamage_event_base;
+  int              xdamage_error_base;
+#endif
+
   guint            use_x_shm : 1;
   guint            scanline_is_x_shm_segment : 1;
   guint            tile_is_x_shm_segment : 1;
+
+  guint            use_xdamage : 1;
 };
 
 
@@ -277,16 +288,40 @@
     }
 }
 
+/* Copies a whole image to a region of our fb_image.  The rectangle specifies
+ * the area that the image is supposed to occupy within the fb_image.
+ */
+static void
+copy_image_to_fb_image (VinoFB *vfb, XImage *image, int x, int y, int width, int height)
+{
+  VinoFBPrivate *priv;
+  char *src, *dest;
+  int   bytes_per_pixel;
+  int   src_bytes_per_line, dest_bytes_per_line;
+  int   i;
+
+  priv = vfb->priv;
+
+  src_bytes_per_line = image->bytes_per_line;
+  dest_bytes_per_line = priv->fb_image->bytes_per_line;
+  bytes_per_pixel = priv->fb_image->bits_per_pixel >> 3;
+
+  src = image->data;
+  dest = priv->fb_image->data + y * dest_bytes_per_line + x * bytes_per_pixel;
+
+  for (i = 0; i < height; i++)
+    {
+      memcpy (dest, src, width * bytes_per_pixel);
+
+      src += src_bytes_per_line;
+      dest += dest_bytes_per_line;
+    }
+}
 
 static gboolean
 vino_fb_copy_tile (VinoFB       *vfb,
-		   GdkRectangle *rect,
-		   int           bytes_per_pixel)
+		   GdkRectangle *rect)
 {
-  char *dest;
-  int   bytes_per_line;
-  int   i;
-
   if (!vino_fb_get_image (vfb,
 			  vfb->priv->root_window,
 			  vfb->priv->tile,
@@ -298,18 +333,7 @@
 			  rect->height))
     return FALSE;
 
-  bytes_per_line = vfb->priv->fb_image->bytes_per_line;
-
-  dest = vfb->priv->fb_image->data + (rect->y * bytes_per_line) + (rect->x * bytes_per_pixel);
-  
-  for (i = 0; i < rect->height; i++)
-    {
-      memcpy (dest,
-	      vfb->priv->tile->data + i * bytes_per_pixel * rect->width,
-	      rect->width * bytes_per_pixel);
-
-      dest += bytes_per_line;
-    }
+  copy_image_to_fb_image (vfb, vfb->priv->tile, rect->x, rect->y, rect->width, rect->height);
 
   return TRUE;
 }
@@ -359,7 +383,7 @@
 
 	  dprintf (POLLING, "damage: (%d, %d) (%d x %d)\n", rect.x, rect.y, rect.width, rect.height);
 
-	  if (vino_fb_copy_tile (vfb, &rect, bytes_per_pixel))
+	  if (vino_fb_copy_tile (vfb, &rect))
 	    {
 	      if (!vfb->priv->damage_region)
 		vfb->priv->damage_region = gdk_region_rectangle (&rect);
@@ -412,16 +436,29 @@
   return TRUE;
 }
 
+/* Frees the Damage object */
 static void
-vino_fb_finalize_screen_data (VinoFB *vfb)
+vino_fb_finalize_xdamage (VinoFB *vfb)
 {
-  if (vfb->priv->damage_region)
-    gdk_region_destroy (vfb->priv->damage_region);
-  vfb->priv->damage_region = NULL;
+#ifdef HAVE_DAMAGE
+  VinoFBPrivate *priv;
+  Display *xdisplay;
 
-  if (vfb->priv->fb_image)
-    XDestroyImage (vfb->priv->fb_image);
-  vfb->priv->fb_image = NULL;
+  priv = vfb->priv;
+
+  xdisplay = GDK_DISPLAY_XDISPLAY (gdk_screen_get_display (priv->screen));
+  XDamageDestroy (xdisplay, priv->xdamage);
+  priv->xdamage = None;
+#endif
+}
+
+/* Frees the scanline and tile data */
+static void
+vino_fb_finalize_polling (VinoFB *vfb)
+{
+  if (vfb->priv->update_timeout)
+    g_source_remove (vfb->priv->update_timeout);
+  vfb->priv->update_timeout = 0;
 
   if (vfb->priv->scanline)
     vino_fb_destroy_image (vfb,
@@ -429,7 +466,7 @@
 			   &vfb->priv->scanline_x_shm_info,
 			   vfb->priv->scanline_is_x_shm_segment);
   vfb->priv->scanline = NULL;
-  
+
   if (vfb->priv->tile)
     vino_fb_destroy_image (vfb,
 			   vfb->priv->tile,
@@ -439,6 +476,23 @@
 }
 
 static void
+vino_fb_finalize_screen_data (VinoFB *vfb)
+{
+  if (vfb->priv->damage_region)
+    gdk_region_destroy (vfb->priv->damage_region);
+  vfb->priv->damage_region = NULL;
+
+  if (vfb->priv->fb_image)
+    XDestroyImage (vfb->priv->fb_image);
+  vfb->priv->fb_image = NULL;
+
+  if (vfb->priv->use_xdamage)
+    vino_fb_finalize_xdamage (vfb);
+  else
+    vino_fb_finalize_polling (vfb);
+}
+
+static void
 vino_fb_screen_size_changed (VinoFB    *vfb,
 			     GdkScreen *screen)
 {
@@ -450,43 +504,166 @@
   emit_size_changed (vfb);
 }
 
+#ifdef HAVE_DAMAGE
+
+/* Fetches a rectangle of the screen to update our fb_image buffer */
 static void
-vino_fb_init_from_screen (VinoFB    *vfb,
-			  GdkScreen *screen)
+update_xrectangle (VinoFB *vfb, XRectangle *xrect)
+{
+  VinoFBPrivate *priv;
+  gboolean use_shm;
+  XShmSegmentInfo shm_info;
+  XImage *image;
+
+  priv = vfb->priv;
+
+  use_shm = vino_fb_create_image (vfb, &image, &shm_info,
+				  xrect->width, xrect->height,
+				  priv->fb_image->bits_per_pixel);
+  if (!image)
+    {
+      g_warning (G_STRLOC ": Could not create XImage; buffer will be out of sync");
+      return;
+    }
+
+  if (vino_fb_get_image (vfb, priv->root_window, image, &shm_info, use_shm,
+			 xrect->x, xrect->y, xrect->width, xrect->height))
+    copy_image_to_fb_image (vfb, image, xrect->x, xrect->y, xrect->width, xrect->height);
+  else
+    g_warning (G_STRLOC ": Could not fetch the XImage; buffer will be out of sync");
+
+  vino_fb_destroy_image (vfb, image, &shm_info, use_shm);
+}
+
+/* Adds an XRectangle to our damage_region */
+static void
+add_xrectangle_to_damage_region (VinoFB *vfb, XRectangle *xrect)
+{
+  VinoFBPrivate *priv;
+  GdkRectangle rect;
+
+  rect.x = xrect->x;
+  rect.y = xrect->y;
+  rect.width = xrect->width;
+  rect.height = xrect->height;
+
+  if (priv->damage_region)
+    gdk_region_union_with_rect (priv->damage_region, &rect);
+  else
+    priv->damage_region = gdk_region_rectangle (&rect);
+}
+
+/* Subtracts an XRectangle from the Damage region */
+static void
+subtract_xrectangle_from_damage (VinoFB *vfb, XRectangle *xrect)
 {
+  VinoFBPrivate *priv;
   Display *xdisplay;
+  XserverRegion region;
 
-  g_return_if_fail (screen != NULL);
+  priv = vfb->priv;
 
-  xdisplay = GDK_DISPLAY_XDISPLAY (gdk_screen_get_display (screen));
+  xdisplay = GDK_DISPLAY_XDISPLAY (gdk_screen_get_display (priv->screen));
 
-  vfb->priv->screen      = screen;
-  vfb->priv->root_window = gdk_screen_get_root_window (screen);
+  region = XFixesCreateRegion (xdisplay, xrect, 1);
+  XDamageSubtract (xdisplay, priv->xdamage, region, None);
+  XFixesDestroyRegion (xdisplay, region);
+}
 
-  g_signal_connect_swapped (vfb->priv->screen, "size-changed",
-			    G_CALLBACK (vino_fb_screen_size_changed),
-			    vfb);
+/* Handles an XDamageNotify event by updating our image buffer and damage
+ * region.
+ */
+static void
+handle_damage_notify_event (VinoFB *vfb, XDamageNotifyEvent *event)
+{
+  VinoFBPrivate *priv;
 
-  vfb->priv->fb_image =
-    XGetImage (xdisplay,
-	       GDK_WINDOW_XWINDOW (vfb->priv->root_window),
-	       0, 0,
-	       gdk_screen_get_width  (screen),
-	       gdk_screen_get_height (screen),
-	       AllPlanes,
-	       ZPixmap);
-  if (!vfb->priv->fb_image)
+  priv = vfb->priv;
+
+  g_assert (event->type == priv->xdamage_event_base + XDamageNotify);
+
+  update_xrectangle (vfb, &event->area);
+  add_xrectangle_to_damage_region (vfb, &event->area);
+  subtract_xrectangle_from_damage (vfb, &event->area);
+  emit_damage_notify (vfb);
+}
+
+static GdkFilterReturn
+root_window_filter_cb (GdkXEvent *xevent, GdkEvent *event, gpointer data)
+{
+  VinoFB *vfb;
+  VinoFBPrivate *priv;
+  XEvent *xev;
+
+  vfb = VINO_FB (data);
+  priv = vfb->priv;
+
+  xev = (XEvent *) xevent;
+
+  if (xev->type == priv->xdamage_event_base + XDamageNotify)
     {
-      g_warning (G_STRLOC ": failed to copy frame buffer contents with XGetImage");
-      return;
+      XDamageNotifyEvent *notify;
+
+      notify = (XDamageNotifyEvent *) xev;
+      handle_damage_notify_event (vfb, notify);
+
+      return GDK_FILTER_REMOVE;
     }
 
-  dprintf (POLLING, "Initialized framebuffer contents (%p) for screen %d: %dx%d %dbpp\n",
-	   vfb->priv->fb_image,
-	   gdk_screen_get_number (vfb->priv->screen),
-	   vfb->priv->fb_image->width,
-	   vfb->priv->fb_image->height,
-	   vfb->priv->fb_image->bits_per_pixel);
+  return GDK_FILTER_CONTINUE;
+}
+
+#endif
+
+static void
+vino_fb_init_xdamage (VinoFB *vfb)
+{
+#ifdef HAVE_DAMAGE
+  Display *xdisplay;
+  int major, minor;
+#endif
+  VinoFBPrivate *priv;
+  GdkDisplay *display;
+
+  priv = vfb->priv;
+
+  priv->use_xdamage = FALSE;
+
+  display = gdk_screen_get_display (priv->screen);
+
+#ifdef HAVE_DAMAGE
+  xdisplay = GDK_DISPLAY_XDISPLAY (display);
+
+  if (!XDamageQueryExtension (xdisplay,
+			      &priv->xdamage_event_base,
+			      &priv->xdamage_error_base))
+    return;
+
+  if (!XDamageQueryVersion (xdisplay, &major, &minor) || major != 1)
+    return;
+
+  priv->xdamage = XDamageCreate (xdisplay,
+				 GDK_WINDOW_XWINDOW (priv->root_window),
+				 XDamageReportRawRectangles);
+  if (priv->xdamage == None)
+    return;
+
+  gdk_x11_register_standard_event_type (display, priv->xdamage_event_base, XDamageNumberEvents);
+  gdk_window_add_filter (priv->root_window, root_window_filter_cb, vfb);
+
+  priv->use_xdamage = TRUE;
+#endif
+}
+
+/* Initializes a VinoFB for polling mode. */
+static void
+vino_fb_init_polling (VinoFB *vfb)
+{
+  Display  *xdisplay;
+
+  g_assert (!vfb->priv->use_xdamage);
+
+  xdisplay  = GDK_DISPLAY_XDISPLAY (gdk_screen_get_display (vfb->priv->screen));
 
 #ifdef HAVE_XSHM
   vfb->priv->use_x_shm = XShmQueryExtension (xdisplay) != False;
@@ -496,7 +673,7 @@
     vino_fb_create_image (vfb,
 			  &vfb->priv->scanline,
 			  &vfb->priv->scanline_x_shm_info,
-			  gdk_screen_get_width (screen), 1,
+			  gdk_screen_get_width (vfb->priv->screen), 1,
 			  vfb->priv->fb_image->bits_per_pixel);
   if (!vfb->priv->scanline)
     {
@@ -537,16 +714,56 @@
     g_timeout_add (20, (GSourceFunc) vino_fb_poll_screen, vfb);
 }
 
+static void
+vino_fb_init_from_screen (VinoFB    *vfb,
+			  GdkScreen *screen)
+{
+  Display *xdisplay;
+
+  g_return_if_fail (screen != NULL);
+
+  xdisplay = GDK_DISPLAY_XDISPLAY (gdk_screen_get_display (screen));
+
+  vfb->priv->screen      = screen;
+  vfb->priv->root_window = gdk_screen_get_root_window (screen);
+
+  g_signal_connect_swapped (vfb->priv->screen, "size-changed",
+			    G_CALLBACK (vino_fb_screen_size_changed),
+			    vfb);
+
+  vfb->priv->fb_image =
+    XGetImage (xdisplay,
+	       GDK_WINDOW_XWINDOW (vfb->priv->root_window),
+	       0, 0,
+	       gdk_screen_get_width  (screen),
+	       gdk_screen_get_height (screen),
+	       AllPlanes,
+	       ZPixmap);
+  if (!vfb->priv->fb_image)
+    {
+      g_warning (G_STRLOC ": failed to copy frame buffer contents with XGetImage");
+      return;
+    }
+
+  dprintf (POLLING, "Initialized framebuffer contents (%p) for screen %d: %dx%d %dbpp\n",
+	   vfb->priv->fb_image,
+	   gdk_screen_get_number (vfb->priv->screen),
+	   vfb->priv->fb_image->width,
+	   vfb->priv->fb_image->height,
+	   vfb->priv->fb_image->bits_per_pixel);
+
+  vino_fb_init_xdamage (vfb);
+
+  if (!vfb->priv->use_xdamage)
+    vino_fb_init_polling (vfb);
+}
+
 
 static void
 vino_fb_finalize (GObject *object)
 {
   VinoFB *vfb = VINO_FB (object);
   
-  if (vfb->priv->update_timeout)
-    g_source_remove (vfb->priv->update_timeout);
-  vfb->priv->update_timeout = 0;
-
   vino_fb_finalize_screen_data (vfb);
  
   g_free (vfb->priv);


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