This resource setting causes each Label widget to have a foreground color of red, regardless of the name of the widget
(and provided that the resource value is not hard−coded for the widget). See Volume Four, X Toolkit Intrinsics
Programming Manual, for a discussion of appropriate widget names and further details on resource specification
syntax.
3.3.6 Event Handling for Widgets
Once we have created and configured the widgets for an application, they must be hooked up to application functions
via callback resources. Before we can talk about callback resources and callback functions, we need to discuss events
and event handling. In one sense, the essence of X programming is the handling of asynchronous events. Events can
occur in any order, in any window, as the user moves the pointer, switches between the mouse and the keyboard,
moves and resizes windows, and invokes functions available through user interface components. X handles events by
dispatching them to the appropriate application and to the separate windows that make up each application.
Xlib provides many low−level functions for handling events. In special cases, which are described later in this book,
you may need to dip down to this level to handle events. However, Xt simplifies event handling by having widgets
handle many events for you, without any application interaction. For example, widgets know how to redraw
themselves, so they respond automatically to WExpose events, which are generated when one window is covered up
by another and then uncovered. These "widget survival skills" are handled by functions called methods deep in the
widget internals. Some typical methods redraw the widget, respond to changes in resource settings that result from
calls to XtVaSetValues(), and free any allocated storage when the widget is destroyed.
The functionality of a widget also encompasses its behavior in response to user events. This type of functionality is
typically handled by action routines. Each widget defines a table of events, called a translation table, to which it
responds. The translation table maps each event, or sequence of events, to one or more actions.
Consider the PushButton in hello.c. Run the program and note how the widget highlights its border as the pointer
moves into it, displays in reverse−video when you click on it, and switches back when you release the button. Watch
how the highlighting disappears when you move the pointer out of the widget. Also, notice how pressing the
SPACEBAR while the pointer is in the widget has the same effect as clicking on it. These behaviors are the kinds of
things that are captured in the widget's translation table:
<Btn1Down>: Arm()
<Btn1Down>,<Btn1Up>: Activate() Disarm()
<Btn1Down>(2+): MultiArm()
<Btn1Up>(2+): MultiActivate()
<Btn1Up>: Activate() Disarm()
<Btn2Down>: ProcessDrag()
<Key>osfSelect: ArmAndActivate()
<Key>osfActivate: PrimitiveParentActivate()
<Key>osfCancel: PrimitiveParentCancel()
<Key>osfHelp: Help()
~Shift ~Meta ~Alt <Key>Return: PrimitiveParentActivate()
~Shift ~Meta ~Alt <Key>space: ArmAndActivate()
<EnterWindow>: Enter()
<LeaveWindow>: Leave()
The translation table contains a list of event translations on the left side, with a set of action functions on the right
side. When an event specified on the left occurs, the action routine on the right is invoked. As we just described,
moving the pointer in and out of the PushButton causes some visual feedback. The EnterWindow and
LeaveWindow events generated by the pointer motion cause the Enter() and Leave() actions to be invoked.
3 The Motif Programming Model 3.3.6 Event Handling for Widgets
37
As another example, when the first mouse button is pressed down inside the PushButton, the Arm() action routine is
called. This routine contains the code that displays the button as if it were "pushed in," as opposed to "pushed out."
When the mouse button is released, both the Activate() and Disarm() routines are invoked in that order. Here
is where your application actually steps in. If you have provided an appropriate callback function, the Activate()
action calls it. The Disarm() routine causes the button to be redrawn so that it appears "pushed out" again. In the Xt
syntax, events are specified using symbols that are tied fairly closely to pure X hardware events, such as
ButtonPress or EnterWindow. For example, <Btn1Down> specifies a button press for the first mouse button.
KeyPress events are indicated by symbols called keysyms, which are hardware−independent symbols that represent
individual -keystrokes. Different keyboards may produce different hardware keycodes for the same key; the X server
uses keysyms as a portable representation, based on the common labels found on the tops of keys.
Motif provides a further level of indirection in the form of virtual keysyms, which describe key events in a completely
device−independent manner. For example, osfActivate indicates that the user invoked an action that Motif
considers to be an activating action. An activating action typically corresponds to the RETURN key being pressed or
the left mouse button being clicked. Similarly, osfHelp corresponds to a user request for help, such as the HELP or
F1 key being pressed.
Virtual keysyms are supposed to be provided by the vendor of the user's hardware, based on the keys on the keyboard,
but some X vendors also provide keysym databases to support multiple keyboards. As of X11 Releaase 5, the X
Consortium provides a virtual keysym database in the file /usr/lib/X11/XKeysymDB. This file contains a number of
predefined key bindings that OSF has registered with the X Consortium to support actions in the Motif toolkit.
Virtual keysyms can be invoked by physical events, but the Motif toolkit goes one step further and defines them in the
form of virtual bindings. Here's the translation table for the PushButton widget expressed using virtual bindings:
BSelect Press: Arm()
BSelect Click: Activate() Disarm()
BSelect Release: Activate() Disarm()
BSelect Press 2+: MultiArm()
BSelect Release 2+: MultiActivate() Disarm()
BTranserPress: ProcessDrag()
KSelect: ArmAndActivate()
KHelp: Help()
Examples of virtual bindings are BSelect, which corresponds to the first mouse button, and KHelp, which is
usually the HELP key on the keyboard. The rule of thumb is that any virtual binding beginning with a "B"
corresponds to a mouse button event, while any binding beginning with a "K" corresponds to a keyboard event. More
than one event can be bound to a single virtual keysym. For example, the Motif Style Guide permits F1 to be a help
key, so that key is also virtually bound to KHelp.
Virtual bindings can be specified by a system administrator, a user, or an application. One common use of virtual
bindings is to reconfigure the operation of the BACKSPACE and DELETE keys. On some keyboards, the
BACKSPACE key is in a particularly difficult location for frequent access. Users of this type of keyboard may prefer
to use the DELETE key for backspacing. These people may find the default operation of the Motif Text widget
annoying, since it does not allow them to backspace using their "normal" backspace key.
Since Xt allows applications and users to override, augment, or replace translation tables, many people familiar with
Xt try to specify a new translation for the DELETE key to make it act like a backspace. The translation invokes the
action routine that backspaces in a Text widget. However, this approach is limited, in that it only works for a single
Text widget. The Text widget has the following translation:
<Key>osfBackSpace: delete−previous−char()
3 The Motif Programming Model 3.3.6 Event Handling for Widgets
38
The virtual keysym osfBackSpace is bound to delete−previous−char(), which is the backspace action.
Rather than changing the translation table to specify that <Key>Delete should invoke this action, a user can
redefine the virtual binding of the osfBackSpace keysym. A user can configure his own bindings by specifying the
new virtual keysym bindings in a .motifbind file in his home directory. The following virtual binding specifies that the
DELETE key is mapped to osfBackSpace:
osfBackSpace : <Key>Delete
As a result of this specification, the DELETE key performs the backspace action in the Text widget, as well as any
other widgets in the Motif toolkit that use the osfBackSpace keysym. The advantage of using virtual bindings is
that the interface remains consistent and nothing in the toolkit or the application needs to change.
Virtual keysym bindings can also be set in a resource file, using the XmNdefaultVirtualBindings resource.
The resource can be specified for all applications or on a per−application basis. To map the DELETE key to
osfBackSpace, use the following specification:
*defaultVirtualBindings: osfBackSpace : <Key>Delete 0 other bindings
The only difference between the syntax for the resource specification and for the .motifbind file is that the resource
specification must have a newline character (\n) between each entry. The complete syntax of Motif virtual bindings is
explained in Volume Six B, Motif Reference Manual.
Motif 1.2 includes a new client, xmbind, that configures the virtual key bindings for Motif applications. This action is
performed by the Motif Window Manager (mwm) or any application that uses the Motif toolkit at startup, so you
really only need to use xmbind if you want to reconfigure the bindings without restarting mwm or a Motif application.
Motif 1.2 also provides a new function, XmTranslateKey(), to translate a keycode into a virtual keysym. This
function allows applications that override the default XtKeyProc to handle Motif's virtual key bindings. Translations
and actions allow a widget class to define associations between events and widget functions. A complex widget, such
as the Motif Text widget, is almost an application in itself, since its actions provide a complete set of editing
functions. But beyond a certain point, a widget is helpless unless control is passed from the widget to the application.
A widget that expects to call application functions defines one or more callback resources, which are the hooks on
which an application can hang its functions. For example, the PushButton widget defines the
XmNactivateCallback, XmNarmCallback, and XmN-disarmCallback callback resources.
It is no accident that the callback resource names bear a resemblance to the names of widget action routines. In
addition to highlighting the widget, the action routines call any application functions associated with the callbacks of
the same name. There is no reason why a callback has to be called by an action; a widget could install a low−level
event handler to perform the same task. However, this convention is followed by most widgets.
the figure illustrates the event−handling path that results in an application callback being invoked. The widget's
translation table registers the widget's interest in a particular type of event. When Xt receives an event that happened
in the widget's window, it tests the event against the translation table. If there is no match, the event is thrown away. If
there is a match, the event is passed to the widget and an action routine is invoked. The action routine may perform a
function internal to the widget, such as changing the widget's appearance by highlighting it. Depending on the design
of the widget, the action routine may then pass control to an application callback function. If the action is associated
with a callback resource, it checks to see if a callback function has been registered for that resource, and if so, it
dispatches the callback.
3 The Motif Programming Model 3.3.6 Event Handling for Widgets
39
Event−handling using action routines and callbacks
There are several ways to connect an application function to a callback resource. The most common is to call
XtAddCallback(), as demonstrated in hello.c:
void button_pushed();
...
XtAddCallback(button, XmNactivateCallback, button_pushed, NULL);
The first argument specifies the widget for which the callback is installed. The second parameter is the name of the
callback resource, while the third is a pointer to the callback function. The fourth argument is referred to as client
data. If this parameter is specified, its value is passed to the callback function when it is called. Here, the client data is
NULL.
3 The Motif Programming Model 3.3.6 Event Handling for Widgets
40
The client data can be a value of any type that has the same size as an XtPointer. An XtPointer is usually the
same as a char pointer; it is typically represented by a 32−bit value. You can pass pointers to variables, data
structures, and arrays as client data. You cannot pass actual data structures; the result of passing a data structure is
undefined. You can pass variables of type int or char, but understand that you are passing the data by value, not by
reference. If you want to pass a variable so that the callback routine can change its value, you must pass the address of
the variable. In this case, you need to make sure that the variable is global, rather than local, since a local variable
loses its scope outside of the routine that calls XtAddCallback().
The callback function itself is passed the widget, the client data, if any, and a third argument that is referred to as call
data. The signature of a callback function can be expressed in one of two ways: using an ANSI−compliant function
prototype or using the older style conventions of K&R C. The ANSI−style function declaration is as follows:
button_pushed (Widget widget, XtPointer client_data, XtPointer call_data)
In the strictest sense, declaring the types of the parameters to the function is the proper way to handle function
declarations and signatures. While this convention is good style and recommended for upwards compatibility, most
compilers today still understand the older style conventions:
button_pushed (widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
Since this style is the least common denominator, your best bet is to use the second, more portable method. In the
course of the book, we make a habit of declaring client_data and call_data as XtPointers, even though
we usually know the actual types of the parameters being passed to the function. Before referencing these parameters,
we cast the values to the appropriate types.
The third parameter in a Motif−based callback function is always a structure that contains information specific to the
widget class that invoked the callback function, as well as information about the event that triggered the callback.
There is a generic callback structure, XmAnyCallbackStruct, as well as variations for specific widget classes and
callback resources. The XmAnyCallbackStruct is defined as follows:
typedef struct {
int reason;
XEvent *event;
} XmAnyCallbackStruct;
The callback structure for the PushButton widget class, XmPushButtonCallbackStruct, is defined as follows:
typedef struct {
int reason;
XEvent *event;
int click_count;
} XmPushButtonCallbackStruct;
We discuss the callback structures for a widget class in this book (see the chapter corresponding to the specific widget
type). The callback structures are also documented in the widget reference pages in Volume Six B, Motif Reference
Manual.
All of the callback structures contain at least the two fields found in XmAnyCallbackStruct. The reason field
always contains a symbolic value that indicates why the callback was called. These values are defined in
/usr/include/Xm/Xm.h and are usually self−explanatory. For example, when a callback function associated with a
3 The Motif Programming Model 3.3.6 Event Handling for Widgets
41
Get Volume 6A: Motif Programming Manual now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.