Note that workprocs do not provide constant speed animation but animate as fast as the application can.
It's important that a workproc not take very long to execute. While a workproc is running, nothing else can run, and the application may appear sluggish or may even appear to hang.
Workprocs return Booleans. To set up a function as a workproc, first prototype the function, then pass its name to XtAppAddWorkProc(). Xt then calls the function whenever there's idle time while Xt is waiting for an event. If the function returns True, it's removed from the list of workprocs; if it returns False, it's kept on the list and called again when there's idle time.
To explicitly remove a workproc, call XtRemoveWorkProc(). Here are the prototypes for the add and remove functions:
XtWorkProcId XtAppAddWorkProc(XtAppContext app_context, XtWorkProc proc, XtPointer client_data) void XtRemoveWorkProc(XtWorkProcId id)The client_data parameter for XtAppAddWorkProc() lets you pass data from the application into the workproc, similar to the equivalent parameter used in setting up a callback.
static void menu(Widget w, XtPointer clientData, XtPointer callData) { int entry = (int) clientData; switch (entry) { case 0: if (state.animate_wpid) { XtRemoveWorkProc(state.animate_wpid); state.animate_wpid = 0; } else { /* register workproc */ state.animate_wpid = XtAppAddWorkProc(state.appctx, redraw_proc, &state.glxwidget); } break; case 1: exit(EXIT_SUCCESS); break; default: break; } }The workproc starts executing if the window is mapped (that is, it could be visible but it may be overlapped):
static void map_change(Widget w, XtPointer clientData, XEvent *event, Boolean *cont) { switch (event->type) { case MapNotify: /* resume animation if we become mapped in the animated state */ if (state.animate_wpid != 0) state.animate_wpid = XtAppAddWorkProc(state.appctx, redraw_proc, &state.glxwidget); break; case UnmapNotify: /* don't animate if we aren't mapped */ if (state.animate_wpid) XtRemoveWorkProc(state.animate_wpid); break; } }If the window is mapped, the workproc calls redraw_proc():
static Boolean redraw_proc(XtPointer clientData) { Widget *w = (Widget *)clientData; draw_scene(*w); return False; /*call the workproc again as possible*/ }The redraw_proc() function, in turn, calls draw_scene(), which swaps the buffers. Note that this program doesn't use glXSwapBuffers(), but instead the convenience function GLwDrawingAreaSwapBuffers().
static void draw_scene(Widget w) { static float rot = 0.; glClear(GL_COLOR_BUFFER_BIT); glColor3f(.1, .1, .8); glPushMatrix(); if ((rot += 5.) > 360.) rot -= 360.; glRotatef(rot,0.,1.,0.); cube(); glScalef(0.3,0.3,0.3); glColor3f(.8, .8, .1); cube(); glPopMatrix(); GLwDrawingAreaSwapBuffers(w); }
Note: If an animation is running and the user selects a menu command, the event handling for the command and the animation may end up in a race condition.