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.
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; }
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.