mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-05 16:20:10 +00:00
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:
parent
e10e51c958
commit
4111cf2065
66
ChangeLog
66
ChangeLog
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
568
gdk/gdkwindow.c
568
gdk/gdkwindow.c
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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)
|
||||
|
168
tests/testgtk.c
168
tests/testgtk.c
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user