Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions

  




 

 

Gtk+/Gnome Application Development
Prev Home Next

Signals

A GtkObject can emit a signal. Signals are stored in a global table by GTK+. Handlers or callbacks can be connected to signals; when a signal is emitted, its callbacks are invoked. The process of invoking all handlers for a signal is called emission.

Abstractly, a signal is a kind of message that an object wants to broadcast; the kind of message is associated with certain conditions (such as the user selecting a list item) and with message-specific parameter types which are passed to connected callbacks (such as the index of the row the user selected). User callbacks are connected to a particular signal and to a particular object instance. That is, you do not connect callbacks to the "clicked" signal of all buttons; rather, you connect to the "clicked" signal of a particular one. (However, there is a way to monitor all emissions of a signal---these callbacks are called "emission hooks.")

Signals are typically associated with a class function pointer which is invoked every time the signal is emitted; if non-NULL, the pointed-to class function serves as a default handler for the signal. It is up to the author of each GtkObject subclass whether to provide a space in the class struct for a default handler, and whether to implement the default handler in the base class. Conventionally, signals have the same name as the class function they are associated with.

For example, the GtkButtonClass struct has a member called clicked; this member is registered as the default handler for the "clicked" signal. However, the GtkButton base class does not implement a default handler, and leaves the clicked member set to NULL. Subclasses of GtkButton could optionally fill it in with an appropriate function. If GtkButton did implement a default clicked handler, subclasses could still override it with a different one.

Note that GTK+ signals have nothing to do with UNIX signals. Sometimes new GTK+ users confuse the two.

Adding a New Signal

Once you understand the GTK+ type system and GtkArg, signal registration is fairly transparent. Here is the signal registration code from GtkButton again:


  button_signals[PRESSED] =
    gtk_signal_new ("pressed",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkButtonClass, pressed),
                    gtk_marshal_NONE__NONE,
                    GTK_TYPE_NONE, 0);
  button_signals[RELEASED] =
    gtk_signal_new ("released",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkButtonClass, released),
                    gtk_marshal_NONE__NONE,
                    GTK_TYPE_NONE, 0);
  button_signals[CLICKED] =
    gtk_signal_new ("clicked",
                    GTK_RUN_FIRST | GTK_RUN_ACTION,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkButtonClass, clicked),
                    gtk_marshal_NONE__NONE,
                    GTK_TYPE_NONE, 0);
  button_signals[ENTER] =
    gtk_signal_new ("enter",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkButtonClass, enter),
                    gtk_marshal_NONE__NONE,
                    GTK_TYPE_NONE, 0);
  button_signals[LEAVE] =
    gtk_signal_new ("leave",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkButtonClass, leave),
                    gtk_marshal_NONE__NONE,
                    GTK_TYPE_NONE, 0);

  gtk_object_class_add_signals (object_class, button_signals, LAST_SIGNAL);



Earlier in gtkbutton.c, an enumeration and an array were declared as follows:


enum {
  PRESSED,
  RELEASED,
  CLICKED,
  ENTER,
  LEAVE,
  LAST_SIGNAL
};


static guint button_signals[LAST_SIGNAL] = { 0 };


gtk_signal_new() has the following effects:

  • It registers the name of the signal.

  • It associates the signal with a particular GtkType.

  • It tells GTK+ where to find the default handler in the class struct, if any.

  • It tells GTK+ what signature the signal's callbacks will have.

  • It registers a marshaller, a function which invokes the signal's callbacks in an appropriate way.

  • It generates an integer identifier which can be used to refer to the signal. (If you refer to the symbol by name, GTK+ will find the ID associated with the name and then use the ID.)

gtk_object_class_add_signals() attaches signal identifiers to the object's class struct, so the signals for a given class can be rapidly located. Conventionally, the argument to this function is an enumeration-indexed static array, as shown for GtkButton. The static array is also useful when implementing the functionality of the class (the signal identifiers are used to emit the signals).

The first argument to gtk_signal_new() is a name for the signal; you refer to the signal by name when you call gtk_signal_connect(), for example. The third argument is the GtkType of the object type emitting the signal, and the fourth is the location of the associated class function in the type's class struct. A macro is provided to compute the offset. If you specify an offset of 0, no class function will be associated with the signal. Note that giving a zero offset is distinct from giving a valid offset but setting the function member in the struct to NULL; in the latter case, subclasses of the object can fill in a value for the default handler.

The second argument is a bitfield. Here are the associated flags:

  • GTK_RUN_FIRST means that the default handler in the class struct, if any, will run before user-connected callbacks. If this flag is set, signal handlers should not return a value.

  • GTK_RUN_LAST means the opposite, the default handler will run last. (Caveat: user callbacks connected with gtk_signal_connect_after() run after a GTK_RUN_LAST default handler. There is no way to ensure a default handler is always run last. GTK_RUN_FIRST handlers are always first, however.)

  • GTK_RUN_BOTH is an alias for (GTK_RUN_FIRST | GTK_RUN_LAST), so the default handler will run twice (on either side of user-connected callbacks).

  • GTK_RUN_NO_RECURSE means that the signal should not be called recursively. If a handler for a signal emits the same signal again, normally the second emission is performed as usual (calling all handlers), and then the first emission continues, invoking its remaining handlers. With GTK_RUN_NO_RECURSE in effect, a second emission aborts the first emission (ignoring any handlers that remain), and restarts the emission process. So only one emission is in progress at a time. (Right now this is used only for GtkAdjustment's "changed" and "value_changed" signals. Usually you don't care about how many times a value changed, only whether it changed and its most recent value. GTK_RUN_NO_RECURSE "compresses" multiple emissions into a single emission.)

  • GTK_RUN_ACTION means the signal can be "bound" and invoked by the user. In other words, no special setup or shutdown is required in order to emit it. Among other things, GTK+ will allow users to bind keyboard accelerators to these signals using statements in the .gtkrc configuration file.

  • GTK_RUN_NO_HOOKS means that emission hooks are not allowed (you can't monitor this signal for an entire object type, only for particular object instances). It is used for GtkObject's "destroy" signal because hooks are not invoked on objects with the GTK_DESTROYED flag set and that flag is set before emitting "destroy". It's probably not good for anything else.

The last few arguments to gtk_signal_new() provide a marshaller, and tell GTK+ the marshaller's type. A marshaller invokes a callback function, based on an array of GtkArg it receives from GTK+. Marshallers are needed because C function argument lists cannot be constructed at runtime. GTK+ comes with a number of prewritten marshallers; here is the one used for all GtkButton signals:


typedef void (*GtkSignal_NONE__NONE) (GtkObject* object,
                                      gpointer user_data);
void 
gtk_marshal_NONE__NONE (GtkObject * object,
                        GtkSignalFunc func,
                        gpointer func_data,
                        GtkArg * args)
{
  GtkSignal_NONE__NONE rfunc;
  rfunc = (GtkSignal_NONE__NONE) func;
  (*rfunc) (object,
            func_data);
}

As you can see, the NONE__NONE refers to the fact that the expected callback type returns no value and has no "special" arguments. GTK+ automatically passes the object emitting the signal and a user_data field to all callbacks; special signal arguments are inserted in between these two. Since there are no signal-specific arguments in this case, the array of GtkArg is ignored.

The naming convention for marshallers places a double underscore between the return value and the special arguments, if any. Here's a more complex example:


typedef gint (*GtkSignal_INT__POINTER) (GtkObject * object,
                                        gpointer arg1,
                                        gpointer user_data);
void 
gtk_marshal_INT__POINTER (GtkObject * object,
                          GtkSignalFunc func,
                          gpointer func_data,
                          GtkArg * args)
{
  GtkSignal_INT__POINTER rfunc;
  gint *return_val;
  return_val = GTK_RETLOC_INT (args[1]);
  rfunc = (GtkSignal_INT__POINTER) func;
  *return_val = (*rfunc) (object,
                          GTK_VALUE_POINTER (args[0]),
                          func_data);
}

Notice that the last element of the array of GtkArg is a space for the return value; if there is no return value, this element will have type GTK_TYPE_NONE and can be ignored. GTK+ provides macros such as GTK_RETLOC_INT() to extract a "return location" from a GtkArg. Similar GTK_RETLOC_ macros exist for all the fundamental types.

The function pointer signatures in the class structure for an object will correspond to the type of the signal. This is a convenient way to find out what signature the callbacks connected to a signal should have, if the GTK+ header files are readily available on your system.

The last arguments to gtk_signal_new() give the type of the signal's marshaller. First a return value type is given, then the number of special arguments, then a variable argument list containing that many GtkType values in the appropriate order. Since GtkButton has no examples of signals with arguments, here is one from GtkWidget:


  widget_signals[BUTTON_PRESS_EVENT] =
    gtk_signal_new("button_press_event",
                   GTK_RUN_LAST,
                   object_class->type,
                   GTK_SIGNAL_OFFSET (GtkWidgetClass, button_press_event),
                   gtk_marshal_BOOL__POINTER,
                   GTK_TYPE_BOOL, 1,
                   GTK_TYPE_GDK_EVENT);

"button_press_event" returns a boolean value, and has a GdkEvent* argument. Notice that the marshaller works with any GTK_TYPE_POINTER, but the signal requires the more-specific boxed type GTK_TYPE_GDK_EVENT, allowing language bindings to query the correct kind of pointer.

Signals can have many arguments; here is one from GtkCList:


  clist_signals[SELECT_ROW] =
    gtk_signal_new ("select_row",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkCListClass, select_row),
                    gtk_marshal_NONE__INT_INT_POINTER,
                    GTK_TYPE_NONE, 3,
                    GTK_TYPE_INT,
                    GTK_TYPE_INT,
                    GTK_TYPE_GDK_EVENT);

The "select_row" signal returns no value, but has three arguments (the selected row and column number, and the event that caused the selection).

Gtk+/Gnome Application Development
Prev Home Next

 
 
  Published under free license. Design by Interspire