The Callback interface

Dive 3 Reference Document
Web site: <http://www.sics.se/dive/manual/callbacks.html>

Olof Hagsand - <olof@sics.se>
The Swedish Institute of Computer Science

Stockholm, September, 1995

1. Introduction

This document describes the Dive functional callback interface. The Dive callback interface enables applications and modules to register interest in system events by supplying a user defined function. As the event occurs, the user supplied function is invoked.

This document is intended for designers making integrated Dive applications in C or C++, using the Dive functional interface and linking with the Dive libraries. It describes Dive system events, how to register Dive callbacks, and how the user-defined functions are invoked.

Alternative approaches include to design the application in the file format [2] and Dive/Tcl behaviour interface [3], or the Dive Client Interface (DCI) [4].

2. Events types

The callback interface is coupled tightly to Dive system events. A Dive event is something that occurs in the system at a specific time. If a callback is registered with an event, the callback is called when the event occurs. Currently, the following Dive events are defined:

Below, the classifications of Dive events into sub-types will be given. Sub-types offer a way to specify (filter) events more specifically. Not all events are classified into sub-types.

3. Registering callbacks

Callbacks can be registered by the following functions:

http://www.sics.se/dive/manual/cref.c.html#callback_register http://www.sics.se/dive/manual/cref.c.html#callback_register_filter

The following functions are only called at the origin (sender) of an event and receivers, respectively:

http://www.sics.se/dive/manual/cref.c.html#callback_register_sender http://www.sics.se/dive/manual/cref.c.html#callback_register_receiver

3.1 Event filtering

Callbacks can be filtered, so that they are only called in specific situations. In particular, a callback can be made only for a specific object, or a specific event sub-type, by calling the following function:

     void *callback_register_filter(dive_event_type event_type,
                                    void(*fn)(), int type, objid_t *id,
                                    char *str, unsigned int flags, void *arg)

The sub-type and matching string argument specified in the filtering have different semantics for the different callbacks. See Section 4 for the meaning of the sub-type for each case.

3.2 Unregistering callbacks

There are two ways of deregistering callbacks, one using the callback function, and one using the return value (cookie) of the registering function:

http://www.sics.se/dive/manual/cref.c.html#callback_unregister http://www.sics.se/dive/manual/cref.c.html#callback_deregister

4. Callback functions

This section describes the associated callback functions and their arguments.

4.1 General arguments

Some arguments occur in many callback functions and have the same meaning:

4.2 Event ENTITY_NEW_EVENT

Called when a new entity has been created. The callback function has the following arguments:


     objid_t *id, objid_t *origin, void *arg

Note that being "new" means that it is created in the local database, it may previously have existed in other peers or worlds.

"id" can not be used to filter events since the identifier cannot be known in advance. The mask versions of entity types (eg. N_POLY_MASK, DIVE_OBJ_MASK) may be used in any combination as the "type" field in filtering commands. The matching string is ignored.

4.3 Event ENTITY_CHANGE_EVENT

Called when an entity has been updated in some way except in the ways covered by the other callbacks. The callback function has the following arguments:

objid_t *id, objid_t *origin, void *arg

where "id" is the entity that has changed, "from" is the source of the event, and "arg" is a user supplied argument.

The identifier and entity type may be used in filtering commands. The matching string is ignored.

4.4 Event ENTITY_REMOVE_EVENT

Called when an entity is removed from the local database. The callback is delivered before it is actually deleted in order to enable processing of the entity. The callback function has the following arguments:


     objid_t *id, objid_t *origin, void *arg

Note that "removing" means removing an entity from the local database. This may cause some confudion. Suppose for example that an entity is moved from one world to another. The entity is then removed only in those peers that does not have the new world.

The identifier and entity type may be used in filtering commands. The matching string is ignored.

4.5 Event ENTITY_MESSAGE_EVENT

Called when a process has sent a user defined message to an entity. The callback function has the following arguments:

objid_t *id, objid_t *origin, char *name, int type, struct prop_link *msg_list, void *arg

where "id" is the entity that receives the event, "origin" is the actor that sent the event, "name" and "type" may be used for dispatching. Typically, "name" identifies a module while "type" identifies a module specific entry. The content of the message is contained as a property list in "msg_list".

In filtering commands, "id", "type" and "name" arguments may be used to match with id, type and str matchings, respectively.

4.6 Event ENTITY_RPC_EVENT

Called when a process has sent a user defined message to an entity. The callback function has the following arguments:

        objid_t *id, objid_t *origin, char *name, int type,
        struct prop_link *msg_list, struct prop_link **reply_list, void *arg

where "id" is the entity that receives the event, "origin" is the actor that sent the event, "name" and "type" may be used for dispatching. Typically, "name" identifies a module while "type" identifies a module specific entry. The content of the message is contained as a property list in "msg_list". Answers to the message is made by assigning properties to "reply_list" and returning from the callback. Note that the process requesting for an RPC only waits for the first answer to come back, other answers are discarded.

As for ENTITY_MESSAGE_EVENT, filtering commands can be made to match against "id", "type" and "name".

4.7 Event ENTITY_PROP_EVENT

A property has changed in an entity. The callback function has the following arguments:

objid_t *id, objid_t *origin, char *name, int type, void *arg

where "id" is the identifier of the entity with the changed property, "origin" is the identifier of the actor that changed the property, "name" is the name of the property, "type" is how the property has changed and "arg" is a user supplied argument.

The property change type can have one of the following values:

The id, type and matching string in filtering commands can be used to match against the "id", "type" and "name" arguments.

4.8 Event ENTITY_FLAG_EVENT

A shared flag field in an entity has changed. The callback function has the following arguments:

objid_t *id, objid_t *origin, unsigned int mask, void *arg

wheer "mask" is a bitvector that indicates which flags have (not the new) value.

The "id" may be used in filtering commands, and "mask" may be used to indicate which flags are of interest. The matching string is ignored.

4.9 Event ENTITY_ADD_SUB_EVENT

An entity has been moved from one place in an hierarchy to another. The callback function has the following arguments:

     objid_t *id, objid_t *oldsuper, objid_t *newsuper,
     objid_t *origin, void *arg

where "id" is the identifier of the inserted entity, "oldsuper" the identifier of the old parent, and "newsuper" the identifier of the new parent. Both parents should be a divenode (ie. a world or a dive_obj).

Note: In the new entity model, worlds are also entities, so almost all objects are placed as a sub in a hierarchy. However, when actors are moved from one world to another, the ACTOR_MIGRATE_EVENT occurs instead.

The identifier and the type of entity of the inserted entity may be used in filtering commands. The matching string is ignored.

4.10 Event ACTOR_MIGRATE_EVENT

An actor changes world and also transfers its associated objects to new world. The callback function has the following arguments:


     objid_t *id, objid_t *wid0, objid_t *wid1, char *name, void *arg
     

where "wid0" is the identifier of the old world, "wid1" the identifier of the new world and "name" is the name of the new world.

The callback will be delivered to both the old and new world. If the new world is not present in the peer, then "remove" callbacks will also be delivered (see ENTITY_REMOVE_EVENT).

Apart from "id", there is no way to filter this callback with type or matching string.

4.11 Event DIVEOBJ_COORD_EVENT

The geometrics or kinematics of a dive_obj has changed. It can be a translation, rotation or a combination. It can also be a change in velocity, acceleration or angular velocity. The callback function has the following arguments:


     objid_t *id, objid_t *origin, point_t *pt0, divetime *time, void *arg

where "pt0" is the old position of the object in local coordinates before the transformation. "pt0" is only defined if the transformation function was "interpolated", such as distr_transform. Apart from "id", there is no way to filter this callback with a type or matching string.

4.12 Event DIVEOBJ_VELOCITY_EVENT

The angular or directional velocity of a dive object has changed. The callback function has the following arguments:


     objid_t *id, point_t *old_dirV, point_t *old_angV, divetime *old_Tstamp

where "old_dirV" is the object's old directional velocity, "old_angV" is the old angular velocity and "old_Tstamp" is the time when these velocities where set.

4.13 Event DIVEOBJ_SCALE_EVENT

The scaling of a dive_obj has changed. The callback function has the following arguments:


     objid_t *id, point_t *ratio, void *arg

where "ratio" are the x, y and z factors of the new (relative) scaling.

Apart from "id", there is no way to filter this callback with a type or matching string.

4.14 Event DIVEOBJ_MATERIAL_EVENT

The material of a dive_obj has changed in some way. A dive_node can be associated with a vector of materials that can be accessed by material-indexes. The callback function has the following arguments:

objid_t *id, objid_t *origin, void *arg

Apart from "id", there is no way to filter this callback with a type or matching string.

4.15 Event PROCESS_NOTIFY_EVENT

A system information message has been generated. This message may be displayed by a browser or interface, it has no system meaning. Typically, notifications are: "The file foo has been read", etc. The callback function has the following arguments:


     char *message, void *arg

This callback can not be filtered in any way, neither with type, id nor matching string.

4.16 Event INTERACTION_SIGNAL

The callback function has the following arguments:

     objid_t *id, interaction_type_t type, objid_t *origin,
     objid_t *src_id, point_t *pt, objid_t *viewid,
     divetime *time, void *arg

where "id" is the identifier of the object that registered the callback (possibly NULL), "viewid" is the view where the actual interaction occured, "type" is the sub-type of the interaction, "origin" is the actor that caused the interaction, "src_id" is the object that was used to select the object (eg a hand), "pt" is the point in world coordinates where the interaction occurred (eg on the surface of the interacted object, if any), and "time" is the time when the event was generated (for instance the time of a mouse button press).

The two types of interactions that are currently supported by the system are to select and to grab objects. A selection corresponds to a single click in many windowing systems, while grabbing is to take the object and hold it for a while (drag-and-drop).

When a selection of an object is made (typically by pointing at and pressing a button) a DIVE_IA_SELECT signal is sent. On succesful deselection (eg. the button is released while still pointing at the same object) a DIVE_IA_DESELECT is sent and the "pt" argument contains the position where the button was released, otherwise DIVE_IA_DESELECT_FAIL is sent and the "pt" argument is a NULL pointer.

When an actor tries to grab an object (typically by pointing and pressing another button), it either suceeds holds the object and sends a DIVE_IA_GRASP signal, or fails and sends a DIVE_IA_GRASP_FAIL signal. An actor can fail to grasp an object if the object is not modifiable, or is a sub-object of the object that grabs it. When the object is released, the actor sends a DIVE_IA_GRAP_RELEASE signal.

Thus, the classification of interactions is:

The "id" and "type" arguments may be used in filtering commands, type

4.17 Event COLLISION_SIGNAL

Collisions can be seen as a binary relation over entities changing over time. A pair of entities ar either in a collided state (eg, their volumes intersect) or they are not. The collision signals are used to indicate a change of this state. The callback function has the following arguments:

     objid_t *id, objid_t *id2, collision_type_t type,
     divetime *time, void *arg

where "id" and "id2" are the two colliding (or non colliding) objects, "type" is the sub-type as defined below, and "time" is the time of collision. Note that two collision signals may be delivered, one with "id2" as its first and "id" as its second argument.

Collision signals are typically emitted by collision managers or servers. In Dive 3 there is a default collision server that works by checking only for collisions between objects with the "obj_collison" flag set. However, this default server can be replaced by an application specific module.

The classification of collisions is:

If an identifier is given as a filtering command, both the callback will be delivered if either id or id2 match. The "type" argument may also be used.

4.18 Event WORLD_SERVER_EVENT

The process is currently "server" for world. Being server means that it is the only process that performs certain actions. The callback can be used to identify a unique process of a world. The callback function has the following arguments:


     objid_t *id, divebool on, void *arg

where "id" is the identifier of the world that the process is server of.

The "id" may be used as filtering command.

4.18 Event IMAGE_EVENT

A new image has been generated. Images are typically texture or video frames that are captured via a frame grabber card and distributed within the system. Each image belongs to a stream, identified by a name. The stream name can be used as a texture name which makes it possible to achieve dynamic textures since each new image in the stream will replace the old.

The callback function has the following arguments:

     char *name, char *format, void *image, void *arg
     objid_t *id, divebool on, void *arg

where 'name' is the stream name, 'format' is the image format and 'image' points to the actual image data.

5. Examples

5.1 Event filtering: selection

Suppose you want to call function fn() when someone has selected an entity with identifier id1. That is, sent an INTERACTION_SIGNAL of type DIVE_IA_SELECT. In this case, you register fn with the following call:

     callback_register_filter(INTERACTION_SIGNAL, fn, DIVE_IA_SELECT,
                              id1, NULL, CALLBACK_ALL, NULL)

The user defined function "fn" should look like:

     void fn(objid_t *id, interaction_type_t type, objid_t *origin,
             objid_t *src_id, point_t *pt, void *arg)

5.2 Event filtering: new entities

Suppose you want to call function fn() when a new entity has been created in your local database. In particular, only dive_objects and lines are of interest. In this case, you register fn with the following call:

     callback_register_filter(ENTITY_NEW_EVENT, fn,
                              (DIVE_OBJ_MASK | LINE_MASK), NULL, NULL,
                              CALLBACK_ALL, NULL)

The user defined function "fn" should look like:


     void fn(objid_t *id, objid_t *origin, void *arg)


5.3 Changing worlds

When actors change worlds, the event sequence can be slightly confusing. An example may clarify the sequence of callbacks. For simplicity, "change" callbacks and callbacks for the actor's associated objects have been omitted.

As an example, suppose actor "A" changes world from W0 to W1 and that there are three peers P, P0, P1 and P2. "A" belongs to P and all events stem from P. P is initially connected to W0, then connects to W1 and disconnects from W0. P0 is connected to W0, P1 is connected to W1 and P2 to both W0 and W1.

The following callbacks occur at the different peers:

References

[1] C Carlsson and O Hagsand, "Dive - A Platform for Multi-User Virtual Environments", "Computers and Graphics", 17(6), 1993

[2] Dive 3.0 file format interface, 1995

[3] E. Frécon and O. Hagsand, The DIVE/TCL Behaviour Interface, Dive 3 Reference Document, 1995, available at
<http://www.sics.se/dive/manual/tcl-behaviour.html>.

[4] E. Frécon and O. Hagsand, "The Dive Client Interface", Dive 3 Reference Document, 1995, available at
<http://www.sics.se/dive/manual/dci.html>