Commit 580ea227 authored by Benjamin Otte's avatar Benjamin Otte

widget: Redo drawing code

Previously, we had a special cae to draw subwindows of widgets.

This is not necessary as conformant widgets should be able to properly
render themselves when all windows need to be painted.
From now on assume that is the case.

We therefore paint nonnative GDK windows "inline" by just returning TRUE
for gtk_cairo_should_draw_window() for those windows.

This speeds up hilighting different rows in the listbox gtk-demo example
tremendously (by a factor of 10 or more) as the previous code was
O(<number of non-window subwidgets> *
<number of subwindows>) which in the listbox example were ~15,000 and
~2,000 respectively.
parent 402cecf9
......@@ -388,9 +388,6 @@ static void gtk_container_buildable_custom_finished (GtkBuildable *buildable,
static gboolean gtk_container_should_propagate_draw (GtkContainer *container,
GtkWidget *child,
cairo_t *cr);
static void gtk_container_propagate_draw_internal (GtkContainer *container,
GtkWidget *child,
cairo_t *cr);
/* --- variables --- */
static GQuark vadjustment_key_id;
......@@ -3615,7 +3612,7 @@ gtk_container_draw (GtkWidget *widget,
for (i = 0; i < child_infos->len; i++)
{
child_info = &g_array_index (child_infos, ChildOrderInfo, i);
gtk_container_propagate_draw_internal (container, child_info->child, cr);
gtk_container_propagate_draw (container, child_info->child, cr);
}
g_array_free (child_infos, TRUE);
......@@ -3669,88 +3666,24 @@ gtk_container_should_propagate_draw (GtkContainer *container,
GtkWidget *child,
cairo_t *cr)
{
GdkEventExpose *event;
GdkWindow *event_window, *child_in_window;
GdkWindow *child_in_window;
if (!_gtk_widget_is_drawable (child))
return FALSE;
/* 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 &&
_gtk_widget_get_has_window (child) &&
gdk_window_has_native (_gtk_widget_get_window (child)))
return FALSE;
/* 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)
if (!gtk_cairo_should_draw_window (cr, child_in_window))
return FALSE;
return TRUE;
}
static void
gtk_container_propagate_draw_internal (GtkContainer *container,
GtkWidget *child,
cairo_t *cr)
{
GtkAllocation allocation;
GdkWindow *window, *w;
int x, y;
/* translate coordinates. Ugly business, that. */
if (!_gtk_widget_get_has_window (GTK_WIDGET (container)))
{
_gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
x = -allocation.x;
y = -allocation.y;
}
else
{
x = 0;
y = 0;
}
window = _gtk_widget_get_window (GTK_WIDGET (container));
for (w = _gtk_widget_get_window (child); w && w != window; w = gdk_window_get_parent (w))
{
int wx, wy;
gdk_window_get_position (w, &wx, &wy);
x += wx;
y += wy;
}
if (w == NULL)
{
x = 0;
y = 0;
}
if (!_gtk_widget_get_has_window (child))
{
_gtk_widget_get_allocation (child, &allocation);
x += allocation.x;
y += allocation.y;
}
cairo_save (cr);
cairo_translate (cr, x, y);
_gtk_widget_draw (child, cr);
cairo_restore (cr);
}
static void
union_with_clip (GtkWidget *widget,
gpointer clip)
......@@ -3804,6 +3737,10 @@ gtk_container_propagate_draw (GtkContainer *container,
GtkWidget *child,
cairo_t *cr)
{
GtkAllocation allocation;
GdkWindow *window, *w;
int x, y;
g_return_if_fail (GTK_IS_CONTAINER (container));
g_return_if_fail (GTK_IS_WIDGET (child));
g_return_if_fail (cr != NULL);
......@@ -3813,7 +3750,48 @@ gtk_container_propagate_draw (GtkContainer *container,
if (!gtk_container_should_propagate_draw (container, child, cr))
return;
gtk_container_propagate_draw_internal (container, child, cr);
/* translate coordinates. Ugly business, that. */
if (!_gtk_widget_get_has_window (GTK_WIDGET (container)))
{
_gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
x = -allocation.x;
y = -allocation.y;
}
else
{
x = 0;
y = 0;
}
window = _gtk_widget_get_window (GTK_WIDGET (container));
for (w = _gtk_widget_get_window (child); w && w != window; w = gdk_window_get_parent (w))
{
int wx, wy;
gdk_window_get_position (w, &wx, &wy);
x += wx;
y += wy;
}
if (w == NULL)
{
x = 0;
y = 0;
}
if (!_gtk_widget_get_has_window (child))
{
_gtk_widget_get_allocation (child, &allocation);
x += allocation.x;
y += allocation.y;
}
cairo_save (cr);
cairo_translate (cr, x, y);
gtk_widget_draw_internal (child, cr, TRUE);
cairo_restore (cr);
}
gboolean
......
......@@ -821,10 +821,6 @@ 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_update_input_shape (GtkWidget *widget);
......@@ -6887,8 +6883,8 @@ gtk_widget_real_mnemonic_activate (GtkWidget *widget,
static const cairo_user_data_key_t event_window_key;
GdkWindow *
_gtk_cairo_get_event_window (cairo_t *cr)
static GdkWindow *
gtk_cairo_get_event_window (cairo_t *cr)
{
g_return_val_if_fail (cr != NULL, NULL);
......@@ -6902,23 +6898,6 @@ gtk_cairo_set_event_window (cairo_t *cr,
cairo_set_user_data (cr, &event_window_key, event_window, NULL);
}
static const cairo_user_data_key_t event_key;
GdkEventExpose *
_gtk_cairo_get_event (cairo_t *cr)
{
g_return_val_if_fail (cr != NULL, NULL);
return cairo_get_user_data (cr, &event_key);
}
static void
gtk_cairo_set_event (cairo_t *cr,
GdkEventExpose *event)
{
cairo_set_user_data (cr, &event_key, event, NULL);
}
/**
* gtk_cairo_should_draw_window:
* @cr: a cairo context
......@@ -6948,26 +6927,23 @@ gtk_cairo_should_draw_window (cairo_t *cr,
g_return_val_if_fail (cr != NULL, FALSE);
g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
event_window = _gtk_cairo_get_event_window (cr);
if (!gdk_window_has_native (window))
return TRUE;
event_window = gtk_cairo_get_event_window (cr);
return event_window == NULL ||
event_window == window;
}
static void
_gtk_widget_draw_internal (GtkWidget *widget,
cairo_t *cr,
gboolean clip_to_size,
GdkWindow *window)
void
gtk_widget_draw_internal (GtkWidget *widget,
cairo_t *cr,
gboolean clip_to_size)
{
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);
if (clip_to_size)
{
cairo_rectangle (cr,
......@@ -6982,7 +6958,7 @@ _gtk_widget_draw_internal (GtkWidget *widget,
{
gboolean result;
gdk_window_mark_paint_from_clip (window, cr);
//gdk_window_mark_paint_from_clip (window, cr);
if (g_signal_has_handler_pending (widget, widget_signals[DRAW], 0, FALSE))
{
......@@ -7034,7 +7010,7 @@ _gtk_widget_draw_internal (GtkWidget *widget,
#endif
if (cairo_status (cr) &&
_gtk_cairo_get_event_window (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
......@@ -7046,112 +7022,13 @@ _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 *children, *l;
GdkRectangle current_clip, window_clip;
int x, y;
if (!gdk_window_is_viewable (window))
return;
window_clip.x = window_x;
window_clip.y = window_y;
window_clip.width = gdk_window_get_width (window);
window_clip.height = gdk_window_get_height (window);
/* Cairo paths are fixed point 24.8, but GDK supports 32-bit window
* sizes, so we can't feed window_clip to e.g. cairo_rectangle()
* directly. Instead, we pre-clip the window clip to the existing
* clip regions in full 32-bit precision and feed that to cairo.
*/
if (!gdk_cairo_get_clip_rectangle (cr, &current_clip) ||
!gdk_rectangle_intersect (&window_clip, &current_clip, &window_clip))
return;
cairo_save (cr);
cairo_rectangle (cr,
window_clip.x, window_clip.y,
window_clip.width, window_clip.height);
cairo_clip (cr);
cairo_translate (cr, window_x, window_y);
if (gdk_cairo_get_clip_rectangle (cr, NULL))
{
gdk_window_get_user_data (window, (gpointer *) &widget);
/* Only clear bg if double buffered. 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);
children = gdk_window_peek_children (window);
for (l = g_list_last (children); l != NULL; l = l->prev)
{
GdkWindow *child_window = l->data;
GdkWindowType type;
int wx, wy;
GtkWidget *window_widget;
gdk_window_get_user_data (child_window, (gpointer *)&window_widget);
if (window_widget != widget)
continue;
if (!gdk_window_is_visible (child_window) ||
gdk_window_is_input_only (child_window))
continue;
type = gdk_window_get_window_type (child_window);
if (type == GDK_WINDOW_OFFSCREEN ||
type == GDK_WINDOW_FOREIGN)
continue;
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;
GList *children, *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
......@@ -7179,48 +7056,7 @@ _gtk_widget_draw (GtkWidget *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)
*/
children = gdk_window_peek_children (window);
for (l = g_list_last (children); l != NULL; l = l->prev)
{
GdkWindow *child_window = l->data;
GtkWidget *window_widget;
gdk_window_get_user_data (child_window, (gpointer *)&window_widget);
if (window_widget != widget)
continue;
if (!gdk_window_is_visible (child_window) ||
gdk_window_is_input_only (child_window))
continue;
type = gdk_window_get_window_type (child_window);
if (type == GDK_WINDOW_OFFSCREEN ||
type == GDK_WINDOW_FOREIGN)
continue;
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);
}
}
gtk_widget_draw_internal (widget, cr, TRUE);
if (push_group)
{
......@@ -7261,14 +7097,26 @@ void
gtk_widget_draw (GtkWidget *widget,
cairo_t *cr)
{
GdkWindow *tmp_event_window;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (!widget->priv->alloc_needed);
g_return_if_fail (!widget->priv->alloc_needed_on_child);
g_return_if_fail (cr != NULL);
cairo_save (cr);
_gtk_widget_draw (widget, 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_window = gtk_cairo_get_event_window (cr);
gtk_cairo_set_event_window (cr, NULL);
gtk_widget_draw_internal (widget, cr, TRUE);
cairo_restore (cr);
gtk_cairo_set_event_window (cr, tmp_event_window);
}
static gboolean
......@@ -7678,30 +7526,34 @@ gint
gtk_widget_send_expose (GtkWidget *widget,
GdkEvent *event)
{
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);
g_return_val_if_fail (gtk_widget_get_realized (widget), TRUE);
g_return_val_if_fail (event != NULL, TRUE);
g_return_val_if_fail (event->type == GDK_EXPOSE, TRUE);
cr = gdk_cairo_create (event->expose.window);
gtk_cairo_set_event_window (cr, event->expose.window);
gdk_cairo_region (cr, event->expose.region);
cairo_clip (cr);
gtk_cairo_set_event (cr, &event->expose);
if (event->expose.window == widget->priv->window)
_gtk_widget_draw (widget, cr);
else
_gtk_widget_draw_windows (event->expose.window, cr, 0, 0);
do_clip = _gtk_widget_get_translation_to_window (widget,
event->expose.window,
&x, &y);
cairo_translate (cr, -x, -y);
gtk_cairo_set_event (cr, NULL);
gtk_widget_draw_internal (widget, cr, do_clip);
/* unset here, so if someone keeps a reference to cr we
* don't leak the window. */
gtk_cairo_set_event_window (cr, NULL);
cairo_destroy (cr);
return result;
return FALSE;
}
static gboolean
......
......@@ -176,8 +176,9 @@ gboolean gtk_widget_needs_allocate (GtkWidget *widget);
void gtk_widget_queue_resize_on_widget (GtkWidget *widget);
void gtk_widget_ensure_resize (GtkWidget *widget);
void gtk_widget_ensure_allocate (GtkWidget *widget);
void _gtk_widget_draw (GtkWidget *widget,
cairo_t *cr);
void gtk_widget_draw_internal (GtkWidget *widget,
cairo_t *cr,
gboolean do_clip);
void _gtk_widget_scale_changed (GtkWidget *widget);
......@@ -216,9 +217,6 @@ 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_set_has_default (GtkWidget *widget,
gboolean has_default);
void _gtk_widget_set_has_grab (GtkWidget *widget,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment