diff --git a/ChangeLog b/ChangeLog index 7e1d588700..d32636ddad 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +2007-06-01 Matthias Clasen + + Add support for composited child windows. (#412882, Ryan Lortie) + + * gdk/gdk.symbols: + * gdk/gdkdisplay.h: + * gdk/gdkinternals.h: + * gdk/gdkwindow.[hc]: Add gdk_display_supports_composite() and + gdk_window_set_composited(). + + * gdk/x11/gdkevents-x11.c: + * gdk/x11/gdkdisplay-x11.[hc]: + * gdk/x11/gdkwindow-x11.[hc]: X11 implementation. + + * gdk/win32/gdkdisplay-win32.c: + * gdk/win32/gdkwindow-win32.c: Dummy win32 implementration. + + * gdk/quartz/gdkdisplay-quartz.c: + * gdk/quartz/gdkwindow-quartz.c: Dummy Quartz implementation. + + * gdk/directfb/gdkdisplay-directfb.c: + * gdk/directfb/gdkwindow-directfb.c: Dummy DirectFB implementation. + + * tests/testgtk.c: Add a "composited window" test. + 2007-06-01 Michael Natterer * gtk/gtkmenuitem.c (gtk_menu_item_position_menu): don't switch diff --git a/docs/reference/ChangeLog b/docs/reference/ChangeLog index f129b25a6f..282238a233 100644 --- a/docs/reference/ChangeLog +++ b/docs/reference/ChangeLog @@ -1,3 +1,8 @@ +2007-06-01 Matthias Clasen + + * gdk/gdk-sections.txt: Add new composited window api + * gdk/tmpl/windows.sgml: Add composited window example + 2007-05-26 Matthias Clasen * gtk/migrating*.sgml: Some cleanups diff --git a/docs/reference/gdk/gdk-sections.txt b/docs/reference/gdk/gdk-sections.txt index 081ce425af..d0828990f5 100644 --- a/docs/reference/gdk/gdk-sections.txt +++ b/docs/reference/gdk/gdk-sections.txt @@ -155,6 +155,7 @@ gdk_display_supports_clipboard_persistence gdk_display_store_clipboard gdk_display_supports_shapes gdk_display_supports_input_shapes +gdk_display_supports_composite GDK_DISPLAY_OBJECT GDK_IS_DISPLAY @@ -651,6 +652,7 @@ gdk_window_unfullscreen gdk_window_set_keep_above gdk_window_set_keep_below gdk_window_set_opacity +gdk_window_set_composited gdk_window_move gdk_window_resize gdk_window_move_resize diff --git a/docs/reference/gdk/tmpl/windows.sgml b/docs/reference/gdk/tmpl/windows.sgml index 25687836f1..6c5d0515bc 100644 --- a/docs/reference/gdk/tmpl/windows.sgml +++ b/docs/reference/gdk/tmpl/windows.sgml @@ -12,6 +12,164 @@ GTK+ level. A #GtkWindow is a toplevel window, the thing a user might think of as a "window" with a titlebar and so on; a #GtkWindow may contain many #GdkWindow. For example, each #GtkButton has a #GdkWindow associated with it. +Composited windows + + +/* The expose event handler for the event box. + * + * This function simply draws a transparency onto a widget on the area + * for which it receives expose events. This is intended to give the + * event box a "transparent" background. + * + * In order for this to work properly, the widget must have an RGBA + * colourmap. The widget should also be set as app-paintable since it + * doesn't make sense for GTK+ to draw a background if we are drawing it + * (and because GTK+ might actually replace our transparency with its + * default background colour). + */ +static gboolean +transparent_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + cairo_t *cr; + + cr = gdk_cairo_create (widget->window); + cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); + gdk_cairo_region (cr, event->region); + cairo_fill (cr); + cairo_destroy (cr); + + return FALSE; +} + +/* The expose event handler for the window. + * + * This function performs the actual compositing of the event box onto + * the already-existing background of the window at 50% normal opacity. + * + * In this case we do not want app-paintable to be set on the widget + * since we want it to draw its own (red) background. Because of this, + * however, we must ensure that we use g_signal_register_after so that + * this handler is called after the red has been drawn. If it was + * called before then GTK would just blindly paint over our work. + * + * Note: if the child window has children, then you need a cairo 1.16 + * feature to make this work correctly. + */ +static gboolean +window_expose_event (GtkWidget *widget, + GdkEventExpose *event) +{ + GdkRegion *region; + GtkWidget *child; + cairo_t *cr; + + /* get our child (in this case, the event box) */ + child = gtk_bin_get_child (GTK_BIN (widget)); + + /* create a cairo context to draw to the window */ + cr = gdk_cairo_create (widget->window); + + /* the source data is the (composited) event box */ + gdk_cairo_set_source_pixmap (cr, child->window, + child->allocation.x, + child->allocation.y); + + /* draw no more than our expose event intersects our child */ + region = gdk_region_rectangle (&child->allocation); + gdk_region_intersect (region, event->region); + gdk_cairo_region (cr, region); + cairo_clip (cr); + + /* composite, with a 50% opacity */ + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + cairo_paint_with_alpha (cr, 0.5); + + /* we're done */ + cairo_destroy (cr); + + return FALSE; +} + +int +main (int argc, char **argv) +{ + GtkWidget *window, *event, *button; + GdkScreen *screen; + GdkColormap *rgba; + GdkColor red; + + gtk_init (&argc, &argv); + + /* Make the widgets */ + button = gtk_button_new_with_label ("A Button"); + event = gtk_event_box_new (); + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + /* Put a red background on the window */ + gdk_color_parse ("red", &red); + gtk_widget_modify_bg (window, GTK_STATE_NORMAL, &red); + + /* Set the colourmap for the event box. + * Must be done before the event box is realised. + */ + screen = gtk_widget_get_screen (event); + rgba = gdk_screen_get_rgba_colormap (screen); + gtk_widget_set_colormap (event, rgba); + + /* Set our event box to have a fully-transparent background + * drawn on it. Currently there is no way to simply tell GTK+ + * that "transparency" is the background colour for a widget. + */ + gtk_widget_set_app_paintable (GTK_WIDGET (event), TRUE); + g_signal_connect (event, "expose-event", + G_CALLBACK (transparent_expose), NULL); + + /* Put them inside one another */ + gtk_container_set_border_width (GTK_CONTAINER (window), 10); + gtk_container_add (GTK_CONTAINER (window), event); + gtk_container_add (GTK_CONTAINER (event), button); + + /* Realise and show everything */ + gtk_widget_show_all (window); + + /* Set the event box GdkWindow to be composited. + * Obviously must be performed after event box is realised. + */ + gdk_window_set_composited (event->window, TRUE); + + /* Set up the compositing handler. + * Note that we do _after_ so that the normal (red) background is drawn + * by gtk before our compositing occurs. + */ + g_signal_connect_after (window, "expose-event", + G_CALLBACK (window_expose_event), NULL); + + gtk_main (); + + return 0; +} +]]> + + +In the example , a button is +placed inside of an event box inside of a window. The event box is +set as composited and therefore is no longer automatically drawn to +the screen. + + +When the contents of the event box change, an expose event is +generated on its parent window (which, in this case, belongs to +the toplevel #GtkWindow). The expose handler for this widget is +responsible for merging the changes back on the screen in the way +that it wishes. + + +In our case, we merge the contents with a 50% transparency. We +also set the background colour of the window to red. The effect is +that the background shows through the button. + @@ -465,6 +623,15 @@ Deprecated equivalent of g_object_unref() @opacity: + + + + + +@window: +@composited: + + diff --git a/gdk/directfb/gdkdisplay-directfb.c b/gdk/directfb/gdkdisplay-directfb.c index 9cf7a2fdb1..04275fee27 100644 --- a/gdk/directfb/gdkdisplay-directfb.c +++ b/gdk/directfb/gdkdisplay-directfb.c @@ -511,6 +511,13 @@ gdk_notify_startup_complete_with_id (const gchar* startup_id) { } + +gboolean +gdk_display_supports_composite (GdkDisplay *display) +{ + return FALSE; +} + #define __GDK_DISPLAY_X11_C__ #include "gdkaliasdef.c" diff --git a/gdk/directfb/gdkwindow-directfb.c b/gdk/directfb/gdkwindow-directfb.c index 59e28b7393..7fe3911852 100644 --- a/gdk/directfb/gdkwindow-directfb.c +++ b/gdk/directfb/gdkwindow-directfb.c @@ -3038,6 +3038,14 @@ gdk_window_set_opacity (GdkWindow *window, cardinal = opacity * 0xff; gdk_directfb_window_set_opacity(window,cardinal); } + +void +_gdk_windowing_window_set_composited (GdkWindow *window, + gboolean composited) +{ +} + + #define __GDK_WINDOW_X11_C__ #include "gdkaliasdef.c" diff --git a/gdk/gdk.symbols b/gdk/gdk.symbols index 56722e7cb6..8757324fcc 100644 --- a/gdk/gdk.symbols +++ b/gdk/gdk.symbols @@ -466,6 +466,7 @@ gdk_display_supports_clipboard_persistence gdk_display_supports_selection_notification gdk_display_supports_shapes gdk_display_supports_input_shapes +gdk_display_supports_composite #endif #endif @@ -673,6 +674,7 @@ gdk_window_remove_filter gdk_window_set_debug_updates gdk_window_set_user_data gdk_window_thaw_updates +gdk_window_set_composited #endif #endif diff --git a/gdk/gdkdisplay.h b/gdk/gdkdisplay.h index c627a08581..b55b312257 100644 --- a/gdk/gdkdisplay.h +++ b/gdk/gdkdisplay.h @@ -181,6 +181,7 @@ void gdk_display_store_clipboard (GdkDisplay *display, gboolean gdk_display_supports_shapes (GdkDisplay *display); gboolean gdk_display_supports_input_shapes (GdkDisplay *display); +gboolean gdk_display_supports_composite (GdkDisplay *display); G_END_DECLS diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h index 1c279f4ec3..f97b97be04 100644 --- a/gdk/gdkinternals.h +++ b/gdk/gdkinternals.h @@ -337,6 +337,9 @@ void _gdk_windowing_window_destroy_foreign (GdkWindow *window); void _gdk_windowing_display_set_sm_client_id (GdkDisplay *display, const gchar *sm_client_id); +void _gdk_windowing_window_set_composited (GdkWindow *window, + gboolean composited); + #define GDK_TYPE_PAINTABLE (_gdk_paintable_get_type ()) #define GDK_PAINTABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_PAINTABLE, GdkPaintable)) #define GDK_IS_PAINTABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_PAINTABLE)) diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index d0d1573a6e..5e1d830698 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -1042,6 +1042,7 @@ gdk_window_end_paint (GdkWindow *window) { #ifdef USE_BACKING_STORE GdkWindowObject *private = (GdkWindowObject *)window; + GdkWindowObject *composited; GdkWindowPaint *paint; GdkGC *tmp_gc; GdkRectangle clip_box; @@ -1094,6 +1095,34 @@ gdk_window_end_paint (GdkWindow *window) g_object_unref (paint->pixmap); gdk_region_destroy (paint->region); g_free (paint); + + /* find a composited window in our hierarchy to signal its + * parent to redraw, calculating the clip box as we go... + * + * stop if parent becomes NULL since then we'd have nowhere + * to draw (ie: 'composited' will always be non-NULL here). + */ + for (composited = private; + composited->parent; + composited = composited->parent) + { + int width, height; + + gdk_drawable_get_size (GDK_DRAWABLE (composited->parent), + &width, &height); + + clip_box.x += composited->x; + clip_box.y += composited->y; + clip_box.width = MIN (clip_box.width, width - clip_box.x); + clip_box.height = MIN (clip_box.height, height - clip_box.y); + + if (composited->composited) + { + gdk_window_invalidate_rect (GDK_WINDOW (composited->parent), + &clip_box, FALSE); + break; + } + } #endif /* USE_BACKING_STORE */ } @@ -2601,7 +2630,8 @@ gdk_window_invalidate_maybe_recurse (GdkWindow *window, child_region = gdk_region_rectangle (&child_rect); /* remove child area from the invalid area of the parent */ - if (GDK_WINDOW_IS_MAPPED (child) && !child->shaped) + if (GDK_WINDOW_IS_MAPPED (child) && !child->shaped && + !child->composited) gdk_region_subtract (visible_region, child_region); if (child_func && (*child_func) ((GdkWindow *)child, user_data)) @@ -3056,5 +3086,64 @@ gdk_window_foreign_new (GdkNativeWindow anid) return gdk_window_foreign_new_for_display (gdk_display_get_default (), anid); } +/** + * gdk_window_set_composited: + * @window: a #GdkWindow + * + * Sets a #GdkWindow as composited. Composited windows do + * not automatically have their contents drawn to the screen. + * Drawing is redirected to an offscreen buffer and an expose + * event is emitted on the parent of the composited window. + * It is the responsibility of the parent's expose handler to + * manually merge the off-screen content onto the screen in + * whatever way it sees fit. See + * for an example. + * + * It only makes sense for child windows to be composited; see + * gdk_window_set_opacity() if you need translucent toplevel + * windows. + * + * An additional effect of this call is that the area of this + * window is no longer clipped from regions marked for + * invalidation on its parent. Draws done on the parent + * window are also no longer clipped by the child. + * + * This call is only supported on some systems (currently, + * only X11 with new enough Xcomposite and Xdamage extensions). + * You must call gdk_display_supports_composite() to check if + * setting a window as composited is supported before + * attempting to do so. + * + * Since: 2.12 + */ +void +gdk_window_set_composited (GdkWindow *window, + gboolean composited) +{ + GdkWindowObject *private = (GdkWindowObject *)window; + GdkDisplay *display; + + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + composited = composited != FALSE; + + if (private->composited == composited) + return; + + display = gdk_drawable_get_display (GDK_DRAWABLE (window)); + + if (!gdk_display_supports_composite (display) && composited) + { + g_warning ("gdk_window_set_composited called but " + "compositing is not supported"); + return; + } + + _gdk_windowing_window_set_composited (window, composited); + + private->composited = composited; +} + #define __GDK_WINDOW_C__ #include "gdkaliasdef.c" diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h index 9ff3a09fca..d50d3e6c9a 100644 --- a/gdk/gdkwindow.h +++ b/gdk/gdkwindow.h @@ -289,6 +289,7 @@ struct _GdkWindowObject guint guffaw_gravity : 1; guint input_only : 1; guint modal_hint : 1; + guint composited : 1; guint destroyed : 2; @@ -394,6 +395,9 @@ void gdk_window_shape_combine_region (GdkWindow *window, */ void gdk_window_set_child_shapes (GdkWindow *window); +void gdk_window_set_composited (GdkWindow *window, + gboolean composited); + /* * This routine allows you to merge (ie ADD) child shapes to your * own window's shape keeping its current shape and ADDING the child diff --git a/gdk/quartz/gdkdisplay-quartz.c b/gdk/quartz/gdkdisplay-quartz.c index b34567eaf7..3f9a01f254 100644 --- a/gdk/quartz/gdkdisplay-quartz.c +++ b/gdk/quartz/gdkdisplay-quartz.c @@ -169,3 +169,11 @@ gdk_display_store_clipboard (GdkDisplay *display, { /* FIXME: Implement */ } + + +gboolean +gdk_display_supports_composite (GdkDisplay *display) +{ + /* FIXME: Implement */ + return FALSE; +} diff --git a/gdk/quartz/gdkwindow-quartz.c b/gdk/quartz/gdkwindow-quartz.c index 31f2b01a39..51f253ecf2 100644 --- a/gdk/quartz/gdkwindow-quartz.c +++ b/gdk/quartz/gdkwindow-quartz.c @@ -2155,3 +2155,8 @@ gdk_window_set_opacity (GdkWindow *window, [impl->toplevel setAlphaValue: opacity]; } + +void +_gdk_windowing_window_set_composited (GdkWindow *window, gboolean composited) +{ +} diff --git a/gdk/win32/gdkdisplay-win32.c b/gdk/win32/gdkdisplay-win32.c index 5b77fb11bb..78c26bf779 100644 --- a/gdk/win32/gdkdisplay-win32.c +++ b/gdk/win32/gdkdisplay-win32.c @@ -389,3 +389,9 @@ gdk_display_supports_input_shapes (GdkDisplay *display) return FALSE; } + +gboolean +gdk_display_supports_composite (GdkDisplay *display) +{ + return FALSE; +} diff --git a/gdk/win32/gdkwindow-win32.c b/gdk/win32/gdkwindow-win32.c index 631d4200d1..fad5644a3b 100644 --- a/gdk/win32/gdkwindow-win32.c +++ b/gdk/win32/gdkwindow-win32.c @@ -3571,3 +3571,8 @@ gdk_window_set_opacity (GdkWindow *window, opacity * 0xff, LWA_ALPHA)); } + +void +_gdk_windowing_window_set_composited (GdkWindow *window, gboolean composited) +{ +} diff --git a/gdk/x11/gdkdisplay-x11.c b/gdk/x11/gdkdisplay-x11.c index c57ef2e833..de7c1f2f83 100644 --- a/gdk/x11/gdkdisplay-x11.c +++ b/gdk/x11/gdkdisplay-x11.c @@ -54,6 +54,14 @@ #include #endif +#ifdef HAVE_XCOMPOSITE +#include +#endif + +#ifdef HAVE_XDAMAGE +#include +#endif + static void gdk_display_x11_dispose (GObject *object); static void gdk_display_x11_finalize (GObject *object); @@ -206,6 +214,29 @@ gdk_display_open (const gchar *display_name) #endif display_x11->have_xfixes = FALSE; +#ifdef HAVE_XCOMPOSITE + if (XCompositeQueryExtension (display_x11->xdisplay, + &ignore, &ignore)) + display_x11->have_xcomposite = TRUE; + else +#endif + display_x11->have_xcomposite = FALSE; + +#ifdef HAVE_XDAMAGE + if (XDamageQueryExtension (display_x11->xdisplay, + &display_x11->xdamage_event_base, + &ignore)) + { + display_x11->have_xdamage = TRUE; + + gdk_x11_register_standard_event_type (display, + display_x11->xdamage_event_base, + XDamageNumberEvents); + } + else +#endif + display_x11->have_xdamage = FALSE; + display_x11->have_shapes = FALSE; display_x11->have_input_shapes = FALSE; #ifdef HAVE_SHAPE_EXT @@ -1363,5 +1394,30 @@ gdk_x11_display_get_startup_notification_id (GdkDisplay *display) return GDK_DISPLAY_X11 (display)->startup_notification_id; } +/** + * gdk_display_supports_composite: + * @display: a #GdkDisplay + * + * Returns %TRUE if gdk_window_set_composited() can be used + * to redirect drawing on the window using compositing. + * + * Currently this only works on X11 with XComposite and + * XDamage extensions available. + * + * Returns: %TRUE if windows may be composited. + * + * Since: 2.12 + */ +gboolean +gdk_display_supports_composite (GdkDisplay *display) +{ + GdkDisplayX11 *x11_display = GDK_DISPLAY_X11 (display); + + return x11_display->have_xcomposite && + x11_display->have_xdamage && + x11_display->have_xfixes; +} + + #define __GDK_DISPLAY_X11_C__ #include "gdkaliasdef.c" diff --git a/gdk/x11/gdkdisplay-x11.h b/gdk/x11/gdkdisplay-x11.h index 14b8fd5b29..7b2654cd7c 100644 --- a/gdk/x11/gdkdisplay-x11.h +++ b/gdk/x11/gdkdisplay-x11.h @@ -81,6 +81,10 @@ struct _GdkDisplayX11 gboolean have_xfixes; gint xfixes_event_base; + gboolean have_xcomposite; + gboolean have_xdamage; + gint xdamage_event_base; + /* If the SECURITY extension is in place, whether this client holds * a trusted authorization and so is allowed to make various requests * (grabs, properties etc.) Otherwise always TRUE. */ diff --git a/gdk/x11/gdkevents-x11.c b/gdk/x11/gdkevents-x11.c index 6df08bb3df..b9560d46ed 100644 --- a/gdk/x11/gdkevents-x11.c +++ b/gdk/x11/gdkevents-x11.c @@ -2101,6 +2101,34 @@ gdk_event_translate (GdkDisplay *display, } else #endif +#if defined(HAVE_XCOMPOSITE) && defined (HAVE_XDAMAGE) && defined (HAVE_XFIXES) + if (display_x11->have_xdamage && window_private->composited && + xevent->type == display_x11->xdamage_event_base + XDamageNotify) + { + XDamageNotifyEvent *damage_event = (XDamageNotifyEvent *) xevent; + XserverRegion repair; + GdkRectangle rect; + + rect.x = window_private->x + damage_event->area.x; + rect.y = window_private->y + damage_event->area.y; + rect.width = damage_event->area.width; + rect.height = damage_event->area.height; + + repair = XFixesCreateRegion (display_x11->xdisplay, + &damage_event->area, 1); + XDamageSubtract (display_x11->xdisplay, + window_impl->damage, + repair, None); + XFixesDestroyRegion (display_x11->xdisplay, repair); + + if (window_private->parent != NULL) + _gdk_window_process_expose (GDK_WINDOW (window_private->parent), + damage_event->serial, &rect); + + return_val = TRUE; + } + else +#endif { /* something else - (e.g., a Xinput event) */ diff --git a/gdk/x11/gdkwindow-x11.c b/gdk/x11/gdkwindow-x11.c index e3e84270d6..a58de4526d 100644 --- a/gdk/x11/gdkwindow-x11.c +++ b/gdk/x11/gdkwindow-x11.c @@ -59,6 +59,18 @@ #include #endif +#ifdef HAVE_XCOMPOSITE +#include +#endif + +#ifdef HAVE_XFIXES +#include +#endif + +#ifdef HAVE_XDAMAGE +#include +#endif + const int _gdk_event_mask_table[21] = { ExposureMask, @@ -185,6 +197,14 @@ gdk_window_impl_x11_finalize (GObject *object) _gdk_xgrab_check_destroy (GDK_WINDOW (wrapper)); +#if defined(HAVE_XCOMPOSITE) && defined(HAVE_XDAMAGE) && defined (HAVE_XFIXES) + if (window_impl->damage != None) + { + XDamageDestroy (GDK_WINDOW_XDISPLAY (object), window_impl->damage); + window_impl->damage = None; + } +#endif + if (!GDK_WINDOW_DESTROYED (wrapper)) { GdkDisplay *display = GDK_WINDOW_DISPLAY (wrapper); @@ -6413,10 +6433,14 @@ gdk_window_beep (GdkWindow *window) * * Request the windowing system to make @window partially transparent, * with opacity 0 being fully transparent and 1 fully opaque. (Values - * of the opacity parameter are clamped to the [0,1] range.) On X11 - * this works only on X screens with a compositing manager running. + * of the opacity parameter are clamped to the [0,1] range.) + * + * On X11, this works only on X screens with a compositing manager + * running. * * For setting up per-pixel alpha, see gdk_screen_get_rgba_colormap(). + * For making non-toplevel windows translucent, see + * gdk_window_set_composited(). * * Since: 2.12 */ @@ -6455,5 +6479,39 @@ gdk_window_set_opacity (GdkWindow *window, (guchar *) cardinal, 1); } +void +_gdk_windowing_window_set_composited (GdkWindow *window, + gboolean composited) +{ +#if defined(HAVE_XCOMPOSITE) && defined(HAVE_XDAMAGE) && defined (HAVE_XFIXES) + GdkWindowObject *private = (GdkWindowObject *) window; + GdkDisplayX11 *x11_display; + GdkWindowImplX11 *impl; + GdkDisplay *display; + Display *dpy; + Window xid; + + impl = GDK_WINDOW_IMPL_X11 (private->impl); + + display = gdk_screen_get_display (GDK_DRAWABLE_IMPL_X11 (impl)->screen); + x11_display = GDK_DISPLAY_X11 (display); + dpy = GDK_DISPLAY_XDISPLAY (display); + xid = GDK_WINDOW_XWINDOW (private); + + if (composited) + { + XCompositeRedirectWindow (dpy, xid, CompositeRedirectManual); + impl->damage = XDamageCreate (dpy, xid, XDamageReportBoundingBox); + } + else + { + XCompositeUnredirectWindow (dpy, xid, CompositeRedirectManual); + XDamageDestroy (dpy, impl->damage); + impl->damage = None; + } +#endif +} + + #define __GDK_WINDOW_X11_C__ #include "gdkaliasdef.c" diff --git a/gdk/x11/gdkwindow-x11.h b/gdk/x11/gdkwindow-x11.h index 18fa23a057..3ab23d6003 100644 --- a/gdk/x11/gdkwindow-x11.h +++ b/gdk/x11/gdkwindow-x11.h @@ -29,6 +29,10 @@ #include +#ifdef HAVE_XDAMAGE +#include +#endif + #ifdef HAVE_XSYNC #include #endif @@ -79,6 +83,10 @@ struct _GdkWindowImplX11 gint8 toplevel_window_type; guint override_redirect : 1; guint use_synchronized_configure : 1; + +#if defined (HAVE_XCOMPOSITE) && defined(HAVE_XDAMAGE) && defined (HAVE_XFIXES) + Damage damage; +#endif }; struct _GdkWindowImplX11Class diff --git a/tests/testgtk.c b/tests/testgtk.c index 82894e9706..44eb4fcc50 100644 --- a/tests/testgtk.c +++ b/tests/testgtk.c @@ -420,6 +420,146 @@ create_alpha_window (GtkWidget *widget) gtk_widget_destroy (window); } +/* + * Composited non-toplevel window + */ + +/* The expose event handler for the event box. + * + * This function simply draws a transparency onto a widget on the area + * for which it receives expose events. This is intended to give the + * event box a "transparent" background. + * + * In order for this to work properly, the widget must have an RGBA + * colourmap. The widget should also be set as app-paintable since it + * doesn't make sense for GTK to draw a background if we are drawing it + * (and because GTK might actually replace our transparency with its + * default background colour). + */ +static gboolean +transparent_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + cairo_t *cr; + + cr = gdk_cairo_create (widget->window); + cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); + gdk_cairo_region (cr, event->region); + cairo_fill (cr); + cairo_destroy (cr); + + return FALSE; +} + +/* The expose event handler for the window. + * + * This function performs the actual compositing of the event box onto + * the already-existing background of the window at 50% normal opacity. + * + * In this case we do not want app-paintable to be set on the widget + * since we want it to draw its own (red) background. Because of this, + * however, we must ensure that we use g_signal_register_after so that + * this handler is called after the red has been drawn. If it was + * called before then GTK would just blindly paint over our work. + */ +static gboolean +window_expose_event (GtkWidget *widget, + GdkEventExpose *event) +{ + GdkRegion *region; + GtkWidget *child; + cairo_t *cr; + + /* get our child (in this case, the event box) */ + child = gtk_bin_get_child (GTK_BIN (widget)); + + /* create a cairo context to draw to the window */ + cr = gdk_cairo_create (widget->window); + + /* the source data is the (composited) event box */ + gdk_cairo_set_source_pixmap (cr, child->window, + child->allocation.x, + child->allocation.y); + + /* draw no more than our expose event intersects our child */ + region = gdk_region_rectangle (&child->allocation); + gdk_region_intersect (region, event->region); + gdk_cairo_region (cr, region); + cairo_clip (cr); + + /* composite, with a 50% opacity */ + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + cairo_paint_with_alpha (cr, 0.5); + + /* we're done */ + cairo_destroy (cr); + + return FALSE; +} + +void +create_composited_window (GtkWidget *widget) +{ + static GtkWidget *window; + + if (!window) + { + GtkWidget *event, *button; + GdkScreen *screen; + GdkColormap *rgba; + GdkColor red; + + /* make the widgets */ + button = gtk_button_new_with_label ("A Button"); + event = gtk_event_box_new (); + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + /* put a red background on the window */ + gdk_color_parse ("red", &red); + gtk_widget_modify_bg (window, GTK_STATE_NORMAL, &red); + + /* set the colourmap for the event box. + * must be done before the event box is realised. + */ + screen = gtk_widget_get_screen (event); + rgba = gdk_screen_get_rgba_colormap (screen); + gtk_widget_set_colormap (event, rgba); + + /* set our event box to have a fully-transparent background + * drawn on it. currently there is no way to simply tell gtk + * that "transparency" is the background colour for a widget. + */ + gtk_widget_set_app_paintable (GTK_WIDGET (event), TRUE); + g_signal_connect (event, "expose-event", + G_CALLBACK (transparent_expose), NULL); + + /* put them inside one another */ + gtk_container_set_border_width (GTK_CONTAINER (window), 10); + gtk_container_add (GTK_CONTAINER (window), event); + gtk_container_add (GTK_CONTAINER (event), button); + + /* realise and show everything */ + gtk_widget_realize (button); + + /* set the event box GdkWindow to be composited. + * obviously must be performed after event box is realised. + */ + gdk_window_set_composited (event->window, TRUE); + + /* set up the compositing handler. + * note that we do _after so that the normal (red) background is drawn + * by gtk before our compositing occurs. + */ + g_signal_connect_after (window, "expose-event", + G_CALLBACK (window_expose_event), NULL); + } + + if (!GTK_WIDGET_VISIBLE (window)) + gtk_widget_show_all (window); + else + gtk_widget_destroy (window); +} + /* * Big windows and guffaw scrolling */ @@ -13290,6 +13430,7 @@ struct { { "check buttons", create_check_buttons }, { "clist", create_clist}, { "color selection", create_color_selection }, + { "composited window", create_composited_window }, { "ctree", create_ctree }, { "cursors", create_cursors }, { "dialog", create_dialog, TRUE },