Next: Dealing With Multiple Windows, Previous: Simple Interaction, Up: Part I Doing Interaction [Contents][Index]
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.
I.e., xev->xmotion.send_event
is true.
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]