X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gdk%2Fgdkwindow.c;h=6d48080294cb93cd9f915d058485eaf67147a283;hb=HEAD;hp=c13b9a88b88ddbe2f5756bb193532a27f5420397;hpb=8e28f53a1db931b62826f7ce4c47b81cbc4c4c05;p=~andy%2Fgtk diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index c13b9a88b..6d4808029 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -13,9 +13,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ /* @@ -39,10 +37,14 @@ #include "gdkdeviceprivate.h" #include "gdkvisualprivate.h" #include "gdkmarshalers.h" +#include "gdkframeclockidle.h" #include "gdkwindowimpl.h" #include +/* for the use of round() */ +#include "fallback-c89.c" + #undef DEBUG_WINDOW_PRINTING @@ -67,25 +69,6 @@ * composited window it is the responsibility of the * application to render the window contents at the right spot. * - * - * Composited windows - * - * FIXME: MISSING XINCLUDE CONTENT - * - * - * 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. - * * * * Offscreen Windows @@ -114,7 +97,7 @@ * be it a toplevel window or a child window. In this setup the * GdkWindow (and other GdkDrawables) were platform independent classes, * and the actual platform specific implementation was in a delegate - * object availible as "impl" in the window object. + * object available as "impl" in the window object. * * With the addition of client side windows and offscreen windows this * changes a bit. The application-visible GdkWindow object behaves as @@ -203,8 +186,9 @@ struct _GdkWindowPaint { cairo_region_t *region; cairo_surface_t *surface; + cairo_region_t *flushed; + guint8 alpha; guint uses_implicit : 1; - guint flushed : 1; }; typedef struct { @@ -255,6 +239,11 @@ static void gdk_window_invalidate_rect_full (GdkWindow *window, const GdkRectangle *rect, gboolean invalidate_children, ClearBg clear_bg); +static void _gdk_window_propagate_has_alpha_background (GdkWindow *window); +static cairo_surface_t *gdk_window_ref_impl_surface (GdkWindow *window); + +static void gdk_window_set_frame_clock (GdkWindow *window, + GdkFrameClock *clock); static guint signals[LAST_SIGNAL] = { 0 }; @@ -264,6 +253,43 @@ static const cairo_user_data_key_t gdk_window_cairo_key; G_DEFINE_ABSTRACT_TYPE (GdkWindow, gdk_window, G_TYPE_OBJECT) +#ifdef DEBUG_WINDOW_PRINTING +char * +print_region (cairo_region_t *region) +{ + GString *s = g_string_new ("{"); + if (cairo_region_is_empty (region)) + { + g_string_append (s, "empty"); + } + else + { + int num = cairo_region_num_rectangles (region); + cairo_rectangle_int_t r; + + if (num == 1) + { + cairo_region_get_rectangle (region, 0, &r); + g_string_append_printf (s, "%dx%d @%d,%d", r.width, r.height, r.x, r.y); + } + else + { + int i; + cairo_region_get_extents (region, &r); + g_string_append_printf (s, "extent: %dx%d @%d,%d, details: ", r.width, r.height, r.x, r.y); + for (i = 0; i < num; i++) + { + g_string_append_printf (s, "[%dx%d @%d,%d]", r.width, r.height, r.x, r.y); + if (i != num -1) + g_string_append (s, ", "); + } + } + } + g_string_append (s, "}"); + return g_string_free (s, FALSE); +} +#endif + GType _gdk_paintable_get_type (void) { @@ -296,6 +322,7 @@ gdk_window_init (GdkWindow *window) window->window_type = GDK_WINDOW_CHILD; window->state = GDK_WINDOW_STATE_WITHDRAWN; + window->fullscreen_mode = GDK_FULLSCREEN_ON_CURRENT_MONITOR; window->width = 1; window->height = 1; window->toplevel_window_type = -1; @@ -398,11 +425,11 @@ gdk_window_class_init (GdkWindowClass *klass) /** * GdkWindow::to-embedder: * @window: the offscreen window on which the signal is emitted - * @offscreen-x: x coordinate in the offscreen window - * @offscreen-y: y coordinate in the offscreen window - * @embedder-x: (out) (type double): return location for the x + * @offscreen_x: x coordinate in the offscreen window + * @offscreen_y: y coordinate in the offscreen window + * @embedder_x: (out) (type double): return location for the x * coordinate in the embedder window - * @embedder-y: (out) (type double): return location for the y + * @embedder_y: (out) (type double): return location for the y * coordinate in the embedder window * * The ::to-embedder signal is emitted to translate coordinates @@ -429,11 +456,11 @@ gdk_window_class_init (GdkWindowClass *klass) /** * GdkWindow::from-embedder: * @window: the offscreen window on which the signal is emitted - * @embedder-x: x coordinate in the embedder window - * @embedder-y: y coordinate in the embedder window - * @offscreen-x: (out) (type double): return location for the x + * @embedder_x: x coordinate in the embedder window + * @embedder_y: y coordinate in the embedder window + * @offscreen_x: (out) (type double): return location for the x * coordinate in the offscreen window - * @offscreen-y: (out) (type double): return location for the y + * @offscreen_y: (out) (type double): return location for the y * coordinate in the offscreen window * * The ::from-embedder signal is emitted to translate coordinates @@ -561,6 +588,9 @@ gdk_window_finalize (GObject *object) if (window->devices_inside) g_list_free (window->devices_inside); + if (window->layered_region) + cairo_region_destroy (window->layered_region); + G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -648,8 +678,68 @@ gdk_window_has_no_impl (GdkWindow *window) return window->impl_window != window; } +static gboolean +gdk_window_has_alpha (GdkWindow *window) +{ + return !gdk_window_has_impl (window) && + (window->has_alpha_background || window->alpha != 255); +} + +static void +remove_layered_child_area (GdkWindow *window, + cairo_region_t *region) +{ + GdkWindow *child; + cairo_region_t *child_region; + GdkRectangle r; + GList *l; + + for (l = window->children; l; l = l->next) + { + child = l->data; + + /* If region is empty already, no need to do + anything potentially costly */ + if (cairo_region_is_empty (region)) + break; + + if (!GDK_WINDOW_IS_MAPPED (child) || child->input_only || child->composited) + continue; + + /* Ignore offscreen children, as they don't draw in their parent and + * don't take part in the clipping */ + if (gdk_window_is_offscreen (child)) + continue; + + /* Only non-impl children with alpha add to the layered region */ + if (!gdk_window_has_alpha (child)) + continue; + + r.x = child->x; + r.y = child->y; + r.width = child->width; + r.height = child->height; + + /* Bail early if child totally outside region */ + if (cairo_region_contains_rectangle (region, &r) == CAIRO_REGION_OVERLAP_OUT) + continue; + + child_region = cairo_region_create_rectangle (&r); + if (child->shape) + { + /* Adjust shape region to parent window coords */ + cairo_region_translate (child->shape, child->x, child->y); + cairo_region_intersect (child_region, child->shape); + cairo_region_translate (child->shape, -child->x, -child->y); + } + + cairo_region_subtract (region, child_region); + cairo_region_destroy (child_region); + } +} + static void -remove_child_area (GdkWindow *private, +remove_child_area (GdkWindow *window, GdkWindow *until, gboolean for_input, cairo_region_t *region, @@ -661,7 +751,7 @@ remove_child_area (GdkWindow *private, GList *l; cairo_region_t *shape; - for (l = private->children; l; l = l->next) + for (l = window->children; l; l = l->next) { child = l->data; @@ -699,7 +789,7 @@ remove_child_area (GdkWindow *private, cairo_region_intersect (child_region, child->shape); cairo_region_translate (child->shape, -child->x, -child->y); } - else if (private->window_type == GDK_WINDOW_FOREIGN) + else if (window->window_type == GDK_WINDOW_FOREIGN) { shape = GDK_WINDOW_IMPL_GET_CLASS (child)->get_shape (child); if (shape) @@ -713,7 +803,7 @@ remove_child_area (GdkWindow *private, { if (child->input_shape) cairo_region_intersect (child_region, child->input_shape); - else if (private->window_type == GDK_WINDOW_FOREIGN) + else if (window->window_type == GDK_WINDOW_FOREIGN) { shape = GDK_WINDOW_IMPL_GET_CLASS (child)->get_input_shape (child); if (shape) @@ -724,7 +814,7 @@ remove_child_area (GdkWindow *private, } } - if (child->has_alpha_background) + if (gdk_window_has_alpha (child)) { if (layered_region != NULL) cairo_region_union (layered_region, child_region); @@ -732,7 +822,6 @@ remove_child_area (GdkWindow *private, else cairo_region_subtract (region, child_region); cairo_region_destroy (child_region); - } } @@ -768,7 +857,7 @@ gdk_window_update_visibility (GdkWindow *window) window->effective_visibility = new_visibility; if (new_visibility != GDK_VISIBILITY_NOT_VIEWABLE && - window->event_mask & GDK_VISIBILITY_NOTIFY) + window->event_mask & GDK_VISIBILITY_NOTIFY_MASK) { event = _gdk_make_event (window, GDK_VISIBILITY_NOTIFY, NULL, FALSE); @@ -1189,11 +1278,12 @@ get_native_device_event_mask (GdkWindow *private, * lists due to some non-native child window. */ if (gdk_window_is_toplevel (private) || - mask & GDK_BUTTON_PRESS_MASK) - mask |= - GDK_POINTER_MOTION_MASK | - GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | - GDK_SCROLL_MASK; + mask & GDK_BUTTON_PRESS_MASK) + mask |= + GDK_TOUCH_MASK | + GDK_POINTER_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_SCROLL_MASK; return mask; } @@ -1245,7 +1335,8 @@ sync_native_window_stack_position (GdkWindow *window) * @parent: (allow-none): 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 + * @attributes_mask: (type GdkWindowAttributesType): mask indicating which + * fields in @attributes are valid * * Creates a new #GdkWindow using the attributes from * @attributes. See #GdkWindowAttr and #GdkWindowAttributesType for @@ -1289,6 +1380,11 @@ gdk_window_new (GdkWindow *parent, return NULL; } + if (attributes_mask & GDK_WA_VISUAL) + { + g_return_val_if_fail (gdk_visual_get_screen (attributes->visual) == screen, NULL); + } + display = gdk_screen_get_display (screen); window = _gdk_display_create_window (display); @@ -1319,6 +1415,7 @@ gdk_window_new (GdkWindow *parent, window->y = y; window->width = (attributes->width > 1) ? (attributes->width) : (1); window->height = (attributes->height > 1) ? (attributes->height) : (1); + window->alpha = 255; if (attributes->wclass == GDK_INPUT_ONLY) { @@ -1364,7 +1461,8 @@ gdk_window_new (GdkWindow *parent, window->depth = window->visual->depth; /* XXX: Cache this somehow? */ - window->background = cairo_pattern_create_rgb (0, 0, 0); + window->background = cairo_pattern_create_rgba (0, 0, 0, 0); + window->has_alpha_background = TRUE; } else { @@ -1375,6 +1473,13 @@ gdk_window_new (GdkWindow *parent, if (window->parent) window->parent->children = g_list_prepend (window->parent->children, window); + if (window->parent->window_type == GDK_WINDOW_ROOT) + { + GdkFrameClock *frame_clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE, NULL); + gdk_window_set_frame_clock (window, frame_clock); + g_object_unref (frame_clock); + } + native = FALSE; if (window->parent->window_type == GDK_WINDOW_ROOT) native = TRUE; /* Always use native windows for toplevels */ @@ -1625,6 +1730,28 @@ gdk_window_reparent (GdkWindow *window, } } + /* If we changed the window type, we might have to set or + * unset the frame clock on the window + */ + if (GDK_WINDOW_TYPE (new_parent) == GDK_WINDOW_ROOT && + GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN) + { + if (window->frame_clock == NULL) + { + GdkFrameClock *frame_clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE, NULL); + gdk_window_set_frame_clock (window, frame_clock); + g_object_unref (frame_clock); + } + } + else + { + if (window->frame_clock != NULL) + { + g_object_run_dispose (G_OBJECT (window->frame_clock)); + gdk_window_set_frame_clock (window, NULL); + } + } + /* We might have changed window type for a native windows, so we need to change the event mask too. */ if (gdk_window_has_impl (window)) @@ -1637,10 +1764,22 @@ gdk_window_reparent (GdkWindow *window, _gdk_window_update_viewable (window); + if (window->background == NULL) + { + /* parent relative background, update has_alpha_background */ + if (window->parent == NULL || + window->parent->window_type == GDK_WINDOW_ROOT) + window->has_alpha_background = FALSE; + else + window->has_alpha_background = window->parent->has_alpha_background; + } + recompute_visible_regions (window, TRUE, FALSE); if (old_parent && GDK_WINDOW_TYPE (old_parent) != GDK_WINDOW_ROOT) recompute_visible_regions (old_parent, FALSE, TRUE); + _gdk_window_propagate_has_alpha_background (window); + /* We used to apply the clip as the shape, but no more. Reset this to the real shape */ if (gdk_window_has_impl (window) && @@ -1932,6 +2071,12 @@ _gdk_window_destroy_hierarchy (GdkWindow *window, } } + if (window->frame_clock) + { + g_object_run_dispose (G_OBJECT (window->frame_clock)); + gdk_window_set_frame_clock (window, NULL); + } + gdk_window_free_paint_stack (window); if (window->background) @@ -1997,12 +2142,8 @@ _gdk_window_destroy_hierarchy (GdkWindow *window, window->clip_region_with_children = NULL; } - if (window->outstanding_moves) - { - g_list_foreach (window->outstanding_moves, (GFunc)gdk_window_region_move_free, NULL); - g_list_free (window->outstanding_moves); - window->outstanding_moves = NULL; - } + g_list_free_full (window->outstanding_moves, (GDestroyNotify) gdk_window_region_move_free); + window->outstanding_moves = NULL; } break; } @@ -2606,7 +2747,7 @@ gdk_window_get_content (GdkWindow *window) g_return_val_if_fail (GDK_IS_WINDOW (window), 0); - surface = _gdk_window_ref_cairo_surface (window); + surface = gdk_window_ref_impl_surface (window); content = cairo_surface_get_content (surface); cairo_surface_destroy (surface); @@ -2625,7 +2766,8 @@ gdk_window_get_content (GdkWindow *window) * directly to the window or a child window. */ static gboolean -gdk_window_begin_implicit_paint (GdkWindow *window, GdkRectangle *rect) +gdk_window_begin_implicit_paint (GdkWindow *window, GdkRectangle *rect, + gboolean with_alpha, guint8 alpha) { GdkWindowPaint *paint; @@ -2634,8 +2776,7 @@ gdk_window_begin_implicit_paint (GdkWindow *window, GdkRectangle *rect) if (GDK_IS_PAINTABLE (window->impl)) return FALSE; /* Implementation does double buffering */ - if (window->paint_stack != NULL || - window->implicit_paint != NULL) + if (window->paint_stack != NULL) return FALSE; /* Don't stack implicit paints */ /* Never do implicit paints for foreign windows, they don't need @@ -2649,14 +2790,15 @@ gdk_window_begin_implicit_paint (GdkWindow *window, GdkRectangle *rect) paint = g_new (GdkWindowPaint, 1); paint->region = cairo_region_create (); /* Empty */ paint->uses_implicit = FALSE; - paint->flushed = FALSE; + paint->flushed = NULL; + paint->alpha = alpha; paint->surface = gdk_window_create_similar_surface (window, - gdk_window_get_content (window), + with_alpha ? CAIRO_CONTENT_COLOR_ALPHA : gdk_window_get_content (window), MAX (rect->width, 1), MAX (rect->height, 1)); cairo_surface_set_device_offset (paint->surface, -rect->x, -rect->y); - window->implicit_paint = paint; + window->implicit_paint = g_slist_prepend (window->implicit_paint, paint); return TRUE; } @@ -2681,55 +2823,66 @@ gdk_cairo_create_for_impl (GdkWindow *window) return cr; } -/* Ensure that all content related to this (sub)window is pushed to the - native region. If there is an active paint then that area is not - pushed, in order to not show partially finished double buffers. */ +/* This is called whenever something is drawing directly to the + * window, bypassing the double buffering. When this happens we + * need to mark any the currently drawn data in the double buffer + * as invalid to avoid later drawing it back over the directly + * rendered pixels. We also need to mark this region as "flushed" + * so that if we later try to paint on it double-buffered we need + * to read back the on-window pixels rather than relying on what + * is in the current double-buffer pixmap. + * + * Note that this doesn't correctly handle the case where the + * non-double buffered drawing uses transparency and relies on + * what the windows below it draws. A fix for that would require + * drawing the existing double-buffered background to the window, + * but that causes ugly flashes. Non-double buffered drawing is + * typically only used in old code or when the drawed widget + * already has a double-buffering layer, and in these cases the + * pixels are opaque anyway. If you need transparency, don't + * disable double buffering. + */ static void gdk_window_flush_implicit_paint (GdkWindow *window) { GdkWindow *impl_window; GdkWindowPaint *paint; cairo_region_t *region; - GSList *list; + GSList *l; impl_window = gdk_window_get_impl_window (window); if (impl_window->implicit_paint == NULL) return; - paint = impl_window->implicit_paint; - paint->flushed = TRUE; - region = cairo_region_copy (window->clip_region_with_children); - - /* Don't flush active double buffers, as that may show partially done - * rendering */ - for (list = window->paint_stack; list != NULL; list = list->next) - { - GdkWindowPaint *tmp_paint = list->data; + /* There is no proper way to flush any non-toplevel implicit + paint, because direct rendering is not compatible with + opacity rendering, as it requires an opacity group. - cairo_region_subtract (region, tmp_paint->region); - } + We warn and flush just the outermost paint in this case. + */ + l = g_slist_last (impl_window->implicit_paint); + if (l != impl_window->implicit_paint) + g_warning ("Non double buffered drawing not supported inside transparent windows"); - cairo_region_translate (region, -window->abs_x, -window->abs_y); - cairo_region_intersect (region, paint->region); + paint = l->data; - if (!GDK_WINDOW_DESTROYED (window) && !cairo_region_is_empty (region)) - { - cairo_t *cr; + region = cairo_region_copy (window->clip_region_with_children); + cairo_region_translate (region, window->abs_x, window->abs_y); - /* Remove flushed region from the implicit paint */ - cairo_region_subtract (paint->region, region); + /* Anything in the whole flushed window that was drawn is now + considered unpainted, so that we don't push it back at the + end of the implicit paint overwriting the directly rendered + pixels. */ + cairo_region_subtract (paint->region, region); - /* Some regions are valid, push these to window now */ - cr = gdk_cairo_create_for_impl (window); - gdk_cairo_region (cr, region); - cairo_clip (cr); - cairo_set_source_surface (cr, paint->surface, 0, 0); - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_paint (cr); - cairo_destroy (cr); + /* Save flushed area so we can read it back if we draw over it later */ + if (paint->flushed == NULL) + paint->flushed = region; + else + { + cairo_region_union (paint->flushed, region); + cairo_region_destroy (region); } - - cairo_region_destroy (region); } /* Ends an implicit paint, paired with gdk_window_begin_implicit_paint returning TRUE */ @@ -2742,26 +2895,72 @@ gdk_window_end_implicit_paint (GdkWindow *window) g_assert (window->implicit_paint != NULL); - paint = window->implicit_paint; + paint = window->implicit_paint->data; - window->implicit_paint = NULL; + window->implicit_paint = g_slist_delete_link (window->implicit_paint, + window->implicit_paint); - if (!GDK_WINDOW_DESTROYED (window) && !cairo_region_is_empty (paint->region)) + if (!GDK_WINDOW_DESTROYED (window) && !cairo_region_is_empty (paint->region) && paint->alpha > 0) { + GdkWindowPaint *parent_paint = NULL; cairo_t *cr; /* Some regions are valid, push these to window now */ - cr = gdk_cairo_create_for_impl (window); + if (window->implicit_paint) + { + parent_paint = window->implicit_paint->data; + + /* If the toplevel implicit paint was flushed, restore it now + before we blend over it. */ + if (parent_paint->flushed != NULL && + !cairo_region_is_empty (parent_paint->flushed)) + { + cairo_surface_t *source_surface = gdk_window_ref_impl_surface (window); + cairo_region_t *flushed = cairo_region_copy (parent_paint->flushed); + + cr = cairo_create (parent_paint->surface); + cairo_set_source_surface (cr, source_surface, 0, 0); + cairo_surface_destroy (source_surface); + + cairo_region_intersect (flushed, paint->region); + gdk_cairo_region (cr, flushed); + cairo_clip (cr); + + cairo_region_subtract (parent_paint->flushed, flushed); + cairo_region_destroy (flushed); + + cairo_paint (cr); + cairo_destroy (cr); + } + + cr = cairo_create (parent_paint->surface); + } + else + cr = gdk_cairo_create_for_impl (window); + gdk_cairo_region (cr, paint->region); cairo_clip (cr); cairo_set_source_surface (cr, paint->surface, 0, 0); - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_paint (cr); + if (paint->alpha == 255) + { + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_paint (cr); + } + else + { + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + cairo_paint_with_alpha (cr, paint->alpha / 255.0); + } + cairo_destroy (cr); + + if (parent_paint) + cairo_region_union (parent_paint->region, paint->region); } - - cairo_region_destroy (paint->region); + cairo_region_destroy (paint->region); + if (paint->flushed) + cairo_region_destroy (paint->flushed); cairo_surface_destroy (paint->surface); g_free (paint); } @@ -2860,7 +3059,7 @@ gdk_window_begin_paint_region (GdkWindow *window, } impl_window = gdk_window_get_impl_window (window); - implicit_paint = impl_window->implicit_paint; + implicit_paint = impl_window->implicit_paint != NULL ? impl_window->implicit_paint->data : NULL; paint = g_new (GdkWindowPaint, 1); paint->region = cairo_region_copy (region); @@ -2895,6 +3094,44 @@ gdk_window_begin_paint_region (GdkWindow *window, MAX (clip_box.width, 1), MAX (clip_box.height, 1)); } + + /* Normally alpha backgrounded client side windows are composited on the implicit paint + by being drawn in back to front order. However, if implicit paints are not used, for + instance if it was flushed due to a non-double-buffered paint in the middle of the + expose we need to copy in the existing data here. */ + if (gdk_window_has_alpha (window) && + (!implicit_paint || + (implicit_paint && implicit_paint->flushed != NULL && !cairo_region_is_empty (implicit_paint->flushed)))) + { + cairo_t *cr = cairo_create (paint->surface); + /* We can't use gdk_cairo_set_source_window here, as that might + flush the implicit paint at an unfortunate time, since this + would be detected as a draw during non-expose time */ + cairo_surface_t *source_surface = gdk_window_ref_impl_surface (impl_window); + cairo_set_source_surface (cr, source_surface, + - (window->abs_x + clip_box.x), + - (window->abs_y + clip_box.y)); + cairo_surface_destroy (source_surface); + + /* Only read back the flushed area if any */ + if (implicit_paint) + { + cairo_region_t *flushed = cairo_region_copy (implicit_paint->flushed); + /* Convert from impl coords */ + cairo_region_translate (flushed, -window->abs_x, -window->abs_y); + cairo_region_intersect (flushed, paint->region); + gdk_cairo_region (cr, flushed); + cairo_clip (cr); + + /* Convert to impl coords */ + cairo_region_translate (flushed, window->abs_x, window->abs_y); + cairo_region_subtract (implicit_paint->flushed, flushed); + cairo_region_destroy (flushed); + } + cairo_paint (cr); + cairo_destroy (cr); + } + cairo_surface_set_device_offset (paint->surface, -clip_box.x, -clip_box.y); for (list = window->paint_stack; list != NULL; list = list->next) @@ -3190,6 +3427,8 @@ move_region_on_impl (GdkWindow *impl_window, cairo_region_t *region, /* In impl window coords */ int dx, int dy) { + GSList *l; + if ((dx == 0 && dy == 0) || cairo_region_is_empty (region)) { @@ -3228,9 +3467,9 @@ move_region_on_impl (GdkWindow *impl_window, /* If we're currently exposing this window, don't copy to this destination, as it will be overdrawn when the expose is done, instead invalidate it and repaint later. */ - if (impl_window->implicit_paint) + for (l = impl_window->implicit_paint; l != NULL; l = l->next) { - GdkWindowPaint *implicit_paint = impl_window->implicit_paint; + GdkWindowPaint *implicit_paint = l->data; cairo_region_t *exposing; exposing = cairo_region_copy (implicit_paint->region); @@ -3302,28 +3541,6 @@ gdk_window_flush (GdkWindow *window) gdk_window_flush_implicit_paint (window); } -/* If we're about to move/resize or otherwise change the - * hierarchy of a client side window in an impl and we're - * called from an expose event handler then we need to - * flush any already painted parts of the implicit paint - * that are not part of the current paint, as these may - * be used when scrolling or may overdraw the changes - * caused by the hierarchy change. - */ -static void -gdk_window_flush_if_exposing (GdkWindow *window) -{ - GdkWindow *impl_window; - - impl_window = gdk_window_get_impl_window (window); - - /* If we're in an implicit paint (i.e. in an expose handler, flush - all the already finished exposes to get things to an uptodate state. */ - if (impl_window->implicit_paint) - gdk_window_flush (window); -} - - static void gdk_window_flush_recursive_helper (GdkWindow *window, GdkWindowImpl *impl) @@ -3414,7 +3631,7 @@ gdk_window_get_visible_region (GdkWindow *window) } static cairo_t * -setup_backing_rect (GdkWindow *window, GdkWindowPaint *paint, int x_offset_cairo, int y_offset_cairo) +setup_backing_rect (GdkWindow *window, GdkWindowPaint *paint) { GdkWindow *bg_window; cairo_pattern_t *pattern = NULL; @@ -3451,45 +3668,15 @@ gdk_window_clear_backing_region (GdkWindow *window, { GdkWindowPaint *paint = window->paint_stack->data; cairo_region_t *clip; - GdkRectangle clipbox; cairo_t *cr; if (GDK_WINDOW_DESTROYED (window)) return; - cr = setup_backing_rect (window, paint, 0, 0); + cr = setup_backing_rect (window, paint); clip = cairo_region_copy (paint->region); cairo_region_intersect (clip, region); - cairo_region_get_extents (clip, &clipbox); - - gdk_cairo_region (cr, clip); - cairo_fill (cr); - - cairo_destroy (cr); - - cairo_region_destroy (clip); -} - -static void -gdk_window_clear_backing_region_direct (GdkWindow *window, - cairo_region_t *region) -{ - GdkWindowPaint paint; - cairo_region_t *clip; - GdkRectangle clipbox; - cairo_t *cr; - - if (GDK_WINDOW_DESTROYED (window)) - return; - - paint.surface = _gdk_window_ref_cairo_surface (window); - - cr = setup_backing_rect (window, &paint, 0, 0); - - clip = cairo_region_copy (window->clip_region_with_children); - cairo_region_intersect (clip, region); - cairo_region_get_extents (clip, &clipbox); gdk_cairo_region (cr, clip); cairo_fill (cr); @@ -3497,18 +3684,6 @@ gdk_window_clear_backing_region_direct (GdkWindow *window, cairo_destroy (cr); cairo_region_destroy (clip); - cairo_surface_destroy (paint.surface); -} - - -static void -gdk_window_clear_region_internal (GdkWindow *window, - cairo_region_t *region) -{ - if (window->paint_stack) - gdk_window_clear_backing_region (window, region); - else - gdk_window_clear_backing_region_direct (window, region); } static void @@ -3646,7 +3821,6 @@ gdk_cairo_create (GdkWindow *window) /* Code for dirty-region queueing */ static GSList *update_windows = NULL; -static guint update_idle = 0; static gboolean debug_updates = FALSE; static inline gboolean @@ -3745,14 +3919,6 @@ gdk_window_remove_update_window (GdkWindow *window) update_windows = g_slist_remove (update_windows, window); } -static gboolean -gdk_window_update_idle (gpointer data) -{ - gdk_window_process_all_updates (); - - return FALSE; -} - static gboolean gdk_window_is_toplevel_frozen (GdkWindow *window) { @@ -3766,16 +3932,20 @@ gdk_window_is_toplevel_frozen (GdkWindow *window) static void gdk_window_schedule_update (GdkWindow *window) { + GdkFrameClock *frame_clock; + if (window && (window->update_freeze_count || gdk_window_is_toplevel_frozen (window))) return; - if (!update_idle) - update_idle = - gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW, - gdk_window_update_idle, - NULL, NULL); + /* If there's no frame clock (a foreign window), then the invalid + * region will just stick around unless gdk_window_process_updates() + * is called. */ + frame_clock = gdk_window_get_frame_clock (window); + if (frame_clock) + gdk_frame_clock_request_phase (gdk_window_get_frame_clock (window), + GDK_FRAME_CLOCK_PHASE_PAINT); } void @@ -3784,15 +3954,28 @@ _gdk_window_process_updates_recurse (GdkWindow *window, { GdkWindow *child; cairo_region_t *clipped_expose_region; + GdkRectangle clip_box; GList *l, *children; + gboolean end_implicit; if (cairo_region_is_empty (expose_region)) return; if (gdk_window_is_offscreen (window->impl_window) && - window == window->impl_window) + gdk_window_has_impl (window)) _gdk_window_add_damage ((GdkWindow *) window->impl_window, expose_region); + end_implicit = FALSE; + if (window->alpha != 255 && !gdk_window_has_impl (window)) + { + if (window->alpha == 0) + return; + + cairo_region_get_extents (expose_region, &clip_box); + clip_box.x += window->abs_x; + clip_box.y += window->abs_y; + end_implicit = gdk_window_begin_implicit_paint (window->impl_window, &clip_box, TRUE, window->alpha); + } /* Paint the window before the children, clipped to the window region with visible child windows removed */ @@ -3831,7 +4014,7 @@ _gdk_window_process_updates_recurse (GdkWindow *window, * piggyback on the implicit paint */ gdk_window_begin_paint_region (window, clipped_expose_region); - gdk_window_clear_region_internal (window, clipped_expose_region); + /* The actual clear happens in begin_paint_region */ gdk_window_end_paint (window); } } @@ -3863,9 +4046,10 @@ _gdk_window_process_updates_recurse (GdkWindow *window, } } - g_list_foreach (children, (GFunc)g_object_unref, NULL); - g_list_free (children); + g_list_free_full (children, g_object_unref); + if (end_implicit) + gdk_window_end_implicit_paint (window->impl_window); } /* Process and remove any invalid area on the native window by creating @@ -3987,7 +4171,7 @@ gdk_window_process_updates_internal (GdkWindow *window) */ cairo_region_get_extents (update_area, &clip_box); - end_implicit = gdk_window_begin_implicit_paint (window, &clip_box); + end_implicit = gdk_window_begin_implicit_paint (window, &clip_box, FALSE, 255); expose_region = cairo_region_copy (update_area); impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl); if (!end_implicit) @@ -4013,7 +4197,7 @@ gdk_window_process_updates_internal (GdkWindow *window) * be to late to anti-expose now. Since this is merely an * optimization we just avoid doing it at all in that case. */ - if (window->implicit_paint != NULL && !window->implicit_paint->flushed) + if (window->implicit_paint != NULL && !((GdkWindowPaint *)window->implicit_paint->data)->flushed) save_region = impl_class->queue_antiexpose (window, update_area); gdk_window_end_implicit_paint (window); @@ -4104,18 +4288,13 @@ gdk_window_process_all_updates (void) /* We can't do this now since that would recurse, so delay it until after the recursion is done. */ got_recursive_update = TRUE; - update_idle = 0; return; } in_process_all_updates = TRUE; got_recursive_update = FALSE; - if (update_idle) - g_source_remove (update_idle); - update_windows = NULL; - update_idle = 0; before_process_all_updates (); @@ -4150,31 +4329,20 @@ gdk_window_process_all_updates (void) redraw now so that it eventually happens, otherwise we could miss an update if nothing else schedules an update. */ - if (got_recursive_update && !update_idle) - update_idle = - gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW, - gdk_window_update_idle, - NULL, NULL); + if (got_recursive_update) + gdk_window_schedule_update (NULL); } -/** - * gdk_window_process_updates: - * @window: a #GdkWindow - * @update_children: whether to also process updates for child windows - * - * Sends one or more expose events to @window. The areas in each - * expose event will cover the entire update area for the window (see - * gdk_window_invalidate_region() for details). Normally GDK calls - * gdk_window_process_all_updates() on your behalf, so there's no - * need to call this function unless you want to force expose events - * to be delivered immediately and synchronously (vs. the usual - * case, where GDK delivers them in an idle handler). Occasionally - * this is useful to produce nicer scrolling behavior, for example. - * - **/ -void -gdk_window_process_updates (GdkWindow *window, - gboolean update_children) + +enum { + PROCESS_UPDATES_NO_RECURSE, + PROCESS_UPDATES_WITH_ALL_CHILDREN, + PROCESS_UPDATES_WITH_SAME_CLOCK_CHILDREN +}; + +static void +gdk_window_process_updates_with_mode (GdkWindow *window, + int recurse_mode) { GdkWindow *impl_window; @@ -4200,7 +4368,7 @@ gdk_window_process_updates (GdkWindow *window, gdk_window_remove_update_window ((GdkWindow *)impl_window); } - if (update_children) + if (recurse_mode != PROCESS_UPDATES_NO_RECURSE) { /* process updates in reverse stacking order so composition or * painting over achieves the desired effect for offscreen windows @@ -4212,8 +4380,14 @@ gdk_window_process_updates (GdkWindow *window, for (node = g_list_last (children); node; node = node->prev) { - gdk_window_process_updates (node->data, TRUE); - g_object_unref (node->data); + GdkWindow *child = node->data; + if (recurse_mode == PROCESS_UPDATES_WITH_ALL_CHILDREN || + (recurse_mode == PROCESS_UPDATES_WITH_SAME_CLOCK_CHILDREN && + child->frame_clock == NULL)) + { + gdk_window_process_updates (child, TRUE); + } + g_object_unref (child); } g_list_free (children); @@ -4222,21 +4396,48 @@ gdk_window_process_updates (GdkWindow *window, g_object_unref (window); } -static void -gdk_window_invalidate_rect_full (GdkWindow *window, - const GdkRectangle *rect, - gboolean invalidate_children, - ClearBg clear_bg) -{ - GdkRectangle window_rect; - cairo_region_t *region; - - g_return_if_fail (GDK_IS_WINDOW (window)); - - if (GDK_WINDOW_DESTROYED (window)) - return; - - if (window->input_only || !window->viewable) +/** + * gdk_window_process_updates: + * @window: a #GdkWindow + * @update_children: whether to also process updates for child windows + * + * Sends one or more expose events to @window. The areas in each + * expose event will cover the entire update area for the window (see + * gdk_window_invalidate_region() for details). Normally GDK calls + * gdk_window_process_all_updates() on your behalf, so there's no + * need to call this function unless you want to force expose events + * to be delivered immediately and synchronously (vs. the usual + * case, where GDK delivers them in an idle handler). Occasionally + * this is useful to produce nicer scrolling behavior, for example. + * + **/ +void +gdk_window_process_updates (GdkWindow *window, + gboolean update_children) +{ + g_return_if_fail (GDK_IS_WINDOW (window)); + + return gdk_window_process_updates_with_mode (window, + update_children ? + PROCESS_UPDATES_WITH_ALL_CHILDREN : + PROCESS_UPDATES_NO_RECURSE); +} + +static void +gdk_window_invalidate_rect_full (GdkWindow *window, + const GdkRectangle *rect, + gboolean invalidate_children, + ClearBg clear_bg) +{ + GdkRectangle window_rect; + cairo_region_t *region; + + g_return_if_fail (GDK_IS_WINDOW (window)); + + if (GDK_WINDOW_DESTROYED (window)) + return; + + if (window->input_only || !window->viewable) return; if (!rect) @@ -4571,7 +4772,7 @@ cairo_region_t * gdk_window_get_update_area (GdkWindow *window) { GdkWindow *impl_window; - cairo_region_t *tmp_region; + cairo_region_t *tmp_region, *to_remove; g_return_val_if_fail (GDK_IS_WINDOW (window), NULL); @@ -4591,7 +4792,21 @@ gdk_window_get_update_area (GdkWindow *window) } else { - cairo_region_subtract (impl_window->update_area, tmp_region); + /* Convert from impl coords */ + cairo_region_translate (tmp_region, -window->abs_x, -window->abs_y); + + /* Don't remove any update area that is overlapped by non-opaque windows + (children or siblings) with alpha, as these really need to be repainted + independently of this window. */ + to_remove = cairo_region_copy (tmp_region); + cairo_region_subtract (to_remove, window->parent->layered_region); + remove_layered_child_area (window, to_remove); + + /* Remove from update_area */ + cairo_region_translate (to_remove, window->abs_x, window->abs_y); + cairo_region_subtract (impl_window->update_area, to_remove); + + cairo_region_destroy (to_remove); if (cairo_region_is_empty (impl_window->update_area) && impl_window->outstanding_moves == NULL) @@ -4602,10 +4817,7 @@ gdk_window_get_update_area (GdkWindow *window) gdk_window_remove_update_window ((GdkWindow *)impl_window); } - /* Convert from impl coords */ - cairo_region_translate (tmp_region, -window->abs_x, -window->abs_y); return tmp_region; - } } else @@ -4697,6 +4909,7 @@ gdk_window_freeze_toplevel_updates_libgtk_only (GdkWindow *window) g_return_if_fail (window->window_type != GDK_WINDOW_CHILD); window->update_and_descendants_freeze_count++; + _gdk_frame_clock_freeze (gdk_window_get_frame_clock (window)); } /** @@ -4717,6 +4930,7 @@ gdk_window_thaw_toplevel_updates_libgtk_only (GdkWindow *window) g_return_if_fail (window->update_and_descendants_freeze_count > 0); window->update_and_descendants_freeze_count--; + _gdk_frame_clock_thaw (gdk_window_get_frame_clock (window)); gdk_window_schedule_update (window); } @@ -5256,8 +5470,6 @@ gdk_window_raise (GdkWindow *window) if (window->destroyed) return; - gdk_window_flush_if_exposing (window); - /* Keep children in (reverse) stacking order */ gdk_window_raise_internal (window); @@ -5378,8 +5590,6 @@ gdk_window_lower (GdkWindow *window) if (window->destroyed) return; - gdk_window_flush_if_exposing (window); - /* Keep children in (reverse) stacking order */ gdk_window_lower_internal (window); @@ -5435,8 +5645,6 @@ gdk_window_restack (GdkWindow *window, return; } - gdk_window_flush_if_exposing (window); - if (gdk_window_is_toplevel (window)) { g_return_if_fail (gdk_window_is_toplevel (sibling)); @@ -5992,8 +6200,6 @@ gdk_window_move_resize_internal (GdkWindow *window, window->y == y))) return; - gdk_window_flush_if_exposing (window); - /* Handle child windows */ expose = FALSE; @@ -6095,7 +6301,7 @@ gdk_window_move_resize_internal (GdkWindow *window, * Everything in the old and new regions that is not copied must be * invalidated (including children) as this is newly exposed */ - if (window->has_alpha_background) + if (gdk_window_has_alpha (window)) copy_area = cairo_region_create (); /* Copy nothing for alpha windows */ else copy_area = cairo_region_copy (new_region); @@ -6278,8 +6484,6 @@ gdk_window_scroll (GdkWindow *window, if (window->destroyed) return; - gdk_window_flush_if_exposing (window); - old_layered_area = cairo_region_copy (window->layered_region); old_native_child_region = collect_native_child_region (window, FALSE); if (old_native_child_region) @@ -6321,7 +6525,7 @@ gdk_window_scroll (GdkWindow *window, impl_window = gdk_window_get_impl_window (window); /* Calculate the area that can be gotten by copying the old area */ - if (window->has_alpha_background) + if (gdk_window_has_alpha (window)) copy_area = cairo_region_create (); /* Copy nothing for alpha windows */ else copy_area = cairo_region_copy (window->clip_region); @@ -6409,12 +6613,19 @@ gdk_window_move_region (GdkWindow *window, impl_window = gdk_window_get_impl_window (window); /* compute source regions */ - copy_area = cairo_region_copy (region); + if (gdk_window_has_alpha (window)) + copy_area = cairo_region_create (); /* Copy nothing for alpha windows */ + else + copy_area = cairo_region_copy (region); cairo_region_intersect (copy_area, window->clip_region_with_children); + cairo_region_subtract (copy_area, window->layered_region); + remove_layered_child_area (window, copy_area); /* compute destination regions */ cairo_region_translate (copy_area, dx, dy); cairo_region_intersect (copy_area, window->clip_region_with_children); + cairo_region_subtract (copy_area, window->layered_region); + remove_layered_child_area (window, copy_area); /* Invalidate parts of the region (source and dest) not covered by the copy */ @@ -6442,6 +6653,8 @@ gdk_window_move_region (GdkWindow *window, * implementing a custom widget.) * * See also gdk_window_set_background_pattern(). + * + * Deprecated: 3.4: Use gdk_window_set_background_rgba() instead. */ void gdk_window_set_background (GdkWindow *window, @@ -6470,8 +6683,8 @@ gdk_window_set_background (GdkWindow *window, * See also gdk_window_set_background_pattern(). **/ void -gdk_window_set_background_rgba (GdkWindow *window, - GdkRGBA *rgba) +gdk_window_set_background_rgba (GdkWindow *window, + const GdkRGBA *rgba) { cairo_pattern_t *pattern; @@ -6486,6 +6699,29 @@ gdk_window_set_background_rgba (GdkWindow *window, cairo_pattern_destroy (pattern); } + +/* Updates has_alpha_background recursively for all child windows + that have parent-relative alpha */ +static void +_gdk_window_propagate_has_alpha_background (GdkWindow *window) +{ + GdkWindow *child; + GList *l; + + for (l = window->children; l; l = l->next) + { + child = l->data; + + if (child->background == NULL && + child->has_alpha_background != window->has_alpha_background) + { + child->has_alpha_background = window->has_alpha_background; + recompute_visible_regions (child, TRUE, FALSE); + _gdk_window_propagate_has_alpha_background (child); + } + } +} + /** * gdk_window_set_background_pattern: * @window: a #GdkWindow @@ -6518,53 +6754,68 @@ gdk_window_set_background_pattern (GdkWindow *window, window->background = pattern; has_alpha = TRUE; - type = cairo_pattern_get_type (pattern); - - if (type == CAIRO_PATTERN_TYPE_SOLID) + + if (pattern == NULL) { - double alpha; - cairo_pattern_get_rgba (pattern, NULL, NULL, NULL, &alpha); - if (alpha == 1.0) + /* parent-relative, copy has_alpha from parent */ + if (window->parent == NULL || + window->parent->window_type == GDK_WINDOW_ROOT) has_alpha = FALSE; + else + has_alpha = window->parent->has_alpha_background; } - else if (type == CAIRO_PATTERN_TYPE_LINEAR || - type == CAIRO_PATTERN_TYPE_RADIAL) + else { - int i, n; - double alpha; + type = cairo_pattern_get_type (pattern); - n = 0; - cairo_pattern_get_color_stop_count (pattern, &n); - has_alpha = FALSE; - for (i = 0; i < n; i++) + if (type == CAIRO_PATTERN_TYPE_SOLID) { - cairo_pattern_get_color_stop_rgba (pattern, i, NULL, - NULL, NULL, NULL, &alpha); - if (alpha != 1.0) + double alpha; + cairo_pattern_get_rgba (pattern, NULL, NULL, NULL, &alpha); + if (alpha == 1.0) + has_alpha = FALSE; + } + else if (type == CAIRO_PATTERN_TYPE_LINEAR || + type == CAIRO_PATTERN_TYPE_RADIAL) + { + int i, n; + double alpha; + + n = 0; + cairo_pattern_get_color_stop_count (pattern, &n); + has_alpha = FALSE; + for (i = 0; i < n; i++) { - has_alpha = TRUE; - break; + cairo_pattern_get_color_stop_rgba (pattern, i, NULL, + NULL, NULL, NULL, &alpha); + if (alpha != 1.0) + { + has_alpha = TRUE; + break; + } } } + else if (type == CAIRO_PATTERN_TYPE_SURFACE) + { + cairo_surface_t *surface; + cairo_content_t content; + + cairo_pattern_get_surface (pattern, &surface); + content = cairo_surface_get_content (surface); + has_alpha = + (content == CAIRO_CONTENT_ALPHA) || + (content == CAIRO_CONTENT_COLOR_ALPHA); + } } - else if (type == CAIRO_PATTERN_TYPE_SURFACE) - { - cairo_surface_t *surface; - cairo_content_t content; - cairo_pattern_get_surface (pattern, &surface); - content = cairo_surface_get_content (surface); - has_alpha = - (content == CAIRO_CONTENT_ALPHA) || - (content == CAIRO_CONTENT_COLOR_ALPHA); - } - if (has_alpha != window->has_alpha_background) { window->has_alpha_background = has_alpha; recompute_visible_regions (window, TRUE, FALSE); + + _gdk_window_propagate_has_alpha_background (window); } - + if (gdk_window_has_impl (window)) { GdkWindowImplClass *impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl); @@ -7575,114 +7826,6 @@ gdk_window_is_shaped (GdkWindow *window) return window->shaped; } -static void -window_get_size_rectangle (GdkWindow *window, - GdkRectangle *rect) -{ - rect->x = rect->y = 0; - rect->width = window->width; - rect->height = window->height; -} - -/* Calculates the real clipping region for a window, in window coordinates, - * taking into account other windows, gc clip region and gc clip mask. - */ -cairo_region_t * -_gdk_window_calculate_full_clip_region (GdkWindow *window, - GdkWindow *base_window, - gboolean do_children, - gint *base_x_offset, - gint *base_y_offset) -{ - GdkRectangle visible_rect; - cairo_region_t *real_clip_region; - gint x_offset, y_offset; - GdkWindow *parentwin, *lastwin; - - if (base_x_offset) - *base_x_offset = 0; - if (base_y_offset) - *base_y_offset = 0; - - if (!window->viewable || window->input_only) - return cairo_region_create (); - - window_get_size_rectangle (window, &visible_rect); - - /* real_clip_region is in window coordinates */ - real_clip_region = cairo_region_create_rectangle (&visible_rect); - - x_offset = y_offset = 0; - - lastwin = window; - 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 == window || lastwin != base_window); - lastwin = parentwin, parentwin = lastwin->parent) - { - GList *cur; - GdkRectangle real_clip_rect; - - if (parentwin != window) - { - x_offset += lastwin->x; - y_offset += lastwin->y; - } - - /* children is ordered in reverse stack order */ - for (cur = parentwin->children; - cur && cur->data != lastwin; - cur = cur->next) - { - GdkWindow *child = cur->data; - - if (!GDK_WINDOW_IS_MAPPED (child) || child->input_only) - continue; - - /* Ignore offscreen children, as they don't draw in their parent and - * don't take part in the clipping */ - if (gdk_window_is_offscreen (child)) - continue; - - window_get_size_rectangle (child, &visible_rect); - - /* Convert rect to "window" coords */ - visible_rect.x += child->x - x_offset; - visible_rect.y += child->y - y_offset; - - /* This shortcut is really necessary for performance when there are a lot of windows */ - cairo_region_get_extents (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; - - cairo_region_subtract_rectangle (real_clip_region, &visible_rect); - } - - /* Clip to the parent */ - window_get_size_rectangle ((GdkWindow *)parentwin, &visible_rect); - /* Convert rect to "window" coords */ - visible_rect.x += - x_offset; - visible_rect.y += - y_offset; - - cairo_region_intersect_rectangle (real_clip_region, &visible_rect); - } - - if (base_x_offset) - *base_x_offset = x_offset; - if (base_y_offset) - *base_y_offset = y_offset; - - return real_clip_region; -} - void _gdk_window_add_damage (GdkWindow *toplevel, cairo_region_t *damaged_region) @@ -8100,12 +8243,16 @@ static const guint type_masks[] = { GDK_ALL_EVENTS_MASK, /* GDK_CLIENT_EVENT = 28 */ GDK_VISIBILITY_NOTIFY_MASK, /* GDK_VISIBILITY_NOTIFY = 29 */ GDK_EXPOSURE_MASK, /* GDK_NO_EXPOSE = 30 */ - GDK_SCROLL_MASK | GDK_BUTTON_PRESS_MASK,/* GDK_SCROLL= 31 */ + GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK,/* GDK_SCROLL= 31 */ 0, /* GDK_WINDOW_STATE = 32 */ 0, /* GDK_SETTING = 33 */ 0, /* GDK_OWNER_CHANGE = 34 */ 0, /* GDK_GRAB_BROKEN = 35 */ 0, /* GDK_DAMAGE = 36 */ + GDK_TOUCH_MASK, /* GDK_TOUCH_BEGIN = 37 */ + GDK_TOUCH_MASK, /* GDK_TOUCH_UPDATE = 38 */ + GDK_TOUCH_MASK, /* GDK_TOUCH_END = 39 */ + GDK_TOUCH_MASK /* GDK_TOUCH_CANCEL = 40 */ }; G_STATIC_ASSERT (G_N_ELEMENTS (type_masks) == GDK_EVENT_LAST); @@ -8137,6 +8284,8 @@ is_button_type (GdkEventType type) type == GDK_2BUTTON_PRESS || type == GDK_3BUTTON_PRESS || type == GDK_BUTTON_RELEASE || + type == GDK_TOUCH_BEGIN || + type == GDK_TOUCH_END || type == GDK_SCROLL; } @@ -8144,10 +8293,20 @@ static gboolean is_motion_type (GdkEventType type) { return type == GDK_MOTION_NOTIFY || + type == GDK_TOUCH_UPDATE || type == GDK_ENTER_NOTIFY || type == GDK_LEAVE_NOTIFY; } +static gboolean +is_touch_type (GdkEventType type) +{ + return type == GDK_TOUCH_BEGIN || + type == GDK_TOUCH_UPDATE || + type == GDK_TOUCH_END || + type == GDK_TOUCH_CANCEL; +} + static GdkWindow * find_common_ancestor (GdkWindow *win1, GdkWindow *win2) @@ -8220,6 +8379,15 @@ _gdk_make_event (GdkWindow *window, event->button.state = the_state; break; + case GDK_TOUCH_BEGIN: + case GDK_TOUCH_UPDATE: + case GDK_TOUCH_END: + case GDK_TOUCH_CANCEL: + event->touch.time = the_time; + event->touch.axes = NULL; + event->touch.state = the_state; + break; + case GDK_SCROLL: event->scroll.time = the_time; event->scroll.state = the_state; @@ -8308,12 +8476,27 @@ send_crossing_event (GdkDisplay *display, GdkEvent *event; guint32 window_event_mask, type_event_mask; GdkDeviceGrabInfo *grab; + GdkTouchGrabInfo *touch_grab = NULL; + GdkPointerWindowInfo *pointer_info; gboolean block_event = FALSE; + GdkEventSequence *sequence; grab = _gdk_display_has_device_grab (display, device, serial); + pointer_info = _gdk_display_get_pointer_info (display, device); - if (grab != NULL && - !grab->owner_events) + sequence = gdk_event_get_event_sequence (event_in_queue); + if (sequence) + touch_grab = _gdk_display_has_touch_grab (display, device, sequence, serial); + + if (touch_grab) + { + if (window != touch_grab->window) + return; + + window_event_mask = touch_grab->event_mask; + } + else if (grab != NULL && + !grab->owner_events) { /* !owner_event => only report events wrt grab window, ignore rest */ if ((GdkWindow *)window != grab->window) @@ -8323,7 +8506,16 @@ send_crossing_event (GdkDisplay *display, else window_event_mask = window->event_mask; - if (type == GDK_LEAVE_NOTIFY) + if (type == GDK_ENTER_NOTIFY && + (pointer_info->need_touch_press_enter || + gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN) && + mode != GDK_CROSSING_TOUCH_BEGIN && + mode != GDK_CROSSING_TOUCH_END) + { + pointer_info->need_touch_press_enter = TRUE; + block_event = TRUE; + } + else if (type == GDK_LEAVE_NOTIFY) { type_event_mask = GDK_LEAVE_NOTIFY_MASK; window->devices_inside = g_list_remove (window->devices_inside, device); @@ -9029,10 +9221,17 @@ do_synthesize_crossing_event (gpointer data) serial); if (new_window_under_pointer != pointer_info->window_under_pointer) { + GdkDevice *source_device; + + if (pointer_info->last_slave) + source_device = pointer_info->last_slave; + else + source_device = device; + _gdk_synthesize_crossing_events (display, pointer_info->window_under_pointer, new_window_under_pointer, - device, NULL, + device, source_device, GDK_CROSSING_NORMAL, pointer_info->toplevel_x, pointer_info->toplevel_y, @@ -9071,17 +9270,54 @@ _gdk_synthesize_crossing_events_for_geometry_change (GdkWindow *changed_window) static GdkWindow * get_event_window (GdkDisplay *display, GdkDevice *device, - GdkWindow *pointer_window, - GdkEventType type, - GdkModifierType mask, - guint *evmask_out, - gulong serial) -{ - guint evmask; + GdkEventSequence *sequence, + GdkWindow *pointer_window, + GdkEventType type, + GdkModifierType mask, + guint *evmask_out, + gboolean pointer_emulated, + gulong serial) +{ + guint evmask, emulated_mask = 0; GdkWindow *grab_window; GdkDeviceGrabInfo *grab; + GdkTouchGrabInfo *touch_grab; - grab = _gdk_display_has_device_grab (display, device, serial); + touch_grab = _gdk_display_has_touch_grab (display, device, sequence, serial); + grab = _gdk_display_get_last_device_grab (display, device); + + if (is_touch_type (type) && pointer_emulated) + { + switch (type) + { + case GDK_TOUCH_BEGIN: + emulated_mask |= GDK_BUTTON_PRESS_MASK; + break; + case GDK_TOUCH_UPDATE: + emulated_mask |= GDK_POINTER_MOTION_MASK; + break; + case GDK_TOUCH_END: + emulated_mask |= GDK_BUTTON_RELEASE_MASK; + default: + break; + } + } + + if (touch_grab != NULL && + (!grab || grab->implicit || touch_grab->serial >= grab->serial_start)) + { + evmask = touch_grab->event_mask; + evmask = update_evmask_for_button_motion (evmask, mask); + + if (evmask & (type_masks[type] | emulated_mask)) + { + if (evmask_out) + *evmask_out = evmask; + return touch_grab->window; + } + else + return NULL; + } if (grab != NULL && !grab->owner_events) { @@ -9090,7 +9326,7 @@ get_event_window (GdkDisplay *display, grab_window = grab->window; - if (evmask & type_masks[type]) + if (evmask & (type_masks[type] | emulated_mask)) { if (evmask_out) *evmask_out = evmask; @@ -9105,7 +9341,7 @@ get_event_window (GdkDisplay *display, evmask = pointer_window->event_mask; evmask = update_evmask_for_button_motion (evmask, mask); - if (evmask & type_masks[type]) + if (evmask & (type_masks[type] | emulated_mask)) { if (evmask_out) *evmask_out = evmask; @@ -9121,7 +9357,7 @@ get_event_window (GdkDisplay *display, evmask = grab->event_mask; evmask = update_evmask_for_button_motion (evmask, mask); - if (evmask & type_masks[type]) + if (evmask & (type_masks[type] | emulated_mask)) { if (evmask_out) *evmask_out = evmask; @@ -9147,8 +9383,10 @@ proxy_pointer_event (GdkDisplay *display, guint state; gdouble toplevel_x, toplevel_y; guint32 time_; - gboolean non_linear; + gboolean non_linear, need_synthetic_enter = FALSE; + gint event_type; + event_type = source_event->type; event_window = source_event->any.window; gdk_event_get_coords (source_event, &toplevel_x, &toplevel_y); gdk_event_get_state (source_event, &state); @@ -9167,6 +9405,15 @@ proxy_pointer_event (GdkDisplay *display, source_event->crossing.detail == GDK_NOTIFY_NONLINEAR_VIRTUAL)) non_linear = TRUE; + if (pointer_info->need_touch_press_enter && + gdk_device_get_source (pointer_info->last_slave) != GDK_SOURCE_TOUCHSCREEN && + (source_event->type != GDK_TOUCH_UPDATE || + _gdk_event_get_pointer_emulated (source_event))) + { + pointer_info->need_touch_press_enter = FALSE; + need_synthetic_enter = TRUE; + } + /* If we get crossing events with subwindow unexpectedly being NULL that means there is a native subwindow that gdk doesn't know about. We track these and forward them, with the correct virtual window @@ -9254,7 +9501,9 @@ proxy_pointer_event (GdkDisplay *display, return TRUE; } - if (pointer_info->window_under_pointer != pointer_window) + if ((source_event->type != GDK_TOUCH_UPDATE || + _gdk_event_get_pointer_emulated (source_event)) && + pointer_info->window_under_pointer != pointer_window) { /* Either a toplevel crossing notify that ended up inside a child window, or a motion notify that got into another child window */ @@ -9271,28 +9520,71 @@ proxy_pointer_event (GdkDisplay *display, serial, non_linear); _gdk_display_set_window_under_pointer (display, device, pointer_window); } - else if (source_event->type == GDK_MOTION_NOTIFY) + else if (source_event->type == GDK_MOTION_NOTIFY || + source_event->type == GDK_TOUCH_UPDATE) { GdkWindow *event_win; guint evmask; gboolean is_hint; + GdkEventSequence *sequence; + + sequence = gdk_event_get_event_sequence (source_event); event_win = get_event_window (display, device, + sequence, pointer_window, source_event->type, state, &evmask, + _gdk_event_get_pointer_emulated (source_event), serial); + if (event_type == GDK_TOUCH_UPDATE) + { + if (_gdk_event_get_pointer_emulated (source_event)) + { + /* Touch events emulating pointer events are transformed back + * to pointer events if: + * 1 - The event window doesn't select for touch events + * 2 - There's no touch grab for this sequence, which means + * it was started as a pointer sequence, but a device + * grab added touch events afterwards, the sequence must + * not mutate in this case. + */ + if ((evmask & GDK_TOUCH_MASK) == 0 || + !_gdk_display_has_touch_grab (display, device, sequence, serial)) + event_type = GDK_MOTION_NOTIFY; + } + else if ((evmask & GDK_TOUCH_MASK) == 0) + return TRUE; + } + + if (is_touch_type (source_event->type) && !is_touch_type (event_type)) + state |= GDK_BUTTON1_MASK; + if (event_win && gdk_device_get_device_type (device) != GDK_DEVICE_TYPE_MASTER && gdk_window_get_device_events (event_win, device) == 0) return TRUE; + /* The last device to interact with the window was a touch device, + * which synthesized a leave notify event, so synthesize another enter + * notify to tell the pointer is on the window. + */ + if (need_synthetic_enter) + _gdk_synthesize_crossing_events (display, + NULL, pointer_window, + device, source_device, + GDK_CROSSING_DEVICE_SWITCH, + toplevel_x, toplevel_y, + state, time_, NULL, + serial, FALSE); + is_hint = FALSE; if (event_win && + event_type == GDK_MOTION_NOTIFY && (evmask & GDK_POINTER_MOTION_HINT_MASK)) { gulong *device_serial; @@ -9310,22 +9602,56 @@ proxy_pointer_event (GdkDisplay *display, } } - if (event_win && !display->ignore_core_events) + if (!event_win) + return TRUE; + + event = gdk_event_new (event_type); + event->any.window = g_object_ref (event_win); + event->any.send_event = source_event->any.send_event; + + gdk_event_set_device (event, gdk_event_get_device (source_event)); + gdk_event_set_source_device (event, source_device); + + if (event_type == GDK_TOUCH_UPDATE) { - event = _gdk_make_event (event_win, GDK_MOTION_NOTIFY, source_event, FALSE); - event->motion.time = time_; + event->touch.time = time_; + event->touch.state = state | GDK_BUTTON1_MASK; + event->touch.sequence = source_event->touch.sequence; + event->touch.emulating_pointer = source_event->touch.emulating_pointer; convert_toplevel_coords_to_window (event_win, toplevel_x, toplevel_y, - &event->motion.x, &event->motion.y); - event->motion.x_root = source_event->motion.x_root; - event->motion.y_root = source_event->motion.y_root; + &event->touch.x, &event->touch.y); + gdk_event_get_root_coords (source_event, + &event->touch.x_root, + &event->touch.y_root); + + event->touch.axes = g_memdup (source_event->touch.axes, + sizeof (gdouble) * gdk_device_get_n_axes (source_event->touch.device)); + } + else + { + event->motion.time = time_; event->motion.state = state; event->motion.is_hint = is_hint; - event->motion.device = source_event->motion.device; - event->motion.axes = g_memdup (source_event->motion.axes, - sizeof (gdouble) * gdk_device_get_n_axes (source_event->motion.device)); - gdk_event_set_source_device (event, source_device); + + convert_toplevel_coords_to_window (event_win, + toplevel_x, toplevel_y, + &event->motion.x, &event->motion.y); + gdk_event_get_root_coords (source_event, + &event->motion.x_root, + &event->motion.y_root); + + if (is_touch_type (source_event->type)) + event->motion.axes = g_memdup (source_event->touch.axes, + sizeof (gdouble) * gdk_device_get_n_axes (source_event->touch.device)); + else + event->motion.axes = g_memdup (source_event->motion.axes, + sizeof (gdouble) * gdk_device_get_n_axes (source_event->motion.device)); } + + /* Just insert the event */ + _gdk_event_queue_insert_after (gdk_window_get_display (event_win), + source_event, event); } /* unlink all move events from queue. @@ -9348,6 +9674,8 @@ proxy_button_event (GdkEvent *source_event, GdkWindow *pointer_window; GdkWindow *parent; GdkEvent *event; + GdkPointerWindowInfo *pointer_info; + GdkDeviceGrabInfo *pointer_grab; guint state; guint32 time_; GdkEventType type; @@ -9355,6 +9683,8 @@ proxy_button_event (GdkEvent *source_event, GdkDisplay *display; GdkWindow *w; GdkDevice *device, *source_device; + GdkEventMask evmask; + GdkEventSequence *sequence; type = source_event->any.type; event_window = source_event->any.window; @@ -9368,9 +9698,17 @@ proxy_button_event (GdkEvent *source_event, toplevel_x, toplevel_y, &toplevel_x, &toplevel_y); - if (type == GDK_BUTTON_PRESS && + sequence = gdk_event_get_event_sequence (source_event); + + pointer_info = _gdk_display_get_pointer_info (display, device); + pointer_grab = _gdk_display_has_device_grab (display, device, serial); + + if ((type == GDK_BUTTON_PRESS || + type == GDK_TOUCH_BEGIN) && !source_event->any.send_event && - _gdk_display_has_device_grab (display, device, serial) == NULL) + (!pointer_grab || + (type == GDK_TOUCH_BEGIN && pointer_grab->implicit && + !_gdk_event_get_pointer_emulated (source_event)))) { pointer_window = _gdk_window_find_descendant_at (toplevel_window, @@ -9383,23 +9721,46 @@ proxy_button_event (GdkEvent *source_event, (parent = get_event_parent (w)) != NULL && parent->window_type != GDK_WINDOW_ROOT) { - if (w->event_mask & GDK_BUTTON_PRESS_MASK) + if (w->event_mask & GDK_BUTTON_PRESS_MASK && + (type == GDK_BUTTON_PRESS || + _gdk_event_get_pointer_emulated (source_event))) break; + + if (type == GDK_TOUCH_BEGIN && + w->event_mask & GDK_TOUCH_MASK) + break; + w = parent; } - pointer_window = (GdkWindow *)w; - - _gdk_display_add_device_grab (display, - device, - pointer_window, - event_window, - GDK_OWNERSHIP_NONE, - FALSE, - gdk_window_get_events (pointer_window), - serial, - time_, - TRUE); - _gdk_display_device_grab_update (display, device, source_device, serial); + pointer_window = w; + + if (pointer_window) + { + if (type == GDK_TOUCH_BEGIN && + pointer_window->event_mask & GDK_TOUCH_MASK) + { + _gdk_display_add_touch_grab (display, device, sequence, + pointer_window, event_window, + gdk_window_get_events (pointer_window), + serial, time_); + } + else if (type == GDK_BUTTON_PRESS || + _gdk_event_get_pointer_emulated (source_event)) + { + _gdk_display_add_device_grab (display, + device, + pointer_window, + event_window, + GDK_OWNERSHIP_NONE, + FALSE, + gdk_window_get_events (pointer_window), + serial, + time_, + TRUE); + _gdk_display_device_grab_update (display, device, + source_device, serial); + } + } } pointer_window = get_pointer_window (display, toplevel_window, device, @@ -9408,17 +9769,73 @@ proxy_button_event (GdkEvent *source_event, event_win = get_event_window (display, device, - pointer_window, - type, state, - NULL, serial); + sequence, + pointer_window, + type, state, + &evmask, + _gdk_event_get_pointer_emulated (source_event), + serial); - if (event_win == NULL || display->ignore_core_events) + if (type == GDK_TOUCH_BEGIN || type == GDK_TOUCH_END) + { + if (_gdk_event_get_pointer_emulated (source_event)) + { + if ((evmask & GDK_TOUCH_MASK) == 0 || + !_gdk_display_has_touch_grab (display, device, sequence, serial)) + { + if (type == GDK_TOUCH_BEGIN) + type = GDK_BUTTON_PRESS; + else if (type == GDK_TOUCH_END) + type = GDK_BUTTON_RELEASE; + } + } + else if ((evmask & GDK_TOUCH_MASK) == 0) + return TRUE; + } + + if (source_event->type == GDK_TOUCH_END && !is_touch_type (type)) + state |= GDK_BUTTON1_MASK; + + if (event_win == NULL) return TRUE; if (gdk_device_get_device_type (device) != GDK_DEVICE_TYPE_MASTER && gdk_window_get_device_events (event_win, device) == 0) return TRUE; + if ((type == GDK_BUTTON_PRESS || + (type == GDK_TOUCH_BEGIN && + _gdk_event_get_pointer_emulated (source_event))) && + pointer_info->need_touch_press_enter) + { + GdkCrossingMode mode; + + /* The last device to interact with the window was a touch device, + * which synthesized a leave notify event, so synthesize another enter + * notify to tell the pointer is on the window. + */ + if (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN) + mode = GDK_CROSSING_TOUCH_BEGIN; + else + mode = GDK_CROSSING_DEVICE_SWITCH; + + pointer_info->need_touch_press_enter = FALSE; + _gdk_synthesize_crossing_events (display, + NULL, + pointer_info->window_under_pointer, + device, source_device, mode, + toplevel_x, toplevel_y, + state, time_, source_event, + serial, FALSE); + } + else if (type == GDK_SCROLL && + (((evmask & GDK_SMOOTH_SCROLL_MASK) == 0 && + source_event->scroll.direction == GDK_SCROLL_SMOOTH) || + ((evmask & GDK_SMOOTH_SCROLL_MASK) != 0 && + source_event->scroll.direction != GDK_SCROLL_SMOOTH && + _gdk_event_get_pointer_emulated (source_event)))) + return FALSE; + event = _gdk_make_event (event_win, type, source_event, FALSE); switch (type) @@ -9429,17 +9846,83 @@ proxy_button_event (GdkEvent *source_event, convert_toplevel_coords_to_window (event_win, toplevel_x, toplevel_y, &event->button.x, &event->button.y); - event->button.x_root = source_event->button.x_root; - event->button.y_root = source_event->button.y_root; - event->button.state = state; - event->button.device = source_event->button.device; - event->button.axes = g_memdup (source_event->button.axes, - sizeof (gdouble) * gdk_device_get_n_axes (source_event->button.device)); - + gdk_event_get_root_coords (source_event, + &event->button.x_root, + &event->button.y_root); + gdk_event_set_device (event, gdk_event_get_device (source_event)); gdk_event_set_source_device (event, source_device); + if (is_touch_type (source_event->type)) + { + if (type == GDK_BUTTON_RELEASE) + event->button.state |= GDK_BUTTON1_MASK; + event->button.button = 1; + event->button.axes = g_memdup (source_event->touch.axes, + sizeof (gdouble) * gdk_device_get_n_axes (source_event->touch.device)); + } + else + { + event->button.button = source_event->button.button; + event->button.axes = g_memdup (source_event->button.axes, + sizeof (gdouble) * gdk_device_get_n_axes (source_event->button.device)); + } + if (type == GDK_BUTTON_PRESS) - _gdk_event_button_generate (display, event); + _gdk_event_button_generate (display, event); + else if ((type == GDK_BUTTON_RELEASE || + (type == GDK_TOUCH_END && + _gdk_event_get_pointer_emulated (source_event))) && + pointer_window == pointer_info->window_under_pointer && + gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN) + { + /* Synthesize a leave notify event + * whenever a touch device is released + */ + pointer_info->need_touch_press_enter = TRUE; + _gdk_synthesize_crossing_events (display, + pointer_window, NULL, + device, source_device, + GDK_CROSSING_TOUCH_END, + toplevel_x, toplevel_y, + state, time_, NULL, + serial, FALSE); + } + return TRUE; + + case GDK_TOUCH_BEGIN: + case GDK_TOUCH_END: + convert_toplevel_coords_to_window (event_win, + toplevel_x, toplevel_y, + &event->button.x, &event->button.y); + gdk_event_get_root_coords (source_event, + &event->touch.x_root, + &event->touch.y_root); + event->touch.state = state; + event->touch.device = source_event->touch.device; + event->touch.axes = g_memdup (source_event->touch.axes, + sizeof (gdouble) * gdk_device_get_n_axes (source_event->touch.device)); + event->touch.sequence = source_event->touch.sequence; + event->touch.emulating_pointer = source_event->touch.emulating_pointer; + + gdk_event_set_source_device (event, source_device); + + if ((type == GDK_TOUCH_END && + _gdk_event_get_pointer_emulated (source_event)) && + pointer_window == pointer_info->window_under_pointer && + gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN) + { + /* Synthesize a leave notify event + * whenever a touch device is released + */ + pointer_info->need_touch_press_enter = TRUE; + _gdk_synthesize_crossing_events (display, + pointer_window, NULL, + device, source_device, + GDK_CROSSING_TOUCH_END, + toplevel_x, toplevel_y, + state, time_, NULL, + serial, FALSE); + } return TRUE; case GDK_SCROLL: @@ -9451,6 +9934,8 @@ proxy_button_event (GdkEvent *source_event, event->scroll.y_root = source_event->scroll.y_root; event->scroll.state = state; event->scroll.device = source_event->scroll.device; + event->scroll.delta_x = source_event->scroll.delta_x; + event->scroll.delta_y = source_event->scroll.delta_y; gdk_event_set_source_device (event, source_device); return TRUE; @@ -9462,11 +9947,16 @@ proxy_button_event (GdkEvent *source_event, } #ifdef DEBUG_WINDOW_PRINTING + +#ifdef GDK_WINDOWING_X11 +#include "x11/gdkx.h" +#endif + static void gdk_window_print (GdkWindow *window, int indent) { - GdkRectangle r; + char *s; const char *window_types[] = { "root", "toplevel", @@ -9505,11 +9995,12 @@ gdk_window_print (GdkWindow *window, g_print (" abs[%d,%d]", window->abs_x, window->abs_y); - cairo_region_get_extents (window->clip_region, &r); - if (cairo_region_is_empty (window->clip_region)) - g_print (" clipbox[empty]"); - else - g_print (" clipbox[%d,%d %dx%d]", r.x, r.y, r.width, r.height); + if (window->alpha != 255) + g_print (" alpha[%d]", + window->alpha); + + s = print_region (window->clip_region); + g_print (" clipbox[%s]", s); g_print ("\n"); } @@ -9535,16 +10026,15 @@ gdk_window_print_tree (GdkWindow *window, void _gdk_windowing_got_event (GdkDisplay *display, - GList *event_link, - GdkEvent *event, - gulong serial) + GList *event_link, + GdkEvent *event, + gulong serial) { GdkWindow *event_window; gdouble x, y; - gboolean unlink_event; - guint old_state, old_button; + gboolean unlink_event = FALSE; GdkDeviceGrabInfo *button_release_grab; - GdkPointerWindowInfo *pointer_info; + GdkPointerWindowInfo *pointer_info = NULL; GdkDevice *device, *source_device; gboolean is_toplevel; @@ -9558,6 +10048,17 @@ _gdk_windowing_got_event (GdkDisplay *display, { GdkInputMode mode; + if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD) + { + pointer_info = _gdk_display_get_pointer_info (display, device); + + if (source_device != pointer_info->last_slave && + gdk_device_get_device_type (source_device) == GDK_DEVICE_TYPE_SLAVE) + pointer_info->last_slave = source_device; + else if (pointer_info->last_slave) + source_device = pointer_info->last_slave; + } + g_object_get (device, "input-mode", &mode, NULL); _gdk_display_device_grab_update (display, device, source_device, serial); @@ -9574,32 +10075,28 @@ _gdk_windowing_got_event (GdkDisplay *display, event_window = event->any.window; if (!event_window) - return; - - pointer_info = _gdk_display_get_pointer_info (display, device); + goto out; #ifdef DEBUG_WINDOW_PRINTING if (event->type == GDK_KEY_PRESS && (event->key.keyval == 0xa7 || event->key.keyval == 0xbd)) { - gdk_window_print_tree (event_window, 0, - event->key.keyval == 0xbd); + gdk_window_print_tree (event_window, 0, event->key.keyval == 0xbd); } #endif if (event->type == GDK_VISIBILITY_NOTIFY) { event_window->native_visibility = event->visibility.state; - gdk_window_update_visibility_recursively (event_window, - event_window); - return; + gdk_window_update_visibility_recursively (event_window, event_window); + goto out; } if (!(is_button_type (event->type) || - is_motion_type (event->type)) || + is_motion_type (event->type)) || event_window->window_type == GDK_WINDOW_ROOT) - return; + goto out; is_toplevel = gdk_window_is_toplevel (event_window); @@ -9628,15 +10125,16 @@ _gdk_windowing_got_event (GdkDisplay *display, */ /* We ended up in this window after some (perhaps other clients) - grab, so update the toplevel_under_window state */ + * grab, so update the toplevel_under_window state + */ if (is_toplevel && - event->type == GDK_ENTER_NOTIFY && - event->crossing.mode == GDK_CROSSING_UNGRAB) - { - if (pointer_info->toplevel_under_pointer) - g_object_unref (pointer_info->toplevel_under_pointer); - pointer_info->toplevel_under_pointer = g_object_ref (event_window); - } + event->type == GDK_ENTER_NOTIFY && + event->crossing.mode == GDK_CROSSING_UNGRAB) + { + if (pointer_info->toplevel_under_pointer) + g_object_unref (pointer_info->toplevel_under_pointer); + pointer_info->toplevel_under_pointer = g_object_ref (event_window); + } unlink_event = TRUE; goto out; @@ -9646,62 +10144,83 @@ _gdk_windowing_got_event (GdkDisplay *display, if (is_toplevel) { if (event->type == GDK_ENTER_NOTIFY && - event->crossing.detail != GDK_NOTIFY_INFERIOR) - { - if (pointer_info->toplevel_under_pointer) - g_object_unref (pointer_info->toplevel_under_pointer); - pointer_info->toplevel_under_pointer = g_object_ref (event_window); - } + event->crossing.detail != GDK_NOTIFY_INFERIOR) + { + if (pointer_info->toplevel_under_pointer) + g_object_unref (pointer_info->toplevel_under_pointer); + pointer_info->toplevel_under_pointer = g_object_ref (event_window); + } else if (event->type == GDK_LEAVE_NOTIFY && - event->crossing.detail != GDK_NOTIFY_INFERIOR && - pointer_info->toplevel_under_pointer == event_window) - { - if (pointer_info->toplevel_under_pointer) - g_object_unref (pointer_info->toplevel_under_pointer); - pointer_info->toplevel_under_pointer = NULL; - } + event->crossing.detail != GDK_NOTIFY_INFERIOR && + pointer_info->toplevel_under_pointer == event_window) + { + if (pointer_info->toplevel_under_pointer) + g_object_unref (pointer_info->toplevel_under_pointer); + pointer_info->toplevel_under_pointer = NULL; + } } - /* Store last pointer window and position/state */ - old_state = pointer_info->state; - old_button = pointer_info->button; + if (pointer_info && + (!is_touch_type (event->type) || + _gdk_event_get_pointer_emulated (event))) + { + guint old_state, old_button; - gdk_event_get_coords (event, &x, &y); - convert_native_coords_to_toplevel (event_window, x, y, &x, &y); - pointer_info->toplevel_x = x; - pointer_info->toplevel_y = y; - gdk_event_get_state (event, &pointer_info->state); - if (event->type == GDK_BUTTON_PRESS || - event->type == GDK_BUTTON_RELEASE) - pointer_info->button = event->button.button; + /* Store last pointer window and position/state */ + old_state = pointer_info->state; + old_button = pointer_info->button; - if (device && - (pointer_info->state != old_state || - pointer_info->button != old_button)) - _gdk_display_enable_motion_hints (display, device); + gdk_event_get_coords (event, &x, &y); + convert_native_coords_to_toplevel (event_window, x, y, &x, &y); + pointer_info->toplevel_x = x; + pointer_info->toplevel_y = y; + gdk_event_get_state (event, &pointer_info->state); + + if (event->type == GDK_BUTTON_PRESS || + event->type == GDK_BUTTON_RELEASE) + pointer_info->button = event->button.button; + else if (event->type == GDK_TOUCH_BEGIN || + event->type == GDK_TOUCH_END) + pointer_info->button = 1; + + if (device && + (pointer_info->state != old_state || + pointer_info->button != old_button)) + _gdk_display_enable_motion_hints (display, device); + } - unlink_event = FALSE; if (is_motion_type (event->type)) - unlink_event = proxy_pointer_event (display, - event, - serial); + unlink_event = proxy_pointer_event (display, event, serial); else if (is_button_type (event->type)) - unlink_event = proxy_button_event (event, - serial); + unlink_event = proxy_button_event (event, serial); - if (event->type == GDK_BUTTON_RELEASE && + if ((event->type == GDK_BUTTON_RELEASE || + event->type == GDK_TOUCH_END) && !event->any.send_event) { - button_release_grab = - _gdk_display_has_device_grab (display, device, serial); - if (button_release_grab && - button_release_grab->implicit && - (event->button.state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (event->button.button - 1))) == 0) - { - button_release_grab->serial_end = serial; - button_release_grab->implicit_ungrab = FALSE; - _gdk_display_device_grab_update (display, device, source_device, serial); - } + GdkEventSequence *sequence; + + sequence = gdk_event_get_event_sequence (event); + if (event->type == GDK_TOUCH_END && sequence) + { + _gdk_display_end_touch_grab (display, device, sequence); + } + + if (event->type == GDK_BUTTON_RELEASE || + _gdk_event_get_pointer_emulated (event)) + { + button_release_grab = + _gdk_display_has_device_grab (display, device, serial); + + if (button_release_grab && + button_release_grab->implicit && + (event->button.state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (event->button.button - 1))) == 0) + { + button_release_grab->serial_end = serial; + button_release_grab->implicit_ungrab = FALSE; + _gdk_display_device_grab_update (display, device, source_device, serial); + } + } } out: @@ -9711,6 +10230,13 @@ _gdk_windowing_got_event (GdkDisplay *display, g_list_free_1 (event_link); gdk_event_free (event); } + + /* This does two things - first it sees if there are motions at the + * end of the queue that can be compressed. Second, if there is just + * a single motion that won't be dispatched because it is a compression + * candidate it queues up flushing the event queue. + */ + _gdk_event_queue_handle_motion_compression (display); } /** @@ -9750,7 +10276,7 @@ gdk_window_create_similar_surface (GdkWindow * window, g_return_val_if_fail (GDK_IS_WINDOW (window), NULL); - window_surface = _gdk_window_ref_cairo_surface (window); + window_surface = gdk_window_ref_impl_surface (window); switch (_gdk_rendering_mode) { @@ -10050,7 +10576,7 @@ gdk_window_get_root_origin (GdkWindow *window, /** * gdk_window_get_frame_extents: * @window: a toplevel #GdkWindow - * @rect: rectangle to fill with bounding box of the window frame + * @rect: (out): rectangle to fill with bounding box of the window frame * * Obtains the bounding box of the window, including window manager * titlebar/borders if any. The frame position is given in root window @@ -10316,6 +10842,67 @@ gdk_window_fullscreen (GdkWindow *window) GDK_WINDOW_IMPL_GET_CLASS (window->impl)->fullscreen (window); } +/** + * gdk_window_set_fullscreen_mode: + * @window: a toplevel #GdkWindow + * @mode: fullscreen mode + * + * Specifies whether the @window should span over all monitors (in a multi-head + * setup) or only the current monitor when in fullscreen mode. + * + * The @mode argument is from the #GdkFullscreenMode enumeration. + * If #GDK_FULLSCREEN_ON_ALL_MONITORS is specified, the fullscreen @window will + * span over all monitors from the #GdkScreen. + * + * On X11, searches through the list of monitors from the #GdkScreen the ones + * which delimit the 4 edges of the entire #GdkScreen and will ask the window + * manager to span the @window over these monitors. + * + * If the XINERAMA extension is not available or not usable, this function + * has no effect. + * + * Not all window managers support this, so you can't rely on the fullscreen + * window to span over the multiple monitors when #GDK_FULLSCREEN_ON_ALL_MONITORS + * is specified. + * + * Since: 3.8 + **/ +void +gdk_window_set_fullscreen_mode (GdkWindow *window, + GdkFullscreenMode mode) +{ + GdkWindowImplClass *impl_class; + + g_return_if_fail (GDK_IS_WINDOW (window)); + + if (window->fullscreen_mode != mode) + { + window->fullscreen_mode = mode; + + impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl); + if (impl_class->apply_fullscreen_mode != NULL) + impl_class->apply_fullscreen_mode (window); + } +} + +/** + * gdk_window_get_fullscreen_mode: + * @window: a toplevel #GdkWindow + * + * Obtains the #GdkFullscreenMode of the @window. + * + * Returns: The #GdkFullscreenMode applied to the window when fullscreen. + * + * Since: 3.8 + **/ +GdkFullscreenMode +gdk_window_get_fullscreen_mode (GdkWindow *window) +{ + g_return_val_if_fail (GDK_IS_WINDOW (window), GDK_FULLSCREEN_ON_CURRENT_MONITOR); + + return window->fullscreen_mode; +} + /** * gdk_window_unfullscreen: * @window: a toplevel #GdkWindow @@ -10622,61 +11209,49 @@ gdk_window_begin_move_drag (GdkWindow *window, * gdk_window_enable_synchronized_configure: * @window: a toplevel #GdkWindow * - * Indicates that the application will cooperate with the window - * system in synchronizing the window repaint with the window - * manager during resizing operations. After an application calls - * this function, it must call gdk_window_configure_finished() every - * time it has finished all processing associated with a set of - * Configure events. Toplevel GTK+ windows automatically use this - * protocol. - * - * On X, calling this function makes @window participate in the - * _NET_WM_SYNC_REQUEST window manager protocol. + * Does nothing, present only for compatiblity. * * Since: 2.6 + * Deprecated: 3.8: this function is no longer needed **/ void gdk_window_enable_synchronized_configure (GdkWindow *window) { - GDK_WINDOW_IMPL_GET_CLASS (window->impl)->enable_synchronized_configure (window); } /** * gdk_window_configure_finished: * @window: a toplevel #GdkWindow - * - * Signal to the window system that the application has finished - * handling Configure events it has received. Window Managers can - * use this to better synchronize the frame repaint with the - * application. GTK+ applications will automatically call this - * function when appropriate. * - * This function can only be called if gdk_window_enable_synchronized_configure() - * was called previously. + * Does nothing, present only for compatiblity. * * Since: 2.6 + * Deprecated: 3.8: this function is no longer needed **/ void gdk_window_configure_finished (GdkWindow *window) { - GDK_WINDOW_IMPL_GET_CLASS (window->impl)->configure_finished (window); } /** * gdk_window_set_opacity: - * @window: a top-level #GdkWindow + * @window: a top-level or non-native #GdkWindow * @opacity: opacity * - * Request the windowing system to make @window partially transparent, + * Set @window to render as 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. + * For toplevel windows this depends on support from the windowing system + * that may not always be there. For instance, On X11, this works only on + * X screens with a compositing manager running. + * + * For child windows this function only works for non-native windows. + * + * For setting up per-pixel alpha topelevels, see gdk_screen_get_rgba_visual(), + * and for non-toplevels, see gdk_window_set_composited(). * - * For setting up per-pixel alpha, see gdk_screen_get_rgba_visual(). - * For making non-toplevel windows translucent, see - * gdk_window_set_composited(). + * Support for non-toplevel windows was added in 3.8. * * Since: 2.12 */ @@ -10684,7 +11259,23 @@ void gdk_window_set_opacity (GdkWindow *window, gdouble opacity) { - GDK_WINDOW_IMPL_GET_CLASS (window->impl)->set_opacity (window, opacity); + if (opacity < 0) + opacity = 0; + else if (opacity > 1) + opacity = 1; + + window->alpha = round (opacity * 255); + + if (window->destroyed) + return; + + if (gdk_window_has_impl (window)) + GDK_WINDOW_IMPL_GET_CLASS (window->impl)->set_opacity (window, opacity); + else + { + recompute_visible_regions (window, TRUE, FALSE); + gdk_window_invalidate_rect_full (window, NULL, TRUE, CLEAR_BG_ALL); + } } /* This function is called when the XWindow is really gone. @@ -10995,3 +11586,113 @@ gdk_property_delete (GdkWindow *window, { GDK_WINDOW_IMPL_GET_CLASS (window->impl)->delete_property (window, property); } + +static void +gdk_window_flush_events (GdkFrameClock *clock, + void *data) +{ + GdkWindow *window; + GdkDisplay *display; + + window = GDK_WINDOW (data); + + display = gdk_window_get_display (window); + _gdk_display_flush_events (display); + _gdk_display_pause_events (display); + + gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS); +} + +static void +gdk_window_paint_on_clock (GdkFrameClock *clock, + void *data) +{ + GdkWindow *window; + + window = GDK_WINDOW (data); + + /* Update window and any children on the same clock. + */ + gdk_window_process_updates_with_mode (window, PROCESS_UPDATES_WITH_SAME_CLOCK_CHILDREN); +} + +static void +gdk_window_resume_events (GdkFrameClock *clock, + void *data) +{ + GdkWindow *window; + GdkDisplay *display; + + window = GDK_WINDOW (data); + + display = gdk_window_get_display (window); + _gdk_display_unpause_events (display); +} + +static void +gdk_window_set_frame_clock (GdkWindow *window, + GdkFrameClock *clock) +{ + g_return_if_fail (GDK_IS_WINDOW (window)); + g_return_if_fail (clock == NULL || GDK_IS_FRAME_CLOCK (clock)); + g_return_if_fail (clock == NULL || gdk_window_is_toplevel (window)); + + if (clock == window->frame_clock) + return; + + if (clock) + { + g_object_ref (clock); + g_signal_connect (G_OBJECT (clock), + "flush-events", + G_CALLBACK (gdk_window_flush_events), + window); + g_signal_connect (G_OBJECT (clock), + "paint", + G_CALLBACK (gdk_window_paint_on_clock), + window); + g_signal_connect (G_OBJECT (clock), + "resume-events", + G_CALLBACK (gdk_window_resume_events), + window); + } + + if (window->frame_clock) + { + g_signal_handlers_disconnect_by_func (G_OBJECT (window->frame_clock), + G_CALLBACK (gdk_window_flush_events), + window); + g_signal_handlers_disconnect_by_func (G_OBJECT (window->frame_clock), + G_CALLBACK (gdk_window_paint_on_clock), + window); + g_signal_handlers_disconnect_by_func (G_OBJECT (window->frame_clock), + G_CALLBACK (gdk_window_resume_events), + window); + g_object_unref (window->frame_clock); + } + + window->frame_clock = clock; +} + +/** + * gdk_window_get_frame_clock: + * @window: window to get frame clock for + * + * Gets the frame clock for the window. The frame clock for a window + * never changes unless the window is reparented to a new toplevel + * window. + * + * Since: 3.8 + * Return value: (transfer none): the frame clock + */ +GdkFrameClock* +gdk_window_get_frame_clock (GdkWindow *window) +{ + GdkWindow *toplevel; + + g_return_val_if_fail (GDK_IS_WINDOW (window), NULL); + + toplevel = gdk_window_get_toplevel (window); + + return toplevel->frame_clock; +}