Compare commits

...

36 Commits

Author SHA1 Message Date
Alexander Larsson
2ce9c8cff2 gtkpixelcache: Use CONTENT_COLOR for solid bg windows 2013-05-02 15:13:03 +02:00
Alexander Larsson
76add3ec39 gdkwindow: Send unclipped invalidate requests to invalidate handler
This way we can handle buffering caches outside a window. For instance
in the treeview which has a bin_window which is just the size of the
viewport.
2013-05-02 14:55:59 +02:00
Alexander Larsson
30b10fe78b GtkTreeView: Fix pixel cache redraws
Don't redraw everything on scroll, and don't clip to bin window
when drawing to pixel cache.
2013-05-02 11:48:50 +02:00
Alexander Larsson
640ead7656 GtkPixelCache: Add debug feature to track redraws
Each time we redraw we tint it in a different color so that
you can see which regions are redrawn.
2013-05-02 11:47:06 +02:00
Alexander Larsson
16623aabd1 GtkTreeView: Invalidate pixel cache on queue_draw
Whenever queue_draw is called on the treeview we invalidate the
whole pixel cache, as there is no way we can know when the area
outside the visible region should be updated from the region
(as it is in widget coords).

This is not typically a problem though, we only queue redraws
on the treeview itself on e.g. resizes or restyles, which are
generally rare.
2013-05-02 11:12:50 +02:00
Alexander Larsson
958df1c4bf GtkTreeView: Invalidate cache for gtk_tree_view_node_queue_redraw
When a row changes we invalidate only the right area in the
bin_window. This means we also invalidate the right area
of the pixel cache.
2013-05-02 11:11:29 +02:00
Alexander Larsson
f05fc2dd57 GtkWidget: Add a vfunc for queue_draw_region
Since widgets now cache drawn state we allow them to override
queue_draw_region to detect when some region of the widget
should be redrawn. For instance, if a widget draws the
background color in a pixel cache we will need to invalidate
that when the style context changes which queues a repaint.
2013-05-02 11:08:10 +02:00
Alexander Larsson
7442ca3f63 GtkPixelCache: Allow NULL for invalidate region 2013-05-02 11:07:09 +02:00
Alexander Larsson
f4006d6eaf GtkTreeView: Initial port to PixelCache 2013-04-30 21:34:22 +02:00
Alexander Larsson
827295d016 GtkPixelCache: Fix x/y typos 2013-04-30 21:33:56 +02:00
Alexander Larsson
770ef2dc83 GtkPixelCache: Make sure we always copy using SOURCE
We were using OVER for the first copy (from source to group surface.
2013-04-30 16:13:44 +02:00
Alexander Larsson
d0e34a0ac9 Split out offscreen surface from GtkViewport to GtkPixelCache
This will make it easier to do similar scrolling in other widgets.
2013-04-30 15:13:36 +02:00
Alexander Larsson
0ec74de37d gtkcontainer: Don't stop propagation if no expose_window
We should always propagate to children in a plain gtk_widget_draw()
call.
2013-04-30 15:02:38 +02:00
Alexander Larsson
8856966e5b wayland: Don't use double buffers for wayland when not needed
If we got the release event for the last buffer then we're
fine with writing directly to the window surface, as wayland
will not be looing at it. This saves us from allocating
and copying more data.
2013-04-29 21:49:12 +02:00
Alexander Larsson
18f540a9d4 gdkwindow: Make GdkPaintable normal GdkWindowImpl vfuncs
There is no need for this to be a separate interface, its just looking
weird.
2013-04-29 19:09:41 +02:00
Alexander Larsson
a6bffcfb57 gdkwindow: Don't error out on NULL backgrounds 2013-04-29 19:09:41 +02:00
Alexander Larsson
cc21fafd20 gdkwindow: Change how paints work
First of all, we now only do paints on native windows, as there is
really no reason anymore to do it for subwindows. Secondly, we
keep track of the paints even for GtkPaintable windows, but for
that case we don't create the offscreen surface, but rather
assume the windowing system does the backing store.
2013-04-29 19:09:40 +02:00
Alexander Larsson
1224011b8a Don't paint window backgrounds for non-double buffered windows
We used to paint the background in begin_paint, which was never
called for non-double buffered windows, so we do the same in the
new world. This makes emacs painting work again.
2013-04-29 19:09:40 +02:00
Alexander Larsson
5dbd2f200b GtkWindow: Move cairo_save/restore from marshaller
We do the save/restore when emitting ::draw rather than in a custom
marshaller, as this saves an extra stack frame, which is helpfull now
that we do painting fully recursive. This is also likely to save a few
cycles.
2013-04-29 19:09:40 +02:00
Alexander Larsson
33f33b0e65 gdkframeclock: Fix layout loop warning 2013-04-29 19:09:40 +02:00
Alexander Larsson
4c5a5bb6d3 GtkWidget: Avoid widget->priv->alloc_needed when propagating exposes
It turns out that at various times we currently propagate draws
when the child has alloc_needed set. We need to fix these, but
we don't want that to block the current work, so for now we just
avoid that assert when we propagate draws (its what we used to
do after all).
2013-04-29 19:09:40 +02:00
Alexander Larsson
08df83daec gdkframeclock: Loop the layout phase if needed
In the case where the layout phase queued a layout we don't
want to progress to the paint phase with invalid allocations, so
we loop the layout. This shouldn't normally happen, but it may
happen in some edge cases like if user/wm resizes clash with
natural window size changes from a gtk widget. This should not
generally loop though, so we detect this after 4 cycles and
print a warning.

This was detected because of an issue in GtkWindow where it
seems to incorrectly handle the case of a user interactive resize.
It seems gtk_window_move_resize() believes that configure_request_size_changed
changed due to hitting some corner case so it calls
gtk_widget_queue_resize_no_redraw(), marking the window as need_alloc
after the layout phase. This commit fixes the issue, but we should
also look into if we can fix that.
2013-04-29 19:09:40 +02:00
Alexander Larsson
0e71895274 Change the way we draw (again)
We can't really just draw by walking down the widget hierarchy, as
this doesn't get the right clipping (so e.g. widgets doing cairo_paint
may draw outside the expected gdkwindow subarea) nor does it let
us paint window backgrounds.

So, we now do multiple draws for each widget, once for each expose event
although we still do it at the same base cairo_t that we get for the
toplevel native window.

We also collect all the windows of a widget so we can expose them inside
the same opacity group if needed.
2013-04-29 19:09:40 +02:00
Alexander Larsson
5eefd88208 gdkwindow: Change update_handler to invalidate_handler
The new name fits better, plus we also remove the return value, and
adds docs.
2013-04-29 19:09:40 +02:00
Alexander Larsson
ebee0d80fb GtkViewport: Add offscreen surface based scrolling
Rather than scroll via XCopyArea (which we no longer do) we keep an
offscreen buffer of the scrolled area with some extra space outside
the visible area and when we expose the viewport we just blit the
offscreen to the right place.
2013-04-29 19:09:40 +02:00
Alexander Larsson
1703c5a87a gtkcontainer: Don't propagate draws to non-drawable widgets
This short-circuits the propagation a bit earlier and fixes
a crash when looking for has_native_window on non-realized widgets.
2013-04-29 19:09:40 +02:00
Alexander Larsson
8251523e1d Add gdk_window_set_update_handler
This lets you register callbacks for when child widgets invalidate
areas of the window read it and/or change it.

For instance, this lets you do rendering effects and keeping offscreen
caches uptodate.
2013-04-29 19:09:39 +02:00
Alexander Larsson
b8d3b98416 gdkwindow: Simplify invalidation
Now that all windows are non-opaque we can simplify the invalidation
a lot. There is no need to clip the invalidate area to child regions,
because we will always redraw everything under all the children.
We only have to handle native childen specially.
2013-04-29 19:09:39 +02:00
Alexander Larsson
7e4f48e55e gdkwindow: Remove implicit paints
We now only do one expose event per native window, so there will
only be one begin/end_paint() call. This means all the work with
implicit paints to combine the paints on a single double buffer
surface is unnecessary, so we can just delete it.
2013-04-29 19:09:39 +02:00
Alexander Larsson
0770104286 Only handle exposes on native window, propagate to children via draw()
We now consider non-native windows non-opaque, which means any invalid
area in a subwindow will also be invalid all the way up to the nearest
native windows. We take advantage of this by ignoring all expose events
on non-native windows (which typically means just the toplevel) and instead
propagating down the draw() calls to children directly via
gtk_container_propagate_draw.

This is nice as it means we always draw widgets the same way, and it
will let us do some interesting ways in the future.

We also clean up the GtkWidget opacity handling as we can now always
rely on the draing happening via cairo.

NOTE: This change neuters gtk_widget_set_double_buffered for
widgets without native windows
2013-04-29 19:09:39 +02:00
Alexander Larsson
f03474a4a0 gdkwindow: Remove ancient USE_BACKING_STORE define
This is always set anyway.
2013-04-29 19:09:39 +02:00
Alexander Larsson
08d0270614 gdkwindow: Simplify clip region calculations
Since we no longer make overlapping siblings affect clip_region we
can further simplify the clip region calculation and updating.
2013-04-29 19:09:39 +02:00
Alexander Larsson
4106b75a25 gdkwindow: Simplify clip region handling
Since we dropped the move region optimization there is really no need
to try carefully keep track of opaque non-overlapped regions, as we
don't use this information to trigger the optimization anymore.

So, by assuming that all windows are non-opaque we can vastly simplify
the clip region stuff. First of all, we don't need clip_region_with_children,
as each window will need to draw under all children anyway. Secondly, we
don't remove overlapping sibling areas from clip_region, as these are
all non-opaque anyway and we need to draw under them

Finally, we don't need to track the layered region anymore as its
essentially unused. The few times something like it is needed we can
compute it explicitly.

For the case of native children of widgets we may cause a repaint
under native windows that are guaranteed to be opaque, but these
will be clipped by the native child anyway.
2013-04-29 19:09:39 +02:00
Alexander Larsson
7c1d9332e2 gdkwindow: Remove translate vfunc
This is not used anymore
2013-04-29 19:09:39 +02:00
Alexander Larsson
ce8bd92cb9 gdkwindow: Remove outstanding_moves stuff
Since we now never move regions directly on the window we can
remove all the stuff that track outstanding moves and flushes then.
2013-04-29 19:09:39 +02:00
Alexander Larsson
896a170421 gdk: Don't ever do copies from the window
This basically neuters gdk_window_move_region, gdk_window_scroll
and gdk_window_move_resize, in that they now never copy any bits but
just invalidate the source and destination regions. This is a performance
loss, but the hope is that the simplifications it later allows will let
us recover this performance loss (which mainly affects scrolling).
2013-04-29 19:09:38 +02:00
23 changed files with 1255 additions and 2215 deletions

View File

@@ -1470,56 +1470,6 @@ _gdk_broadway_window_queue_antiexpose (GdkWindow *window,
return TRUE;
}
static void
copy_region (cairo_surface_t *surface,
cairo_region_t *area,
gint dx,
gint dy)
{
cairo_t *cr;
cr = cairo_create (surface);
gdk_cairo_region (cr, area);
cairo_clip (cr);
/* NB: This is a self-copy and Cairo doesn't support that yet.
* So we do a litle trick.
*/
cairo_push_group (cr);
cairo_set_source_surface (cr, surface, dx, dy);
cairo_paint (cr);
cairo_pop_group_to_source (cr);
cairo_paint (cr);
cairo_destroy (cr);
}
void
_gdk_broadway_window_translate (GdkWindow *window,
cairo_region_t *area,
gint dx,
gint dy)
{
GdkWindowImplBroadway *impl;
GdkBroadwayDisplay *broadway_display;
impl = GDK_WINDOW_IMPL_BROADWAY (window->impl);
if (impl->surface)
{
copy_region (impl->surface, area, dx, dy);
broadway_display = GDK_BROADWAY_DISPLAY (gdk_window_get_display (window));
if (_gdk_broadway_server_window_translate (broadway_display->server,
impl->id,
area, dx, dy))
queue_flush (window);
}
}
guint32
gdk_broadway_get_last_seen_time (GdkWindow *window)
{
@@ -1558,7 +1508,6 @@ gdk_window_impl_broadway_class_init (GdkWindowImplBroadwayClass *klass)
impl_class->input_shape_combine_region = gdk_window_broadway_input_shape_combine_region;
impl_class->set_static_gravities = gdk_window_broadway_set_static_gravities;
impl_class->queue_antiexpose = _gdk_broadway_window_queue_antiexpose;
impl_class->translate = _gdk_broadway_window_translate;
impl_class->destroy = _gdk_broadway_window_destroy;
impl_class->destroy_foreign = gdk_broadway_window_destroy_foreign;
impl_class->resize_cairo_surface = gdk_window_broadway_resize_cairo_surface;

View File

@@ -384,6 +384,7 @@ gdk_frame_clock_paint_idle (void *data)
case GDK_FRAME_CLOCK_PHASE_LAYOUT:
if (priv->freeze_count == 0)
{
int iter;
#ifdef G_ENABLE_DEBUG
if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
{
@@ -394,11 +395,20 @@ gdk_frame_clock_paint_idle (void *data)
#endif /* G_ENABLE_DEBUG */
priv->phase = GDK_FRAME_CLOCK_PHASE_LAYOUT;
if (priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT)
/* We loop in the layout phase, because we don't want to progress
* into the paint phase with invalid size allocations. This may
* happen in some situation like races between user window
* resizes and natural size changes.
*/
iter = 0;
while ((priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT) &&
priv->freeze_count == 0 && iter++ < 4)
{
priv->requested &= ~GDK_FRAME_CLOCK_PHASE_LAYOUT;
g_signal_emit_by_name (G_OBJECT (clock), "layout");
}
if (iter == 5)
g_warning ("gdk-frame-clock: layout continuously requested, giving up after 4 tries");
}
case GDK_FRAME_CLOCK_PHASE_PAINT:
if (priv->freeze_count == 0)

View File

@@ -224,6 +224,7 @@ struct _GdkWindow
guint native_visibility : 2; /* the native visibility of a impl windows */
guint viewable : 1; /* mapped and all parents mapped */
guint applied_shape : 1;
guint in_update : 1;
GdkFullscreenMode fullscreen_mode;
/* The GdkWindow that has the impl, ref:ed if another window.
@@ -250,10 +251,6 @@ struct _GdkWindow
GdkCursor *cursor;
GHashTable *device_cursor;
GSList *implicit_paint;
GList *outstanding_moves;
cairo_region_t *shape;
cairo_region_t *input_shape;
@@ -269,6 +266,7 @@ struct _GdkWindow
guint num_offscreen_children;
GdkFrameClock *frame_clock; /* NULL to use from parent or default */
GdkWindowInvalidateHandlerFunc invalidate_handler;
};
#define GDK_WINDOW_TYPE(d) (((GDK_WINDOW (d)))->window_type)
@@ -354,26 +352,6 @@ void _gdk_windowing_got_event (GdkDisplay *display,
#define GDK_WINDOW_IS_MAPPED(window) (((window)->state & GDK_WINDOW_STATE_WITHDRAWN) == 0)
#define GDK_TYPE_PAINTABLE (_gdk_paintable_get_type ())
#define GDK_PAINTABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_PAINTABLE, GdkPaintable))
#define GDK_IS_PAINTABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_PAINTABLE))
#define GDK_PAINTABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GDK_TYPE_PAINTABLE, GdkPaintableIface))
typedef struct _GdkPaintable GdkPaintable;
typedef struct _GdkPaintableIface GdkPaintableIface;
struct _GdkPaintableIface
{
GTypeInterface g_iface;
void (* begin_paint_region) (GdkPaintable *paintable,
GdkWindow *window,
const cairo_region_t *region);
void (* end_paint) (GdkPaintable *paintable);
};
GType _gdk_paintable_get_type (void) G_GNUC_CONST;
void _gdk_window_invalidate_for_expose (GdkWindow *window,
cairo_region_t *region);

View File

@@ -555,42 +555,6 @@ gdk_offscreen_window_queue_antiexpose (GdkWindow *window,
return FALSE;
}
static void
gdk_offscreen_window_translate (GdkWindow *window,
cairo_region_t *area,
gint dx,
gint dy)
{
GdkOffscreenWindow *offscreen = GDK_OFFSCREEN_WINDOW (window->impl);
if (offscreen->surface)
{
cairo_t *cr;
cr = cairo_create (offscreen->surface);
area = cairo_region_copy (area);
gdk_cairo_region (cr, area);
cairo_clip (cr);
/* NB: This is a self-copy and Cairo doesn't support that yet.
* So we do a litle trick.
*/
cairo_push_group (cr);
cairo_set_source_surface (cr, offscreen->surface, dx, dy);
cairo_paint (cr);
cairo_pop_group_to_source (cr);
cairo_paint (cr);
cairo_destroy (cr);
}
_gdk_window_add_damage (window, area);
}
static cairo_surface_t *
gdk_offscreen_window_resize_cairo_surface (GdkWindow *window,
cairo_surface_t *surface,
@@ -752,7 +716,6 @@ gdk_offscreen_window_class_init (GdkOffscreenWindowClass *klass)
impl_class->input_shape_combine_region = gdk_offscreen_window_input_shape_combine_region;
impl_class->set_static_gravities = gdk_offscreen_window_set_static_gravities;
impl_class->queue_antiexpose = gdk_offscreen_window_queue_antiexpose;
impl_class->translate = gdk_offscreen_window_translate;
impl_class->destroy = gdk_offscreen_window_destroy;
impl_class->destroy_foreign = NULL;
impl_class->resize_cairo_surface = gdk_offscreen_window_resize_cairo_surface;

File diff suppressed because it is too large Load Diff

View File

@@ -634,6 +634,26 @@ gboolean gdk_window_set_static_gravities (GdkWindow *window,
/* GdkWindow */
/**
* GdkWindowInvalidateHandlerFunc:
* @window: a #GdkWindow
* @region: a #cairo_region_t
*
* Whenever some area of the window is invalidated (directly in the
* window or in a child window) this gets called with @region in
* the coordinate space of @window. You can use @region to just
* keep track of the dirty region, or you can actually change
* @region in case you are doing display tricks like showing
* a child in multiple places.
*
* Since: 3.10
*/
typedef void (*GdkWindowInvalidateHandlerFunc) (GdkWindow *window,
cairo_region_t *region);
GDK_AVAILABLE_IN_3_10
void gdk_window_set_invalidate_handler (GdkWindow *window,
GdkWindowInvalidateHandlerFunc handler);
gboolean gdk_window_has_native (GdkWindow *window);
void gdk_window_set_type_hint (GdkWindow *window,
GdkWindowTypeHint hint);

View File

@@ -101,6 +101,9 @@ struct _GdkWindowImplClass
gint *x,
gint *y,
GdkModifierType *mask);
gboolean (* begin_paint_region) (GdkWindow *window,
const cairo_region_t *region);
void (* end_paint) (GdkWindow *window);
cairo_region_t * (* get_shape) (GdkWindow *window);
cairo_region_t * (* get_input_shape) (GdkWindow *window);
@@ -125,16 +128,6 @@ struct _GdkWindowImplClass
gboolean (* queue_antiexpose) (GdkWindow *window,
cairo_region_t *update_area);
/* Called to move @area inside @window by @dx x @dy pixels. @area is
* guaranteed to be inside @window. If part of @area is not invisible or
* invalid, it is this function's job to queue expose events in those
* areas.
*/
void (* translate) (GdkWindow *window,
cairo_region_t *area,
gint dx,
gint dy);
/* Called to do the windowing system specific part of gdk_window_destroy(),
*
* window: The window being destroyed

View File

@@ -364,19 +364,18 @@ gdk_window_impl_quartz_init (GdkWindowImplQuartz *impl)
impl->type_hint = GDK_WINDOW_TYPE_HINT_NORMAL;
}
static void
gdk_window_impl_quartz_begin_paint_region (GdkPaintable *paintable,
GdkWindow *window,
static gboolean
gdk_window_impl_quartz_begin_paint_region (GdkWindow *window,
const cairo_region_t *region)
{
GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (paintable);
GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->imp);
cairo_region_t *clipped_and_offset_region;
cairo_t *cr;
clipped_and_offset_region = cairo_region_copy (region);
cairo_region_intersect (clipped_and_offset_region,
window->clip_region_with_children);
window->clip_region);
cairo_region_translate (clipped_and_offset_region,
window->abs_x, window->abs_y);
@@ -415,12 +414,14 @@ gdk_window_impl_quartz_begin_paint_region (GdkPaintable *paintable,
done:
cairo_region_destroy (clipped_and_offset_region);
return FALSE;
}
static void
gdk_window_impl_quartz_end_paint (GdkPaintable *paintable)
gdk_window_impl_quartz_end_paint (GdkWindow *window)
{
GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (paintable);
GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
impl->begin_paint_count--;
@@ -536,13 +537,6 @@ _gdk_quartz_display_after_process_all_updates (GdkDisplay *display)
NSEnableScreenUpdates ();
}
static void
gdk_window_impl_quartz_paintable_init (GdkPaintableIface *iface)
{
iface->begin_paint_region = gdk_window_impl_quartz_begin_paint_region;
iface->end_paint = gdk_window_impl_quartz_end_paint;
}
static const gchar *
get_default_title (void)
{
@@ -2237,49 +2231,6 @@ gdk_quartz_window_queue_antiexpose (GdkWindow *window,
return FALSE;
}
static void
gdk_quartz_window_translate (GdkWindow *window,
cairo_region_t *area,
gint dx,
gint dy)
{
cairo_region_t *invalidate, *scrolled;
GdkWindowImplQuartz *impl = (GdkWindowImplQuartz *)window->impl;
GdkRectangle extents;
cairo_region_get_extents (area, &extents);
[impl->view scrollRect:NSMakeRect (extents.x - dx, extents.y - dy,
extents.width, extents.height)
by:NSMakeSize (dx, dy)];
if (impl->needs_display_region)
{
cairo_region_t *intersection;
/* Invalidate already invalidated area that was moved at new
* location.
*/
intersection = cairo_region_copy (impl->needs_display_region);
cairo_region_intersect (intersection, area);
cairo_region_translate (intersection, dx, dy);
gdk_quartz_window_set_needs_display_in_region (window, intersection);
cairo_region_destroy (intersection);
}
/* Calculate newly exposed area that needs invalidation */
scrolled = cairo_region_copy (area);
cairo_region_translate (scrolled, dx, dy);
invalidate = cairo_region_copy (area);
cairo_region_subtract (invalidate, scrolled);
cairo_region_destroy (scrolled);
gdk_quartz_window_set_needs_display_in_region (window, invalidate);
cairo_region_destroy (invalidate);
}
static void
gdk_quartz_window_set_focus_on_map (GdkWindow *window,
gboolean focus_on_map)
@@ -3078,12 +3029,13 @@ gdk_window_impl_quartz_class_init (GdkWindowImplQuartzClass *klass)
impl_class->input_shape_combine_region = gdk_window_quartz_input_shape_combine_region;
impl_class->set_static_gravities = gdk_window_quartz_set_static_gravities;
impl_class->queue_antiexpose = gdk_quartz_window_queue_antiexpose;
impl_class->translate = gdk_quartz_window_translate;
impl_class->destroy = gdk_quartz_window_destroy;
impl_class->destroy_foreign = gdk_quartz_window_destroy_foreign;
impl_class->resize_cairo_surface = gdk_window_quartz_resize_cairo_surface;
impl_class->get_shape = gdk_quartz_window_get_shape;
impl_class->get_input_shape = gdk_quartz_window_get_input_shape;
impl_class->begin_paint_region = gdk_window_impl_quartz_begin_paint_region;
impl_class->end_paint = gdk_window_impl_quartz_end_paint;
impl_class->focus = gdk_quartz_window_focus;
impl_class->set_type_hint = gdk_quartz_window_set_type_hint;
@@ -3159,19 +3111,9 @@ _gdk_window_impl_quartz_get_type (void)
(GInstanceInitFunc) gdk_window_impl_quartz_init,
};
const GInterfaceInfo paintable_info =
{
(GInterfaceInitFunc) gdk_window_impl_quartz_paintable_init,
NULL,
NULL
};
object_type = g_type_register_static (GDK_TYPE_WINDOW_IMPL,
"GdkWindowImplQuartz",
&object_info, 0);
g_type_add_interface_static (object_type,
GDK_TYPE_PAINTABLE,
&paintable_info);
}
return object_type;

View File

@@ -382,11 +382,24 @@ on_frame_clock_before_paint (GdkFrameClock *clock,
}
}
static const cairo_user_data_key_t gdk_wayland_cairo_key;
typedef struct _GdkWaylandCairoSurfaceData {
gpointer buf;
size_t buf_length;
struct wl_shm_pool *pool;
struct wl_buffer *buffer;
GdkWaylandDisplay *display;
int32_t width, height;
gboolean busy;
} GdkWaylandCairoSurfaceData;
static void
on_frame_clock_after_paint (GdkFrameClock *clock,
GdkWindow *window)
{
GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
GdkWaylandCairoSurfaceData *data;
struct wl_callback *callback;
if (!impl->pending_commit)
@@ -400,6 +413,10 @@ on_frame_clock_after_paint (GdkFrameClock *clock,
_gdk_frame_clock_freeze (clock);
wl_surface_commit (impl->surface);
data = cairo_surface_get_user_data (impl->cairo_surface,
&gdk_wayland_cairo_key);
data->busy = TRUE;
}
void
@@ -462,17 +479,6 @@ _gdk_wayland_display_create_window_impl (GdkDisplay *display,
G_CALLBACK (on_frame_clock_after_paint), window);
}
static const cairo_user_data_key_t gdk_wayland_cairo_key;
typedef struct _GdkWaylandCairoSurfaceData {
gpointer buf;
size_t buf_length;
struct wl_shm_pool *pool;
struct wl_buffer *buffer;
GdkWaylandDisplay *display;
int32_t width, height;
} GdkWaylandCairoSurfaceData;
static void
gdk_wayland_window_attach_image (GdkWindow *window)
{
@@ -588,6 +594,19 @@ _create_shm_pool (struct wl_shm *shm,
return pool;
}
static void
buffer_release_callback (void *_data, struct wl_buffer *wl_buffer)
{
GdkWaylandCairoSurfaceData *data = _data;
data->busy = FALSE;
}
static const struct wl_buffer_listener buffer_listener = {
buffer_release_callback
};
static cairo_surface_t *
gdk_wayland_create_cairo_surface (GdkWaylandDisplay *display,
int width, int height)
@@ -602,6 +621,7 @@ gdk_wayland_create_cairo_surface (GdkWaylandDisplay *display,
data->buffer = NULL;
data->width = width;
data->height = height;
data->busy = FALSE;
stride = width * 4;
@@ -613,6 +633,7 @@ gdk_wayland_create_cairo_surface (GdkWaylandDisplay *display,
data->buffer = wl_shm_pool_create_buffer (data->pool, 0,
width, height,
stride, WL_SHM_FORMAT_ARGB8888);
wl_buffer_add_listener (data->buffer, &buffer_listener, data);
surface = cairo_image_surface_create_for_data (data->buf,
CAIRO_FORMAT_ARGB32,
@@ -668,6 +689,20 @@ gdk_wayland_window_ref_cairo_surface (GdkWindow *window)
}
static gboolean
gdk_window_impl_wayland_begin_paint_region (GdkWindow *window,
const cairo_region_t *region)
{
GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
GdkWaylandCairoSurfaceData *data;
gdk_wayland_window_ensure_cairo_surface (window);
data = cairo_surface_get_user_data (impl->cairo_surface,
&gdk_wayland_cairo_key);
return data->busy;
}
static void
gdk_window_impl_wayland_finalize (GObject *object)
{
@@ -1193,15 +1228,6 @@ gdk_wayland_window_queue_antiexpose (GdkWindow *window,
return FALSE;
}
static void
gdk_wayland_window_translate (GdkWindow *window,
cairo_region_t *area,
gint dx,
gint dy)
{
_gdk_window_invalidate_for_expose (window, area);
}
static void
gdk_wayland_window_destroy (GdkWindow *window,
gboolean recursing,
@@ -1908,12 +1934,12 @@ _gdk_window_impl_wayland_class_init (GdkWindowImplWaylandClass *klass)
impl_class->input_shape_combine_region = gdk_window_wayland_input_shape_combine_region;
impl_class->set_static_gravities = gdk_window_wayland_set_static_gravities;
impl_class->queue_antiexpose = gdk_wayland_window_queue_antiexpose;
impl_class->translate = gdk_wayland_window_translate;
impl_class->destroy = gdk_wayland_window_destroy;
impl_class->destroy_foreign = gdk_window_wayland_destroy_foreign;
impl_class->resize_cairo_surface = gdk_window_wayland_resize_cairo_surface;
impl_class->get_shape = gdk_wayland_window_get_shape;
impl_class->get_input_shape = gdk_wayland_window_get_input_shape;
impl_class->begin_paint_region = gdk_window_impl_wayland_begin_paint_region;
/* impl_class->beep */
impl_class->focus = gdk_wayland_window_focus;

View File

@@ -3319,77 +3319,6 @@ _gdk_win32_window_queue_antiexpose (GdkWindow *window,
return FALSE;
}
/* Gets called from gdwindow.c(do_move_region_bits_on_impl)
* and got tested with testgtk::big_window. Given the previous,
* untested implementation this one looks much too simple ;)
*/
static void
_gdk_win32_window_translate (GdkWindow *window,
cairo_region_t *area, /* In impl window coords */
gint dx,
gint dy)
{
GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
HRGN hrgn, area_hrgn;
cairo_region_t *update_region;
HDC hdc;
int ret;
/* Note: This is the destination area, not the source, and
it has been moved by dx, dy from the source area */
area_hrgn = cairo_region_to_hrgn (area, 0, 0);
/* First we copy any outstanding invalid areas in the
source area to the new position in the destination area */
hrgn = CreateRectRgn (0, 0, 0, 0);
ret = GetUpdateRgn (GDK_WINDOW_HWND (window), hrgn, FALSE);
if (ret == ERROR)
WIN32_API_FAILED ("GetUpdateRgn");
else if (ret != NULLREGION)
{
/* Convert the source invalid region as it would be copied */
OffsetRgn (hrgn, dx, dy);
/* Keep what intersects the copy destination area */
ret = CombineRgn (hrgn, hrgn, area_hrgn, RGN_AND);
/* And invalidate it */
if (ret == ERROR)
WIN32_API_FAILED ("CombineRgn");
else if (ret != NULLREGION)
API_CALL (InvalidateRgn, (GDK_WINDOW_HWND (window), hrgn, TRUE));
}
/* Then we copy the bits, invalidating whatever is copied from
otherwise invisible areas */
hdc = _gdk_win32_impl_acquire_dc (impl);
/* Clip hdc to target region */
API_CALL (SelectClipRgn, (hdc, area_hrgn));
SetRectRgn (hrgn, 0, 0, 0, 0);
if (!ScrollDC (hdc, dx, dy, NULL, NULL, hrgn, NULL))
WIN32_GDI_FAILED ("ScrollDC");
else
{
update_region = _gdk_win32_hrgn_to_region (hrgn);
if (!cairo_region_is_empty (update_region))
_gdk_window_invalidate_for_expose (window, update_region);
cairo_region_destroy (update_region);
}
/* Unset hdc clip region */
API_CALL (SelectClipRgn, (hdc, NULL));
_gdk_win32_impl_release_dc (impl);
if (!DeleteObject (hrgn))
WIN32_GDI_FAILED ("DeleteObject");
if (!DeleteObject (area_hrgn))
WIN32_GDI_FAILED ("DeleteObject");
}
static void
gdk_win32_input_shape_combine_region (GdkWindow *window,
const cairo_region_t *shape_region,
@@ -3556,7 +3485,6 @@ gdk_window_impl_win32_class_init (GdkWindowImplWin32Class *klass)
impl_class->input_shape_combine_region = gdk_win32_input_shape_combine_region;
impl_class->set_static_gravities = gdk_win32_window_set_static_gravities;
impl_class->queue_antiexpose = _gdk_win32_window_queue_antiexpose;
impl_class->translate = _gdk_win32_window_translate;
impl_class->destroy = gdk_win32_window_destroy;
impl_class->destroy_foreign = gdk_win32_window_destroy_foreign;
impl_class->resize_cairo_surface = gdk_win32_window_resize_cairo_surface;

View File

@@ -28,26 +28,11 @@
typedef struct _GdkWindowQueueItem GdkWindowQueueItem;
typedef struct _GdkWindowParentPos GdkWindowParentPos;
typedef enum {
GDK_WINDOW_QUEUE_TRANSLATE,
GDK_WINDOW_QUEUE_ANTIEXPOSE
} GdkWindowQueueType;
struct _GdkWindowQueueItem
{
GdkWindow *window;
gulong serial;
GdkWindowQueueType type;
union {
struct {
cairo_region_t *area;
gint dx;
gint dy;
} translate;
struct {
cairo_region_t *area;
} antiexpose;
} u;
cairo_region_t *antiexpose_area;
};
void
@@ -140,14 +125,7 @@ queue_item_free (GdkWindowQueueItem *item)
(gpointer *)&(item->window));
}
if (item->type == GDK_WINDOW_QUEUE_ANTIEXPOSE)
cairo_region_destroy (item->u.antiexpose.area);
else
{
if (item->u.translate.area)
cairo_region_destroy (item->u.translate.area);
}
cairo_region_destroy (item->antiexpose_area);
g_free (item);
}
@@ -213,11 +191,8 @@ gdk_window_queue (GdkWindow *window,
GdkWindowQueueItem *item = tmp_list->data;
GList *next = tmp_list->next;
if (item->type == GDK_WINDOW_QUEUE_ANTIEXPOSE)
{
queue_delete_link (display_x11->translate_queue, tmp_list);
queue_item_free (item);
}
queue_delete_link (display_x11->translate_queue, tmp_list);
queue_item_free (item);
tmp_list = next;
}
@@ -232,86 +207,12 @@ gdk_window_queue (GdkWindow *window,
g_queue_push_tail (display_x11->translate_queue, item);
}
static GC
_get_scratch_gc (GdkWindow *window, cairo_region_t *clip_region)
{
GdkX11Screen *screen;
XRectangle *rectangles;
gint n_rects;
gint depth;
screen = GDK_X11_SCREEN (gdk_window_get_screen (window));
depth = gdk_visual_get_depth (gdk_window_get_visual (window)) - 1;
if (!screen->subwindow_gcs[depth])
{
XGCValues values;
values.graphics_exposures = True;
values.subwindow_mode = IncludeInferiors;
screen->subwindow_gcs[depth] = XCreateGC (screen->xdisplay,
GDK_WINDOW_XID (window),
GCSubwindowMode | GCGraphicsExposures,
&values);
}
_gdk_x11_region_get_xrectangles (clip_region,
0, 0,
&rectangles,
&n_rects);
XSetClipRectangles (screen->xdisplay,
screen->subwindow_gcs[depth],
0, 0,
rectangles, n_rects,
YXBanded);
g_free (rectangles);
return screen->subwindow_gcs[depth];
}
void
_gdk_x11_window_translate (GdkWindow *window,
cairo_region_t *area,
gint dx,
gint dy)
{
GdkWindowQueueItem *item;
GC xgc;
GdkRectangle extents;
cairo_region_get_extents (area, &extents);
xgc = _get_scratch_gc (window, area);
cairo_region_translate (area, -dx, -dy); /* Move to source region */
item = g_new (GdkWindowQueueItem, 1);
item->type = GDK_WINDOW_QUEUE_TRANSLATE;
item->u.translate.area = cairo_region_copy (area);
item->u.translate.dx = dx;
item->u.translate.dy = dy;
gdk_window_queue (window, item);
XCopyArea (GDK_WINDOW_XDISPLAY (window),
GDK_WINDOW_XID (window),
GDK_WINDOW_XID (window),
xgc,
extents.x - dx, extents.y - dy,
extents.width, extents.height,
extents.x, extents.y);
}
gboolean
_gdk_x11_window_queue_antiexpose (GdkWindow *window,
cairo_region_t *area)
{
GdkWindowQueueItem *item = g_new (GdkWindowQueueItem, 1);
item->type = GDK_WINDOW_QUEUE_ANTIEXPOSE;
item->u.antiexpose.area = area;
item->antiexpose_area = area;
gdk_window_queue (window, item);
@@ -339,28 +240,7 @@ _gdk_x11_window_process_expose (GdkWindow *window,
if (serial - item->serial > (gulong) G_MAXLONG)
{
if (item->window == window)
{
if (item->type == GDK_WINDOW_QUEUE_TRANSLATE)
{
if (item->u.translate.area)
{
cairo_region_t *intersection;
intersection = cairo_region_copy (invalidate_region);
cairo_region_intersect (intersection, item->u.translate.area);
cairo_region_subtract (invalidate_region, intersection);
cairo_region_translate (intersection, item->u.translate.dx, item->u.translate.dy);
cairo_region_union (invalidate_region, intersection);
cairo_region_destroy (intersection);
}
else
cairo_region_translate (invalidate_region, item->u.translate.dx, item->u.translate.dy);
}
else /* anti-expose */
{
cairo_region_subtract (invalidate_region, item->u.antiexpose.area);
}
}
cairo_region_subtract (invalidate_region, item->antiexpose_area);
}
else
{

View File

@@ -5334,7 +5334,6 @@ gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass)
impl_class->input_shape_combine_region = gdk_window_x11_input_shape_combine_region;
impl_class->set_static_gravities = gdk_window_x11_set_static_gravities;
impl_class->queue_antiexpose = _gdk_x11_window_queue_antiexpose;
impl_class->translate = _gdk_x11_window_translate;
impl_class->destroy = gdk_x11_window_destroy;
impl_class->destroy_foreign = gdk_x11_window_destroy_foreign;
impl_class->resize_cairo_surface = gdk_window_x11_resize_cairo_surface;

View File

@@ -519,6 +519,7 @@ gtk_private_h_sources = \
gtkprintoperation-private.h \
gtkprintutils.h \
gtkprivate.h \
gtkpixelcacheprivate.h \
gtkquery.h \
gtkrbtree.h \
gtkrecentchooserdefault.h \
@@ -800,6 +801,7 @@ gtk_base_c_sources = \
gtkprivate.c \
gtkprivatetypebuiltins.c \
gtkprogressbar.c \
gtkpixelcache.c \
gtkradioaction.c \
gtkradiobutton.c \
gtkradiomenuitem.c \

View File

@@ -3365,7 +3365,7 @@ gtk_container_propagate_draw (GtkContainer *container,
{
GdkEventExpose *event;
GtkAllocation allocation;
GdkWindow *window, *w;
GdkWindow *window, *w, *event_window, *child_in_window;
int x, y;
g_return_if_fail (GTK_IS_CONTAINER (container));
@@ -3374,13 +3374,26 @@ gtk_container_propagate_draw (GtkContainer *container,
g_assert (gtk_widget_get_parent (child) == GTK_WIDGET (container));
if (!gtk_widget_is_drawable (child))
return;
/* Only propagate to native child window if we're not handling
an expose (i.e. in a pure gtk_widget_draw() call */
event = _gtk_cairo_get_event (cr);
if (event)
{
if (gtk_widget_get_has_window (child) ||
gtk_widget_get_window (child) != event->window)
return;
}
if (event &&
(gtk_widget_get_has_window (child) &&
gdk_window_has_native (gtk_widget_get_window (child))))
return;
/* Never propagate to a child window when exposing a window
that is not the one the child widget is in. */
event_window = _gtk_cairo_get_event_window (cr);
if (gtk_widget_get_has_window (child))
child_in_window = gdk_window_get_parent (gtk_widget_get_window (child));
else
child_in_window = gtk_widget_get_window (child);
if (event_window != NULL && child_in_window != event_window)
return;
cairo_save (cr);
@@ -3422,7 +3435,7 @@ gtk_container_propagate_draw (GtkContainer *container,
cairo_translate (cr, x, y);
_gtk_widget_draw_internal (child, cr, TRUE);
_gtk_widget_draw (child, cr);
cairo_restore (cr);
}

View File

@@ -48,7 +48,8 @@ typedef enum {
GTK_DEBUG_BUILDER = 1 << 11,
GTK_DEBUG_SIZE_REQUEST = 1 << 12,
GTK_DEBUG_NO_CSS_CACHE = 1 << 13,
GTK_DEBUG_BASELINES = 1 << 14
GTK_DEBUG_BASELINES = 1 << 14,
GTK_DEBUG_PIXEL_CACHE = 1 << 15
} GtkDebugFlag;
#ifdef G_ENABLE_DEBUG

View File

@@ -172,7 +172,8 @@ static const GDebugKey gtk_debug_keys[] = {
{"builder", GTK_DEBUG_BUILDER},
{"size-request", GTK_DEBUG_SIZE_REQUEST},
{"no-css-cache", GTK_DEBUG_NO_CSS_CACHE},
{"baselines", GTK_DEBUG_BASELINES}
{"baselines", GTK_DEBUG_BASELINES},
{"pixel-cache", GTK_DEBUG_PIXEL_CACHE}
};
#endif /* G_ENABLE_DEBUG */
@@ -1615,9 +1616,17 @@ gtk_main_do_event (GdkEvent *event)
case GDK_EXPOSE:
if (event->any.window && gtk_widget_get_double_buffered (event_widget))
{
gdk_window_begin_paint_region (event->any.window, event->expose.region);
gtk_widget_send_expose (event_widget, event);
gdk_window_end_paint (event->any.window);
/* We handle exposes only on native windows, relying on the
* draw() handler to propagate down to non-native windows.
* This is ok now that we child windows always are considered
* (semi)transparent.
*/
if (gdk_window_has_native (event->expose.window))
{
gdk_window_begin_paint_region (event->any.window, event->expose.region);
gtk_widget_send_expose (event_widget, event);
gdk_window_end_paint (event->any.window);
}
}
else
{

362
gtk/gtkpixelcache.c Normal file
View File

@@ -0,0 +1,362 @@
/* GTK - The GIMP Toolkit
* Copyright (C) 2013 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkdebug.h"
#include "gtkpixelcacheprivate.h"
/* The extra size of the offscreen surface we allocate
to make scrolling more efficient */
#define EXTRA_SIZE 64
/* When resizing viewport to smaller we allow this extra
size to avoid constantly reallocating when resizing */
#define ALLOW_LARGER_SIZE 32
struct _GtkPixelCache {
cairo_surface_t *surface;
/* Valid if surface != NULL */
int surface_x;
int surface_y;
int surface_w;
int surface_h;
/* may be null if not dirty */
cairo_region_t *surface_dirty;
};
GtkPixelCache *
_gtk_pixel_cache_new ()
{
GtkPixelCache *cache;
cache = g_new0 (GtkPixelCache, 1);
return cache;
}
void
_gtk_pixel_cache_free (GtkPixelCache *cache)
{
if (cache == NULL)
return;
if (cache->surface != NULL)
cairo_surface_destroy (cache->surface);
if (cache->surface_dirty != NULL)
cairo_region_destroy (cache->surface_dirty);
g_free (cache);
}
/* Region is in canvas coordinates */
void
_gtk_pixel_cache_invalidate (GtkPixelCache *cache,
cairo_region_t *region)
{
cairo_rectangle_int_t r;
cairo_region_t *free_region = NULL;
if (cache->surface == NULL ||
(region != NULL && cairo_region_is_empty (region)))
return;
if (region == NULL)
{
r.x = cache->surface_x;
r.y = cache->surface_y;
r.width = cache->surface_w;
r.height = cache->surface_h;
free_region = region =
cairo_region_create_rectangle (&r);
}
if (cache->surface_dirty == NULL)
{
cache->surface_dirty = cairo_region_copy (region);
cairo_region_translate (cache->surface_dirty,
-cache->surface_x,
-cache->surface_y);
}
else
{
cairo_region_translate (region,
-cache->surface_x,
-cache->surface_y);
cairo_region_union (cache->surface_dirty, region);
cairo_region_translate (region,
cache->surface_x,
cache->surface_y);
}
if (free_region)
cairo_region_destroy (free_region);
r.x = 0;
r.y = 0;
r.width = cache->surface_w;
r.height = cache->surface_h;
cairo_region_intersect_rectangle (cache->surface_dirty, &r);
}
static void
_gtk_pixel_cache_create_surface_if_needed (GtkPixelCache *cache,
GdkWindow *window,
cairo_rectangle_int_t *view_rect,
cairo_rectangle_int_t *canvas_rect)
{
cairo_rectangle_int_t rect;
int surface_w, surface_h;
cairo_content_t content;
cairo_pattern_t *bg;
double red, green, blue, alpha;
content = CAIRO_CONTENT_COLOR_ALPHA;
bg = gdk_window_get_background_pattern (window);
if (bg != NULL &&
cairo_pattern_get_type (bg) == CAIRO_PATTERN_TYPE_SOLID &&
cairo_pattern_get_rgba (bg, &red, &green, &blue, &alpha) == CAIRO_STATUS_SUCCESS &&
alpha == 1.0)
content = CAIRO_CONTENT_COLOR;
surface_w = view_rect->width;
if (canvas_rect->width > surface_w)
surface_w = MIN (surface_w + EXTRA_SIZE, canvas_rect->width);
surface_h = view_rect->height;
if (canvas_rect->height > surface_h)
surface_h = MIN (surface_h + EXTRA_SIZE, canvas_rect->height);
/* If current surface can't fit view_rect or is too large, kill it */
if (cache->surface != NULL &&
(cairo_surface_get_content (cache->surface) != content ||
cache->surface_w < view_rect->width ||
cache->surface_w > surface_w + ALLOW_LARGER_SIZE ||
cache->surface_h < view_rect->height ||
cache->surface_h > surface_h + ALLOW_LARGER_SIZE))
{
cairo_surface_destroy (cache->surface);
cache->surface = NULL;
if (cache->surface_dirty)
cairo_region_destroy (cache->surface_dirty);
cache->surface_dirty = NULL;
}
/* Don't allocate a surface if view >= canvas, as we won't
be scrolling then anyway */
if (cache->surface == NULL &&
(view_rect->width < canvas_rect->width ||
view_rect->height < canvas_rect->height))
{
cache->surface_x = -canvas_rect->x;
cache->surface_y = -canvas_rect->y;
cache->surface_w = surface_w;
cache->surface_h = surface_h;
cache->surface =
gdk_window_create_similar_surface (window, content,
surface_w, surface_h);
rect.x = 0;
rect.y = 0;
rect.width = surface_w;
rect.height = surface_h;
cache->surface_dirty =
cairo_region_create_rectangle (&rect);
}
}
void
_gtk_pixel_cache_set_position (GtkPixelCache *cache,
cairo_rectangle_int_t *view_rect,
cairo_rectangle_int_t *canvas_rect)
{
cairo_rectangle_int_t r, view_pos;
cairo_region_t *copy_region;
int new_surf_x, new_surf_y;
cairo_t *backing_cr;
if (cache->surface == NULL)
return;
/* Position of view inside canvas */
view_pos.x = -canvas_rect->x;
view_pos.y = -canvas_rect->y;
view_pos.width = view_rect->width;
view_pos.height = view_rect->height;
/* Reposition so all is visible */
if (view_pos.x < cache->surface_x ||
view_pos.x + view_pos.width >
cache->surface_x + cache->surface_w ||
view_pos.y < cache->surface_y ||
view_pos.y + view_pos.height >
cache->surface_y + cache->surface_h)
{
new_surf_x = cache->surface_x;
if (view_pos.x < cache->surface_x)
new_surf_x = MAX (view_pos.x + view_pos.width - cache->surface_w, 0);
else if (view_pos.x + view_pos.width >
cache->surface_x + cache->surface_w)
new_surf_x = MIN (view_pos.x, canvas_rect->width - cache->surface_w);
new_surf_y = cache->surface_y;
if (view_pos.y < cache->surface_y)
new_surf_y = MAX (view_pos.y + view_pos.height - cache->surface_h, 0);
else if (view_pos.y + view_pos.height >
cache->surface_y + cache->surface_h)
new_surf_y = MIN (view_pos.y, canvas_rect->height - cache->surface_h);
r.x = 0;
r.y = 0;
r.width = cache->surface_w;
r.height = cache->surface_h;
copy_region = cairo_region_create_rectangle (&r);
if (cache->surface_dirty)
{
cairo_region_subtract (copy_region, cache->surface_dirty);
cairo_region_destroy (cache->surface_dirty);
cache->surface_dirty = NULL;
}
cairo_region_translate (copy_region,
cache->surface_x - new_surf_x,
cache->surface_y - new_surf_y);
cairo_region_intersect_rectangle (copy_region, &r);
backing_cr = cairo_create (cache->surface);
gdk_cairo_region (backing_cr, copy_region);
cairo_set_operator (backing_cr, CAIRO_OPERATOR_SOURCE);
cairo_clip (backing_cr);
cairo_push_group (backing_cr);
cairo_set_source_surface (backing_cr, cache->surface,
cache->surface_x - new_surf_x,
cache->surface_y - new_surf_y);
cairo_paint (backing_cr);
cairo_pop_group_to_source (backing_cr);
cairo_paint (backing_cr);
cairo_destroy (backing_cr);
cache->surface_x = new_surf_x;
cache->surface_y = new_surf_y;
cairo_region_xor_rectangle (copy_region, &r);
cache->surface_dirty = copy_region;
}
}
void
_gtk_pixel_cache_repaint (GtkPixelCache *cache,
GtkPixelCacheDrawFunc draw,
cairo_rectangle_int_t *view_rect,
cairo_rectangle_int_t *canvas_rect,
gpointer user_data)
{
cairo_t *backing_cr;
if (cache->surface &&
cache->surface_dirty &&
!cairo_region_is_empty (cache->surface_dirty))
{
backing_cr = cairo_create (cache->surface);
gdk_cairo_region (backing_cr, cache->surface_dirty);
cairo_clip (backing_cr);
cairo_translate (backing_cr,
-cache->surface_x - canvas_rect->x - view_rect->x,
-cache->surface_y - canvas_rect->y - view_rect->y);
cairo_set_source_rgba (backing_cr,
0.0, 0, 0, 0.0);
cairo_set_operator (backing_cr, CAIRO_OPERATOR_SOURCE);
cairo_paint (backing_cr);
cairo_set_operator (backing_cr, CAIRO_OPERATOR_OVER);
cairo_save (backing_cr);
draw (backing_cr, user_data);
cairo_restore (backing_cr);
#ifdef G_ENABLE_DEBUG
if (gtk_get_debug_flags () & GTK_DEBUG_PIXEL_CACHE)
{
GdkRGBA colors[] = {
{ 1, 0, 0, 0.08},
{ 0, 1, 0, 0.08},
{ 0, 0, 1, 0.08},
{ 1, 0, 1, 0.08},
{ 1, 1, 0, 0.08},
{ 0, 1, 1, 0.08},
};
static int current_color = 0;
gdk_cairo_set_source_rgba (backing_cr, &colors[(current_color++) % G_N_ELEMENTS (colors)]);
cairo_paint (backing_cr);
}
#endif
cairo_destroy (backing_cr);
}
if (cache->surface_dirty)
{
cairo_region_destroy (cache->surface_dirty);
cache->surface_dirty = NULL;
}
}
void
_gtk_pixel_cache_draw (GtkPixelCache *cache,
cairo_t *cr,
GdkWindow *window,
/* View position in widget coords */
cairo_rectangle_int_t *view_rect,
/* Size and position of canvas in view coords */
cairo_rectangle_int_t *canvas_rect,
GtkPixelCacheDrawFunc draw,
gpointer user_data)
{
_gtk_pixel_cache_create_surface_if_needed (cache, window,
view_rect, canvas_rect);
_gtk_pixel_cache_set_position (cache, view_rect, canvas_rect);
_gtk_pixel_cache_repaint (cache, draw, view_rect, canvas_rect, user_data);
if (cache->surface &&
/* Don't use backing surface if rendering elsewhere */
cairo_surface_get_type (cache->surface) == cairo_surface_get_type (cairo_get_target (cr)))
{
cairo_save (cr);
cairo_set_source_surface (cr, cache->surface,
cache->surface_x + view_rect->x + canvas_rect->x,
cache->surface_y + view_rect->y + canvas_rect->y);
cairo_rectangle (cr, view_rect->x, view_rect->y,
view_rect->width, view_rect->height);
cairo_fill (cr);
cairo_restore (cr);
}
else
{
cairo_rectangle (cr,
view_rect->x, view_rect->y,
view_rect->width, view_rect->height);
cairo_clip (cr);
draw (cr, user_data);
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright © 2013 Red Hat Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* 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, see <http://www.gnu.org/licenses/>.
*
* Authors: Alexander Larsson <alexl@gnome.org>
*/
#ifndef __GTK_PIXEL_CACHE_PRIVATE_H__
#define __GTK_PIXEL_CACHE_PRIVATE_H__
#include <glib-object.h>
#include <gtkwidget.h>
G_BEGIN_DECLS
typedef struct _GtkPixelCache GtkPixelCache;
typedef void (*GtkPixelCacheDrawFunc) (cairo_t *cr,
gpointer user_data);
GtkPixelCache *_gtk_pixel_cache_new (void);
void _gtk_pixel_cache_free (GtkPixelCache *cache);
void _gtk_pixel_cache_invalidate (GtkPixelCache *cache,
cairo_region_t *region);
void _gtk_pixel_cache_draw (GtkPixelCache *cache,
cairo_t *cr,
GdkWindow *window,
cairo_rectangle_int_t *view_rect,
cairo_rectangle_int_t *canvas_rect,
GtkPixelCacheDrawFunc draw,
gpointer user_data);
G_END_DECLS
#endif /* __GTK_PIXEL_CACHE_PRIVATE_H__ */

View File

@@ -53,6 +53,7 @@
#include "gtkmain.h"
#include "gtksettings.h"
#include "gtkwidgetpath.h"
#include "gtkpixelcacheprivate.h"
#include "a11y/gtktreeviewaccessibleprivate.h"
@@ -304,6 +305,8 @@ struct _GtkTreeViewPrivate
GdkWindow *bin_window;
GdkWindow *header_window;
GtkPixelCache *pixel_cache;
/* Scroll position state keeping */
GtkTreeRowReference *top_row;
gint top_row_dy;
@@ -492,6 +495,8 @@ struct _GtkTreeViewPrivate
guint in_grab : 1;
guint in_scroll : 1;
/* Whether our key press handler is to avoid sending an unhandled binding to the search entry */
guint search_entry_avoid_unhandled_binding : 1;
@@ -794,6 +799,8 @@ static void gtk_tree_view_stop_rubber_band (GtkTreeView
static void update_prelight (GtkTreeView *tree_view,
int x,
int y);
static void gtk_tree_view_queue_draw_region (GtkWidget *widget,
const cairo_region_t *region);
static inline gint gtk_tree_view_get_effective_header_height (GtkTreeView *tree_view);
@@ -960,6 +967,7 @@ gtk_tree_view_class_init (GtkTreeViewClass *class)
widget_class->style_updated = gtk_tree_view_style_updated;
widget_class->grab_notify = gtk_tree_view_grab_notify;
widget_class->state_flags_changed = gtk_tree_view_state_flags_changed;
widget_class->queue_draw_region = gtk_tree_view_queue_draw_region;
/* GtkContainer signals */
container_class->remove = gtk_tree_view_remove;
@@ -1730,6 +1738,8 @@ gtk_tree_view_init (GtkTreeView *tree_view)
tree_view->priv->headers_visible = TRUE;
tree_view->priv->activate_on_single_click = FALSE;
tree_view->priv->pixel_cache = _gtk_pixel_cache_new ();
/* We need some padding */
tree_view->priv->dy = 0;
tree_view->priv->cursor_offset = 0;
@@ -2130,6 +2140,10 @@ gtk_tree_view_destroy (GtkWidget *widget)
tree_view->priv->vadjustment = NULL;
}
if (tree_view->priv->pixel_cache)
_gtk_pixel_cache_free (tree_view->priv->pixel_cache);
tree_view->priv->pixel_cache = NULL;
GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->destroy (widget);
}
@@ -2217,6 +2231,47 @@ gtk_tree_view_ensure_background (GtkTreeView *tree_view)
gtk_style_context_set_background (context, tree_view->priv->header_window);
}
static void
gtk_tree_view_bin_window_invalidate_handler (GdkWindow *window,
cairo_region_t *region)
{
gpointer widget;
GtkTreeView *tree_view;
int y;
gdk_window_get_user_data (window, &widget);
tree_view = GTK_TREE_VIEW (widget);
/* Scrolling will invalidate everything in the bin window,
but we already have it in the cache, so we can ignore that */
if (tree_view->priv->in_scroll)
return;
y = gtk_adjustment_get_value (tree_view->priv->vadjustment);
cairo_region_translate (region,
0, y);
_gtk_pixel_cache_invalidate (tree_view->priv->pixel_cache, region);
cairo_region_translate (region,
0, -y);
}
static void
gtk_tree_view_queue_draw_region (GtkWidget *widget,
const cairo_region_t *region)
{
GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
/* There is no way we can know if a region targets the
not-currently-visible but in pixel cache region, so we
always just invalidate the whole thing whenever the
tree view gets a queue draw. This doesn't normally happen
in normal scrolling cases anyway. */
_gtk_pixel_cache_invalidate (tree_view->priv->pixel_cache, NULL);
GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->queue_draw_region (widget,
region);
}
static void
gtk_tree_view_realize (GtkWidget *widget)
{
@@ -2268,6 +2323,8 @@ gtk_tree_view_realize (GtkWidget *widget)
tree_view->priv->bin_window = gdk_window_new (window,
&attributes, attributes_mask);
gtk_widget_register_window (widget, tree_view->priv->bin_window);
gdk_window_set_invalidate_handler (tree_view->priv->bin_window,
gtk_tree_view_bin_window_invalidate_handler);
gtk_widget_get_allocation (widget, &allocation);
@@ -4769,8 +4826,6 @@ gtk_tree_view_bin_draw (GtkWidget *widget,
bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
bin_window_height = gdk_window_get_height (tree_view->priv->bin_window);
cairo_rectangle (cr, 0, 0, bin_window_width, bin_window_height);
cairo_clip (cr);
if (!gdk_cairo_get_clip_rectangle (cr, &clip))
return TRUE;
@@ -5368,6 +5423,35 @@ done:
return FALSE;
}
static void
draw_bin (cairo_t *cr,
gpointer user_data)
{
GtkWidget *widget = GTK_WIDGET (user_data);
GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
GList *tmp_list;
cairo_save (cr);
gtk_cairo_transform_to_window (cr, widget, tree_view->priv->bin_window);
gtk_tree_view_bin_draw (widget, cr);
cairo_restore (cr);
/* We can't just chain up to Container::draw as it will try to send the
* event to the headers, so we handle propagating it to our children
* (eg. widgets being edited) ourselves.
*/
tmp_list = tree_view->priv->children;
while (tmp_list)
{
GtkTreeViewChild *child = tmp_list->data;
tmp_list = tmp_list->next;
gtk_container_propagate_draw (GTK_CONTAINER (tree_view), child->widget, cr);
}
}
static gboolean
gtk_tree_view_draw (GtkWidget *widget,
cairo_t *cr)
@@ -5384,27 +5468,22 @@ gtk_tree_view_draw (GtkWidget *widget,
if (gtk_cairo_should_draw_window (cr, tree_view->priv->bin_window))
{
GList *tmp_list;
cairo_rectangle_int_t view_rect;
cairo_rectangle_int_t canvas_rect;
cairo_save (cr);
view_rect.x = 0;
view_rect.y = gtk_tree_view_get_effective_header_height (tree_view);
view_rect.width = gdk_window_get_width (tree_view->priv->bin_window);
view_rect.height = gdk_window_get_height (tree_view->priv->bin_window);
gtk_cairo_transform_to_window (cr, widget, tree_view->priv->bin_window);
gtk_tree_view_bin_draw (widget, cr);
gdk_window_get_position (tree_view->priv->bin_window, &canvas_rect.x, &canvas_rect.y);
canvas_rect.y = -gtk_adjustment_get_value (tree_view->priv->vadjustment);
canvas_rect.width = gdk_window_get_width (tree_view->priv->bin_window);
canvas_rect.height = tree_view->priv->height;
cairo_restore (cr);
/* We can't just chain up to Container::draw as it will try to send the
* event to the headers, so we handle propagating it to our children
* (eg. widgets being edited) ourselves.
*/
tmp_list = tree_view->priv->children;
while (tmp_list)
{
GtkTreeViewChild *child = tmp_list->data;
tmp_list = tmp_list->next;
gtk_container_propagate_draw (GTK_CONTAINER (tree_view), child->widget, cr);
}
_gtk_pixel_cache_draw (tree_view->priv->pixel_cache, cr, tree_view->priv->bin_window,
&view_rect, &canvas_rect,
draw_bin, widget);
}
gtk_style_context_save (context);
@@ -5974,18 +6053,17 @@ gtk_tree_view_node_queue_redraw (GtkTreeView *tree_view,
GtkRBTree *tree,
GtkRBNode *node)
{
GtkAllocation allocation;
gint y;
GdkRectangle rect;
y = _gtk_rbtree_node_find_offset (tree, node)
- gtk_adjustment_get_value (tree_view->priv->vadjustment)
+ gtk_tree_view_get_effective_header_height (tree_view);
rect.x = 0;
rect.y =
_gtk_rbtree_node_find_offset (tree, node)
- gtk_adjustment_get_value (tree_view->priv->vadjustment);
rect.width = gtk_widget_get_allocated_width (GTK_WIDGET (tree_view));
rect.height = GTK_RBNODE_GET_HEIGHT (node);
gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
0, y,
allocation.width,
GTK_RBNODE_GET_HEIGHT (node));
gdk_window_invalidate_rect (tree_view->priv->bin_window,
&rect, TRUE);
}
static gboolean
@@ -11236,7 +11314,9 @@ gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment,
}
}
}
tree_view->priv->in_scroll = TRUE;
gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
tree_view->priv->in_scroll = FALSE;
if (tree_view->priv->dy != (int) gtk_adjustment_get_value (tree_view->priv->vadjustment))
{

View File

@@ -32,6 +32,7 @@
#include "gtkprivate.h"
#include "gtkscrollable.h"
#include "gtktypebuiltins.h"
#include "gtkpixelcacheprivate.h"
/**
@@ -66,6 +67,8 @@ struct _GtkViewportPrivate
GdkWindow *bin_window;
GdkWindow *view_window;
GtkPixelCache *pixel_cache;
/* GtkScrollablePolicy needs to be checked when
* driving the scrollable adjustment values */
guint hscroll_policy : 1;
@@ -249,6 +252,8 @@ gtk_viewport_init (GtkViewport *viewport)
priv->hadjustment = NULL;
priv->vadjustment = NULL;
priv->pixel_cache = _gtk_pixel_cache_new ();
viewport_set_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL, NULL);
viewport_set_adjustment (viewport, GTK_ORIENTATION_VERTICAL, NULL);
}
@@ -301,10 +306,15 @@ static void
gtk_viewport_destroy (GtkWidget *widget)
{
GtkViewport *viewport = GTK_VIEWPORT (widget);
GtkViewportPrivate *priv = viewport->priv;
viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL);
viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_VERTICAL);
if (priv->pixel_cache)
_gtk_pixel_cache_free (priv->pixel_cache);
priv->pixel_cache = NULL;
GTK_WIDGET_CLASS (gtk_viewport_parent_class)->destroy (widget);
}
@@ -649,6 +659,21 @@ gtk_viewport_get_view_window (GtkViewport *viewport)
return viewport->priv->view_window;
}
static void
gtk_viewport_bin_window_invalidate_handler (GdkWindow *window,
cairo_region_t *region)
{
gpointer widget;
GtkViewport *viewport;
GtkViewportPrivate *priv;
gdk_window_get_user_data (window, &widget);
viewport = GTK_VIEWPORT (widget);
priv = viewport->priv;
_gtk_pixel_cache_invalidate (priv->pixel_cache, region);
}
static void
gtk_viewport_realize (GtkWidget *widget)
{
@@ -713,6 +738,8 @@ gtk_viewport_realize (GtkWidget *widget)
priv->bin_window = gdk_window_new (priv->view_window, &attributes, attributes_mask);
gtk_widget_register_window (widget, priv->bin_window);
gdk_window_set_invalidate_handler (priv->bin_window,
gtk_viewport_bin_window_invalidate_handler);
child = gtk_bin_get_child (bin);
if (child)
@@ -743,6 +770,25 @@ gtk_viewport_unrealize (GtkWidget *widget)
GTK_WIDGET_CLASS (gtk_viewport_parent_class)->unrealize (widget);
}
static void
draw_bin (cairo_t *cr,
gpointer user_data)
{
GtkWidget *widget = GTK_WIDGET (user_data);
GtkViewport *viewport = GTK_VIEWPORT (widget);
GtkViewportPrivate *priv = viewport->priv;
GtkStyleContext *context;
int x, y;
context = gtk_widget_get_style_context (widget);
gdk_window_get_position (priv->bin_window, &x, &y);
gtk_render_background (context, cr, x, y,
gdk_window_get_width (priv->bin_window),
gdk_window_get_height (priv->bin_window));
GTK_WIDGET_CLASS (gtk_viewport_parent_class)->draw (widget, cr);
}
static gint
gtk_viewport_draw (GtkWidget *widget,
cairo_t *cr)
@@ -750,7 +796,6 @@ gtk_viewport_draw (GtkWidget *widget,
GtkViewport *viewport = GTK_VIEWPORT (widget);
GtkViewportPrivate *priv = viewport->priv;
GtkStyleContext *context;
int x, y;
context = gtk_widget_get_style_context (widget);
@@ -766,30 +811,23 @@ gtk_viewport_draw (GtkWidget *widget,
gtk_style_context_restore (context);
}
if (gtk_cairo_should_draw_window (cr, priv->view_window))
{
/* This is a cute hack to ensure the contents of bin_window are
* restricted to where they are visible. We only need to do this
* clipping when called via gtk_widget_draw() and not in expose
* events. And when that happens every window (including this one)
* should be drawn.
*/
gdk_window_get_position (priv->view_window, &x, &y);
cairo_rectangle (cr, x, y,
gdk_window_get_width (priv->view_window),
gdk_window_get_height (priv->view_window));
cairo_clip (cr);
}
if (gtk_cairo_should_draw_window (cr, priv->bin_window))
{
gdk_window_get_position (priv->bin_window, &x, &y);
gtk_render_background (context, cr, x, y,
gdk_window_get_width (priv->bin_window),
gdk_window_get_height (priv->bin_window));
cairo_rectangle_int_t view_rect;
cairo_rectangle_int_t canvas_rect;
GTK_WIDGET_CLASS (gtk_viewport_parent_class)->draw (widget, cr);
gdk_window_get_position (priv->view_window, &view_rect.x, &view_rect.y);
view_rect.width = gdk_window_get_width (priv->view_window);
view_rect.height = gdk_window_get_height (priv->view_window);
gdk_window_get_position (priv->bin_window, &canvas_rect.x, &canvas_rect.y);
canvas_rect.width = gdk_window_get_width (priv->bin_window);
canvas_rect.height = gdk_window_get_height (priv->bin_window);
_gtk_pixel_cache_draw (priv->pixel_cache, cr, priv->bin_window,
&view_rect, &canvas_rect,
draw_bin, widget);
}
return FALSE;
@@ -843,7 +881,7 @@ gtk_viewport_size_allocate (GtkWidget *widget,
viewport_set_hadjustment_values (viewport);
viewport_set_vadjustment_values (viewport);
child_allocation.x = 0;
child_allocation.y = 0;
child_allocation.width = gtk_adjustment_get_upper (hadjustment);

View File

@@ -751,6 +751,8 @@ static void gtk_widget_real_get_width_for_height (GtkWidget
gint *natural_width);
static void gtk_widget_real_state_flags_changed (GtkWidget *widget,
GtkStateFlags old_state);
static void gtk_widget_real_queue_draw_region (GtkWidget *widget,
const cairo_region_t *region);
static const GtkWidgetAuxInfo* _gtk_widget_get_aux_info_or_defaults (GtkWidget *widget);
static GtkWidgetAuxInfo* gtk_widget_get_aux_info (GtkWidget *widget,
gboolean create);
@@ -854,9 +856,10 @@ static void gtk_widget_on_frame_clock_update (GdkFrameClock *frame_clock,
GtkWidget *widget);
static gboolean event_window_is_still_viewable (GdkEvent *event);
static void gtk_cairo_set_event_window (cairo_t *cr,
GdkWindow *window);
static void gtk_cairo_set_event (cairo_t *cr,
GdkEventExpose *event);
static void gtk_widget_propagate_alpha (GtkWidget *widget);
/* --- variables --- */
static gpointer gtk_widget_parent_class = NULL;
@@ -955,110 +958,6 @@ child_property_notify_dispatcher (GObject *object,
GTK_WIDGET_GET_CLASS (object)->dispatch_child_properties_changed (GTK_WIDGET (object), n_pspecs, pspecs);
}
/* We guard against the draw signal callbacks modifying the state of the
* cairo context by surounding it with save/restore.
* Maybe we should also cairo_new_path() just to be sure?
*/
static void
gtk_widget_draw_marshaller (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data)
{
GtkWidget *widget = g_value_get_object (&param_values[0]);
GdkEventExpose *tmp_event;
gboolean push_group;
cairo_t *cr = g_value_get_boxed (&param_values[1]);
cairo_save (cr);
tmp_event = _gtk_cairo_get_event (cr);
push_group =
widget->priv->opacity_group ||
(widget->priv->alpha != 255 &&
(!gtk_widget_get_has_window (widget) || tmp_event == NULL));
if (push_group)
{
cairo_push_group (cr);
gtk_cairo_set_event (cr, NULL);
}
_gtk_marshal_BOOLEAN__BOXED (closure,
return_value,
n_param_values,
param_values,
invocation_hint,
marshal_data);
if (push_group)
{
cairo_pop_group_to_source (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
cairo_paint_with_alpha (cr, widget->priv->alpha / 255.0);
}
gtk_cairo_set_event (cr, tmp_event);
cairo_restore (cr);
}
static void
gtk_widget_draw_marshallerv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types)
{
GtkWidget *widget = GTK_WIDGET (instance);
GdkEventExpose *tmp_event;
gboolean push_group;
cairo_t *cr;
va_list args_copy;
G_VA_COPY (args_copy, args);
cr = va_arg (args_copy, gpointer);
cairo_save (cr);
tmp_event = _gtk_cairo_get_event (cr);
push_group =
widget->priv->opacity_group ||
(widget->priv->alpha != 255 &&
(!gtk_widget_get_has_window (widget) || tmp_event == NULL));
if (push_group)
{
cairo_push_group (cr);
gtk_cairo_set_event (cr, NULL);
}
_gtk_marshal_BOOLEAN__BOXEDv (closure,
return_value,
instance,
args,
marshal_data,
n_params,
param_types);
if (push_group)
{
cairo_pop_group_to_source (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
cairo_paint_with_alpha (cr, widget->priv->alpha / 255.0);
}
gtk_cairo_set_event (cr, tmp_event);
cairo_restore (cr);
va_end (args_copy);
}
static void
gtk_widget_class_init (GtkWidgetClass *klass)
{
@@ -1181,6 +1080,7 @@ gtk_widget_class_init (GtkWidgetClass *klass)
klass->adjust_baseline_request = gtk_widget_real_adjust_baseline_request;
klass->adjust_size_allocation = gtk_widget_real_adjust_size_allocation;
klass->adjust_baseline_allocation = gtk_widget_real_adjust_baseline_allocation;
klass->queue_draw_region = gtk_widget_real_queue_draw_region;
g_object_class_install_property (gobject_class,
PROP_NAME,
@@ -1994,12 +1894,12 @@ gtk_widget_class_init (GtkWidgetClass *klass)
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkWidgetClass, draw),
_gtk_boolean_handled_accumulator, NULL,
gtk_widget_draw_marshaller,
_gtk_boolean_handled_accumulator, NULL,
_gtk_marshal_BOOLEAN__BOXED,
G_TYPE_BOOLEAN, 1,
CAIRO_GOBJECT_TYPE_CONTEXT);
CAIRO_GOBJECT_TYPE_CONTEXT | G_SIGNAL_TYPE_STATIC_SCOPE);
g_signal_set_va_marshaller (widget_signals[DRAW], G_TYPE_FROM_CLASS (klass),
gtk_widget_draw_marshallerv);
_gtk_marshal_BOOLEAN__BOXEDv);
/**
* GtkWidget::mnemonic-activate:
@@ -4235,8 +4135,6 @@ gtk_widget_unparent (GtkWidget *widget)
g_object_notify_queue_clear (G_OBJECT (widget), nqueue);
g_object_notify_queue_thaw (G_OBJECT (widget), nqueue);
gtk_widget_propagate_alpha (widget);
gtk_widget_pop_verify_invariants (widget);
g_object_unref (widget);
}
@@ -5066,6 +4964,15 @@ gtk_widget_unrealize (GtkWidget *widget)
* Draw queueing.
*****************************************/
static void
gtk_widget_real_queue_draw_region (GtkWidget *widget,
const cairo_region_t *region)
{
GtkWidgetPrivate *priv = widget->priv;
gdk_window_invalidate_region (priv->window, region, TRUE);
}
/**
* gtk_widget_queue_draw_region:
* @widget: a #GtkWidget
@@ -5103,7 +5010,7 @@ gtk_widget_queue_draw_region (GtkWidget *widget,
if (!gtk_widget_get_mapped (w))
return;
gdk_window_invalidate_region (priv->window, region, TRUE);
WIDGET_CLASS (widget)->queue_draw_region (widget, region);
}
/**
@@ -6314,6 +6221,23 @@ gtk_widget_real_mnemonic_activate (GtkWidget *widget,
return TRUE;
}
static const cairo_user_data_key_t event_window_key;
GdkWindow *
_gtk_cairo_get_event_window (cairo_t *cr)
{
g_return_val_if_fail (cr != NULL, NULL);
return cairo_get_user_data (cr, &event_window_key);
}
static void
gtk_cairo_set_event_window (cairo_t *cr,
GdkWindow *event_window)
{
cairo_set_user_data (cr, &event_window_key, event_window, NULL);
}
static const cairo_user_data_key_t event_key;
GdkEventExpose *
@@ -6326,7 +6250,7 @@ _gtk_cairo_get_event (cairo_t *cr)
static void
gtk_cairo_set_event (cairo_t *cr,
GdkEventExpose *event)
GdkEventExpose *event)
{
cairo_set_user_data (cr, &event_key, event, NULL);
}
@@ -6355,15 +6279,15 @@ gboolean
gtk_cairo_should_draw_window (cairo_t *cr,
GdkWindow *window)
{
GdkEventExpose *event;
GdkWindow *event_window;
g_return_val_if_fail (cr != NULL, FALSE);
g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
event = _gtk_cairo_get_event (cr);
event_window = _gtk_cairo_get_event_window (cr);
return event == NULL ||
event->window == window;
return event_window == NULL ||
event_window == window;
}
static gboolean
@@ -6380,17 +6304,20 @@ gtk_widget_get_clip_draw (GtkWidget *widget)
return TRUE;
}
/* code shared by gtk_container_propagate_draw() and
* gtk_widget_draw()
*/
void
static void
_gtk_widget_draw_internal (GtkWidget *widget,
cairo_t *cr,
gboolean clip_to_size)
gboolean clip_to_size,
GdkWindow *window)
{
GdkWindow *tmp_event_window;
if (!gtk_widget_is_drawable (widget))
return;
tmp_event_window = _gtk_cairo_get_event_window (cr);
gtk_cairo_set_event_window (cr, window);
clip_to_size &= gtk_widget_get_clip_draw (widget);
if (clip_to_size)
@@ -6406,10 +6333,14 @@ _gtk_widget_draw_internal (GtkWidget *widget,
{
gboolean result;
cairo_save (cr);
g_signal_emit (widget, widget_signals[DRAW],
0, cr,
&result);
cairo_restore (cr);
#ifdef G_ENABLE_DEBUG
if (G_UNLIKELY (gtk_get_debug_flags () & GTK_DEBUG_BASELINES))
{
@@ -6431,7 +6362,7 @@ _gtk_widget_draw_internal (GtkWidget *widget,
#endif
if (cairo_status (cr) &&
_gtk_cairo_get_event (cr))
_gtk_cairo_get_event_window (cr))
{
/* We check the event so we only warn about internal GTK calls.
* Errors might come from PDF streams having write failures and
@@ -6443,8 +6374,174 @@ _gtk_widget_draw_internal (GtkWidget *widget,
cairo_status_to_string (cairo_status (cr)));
}
}
gtk_cairo_set_event_window (cr, tmp_event_window);
}
/* Emit draw() on the widget that owns window,
and on any child windows that also belong
to the widget. */
static void
_gtk_widget_draw_windows (GdkWindow *window,
cairo_t *cr,
int window_x,
int window_y)
{
cairo_pattern_t *pattern;
gboolean do_clip;
GtkWidget *widget = NULL;
GList *l;
int x, y;
if (!gdk_window_is_viewable (window))
return;
cairo_save (cr);
cairo_translate (cr, window_x, window_y);
cairo_rectangle (cr, 0, 0,
gdk_window_get_width (window),
gdk_window_get_height (window));
cairo_clip (cr);
if (gdk_cairo_get_clip_rectangle (cr, NULL))
{
gdk_window_get_user_data (window, (gpointer *) &widget);
/* Only clear bg if double bufferer. This is what we used
to do before, where begin_paint() did the clearing. */
pattern = gdk_window_get_background_pattern (window);
if (pattern != NULL &&
widget->priv->double_buffered)
{
cairo_save (cr);
cairo_set_source (cr, pattern);
cairo_paint (cr);
cairo_restore (cr);
}
do_clip = _gtk_widget_get_translation_to_window (widget, window,
&x, &y);
cairo_save (cr);
cairo_translate (cr, -x, -y);
_gtk_widget_draw_internal (widget, cr, do_clip, window);
cairo_restore (cr);
for (l = g_list_last (gdk_window_peek_children (window));
l != NULL;
l = l->prev)
{
GdkWindow *child_window = l->data;
gpointer child_data;
GdkWindowType type;
int wx, wy;
type = gdk_window_get_window_type (child_window);
if (!gdk_window_is_visible (child_window) ||
gdk_window_is_input_only (child_window) ||
type == GDK_WINDOW_OFFSCREEN ||
type == GDK_WINDOW_FOREIGN)
continue;
gdk_window_get_user_data (child_window, &child_data);
if (child_data == (gpointer)widget)
{
gdk_window_get_position (child_window, &wx, &wy);
_gtk_widget_draw_windows (child_window, cr, wx,wy);
}
}
}
cairo_restore (cr);
}
void
_gtk_widget_draw (GtkWidget *widget,
cairo_t *cr)
{
GdkWindow *window, *child_window;
gpointer child_data;
GList *l;
int wx, wy;
gboolean push_group;
GdkWindowType type;
/* We get expose events only on native windows, so the draw
* implementation has to walk the entire widget hierarchy, except
* that it stops at native subwindows while we're in an expose
* event (_gtk_cairo_get_event () != NULL).
*
* However, we need to properly clip drawing into child windows
* to avoid drawing outside if widgets use e.g. cairo_paint(), so
* we traverse over GdkWindows as well as GtkWidgets.
*
* In order to be able to have opacity groups for entire widgets
* that consists of multiple windows we collect all the windows
* that belongs to a widget and draw them in one go. This means
* we may somewhat reorder GdkWindows when we paint them, but
* thats not generally a problem, as if you want a guaranteed
* order you generally use a windowed widget where you control
* the window hierarchy.
*/
cairo_save (cr);
push_group =
widget->priv->opacity_group ||
(widget->priv->alpha != 255 &&
!gtk_widget_is_toplevel (widget));
if (push_group)
cairo_push_group (cr);
window = gtk_widget_get_window (widget);
if (gtk_widget_get_has_window (widget))
{
/* The widget will be completely contained in its window, so just
* expose that (and any child window belonging to the widget) */
_gtk_widget_draw_windows (window, cr, 0, 0);
}
else
{
/* The widget draws in its parent window, so we send a draw() for
* that. */
_gtk_widget_draw_internal (widget, cr, TRUE, window);
/* But, it may also have child windows in the parent which we should
* draw (after having drawn on the parent) */
for (l = g_list_last (gdk_window_peek_children (window));
l != NULL;
l = l->prev)
{
child_window = l->data;
type = gdk_window_get_window_type (child_window);
if (!gdk_window_is_visible (child_window) ||
gdk_window_is_input_only (child_window) ||
type == GDK_WINDOW_OFFSCREEN ||
type == GDK_WINDOW_FOREIGN)
continue;
gdk_window_get_user_data (child_window, &child_data);
if (child_data == (gpointer)widget)
{
gdk_window_get_position (child_window, &wx, &wy);
_gtk_widget_draw_windows (child_window, cr,
wx - widget->priv->allocation.x,
wy - widget->priv->allocation.y);
}
}
}
if (push_group)
{
cairo_pop_group_to_source (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
cairo_paint_with_alpha (cr, widget->priv->alpha / 255.0);
}
cairo_restore (cr);
}
/**
* gtk_widget_draw:
* @widget: the widget to draw. It must be drawable (see
@@ -6475,24 +6572,11 @@ void
gtk_widget_draw (GtkWidget *widget,
cairo_t *cr)
{
GdkEventExpose *tmp_event;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (!widget->priv->alloc_needed);
g_return_if_fail (cr != NULL);
cairo_save (cr);
/* We have to reset the event here so that draw functions can call
* gtk_widget_draw() on random other widgets and get the desired
* effect: Drawing all contents, not just the current window.
*/
tmp_event = _gtk_cairo_get_event (cr);
gtk_cairo_set_event (cr, NULL);
_gtk_widget_draw_internal (widget, cr, TRUE);
gtk_cairo_set_event (cr, tmp_event);
cairo_restore (cr);
_gtk_widget_draw (widget, cr);
}
static gboolean
@@ -6787,8 +6871,6 @@ gtk_widget_send_expose (GtkWidget *widget,
{
gboolean result = FALSE;
cairo_t *cr;
int x, y;
gboolean do_clip;
g_return_val_if_fail (GTK_IS_WIDGET (widget), TRUE);
g_return_val_if_fail (gtk_widget_get_realized (widget), TRUE);
@@ -6796,21 +6878,18 @@ gtk_widget_send_expose (GtkWidget *widget,
g_return_val_if_fail (event->type == GDK_EXPOSE, TRUE);
cr = gdk_cairo_create (event->expose.window);
gtk_cairo_set_event (cr, &event->expose);
gdk_cairo_region (cr, event->expose.region);
cairo_clip (cr);
do_clip = _gtk_widget_get_translation_to_window (widget,
event->expose.window,
&x, &y);
cairo_translate (cr, -x, -y);
gtk_cairo_set_event (cr, &event->expose);
_gtk_widget_draw_internal (widget, cr, do_clip);
if (event->expose.window == widget->priv->window)
_gtk_widget_draw (widget, cr);
else
_gtk_widget_draw_windows (event->expose.window, cr, 0, 0);
/* unset here, so if someone keeps a reference to cr we
* don't leak the window. */
gtk_cairo_set_event (cr, NULL);
cairo_destroy (cr);
return result;
@@ -8492,6 +8571,9 @@ gtk_widget_get_app_paintable (GtkWidget *widget)
* expose events, since even the clearing to the background color or
* pixmap will not happen automatically (as it is done in
* gdk_window_begin_paint_region()).
*
* Since 3.10 this function only works for widgets with native
* windows.
**/
void
gtk_widget_set_double_buffered (GtkWidget *widget,
@@ -8750,8 +8832,6 @@ gtk_widget_set_parent (GtkWidget *widget,
gtk_widget_queue_compute_expand (parent);
}
gtk_widget_propagate_alpha (widget);
gtk_widget_pop_verify_invariants (widget);
}
@@ -14588,10 +14668,6 @@ gtk_widget_set_window (GtkWidget *widget,
{
priv->window = window;
if (gtk_widget_get_has_window (widget) && window != NULL && !gdk_window_has_native (window))
gdk_window_set_opacity (window,
priv->norender ? 0 : priv->alpha / 255.0);
g_object_notify (G_OBJECT (widget), "window");
}
}
@@ -14730,86 +14806,8 @@ gtk_widget_set_support_multidevice (GtkWidget *widget,
* in gtk_widget_set_opacity, secondly we can get it from the css opacity. These two
* are multiplied together to form the total alpha. Secondly, the user can specify
* an opacity group for a widget, which means we must essentially handle it as having alpha.
*
* We handle opacity in two ways. For a windowed widget, with opacity set but no opacity
* group we directly set the opacity of widget->window. This will cause gdk to properly
* redirect drawing inside the window to a buffer and do OVER paint_with_alpha.
*
* However, if the widget is not windowed, or the user specified an opacity group for the
* widget we do the opacity handling in the ::draw marshaller for the widget. A naive
* implementation of this would break for windowed widgets or descendant widgets with
* windows, as these would not be handled by the ::draw signal. To handle this we set
* all such gdkwindows as fully transparent and then override gtk_cairo_should_draw_window()
* to make the draw signal propagate to *all* child widgets/windows.
*
* Note: We don't make all child windows fully transparent, we stop at the first one
* in each branch when propagating down the hierarchy.
*/
/* This is called when priv->alpha or priv->opacity_group group changes, and should
* update priv->norender and GdkWindow opacity for this widget and any children that
* needs changing. It is also called whenver the parent changes, the parents
* norender_children state changes, or the has_window state of the widget changes.
*/
static void
gtk_widget_propagate_alpha (GtkWidget *widget)
{
GtkWidgetPrivate *priv = widget->priv;
GtkWidget *parent;
gboolean norender, norender_children;
GList *l;
parent = priv->parent;
/* Norender affects only windowed widget and means don't render widget->window in the
normal fashion.
We only set this if the parent has norender_children, because:
a) For an opacity group (that does not have a norender_children parent) we still
need to render the window or we will never get an expose event.
b) For alpha we set the opacity of window->widget directly, so no other
work is needed.
*/
norender = (parent != NULL && parent->priv->norender_children);
/* windows under this widget should not render if:
a) This widget has an opacity group
b) This widget has alpha and is no-windowed (otherwise we'd set alpha on widget->window)
c) This widget has norender but is no-windowed (a windowed widget would "swallow" the norender)
*/
norender_children =
priv->opacity_group ||
(!gtk_widget_get_has_window (widget) &&
( norender || priv->alpha != 255));
if (gtk_widget_get_has_window (widget))
{
if (priv->window != NULL &&
(!gdk_window_has_native (priv->window) || gtk_widget_is_toplevel (widget)))
gdk_window_set_opacity (priv->window,
norender ? 0 : priv->alpha / 255.0);
}
for (l = priv->registered_windows; l != NULL; l = l->next)
{
GdkWindow *w = l->data;
if (w != priv->window && !gdk_window_has_native (w))
gdk_window_set_opacity (w, norender_children ? 0.0 : 1.0);
}
priv->norender = norender;
if (priv->norender_children != norender_children)
{
priv->norender_children = norender_children;
if (GTK_IS_CONTAINER (widget))
gtk_container_forall (GTK_CONTAINER (widget), (GtkCallback)gtk_widget_propagate_alpha, NULL);
}
if (gtk_widget_get_realized (widget))
gtk_widget_queue_draw (widget);
}
static void
gtk_widget_update_alpha (GtkWidget *widget)
{
@@ -14836,8 +14834,12 @@ gtk_widget_update_alpha (GtkWidget *widget)
priv->alpha = alpha;
gtk_widget_propagate_alpha (widget);
if (gtk_widget_is_toplevel (widget))
gdk_window_set_opacity (priv->window,
priv->alpha / 255.0);
if (gtk_widget_get_realized (widget))
gtk_widget_queue_draw (widget);
}
static void
@@ -14857,7 +14859,8 @@ gtk_widget_set_has_opacity_group (GtkWidget *widget,
priv->opacity_group = has_opacity_group;
gtk_widget_propagate_alpha (widget);
if (gtk_widget_get_realized (widget))
gtk_widget_queue_draw (widget);
}
/**

View File

@@ -444,13 +444,14 @@ struct _GtkWidgetClass
gint *natural_baseline);
void (* adjust_baseline_allocation) (GtkWidget *widget,
gint *baseline);
void (*queue_draw_region) (GtkWidget *widget,
const cairo_region_t *region);
/*< private >*/
GtkWidgetClassPrivate *priv;
/* Padding for future expansion */
void (*_gtk_reserved5) (void);
void (*_gtk_reserved6) (void);
void (*_gtk_reserved7) (void);
};

View File

@@ -45,6 +45,8 @@ void _gtk_widget_set_shadowed (GtkWidget *widget,
gboolean _gtk_widget_get_alloc_needed (GtkWidget *widget);
void _gtk_widget_set_alloc_needed (GtkWidget *widget,
gboolean alloc_needed);
void _gtk_widget_draw (GtkWidget *widget,
cairo_t *cr);
void _gtk_widget_add_sizegroup (GtkWidget *widget,
gpointer group);
@@ -89,11 +91,9 @@ const gchar* _gtk_widget_get_accel_path (GtkWidget *widget,
AtkObject * _gtk_widget_peek_accessible (GtkWidget *widget);
GdkWindow * _gtk_cairo_get_event_window (cairo_t *cr);
GdkEventExpose * _gtk_cairo_get_event (cairo_t *cr);
void _gtk_widget_draw_internal (GtkWidget *widget,
cairo_t *cr,
gboolean clip_to_size);
void _gtk_widget_set_has_default (GtkWidget *widget,
gboolean has_default);
void _gtk_widget_set_has_grab (GtkWidget *widget,