X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gdk%2Fgdkwindow.c;h=2683073d0993b8ce1ddf580958731ae1b645ad2b;hb=bbb7b2987e00b050ba9fb06ce211b659341f232e;hp=cc251a9e1adef5ec16c9d1e75252aa2ba2d0d313;hpb=915e9d57f981031d8cfaec344356f54acda405dc;p=~andy%2Fgtk diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index cc251a9e1..2683073d0 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 . */ /* @@ -203,9 +201,8 @@ struct _GdkWindowPaint { cairo_region_t *region; cairo_surface_t *surface; + cairo_region_t *flushed; guint uses_implicit : 1; - guint flushed : 1; - guint32 region_tag; }; typedef struct { @@ -256,6 +253,7 @@ 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 guint signals[LAST_SIGNAL] = { 0 }; @@ -263,14 +261,6 @@ static gpointer parent_class = NULL; static const cairo_user_data_key_t gdk_window_cairo_key; -static guint32 -new_region_tag (void) -{ - static guint32 tag = 0; - - return ++tag; -} - G_DEFINE_ABSTRACT_TYPE (GdkWindow, gdk_window, G_TYPE_OBJECT) GType @@ -570,6 +560,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); } @@ -658,10 +651,64 @@ gdk_window_has_no_impl (GdkWindow *window) } static void -remove_child_area (GdkWindow *private, +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 (!child->has_alpha_background && gdk_window_has_impl (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 *window, GdkWindow *until, gboolean for_input, - cairo_region_t *region) + cairo_region_t *region, + cairo_region_t *layered_region) { GdkWindow *child; cairo_region_t *child_region; @@ -669,7 +716,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; @@ -707,7 +754,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) @@ -721,7 +768,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) @@ -732,9 +779,14 @@ remove_child_area (GdkWindow *private, } } - cairo_region_subtract (region, child_region); + if (child->has_alpha_background) + { + if (layered_region != NULL) + cairo_region_union (layered_region, child_region); + } + else + cairo_region_subtract (region, child_region); cairo_region_destroy (child_region); - } } @@ -770,7 +822,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); @@ -877,7 +929,7 @@ recompute_visible_regions_internal (GdkWindow *private, GdkRectangle r; GList *l; GdkWindow *child; - cairo_region_t *new_clip, *old_clip_region_with_children; + cairo_region_t *new_clip, *new_layered; gboolean clip_region_changed; gboolean abs_pos_changed; int old_abs_x, old_abs_y; @@ -910,6 +962,7 @@ recompute_visible_regions_internal (GdkWindow *private, clip_region_changed = FALSE; if (recalculate_clip) { + new_layered = cairo_region_create (); if (private->viewable) { /* Calculate visible region (sans children) in parent window coords */ @@ -922,39 +975,45 @@ recompute_visible_regions_internal (GdkWindow *private, if (!gdk_window_is_toplevel (private)) { cairo_region_intersect (new_clip, private->parent->clip_region); + cairo_region_union (new_layered, private->parent->layered_region); /* Remove all overlapping children from parent. */ - remove_child_area (private->parent, private, FALSE, new_clip); + remove_child_area (private->parent, private, FALSE, new_clip, new_layered); } /* Convert from parent coords to window coords */ cairo_region_translate (new_clip, -private->x, -private->y); + cairo_region_translate (new_layered, -private->x, -private->y); if (private->shape) cairo_region_intersect (new_clip, private->shape); } else - new_clip = cairo_region_create (); + new_clip = cairo_region_create (); + cairo_region_intersect (new_layered, new_clip); + if (private->clip_region == NULL || !cairo_region_equal (private->clip_region, new_clip)) clip_region_changed = TRUE; + if (private->layered_region == NULL || + !cairo_region_equal (private->layered_region, new_layered)) + clip_region_changed = TRUE; + if (private->clip_region) cairo_region_destroy (private->clip_region); private->clip_region = new_clip; - old_clip_region_with_children = private->clip_region_with_children; + if (private->layered_region != NULL) + cairo_region_destroy (private->layered_region); + private->layered_region = new_layered; + + if (private->clip_region_with_children) + cairo_region_destroy (private->clip_region_with_children); private->clip_region_with_children = cairo_region_copy (private->clip_region); if (private->window_type != GDK_WINDOW_ROOT) - remove_child_area (private, NULL, FALSE, private->clip_region_with_children); - - if (clip_region_changed || - !cairo_region_equal (private->clip_region_with_children, old_clip_region_with_children)) - private->clip_tag = new_region_tag (); - - if (old_clip_region_with_children) - cairo_region_destroy (old_clip_region_with_children); + remove_child_area (private, NULL, FALSE, private->clip_region_with_children, NULL); } if (clip_region_changed) @@ -1184,11 +1243,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; } @@ -1359,7 +1419,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 { @@ -1632,10 +1693,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) && @@ -1992,12 +2065,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; } @@ -2644,7 +2713,7 @@ 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->surface = gdk_window_create_similar_surface (window, gdk_window_get_content (window), MAX (rect->width, 1), @@ -2692,9 +2761,17 @@ gdk_window_flush_implicit_paint (GdkWindow *window) return; paint = impl_window->implicit_paint; - paint->flushed = TRUE; region = cairo_region_copy (window->clip_region_with_children); + cairo_region_translate (region, window->abs_x, window->abs_y); + + if (paint->flushed == NULL) + paint->flushed = cairo_region_copy (region); + else + cairo_region_union (paint->flushed, region); + + cairo_region_intersect (region, paint->region); + /* Don't flush active double buffers, as that may show partially done * rendering */ for (list = window->paint_stack; list != NULL; list = list->next) @@ -2704,9 +2781,6 @@ gdk_window_flush_implicit_paint (GdkWindow *window) cairo_region_subtract (region, tmp_paint->region); } - cairo_region_translate (region, -window->abs_x, -window->abs_y); - cairo_region_intersect (region, paint->region); - if (!GDK_WINDOW_DESTROYED (window) && !cairo_region_is_empty (region)) { cairo_t *cr; @@ -2723,7 +2797,7 @@ gdk_window_flush_implicit_paint (GdkWindow *window) cairo_paint (cr); cairo_destroy (cr); } - + cairo_region_destroy (region); } @@ -2756,7 +2830,8 @@ gdk_window_end_implicit_paint (GdkWindow *window) } cairo_region_destroy (paint->region); - + if (paint->flushed) + cairo_region_destroy (paint->flushed); cairo_surface_destroy (paint->surface); g_free (paint); } @@ -2859,7 +2934,6 @@ gdk_window_begin_paint_region (GdkWindow *window, paint = g_new (GdkWindowPaint, 1); paint->region = cairo_region_copy (region); - paint->region_tag = new_region_tag (); cairo_region_intersect (paint->region, window->clip_region_with_children); cairo_region_get_extents (paint->region, &clip_box); @@ -2891,6 +2965,45 @@ 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_impl (window) && + window->has_alpha_background && + (!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) @@ -3410,7 +3523,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; @@ -3447,45 +3560,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); @@ -3493,18 +3576,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 @@ -3779,68 +3850,23 @@ _gdk_window_process_updates_recurse (GdkWindow *window, cairo_region_t *expose_region) { GdkWindow *child; - cairo_region_t *child_region; - GdkRectangle r; + cairo_region_t *clipped_expose_region; GList *l, *children; 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); - /* Make this reentrancy safe for expose handlers freeing windows */ - children = g_list_copy (window->children); - g_list_foreach (children, (GFunc)g_object_ref, NULL); - - /* Iterate over children, starting at topmost */ - for (l = children; l != NULL; l = l->next) - { - child = l->data; - - if (child->destroyed || !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; - - r.x = child->x; - r.y = child->y; - r.width = child->width; - r.height = child->height; - - 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); - } - - if (child->impl == window->impl) - { - /* Client side child, expose */ - cairo_region_intersect (child_region, expose_region); - cairo_region_subtract (expose_region, child_region); - cairo_region_translate (child_region, -child->x, -child->y); - _gdk_window_process_updates_recurse ((GdkWindow *)child, child_region); - } - else - { - /* Native child, just remove area from expose region */ - cairo_region_subtract (expose_region, child_region); - } - cairo_region_destroy (child_region); - } - g_list_foreach (children, (GFunc)g_object_unref, NULL); - g_list_free (children); + /* Paint the window before the children, clipped to the window region + with visible child windows removed */ + clipped_expose_region = cairo_region_copy (expose_region); + cairo_region_intersect (clipped_expose_region, window->clip_region_with_children); - if (!cairo_region_is_empty (expose_region) && + if (!cairo_region_is_empty (clipped_expose_region) && !window->destroyed) { if (window->event_mask & GDK_EXPOSURE_MASK) @@ -3851,8 +3877,8 @@ _gdk_window_process_updates_recurse (GdkWindow *window, event.expose.window = g_object_ref (window); event.expose.send_event = FALSE; event.expose.count = 0; - event.expose.region = expose_region; - cairo_region_get_extents (expose_region, &event.expose.area); + event.expose.region = clipped_expose_region; + cairo_region_get_extents (clipped_expose_region, &event.expose.area); _gdk_event_emit (&event); @@ -3871,11 +3897,41 @@ _gdk_window_process_updates_recurse (GdkWindow *window, * We use begin/end_paint around the clear so that we can * piggyback on the implicit paint */ - gdk_window_begin_paint_region (window, expose_region); - gdk_window_clear_region_internal (window, expose_region); + gdk_window_begin_paint_region (window, clipped_expose_region); + /* The actual clear happens in begin_paint_region */ gdk_window_end_paint (window); } } + cairo_region_destroy (clipped_expose_region); + + /* Make this reentrancy safe for expose handlers freeing windows */ + children = g_list_copy (window->children); + g_list_foreach (children, (GFunc)g_object_ref, NULL); + + /* Iterate over children, starting at bottommost */ + for (l = g_list_last (children); l != NULL; l = l->prev) + { + child = l->data; + + if (child->destroyed || !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; + + /* Client side child, expose */ + if (child->impl == window->impl) + { + cairo_region_translate (expose_region, -child->x, -child->y); + _gdk_window_process_updates_recurse ((GdkWindow *)child, expose_region); + cairo_region_translate (expose_region, child->x, child->y); + } + } + + g_list_free_full (children, g_object_unref); + } /* Process and remove any invalid area on the native window by creating @@ -4581,7 +4637,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); @@ -4601,7 +4657,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) @@ -4612,10 +4682,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 @@ -5261,8 +5328,6 @@ gdk_window_show_unraised (GdkWindow *window) void gdk_window_raise (GdkWindow *window) { - cairo_region_t *old_region, *new_region; - g_return_if_fail (GDK_IS_WINDOW (window)); if (window->destroyed) @@ -5270,26 +5335,14 @@ gdk_window_raise (GdkWindow *window) gdk_window_flush_if_exposing (window); - old_region = NULL; - if (gdk_window_is_viewable (window) && - !window->input_only) - old_region = cairo_region_copy (window->clip_region); - /* Keep children in (reverse) stacking order */ gdk_window_raise_internal (window); recompute_visible_regions (window, TRUE, FALSE); - if (old_region) - { - new_region = cairo_region_copy (window->clip_region); - - cairo_region_subtract (new_region, old_region); - gdk_window_invalidate_region_full (window, new_region, TRUE, CLEAR_BG_ALL); - - cairo_region_destroy (old_region); - cairo_region_destroy (new_region); - } + if (gdk_window_is_viewable (window) && + !window->input_only) + gdk_window_invalidate_region_full (window, window->clip_region, TRUE, CLEAR_BG_ALL); } static void @@ -5989,7 +6042,7 @@ gdk_window_move_resize_internal (GdkWindow *window, gint width, gint height) { - cairo_region_t *old_region, *new_region, *copy_area; + cairo_region_t *old_region, *old_layered, *new_region, *copy_area; cairo_region_t *old_native_child_region, *new_native_child_region; GdkWindow *impl_window; GdkWindowImplClass *impl_class; @@ -6022,6 +6075,7 @@ gdk_window_move_resize_internal (GdkWindow *window, expose = FALSE; old_region = NULL; + old_layered = NULL; impl_window = gdk_window_get_impl_window (window); @@ -6035,8 +6089,10 @@ gdk_window_move_resize_internal (GdkWindow *window, expose = TRUE; old_region = cairo_region_copy (window->clip_region); - /* Adjust region to parent window coords */ + old_layered = cairo_region_copy (window->layered_region); + /* Adjust regions to parent window coords */ cairo_region_translate (old_region, window->x, window->y); + cairo_region_translate (old_layered, window->x, window->y); old_native_child_region = collect_native_child_region (window, TRUE); if (old_native_child_region) @@ -6116,7 +6172,19 @@ 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 */ - copy_area = cairo_region_copy (new_region); + if (window->has_alpha_background) + copy_area = cairo_region_create (); /* Copy nothing for alpha windows */ + else + copy_area = cairo_region_copy (new_region); + + /* Don't copy from a previously layered region */ + cairo_region_translate (old_layered, dx, dy); + cairo_region_subtract (copy_area, old_layered); + + /* Don't copy into a layered region */ + cairo_region_translate (copy_area, -window->x, -window->y); + cairo_region_subtract (copy_area, window->layered_region); + cairo_region_translate (copy_area, window->x, window->y); cairo_region_union (new_region, old_region); @@ -6165,6 +6233,7 @@ gdk_window_move_resize_internal (GdkWindow *window, gdk_window_invalidate_region_full (window->parent, new_region, TRUE, CLEAR_BG_ALL); cairo_region_destroy (old_region); + cairo_region_destroy (old_layered); cairo_region_destroy (new_region); } @@ -6274,7 +6343,7 @@ gdk_window_scroll (GdkWindow *window, gint dy) { GdkWindow *impl_window; - cairo_region_t *copy_area, *noncopy_area; + cairo_region_t *copy_area, *noncopy_area, *old_layered_area; cairo_region_t *old_native_child_region, *new_native_child_region; GList *tmp_list; @@ -6288,6 +6357,7 @@ gdk_window_scroll (GdkWindow *window, 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) { @@ -6328,7 +6398,11 @@ 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 */ - copy_area = cairo_region_copy (window->clip_region); + if (window->has_alpha_background) + copy_area = cairo_region_create (); /* Copy nothing for alpha windows */ + else + copy_area = cairo_region_copy (window->clip_region); + cairo_region_subtract (copy_area, old_layered_area); if (old_native_child_region) { /* Don't copy from inside native children, as this is copied by @@ -6342,6 +6416,7 @@ gdk_window_scroll (GdkWindow *window, } cairo_region_translate (copy_area, dx, dy); cairo_region_intersect (copy_area, window->clip_region); + cairo_region_subtract (copy_area, window->layered_region); /* And the rest need to be invalidated */ noncopy_area = cairo_region_copy (window->clip_region); @@ -6363,6 +6438,7 @@ gdk_window_scroll (GdkWindow *window, gdk_window_invalidate_region_full (window, noncopy_area, TRUE, CLEAR_BG_ALL); cairo_region_destroy (noncopy_area); + cairo_region_destroy (old_layered_area); if (old_native_child_region) { @@ -6410,12 +6486,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 (window->has_alpha_background) + 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 */ @@ -6443,6 +6526,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, @@ -6487,6 +6572,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 @@ -6504,6 +6612,9 @@ void gdk_window_set_background_pattern (GdkWindow *window, cairo_pattern_t *pattern) { + gboolean has_alpha; + cairo_pattern_type_t type; + g_return_if_fail (GDK_IS_WINDOW (window)); if (window->input_only) @@ -6515,6 +6626,69 @@ gdk_window_set_background_pattern (GdkWindow *window, cairo_pattern_destroy (window->background); window->background = pattern; + has_alpha = TRUE; + + if (pattern == NULL) + { + /* 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 + { + type = cairo_pattern_get_type (pattern); + + if (type == CAIRO_PATTERN_TYPE_SOLID) + { + 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++) + { + 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); + } + } + + 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); @@ -7140,7 +7314,7 @@ do_child_shapes (GdkWindow *window, r.height = window->height; region = cairo_region_create_rectangle (&r); - remove_child_area (window, NULL, FALSE, region); + remove_child_area (window, NULL, FALSE, region, NULL); if (merge && window->shape) cairo_region_subtract (region, window->shape); @@ -7259,7 +7433,7 @@ do_child_input_shapes (GdkWindow *window, r.height = window->height; region = cairo_region_create_rectangle (&r); - remove_child_area (window, NULL, TRUE, region); + remove_child_area (window, NULL, TRUE, region, NULL); if (merge && window->shape) cairo_region_subtract (region, window->shape); @@ -7525,114 +7699,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) @@ -8050,12 +8116,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); @@ -8087,6 +8157,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; } @@ -8094,10 +8166,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) @@ -8170,6 +8252,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; @@ -8258,12 +8349,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) @@ -8273,7 +8379,14 @@ 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 && + mode != GDK_CROSSING_TOUCH_BEGIN && + mode != GDK_CROSSING_TOUCH_END) + { + 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); @@ -8982,7 +9095,7 @@ do_synthesize_crossing_event (gpointer data) _gdk_synthesize_crossing_events (display, pointer_info->window_under_pointer, new_window_under_pointer, - device, NULL, + device, pointer_info->last_slave, GDK_CROSSING_NORMAL, pointer_info->toplevel_x, pointer_info->toplevel_y, @@ -9021,17 +9134,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) { @@ -9040,7 +9190,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; @@ -9055,7 +9205,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; @@ -9071,7 +9221,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; @@ -9097,8 +9247,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); @@ -9117,6 +9269,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 @@ -9204,7 +9365,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 */ @@ -9221,28 +9384,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; @@ -9260,22 +9466,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. @@ -9298,6 +9538,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; @@ -9305,6 +9547,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; @@ -9318,9 +9562,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, @@ -9333,23 +9585,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, @@ -9358,17 +9633,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 (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 (event_win == NULL || display->ignore_core_events) + 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) @@ -9379,17 +9710,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: @@ -9401,6 +9798,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; @@ -9485,16 +9884,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; GdkDeviceGrabInfo *button_release_grab; - GdkPointerWindowInfo *pointer_info; + GdkPointerWindowInfo *pointer_info = NULL; GdkDevice *device, *source_device; gboolean is_toplevel; @@ -9508,6 +9906,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 + source_device = pointer_info->last_slave; + } + g_object_get (device, "input-mode", &mode, NULL); _gdk_display_device_grab_update (display, device, source_device, serial); @@ -9526,28 +9935,24 @@ _gdk_windowing_got_event (GdkDisplay *display, if (!event_window) return; - pointer_info = _gdk_display_get_pointer_info (display, device); - #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); + gdk_window_update_visibility_recursively (event_window, event_window); return; } if (!(is_button_type (event->type) || - is_motion_type (event->type)) || + is_motion_type (event->type)) || event_window->window_type == GDK_WINDOW_ROOT) return; @@ -9578,15 +9983,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; @@ -9596,62 +10002,84 @@ _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: @@ -10000,7 +10428,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