Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to determine if GTK widget has been destroyed

Tags:

c

widget

gtk

I have some Gtk+ code written in C that does some animation using Cairo and a timer. Most of the time when I click on the close application icon I get the following message on the terminal:

Gtk-CRITICAL **: gtk_widget_queue_draw: assertion `GTK_IS_WIDGET (widget)' failed

Now I'm assuming this is happing because at the moment I close the application the timer is fired and the main window widget is accessed but has since been destroyed. What is the usual method to determine if a Gtk widget is still valid and can be referenced?

The offending code is here:

gboolean rotate_cb( void *degrees )
{
    rotation += DegreesToRadians((*(int*)(degrees)));
    // Tell our window that it should repaint itself (ie. emit an expose event)
    /* need to only call gtk_widget_queue_draw() if window is still valid / exists */
    gtk_widget_queue_draw(window);
    return( TRUE );
}

I'm assuming there must be some way to test whether or not window is still active and valid?

like image 649
Chimera Avatar asked Jun 14 '12 17:06

Chimera


1 Answers

Your problem is quite subtle. This usually happens because of the rules of ownership and destruction of GObject/GtkObject. Let me remind them:

  • GObjects are simply count referenced. They are destroyed when the count reaches 0. A newly created object has count 1.
  • GInitiallyUnowneds are also count referenced, and they are also destroyed when the count reaches 0. But a newly created object has a floating count. That means that the first time the count is to be incremented it is not actually incremented, but the floating count is sunk, that is, converted to a normal reference.

GtkObjects are GInitiallyUnowned objects, so they have the magic floating count.

But you probably know all this... Now, my question:

Who owns the counter of the visible main GtkWindow?

That's actually easy, the GTK framework has a list of these and retains a reference of every visible GtkWindow. But then, another question:

When does the GTK framework free the references of its GtkWindow?

Do you remember the gtk_widget_destroy() function and the destroy signal? They are exactly for that: when you want to remove a toplevel GtkWindow you call gtk_widget_destroy(), it activates the signal destroy, that is received by the GTK framework that removes the actual window and frees its reference to the object.

And here comes the reason for your problem: if the GTK framework retains the only existing reference to the GtkWindow, the object is actually freed. If then, your timer tries to access to it, it will fail, because the window is no more.

And here, at last, comes (hopefully) the solution:

  • Call g_object_ref()/g_object_ref_sink() on the window when you start the timer. Also register a handler to the destroy signal of the window.
  • In the handler of the destroy signal of the window: call g_object_unref() on the window and stop the timer.

Naturally, this partial solution should also work, because the window will not be destroyed without sending the destroy signal:

  • Register a handler to the destroy signal of the window.
  • In the handler of the destroy signal of the window: stop the timer.

But it is considered a good practice to increment the ref-counter of the objects when you are actually keeping a pointer to them.

like image 128
rodrigo Avatar answered Sep 30 '22 16:09

rodrigo