Next: Working with mutiple Up: Motif Tutorial Previous: Core Widget

Callbacks

Once you understand resources, you are half-way there in terms of understanding Motif programming. The other half is called "call backs". Many widgets have callbacks. They are procedures that are called by a widget whenever certain events occur for the widget.

In order to understand callbacks, we are going to have to change the program we have been using so that it contains a widget capable of producing a callback in response to a user event. [A label widget doesn't produce any callbacks in response to user events, but like all other widgets it does produce a callback when it is destroyed.] A pushbutton widget is probably the simplest widget that does this, so we will use it. The description of a PushButton widget is given at the end of this tutorial. Take a second to quickly look through the description. Notice that you have to use the PushB.h include file for this widget. Also notice that the superclass is a label widget, so the pushbutton inherits all label resources (and callbacks, if there were any).

To try the pushbutton, type in and execute the following code. When you execute it, try pushing the push button labeled "push me" a few times.


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

        main(int argc, char *argv[])
        {
          Widget toplevel, button;
          Arg al[10];
          int ac;

          toplevel=XtInitialize(argv[0],"",NULL,0,&argc,argv);

          ac=0;
          XtSetArg(al[ac],XmNlabelString,
                XmStringCreate("Push Me",
                XmSTRING_DEFAULT_CHARSET)); ac++;
          button=XtCreateManagedWidget("button",xmPushButtonWidgetClass,
                toplevel,al,ac);

          XtRealizeWidget(toplevel);
          XtMainLoop();
        }

This code is very similar to the very first program that we used, except that the label widget is replaced by a pushbutton widget. When you ran it, clicking on the pushbutton should have caused the button to highlight. In order to DO anything with the pushbutton though, we need to make use of it's callbacks.

The idea behind a callback is that you create a procedure, and tell the widget to call that procedure whenever an event triggers the callback. An event might be, in this case, the user pushing the button. To demonstrate this, we will modify the above code so that it will print the word "ouch" to stdout whenever the button is pushed. Here's the new code:


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

        void handle_button(Widget w,
                           caddr_t client_data,
                           XmAnyCallbackStruct *call_data)
        {
          printf("ouch\n");
        }

        main(int argc, char *argv[])
        {
          Widget toplevel, button;
          Arg al[10];
          int ac;

          toplevel=XtInitialize(argv[0],"",NULL,0,&argc,argv);

          ac=0;
          XtSetArg(al[ac],XmNlabelString,
                XmStringCreate("Push Me",
                XmSTRING_DEFAULT_CHARSET)); ac++;
          button=XtCreateManagedWidget("button",xmPushButtonWidgetClass,
                toplevel,al,ac);

          XtAddCallback(button,XmNactivateCallback,handle_button,NULL);

          XtRealizeWidget(toplevel);
          XtMainLoop();
        }

This is an extremely simple example. We will look at why this works, and then look at extensions to the idea. Some questions that you might have right now are, "What does call_dataand client_datado?" and "what do the other two callbacks for the pushbutton do?".

This program starts by setting up the pushbutton widget, as in the first program. The new XtAddCallback line is the only difference. This tells Motif to cause the button widget, whenever its "activate" callback is triggered, to call the procedure named handle_button. Now when the user clicks the button, handle_buttonis called. The handle_buttoncallback procedure simply prints out the word "ouch" to stdout.

Any callback procedure receives three pieces of information when it is called. The first parameter is the widget that triggered the callback, the second is a peice of programmer-defined data (it can be anything that fits in 4 bytes - an integer, a pointer, etc.), and the third is a pointer to a "callback structure", which contains an integer holding the reason for the callback and the complete event structure describing the event (an XEvent structure). Here's the description of the XmAnyCallbackStruct:


        typedef struct {
                int reason;
                Xevent *event;
        } XmAnyCallbackStruct;

Generally you ignore this whole thing, but sometimes it contains useful information. The "reason" integer is sometimes useful if multiple callbacks are activating the same procedure.

The client_datafield can be extremely useful at times, either to differentiate multiple callbacks to the same procedure, or to send in a pointer to a record that contains a bunch of infomation that the callback will need. The code below demonstrates the "differentiation" capability of the client_datafield, and will also help you to learn the difference between an arm, disarm and activate event.


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

        void handle_button(Widget w,
                           int client_data,
                           XmAnyCallbackStruct *call_data)
        {
          switch (client_data)
          {
            case 1: printf("activate\n");
            case 2: printf("arm\n");
            case 3: printf("disarm\n");
          }
        }

        main(int argc, char *argv[])
        {
          Widget toplevel, button;
          Arg al[10];
          int ac;

          toplevel=XtInitialize(argv[0],"",NULL,0,&argc,argv);

          ac=0;
          XtSetArg(al[ac],XmNlabelString,
                XmStringCreate("Push Me",
                XmSTRING_DEFAULT_CHARSET)); ac++;
          button=XtCreateManagedWidget("button",xmPushButtonWidgetClass,
                toplevel,al,ac);

          XtAddCallback(button,XmNactivateCallback,handle_button,1);
          XtAddCallback(button,XmNarmCallback,handle_button,2);
          XtAddCallback(button,XmNdisarmCallback,handle_button,3);

          XtRealizeWidget(toplevel);
          XtMainLoop();
        }

This code is very similar to the previous code, except that now an integer piece of data is being passed to the callback routine (yes, I should have #defined constants, but this makes it more obvious). In the callback, the client_datais used to guide a switch statement.

This code should also let you find out what the difference between arm, disarm and activate are. I don't understand it, but maybe you will. I've only ever found the activate callback to be useful myself.

You can declare client_datato be any type. For example, say you have a struct called data, of type "struct data_typedata", and you want to pass the thing as client_data. You can use the following line when creating the callback:


          XtAddCallback(button,XmNactivateCallback,handle_button,&data);


\paragraph{}
The client\_data parameter in the callback would be declared as 
"struct data\_type *client\_data", and the client data would be used in the
callback by referring to "client\_data->fieldname".

\paragraph{}
This should be enough for you to understand callbacks. In the next two
tutorials we'll look at a few other necessary pieces of knowledge, and then 
get around to actually creating an application.

-----------------------------------------------------------------------------

\section{PushButton Widget}

\begin{verbatim}
Class Pointer:  xmPushButtonWidgetClass
Class Name:     XmPushButton
Include File:   <Xm/PushB.h>
Superclass:     Xmlabel
Description:
        Lets user issue a command by pushing a button.

Resources:
Name                    Type                    Default
---------------------   ---------------------   ------------------------
XmNfillOnArm            XmRBoolean              TRUE
XmNarmColor             XmRPixel                dynamic
XmNarmPixmap            XmRPrimForegroundPixmap XmUNSPECIFIED_PIXMAP
XmNshowAsDefault        XmRShort                0
XmNshadowThickness      XmRShort                2

Callbacks:
Callback List           Call Data Type          Reason
---------------------   ---------------------   -------------------------
XmNactivateCallback     XmAnyCallbackStruct     XmCR_ACTIVATE
XmNarmCallback          XmAnyCallbackStruct     XmCR_ARM
XmNdisarmCallback       XmAnyCallbackStruct     XmCR_DISARM



Next: Working with mutiple Up: Motif Tutorial Previous: Core Widget


morbe@enstb.enst-bretagne.fr