Events may be either asynchronous or synchronous. Asynchronous events are the default. Synchronous events have not yet been designed, although one trial implementation has been done. Further work must be done to determine what kinds of synchronous events would be useful.
An event may optionally contain a parameter. The parameter consists of a string of arbitrary length. In a Tcl script, the parameter can be accessed via the ``eventArgs'' variable. This variable is intended to be similar to the ``args'' parameter passed to Tcl procedures.
See the Event System User's Guide for more information on how to use names, types, and parameters in events.
Asynchronous Events
The most commonly used type of event is an asynchronous announcement. For example, the ``change'' event mentioned above is asynchronous, which means that the sender broadcasts an event and immediately continues execution, without waiting for an acknowledgement or reply. It cannot determine whether any other processes received its event or not. When the event reaches receivers, they will immediately execute a Tcl command at the global level, in much the same way as a Tk event binding. If an event occurs during the execution of the command, a Tk error dialog box will pop up and inform the user that he is a moron.
Synchronous Events
Since I do not know what sorts of synchronization are necessary, I'll just use this section of the document to list the possibilities:
Acknowledgement: Same as asynchronous, but the sender waits until at least one recipient acknowledges receipt.
n-Acknowledgements: Same as asynchronous, but the sender waits until n recipients have acknowledged the event.
Handled: The sender blocks until someone handles the event. Each receiver may choose to either observe or handle. If multiple receivers try to handle the event, either an error could occur or it could be quietly ignored.
Send-Implicit Reply: The sender blocks until it receives a reply. The receiver behaves as in the asynchronous case, except that after it executes the callback command, it sends a reply back to the sender to unblock it. Similar to (1) Acknowledgement, above, except the reply is only sent after the command is executed (successfully?).
Send-Explicit Reply: Same as (4), but each recipient specifies (either at registration time or in the command) whether it will simply observe the event or will reply to the event. Sender is still blocked until at least one reply is received; multiple replies could either cause an error or be ignored.
Send-Receive-Reply: Completely synchronous. The sender blocks until it receives a reply. Receivers explicitly call a receive procedure and block until they get a message. Reply is also an explicit procedure call. Again, the effects of multiple repliers may be either an error or a no-op.
Note: In numbers 5 and 6, the difference between treating multiple replies as an error or a no-op makes a major semantic difference. Those cases should probably be split up.
Initialization
When Fcl starts up, it calls event_Init as part of its initialization. The first thing it does is to call TT_Init, which really only calls TTInitializeSession (found in the file TTInterface.c). TTInitializeSession does the following:
Calls tt_open. tt_open is part of the ToolTalk API. It ensures that the ToolTalk server is running (and starts it running if it isn't). It also opens a connection to the server for the current process.
Calls tt_default_session. This tells the server which ToolTalk session the current process belongs to. By default, ToolTalk treats all processes using the same X display as members of the same session.
Initializes the global variables session_id and proc_id. These are strings which hold ToolTalk's representation of the current session and process. session_id is used later to insert patterns into the correct session. proc_id is never used, but may be useful for debugging.
Initializes the global variable event_fd with the stream descriptor which is connected to the ToolTalk server. When this file descriptor becomes active (determined through the use of select), a message is waiting to be received. This file descriptor will be passed to Tk so it can be checked during the main Tk event loop.
The next stage in initialization is the remainder of event_Init, which does the following:
Creates the Tcl command ``event''. When the command ``event'' is executed from a Tcl script, it will call the function event_EventDispatch, which will parse the parameters to figure out which C subroutine to call.
Initializes the two global hash tables callbackHashTable and patternHashTable. callbackHashTable contains a mapping from event names to Tcl command strings. When an event is received, this table provides the command to execute. patternHashTable contains a mapping from event names to ToolTalk patterns. These are initialized whenever a new event name is registered, and used only to unregister the event.
Calls Tk_CreateFileHandler to make Tk watch for incoming ToolTalk messages on the file descriptor event_fd. Whenever the file descriptor becomes active, Tk will call the procedure eventCallback.
At this point, the event system is fully set up and ready to be used.
Sending
When a Tcl script wants to send an event, the following things happen:
The script executes ``event broadcast <event name> ?arguments?''. This causes the procedure event_SendCmd to be invoked.
tt_message_create is called to create a message (which is now called an event, although I am horribly overloading the word event already).
TTInitEvent is called to initialize the fields of the ToolTalk message to the defaults for an event. This is performed through the ToolTalk procedures tt_message_<field>_set. For an event, the fields are set to:
op -- The name of the event
class -- TT_NOTICE. This means that the sender is merely publishing a notice, and does not care whether anyone receives it. The other option is TT_REQUEST, which causes tt_message_send to fail if no process handles the message.
scope -- TT_SESSION. This means that the event will be seen by everyone interested in the current session.
address -- TT_PROCEDURE. I don't remember exactly what the other values for this field do, but this is pretty much the `dummy' one -- it does what you would expect it to. Remember to change this comment.
<-- fix this number
If an argument was passed in, call tt_message_arg_add to insert it into the event.
Finally, call tt_message_send to ship the event off to the ToolTalk message server.
Receiving (use the new template everywhere, instead of starting halfway through)
To receive an event, a Tcl script executes the command ``event register <event name> <command>'', which causes event_RegisterCmd to be called. This does the following:
Checks the callbackHashTable to see if this event has already been registered. If so, returns an error.
Creates a new hash table entry keyed by the event name, and inserts the given callback command into it.
Creates a new ToolTalk pattern to match this event by calling tt_pattern_create and passing the result to TTInitPattern. This fills in the ToolTalk message fields to the necessary values:
op -- The name of the event to listen for.
category -- TT_OBSERVE, which means the receiever only observes the event, it doesn't handle it. Handling is a ToolTalk mechanism which is not used for asynchronous events.
class -- TT_CLASS_UNDEFINED. We want to receive events whether the sender regards them as notices or requests. Ignore this for now.
scope -- TT_SESSION. Ignore.
address -- TT_PROCEDURE. Ignore.
<-- fix this number too
Call TTRegisterPattern, which calls tt_pattern_register to tell the ToolTalk server that we want to receive messages which match the pattern we just built, and tt_session_join to add the current set of things we're registered for to the list of things scanned by the server for the current session. At least, I assume that's what it does.
Last, the newly-created pattern is added to the patternHashTable so that it can be unregistered sometime in the future.
The Tcl script now continues execution until an event arrives. Its arrival initializes the following sequence of actions:
Tk calls eventCallback when it notices that the event file descriptor has become active.
eventCallback calls tt_message_receive to read in the incoming message.
The callbackHashTable is indexed by the received event name to get the command to be executed for this event.
If the event contained an argument, the global variable ``eventArgs'' is set to the string contained in the received event.
The command is passed to Tcl to be executed at the global level.
If the execution resulted in a Tcl error, the procedure event_BackgroundError is called to disable further events, display an error dialog box, and re-enable incoming events after the user dismisses it.
Unregistration
Figure it out yourself. I'm running really short on time.