[gtk+] widget: Redo drawing code



commit 580ea227a6bb19ad6c6d4766b3a36dbad24583f3
Author: Benjamin Otte <otte redhat com>
Date:   Tue Jan 19 02:42:58 2016 +0100

    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.

 gtk/gtkcontainer.c     |  120 ++++++++++---------------
 gtk/gtkwidget.c        |  228 +++++++++---------------------------------------
 gtk/gtkwidgetprivate.h |    8 +-
 3 files changed, 92 insertions(+), 264 deletions(-)
---
diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c
index 3b2d922..7962208 100644
--- a/gtk/gtkcontainer.c
+++ b/gtk/gtkcontainer.c
@@ -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,89 +3666,25 @@ 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
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 75935bc..3a9e5d2 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -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
diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h
index 18bc980..a3402f3 100644
--- a/gtk/gtkwidgetprivate.h
+++ b/gtk/gtkwidgetprivate.h
@@ -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,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]