Periodic Events and Non-blocking Interaction (XForms (Forms Library) - Version 1.2 (Rev. 4) A Graphical User Interface Toolkit for X)

Next: Dealing With Multiple Windows, Previous: Simple Interaction, Up: Part I Doing Interaction [Contents][Index]

### 4.3 Periodic Events and Non-blocking Interaction

The interaction mentioned above is adequate for many application programs but not for all. When the program also has to perform tasks when no user action takes place (e.g., redrawing a rotating image all the time), some other means of interaction are needed.

There exist two different, but somewhat similar, mechanisms in the library that are designed specifically for generating and handling periodic events or achieving non-blocking interaction. Depending on the application, one method may be more appropriate than the other.

For periodic tasks, e.g., rotating an image, checking the status of some external device or application state etc., interaction via an idle callback comes in very handy. An idle callback is an application function that is registered with the system and is called whenever there are no events pending for forms (or application windows).

To register an idle callback, use the following routine

    FL_APPEVENT_CB fl_set_idle_callback(FL_APPEVENT_CB callback,
                                        void *user_data);

After the registration, whenever the main loop (`fl_do_forms()`) is idle, i.e., no user action or light user action, the callback function of type `FL_APPEVENT_CB` is called

    typedef int (*FL_APPEVENT_CB)(XEvent *xev, void *user_data);

i.e., a function with the signature

    int idle_callback(XEvent *xev, void *user_data);

where `user_data` is the void pointer passed to the system in `fl_set_idle_callback()` through which some information about the application can be passed. The return value of the callback function is currently not used. `xev` is a pointer to a synthetic7 `MotionNotify` event from which some information about mouse position etc. can be obtained. To remove the idle callback, use `fl_set_idle_callback()` with callback set to `NULL`.

Timeouts are similar to idle callbacks but with somewhat more accurate timing. Idle callbacks are called whenever the system is idle, the time interval between any two invocations of the idle callback can vary a great deal depending upon many factors. Timeout callbacks, on the other hand, will never be called before the specified time is elapsed. You can think of timeouts as regularized idle callbacks, and further you can have more than one timeout callbacks.

To add a timeout callback, use the following routine

    typedef void (*FL_TIMEOUT_CALLBACK)(int, void *);
    int fl_add_timeout(long msec, FL_TIMEOUT_CALLBACK callback,
                       void *data);

The function returns the timeout’s ID8. When the time interval specified by `msec` (in milli-seconds) has elapsed the timeout is removed, then the callback function is called. The timeout ID is passed to the callback function as the first parameter. The second parameter the callback function is passed is the data pointer that was passed to `fl_add_timeout()`.

To remove a timeout before it triggers, use the following routine

    void fl_remove_timeout(int id);

where `id` is the timeout ID returned by `fl_add_timeout()`. There is also an `FL_OBJECT`, the `FL_TIMER` object, especially the invisible type, that can be used to do timeout. Since it is a proper Forms Library object, it may be easier to use simply because it has the same API as any other GUI elements and is supported by the Form Designer. See Timer Object, for complete information on the `FL_TIMER` object.

Note that idle callback and timeout are not appropriate for tasks that block or take a long time to finish because during the busy or blocked period, no interaction with the GUI can take place (both idle callback and timeout are invoked by the main loop, blockage or busy executing application code prevents the main loop from performing its tasks).

So what to do in situations where the application program does require a lengthy computation while still wanting to have the ability to interact with the user interface (for example, a Stop button to terminate the lengthy computation)?

In these situations, the following routine can be used:

    FL_OBJECT *fl_check_forms(void);

This function is similar to `fl_do_forms()` in that it takes care of handling events and appropriate callbacks, but it does not block. Instead it always returns to the application program immediately. If a change has occurred in some object the object is returned as with `fl_do_forms()`. But when no change has occurred control is also returned but this time a `NULL` object is returned. Thus, by inserting this statement in the middle of the computation in appropriate places in effect "polls" the user interface. The downside of using this function is that if used excessively, as with all excessive polls, it can chew up considerable CPU cycles. Therefore, it should only be used outside the inner most loops of the computation. If all objects have callbacks bound to them, `fl_check_forms()` always returns `NULL`, otherwise, code similar to the following is needed:

    obj = fl_check_forms();
    if (obj == obj1)
        /* handle it */
    ...

Depending on the applications, it may be possible to partition the computation into smaller tasks that can be performed within an idle callback one after another, thus eliminating the need of using `fl_check_forms()`.

Handling intensive computation while maintaining user interface responsiveness can be tricky and by no means the above methods are the only options. You can, for example, fork a child process to do some of the tasks and communicate with the interface via pipes and/or signals, both of which can be handled with library routines documented later, or use multi-thread (but be careful to limit Xserver access within one thread). Be creative and have fun.

For running external executables while maintaining responsiveness of the interface, see `fl_exe_command()` and `fl_popen()` documented later in Command Log.

#### Footnotes

### (7)

I.e., `xev->xmotion.send_event` is true.

### (8)

The function will not return 0 or -1 as timeout IDs, so the application program can use these values to tag invalid or expired timeouts.

Next: Dealing With Multiple Windows, Previous: Simple Interaction, Up: Part I Doing Interaction [Contents][Index]