PSA: GtkBuilder, toplevels, and gtk_widget_destroy()

So this has nailed me twice and maybe this time I’ll remember.  If you have a toplevel (GtkWindow, GtkDialog, etc) in a GtkBuilder file, and you load that file into a GtkBuilder object, you need to remember to explicitly call gtk_widget_destroy() on it.  GtkBuilder will sink the initial GTK floating ref for you, but that means you now have widget with 2 references (object creation and the ref sink) and getting rid of the GtkBuilder will only remove one of those references for you.  You then need to remember to call gtk_widget_destroy() to get rid of the other one.  Not g_object_unref() apparently, as that’ll cause segfaults somewhere later on during widget destruction when something tries to disconnect some signal handlers somewhere, but gtk_widget_destroy().  This also removes the toplevels from GTK’s “toplevel_list” which, if you’re not careful and forget to destroy it, can lead to segfaults later when GTK tries to issue grabs when you’re scrolling.  Those are always entertaining to track down.  And when I say entertaining I don’t actually mean it.

GtkBuilder even has documentation about this:

For toplevel windows constructed by a builder, it is the responsibility of the user to call gtk_widget_destroy() to get rid of them and all the widgets they contain.

but somehow I keep forgetting.  And then I waste and hour figuring out WTF is going wrong.

3 Responses to “PSA: GtkBuilder, toplevels, and gtk_widget_destroy()”

  1. johan says:

    Owen explained this once to me. It’s the essentially the same behavior for all GtkWindows (or toplevels) that contains a hierarchy of widgets.

    The idea is that you normally want to unref the builder as early as possible, there’s really no need to keep the parse tree around in your application. So you want windows to survive the destruction of the builder and maintain the semantics of normal toplevel windows (eg, floating ones).

  2. pbor says:

    Usually you unref the builder right away after obtaining the widgets you need, that way not only you just have on reference to the toplevel window, but you also avoid to keep the builder object in memory for the whole duration of the program.

  3. desrt says:

    It’s quite simple, actually.

    For every widget, you create it with its _new() function and you get a floating reference. The only thing that GtkBuilder does differently is that it immediately sinks the floating reference. No surprises there: GtkBuilder always owns one strong reference on each object.

    What is different about GtkWindow is that Gtk itself owns a reference on all toplevel windows. You can’t call g_object_unref() because you don’t own the reference — Gtk does. Calling gtk_widget_destroy() on a toplevel is basically just telling Gtk to release its reference.