gtk+ r20122 - in trunk: . gdk gdk/x11 gtk tests



Author: timj
Date: Wed May 21 19:04:24 2008
New Revision: 20122
URL: http://svn.gnome.org/viewvc/gtk+?rev=20122&view=rev

Log:
2008-03-18 10:49:20  Tim Janik  <timj imendio com>

	* Applied pixmap redirection patch by Alexander Larsson with
	various updates from:
	Bug 318807 â Offscreen windows and window redirection.


	Updates:

	* updated docs to mention "Since 2.16".

	* tests/testgtk.c: fixed snapshooting pixmap leak.
	convert pixmap to pixbuf after snapshooting, to compensate for different
	bit depths (occurs when snapshooting ARGB visuals and displaying the
	pixmap in an RGB visual).

	* gdk/gdkwindow.[hc]: made GdkWindowRedirect private.

	* gdk/gdkwindow.c: removed damage idle handler, there's no aparent
	need for it. enqueue damage notification as GDK_DAMAGE events
	for each painting redirection at the start of the event queue.
	consider windows with a redirection fully visible when invalidating,
	and when updating from backing store. cleaned up stale variables.

	* gdk/gdkevents.c: added _gdk_event_queue_prepend().

	* gtk/gtkwidget.c: fixed coordinates for !NO_WINDOW widgets in
	gtk_widget_get_snapshot; this fixes garbage snap offsets for gammacurve,
	tree, drawingarea, text, handlebox, etc.
	clip the redirected window hierarchy to window sizes, the visible
	rectangles don't need to be taken into account here.
	extended snapshooting docs to recommend gdk_pixbuf_get_from_drawable()
	in case pixmap visuals could mismatch.

	* gdk/x11/gdkwindow-x11.c: removed _gdk_windowing_window_get_visible_rect().


	Base patch:

	* tests/testgtk.c: add a "Snapshot" test to demonstrate snapshooting
	of possibly obscured widgets into an offscreen pixmap.

	* gtk/gtkwidget.[hc]: add GtkWidget::damage-event signal, add
	gtk_widget_get_snapshot() to render a widget's contents to a GdkPixmap.

	* gtk/gtkmain.c: dispatch GDK_DAMAGE events.

	* gdk/gdkwindow.c: moved outer gdk_window_new() and gdk_window_reparent()
	implementations here, adapted them to propagate redirects to child windows.
	gdk_window_end_paint(): copy repainted window contents to redirection pixmap,
	clipped to visible region. queue GDK_DAMAGE event delivery.
	gdk_window_redirect_to_drawable(): install window painting redirection.
	gdk_window_remove_redirection(): remove previously installed redirection.

	* gdk/x11/gdkwindow-x11.c: added _gdk_windowing_window_get_visible_rect(),
	renamed _gdk_window_new() and _gdk_window_reparent().

	* gdk/gdkwindow.h: added GdkWindowRedirect* to GdkWindowObject, export
	gdk_window_redirect_to_drawable() and gdk_window_remove_redirection().

	* gdk/gdkevents.h: added GDK_DAMAGE event type.

	* gdk/gdkevents.c: extract time and state from GDK_DAMAGE events.

	* gdk/gdkinternals.h: added internal prototypes.




Modified:
   trunk/ChangeLog
   trunk/gdk/gdk.symbols
   trunk/gdk/gdkevents.c
   trunk/gdk/gdkevents.h
   trunk/gdk/gdkinternals.h
   trunk/gdk/gdkwindow.c
   trunk/gdk/gdkwindow.h
   trunk/gdk/x11/gdkwindow-x11.c
   trunk/gtk/gtkmain.c
   trunk/gtk/gtkwidget.c
   trunk/gtk/gtkwidget.h
   trunk/tests/testgtk.c

Modified: trunk/gdk/gdk.symbols
==============================================================================
--- trunk/gdk/gdk.symbols	(original)
+++ trunk/gdk/gdk.symbols	Wed May 21 19:04:24 2008
@@ -646,6 +646,8 @@
 #if IN_HEADER(__GDK_WINDOW_H__)
 #if IN_FILE(__GDK_WINDOW_C__)
 gdk_get_default_root_window
+gdk_window_new
+gdk_window_reparent
 gdk_window_add_filter
 gdk_window_at_pointer
 gdk_window_begin_paint_rect
@@ -703,7 +705,6 @@
 
 #if IN_HEADER(__GDK_WINDOW_H__)
 #if IN_FILE(__GDK_WINDOW_X11_C__)
-gdk_window_new
 gdk_window_foreign_new_for_display
 gdk_window_lookup
 gdk_window_lookup_for_display
@@ -714,7 +715,6 @@
 gdk_window_move
 gdk_window_resize
 gdk_window_move_resize
-gdk_window_reparent
 gdk_window_raise
 gdk_window_lower
 gdk_window_focus

Modified: trunk/gdk/gdkevents.c
==============================================================================
--- trunk/gdk/gdkevents.c	(original)
+++ trunk/gdk/gdkevents.c	Wed May 21 19:04:24 2008
@@ -79,6 +79,25 @@
 }
 
 /**
+ * _gdk_event_queue_prepend:
+ * @display: a #GdkDisplay
+ * @event: Event to prepend.
+ *
+ * Prepends an event before the head of the event queue.
+ *
+ * Returns: the newly prepended list node.
+ **/
+GList*
+_gdk_event_queue_prepend (GdkDisplay *display,
+			  GdkEvent   *event)
+{
+  display->queued_events = g_list_prepend (display->queued_events, event);
+  if (!display->queued_tail)
+    display->queued_tail = display->queued_events;
+  return display->queued_events;
+}
+
+/**
  * _gdk_event_queue_append:
  * @display: a #GdkDisplay
  * @event: Event to append.
@@ -546,6 +565,7 @@
       case GDK_CONFIGURE:
       case GDK_FOCUS_CHANGE:
       case GDK_NOTHING:
+      case GDK_DAMAGE:
       case GDK_DELETE:
       case GDK_DESTROY:
       case GDK_EXPOSE:
@@ -616,6 +636,7 @@
       case GDK_SELECTION_NOTIFY:
       case GDK_PROXIMITY_IN:
       case GDK_PROXIMITY_OUT:
+      case GDK_DAMAGE:
       case GDK_DRAG_ENTER:
       case GDK_DRAG_LEAVE:
       case GDK_DRAG_MOTION:

Modified: trunk/gdk/gdkevents.h
==============================================================================
--- trunk/gdk/gdkevents.h	(original)
+++ trunk/gdk/gdkevents.h	Wed May 21 19:04:24 2008
@@ -146,7 +146,8 @@
   GDK_WINDOW_STATE      = 32,
   GDK_SETTING           = 33,
   GDK_OWNER_CHANGE      = 34,
-  GDK_GRAB_BROKEN       = 35
+  GDK_GRAB_BROKEN       = 35,
+  GDK_DAMAGE            = 36
 } GdkEventType;
 
 /* Event masks. (Used to select what types of events a window

Modified: trunk/gdk/gdkinternals.h
==============================================================================
--- trunk/gdk/gdkinternals.h	(original)
+++ trunk/gdk/gdkinternals.h	Wed May 21 19:04:24 2008
@@ -184,6 +184,8 @@
 GList* _gdk_event_queue_find_first  (GdkDisplay *display);
 void   _gdk_event_queue_remove_link (GdkDisplay *display,
 				     GList      *node);
+GList*  _gdk_event_queue_prepend    (GdkDisplay *display,
+				     GdkEvent   *event);
 GList*  _gdk_event_queue_append     (GdkDisplay *display,
 				     GdkEvent   *event);
 void _gdk_event_button_generate     (GdkDisplay *display,
@@ -310,6 +312,14 @@
 gint _gdk_windowing_get_bits_for_depth (GdkDisplay *display,
 					gint        depth);
 
+GdkWindow* _gdk_window_new                        (GdkWindow     *parent,
+						   GdkWindowAttr *attributes,
+						   gint           attributes_mask);
+void       _gdk_window_reparent                   (GdkWindow     *window,
+						   GdkWindow     *new_parent,
+						   gint           x,
+						   gint           y);
+
 #define GDK_WINDOW_IS_MAPPED(window) ((((GdkWindowObject*)window)->state & GDK_WINDOW_STATE_WITHDRAWN) == 0)
 
 /* Called before processing updates for a window. This gives the windowing

Modified: trunk/gdk/gdkwindow.c
==============================================================================
--- trunk/gdk/gdkwindow.c	(original)
+++ trunk/gdk/gdkwindow.c	Wed May 21 19:04:24 2008
@@ -47,6 +47,26 @@
   cairo_surface_t *surface;
 };
 
+typedef struct {
+  GdkRegion *old_region;
+  gint old_clip_x_origin;
+  gint old_clip_y_origin;
+  gint x_offset;
+  gint y_offset;
+} GdkWindowClipData;
+
+struct _GdkWindowRedirect
+{
+  GdkWindowObject *redirected;
+  GdkDrawable *pixmap;
+  gint src_x;
+  gint src_y;
+  gint dest_x;
+  gint dest_y;
+  gint width;
+  gint height;
+};
+
 static GdkGC *gdk_window_create_gc      (GdkDrawable     *drawable,
                                          GdkGCValues     *values,
                                          GdkGCValuesMask  mask);
@@ -191,6 +211,23 @@
 					   gint       y,
 					   gint       width,
 					   gint       height);
+static void setup_redirect_clip           (GdkWindow         *window,
+					   GdkGC             *gc,
+					   GdkWindowClipData *data);
+static void reset_redirect_clip           (GdkWindow         *offscreen,
+					   GdkGC             *gc,
+					   GdkWindowClipData *data);
+static void gdk_window_redirect_free      (GdkWindowRedirect *redirect);
+static void apply_redirect_to_children    (GdkWindowObject   *private,
+					   GdkWindowRedirect *redirect);
+static void remove_redirect_from_children (GdkWindowObject   *private,
+					   GdkWindowRedirect *redirect);
+static GdkRegion *_gdk_window_calculate_full_clip_region (GdkWindow *window,
+							  GdkWindow *base_window,
+							  GdkGC *gc,
+							  gboolean do_children,
+							  gint *base_x_offset,
+							  gint *base_y_offset);
 
 static gpointer parent_class = NULL;
 
@@ -311,6 +348,91 @@
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
+/**
+ * gdk_window_new:
+ * @parent: a #GdkWindow, or %NULL to create the window as a child of
+ *   the default root window for the default display.
+ * @attributes: attributes of the new window
+ * @attributes_mask: mask indicating which fields in @attributes are valid
+ * 
+ * Creates a new #GdkWindow using the attributes from
+ * @attributes. See #GdkWindowAttr and #GdkWindowAttributesType for
+ * more details.  Note: to use this on displays other than the default
+ * display, @parent must be specified.
+ * 
+ * Return value: the new #GdkWindow
+ **/
+GdkWindow*
+gdk_window_new (GdkWindow     *parent,
+		GdkWindowAttr *attributes,
+		gint           attributes_mask)
+{
+  GdkWindow *window;
+  GdkWindowObject *private, *parent_private;
+  
+  g_return_val_if_fail (attributes != NULL, NULL);
+
+  window = _gdk_window_new (parent, attributes, attributes_mask);
+
+  /* Inherit redirection from parent */
+  if (parent != NULL)
+    {
+      parent_private = GDK_WINDOW_OBJECT (parent);
+      private = GDK_WINDOW_OBJECT (window);
+      private->redirect = parent_private->redirect;
+    }
+  
+  return window;
+}
+
+/**
+ * gdk_window_reparent:
+ * @window: a #GdkWindow
+ * @new_parent: new parent to move @window into
+ * @x: X location inside the new parent
+ * @y: Y location inside the new parent
+ *
+ * Reparents @window into the given @new_parent. The window being
+ * reparented will be unmapped as a side effect.
+ * 
+ **/
+void
+gdk_window_reparent (GdkWindow *window,
+		     GdkWindow *new_parent,
+		     gint       x,
+		     gint       y)
+{
+  GdkWindowObject *private;
+  
+  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (new_parent == NULL || GDK_IS_WINDOW (new_parent));
+  g_return_if_fail (GDK_WINDOW_TYPE (window) != GDK_WINDOW_ROOT);
+
+  if (GDK_WINDOW_DESTROYED (window) ||
+      (new_parent && GDK_WINDOW_DESTROYED (new_parent)))
+    {
+      return;
+    }
+
+  private = (GdkWindowObject *) window;
+
+  /* Break up redirection if inherited */
+  if (private->redirect && private->redirect->redirected != private)
+    {
+      remove_redirect_from_children (private, private->redirect);
+      private->redirect = NULL;
+    }
+  
+  _gdk_window_reparent (window, new_parent, x, y);
+
+  /* Inherit parent redirect if we don't have our own */
+  if (private->parent && private->redirect == NULL)
+    {
+      private->redirect = private->parent->redirect;
+      apply_redirect_to_children (private, private->redirect);
+    }
+}
+
 static void
 window_remove_filters (GdkWindow *window)
 {
@@ -446,6 +568,11 @@
 	  window_remove_filters (window);
 
           gdk_drawable_set_colormap (GDK_DRAWABLE (window), NULL);
+
+	  /* If we own the redirect, free it */
+	  if (private->redirect && private->redirect->redirected == private)
+	    gdk_window_redirect_free (private->redirect);
+	  private->redirect = NULL;
 	}
       break;
     }
@@ -1089,6 +1216,20 @@
                      clip_box.x - x_offset, clip_box.y - y_offset,
                      clip_box.width, clip_box.height);
 
+  if (private->redirect)
+    {
+      GdkWindowClipData data;
+      
+      setup_redirect_clip (window, tmp_gc, &data);
+      gdk_draw_drawable (private->redirect->pixmap, tmp_gc, paint->pixmap,
+			 clip_box.x - paint->x_offset,
+			 clip_box.y - paint->y_offset,
+			 clip_box.x + data.x_offset,
+			 clip_box.y + data.y_offset,
+			 clip_box.width, clip_box.height);
+      reset_redirect_clip (window, tmp_gc, &data);
+    }
+  
   /* Reset clip region of the cached GdkGC */
   gdk_gc_set_clip_region (tmp_gc, NULL);
 
@@ -1946,6 +2087,65 @@
 #endif
 }
 
+static void
+gdk_window_clear_backing_rect_redirect (GdkWindow *window,
+					gint       x,
+					gint       y,
+					gint       width,
+					gint       height)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkWindowRedirect *redirect = private->redirect;
+  GdkRegion *clip_region;
+  gint x_offset, y_offset;
+  BackingRectMethod method;
+  GdkWindowPaint paint;
+  
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+
+  paint.x_offset = x_offset;
+  paint.y_offset = y_offset;
+  paint.pixmap = redirect->pixmap;
+  paint.surface = _gdk_drawable_ref_cairo_surface (redirect->pixmap);
+  
+  clip_region = _gdk_window_calculate_full_clip_region (window,
+							GDK_WINDOW (redirect->redirected),
+							NULL, TRUE,
+							&x_offset, &y_offset);
+  
+
+  method.cr = NULL;
+  method.gc = NULL;
+  setup_backing_rect_method (&method, window, &paint, 0, 0);
+
+  if (method.cr)
+    {
+      g_assert (method.gc == NULL);
+
+      cairo_rectangle (method.cr, x, y, width, height);
+      cairo_clip (method.cr);
+
+      gdk_cairo_region (method.cr, clip_region);
+      cairo_fill (method.cr);
+
+      cairo_destroy (method.cr);
+    }
+  else
+    {
+      g_assert (method.gc != NULL);
+
+      gdk_gc_set_clip_region (method.gc, clip_region);
+      gdk_draw_rectangle (window, method.gc, TRUE, x, y, width, height);
+      g_object_unref (method.gc);
+
+    }
+
+  gdk_region_destroy (clip_region);
+  cairo_surface_destroy (paint.surface);
+}
+
+
 /**
  * gdk_window_clear:
  * @window: a #GdkWindow
@@ -1992,7 +2192,12 @@
   if (private->paint_stack)
     gdk_window_clear_backing_rect (window, x, y, width, height);
   else
-    _gdk_windowing_window_clear_area (window, x, y, width, height);
+    {
+      if (private->redirect)
+	gdk_window_clear_backing_rect_redirect (window, x, y, width, height);
+	
+      _gdk_windowing_window_clear_area (window, x, y, width, height);
+    }
 }
 
 /**
@@ -2025,6 +2230,9 @@
   if (private->paint_stack)
     gdk_window_clear_backing_rect (window, x, y, width, height);
 
+  if (private->redirect)
+    gdk_window_clear_backing_rect_redirect (window, x, y, width, height);
+  
   _gdk_windowing_window_clear_area_e (window, x, y, width, height);
 }
 
@@ -2629,7 +2837,18 @@
       return;
     }
 
-  visible_region = gdk_drawable_get_visible_region (window);
+  /* windows that a redirection has ben setup for need to be considered
+   * fully visible, in order to avoid missing redirected paint ops
+   * anywhere in the window area.
+   */
+  if (private->redirect && private->redirect->redirected == private)
+    {
+      GdkRectangle visible_rect = { 0, 0, 0, 0 };
+      gdk_drawable_get_size (GDK_DRAWABLE (window), &visible_rect.width, &visible_rect.height);
+      visible_region = gdk_region_rectangle (&visible_rect);
+    }
+  else
+    visible_region = gdk_drawable_get_visible_region (window);
   gdk_region_intersect (visible_region, region);
 
   tmp_list = private->children;
@@ -2678,7 +2897,7 @@
     {
       if (debug_updates)
         draw_ugly_color (window, region);
-      
+
       if (private->update_area)
 	{
 	  gdk_region_union (private->update_area, visible_region);
@@ -3223,5 +3442,348 @@
   private->composited = composited;
 }
 
+
+static void
+remove_redirect_from_children (GdkWindowObject *private, GdkWindowRedirect *redirect)
+{
+  GList *l;
+  GdkWindowObject *child;
+
+  for (l = private->children; l != NULL; l = l->next)
+    {
+      child = l->data;
+
+      /* Don't redirect this child if it already has another redirect */
+      if (child->redirect == redirect)
+	{
+	  child->redirect = NULL;
+	  remove_redirect_from_children (child, redirect);
+	}
+    }
+}
+
+/**
+ * gdk_window_remove_redirection:
+ * @window: a #GdkWindow
+ *
+ * Removes and active redirection started by
+ * gdk_window_redirect_to_drawable().
+ **/
+void
+gdk_window_remove_redirection (GdkWindow *window)
+{
+  GdkWindowObject *private;
+  
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  private = (GdkWindowObject *) window;
+
+  if (private->redirect &&
+      private->redirect->redirected == private)
+    {
+      remove_redirect_from_children (private, private->redirect);
+      gdk_window_redirect_free (private->redirect);
+      private->redirect = NULL;
+    }
+}
+
+static void
+apply_redirect_to_children (GdkWindowObject *private, GdkWindowRedirect *redirect)
+{
+  GList *l;
+  GdkWindowObject *child;
+
+  for (l = private->children; l != NULL; l = l->next)
+    {
+      child = l->data;
+
+      /* Don't redirect this child if it already has another redirect */
+      if (!child->redirect)
+	{
+	  child->redirect = redirect;
+	  apply_redirect_to_children (child, redirect);
+	}
+    }
+}
+
+/**
+ * gdk_window_redirect_to_drawable:
+ * @window: a #GdkWindow
+ * @drawable: a #GdkDrawable
+ * src_x: x position in @window
+ * src_y: y position in @window
+ * dest_x: x position in @drawable
+ * dest_y: y position in @drawable
+ * width: width of redirection
+ * height: height of redirection
+ *
+ * Redirects drawing into @windows so that drawing to the
+ * window in the rectangle specified by @src_x, @src_y,
+ * @width and @height is also drawn into @drawable at
+ * @dest_x, @dest_y.
+ *
+ * Only drawing between gdk_window_begin_paint_region() and
+ * gdk_window_end_paint() is redirected.
+ *
+ * Redirection is active until gdk_window_remove_redirection()
+ * is called.
+ *
+ * This function should not be used on windows created by
+ * gdk_window_new_offscreen(), as that is implemented using
+ * redirection.
+ **/
+void
+gdk_window_redirect_to_drawable (GdkWindow *window,
+				 GdkDrawable *drawable,
+				 gint src_x, gint src_y,
+				 gint dest_x, gint dest_y,
+				 gint width, gint height)
+{
+  GdkWindowObject *private;
+  
+  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_DRAWABLE (drawable));
+  g_return_if_fail (GDK_WINDOW_TYPE (window) != GDK_WINDOW_ROOT);
+
+  private = (GdkWindowObject *) window;
+
+  if (private->redirect)
+    gdk_window_remove_redirection (window);
+
+  if (width == -1 || height == -1)
+    {
+      gint w, h;
+      gdk_drawable_get_size (GDK_DRAWABLE (window), &w, &h);
+      if (width == -1)
+	width = w;
+      if (height == -1)
+	height = h;
+    }
+  
+  private->redirect = g_new0 (GdkWindowRedirect, 1);
+  private->redirect->redirected = private;
+  private->redirect->pixmap = g_object_ref (drawable);
+  private->redirect->src_x = src_x;
+  private->redirect->src_y = src_y;
+  private->redirect->dest_x = dest_x;
+  private->redirect->dest_y = dest_y;
+  private->redirect->width = width;
+  private->redirect->height = height;
+
+  apply_redirect_to_children (private, private->redirect);
+}
+
+static void
+window_get_size_rectangle (GdkWindow    *window,
+                           GdkRectangle *rect)
+{
+  rect->x = rect->y = 0;
+  gdk_drawable_get_size (GDK_DRAWABLE (window), &rect->width, &rect->height);
+}
+
+/* Calculates the real clipping region for a window, in window coordinates,
+ * taking into account other windows, gc clip region and gc clip mask.
+ */
+static GdkRegion *
+_gdk_window_calculate_full_clip_region (GdkWindow *window,
+					GdkWindow *base_window,
+					GdkGC *gc,
+					gboolean do_children,
+					gint *base_x_offset,
+					gint *base_y_offset)
+{
+  GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
+  GdkRectangle visible_rect;
+  GdkRegion *real_clip_region, *tmpreg;
+  gint x_offset, y_offset;
+  GdkWindowObject *parentwin, *lastwin;
+
+  if (base_x_offset)
+    *base_x_offset = 0;
+  if (base_y_offset)
+    *base_y_offset = 0;
+  
+  if (!GDK_WINDOW_IS_MAPPED (window) || private->input_only)
+    return gdk_region_new ();
+
+  window_get_size_rectangle (window, &visible_rect);
+
+  /* windows that a redirection has ben setup for need to be considered
+   * fully visible, in order to avoid missing redirected paint ops
+   * anywhere in the window area.
+   */
+  if (private->redirect && private->redirect->redirected == private)
+    return gdk_region_rectangle (&visible_rect);
+
+  /* real_clip_region is in window coordinates */
+  real_clip_region = gdk_region_rectangle (&visible_rect);
+
+  x_offset = y_offset = 0;
+
+  lastwin = private;
+  if (do_children)
+    parentwin = lastwin;
+  else
+    parentwin = lastwin->parent;
+  
+  /* Remove the areas of all overlapping windows above parentwin in the hiearachy */
+  for (; parentwin != NULL && (parentwin == private || lastwin != (GdkWindowObject *)base_window);
+       lastwin = parentwin, parentwin = lastwin->parent)
+    {
+      GList *cur;
+      GdkRectangle real_clip_rect;
+      
+      if (parentwin != private)
+	{
+	  x_offset += GDK_WINDOW_OBJECT (lastwin)->x;
+	  y_offset += GDK_WINDOW_OBJECT (lastwin)->y;
+	}
+      
+      /* children is ordered in reverse stack order */
+      for (cur = GDK_WINDOW_OBJECT (parentwin)->children; cur && cur->data != lastwin; cur = cur->next)
+	{
+	  GdkWindow *child = cur->data;
+	  GdkWindowObject *child_private = (GdkWindowObject *)child;
+	  
+	  if (!GDK_WINDOW_IS_MAPPED (child) || child_private->input_only)
+	    continue;
+	  
+	  window_get_size_rectangle (child, &visible_rect);
+	  
+	  /* Convert rect to "window" coords */
+	  visible_rect.x += child_private->x - x_offset;
+	  visible_rect.y += child_private->y - y_offset;
+	  
+	  /* This shortcut is really necessary for performance when there are a lot of windows */
+	  gdk_region_get_clipbox (real_clip_region, &real_clip_rect);
+	  if (visible_rect.x >= real_clip_rect.x + real_clip_rect.width ||
+	      visible_rect.x + visible_rect.width <= real_clip_rect.x ||
+	      visible_rect.y >= real_clip_rect.y + real_clip_rect.height ||
+	      visible_rect.y + visible_rect.height <= real_clip_rect.y)
+	    continue;
+	  
+	  tmpreg = gdk_region_rectangle (&visible_rect);
+	  gdk_region_subtract (real_clip_region, tmpreg);
+	  gdk_region_destroy (tmpreg);
+	}
+      
+    }
+
+  if (gc)
+    {
+      GdkRegion *clip_region = _gdk_gc_get_clip_region (gc);
+      
+      if (clip_region)
+	{
+	  /* clip_region is relative to gc clip origin which is relative to the window */
+	  /* offset it to window relative: */
+	  tmpreg = gdk_region_copy (clip_region);
+	  gdk_region_offset (real_clip_region,
+			     gc->clip_x_origin,
+			     gc->clip_y_origin);
+	  /* Intersect it with window hierarchy cliprect: */
+	  gdk_region_intersect (real_clip_region, tmpreg);
+	  gdk_region_destroy (tmpreg);
+	}
+    }
+
+  if (base_x_offset)
+    *base_x_offset = x_offset;
+  if (base_y_offset)
+    *base_y_offset = y_offset;
+
+  return real_clip_region;
+}
+
+static void
+gdk_window_add_damage (GdkWindow *toplevel,
+		       GdkRegion *damaged_region)
+{
+  GdkDisplay *display;
+  GdkEvent event = { 0, };
+  event.expose.type = GDK_DAMAGE;
+  event.expose.window = toplevel;
+  event.expose.send_event = FALSE;
+  event.expose.region = damaged_region;
+  gdk_region_get_clipbox (event.expose.region, &event.expose.area);
+  display = gdk_drawable_get_display (event.expose.window);
+  _gdk_event_queue_append (display, gdk_event_copy (&event));
+}
+
+static void
+setup_redirect_clip (GdkWindow         *window,
+		     GdkGC             *gc,
+		     GdkWindowClipData *data)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkRegion *visible_region;
+  GdkRectangle dest_rect;
+  GdkRegion *tmpreg;
+  GdkWindow *toplevel;
+
+  data->old_region = _gdk_gc_get_clip_region (gc);
+  if (data->old_region) 
+    data->old_region = gdk_region_copy (data->old_region);
+
+  data->old_clip_x_origin = gc->clip_x_origin;
+  data->old_clip_y_origin = gc->clip_y_origin;
+
+  toplevel = GDK_WINDOW (private->redirect->redirected);
+  
+  /* Get the clip region for gc clip rect + window hierarchy in
+     window relative coords */
+  visible_region =
+    _gdk_window_calculate_full_clip_region (window, toplevel,
+					    gc, TRUE,
+					    &data->x_offset, 
+					    &data->y_offset);
+
+  /* Compensate for the source pos/size */
+  data->x_offset -= private->redirect->src_x;
+  data->y_offset -= private->redirect->src_y;
+  dest_rect.x = -data->x_offset;
+  dest_rect.y = -data->y_offset;
+  dest_rect.width = private->redirect->width;
+  dest_rect.height = private->redirect->height;
+  tmpreg = gdk_region_rectangle (&dest_rect);
+  gdk_region_intersect (visible_region, tmpreg);
+  gdk_region_destroy (tmpreg);
+
+  /* Compensate for the dest pos */
+  data->x_offset += private->redirect->dest_x;
+  data->y_offset += private->redirect->dest_y;
+
+  gdk_gc_set_clip_region (gc, visible_region); /* This resets clip origin! */
+
+  /* offset clip and tiles from window coords to pixmaps coords */
+  gdk_gc_offset (gc, -data->x_offset, -data->y_offset);
+
+  /* Offset region to abs coords and add to damage */
+  gdk_region_offset (visible_region, data->x_offset, data->y_offset);
+  gdk_window_add_damage (toplevel, visible_region);
+  
+  gdk_region_destroy (visible_region);
+}
+
+static void
+reset_redirect_clip (GdkWindow *offscreen, GdkGC *gc, GdkWindowClipData *data)
+{
+  /* offset back */
+  gdk_gc_offset (gc, data->x_offset, data->y_offset);
+
+  /* reset old clip */
+  gdk_gc_set_clip_region (gc, data->old_region);
+  if (data->old_region)
+    gdk_region_destroy (data->old_region);
+  gdk_gc_set_clip_origin (gc, data->old_clip_x_origin, data->old_clip_y_origin);
+}
+
+static void
+gdk_window_redirect_free (GdkWindowRedirect *redirect)
+{
+  g_object_unref (redirect->pixmap);
+  g_free (redirect);
+}
+
 #define __GDK_WINDOW_C__
 #include "gdkaliasdef.c"

Modified: trunk/gdk/gdkwindow.h
==============================================================================
--- trunk/gdk/gdkwindow.h	(original)
+++ trunk/gdk/gdkwindow.h	Wed May 21 19:04:24 2008
@@ -36,6 +36,7 @@
 typedef struct _GdkGeometry           GdkGeometry;
 typedef struct _GdkWindowAttr	      GdkWindowAttr;
 typedef struct _GdkPointerHooks	      GdkPointerHooks;
+typedef struct _GdkWindowRedirect     GdkWindowRedirect;
 
 /* Classes of windows.
  *   InputOutput: Almost every window should be of this type. Such windows
@@ -300,6 +301,8 @@
   GdkEventMask event_mask;
 
   guint update_and_descendants_freeze_count;
+
+  GdkWindowRedirect *redirect;
 };
 
 struct _GdkWindowObjectClass
@@ -638,6 +641,13 @@
 
 GdkWindow *gdk_get_default_root_window (void);
 
+void gdk_window_redirect_to_drawable (GdkWindow *window,
+				      GdkDrawable *drawable,
+				      gint src_x, gint src_y,
+				      gint dest_x, gint dest_y,
+				      gint width, gint height);
+void gdk_window_remove_redirection   (GdkWindow *window);
+
 #ifndef GDK_DISABLE_DEPRECATED
 #define GDK_ROOT_PARENT()             (gdk_get_default_root_window ())
 #define gdk_window_get_size            gdk_drawable_get_size

Modified: trunk/gdk/x11/gdkwindow-x11.c
==============================================================================
--- trunk/gdk/x11/gdkwindow-x11.c	(original)
+++ trunk/gdk/x11/gdkwindow-x11.c	Wed May 21 19:04:24 2008
@@ -647,24 +647,10 @@
   ensure_sync_counter (window);
 }
 
-/**
- * gdk_window_new:
- * @parent: a #GdkWindow, or %NULL to create the window as a child of
- *   the default root window for the default display.
- * @attributes: attributes of the new window
- * @attributes_mask: mask indicating which fields in @attributes are valid
- * 
- * Creates a new #GdkWindow using the attributes from
- * @attributes. See #GdkWindowAttr and #GdkWindowAttributesType for
- * more details.  Note: to use this on displays other than the default
- * display, @parent must be specified.
- * 
- * Return value: the new #GdkWindow
- **/
 GdkWindow*
-gdk_window_new (GdkWindow     *parent,
-		GdkWindowAttr *attributes,
-		gint           attributes_mask)
+_gdk_window_new (GdkWindow     *parent,
+		 GdkWindowAttr *attributes,
+		 gint           attributes_mask)
 {
   GdkWindow *window;
   GdkWindowObject *private;
@@ -1843,22 +1829,11 @@
     }
 }
 
-/**
- * gdk_window_reparent:
- * @window: a #GdkWindow
- * @new_parent: new parent to move @window into
- * @x: X location inside the new parent
- * @y: Y location inside the new parent
- *
- * Reparents @window into the given @new_parent. The window being
- * reparented will be unmapped as a side effect.
- * 
- **/
 void
-gdk_window_reparent (GdkWindow *window,
-		     GdkWindow *new_parent,
-		     gint       x,
-		     gint       y)
+_gdk_window_reparent (GdkWindow *window,
+		      GdkWindow *new_parent,
+		      gint       x,
+		      gint       y)
 {
   GdkWindowObject *window_private;
   GdkWindowObject *parent_private;
@@ -1866,16 +1841,6 @@
   GdkWindowImplX11 *impl;
   gboolean was_toplevel;
   
-  g_return_if_fail (GDK_IS_WINDOW (window));
-  g_return_if_fail (new_parent == NULL || GDK_IS_WINDOW (new_parent));
-  g_return_if_fail (GDK_WINDOW_TYPE (window) != GDK_WINDOW_ROOT);
-
-  if (GDK_WINDOW_DESTROYED (window) ||
-      (new_parent && GDK_WINDOW_DESTROYED (new_parent)))
-    {
-      return;
-    }
-  
   if (!new_parent)
     new_parent = gdk_screen_get_root_window (GDK_WINDOW_SCREEN (window));
 

Modified: trunk/gtk/gtkmain.c
==============================================================================
--- trunk/gtk/gtkmain.c	(original)
+++ trunk/gtk/gtkmain.c	Wed May 21 19:04:24 2008
@@ -1529,6 +1529,7 @@
     case GDK_VISIBILITY_NOTIFY:
     case GDK_WINDOW_STATE:
     case GDK_GRAB_BROKEN:
+    case GDK_DAMAGE:
       gtk_widget_event (event_widget, event);
       break;
 

Modified: trunk/gtk/gtkwidget.c
==============================================================================
--- trunk/gtk/gtkwidget.c	(original)
+++ trunk/gtk/gtkwidget.c	Wed May 21 19:04:24 2008
@@ -127,6 +127,7 @@
   QUERY_TOOLTIP,
   KEYNAV_FAILED,
   DRAG_FAILED,
+  DAMAGE_EVENT,
   LAST_SIGNAL
 };
 
@@ -1994,6 +1995,28 @@
 		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
 
   /**
+   * GtkWidget::damage-event:
+   * @widget: the object which received the signal
+   * @event: the #GdkEventExpose event
+   *
+   * Emitted when a redirected window belonging to @widget gets drawn into.
+   * The region/area members of the event shows what area of the redirected
+   * drawable was drawn into.
+   *
+   * Returns: %TRUE to stop other handlers from being invoked for the event.
+   *   %FALSE to propagate the event further.
+   *
+   * Since: 2.16
+   */
+  widget_signals[DAMAGE_EVENT] =
+    g_signal_new ("damage_event",
+		  G_TYPE_FROM_CLASS (gobject_class),
+		  G_SIGNAL_RUN_LAST, 0,
+		  _gtk_boolean_handled_accumulator, NULL,
+		  _gtk_marshal_BOOLEAN__BOXED,
+		  G_TYPE_BOOLEAN, 1,
+		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+/**
    * GtkWidget::grab-broken-event:
    * @widget: the object which received the signal
    * @event: the #GdkEventGrabBroken event
@@ -4667,6 +4690,9 @@
 	case GDK_GRAB_BROKEN:
 	  signal_num = GRAB_BROKEN;
 	  break;
+	case GDK_DAMAGE:
+	  signal_num = DAMAGE_EVENT;
+	  break;
 	default:
 	  g_warning ("gtk_widget_event(): unhandled event type: %d", event->type);
 	  signal_num = -1;
@@ -8285,6 +8311,53 @@
   g_object_unref ((GObject*) widget);
 }
 
+/**
+ * gtk_widget_get_snapshot:
+ * @widget: a #GtkWidget
+ *
+ * Creates a #GdkPixmap of the contents of the widget and its
+ * children. Works even if the widget is obscured.
+ * Note that the depth and visual of the resulting pixmap is dependent
+ * on the widget being snapshot and likely differs from those of a target
+ * widget displaying the pixmap. Use gdk_pixbuf_get_from_drawable()
+ * to convert the pixmap to a visual independant representation.
+ *
+ * Return value: #GdkPixmap of the widget
+ * Since: 2.16
+ **/
+GdkPixmap*
+gtk_widget_get_snapshot (GtkWidget *widget)
+{
+  GdkPixmap *pixmap;
+  int x, y;
+
+  if (!GTK_WIDGET_REALIZED (widget))
+    gtk_widget_realize (widget);
+
+  pixmap = gdk_pixmap_new (widget->window,
+			   widget->allocation.width,
+			   widget->allocation.height,
+			   gdk_drawable_get_depth (widget->window));
+  if (GTK_WIDGET_NO_WINDOW (widget))
+    {
+      x = widget->allocation.x;
+      y = widget->allocation.y;
+    }
+  else
+    x = y = 0;
+
+  gdk_window_redirect_to_drawable (widget->window,
+				   pixmap,
+				   x, y,
+				   0, 0,
+				   widget->allocation.width,
+				   widget->allocation.height);
+  gtk_widget_queue_draw (widget);
+  gdk_window_process_updates (widget->window, TRUE);
+  gdk_window_remove_redirection (widget->window);
+
+  return pixmap;
+}
 
 /* style properties
  */

Modified: trunk/gtk/gtkwidget.h
==============================================================================
--- trunk/gtk/gtkwidget.h	(original)
+++ trunk/gtk/gtkwidget.h	Wed May 21 19:04:24 2008
@@ -416,6 +416,10 @@
 				       gint        y,
 				       gboolean    keyboard_tooltip,
 				       GtkTooltip *tooltip);
+  /* Signals without a C default handler class slot:
+   * gboolean	(*damage_event)	(GtkWidget      *widget,
+   *                             GdkEventExpose *event);
+   */
 
   /* Padding for future expansion */
   void (*_gtk_reserved5) (void);
@@ -610,6 +614,7 @@
 GtkSettings*  gtk_widget_get_settings    (GtkWidget *widget);
 GtkClipboard *gtk_widget_get_clipboard   (GtkWidget *widget,
 					  GdkAtom    selection);
+GdkPixmap *   gtk_widget_get_snapshot    (GtkWidget *widget);
 
 #ifndef GTK_DISABLE_DEPRECATED
 #define gtk_widget_set_visual(widget,visual)  ((void) 0)

Modified: trunk/tests/testgtk.c
==============================================================================
--- trunk/tests/testgtk.c	(original)
+++ trunk/tests/testgtk.c	Wed May 21 19:04:24 2008
@@ -12206,6 +12206,171 @@
   
 }
 
+struct SnapshotData {
+  GtkWidget *toplevel_button;
+  GtkWidget **window;
+  GdkCursor *cursor;
+  gboolean in_query;
+  gboolean is_toplevel;
+  gint handler;
+};
+
+static void
+destroy_snapshot_data (GtkWidget             *widget,
+		       struct SnapshotData *data)
+{
+  if (*data->window)
+    *data->window = NULL;
+  
+  if (data->cursor)
+    {
+      gdk_cursor_unref (data->cursor);
+      data->cursor = NULL;
+    }
+
+  if (data->handler)
+    {
+      g_signal_handler_disconnect (widget, data->handler);
+      data->handler = 0;
+    }
+
+  g_free (data);
+}
+
+static gint
+snapshot_widget_event (GtkWidget	       *widget,
+		       GdkEvent	       *event,
+		       struct SnapshotData *data)
+{
+  GtkWidget *res_widget = NULL;
+
+  if (!data->in_query)
+    return FALSE;
+  
+  if (event->type == GDK_BUTTON_RELEASE)
+    {
+      gtk_grab_remove (widget);
+      gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
+				  GDK_CURRENT_TIME);
+      
+      res_widget = find_widget_at_pointer (gtk_widget_get_display (widget));
+      if (data->is_toplevel && res_widget)
+	res_widget = gtk_widget_get_toplevel (res_widget);
+      if (res_widget)
+	{
+	  GdkPixmap *pixmap;
+          GdkPixbuf *pixbuf = NULL;
+	  GtkWidget *window, *image;
+
+	  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+	  pixmap = gtk_widget_get_snapshot (res_widget);
+          gtk_widget_realize (window);
+          if (gdk_drawable_get_depth (window->window) != gdk_drawable_get_depth (pixmap))
+            {
+              /* this branch is needed to convert ARGB -> RGB */
+              int width, height;
+              gdk_drawable_get_size (pixmap, &width, &height);
+              pixbuf = gdk_pixbuf_get_from_drawable (NULL, pixmap,
+                                                     gtk_widget_get_colormap (res_widget),
+                                                     0, 0,
+                                                     0, 0,
+                                                     width, height);
+              image = gtk_image_new_from_pixbuf (pixbuf);
+            }
+          else
+            image = gtk_image_new_from_pixmap (pixmap, NULL);
+          g_object_unref (pixbuf);
+	  gtk_container_add (GTK_CONTAINER (window), image);
+          g_object_unref (pixmap);
+	  gtk_widget_show_all (window);
+	}
+
+      data->in_query = FALSE;
+    }
+  return FALSE;
+}
+
+
+static void
+snapshot_widget (GtkButton *button,
+		 struct SnapshotData *data)
+{
+  gint failure;
+
+  g_signal_connect (button, "event",
+		    G_CALLBACK (snapshot_widget_event), data);
+
+  data->is_toplevel = GTK_WIDGET (button) == data->toplevel_button;
+  
+  if (!data->cursor)
+    data->cursor = gdk_cursor_new_for_display (gtk_widget_get_display (GTK_WIDGET (button)),
+					       GDK_TARGET);
+  
+  failure = gdk_pointer_grab (GTK_WIDGET (button)->window,
+			      TRUE,
+			      GDK_BUTTON_RELEASE_MASK,
+			      NULL,
+			      data->cursor,
+			      GDK_CURRENT_TIME);
+
+  gtk_grab_add (GTK_WIDGET (button));
+
+  data->in_query = TRUE;
+}
+
+static void
+create_snapshot (GtkWidget *widget)
+{
+  static GtkWidget *window = NULL;
+  GtkWidget *button;
+  GtkWidget *vbox;
+  struct SnapshotData *data;
+
+  data = g_new (struct SnapshotData, 1);
+  data->window = &window;
+  data->in_query = FALSE;
+  data->cursor = NULL;
+  data->handler = 0;
+
+  if (!window)
+    {
+      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+      gtk_window_set_screen (GTK_WINDOW (window),
+			     gtk_widget_get_screen (widget));      
+
+      data->handler = g_signal_connect (window, "destroy",
+					G_CALLBACK (destroy_snapshot_data),
+					data);
+
+      gtk_window_set_title (GTK_WINDOW (window), "test snapshot");
+      gtk_container_set_border_width (GTK_CONTAINER (window), 10);
+
+      vbox = gtk_vbox_new (FALSE, 1);
+      gtk_container_add (GTK_CONTAINER (window), vbox);
+            
+      button = gtk_button_new_with_label ("Snapshot widget");
+      gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
+      g_signal_connect (button, "clicked",
+			G_CALLBACK (snapshot_widget),
+			data);
+      
+      button = gtk_button_new_with_label ("Snapshot toplevel");
+      data->toplevel_button = button;
+      gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
+      g_signal_connect (button, "clicked",
+			G_CALLBACK (snapshot_widget),
+			data);
+    }
+
+  if (!GTK_WIDGET_VISIBLE (window))
+    gtk_widget_show_all (window);
+  else
+    gtk_widget_destroy (window);
+  
+}
+
+
 
 /*
  * Color Preview
@@ -13489,6 +13654,7 @@
   { "scrolled windows", create_scrolled_windows },
   { "shapes", create_shapes },
   { "size groups", create_size_groups },
+  { "snapshot", create_snapshot },
   { "spinbutton", create_spins },
   { "statusbar", create_statusbar },
   { "styles", create_styles },
@@ -13524,7 +13690,7 @@
 
   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
   gtk_widget_set_name (window, "main window");
-  gtk_widget_set_uposition (window, 20, 20);
+  gtk_widget_set_uposition (window, 50, 20);
   gtk_window_set_default_size (GTK_WINDOW (window), -1, 400);
 
   geometry.min_width = -1;



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