Next | Prev | Up | Top | Contents | Index

Controlling an Animation With Workprocs

A workproc (work procedure) is a procedure that Xt calls when the application is idle. The application registers workprocs with Xt and unregisters them when it's time to stop calling them.

Note that workprocs do not provide constant speed animation but animate as fast as the application can.


General Workproc Information

Workprocs can be used to carry out a variety of useful tasks: animation, setting up widgets in the background (to improve application startup time), keeping a file up to date, and so on.

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.


Workproc Example

This section illustrates using workprocs. The example, motif/animate.c, is a simple animation driven by a workproc. When the user selects "animate" from the menu, the workproc is registered, as follows:

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.


Next | Prev | Up | Top | Contents | Index