A First Motif Program

  In this Chapter we will develop our first Motif program. The main purpose of the program is to illustrate and explain many of the fundamental steps of nearly every Motif program.

What will our program do?

Our program, push.c , will create a window with a single push button   in it. The button contains the string, ``Push Me''. When the button is pressed (with the left mouse button) a string is printed to standard output. We are not yet in a position to write back to any window that we have created. Later Chapters will explore this possibility. However, this program does illustrate a simple interface between Motif GUI and the application code.

The program also runs forever. This is a key feature of event driven processing. For now we will have to quit our programs by either:

In forthcoming Chapters (see also Exercise 4.1) we will see how to quit the program from within our programs.

The display of push.c on screen will look like this:


Fig. 4.1 Push.c display

What will we learn from this program?

As was previously stated, the main purpose of studying the program is to gain a fundamental understanding of Motif programming. Specifically the lessons that should be clear before embarking on further Motif programs are:

We now list the complete program code and then go on to study the code in detail in the remainder of this Chapter.

The push.c program


The complete program listing for the push.c program is as follows:

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

/* Prototype Callback function */

void pushed_fn(Widget , XtPointer , 
               XmPushButtonCallbackStruct *);

main(int argc, char **argv) 

{   Widget top_wid, button;
    XtAppContext  app;
    top_wid = XtVaAppInitialize(&app, "Push", NULL, 0,
        &argc, argv, NULL, NULL);

    button = XmCreatePushButton(top_wid, "Push_me", NULL, 0);

    /* tell Xt to manage button */
				/* attach fn to widget */
    XtAddCallback(button, XmNactivateCallback, pushed_fn, NULL);

    XtRealizeWidget(top_wid); /* display widget hierarchy */
    XtAppMainLoop(app); /* enter processing loop */ 


void pushed_fn(Widget w, XtPointer client_data, 
               XmPushButtonCallbackStruct *cbs) 
     printf("Don't Push Me!!\n");

Calling Motif, Xt and Xlib functions


When writing a Motif program you will invariably call upon both Motif and Xt functions and data structures explicitly. You will not always call Xlib functions or structures explicitly (but recall that Motif and Xt are built upon Xlib and they may call Xlib function from their own function calls).

In order to distinguish between the various toolkits, X adopts the following convention:

Header Files


In order to be able to use various Motif, Xt Intrinsics or Xlib data structures we must include header files that contain their definitions.

The X system is very large and there are many header files. Motif header files are found in #include<Xm/...> subdirectories, the Xt and Xlib header files in #include<X11/...> subdirectories (E.g. the Xt Intrinsic definitions are in #include<X11/Intrinsics.h>).

Every Motif widget has its own header file, so we have to include the
<Xm/PushB.h> file for the push button widget in push.c.

We do not have to explicitly include the Xt header file as <Xm/Xm.h> does this automatically. Every Motif program will include <Xm/Xm.h> -- the general header for the motif library.

Compiling Motif Programs

    To compile a Motif program we have to link in the Motif, Xt and Xlib libraries. To do this use: -lXm -lXt -lX11 from the compiler command line. NOTE: The order of these is important.

So to compile our push.c program we should do:

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

The exact compilation of your Motif programs may require other compiler directives that depend on the operating system and compiler you use. You should always check your local system documentation or check with your system manager as to the exact compilation directives. You should also check your C compiler documentation. For example you may need to specify the exact path to a nonstandard location of include (-I flag) or library (-L flag) files. Also our push.c program is written with ANSI style function calls and some compilers may require this knowledge explicitly. Some implementations of X/Motif do not strictly adhere to the ANSI C standard. In this case you may need to turn ANSI C function prototyping etc. off.

Having successfully complied you Motif program the command:


should successfully run the program and display the PushButton on the screen.

Basic Motif Programming Principles

Let us now analyse the push.c in detail. There are six basic steps that nearly all Motif programs have to follow. These are:

Initializing the toolkit
Widget creation
Managing widgets
Setting up events and callback functions
Displaying the widget hierarchy
Enter the main event handling loop

We will now look at each of these steps in detail.

Initialising the toolkit


The initialisation of the Xt Intrinsics toolkit must be the first stage of any basic Motif program.

There are several ways to initialise the toolkit. XtVaAppInitialize()  is one common method. For most of our programs this is the only one that need concern us.

When the XtVaAppInitialize() function is called, the following tasks are performed:

XtVaAppInitialize() has several arguments:

Application context
  (address of) -- This is a structure that Xt requires for operation. For the Motif programs that we will be considering we do not need to know anything about this, except the need to set it in our program.
Application class name
  -- A string that is used to reference and set resources common to the application of even a collection of applications. Chapter [*] deals with many resource setting mechanisms that uses this class name. In these coming examples note that the class name has been set to the string, ``Push''.
Command line arguments
   -- The third and fourth arguments specify a list of objects of the special X command line arguments that can be specified to an X program. The third argument is the list, the fourth the number in the list. This is advanced X use and is not considered further in this text. Just set the third argument to NULL and the fourth to 0. The fifth and sixth arguments &argc and argv contain the values of any command line argument given. These arguments may be used to receive command line input of data in standard C fashion (e.g. filenames for the program to read). Note that the command line may be used (Section [*]) to set certain resources in X. However these will have been removed from the argv list if they have been correctly parsed and acted upon before being passed on to the remainder of the program.
Fallback Resources
   -- Fallback resources provide security against errors in other setting mechanisms. Fallback resources are ignored, if resources are set by any other means. Chapter [*] deals with many resource setting mechanisms and Section [*] gives examples of setting fallback resources. A fallback resource is a NULL terminated list of Strings. For now we will simply set it to NULL as no fallback resources have been specified.
Additional Parameters
-- a NULL terminated list. These are also used only for advanced applications, so we will set them to NULL.

Widget Creation


There are several ways to create a widget in Motif:

We will introduce the convenience functions shortly but for now we will continue with the simpler first method of widget creation.

In general we create a widget using the function:  

XmCreate<widget name>().

So, to create a push button widget we use XmCreatePushButton() 

Most XmCreate<widget name>() functions take 4 arguments:

The argument list can be used to set widget resources (height, width etc.) at creation. The name of the widget may also be important when setting a widget's resources. The actual resources set depend on the class of the widget created. The individual Chapters and reference pages on specific widgets list widget resources. Chapter [*] deals with general issues of setting resources and explores the methods described here further.

Managing Widgets

    Once a widget has been created it will usually want to be managed.
XtManageChild()  is a function that performs this task.

When this happens all aspects of the widget are placed under the control of its parent. The most important aspect of this is that if a widget is left unmanaged, then it will remain invisible even when the parent is displayed. This provides a mechanism with which we can control the on screen visibility of a widget -- we will look at this in more detail in Chapter 9. Note that if a parent widget is not managed, then a child widget will remain invisible even if the child is managed.

Let us leave this topic by noting that we can actually create and manage a widget in one function called XtVaCreateManagedWidget(). This function can be used to create any widget. We will meet this function later in the Chapter [*].

Events and Callback Functions


Principles of Event Handling


When a widget is created it will automatically respond to certain internal events such as a window manager request to change size or colour and how to change appearance when pressed. This is because Xt and Motif frees the application program from the burden of having to intercept and process most of these events. However, in order to be useful to the application programmer, a widget must be able to be easily attached to application functions.

Widgets have special callback functions to take care of this.

An event  is defined to be any mouse or keyboard (or any input device) action. The effect of an event is numerous including window resizes, window repositioning and the invoking functions available from the GUI.

X handles events asynchronously  , that is, events can occur in any order. X basically takes a continuous stream of events and then dispatches them according to the appropriate applications which then take appropriate actions (remember X can run more than one program at a time).

If you write programs in Xlib then there are many low level functions for handling events. Xt, however, simplifies the event handling task since widgets are capable of handling many events for us (e.g. widgets are automatically redrawn and automatically respond to mouse presses). How widgets respond to certain actions is predefined as part of the widget's resources. Chapter 16 gives a practical example of changing a widget's default response to events.

Translation tables

     Every widget has a translation table that defines how a widget will respond to particular events. These events can enable one or more actions. Full details of each widgets response can be found in the Motif Reference material and manuals.

An example of part of the translation table for the push button is:

bBtn1downn:    Activate(), Disarm()BSelect Press: 		Arm()
BSelect Click: 		 Activate(), Disarm()

BSelect Press corresponds to a left mouse pressed down and the action is the Arm() function being called which cause the display of the button to appear as it was depressed. If the mouse is clicked (pressed and released), then the Activate() and Disarm() functions are called, which will cause the button to be reactivated.

Keyboard events can be listed in the table as well to provide facilities such as hot keys, function/numeric select keys and help facilities. These can provide short cuts to point and click selections.

Examples include: KActivate -- typically the return key, KHelp -- the HELP or F1 key.

Adding callbacks


The function Arm(), Disarm() and Activate() are examples of predefined callback functions.

For any application program, Motif will only provide the GUI. The Main body of the application will be attached to the GUI and functions called from various events within the GUI.

To do this in Motif we have to add our own callback functions.

In push.c we have a function pushed_fn() which prints to standard output.

The function XtAddCallback()  is the most commonly used function to attach a function to a widget.

It has four arguments:

In addition to performing a job like highlighting the widget, each event action can also call a program function. So, we can also hang functions off the arm, disarm etc. actions as well. We use XmNarmCallback, XmNdisarmCallback   names to do this.

So, if we wanted to attach a function quit() to a disarm for the button widget, we would write:

XtAddCallback(button, XmNdisarmCallback, quit, NULL);

Declaring callback functions

Let us now look at the declaration of the application defined callback function. All callback functions have this form.

void pushed_fn(Widget w, 
               XtPointer client_data, 
               XmPushButtonCallbackStruct *cbs)

The first parameter of the function is the widget associated with the function (button in our case).

The second parameter is used to pass client data to the function. We will see how to attach client data to a callback later. We do not use it in this example so just leave it defined as above for now.

The third parameter is a pointer to a structure that contains data specific to the particular widget that called the function and also information about the event that triggered the call.

The structure we have used is a XmPushButtonCallbackStruct . A Callback Structure  has the following general form:

typedef struct {
     int reason;
     XEvent *event;
     .... widget specifics ... } Xm<widget>CallbackStruct;

The reason element contains information about the callback such as whether arm or disarm invoked the call and the event element is a pointer to an (Xlib) XEvent  structure that contains information about the event.

Finishing off -- displaying widgets and event loops

We have nearly finished our first program. We have two final stages to perform which every Motif program has to perform. That is to tell X to:


Exercise 6697

  Write a Motif program that displays a button labelled ``Quit'' which terminates the program when the button is depressed with the left mouse button.

Dave Marshall