Motif/Lesstif Application Development

A tutorial designed to help you build your own GUI.
Exposing the Interface

Once the interface has been constructed, a call to XtRealizeWidget (line 215) for the application's top-level shell will cause all of the managed widgets in the hierarchy parented by that shell to display. Widgets can be constructed and destroyed at runtime. You are not required to create them all before exposing them; in fact, in some cases you will not have enough information to do so, but as much as possible of the main screen of the application should appear on startup.

The call to XtAppMainLoop (line 217) relinquishes control to the event dispatch loop. The only way for code in your application to execute from this point is in response to an event. Many events (such as expose and hide) are handled automatically by the Xt library. It is generally a good idea to assume XtAppMainLoop will never return. Applications commonly provide a routine to handle shutdown tasks such as closing files and cleaning up temporary workspaces. This routine would be called from the “Exit” menu item, for example.

The callback functions installed for the various widgets will actually give life to your application. Mundane tasks such as reacting to mouse button presses and keyboard input are all handled by the libraries supporting the instantiated widgets. This can make the application challenging to debug, since the flow of control is rather flexible, and it underscores the importance of careful design. Callbacks that call other functions invoking actions of other widgets—you get the idea. There is no easy way to automatically generate a call graph with this sort of implementation.

If your application needs to do something time consuming, it may be best to use a co-process, or at the very least, issue regular calls to XmUpdateDisplay (as on line 293). This will prevent the GUI from appearing locked. A number of different approaches for implementing progress bars and other user feedback are also available. You must remember that your application can react to the user's input only when it is not executing your code.

Callback Functions

Callbacks can be intimidating to deal with initially, but they are nothing more than pointers to functions placed in an event table. When the event for which they are registered occurs, the event dispatch loop invokes them with a few parameters. Since callbacks are passed limited state information, much of that information must be retrieved from the environment.

Callbacks are associated with events explicitly or implicitly. The XmVaCreateSimplePulldownMenu call on line 133 implicitly registers fileCB to be called whenever the user selects a menu item. The call to XtAddCallback on line 147 registers fileselCB to be called when the OK button in the file selection dialog box is pressed.

When Xt or Motif invokes a callback, it passes the widget for which the callback was registered, a single piece of client-specified data and a callback data structure specific to the widget and event for which the callback was invoked. When a callback is added to a widget via XtAddCallback, the final argument to the function is user-specified data of type XtPointer. This gives you a hook to pass information via pointers to structures. The user data element would then be cast back to the original type within the callback—be very careful here, unless you enjoy generating core files.

The call_data parameter is usually an instance of a callback structure derived from a basic callback structure—the first few elements in all of the Xm*CallbackStruct types are the same. For XmFileSelectionBox, the structure is defined as:

typedef struct
       int      reason;
       XEvent   * event;
       XmString value;
       int      length;
       XmString mask;
       int      mask_length;
       XmString dir;
       int      dir_length;
       XmString pattern;
       int      pattern_length;
     } XmFileSelectionBoxCallbackStruct;

The meaning of each element is explained in detail in the Motif Reference Manual, and I make use of the reason and value elements on lines 275 and 277. The reason tells you why the callback was invoked; this is especially important with something like the file selection box, since it is common to use the same callback for multiple events. The value member is the actual file that was selected (its complete path). A quick look at an instance of a file selection box on the screen will give you a good idea of how the other elements are used. If you are not sure about the type of callback structure at runtime, you should cast it to type XmAnyCallbackStruct. This at least has the reason and event members which can be used to determine the nature of the call.