Registering Procedures
Three primary methods exist for registering procedures for the receipt of events in an X Window application: callbacks, event handlers, and translations. Each method has the common purpose of providing the X Server a point of entry into the application at the occurrence of specific events. This is accomplished by invoking the registered procedure and passing as a parameter the appropriate event structure for processing.
Callbacks
Callbacks, which are provided by the internals of a widget, are function lists that are maintained by the widget for invocation when events that are consistent with the list's purpose are received from the X Server.
All callbacks are registered with the Xt function XtAddCallback. The function expects the following four parameters:
The widget in which the event must occur
-
The callback reason
-
A pointer to the procedure being registered (meaning one placed in the widget's function list for the specified reason)
Client data to pass to the procedure when it is invoked
Client data to pass to the procedure when it is invoked
Consider the following lines from Listing 1:
130: XtAddCallback( btn, XtNcallback, 131: callbackProc, data->func );
The widget specified in the XtAddCallback request is the newly created button. XtNcallback is the reason or function list in which the procedure pointer is stored. The final two parameters include a pointer to the callback function callbackProc (found in lines 54 through 65 of Listing 1), and the function pointer stored in the menuButtons array (in lines 30 through 41), which is passed as client data to callbackProc.
In defining a callback function for registration with the widget, it is crucial to honor the expected parameter list. Notice the three arguments expected by callbackProc:
54: static void callbackProc( Widget w, XtPointer _func, 55: XtPointer cbs )
As registered procedures are invoked from within the widget upon receipt of the appropriate event, the application programmer must conform to the standardized parameter list imposed by the authors of X.
The standard parameters expected by XtCallbackProc (third parameter to XtAddCallback) follows this form:
void function( Widget, XtPointer, XtPointer );
Similarly, other methods of registering procedures dictate the prototypes of the procedures registered.
This example accounts for the button press event that occurs in the button widget. However, what if the programmer desires notification when the mouse cursor enters or leaves the widget's window? Because the button widget does not inherently allow for procedure registration for these notification events, it is necessary to augment the widget by assigning an event handler.
Event Handlers
The addition of event handlers to augment a widget's functionality is provided by merit of the Xt layer of the X Window development environment. Any widget that is instantiated in an application may assign any number of event handlers by using the function XtAddEventHandler, as demonstrated in the following lines from Listing 1:
122: XtAddEventHandler(btn, EnterWindowMask, False, 123: (XtEventHandler)statusProc, 124: (XtPointer)data->message); 125: 126: XtAddEventHandler(btn, LeaveWindowMask, False, 127: (XtEventHandler)statusProc, 128: (XtPointer)NULL );
The two calls to XtAddEventHandler could be combined by OR-ing the event masks EnterWindowMask and LeaveWindowMask together using the pipe symbol (|). However, notice that the fifth parameter in the two invocations differ. Upon entering the window (EnterWindowMask), the statusProc function is passed the message field of the menuButton to display; when leaving the window, it is passed NULL, informing statusProc to clear the status field.
An event handler may be assigned to any widget using one or more of nearly two dozen event masks that are defined by X in the file X.h, which is found under Linux in the path /usr/include/X11/.
As with the registration of the XtCallbackProc described earlier, the Intrinsics layer of X invokes all event handlers that are assigned to the widget when it receives the specified event in the widget's window.
The difference between callbacks and event handlers should be clear. Callbacks are lists that are inherent to a widget and are used to associate procedures to events. Event handlers are less direct and they augment a widget's capability.
Translations are similar to event handlers, but are registered within the widget at a lower level. A translation constitutes an advanced method of requesting and processing events in an X Window application.
Translations
Every widget has a table that defines how to translate actions that occur in the widget's window. An example of a translation table for the commandWidget is shown here:
BSelect Press: Arm()BSelect Click: Activate(), Disarm()
In these lines, BSelect Press corresponds to a mouse press, which the translation table associates with the action Arm. The Arm action maps to a function inherent in the widget that is responsible for altering the colors of the widget, in order to make it appear as if the button has been pressed.
If the mouse is clicked (pressed and released), then the Activate and Disarm functions are called. The Activate function consults the XtNcallback list for procedures registered with the widget, invoking those it finds. The Disarm function is responsible for returning normal colors to the widget so that it no longer appears to be pressed.
The X Toolkit Intrinsics layer of X Window development allows the programmer to manipulate translation tables by optionally defining and installing new actions and translations. Consider the install_translations routine defined in Listing 1:
250: void install_translations( Widget w ) 251: { 252: /* define a new action table */ 253: static XtActionsRec actions[] = { 254: { "FollowCursor", (XtActionProc)actionProc }, 255: }; 256: /* define a new translation table */ 257: static char trans_tbl[] = 258: ": FollowCursor()\n"; 259: /* install the actions into the application 260: * context of the application 261: */ 262: XtAppAddActions(XtWidgetToApplicationContext(w), 263: actions, 264: XtNumber(actions)); 265: /* install the parsed translations in the 266: * widget specified 267: */ 268: XtVaSetValues( w, 269: XtNtranslations, 270: XtParseTranslationTable(trans_tbl), 271: NULL); 272: }
Three steps are necessary for the installation of new translations for a widget. First come the definition of an action such as “FollowCursor” and the registration of a function to invoke when the action occurs. The second step requires creating the interpreter or translation table for associating an event type to the new action. Finally, the translations are installed in the widget by assigning the result of the parsed translation table to the widget's internal table.
In this example, the widget’s entire translation is overwritten with a new one. It is often necessary to leave the widget's existing table intact and simply to complement it. In order to accomplish this, the function XtAugmentTranslations is used to install the new translations in the widget without affecting the existing table's contents.