Features


X Window Programming

Part 6: Motif Programming

Eric F. Johnson and Kevin Reichard


Johnson and Reichard are authors of X Window Applications Programming, Advanced X Window Applications Programming, and Power Programming Motif (all MIS:Press). Johnson can be reached at erc@pai.mn.org via UUCP mail. Reichard's Compuserve address is 73670, 3422.

In our series on the X Window System, we've shown how hard X programming can be and how X toolkits can dramatically decrease the amount of code you must write, though at a cost of significantly increased code complexity and program executable size. It's up to you to decide whether the opportunity to play with expensive graphics workstations is worth the extra cost (but how else would you justify all that extra RAM and disk space?).

In Part 5 we covered a simple program using the Athena widget set. In this, our final installment, we'll show you how to implement the same program using a different widget set with a different look and feel, the Motif toolkit.

What Is Motif?

Motif is a toolkit for writing graphical applications in the X Window System. It is a widget set, and like the Athena widgets, it sits on top of the X Toolkit Intrinsics (Xt for short). For this reason Motif code will look a lot like the Athena code presented in the last installment (that is to say, convoluted).

Motif provides a 3D look-and-feel that makes Motif programs look dramatically better than Athena widget programs. Motif is a product of the Open Software Foundation, an industry body organized by Hewlett-Packard, Digital Equipment, and IBM, among others.

Motif comprises more than merely a widget set: it is a look-and-feel style (with a compliance certification process), a window manager, and the toolkit widget set. Like the Athena widget set, it provides a look and feel on top of the generic Xt Intrinsics.

What Is A Widget?

Widgets are C data structures — very convoluted data structures — that can be used as opaque types to create scroll bars, menus, and mouse-based interfaces. Widget sets are C libraries that sit on top of the Xt Intrinsics library, which, in turn, sits on top of the low-level X library (now you know why minimalist Motif programs typically run 700 Kbytes or larger).

Initializing The Xt Intrinsics

Since Motif sits on top of the Xt Intrinsics, the first step in working with it is to initialize the Xt library using XtInitialize () or XtAppInitialize().

#include <X11/Intrinsic.h>
parent = XtInitialize( shell_name,
        application_class,
        xrm_options,
        number_options,
        argc, argv );
Widget             parent;
String             shell_name;
String             application_class;
XrmOptionDescRec   xrm_options [];
Cardinal           number_options;
Cardinal           *argc;  /* note indirection */
String             argv [];
We've used XtInitialized(), because XtAppInitialize() is new with X11 Release 4 and many of you won't have this function available. If your system is at X Release 3 (or Motif 1.0), use XtInitialize(); if it's X11 Release 4 and Motif 1.1, use XtAppInitialize(). If you have to support both older R3-based systems and R4, we suggest using XtInitialize() since it works under both.

#include <stdio.h>
#include <X11/Intrinsic.h>

main( argc, argv )

int     argc;
char    *argv [];

{
       Widget  parent;
       ...
       parent = XtInitialize( argv[0],
               "Motif1",
               NULL, 0,
               &argc, argv );
       ...
}
At this point we've avoided using any Motif functions; instead, we've stayed with Xt functions because Motif makes heavy use of the Xt Intrinsics library. In fact, we'll use only a few Motif library functions in the sample program.

For simplicity, we skip the Xrm (X Resource Manager) options. Motif1 is the application's class name. The class name is used for finding application-specific customizations (called resources) and is usually the application name (in this case we call our program motif1), with an upper-case first letter (and sometimes an upper-case second letter as well).

Once you've initialized the Xt Intrinsics, you get back a top-level shell widget which serves as the highest parent for all of your application's widgets. Since every widget you create must have a parent, your next level of widgets will use this as their parent widget. In our sample program we'll create three simple widgets.

Motif include Files

The Motif include files are usually in the directory/usr/include/Xm, while the standard X Window include files are usually in/usr/include/X11. Just about every Motif program will include <Xm/Xm.h>, as well as the Xt include files <X11/Intrinsic.h> and <X11/StringDefs.h>. Most Motif widgets have an extra widget-based include file, too, including the label widget, which uses <Xm/Label. h>.

The Motif Label Widget

Like the Athena label widget, the Motif label widget (also called XmLabel) displays some text in a window. We use an Xt function, XtCreateManagedWidget() to create and manage a widget.

String          name;
WidgetClass     widgetclass;
Widget          parent;
ArgList         args;
Cardinal        number_args;

Widget XtCreateManagedWidget( name,
        widgetclass,
        parent,
        args,
        number_args );
For the Motif label widget, we pass a class of xmLabelWidgetClass. We could also use a Motif function called XmCreateLabel(), but XtCreateManagedWidget() is easier (and we used it in our last program).

#include <Xm/Xm.h>
#include <Xm/Label.h>

Widget      label_widget, parent_widget;
Arg         args [20];
int         n;
XmString    motif_string;

n = 0;
XtSetArg( args[n], XmNlabelString, motif_string );
n++;

/* Create a label widget */
label_widget = XtCreateManagedWidget ("label",
             xmLabel_WidgetClass,
             pane_widget, args, n );

Setting Widget Options

We use XtSetArg() to set up options before we create the label widget. XtSetArg() sets one option (argument) for a widget, in an Arg structure.

XtSetArg() takes three parameters: an Arg structure, a name for the resource being set, and a value. You normally needn't worry about these type definitions; instead, just follow the conventions when calling XtSetArg() (though this makes for some strange-looking code).

n = 0;
XtSetArg( args[n], XmNlabelString, motif_string );
n++;
The macro XtSetArg() fills in one element of an Arg array (or just an Arg struct) with a name — (XmNlabelString, which resolves to labelString) and a value — motif_string, an XmString.

Text Strings And Motif

As you may have guessed by looking at the code above, Motif normally doesn't use traditional C strings (a pointer to a character array with a NULL terminator), but instead uses something called an XmString, a Motif creation for dealing with multiline and perhaps multilingual text. Unfortunately, what this means is that the Motif programmer does a lot of work translating strings into the XmString format and back again to the traditional C format. To make matters worse, the XmString conversion routines can be terribly inefficient.

To create an XmString out of a C string, we use XmStringCreateLtoR(). The LtoR (for left to right) means that for every newline, '\n', in our string, there will be the XmString equivalent of a new line marker, making for multiline text.

XmString  motif_string;
char  c_string [100];

/* Set up XmString for
   label widget */
motif_string =
XmStringCreateLtoR( c_string,
     XmSTRING_DEFAULT_CHARSET );
Now we get our first true Motif function (notice that Motif functions, data types, and constants tend to start with Xm). We use the Motif default charset, XmSTRING_DEFAULT_CHARSET, since we assume that most of our readers use western alphabets.

We free an XmString when done, using another Motif library function, XmStringFree().

XmStringFree( motif_string );

The Motif Pushbutton Widget

The Motif pushbutton widget is almost the same as the Athena Command widget introduced last time. This widget will call back one of your functions whenever the user clicks the mouse in the widget. To create a pushbutton widget, we use XtCreateManagedWidget (), with a class of xmPushButtonWidgetClass.

Widget  parent, quit_widget;
Arg     args [20];
int     n;

n = 0;
quit_widget = XtCreateManagedWidget ( "quit",
            xmPushButtonWidgetClass,
            pane_widget, args, n );

Motif Callback Functions

Pushbutton widgets aren't very useful unless function has been set up to be called back (hence callback function) whenever the pushbutton is activated, that is, when the user clicks a mouse button in the pushbutton widget. To set up a callback, we call XtAddCallback ().

Widget          widget;
String          callback_name;
XtCallbackProc  callback_function;
XtPointer       client_data;

XtAddCallback( widget,
     callback_name,
     callback_function,
     client_data );
Motif uses different names for its callbacks. In this case, we're using the "activate" callback, so we pass XmNactivateCallback and the address of our function.

XtAddCallback( quit_widget,
             XmNactivateCallback,
      quit_callback, (caddr_t) NULL );
Our callback function, quit_callback(), receives three parameters from the X toolkit:

void quit_callback( widget,
        client_data, call_data )

Widget widget;
caddr_t client_data;
caddr_t call_data;

{     /* quit_callback */
}     /* quit_callback */

Using The Paned Window Widget To Hold Other Widgets

If you create more than one widget, you'll need to create some form of "container" to hold your widgets, keep them in place, and figure out what to do when the user changes the size of the application's main window. Container widgets generally control the behavior of their children, the child widgets.

The paned window widget is a simple Motif container widget that divides an area (in this case our whole application window) up into "panes," going across the window. The panes start at the top and work their way down to the bottom. Generally, there's a pane for each child of the paned window widget. The panes may also have, if you configure them, "sashes" — lines with a little box (a place to grab on to with the mouse) near the right side. These sashes allow the user to resize any of the child panes. The Motif paned window widget acts almost the same as the Athena paned widget (described in Part 5).

We create a paned window widget with a class of xmPanedWindowWidgetClass.

n = 0;
XtSetArg( args[n], XmNseparatorOn, True ); n++;
XtSetArg( args[n], XmNallowResize, True ); n++;

pane_widget = XtCreateManagedWidget( "pane",
             xmPanedWindowWidgetClass,
             parent, args, n );
This time, though, we need to set up two options in an Arg array before we create the paned window widget. First, we set the XmNseparatorOn resource (really separatorOn) to True, so that a "sash" appears to separate the child widgets of the paned window.

Then we also set the XmNallowResize resource (really allowResize) to True, which allows the user to resize our paned window.

Remember that the paned window widget must be created first, as a child of the top-level shell widget returned from XtInitialize(). Then, the label and pushbutton widgets can be created as children of the paned window.

Realizing Widgets And The Event Loop

We've shown that with X library programs nothing appears when you first create a window. To get something to appear, you have to map that window to the screen. This process is also necessary for X toolkit programs, but the process here is called "realizing" rather than mapping. Use XtRealizeWidget() to realize a widget and all of its managed children (all the children of our first parent widget are managed, since we used XtCreateManagedWidget () to create them).

Widget parent;
XtRealizeWidget( parent );

The Widget Event Loop

When you've created and realized all your widgets, call XtMainLoop() to handle all loops. (If you have Release 4, you'll probably want to use the newer XtAppMainLoop(), instead of XtMainLoop().) This function takes over your application and loops on events — forever. As each event comes in from the X server (or other input source), XtMainLoop() dispatches the event to the proper event handler for whatever widget the event occurred in. XtMainLoop() never exits, so you need to call exit() from a callback function to gracefully terminate your program, which we do in quit_callback().

A Motif Program

In Listing 1, we've included a short Motif program that does everything we've covered this issue. This program is also very close to the Athena widget program of part 5. (We did this on purpose so you could compare the two toolkits and see how close they really are.)

Compiling And Linking Motif Programs

Motif programs need the Motif library, Xm; the Xt Intrinsics library, Xt; and the X library, X11. Depending system, other libraries, especially networking libraries, may also be required. Most UNIX-based systems will compile the example program with a command like

cc -o motif1 motif1.c -lXm -lXt -lX11

Problems

If you are running Motif 1.0 programs in a Release 4 X server, you may have problems when resizing the panes in the paned window widget (we know, we found out the hard way). Generally, your program will die with an X error. To get around this, you need to enable the X server's bug compatibilty mode, using the xset program:

xset bc

Where To Get Motif

Motif is standard equipment on many systems, including those from Data General, HP, and the Santa Cruz Operation. This is by far the easiest way to get Motif, since no special effort is required.

Officially, Motif is a product of the Open Software Foundation (OSF), a consortium including IBM, DEC, and Hewlett-Packard, and you can license the Motif source code directly from the OSF, generally for $1,000. If you are writing commercial Motif applications, it's likely that you'll want to license the Motif source code. OSF can be contacted at:

Open Software Foundation
11 Cambridge Center
Cambridge, MA 02142 USA
+1 617 621 8700

Other vendors such as Quest and ICS also market Motif binaries, especially for Sun SPARCStations, since Sun Microsystems pushes Open Look, a competing X Window interface and libraries, over Motif.

For More Information On Programming Motif

Johnson, Eric F. and Kevin Reichard, Power Programming Motif, MIS: Press, Portland, OR, 1990. ISBN 1-55828-059-6.

Nye, Adrian and Tim O'Reilly, X Toolkit Intrinsics Programming Manual, Motif Edition, O'Reilly and Assoc., Sebastopol, CA, 1991. ISBN 0-937175-62-5.

Young, Douglas A., The X Window System: Programming and Applications with Xt, OSF/Motif Edition, Prentice Hall, Englewood Cliffs, NJ, 1990. ISBN 0-13-497074-8.

Listing 2