X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gdk%2Fgdkwindow.c;h=b0ccd9c7154ea68b4bf808147758d5207c3c3945;hb=d7ea5b526617d9e29c85927f38e80d38196a41e3;hp=f5f07b21a861a74f190262bded10289fc6630a92;hpb=c72a77b04c76b8f28e7680f4acd7a3ca7ec9845c;p=~andy%2Fgtk diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index f5f07b21a..b0ccd9c71 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -37,6 +37,7 @@ #include "gdkdeviceprivate.h" #include "gdkvisualprivate.h" #include "gdkmarshalers.h" +#include "gdkframeclockidle.h" #include "gdkwindowimpl.h" #include @@ -65,25 +66,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 @@ -112,7 +94,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 @@ -202,6 +184,7 @@ struct _GdkWindowPaint cairo_region_t *region; cairo_surface_t *surface; cairo_region_t *flushed; + guint8 alpha; guint uses_implicit : 1; }; @@ -254,6 +237,10 @@ static void gdk_window_invalidate_rect_full (GdkWindow *window, 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 }; @@ -263,6 +250,42 @@ 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 + { + 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 (int 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) { @@ -295,6 +318,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; @@ -397,11 +421,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 @@ -428,11 +452,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 @@ -650,6 +674,13 @@ 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) @@ -677,7 +708,7 @@ remove_layered_child_area (GdkWindow *window, continue; /* Only non-impl children with alpha add to the layered region */ - if (!child->has_alpha_background && gdk_window_has_impl (child)) + if (!gdk_window_has_alpha (child)) continue; r.x = child->x; @@ -779,7 +810,7 @@ remove_child_area (GdkWindow *window, } } - if (child->has_alpha_background) + if (gdk_window_has_alpha (child)) { if (layered_region != NULL) cairo_region_union (layered_region, child_region); @@ -1243,11 +1274,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; } @@ -1299,7 +1331,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 @@ -1343,6 +1376,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); @@ -1373,6 +1411,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) { @@ -1430,6 +1469,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 */ @@ -1680,6 +1726,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)) @@ -1999,6 +2067,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) @@ -2669,7 +2743,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); @@ -2688,7 +2762,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; @@ -2697,8 +2772,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 @@ -2713,13 +2787,14 @@ gdk_window_begin_implicit_paint (GdkWindow *window, GdkRectangle *rect) paint->region = cairo_region_create (); /* Empty */ paint->uses_implicit = 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; } @@ -2744,60 +2819,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; - region = cairo_region_copy (window->clip_region_with_children); + /* 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_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); + 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_intersect (region, paint->region); + paint = l->data; - /* 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; + region = cairo_region_copy (window->clip_region_with_children); + cairo_region_translate (region, window->abs_x, window->abs_y); - cairo_region_subtract (region, tmp_paint->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); - if (!GDK_WINDOW_DESTROYED (window) && !cairo_region_is_empty (region)) + /* Save flushed area so we can read it back if we draw over it later */ + if (paint->flushed == NULL) + paint->flushed = region; + else { - cairo_t *cr; - - /* Remove flushed region from the implicit paint */ - 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); + 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 */ @@ -2810,24 +2891,69 @@ 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); if (paint->flushed) cairo_region_destroy (paint->flushed); @@ -2929,7 +3055,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); @@ -2969,8 +3095,7 @@ gdk_window_begin_paint_region (GdkWindow *window, 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 && + if (gdk_window_has_alpha (window) && (!implicit_paint || (implicit_paint && implicit_paint->flushed != NULL && !cairo_region_is_empty (implicit_paint->flushed)))) { @@ -3298,6 +3423,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)) { @@ -3336,9 +3463,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); @@ -3410,28 +3537,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) @@ -3712,7 +3817,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 @@ -3811,14 +3915,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) { @@ -3832,16 +3928,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 @@ -3850,7 +3950,9 @@ _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; @@ -3859,6 +3961,17 @@ _gdk_window_process_updates_recurse (GdkWindow *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 */ @@ -3931,6 +4044,8 @@ _gdk_window_process_updates_recurse (GdkWindow *window, 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 @@ -4052,7 +4167,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) @@ -4078,7 +4193,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); @@ -4169,18 +4284,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 (); @@ -4215,31 +4325,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; @@ -4265,7 +4364,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 @@ -4277,8 +4376,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); @@ -4287,6 +4392,33 @@ gdk_window_process_updates (GdkWindow *window, g_object_unref (window); } +/** + * 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, @@ -4773,6 +4905,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)); } /** @@ -4793,6 +4926,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); } @@ -5332,8 +5466,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); @@ -5454,8 +5586,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); @@ -5511,8 +5641,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)); @@ -6068,8 +6196,6 @@ gdk_window_move_resize_internal (GdkWindow *window, window->y == y))) return; - gdk_window_flush_if_exposing (window); - /* Handle child windows */ expose = FALSE; @@ -6171,7 +6297,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); @@ -6354,8 +6480,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) @@ -6397,7 +6521,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); @@ -6485,7 +6609,7 @@ gdk_window_move_region (GdkWindow *window, impl_window = gdk_window_get_impl_window (window); /* compute source regions */ - 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 (region); @@ -6555,8 +6679,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; @@ -8115,7 +8239,7 @@ 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 */ @@ -8170,6 +8294,15 @@ is_motion_type (GdkEventType type) 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) @@ -8251,7 +8384,6 @@ _gdk_make_event (GdkWindow *window, event->touch.state = the_state; break; - case GDK_SCROLL: event->scroll.time = the_time; event->scroll.state = the_state; @@ -8340,14 +8472,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) @@ -8358,10 +8503,12 @@ send_crossing_event (GdkDisplay *display, window_event_mask = window->event_mask; if (type == GDK_ENTER_NOTIFY && - pointer_info->need_touch_press_enter && + (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) @@ -9070,10 +9217,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, pointer_info->last_slave, + device, source_device, GDK_CROSSING_NORMAL, pointer_info->toplevel_x, pointer_info->toplevel_y, @@ -9117,15 +9271,33 @@ get_event_window (GdkDisplay *display, GdkEventType type, GdkModifierType mask, guint *evmask_out, + gboolean pointer_emulated, gulong serial) { - guint evmask; + guint evmask, emulated_mask = 0; GdkWindow *grab_window; GdkDeviceGrabInfo *grab; GdkTouchGrabInfo *touch_grab; touch_grab = _gdk_display_has_touch_grab (display, device, sequence, serial); - grab = _gdk_display_has_device_grab (display, device, 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)) @@ -9133,7 +9305,7 @@ get_event_window (GdkDisplay *display, evmask = touch_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; @@ -9150,7 +9322,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; @@ -9165,7 +9337,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; @@ -9181,7 +9353,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; @@ -9208,7 +9380,9 @@ proxy_pointer_event (GdkDisplay *display, gdouble toplevel_x, toplevel_y; guint32 time_; 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); @@ -9229,8 +9403,8 @@ proxy_pointer_event (GdkDisplay *display, if (pointer_info->need_touch_press_enter && gdk_device_get_source (pointer_info->last_slave) != GDK_SOURCE_TOUCHSCREEN && - gdk_device_get_source (pointer_info->last_slave) != GDK_SOURCE_TOUCHPAD) - + (source_event->type != GDK_TOUCH_UPDATE || + _gdk_event_get_pointer_emulated (source_event))) { pointer_info->need_touch_press_enter = FALSE; need_synthetic_enter = TRUE; @@ -9359,8 +9533,32 @@ proxy_pointer_event (GdkDisplay *display, 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) @@ -9382,6 +9580,7 @@ proxy_pointer_event (GdkDisplay *display, is_hint = FALSE; if (event_win && + event_type == GDK_MOTION_NOTIFY && (evmask & GDK_POINTER_MOTION_HINT_MASK)) { gulong *device_serial; @@ -9402,32 +9601,53 @@ proxy_pointer_event (GdkDisplay *display, if (!event_win) return TRUE; - if (!display->ignore_core_events) - { - GdkEventType event_type; - - event_type = source_event->type; - - event = gdk_event_new (event_type); - event->any.window = g_object_ref (event_win); - event->any.send_event = source_event->any.send_event; - event->motion.time = time_; - 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->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); - - /* Just insert the event */ - _gdk_event_queue_insert_after (gdk_window_get_display (event_win), - source_event, event); + 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->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->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; + + 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. @@ -9451,6 +9671,7 @@ proxy_button_event (GdkEvent *source_event, GdkWindow *parent; GdkEvent *event; GdkPointerWindowInfo *pointer_info; + GdkDeviceGrabInfo *pointer_grab; guint state; guint32 time_; GdkEventType type; @@ -9476,11 +9697,14 @@ proxy_button_event (GdkEvent *source_event, 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, @@ -9544,16 +9768,40 @@ proxy_button_event (GdkEvent *source_event, sequence, pointer_window, type, state, - &evmask, serial); + &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 && + if ((type == GDK_BUTTON_PRESS || + (type == GDK_TOUCH_BEGIN && + _gdk_event_get_pointer_emulated (source_event))) && pointer_info->need_touch_press_enter) { GdkCrossingMode mode; @@ -9562,8 +9810,7 @@ proxy_button_event (GdkEvent *source_event, * 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 || - gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHPAD) + if (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN) mode = GDK_CROSSING_TOUCH_BEGIN; else mode = GDK_CROSSING_DEVICE_SWITCH; @@ -9577,6 +9824,13 @@ proxy_button_event (GdkEvent *source_event, 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); @@ -9588,20 +9842,34 @@ 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); - else if (type == GDK_BUTTON_RELEASE && + 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 || - gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHPAD)) + gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN) { /* Synthesize a leave notify event * whenever a touch device is released @@ -9622,13 +9890,15 @@ proxy_button_event (GdkEvent *source_event, convert_toplevel_coords_to_window (event_win, toplevel_x, toplevel_y, &event->button.x, &event->button.y); - event->touch.x_root = source_event->touch.x_root; - event->touch.y_root = source_event->touch.y_root; + 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); @@ -9660,6 +9930,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; @@ -9671,11 +9943,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", @@ -9714,11 +9991,8 @@ 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); + s = print_region (window->clip_region); + g_print (" clipbox[%s]", s); g_print ("\n"); } @@ -9750,7 +10024,7 @@ _gdk_windowing_got_event (GdkDisplay *display, { GdkWindow *event_window; gdouble x, y; - gboolean unlink_event; + gboolean unlink_event = FALSE; GdkDeviceGrabInfo *button_release_grab; GdkPointerWindowInfo *pointer_info = NULL; GdkDevice *device, *source_device; @@ -9773,7 +10047,7 @@ _gdk_windowing_got_event (GdkDisplay *display, 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 + else if (pointer_info->last_slave) source_device = pointer_info->last_slave; } @@ -9793,7 +10067,7 @@ _gdk_windowing_got_event (GdkDisplay *display, event_window = event->any.window; if (!event_window) - return; + goto out; #ifdef DEBUG_WINDOW_PRINTING if (event->type == GDK_KEY_PRESS && @@ -9808,13 +10082,13 @@ _gdk_windowing_got_event (GdkDisplay *display, { event_window->native_visibility = event->visibility.state; gdk_window_update_visibility_recursively (event_window, event_window); - return; + goto out; } if (!(is_button_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); @@ -9878,7 +10152,9 @@ _gdk_windowing_got_event (GdkDisplay *display, } } - if (pointer_info) + if (pointer_info && + (!is_touch_type (event->type) || + _gdk_event_get_pointer_emulated (event))) { guint old_state, old_button; @@ -9895,6 +10171,9 @@ _gdk_windowing_got_event (GdkDisplay *display, 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 || @@ -9902,7 +10181,6 @@ _gdk_windowing_got_event (GdkDisplay *display, _gdk_display_enable_motion_hints (display, device); } - unlink_event = FALSE; if (is_motion_type (event->type)) unlink_event = proxy_pointer_event (display, event, serial); else if (is_button_type (event->type)) @@ -9944,6 +10222,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); } /** @@ -9983,7 +10268,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) { @@ -10283,7 +10568,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 @@ -10549,6 +10834,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 @@ -10855,61 +11201,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 */ @@ -10917,7 +11251,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. @@ -11228,3 +11578,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; +}