Next | Prev | Up | Top | Contents | Index

Input Handling With Widgets and Xt

This section explains how to perform input handling with widgets and Xt. It covers:


Background Information

Motif programs are callback driven. They differ in that respect from IRIS GL programs, which implement their own event loops to process events. To handle input with a widget, you can either use the input callback built into the widget or use actions and translations (Xt-provided mechanisms that map keyboard input into user-provided routines). Both approaches have advantages:

Note: To allow smooth porting to other systems, as well as for easier integration of X and OpenGL, always separate event handling from the rest of your program.


Using the Input Callback

By default, the input callback is called with every key press and release, with every mouse button press and release, and whenever the mouse is moved while a mouse button is pressed. You can change this by providing a different translation table, although the default setting should be suitable for most applications.

For example, to have the input callback called on all pointer motions, not just on mouse button presses, add the following to the app-defaults file:

appname*widgetname.translations : \
    <KeyDown>:      glwInput() \n\
    <KeyUp>:        glwInput() \n\
    <BtnDown>:      glwInput() \n\
    <BtnUp>:        glwInput() \n\
    <BtnMotion>:    glwInput() \n\
    <PtrMoved>:     glwInput()
The callback is passed an X event. It interprets the X events and performs the appropriate action. It's your application's responsibility to interpret the event--for example, to convert an X keycode into a key symbol--and to decide what to do with it.

Example 3-1 is from motif/mouse.c, a double-buffered RGBA program that uses mouse motion events.

Example 3-1 : Motif Program That Handles Mouse Events

static void
input(Widget w, XtPointer client_data, XtPointer call) {
   char buffer[31];
   KeySym keysym;
   XEvent *event = ((GLwDrawingAreaCallbackStruct *) call)->event;
   static mstate, omx, omy, mx, my;

   switch(event->type) {
   case KeyRelease:
      XLookupString(&event->xkey, buffer, 30, &keysym, NULL);
      switch(keysym) {
      case XK_Escape:
         exit(EXIT_SUCCESS);
         break;
      default: break;
      }
      break;
    case ButtonPress:
        if (event->xbutton.button == Button2) {
            mstate |= 2;
            mx = event->xbutton.x;
            my = event->xbutton.y;
        } else if (event->xbutton.button == Button1) {
            mstate |= 1;
            mx = event->xbutton.x;
            my = event->xbutton.y;
        }
        break;
    case ButtonRelease:
        if (event->xbutton.button == Button2)
            mstate &= ~2;
        else if (event->xbutton.button == Button1)
            mstate &= ~1;
        break;
    case MotionNotify:
        if (mstate) {
            omx = mx;
            omy = my;
            mx = event->xbutton.x;
            my = event->xbutton.y;
            update_view(mstate, omx,mx,omy,my);
        }
        break;
   }

Using Actions and Translations

Actions and translations provide a mechanism for binding a key or mouse event to a function call. For example, you can set things up so that

The translations need to be combined with an action task that maps string names like quit() to real function pointers. Below is an example of a translation table:

program*glwidget*translations:      #override \n
    <Btn1Down>:        start_rotate()   \n\
    <Btn1Up>:          stop_rotate()    \n\
    <Btn1Motion>:      rotate()         \n\
    <Key>f:            zoom_in()        \n\
    <Key>b:            zoom_out()       \n\
    <KeyUp>osfCancel:  quit()
When you press the left mouse button, the start_rotate() action is called; when it is released, the stop_rotate() action is called.

The last entry is a little cryptic. It actually says that when the user presses the <Esc> key, quit() is called. However, OSF has implemented virtual bindings, which allow the same programs to work on computers with different keyboards that may be missing various keys. If a key has a virtual binding, the virtual binding name must be specified in the translation. Thus, the example above specifies osfCancel rather than <Esc>. To use the above translation in a program that is not based on IRIS IM or OSF/Motif, replace <KeyUp>osfCancel with <KeyUp><Esc>.

The translation is only half of what it takes to set up this binding. Although the translation table above contains what look like function names, they're really action names. Your program must also create an action table to bind the action names to actual functions in the program.

For more information on actions and translations, see O'Reilly, X Toolkit Intrinsics Programming Manual (Volume 4), most notably Chapter 4, "An Example Application," and Chapter 8, "Events, Translations, and Accelerators." You can view this manual online using IRIS InSight.


Next | Prev | Up | Top | Contents | Index