Blame docs/refcounting.txt

Packit 98cdb6
The Reference Counting Scheme of GDK an GTK+
Packit 98cdb6
============================================
Packit 98cdb6
Packit 98cdb6
Each data structure that provides reference counting offers a bunch of
Packit 98cdb6
functions that follow these conventions:
Packit 98cdb6
Packit 98cdb6
  *_new:      Create a new structure with a reference count of 1.
Packit 98cdb6
  *_ref:      Increase ref count by one.
Packit 98cdb6
  *_unref:    Decrease ref count by one.  If the count drops to zero,
Packit 98cdb6
              run appropriate finalization code and free the memory.
Packit 98cdb6
	      For data structures with a _destroy function, it will be
Packit 98cdb6
	      invoked at this point, if the data structure is not
Packit 98cdb6
              already in a destroyed state.
Packit 98cdb6
Packit 98cdb6
GtkObjects also provide the following functions:
Packit 98cdb6
Packit 98cdb6
  *_destroy:  Render an object `unusable', but as long as there are
Packit 98cdb6
              references to it, its allocated memory will not be freed.
Packit 98cdb6
  *_sink:     Clear a GtkObjects `floating' state and decrement the
Packit 98cdb6
	      reference count by 1.
Packit 98cdb6
Packit 98cdb6
GdkWindow
Packit 98cdb6
---------
Packit 98cdb6
Packit 98cdb6
A GdkWindow has to be explicitly destroyed with gdk_window_destroy.
Packit 98cdb6
This will send out a request to destroy this window and all its
Packit 98cdb6
children, and will decrement the ref_count of the GdkWindow by one.
Packit 98cdb6
Thus, it releases the initial reference created by gdk_window_new.
Packit 98cdb6
Packit 98cdb6
All GdkWindows are kept in a hash table to translate from their XId to
Packit 98cdb6
the actual structure and the pointer in the hash table is reflected in
Packit 98cdb6
the reference count.  When a DestroyNotify event is received for a
Packit 98cdb6
particular GdkWindow, it is removed from the hash table and the
Packit 98cdb6
ref_count is updated accordingly.
Packit 98cdb6
Packit 98cdb6
You can call gdk_window_destroy more than once on a particular
Packit 98cdb6
GdkWindow, it will only be destroyed when it hasn't been yet.  The
Packit 98cdb6
ref_count is *always* decremented, tho. Be careful.
Packit 98cdb6
Packit 98cdb6
Remark: When writing NO_WINDOW widgets, care should be taken about
Packit 98cdb6
        proper referencing/unreferencing of the parent's GdkWindow
Packit 98cdb6
        that is used by the widget.
Packit 98cdb6
 
Packit 98cdb6
GdkPixmap
Packit 98cdb6
---------
Packit 98cdb6
Packit 98cdb6
There is no gdk_pixmap_destroy function.  The Pixmap is destroyed when
Packit 98cdb6
the last reference to it vanishes.
Packit 98cdb6
Packit 98cdb6
GdkPixmaps are kept in the same hash table as GdkWindows but the
Packit 98cdb6
pointer in the hash table is *not* reflected in the ref_count.
Packit 98cdb6
Packit 98cdb6
This works only when Pixmaps never get XEvents.  I'm not sure if this
Packit 98cdb6
is the case.
Packit 98cdb6
Packit 98cdb6
GdkBitmap
Packit 98cdb6
---------
Packit 98cdb6
Packit 98cdb6
A GdkBitmap is only another name for a special use of GdkPixmap.
Packit 98cdb6
Packit 98cdb6
GdkVisual
Packit 98cdb6
---------
Packit 98cdb6
Packit 98cdb6
There are no *_new or *_destroy functions and the *_ref and *_unref
Packit 98cdb6
functions are no-ops.  GdkVisuals are static structures and thus do not
Packit 98cdb6
need reference counting.  The ref counting functions are only there
Packit 98cdb6
for extra defensive programming.
Packit 98cdb6
Packit 98cdb6
GdkColormap
Packit 98cdb6
-----------
Packit 98cdb6
Packit 98cdb6
Nothing special.  There is no gdk_colormap_destroy function.
Packit 98cdb6
Packit 98cdb6
GdkFont / GdkFontSet
Packit 98cdb6
--------------------
Packit 98cdb6
Packit 98cdb6
GdkFont and GdkFontSet are equivalent as far as ref counting is
Packit 98cdb6
concerned.  Use gdk_font_ref and gdk_font_unref for both.
Packit 98cdb6
Packit 98cdb6
There is no gdk_font_free or gdk_fontset_free function.
Packit 98cdb6
Packit 98cdb6
GtkAcceleratorTable
Packit 98cdb6
-------------------
Packit 98cdb6
Packit 98cdb6
There is no gtk_accelerator_table_destroy function.
Packit 98cdb6
Packit 98cdb6
GtkTooltips
Packit 98cdb6
-----------
Packit 98cdb6
Packit 98cdb6
There is no gtk_tooltips_destroy function.
Packit 98cdb6
Packit 98cdb6
GtkStyle
Packit 98cdb6
--------
Packit 98cdb6
Packit 98cdb6
There is no gtk_style_destroy function.
Packit 98cdb6
Packit 98cdb6
GtkObject
Packit 98cdb6
---------
Packit 98cdb6
Packit 98cdb6
GtkObjects follow the usual ref_counting strategy, but with a twist.
Packit 98cdb6
Packit 98cdb6
They are created with a ref_count of 1.  GtkObjects are able to
Packit 98cdb6
run finalization code when the ref_count drops to zero but you cannot
Packit 98cdb6
register arbitrary signal handlers to run at finalization time.
Packit 98cdb6
Packit 98cdb6
There is also the old gtk_object_destroy function and the "destroy"
Packit 98cdb6
signal but they are somewhat independent from finalization.  Just as
Packit 98cdb6
stated at the top of this text, gtk_object_destroy merely renders an
Packit 98cdb6
object unusable.  When the object is a container widget for example,
Packit 98cdb6
it unrealizes that widget, removes all children and disconnects all
Packit 98cdb6
signal handlers.  The finalization code is different, it would for
Packit 98cdb6
example free associated memory for text strings and release the
Packit 98cdb6
attached style.
Packit 98cdb6
Packit 98cdb6
This is the biggest change.  Every widget must be revised to have a
Packit 98cdb6
proper "destroy" function, etc.  Such a destroy function will only
Packit 98cdb6
be called once and is expected to leave the widget in a minimal but
Packit 98cdb6
consistent state.  Widgets that have been "destroyed" but not yet
Packit 98cdb6
finalized are flagged with GTK_DESTROY.  The "finalization" function
Packit 98cdb6
is new and should perform last-minute cleanup actions, in contrast
Packit 98cdb6
to the destroy function it will not be emitted as signal though.
Packit 98cdb6
It can assume that the "destroy" function has been called as the
Packit 98cdb6
last function on this widget.
Packit 98cdb6
Packit 98cdb6
Essentially, the old "destroy" function has been split into a
Packit 98cdb6
"finalize" plus a "destroy" function.
Packit 98cdb6
Packit 98cdb6
It is not possible to create GtkObjects with a ref_count of 0
Packit 98cdb6
because the first ref/unref pair will destroy it unintentionally.
Packit 98cdb6
Packit 98cdb6
To be mostly backward compatible with existing practice, a GtkObject
Packit 98cdb6
leads a more complicated life than the other reference counted structures.
Packit 98cdb6
Packit 98cdb6
When a GtkObject is created, it starts out in a special state called
Packit 98cdb6
"floating" (this is the twist).  This means that it is alive and has a
Packit 98cdb6
reference to it, but the `owner' of this reference is not known.
Packit 98cdb6
There are certain `potential owners' that will adopt a floating
Packit 98cdb6
GtkObject.  For GtkWidgets the most common adopters are the parent
Packit 98cdb6
widget.
Packit 98cdb6
Packit 98cdb6
When you want to adopt a possibly floating GtkObject, you call
Packit 98cdb6
gtk_object_sink on it.  This clears the floating state of the
Packit 98cdb6
GtkObject and decrements the ref_count by one, if it has been floating
Packit 98cdb6
previously.  Once the floating state has been cleared, it will never
Packit 98cdb6
be set again.
Packit 98cdb6
Packit 98cdb6
All widgets that are part of the display are linked into a
Packit 98cdb6
parent/child tree.  The link from the parent to a child is reflected
Packit 98cdb6
in the ref_count of the child, but the link from the child to the
Packit 98cdb6
parent is not reflected in the ref_count of the parent.
Packit 98cdb6
Packit 98cdb6
Like a GtkObject, a GtkWidget is created with a ref_count of 1 and
Packit 98cdb6
initially flagged as `floating'.  As soon as it is added as a child to
Packit 98cdb6
a parent, the `floating' flag is cleared and never will be set again.
Packit 98cdb6
Not even when it is later unparented.  The act of clearing the
Packit 98cdb6
`floating' flag also decrements the ref_count of the widget by one.
Packit 98cdb6
Packit 98cdb6
When the widget is unparented, its underlying GdkWindow is destroyed
Packit 98cdb6
(when it has one), it loses its reference from the parent and
Packit 98cdb6
naturally the ref_count is decremented.
Packit 98cdb6
Packit 98cdb6
It is considered a bug if a widget still has a GdkWindow when it is
Packit 98cdb6
being freed.
Packit 98cdb6
Packit 98cdb6
Toplevel widgets, which don't have a `natural' parent, are adopted by
Packit 98cdb6
special registering functions.  Because the of the reference count that
Packit 98cdb6
is set by the registering functions, toplevel widgets will have to be
Packit 98cdb6
explicitly destroyed, with the exception of GtkMenus.  GtkMenus are a
Packit 98cdb6
special case of toplevel widgets in that they will be `attached' to and
Packit 98cdb6
`detached' from other widgets.  The act of attaching a GtkMenu to a
Packit 98cdb6
widget will be reflected in its reference count.  The act of detaching
Packit 98cdb6
a GtkMenu will revert that.  Therefore GtkMenus naturally get destroyed
Packit 98cdb6
and finalized once they are detached from their reference holder.
Packit 98cdb6
Packit 98cdb6
So, the typical career of a GtkWindow a GtMenu attached to a
Packit 98cdb6
GtkOptionMenu looks like this:
Packit 98cdb6
Packit 98cdb6
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
Packit 98cdb6
  /* window is created with ref_count == 1.  It is not flagged as
Packit 98cdb6
   * `floating' because it has already been registered as a toplevel
Packit 98cdb6
   * widget.
Packit 98cdb6
   */
Packit 98cdb6
Packit 98cdb6
  option_menu = gtk_option_menu_new ();
Packit 98cdb6
  /* option_menu->ref_count == 1 and it is flagged as `floating'.
Packit 98cdb6
   */
Packit 98cdb6
  
Packit 98cdb6
  gtk_container_add (window, option_menu);
Packit 98cdb6
  /* option_menu->ref_count still == 1, but it is no longer `floating'.
Packit 98cdb6
   */
Packit 98cdb6
  
Packit 98cdb6
  menu = gtk_menu_new ();
Packit 98cdb6
  /* menu->ref_count == 1 and it is flagged as `floating'.
Packit 98cdb6
   */
Packit 98cdb6
  
Packit 98cdb6
  menu_item = gtk_menu_item_new_with_label ("Choose Me");
Packit 98cdb6
  /* menu_item->ref_count == 1 and it is flagged as `floating'.
Packit 98cdb6
   */
Packit 98cdb6
Packit 98cdb6
  gtk_menu_append (GTK_MENU (menu), menu_item);
Packit 98cdb6
  /* menu_item->ref_count still == 1, but it is no longer `floating'.
Packit 98cdb6
   */
Packit 98cdb6
  
Packit 98cdb6
  gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
Packit 98cdb6
  /* menu->ref_count still == 1, but it is no longer `floating'.
Packit 98cdb6
   */
Packit 98cdb6
Packit 98cdb6
  gtk_widget_show (menu_item);
Packit 98cdb6
  gtk_widget_show (option_menu);
Packit 98cdb6
  gtk_widget_show (window);
Packit 98cdb6
Packit 98cdb6
  /* The widgets get their GdkWindows, nothing significant happens to
Packit 98cdb6
   * the ref_counts.
Packit 98cdb6
   */
Packit 98cdb6
Packit 98cdb6
Then, when the user wants to get rid of the window:
Packit 98cdb6
Packit 98cdb6
  gtk_widget_destroy (window);
Packit 98cdb6
Packit 98cdb6
  /* The GdkWindow of `window' and all its child GdkWindows are
Packit 98cdb6
   * destroyed.
Packit 98cdb6
   *
Packit 98cdb6
   * window is unregistered from the toplevel list and its ref_count
Packit 98cdb6
   * drops to zero.  The destroy code of `window' destroys `option_menu'.
Packit 98cdb6
   *
Packit 98cdb6
   * The destroy code of `option_menu' causes the `menu' to be detached
Packit 98cdb6
   * from it and its reference count drops to zero.
Packit 98cdb6
   *
Packit 98cdb6
   * The destroy code of `menu' destroys `menu_item'.
Packit 98cdb6
   *
Packit 98cdb6
   * The destruction of `menu_item' removes it from its parent, the
Packit 98cdb6
   * menu_item->ref_count drops to zero and `menu_item' is finalized (freed).
Packit 98cdb6
   *
Packit 98cdb6
   * Now `menu', `option_menu' and `window' will be destroyed and finalized,
Packit 98cdb6
   * in this order, since the reference count of each is zero.
Packit 98cdb6
   */
Packit 98cdb6
Packit 98cdb6
Packit 98cdb6
Taking care of proper referencing
Packit 98cdb6
---------------------------------
Packit 98cdb6
Packit 98cdb6
There are some cases where referencing of widgets from outside the toolkit
Packit 98cdb6
(on the application side) is needed.
Packit 98cdb6
Once the application performs an operation on a widget that will cause
Packit 98cdb6
its reference count to drop, if it wants to take further actions on the
Packit 98cdb6
widget, it needs to hold a reference to it.
Packit 98cdb6
Packit 98cdb6
Example code sequences that require reference wraps:
Packit 98cdb6
Packit 98cdb6
   /* gtk_container_remove() will unparent the child and therefore
Packit 98cdb6
    * cause its reference count to be decremented by one.
Packit 98cdb6
    */
Packit 98cdb6
   g_object_ref (widget);
Packit 98cdb6
   gtk_container_remove (container, widget);
Packit 98cdb6
   /* without the reference count, the widget would have been destroyed here.
Packit 98cdb6
   */
Packit 98cdb6
   gtk_container_add (container, widget);
Packit 98cdb6
   g_object_unref (widget);
Packit 98cdb6
Packit 98cdb6
Packit 98cdb6
  /* all items in item_list need to be referenced
Packit 98cdb6
   * before gtk_list_remove_items() is invoked.
Packit 98cdb6
   * this is somewhat tricky as gtk_list_append_items/gtk_list_prepend_items/
Packit 98cdb6
   * gtk_list_insert_items will take over the lists nodes.
Packit 98cdb6
   * we therefore have an extra GSList `*slist' for later unreferencing.
Packit 98cdb6
   */
Packit 98cdb6
   slist = NULL;
Packit 98cdb6
   for (list = item_list; list; list = list->next)
Packit 98cdb6
   {
Packit 98cdb6
     g_object_ref (GTK_WIDGET (list->data));
Packit 98cdb6
     slist = g_slist_prepend (slist, list->data);
Packit 98cdb6
   }
Packit 98cdb6
   gtk_list_remove_items (list, item_list);
Packit 98cdb6
   gtk_list_append_items (other_list, item_list);
Packit 98cdb6
   /* gtk_list_prepend_items (other_list, item_list); */
Packit 98cdb6
   /* gtk_list_insert_items (other_list, item_list, 3); */
Packit 98cdb6
   while (slist)
Packit 98cdb6
   {
Packit 98cdb6
     GSList *tmp;
Packit 98cdb6
     
Packit 98cdb6
     tmp = slist;
Packit 98cdb6
     slist = slist->next;
Packit 98cdb6
     g_object_unref (GTK_WIDGET (tmp->data));
Packit 98cdb6
     g_slist_free_1 (tmp);
Packit 98cdb6
   }
Packit 98cdb6
   
Packit 98cdb6
   /* Alternatively to the removal above you could just use
Packit 98cdb6
    * gtk_list_remove_items_no_unref() which will add the additional
Packit 98cdb6
    * reference count to the widget.
Packit 98cdb6
    */
Packit 98cdb6
   gtk_list_remove_items_no_unref (list, item_list);
Packit 98cdb6
   gtk_list_prepend_items (other_list, item_list);
Packit 98cdb6
Packit 98cdb6
Packit 98cdb6
Now a (hopefully) complete list of functions that require
Packit 98cdb6
wrappers similar to the examples above:
Packit 98cdb6
Packit 98cdb6
void       gtk_container_remove         (GtkContainer     *container,
Packit 98cdb6
                                         GtkWidget        *widget);
Packit 98cdb6
void       gtk_list_remove_items        (GtkList          *list,
Packit 98cdb6
                                         GList            *items);
Packit 98cdb6
void       gtk_tree_remove_items        (GtkTree          *tree,
Packit 98cdb6
                                         GList            *items);
Packit 98cdb6
void       gtk_tree_item_remove_subtree (GtkTreeItem      *tree_item);
Packit 98cdb6
void       gtk_menu_item_remove_submenu (GtkMenuItem      *menu_item);
Packit 98cdb6
void       gtk_option_menu_remove_menu  (GtkOptionMenu    *option_menu);
Packit 98cdb6
Packit 98cdb6
Packit 98cdb6
Packit 98cdb6
Initial proposal:
Packit 98cdb6
	- Marius Vollmer <mvo@zagadka.ping.de>
Packit 98cdb6
Packit 98cdb6
Some modifications/additions, "Taking care of proper referencing" and
Packit 98cdb6
reference counting solution for GtkMenus:
Packit 98cdb6
	- Tim Janik <timj@gimp.org>