Signals

A signal is defined by a class or interface and is emitted by an object (instance) when a particular event occurs. A function is called on emission of a signal by connecting to the signal and specifying the function as the handler.

The special structure Signal provides support for working with signals in a type safe way.

Referring to signals

In GObject, a signal name is string of ASCII letters and digits with words separated by a hyphen (or underscore) where the first word starts with a letter. The SML interface provides an abstract value for each signal. For a signal “some-signal-name” of a class or interface Type in a namespace Namespace, the hierarchical SML name is Namespace.Type.someSignalNameSig.

For example, the signal “insert-at-cursor” of the class TextView in the namespace Gtk is Gtk.TextView.insertAtCursorSig

In order to provide a type-safe interface for using signals, each signal has an SML specification constraining the instance type that emits the signal and the type of the emitter and handler functions.

The specification of Namespace.Type.someSignalNameSig in the signature NAMESPACE_TYPE has the form:

val someSignalNameSig : (instanceType, argType_e, argType_h, resType_h, resType_e) Signal.t

where

instanceType

is:

  • 'a class for a class, which allows an instance of the class or any subclass of the class;

  • t for an interface;

argType_e -> resType_e

is the type of a function that emits the signal;

argType_h -> resType_h

is the type of a function that handles the signal;

argType_e and argType_h

contain the types of the ‘in’ and ‘inout’ arguments, as a tuple if there are two or more and as unit if there are none;

resType_e and resType_h

contain the following types as a tuple if there are two or more and as unit if there are none:

  • the types of the ‘out’ and ‘inout’ arguments, if the return type is void;

  • the return type followed by the types of the ‘out’ and ‘inout’ arguments, if the return type is not void.

The difference between argType_e and argType_h and between resType_e and resType_h is in the SML types for a class or union: argType_e and resType_h allow an object of any subclass of the class and a union instance to have the type of any field of the union whereas resType_e and argType_h do not.

For example, the specification of Gtk.TextView.insertAtCursorSig is

val insertAtCursorSig : ('a class, string, string, unit, unit) Signal.t

As the example above shows, neither argType_e nor argType_h have an argument for the instance that emits the signal.

Specifying signal detail

In GObject, the detail of a signal is a string whose format is specific to the signal. The SML representation of a signal includes the detail, which is used by both signal connection and emission functions. The detail of a signal value is returned by the function Signal.detail and is overridden by the functions Signal.withDetail and Signal.withPropDetail which return a new signal value.

The detail of a signal signal is given by

Signal.detail signal

The detail of each signal provided in the SML interface is empty: for a signal “a” of a class or interface Namespace.Type, Signal.detail Namespace.Type.aSig is "".

A signal signal with the detail replaced by detail is given by

Signal.withDetail (signal, detail)

Specifying detail as "" has the effect of removing any detail.

The detail of the signal “notify” of the class GObject.Object requires a property name if non-empty. Using Signal.withDetail to override this detail requires the property name to be written as a string. Instead, this detail can be overridden by specifying a property value using Signal.withPropDetail. A signal signal with the detail replaced by the name of a property prop and with its type constrained for use only on instances with the property is given by

Signal.withPropDetail (signal, prop)

Signal.withPropDetail is preferable for a signal whose detail is a property name because type checking also ensures that:

  • the property exists;

  • the property is valid for the object on which the signal is defined;

  • the resulting signal cannot be used on an object that does not have the property.

Handling signals

At present, there is support only for handling the emission of a signal from an instance of a class or interface. There is no support for

  • handling a signal on all instances of a class or interface, i.e. overriding the class closure

  • emission hooks

Connecting signal handlers

A handler function is connected to a signal of an object (instance) using Signal.connect or Signal.connectAfter. The handler function is called before the default handler by using Signal.connect and after the default handler by using Signal.connectAfter.

For a signal “a” of a class Class in a namespace ClassNamespace, a handler function f is connected to the signal on an object obj by

Signal.connect obj (ClassNamespace.Class.aSig, f)

whose value is a handler id to allow the signal connection to be modified or checked subsequently.

For a signal “a” of an interface IfaceNamespace.Iface implemented by the class ClassNamespace.Class, a handler function f is connected to the signal on the object obj by either converting obj:

Signal.connect (ClassNamespace.Class.asIface obj) (IfaceNamespace.Iface.aSig, f)

or converting the signal:

Signal.connect obj (Signal.conv ClassNamespace.Class.asIface IfaceNamespace.Iface.aSig, f)

For example, to handle emission of the signal “insert-at-cursor” from the object textView using the function onInsert, connect to the signal as follows:

val handlerId = Signal.connect textView (Gtk.TextView.insertAtCursorSig, onInsert)

handlerId can be used to modify this signal connection subsequently.

It is not unusual for the handler function f to need to refer to the object that emitted the signal, obj, but argType_h does not have an argument to pass obj to the handler f. This is easily achieved by defining f to take the emitting object as the first argument and supplying the argument when connecting the handler as follows:

Signal.connect obj (Namespace.Type.aSig, f obj)

Type checking ensures that when a handler is connected to a signal of an object,

  • the handler function has the correct type for the signal;

  • the signal is valid for the object.

Blocking signal handlers

A signal handler is blocked using Signal.handlerBlock and unblocked using Signal.handlerUnblock. (Note that blocking is cumulative so a signal handler becomes active only once it has been unblocked as many times as it has been blocked.) The signal handler to block or unblock is identified by the object that emits the signal and the handler id returned when it was connected.

For example, a signal handler on textView identified by handlerId is blocked as follows:

Signal.handlerBlock textView handlerId

and is unblocked as follows:

Signal.handlerUnblock textView handlerId

Disconnecting signal handlers

A signal handler is disconnected using Signal.handlerDisconnect. The signal handler to disconnect is identified by the object that emits the signal and the handler id returned when it was connected.

For example, a signal handler on textView identified by handlerId is disconnected as follows:

Signal.handlerDisconnect textView handlerId

Testing whether signal handlers are still connected

Signal.handlerIsConnected tests whether a signal is connected. The signal handler is identified by the object that emits the signal and the handler id returned when it was connected.

For example, a signal handler on textView identified by handlerId is tested as follows:

Signal.handlerIsConnected textView handlerId

Emitting signals

A signal is emitted on an object (instance) using Signal.emit.

For a signal “a” of a class ClassNamespace.Class, the signal is emitted on an object obj with an argument arg by

Signal.emit obj ClassNamespace.Class.aSig arg

whose value is the result.

Not all signals have a default handler. If emission of a signal is not handled, the result contains

  • the default value for ‘out’ arguments and the return value and

  • the before value for ‘inout’ arguments.

The default value is the value set on initialization of a GValue by g_value_init for the GType of the corresponding ‘out’ argument or return value.

For a signal “a” of an interface IfaceNamespace.Iface implemented by the class ClassNamespace.Class, the signal is emitted on an object obj with an argument arg by either converting obj:

Signal.emit (ClassNamespace.Class.asIface obj) IfaceNamespace.Iface.aSig arg

or converting the signal:

Signal.emit obj (Signal.conv ClassNamespace.Class.asIface IfaceNamespace.Iface.aSig) arg

For example, to emit the signal “button-press-event” on the object button with the argument e, emit the signal as follows:

val res = Signal.emit button Widget.buttonPressEventSig e

Type checking ensures that when a signal is emitted on an object,

  • the emit function has the correct type for the signal;

  • the signal is valid for the object.