Bug 318807 – Offscreen windows and window redirection.

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.



svn path=/trunk/; revision=20122
This commit is contained in:
10:49:20 Tim Janik 2008-05-21 19:04:24 +00:00 committed by Tim Janik
parent e10e51c958
commit 4111cf2065
12 changed files with 929 additions and 49 deletions

View File

@ -1,3 +1,69 @@
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.
2008-05-21 Michael Natterer <mitch@imendio.com>
* gtk/gtkalignment.c

View File

@ -646,6 +646,8 @@ gdk_window_impl_x11_get_type G_GNUC_CONST
#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 @@ gdk_window_move_region
#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_withdraw
gdk_window_move
gdk_window_resize
gdk_window_move_resize
gdk_window_reparent
gdk_window_raise
gdk_window_lower
gdk_window_focus

View File

@ -78,6 +78,25 @@ _gdk_event_queue_find_first (GdkDisplay *display)
return NULL;
}
/**
* _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
@ -546,6 +565,7 @@ gdk_event_get_time (const GdkEvent *event)
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 @@ gdk_event_get_state (const GdkEvent *event,
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:

View File

@ -146,7 +146,8 @@ typedef enum
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

View File

@ -184,6 +184,8 @@ GdkEvent* _gdk_event_unqueue (GdkDisplay *display);
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 @@ GdkWindow* _gdk_windowing_window_at_pointer (GdkDisplay *display,
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

View File

@ -47,6 +47,26 @@ struct _GdkWindowPaint
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 @@ static void gdk_window_clear_backing_rect (GdkWindow *window,
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 @@ gdk_window_finalize (GObject *object)
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 @@ _gdk_window_destroy_hierarchy (GdkWindow *window,
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 @@ gdk_window_end_paint (GdkWindow *window)
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 @@ gdk_window_clear_backing_rect (GdkWindow *window,
#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 @@ gdk_window_clear_area (GdkWindow *window,
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 @@ gdk_window_clear_area_e (GdkWindow *window,
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 @@ gdk_window_invalidate_maybe_recurse (GdkWindow *window,
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 @@ gdk_window_invalidate_maybe_recurse (GdkWindow *window,
{
if (debug_updates)
draw_ugly_color (window, region);
if (private->update_area)
{
gdk_region_union (private->update_area, visible_region);
@ -3223,5 +3442,348 @@ gdk_window_set_composited (GdkWindow *window,
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"

View File

@ -36,6 +36,7 @@ G_BEGIN_DECLS
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 @@ struct _GdkWindowObject
GdkEventMask event_mask;
guint update_and_descendants_freeze_count;
GdkWindowRedirect *redirect;
};
struct _GdkWindowObjectClass
@ -638,6 +641,13 @@ GdkPointerHooks *gdk_set_pointer_hooks (const GdkPointerHooks *new_hooks);
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

View File

@ -647,24 +647,10 @@ setup_toplevel_window (GdkWindow *window,
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_move_resize (GdkWindow *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)
_gdk_window_reparent (GdkWindow *window,
GdkWindow *new_parent,
gint x,
gint y)
{
GdkWindowObject *window_private;
GdkWindowObject *parent_private;
@ -1866,16 +1841,6 @@ gdk_window_reparent (GdkWindow *window,
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));

View File

@ -1529,6 +1529,7 @@ gtk_main_do_event (GdkEvent *event)
case GDK_VISIBILITY_NOTIFY:
case GDK_WINDOW_STATE:
case GDK_GRAB_BROKEN:
case GDK_DAMAGE:
gtk_widget_event (event_widget, event);
break;

View File

@ -127,6 +127,7 @@ enum {
QUERY_TOOLTIP,
KEYNAV_FAILED,
DRAG_FAILED,
DAMAGE_EVENT,
LAST_SIGNAL
};
@ -1994,6 +1995,28 @@ gtk_widget_class_init (GtkWidgetClass *klass)
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 @@ gtk_widget_event_internal (GtkWidget *widget,
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 @@ gtk_widget_unref (GtkWidget *widget)
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
*/

View File

@ -416,6 +416,10 @@ struct _GtkWidgetClass
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 @@ GdkWindow * gtk_widget_get_root_window (GtkWidget *widget);
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)

View File

@ -12206,6 +12206,171 @@ create_properties (GtkWidget *widget)
}
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 @@ struct {
{ "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 @@ create_main_window (void)
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;