How to determine if a GTK widget has been destroyed

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

Gtk-CRITICAL **: gtk_widget_queue_draw: claim `GTK_IS_WIDGET (widget)" failed

Now I assume that this is happening, because at the time the application is closed, the timer starts and the widget of the main window is available, but has since been destroyed. What is the usual method for determining if a Gtk widget is valid and referenced?

Violation code 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 assume there must be some way to check if window really active and valid?

+4
source share
3 answers

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

  • GObject simply refers to a number. They are destroyed when the counter reaches 0. The newly created object has a count of 1.
  • GInitiallyUnowned also refers to a number, and they are also destroyed when the counter reaches 0. But the newly created object has a floating point number. This means that the first time the counter should increase, it does not actually increase, but the floating counter drops, that is, it is converted to a normal link.

GtkObject are GInitiallyUnowned objects, so they have a magic floating point number.

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

Who owns the visible main GtkWindow counter?

It's really simple, the GTK framework has a list of them and maintains a link to all visible GtkWindow . But then another question arises:

When does a GTK frame free links to its GtkWindow?

Do you remember the gtk_widget_destroy() function and the destroy signal? They are designed precisely for this: when you want to remove toplevel GtkWindow , you call gtk_widget_destroy() , it activates the destroy signal, which receives a GTK frame, which removes the actual window and releases the link to the object.

And here is the cause of your problem: if the GTK structure only retains the existing link to GtkWindow , the object is actually freed. If then your timer tries to access it, it will not work, because the window is no longer there.

And finally, the solution comes (hopefully):

  • Call g_object_ref()/g_object_ref_sink() in the window when the timer starts. Also register a handler for the destroy window signal.
  • In the window destroy signal handler: 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 window destroy signal handler.
  • In the window destroy signal handler: stop the timer.

But it is considered good practice to escalate ref-counter objects when you actually keep a pointer to them.

+9
source

g_object_weak_ref() also useful for these cases.

Weak links are used to notify when an object is completed.

You can attach a weak ref callback (e.g. widget_destroy_cb ) to disconnect rotate_cb from the widget before it is destroyed, so rotate_cb should not be called after the widget is destroyed.

+3
source

There is no way, because you are requesting the state of an object that is no longer in memory, i.e. some piece of memory is undefined. Such a function may fail, for example. if there is another widget created at the same memory address. Basically, you can simply use this macro GTK_IS_WIDGET() , but it is not guaranteed to work correctly, even if it most often will.

The correct way to solve your problem is to remove the rotate_cb() callback when the window is destroyed. To do this, there is a "destroy" signal in the GtkWidget class. So, you must connect to "destroy" and in this handler delete rotate_cb() (or set some flag that makes rotate_cb() do nothing).

+2
source

All Articles