Commit 4b867176 authored by Tim Janik's avatar Tim Janik

documentation. -timj

documentation.
-timj
parent 8d2bb0f4
How ref counting works within Gdk and Gtk
=========================================
The Reference Counting Scheme of GDK an GTK+
============================================
Each data structure that provides ref counting offers a bunch of
Each data structure that provides reference counting offers a bunch of
functions that follow these conventions:
*_new: Create a new structure with a reference count of 1.
......@@ -11,10 +11,12 @@ functions that follow these conventions:
No user visible actions should take place, like
destryoing windows, etc.
Some structures also provide a *_destroy function, but it is generally
unrelated to freeing the memory. `Destroying' merely renders an
object `unusable'. But as long as there are references to it, it will
stick around.
GtkObjects also provide the following functions:
*_destroy: Render an object `unusable', but as long as there are
references to it, it's allocated memory will not be freed.
*_sink: Clear a GtkObjects `floating' state and decrement the
reference count by 1.
GdkWindow
---------
......@@ -92,7 +94,7 @@ GtkObject
GtkObjects follow the usual ref_counting strategy, but with a twist.
They are created with a ref_count of 1. GtkObjects are able able to
They are created with a ref_count of 1. GtkObjects are able to
run finalization code when the ref_count drops to zero but you cannot
register arbitrary signal handlers to run at finalization time.
......@@ -105,20 +107,21 @@ signal handlers. The finalization code is different, it would for
example free associated memory for text strings and release the
attached style.
[This is the biggest change. Every widget must be revised to have a
proper "destroy" function, etc. Such a destroy function must be able
to be called any number of times and generally leave the widget in a
minimal but consistent state. The "finalization" function is new and
should perform last-minute cleanup actions. It can assume that the
"destroy" function has been called as the last function on this
widget.
This is the biggest change. Every widget must be revised to have a
proper "destroy" function, etc. Such a destroy function will only
be called once and is expected to leave the widget in a minimal but
consistent state. Widgets that have been "destroyed" but not yet
finalized are flagged with GTK_DESTROY. The "finalization" function
is new and should perform last-minute cleanup actions, in contrast
to the destroy function it will not be emitted as signal though.
It can assume that the "destroy" function has been called as the
last function on this widget.
Essentially, the old "destroy" function has been split into a
"finalize" plus a "destroy" function.]
Essentially, the old "destroy" function has been split into a
"finalize" plus a "destroy" function.
It is not possible to create GtkObjects with a ref_count of 0 (as it
is done now) because the first ref/unref pair will destroy it
unintentionally.
It is not possible to create GtkObjects with a ref_count of 0
because the first ref/unref pair will destroy it unintentionally.
To be mostly backward compatible with existing practice, a GtkObject
leads a more complicated life than the other reference counted structures.
......@@ -155,47 +158,147 @@ It is considered a bug if a widget still has a GdkWindow when it is
being freed.
Toplevel widgets, which don't have a `natural' parent, are adopted by
a special widget, maybe a GtkDisplay or GtkScreen. This special
parent of all toplevel widgets is never freed. The toplevel widgets
are added to this parent as soon as they are created. [Maybe this
special widget will only exist conceptually because toplevel widgets
are identified by parent == NULL through-out the code.]
So, the typical career of a GtkWindow and the GtkButton that sits in
it looks like this:
special registering functions. Because the of the reference count that
is set by the registering functions, toplevel widgets will have to be
explicitly destroyed, with the exception of GtkMenus. GtkMenus are a
special case of toplevel widgets in that they will be `attached' to and
`detached' from other widgets. The act of attaching a GtkMenu to a
widget will be reflected in its reference count. The act of detaching
a GtkMenu will revert that. Therefore GtkMenus naturally get destroyed
and finalized once they are detached from their reference holder.
So, the typical career of a GtkWindow a GtMenu attached to a
GtkOptionMenu looks like this:
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
// window is created with ref_count == 1. It is not flagged as
// `floating' because it has already been added to the special
// parent of all toplevel widgets.
button = gtk_button_new_with_label ("Yo!");
// button->ref_count == 1 and it is flagged as `floating'.
gtk_container_add (window, button);
// button->ref_count still == 1, but it is no longer `floating'.
gtk_widget_show (button);
/* window is created with ref_count == 1. It is not flagged as
* `floating' because it has already been registered as a toplevel
* widget.
*/
option_menu = gtk_option_menu_new ();
/* option_menu->ref_count == 1 and it is flagged as `floating'.
*/
gtk_container_add (window, option_menu);
/* option_menu->ref_count still == 1, but it is no longer `floating'.
*/
menu = gtk_menu_new ();
/* menu->ref_count == 1 and it is flagged as `floating'.
*/
menu_item = gtk_menu_item_new_with_label ("Choose Me");
/* menu_item->ref_count == 1 and it is flagged as `floating'.
*/
gtk_menu_append (GTK_MENU (menu), menu_item);
/* menu_item->ref_count still == 1, but it is no longer `floating'.
*/
gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
/* menu->ref_count still == 1, but it is no longer `floating'.
*/
gtk_widget_show (menu_item);
gtk_widget_show (option_menu);
gtk_widget_show (window);
// The widgets get their GdkWindows, nothing significant happens to
// the ref_counts.
/* The widgets get their GdkWindows, nothing significant happens to
* the ref_counts.
*/
Then, when the user wants to get rid of the window:
gtk_widget_destroy (window);
// The GdkWindow of `window' and all its child GdkWindows are
// destroyed.
// window is removed from its (conceptual) parent and its ref_count
// drops to zero. The destroy code of `window' destroyes `button'.
// The destriction of the button removes it from its parent, the
// button->ref_count drops to zero and the button is freed, too.
- Marius Vollmer <mvo@zagadka.ping.de>
/* The GdkWindow of `window' and all its child GdkWindows are
* destroyed.
*
* window is unregistered from the loplevel list and its ref_count
* drops to zero. The destroy code of `window' destroyes `option_menu'.
*
* The destroy code of `option_menu' causes the `menu' to be detached
* from it and its reference count drops to zero.
*
* The destroy code of `menu' destroyes `menu_item'.
*
* The destruction of `menu_item' removes it from its parent, the
* menu_item->ref_count drops to zero and `menu_item' is finalized (freed).
*
* Now `menu', `option_menu' and `window' will be destroyed and finalized,
* in this order, since the reference count of each is zero.
*/
Taking care of proper referencing
---------------------------------
There are some cases where referencing of widgets from outside the toolkit
(on the application side is needed).
Once the application performes an operation on a widget that will cause
its reference count to drop, if it wants to take further actions on the
widget, it needs to hold a reference to it.
Example code sequences that require reference wraps:
/* gtk_container_remove() will unparent the child and therefore
* cause it's reference count to be decremented by one.
*/
gtk_widget_ref (widget);
gtk_container_remove (container, widget);
/* without the reference count, the widget would have been destroyed here.
*/
gtk_container_add (container, widget);
gtk_widget_unref (widget);
/* all items in item_list need to be referenced
* before gtk_list_remove_items() is invoked.
* this is somewhat tricky as gtk_list_append_items/gtk_list_prepend_items/
* gtk_list_insert_items will take over the lists nodes.
* we therefore have an extra GSList `*slist' for later unreferencing.
*/
slist = NULL;
for (list = item_list; list; list = list->next)
{
gtk_widget_ref (GTK_WIDGET (list->data));
slist = g_slist_prepend (slist, list->data);
}
gtk_list_remove_items (list, item_list);
gtk_list_append_items (other_list, item_list);
/* gtk_list_prepend_items (other_list, item_list); */
/* gtk_list_insert_items (other_list, item_list, 3); */
while (slist)
{
GSList *tmp;
tmp = slist;
slist = slist->next;
gtk_widget_unref (GTK_WIDGET (tmp->data));
g_slist_free_1 (tmp);
}
Now a (hopefully) complete list of functions that require
wrappers similar to the examples above:
void gtk_container_remove (GtkContainer *container,
GtkWidget *widget);
void gtk_list_remove_items (GtkList *list,
GList *items);
void gtk_tree_remove_items (GtkTree *tree,
GList *items);
void gtk_tree_item_remove_subtree (GtkTreeItem *tree_item);
void gtk_menu_item_remove_submenu (GtkMenuItem *menu_item);
void gtk_option_menu_remove_menu (GtkOptionMenu *option_menu);
Initial proposal:
- Marius Vollmer <mvo@zagadka.ping.de>
Small fixups, "Taking care of proper referencing" and reference
counting solution for GtkMenus:
- Tim Janik <timj@gimp.org>
Notes about the inner workings of the widget system of GTK+
===========================================================
This file contains some notes as to how the widget system does
and should work. It consists of three parts:
......@@ -5,7 +8,7 @@ and should work. It consists of three parts:
II) A list of invariants about the states of the widgets.
(Throughout this document, we refer to the states of the
widgets by referring to the flags for gtkwidget)
widgets by referring to the flags for GtkWidget)
III) Some notes about the ways that a widget changes states
......@@ -26,73 +29,176 @@ Section II is mostly of interest to those maintaining GTK, the
other sections may also be interesting to people writing
new widgets.
Owen Taylor <owt1@cornell.edu>
98/02/03
Main outline:
- Owen Taylor <owt1@cornell.edu>
98/02/03
Flag descriptions:
- Tim Janik <timj@gimp.org>
98/02/04
I. Flags
--------
GtkObject:
GTK_DESTROYED:
This flagged is set for a GtkObject right before its
destruction code is executed. Its main use is the
prevention of multiple destruction invokations.
GTK_FLOATING:
This flag reflects the fact that the holder of the
initial reference count is unknown. Refer to refcounting.txt
for further details.
GTK_RESERVED_1:
GTK_RESERVED_2:
Reserved flags.
GtkWidget, public flags:
GTK_TOPLEVEL:
Widgets witghout a real parent, as there are GtkWindows and
GtkMenus have this flag set throughout their lifetime.
Toplevel widgets always contain their own GdkWindow.
GTK_NO_WINDOW:
This flag is indicative for a widget that does not provide
its own GdkWindow. Visible action (e.g. drawing) is performed
on the parents GdkWindow.
GTK_REALIZED:
set by gtk_widget_realize, unset by gtk_widget_unrealize
relies on ((widget->parent && widget->parent->window)
|| GTK_WIDGET_TOPLEVEL (widget);
means: widget has an associated GdkWindow (XWindow)
Set by gtk_widget_realize, unset by gtk_widget_unrealize.
Relies on ((widget->parent && widget->parent->window)
|| GTK_WIDGET_TOPLEVEL (widget));
Means: widget has an associated GdkWindow (XWindow).
GTK_MAPPED:
set by gtk_widget_map, unset by gtk_widget_unmap
may only be set if GTK_WIDGET_REALIZED(widget)
means: gdk_window_show() has been called on the widgets window(s).
Set by gtk_widget_map, unset by gtk_widget_unmap.
May only be set if GTK_WIDGET_REALIZED (widget).
Means: gdk_window_show() has been called on the widgets window(s).
GTK_VISIBLE:
set by gtk_widget_show
implies that a widget will be GTK_MAPPED as soon as it's
parent is GTK_MAPPED.
Set by gtk_widget_show.
Implies that a widget will be flagged GTK_MAPPED as soon as its
parent is mapped.
!GTK_VISIBLE:
set by gtk_widget_hide
implies that a widget is not onscreen, therefore !GTK_MAPPED
Set by gtk_widget_hide.
Implies that a widget is not onscreen, therefore !GTK_MAPPED.
GTK_SENSITIVE:
Set and unset by gtk_widget_set_sensitive.
The sensitivity of a widget determines wether it will receive
certain events (e.g. button or key presses). One premise for
the widgets sensitivity is to have GTK_SENSITIVE set.
GTK_PARENT_SENSITIVE:
Set and unset by gtk_widget_set_sensitive operations on the
parents of the widget.
This is the second premise for the widgets sensitivity. Once
it has GTK_SENSITIVE and GTK_PARENT_SENSITIVE set, its state is
effectively sensitive. This is expressed (and can be examined) by
the GTK_WIDGET_IS_SENSITIVE macro.
GTK_CAN_FOCUS:
There are no directly corresponding functions for setting/unsetting
this flag, but it can be affected by the GtkWidget::has_focus argument
via gtk_widget_set_arg.
This flag determines wether a widget is able to handle focus grabs.
GTK_HAS_FOCUS:
This flag will be set by gtk_widget_grab_focus for widgets that also
have GTK_CAN_FOCUS set. The flag will bve unset once another widget
grabs the focus.
GTK_CAN_DEFAULT:
GTK_HAS_DEFAULT:
These two flags are mostly equal in functionality to their *_FOCUS
counterparts, but for the defalt widget.
GTK_HAS_GRAB:
Set by gtk_grab_add, unset by gtk_grab_remove.
Means: widget is in the grab_widgets stack, and will be the preferred
one for receiving events other than ones of cosmetic value.
GTK_BASIC:
The GTK_BASIC flag is an attempted at making a distinction between
widgets that will proper handle user input e.g. key/button presses.
Supsequent parent<->child relation ships of non `basic' widgets
should be avoided. The checking for this is currently not proper
enforced in the code. For example GtkButton is a non `basic' widget,
that will therefore disallow to act as a container for another
GtkButton. Now the gnit is, one can add a GtkHBox (which is a
`basic' widget) to the first button, and put the second into
the box.
GtkWidget, private flags:
GTK_USER_STYLE:
A widget is flagged to have a user style, once gtk_widget_set_style
has been invoked for it. The use of this flag is to tell widgets
wich share a global user style from the ones which got a certain
style assign from outside the toolkit.
GTK_REDRAW_PENDING:
relies on GTK_WIDGET_MAPPED(widget)
[this is not really enforced throughout the code, but should
Relies on GTK_WIDGET_MAPPED (widget).
[FIXME: this is not really enforced throughout the code, but should
be. it only requires a few checks for GTK_WIDGET_MAPPED and
minor changes to gtk_widget_unmap, we can then remove the check
in gtk_widget_real_destroy]
means: there is an idle handler waiting for the widget, that
will cause a full redraw (gtk_widget_draw(widget, NULL))
Means: there is an idle handler waiting for the widget, that
will cause a full redraw (gtk_widget_draw (widget, NULL)).
GTK_RESIZE_PENDING:
first, this is only valid for GtkContainers.
First, this is only valid for GtkContainers.
[some of the code should move to gtkcontainer.c therefore]
relies on GTK_WIDGET_REALIZED(widget)
Relies on GTK_WIDGET_REALIZED(widget)
[this is not really enforced throughout the code, but should
be. it only requires a few checks for GTK_WIDGET_RELIZED and
minor changes to gtk_widget_unrealize, we can then remove the check
in gtk_widget_real_destroy]
means: there is an idle handler waiting for the container to
Means: there is an idle handler waiting for the container to
resize it.
GTK_RESIZE_NEEDED:
relies on GTK_WIDGET_REALIZED(widget)
[puhhh, this isn't rely enforced either..., bla bla ...remove check
in gtk_widget_real_destroy]
means: a widget has been added to the resize_widgets list of
Relies on GTK_WIDGET_REALIZED(widget)
[this is not really enforced throughout the code, but should
be. once this is done special checking in gtk_widget_real_destroy
can be avoided]
Means: a widget has been added to the resize_widgets list of
its _toplevel_ container (keep this in mind for GtkViewport).
remark: this flag is also used internaly by gtkwindow.c.
Remark: this flag is also used internaly by gtkwindow.c during
the evaluation of resizing worthy widgets.
GTK_LEAVE_PENDING:
a widget is flagged as such if there is a leave_notify event
pending for it. it receives this event regardless of a grab or
its current sensitivity.
A widget is flagged as such if there is a leave_notify event
pending for it. It will receive this event regardless of a grab
through another widget or its current sensitivity.
[this should be made relying on GTK_REALIZED]
GTK_HAS_GRAB:
set by gtk_grab_add, unset by gtk_grab_remove
means: widget is in the grab_widgets stack.
GTK_HAS_SHAPE_MASK:
Set by gtk_widget_shape_combine_mask if a widget got a shape mask
assigned (making use of the X11 shaped window extension).
GTK_IN_REPARENT:
During the act of reparentation widgets which are already
realized and will be added to an already realized parent need
to have this flag set to prevent natural unrealization on the
process of getting unparented.
Related Macros:
GTK_WIDGET_DRAWABLE:
a widget is flagged as GTK_WIDGET_VISIBLE and GTK_WIDGET_MAPPED
means: it _makes sense_ to draw in a widgets window.
This macro examines wether a widget is flagged as GTK_WIDGET_VISIBLE
and GTK_WIDGET_MAPPED.
Means: it _makes sense_ to draw in a widgets window.
GTK_WIDGET_IS_SENSITIVE:
This macro tells the real sensitivity state of a widget. It returns
wether both the widget and all its parents are in sensitive state.
II. Invariants:
......@@ -105,7 +211,7 @@ In the following
A => B means if A is true, than B is true
A <=> B means A is true, if and only if B is true
(equivalent to A => B and B <= A)
(equivalent to A => B and A <= B)
1) GTK_WIDGET_DESTROYED (widget) => !GTK_WIDGET_REALIZED (widget)
......@@ -375,5 +481,3 @@ it was not overridden in the Bar class structure) and
gtk_widget_destroy will call gtk_object_destroy because
the parent_class variable referenced by gtk_foo_destroy is the
static variable in gtkwidget.c: GtkObjectClass.
......@@ -3824,6 +3824,13 @@ gtk_widget_dnd_data_set (GtkWidget *widget,
gdk_window_dnd_data_set (widget->window, event, data, data_numbytes);
}
void
gtk_widget_sink (GtkWidget *widget)
{
gtk_object_sink (GTK_OBJECT (widget));
}
#undef gtk_widget_ref
#undef gtk_widget_unref
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment