[gtk+] Verify GtkWidget invariants if G_ENABLE_DEBUG is defined



commit 23ce44c9fecefb9c243c09709c8c1decfd1eb530
Author: Havoc Pennington <hp pobox com>
Date:   Mon Dec 20 12:46:51 2010 -0500

    Verify GtkWidget invariants if G_ENABLE_DEBUG is defined
    
    These checks are a bit expensive so require --enable-debug=yes.
    gtk_widget_verify_invariants() checks invariants mentioned
    in docs/widget_system.txt in particular, and can verify
    others in the future.
    
    Some of the invariants in docs/widget_system.txt don't
    in fact hold right now, so those are #if 0'd in this
    patch pending someone fixing either the docs or the code.

 gtk/gtkwidget.c |  234 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 227 insertions(+), 7 deletions(-)
---
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index ffdd76d..d3ff2b8 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -368,6 +368,11 @@ struct _GtkWidgetPrivate
    */
   GtkWidget *parent;
 
+#ifdef G_ENABLE_DEBUG
+  /* Number of gtk_widget_push_verify_invariants () */
+  guint verifying_invariants_count;
+#endif /* G_ENABLE_DEBUG */
+
   /* Widget's path for styling */
   GtkWidgetPath *path;
 };
@@ -555,6 +560,15 @@ static void             gtk_widget_real_move_focus              (GtkWidget
                                                                  GtkDirectionType  direction);
 static gboolean		gtk_widget_real_keynav_failed		(GtkWidget        *widget,
 								 GtkDirectionType  direction);
+#ifdef G_ENABLE_DEBUG
+static void             gtk_widget_verify_invariants            (GtkWidget        *widget);
+static void             gtk_widget_push_verify_invariants       (GtkWidget        *widget);
+static void             gtk_widget_pop_verify_invariants        (GtkWidget        *widget);
+#else
+#define                 gtk_widget_verify_invariants(widget)
+#define                 gtk_widget_push_verify_invariants(widget)
+#define                 gtk_widget_pop_verify_invariants(widget)
+#endif
 static PangoContext*	gtk_widget_peek_pango_context		(GtkWidget	  *widget);
 static void     	gtk_widget_update_pango_context		(GtkWidget	  *widget);
 static void		gtk_widget_propagate_state		(GtkWidget	  *widget,
@@ -3694,8 +3708,9 @@ gtk_widget_unparent (GtkWidget *widget)
   if (priv->parent == NULL)
     return;
 
-  /* keep this function in sync with gtk_menu_detach()
-   */
+  /* keep this function in sync with gtk_menu_detach() */
+
+  gtk_widget_push_verify_invariants (widget);
 
   g_object_freeze_notify (G_OBJECT (widget));
   nqueue = g_object_notify_queue_freeze (G_OBJECT (widget), _gtk_widget_child_property_notify_context);
@@ -3767,6 +3782,8 @@ gtk_widget_unparent (GtkWidget *widget)
   if (!priv->parent)
     g_object_notify_queue_clear (G_OBJECT (widget), nqueue);
   g_object_notify_queue_thaw (G_OBJECT (widget), nqueue);
+
+  gtk_widget_pop_verify_invariants (widget);
   g_object_unref (widget);
 }
 
@@ -3848,8 +3865,10 @@ gtk_widget_show (GtkWidget *widget)
   if (!gtk_widget_get_visible (widget))
     {
       g_object_ref (widget);
+      gtk_widget_push_verify_invariants (widget);
+
       if (!gtk_widget_is_toplevel (widget))
-	gtk_widget_queue_resize (widget);
+        gtk_widget_queue_resize (widget);
 
       /* see comment in set_parent() for why this should and can be
        * conditional
@@ -3864,6 +3883,8 @@ gtk_widget_show (GtkWidget *widget)
 
       g_signal_emit (widget, widget_signals[SHOW], 0);
       g_object_notify (G_OBJECT (widget), "visible");
+
+      gtk_widget_pop_verify_invariants (widget);
       g_object_unref (widget);
     }
 }
@@ -3945,8 +3966,10 @@ gtk_widget_hide (GtkWidget *widget)
       GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
 
       g_object_ref (widget);
+      gtk_widget_push_verify_invariants (widget);
+
       if (toplevel != widget && gtk_widget_is_toplevel (toplevel))
-	_gtk_window_unset_focus_and_default (GTK_WINDOW (toplevel), widget);
+        _gtk_window_unset_focus_and_default (GTK_WINDOW (toplevel), widget);
 
       /* a parent may now be expand=FALSE since we're hidden. */
       if (widget->priv->need_compute_expand ||
@@ -3960,6 +3983,8 @@ gtk_widget_hide (GtkWidget *widget)
       if (!gtk_widget_is_toplevel (widget))
 	gtk_widget_queue_resize (widget);
       g_object_notify (G_OBJECT (widget), "visible");
+
+      gtk_widget_pop_verify_invariants (widget);
       g_object_unref (widget);
     }
 }
@@ -4119,13 +4144,17 @@ gtk_widget_map (GtkWidget *widget)
 
   if (!gtk_widget_get_mapped (widget))
     {
+      gtk_widget_push_verify_invariants (widget);
+
       if (!gtk_widget_get_realized (widget))
-	gtk_widget_realize (widget);
+        gtk_widget_realize (widget);
 
       g_signal_emit (widget, widget_signals[MAP], 0);
 
       if (!gtk_widget_get_has_window (widget))
-	gdk_window_invalidate_rect (priv->window, &priv->allocation, FALSE);
+        gdk_window_invalidate_rect (priv->window, &priv->allocation, FALSE);
+
+      gtk_widget_pop_verify_invariants (widget);
 
       _gtk_widget_start_state_transitions (widget);
     }
@@ -4149,10 +4178,14 @@ gtk_widget_unmap (GtkWidget *widget)
 
   if (gtk_widget_get_mapped (widget))
     {
+      gtk_widget_push_verify_invariants (widget);
+
       if (!gtk_widget_get_has_window (widget))
 	gdk_window_invalidate_rect (priv->window, &priv->allocation, FALSE);
       _gtk_tooltip_hide (widget);
       g_signal_emit (widget, widget_signals[UNMAP], 0);
+
+      gtk_widget_pop_verify_invariants (widget);
     }
 }
 
@@ -4218,6 +4251,8 @@ gtk_widget_realize (GtkWidget *widget)
 
   if (!gtk_widget_get_realized (widget))
     {
+      gtk_widget_push_verify_invariants (widget);
+
       /*
 	if (GTK_IS_CONTAINER (widget) && gtk_widget_get_has_window (widget))
 	  g_message ("gtk_widget_realize(%s)", G_OBJECT_TYPE_NAME (widget));
@@ -4254,6 +4289,8 @@ gtk_widget_realize (GtkWidget *widget)
         gdk_window_set_support_multidevice (priv->window, TRUE);
 
       _gtk_widget_enable_device_events (widget);
+
+      gtk_widget_pop_verify_invariants (widget);
     }
 }
 
@@ -4270,6 +4307,8 @@ gtk_widget_unrealize (GtkWidget *widget)
 {
   g_return_if_fail (GTK_IS_WIDGET (widget));
 
+  gtk_widget_push_verify_invariants (widget);
+
   if (widget->priv->has_shape_mask)
     gtk_widget_shape_combine_region (widget, NULL);
 
@@ -4285,6 +4324,8 @@ gtk_widget_unrealize (GtkWidget *widget)
       gtk_widget_set_mapped (widget, FALSE);
       g_object_unref (widget);
     }
+
+  gtk_widget_pop_verify_invariants (widget);
 }
 
 /*****************************************
@@ -4584,6 +4625,8 @@ gtk_widget_size_allocate (GtkWidget	*widget,
 
   g_return_if_fail (GTK_IS_WIDGET (widget));
 
+  gtk_widget_push_verify_invariants (widget);
+
 #ifdef G_ENABLE_DEBUG
   if (gtk_get_debug_flags () & GTK_DEBUG_GEOMETRY)
     {
@@ -4683,7 +4726,7 @@ gtk_widget_size_allocate (GtkWidget	*widget,
 		      old_allocation.y != real_allocation.y);
 
   if (!alloc_needed && !size_changed && !position_changed)
-    return;
+    goto out;
 
   g_signal_emit (widget, widget_signals[SIZE_ALLOCATE], 0, &real_allocation);
 
@@ -4735,6 +4778,9 @@ gtk_widget_size_allocate (GtkWidget	*widget,
       gtk_widget_invalidate_widget_windows (priv->parent, invalidate);
       cairo_region_destroy (invalidate);
     }
+
+out:
+  gtk_widget_pop_verify_invariants (widget);
 }
 
 /**
@@ -7515,6 +7561,9 @@ gtk_widget_set_parent (GtkWidget *widget,
    */
 
   g_object_ref_sink (widget);
+
+  gtk_widget_push_verify_invariants (widget);
+
   priv->parent = parent;
 
   parent_flags = gtk_widget_get_state_flags (parent);
@@ -7578,6 +7627,8 @@ gtk_widget_set_parent (GtkWidget *widget,
       gtk_style_context_set_screen (widget->priv->context,
                                     gtk_widget_get_screen (widget));
     }
+
+  gtk_widget_pop_verify_invariants (widget);
 }
 
 /**
@@ -8578,6 +8629,173 @@ gtk_widget_get_default_style (void)
   return gtk_default_style;
 }
 
+#ifdef G_ENABLE_DEBUG
+/* Verify invariants, see docs/widget_system.txt for notes on much of
+ * this.  Invariants may be temporarily broken while we're in the
+ * process of updating state, of course, so you can only
+ * verify_invariants() after a given operation is complete.
+ * Use push/pop_verify_invariants to help with that.
+ */
+static void
+gtk_widget_verify_invariants (GtkWidget *widget)
+{
+  GtkWidget *parent;
+
+  if (widget->priv->verifying_invariants_count > 0)
+    return;
+
+  parent = widget->priv->parent;
+
+  if (widget->priv->mapped)
+    {
+      /* Mapped implies ... */
+
+      if (!widget->priv->realized)
+        g_warning ("%s %p is mapped but not realized",
+                   G_OBJECT_TYPE_NAME (widget), widget);
+
+      if (!widget->priv->visible)
+        g_warning ("%s %p is mapped but not visible",
+                   G_OBJECT_TYPE_NAME (widget), widget);
+
+      if (!widget->priv->toplevel)
+        {
+          if (!widget->priv->child_visible)
+            g_warning ("%s %p is mapped but not child_visible",
+                       G_OBJECT_TYPE_NAME (widget), widget);
+        }
+    }
+  else
+    {
+      /* Not mapped implies... */
+
+      if (widget->priv->toplevel)
+        {
+          if (widget->priv->visible)
+            g_warning ("%s %p toplevel is visible but not mapped",
+                       G_OBJECT_TYPE_NAME (widget), widget);
+        }
+    }
+
+  /* Parent related checks aren't possible if parent has
+   * verifying_invariants_count > 0 because parent needs to recurse
+   * children first before the invariants will hold.
+   */
+  if (parent == NULL || parent->priv->verifying_invariants_count == 0)
+    {
+      if (parent &&
+          parent->priv->realized)
+        {
+          /* Parent realized implies... */
+
+#if 0
+          /* This is in widget_system.txt but appears to fail
+           * because there's no gtk_container_realize() that
+           * realizes all children... instead we just lazily
+           * wait for map to fix things up.
+           */
+          if (!widget->priv->realized)
+            g_warning ("%s %p is realized but child %s %p is not realized",
+                       G_OBJECT_TYPE_NAME (parent), parent,
+                       G_OBJECT_TYPE_NAME (widget), widget);
+#endif
+        }
+      else if (!widget->priv->toplevel)
+        {
+          /* No parent or parent not realized on non-toplevel implies... */
+
+          if (widget->priv->realized && !widget->priv->in_reparent)
+            g_warning ("%s %p is not realized but child %s %p is realized",
+                       parent ? G_OBJECT_TYPE_NAME (parent) : "no parent", parent,
+                       G_OBJECT_TYPE_NAME (widget), widget);
+        }
+
+      if (parent &&
+          parent->priv->mapped &&
+          widget->priv->visible &&
+          widget->priv->child_visible)
+        {
+          /* Parent mapped and we are visible implies... */
+
+          if (!widget->priv->mapped)
+            g_warning ("%s %p is mapped but visible child %s %p is not mapped",
+                       G_OBJECT_TYPE_NAME (parent), parent,
+                       G_OBJECT_TYPE_NAME (widget), widget);
+        }
+    }
+
+  if (!widget->priv->realized)
+    {
+      /* Not realized implies... */
+
+#if 0
+      /* widget_system.txt says these hold, but they don't. */
+      if (widget->priv->resize_pending)
+        g_warning ("%s %p resize pending but not realized",
+                   G_OBJECT_TYPE_NAME (widget), widget);
+
+      if (widget->priv->alloc_needed)
+        g_warning ("%s %p alloc needed but not realized",
+                   G_OBJECT_TYPE_NAME (widget), widget);
+
+      if (widget->priv->width_request_needed)
+        g_warning ("%s %p width request needed but not realized",
+                   G_OBJECT_TYPE_NAME (widget), widget);
+
+      if (widget->priv->height_request_needed)
+        g_warning ("%s %p height request needed but not realized",
+                   G_OBJECT_TYPE_NAME (widget), widget);
+#endif
+    }
+}
+
+/* The point of this push/pop is that invariants may not hold while
+ * we're busy making changes. So we only check at the outermost call
+ * on the call stack, after we finish updating everything.
+ */
+static void
+gtk_widget_push_verify_invariants (GtkWidget *widget)
+{
+  widget->priv->verifying_invariants_count += 1;
+}
+
+static void
+gtk_widget_verify_child_invariants (GtkWidget *widget,
+                                    gpointer   client_data)
+{
+  /* We don't recurse further; this is a one-level check. */
+  gtk_widget_verify_invariants (widget);
+}
+
+static void
+gtk_widget_pop_verify_invariants (GtkWidget *widget)
+{
+  g_assert (widget->priv->verifying_invariants_count > 0);
+
+  widget->priv->verifying_invariants_count -= 1;
+
+  if (widget->priv->verifying_invariants_count == 0)
+    {
+      gtk_widget_verify_invariants (widget);
+
+      if (GTK_IS_CONTAINER (widget))
+        {
+          /* Check one level of children, because our
+           * push_verify_invariants() will have prevented some of the
+           * checks. This does not recurse because if recursion is
+           * needed, it will happen naturally as each child has a
+           * push/pop on that child. For example if we're recursively
+           * mapping children, we'll push/pop on each child as we map
+           * it.
+           */
+          gtk_container_forall (GTK_CONTAINER (widget),
+                                gtk_widget_verify_child_invariants,
+                                NULL);
+        }
+    }
+}
+#endif /* G_ENABLE_DEBUG */
+
 static PangoContext *
 gtk_widget_peek_pango_context (GtkWidget *widget)
 {
@@ -8907,6 +9125,7 @@ gtk_widget_set_child_visible (GtkWidget *widget,
   priv = widget->priv;
 
   g_object_ref (widget);
+  gtk_widget_verify_invariants (widget);
 
   if (is_visible)
     priv->child_visible = TRUE;
@@ -8931,6 +9150,7 @@ gtk_widget_set_child_visible (GtkWidget *widget,
 	gtk_widget_unmap (widget);
     }
 
+  gtk_widget_verify_invariants (widget);
   g_object_unref (widget);
 }
 



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