Next: Handling Other Input Sources, Previous: Dealing With Multiple Windows, Up: Part I Doing Interaction [Contents][Index]
As stated earlier, the recommended method of interaction is to use callback functions. A callback function is a function supplied to the library by the application program that binds a specific condition (e.g., a button is pushed) to the invocation of the function by the system.
The application program can bind a callback routine to any object.
Once a callback function is bound and the specified condition is met,
fl_do_forms()
or fl_check_forms()
invokes
the callback function instead of returning the object.
To bind a callback routine to an object, use the following
typedef void (*FL_CALLBACKPTR)(FL_OBJECT *obj, long argument); FL_CALLBACKPTR fl_set_object_callback(FL_OBJECT *obj, FL_CALLBACKPTR callback, long argument);
where callback
is the callback function. argument
is an
argument that is passed to the callback routine so that it can take
different actions for different objects. The function returns the old
callback routine already bound to the object. You can change the
callback routine anytime using this function. See, for example, demo
program timer.c.
The callback routine should have the form
void callback(FL_OBJECT *obj, long argument);
The first argument to every callback function is the object to which
the callback is bound. The second parameter is the argument specified
by the application program in the call to
fl_set_object_callback()
.
See program yesno_cb.c for an example of the use of callback routines. Note that callback routines can be combined with normal objects. It is possible to change the callback routine at any moment.
Sometimes it is necessary to access other objects on the form from
within the callback function. This presents a difficult situation that
calls for global variables for all the objects on the form. This runs
against good programming methodology and can make a program hard to
maintain. Forms Library solves (to some degree) this problem by creating
three fields, void *u_vdata
, char *u_cdata
and long
u_ldata
, in the FL_OBJECT
structure that you can use to hold the
necessary data to be used in the callback function. A better and more
general solution to the problem is detailed in Part II of this
documentation where all objects on a form is are grouped into a single
structure which can then be "hang" off of u_vdata
or some field
in the FL_FORM
structure.
Another communication problem might arise when the callback function
is called and, from within the callback function, some other objects’
state is explicitly changed, say, via fl_set_button()
,
fl_set_input()
etc. You probably don’t want to put the
state change handling code of these objects in another object’s
callback. To handle this situation, you can simply call
void fl_call_object_callback(FL_OBJECT *obj);
When dealing with multiple forms, the application program can also bind a callback routine to an entire form. To this end it should use the routine
void fl_set_form_callback(FL_FORM *form, void (*callback)(FL_OBJECT *, void *), void *data);
Whenever fl_do_forms()
or fl_check_forms()
would return an object in form they call the routine callback instead,
with the object as an argument. So the callback should have the form
void callback(FL_OBJECT *obj, void *data);
With each form you can associate its own callback routine. For objects that have their own callbacks the object callbacks have priority over the form callback.
When the application program also has its own windows (via Xlib or Xt),
it most likely also wants to know about XEvents for the window. As
explained earlier, this can be accomplished by checking
for FL_EVENT
objects. Another (and better) way is to
add an event callback routine. This routine will be called whenever an
XEvent is pending for the application’s own window. To setup an event
callback routine (of type FL_APPEVENT_CB
use the call
typedef int (*FL_APPEVENT_CB)(XEvent *, void *); FL_APPEVENT_CB fl_set_event_callback(int (*callback)(XEvent *ev, void *data), void *data);
Whenever an event happens the callback function is invoked with the event as the first argument and a pointer to data you want it to receive. So the callback should have the form
int callback(XEvent *xev, void *data);
This assumes the application program solicits the events and further,
the callback routine should be prepared to handle all XEvent for all
non-form windows. The callback function normally should return
0
unless the event isn’t for one of the applcation-managed
windows.
This could be undesirable if more than one application window is active. To further partition and simplify the interaction, callbacks for a specific event on a specific window can be registered:
FL_APPEVENT_CB fl_add_event_callback(Window window, int xev_type, FL_APPEVENT_CB callback, void *user_data);
where window
is the window for which the callback routine is to
be registered. xev_type
is the XEvent type you’re interested
in, e.g., Expose
etc. If xev_type
is 0, it is taken to
mean that the callback routine will handle all events for the window.
The newly installed callback replaces the callback already installed.
Note that this function only works for windows created directly by the
application program (i.e., it won’t work for forms’ windows or windows
created by the canvas object). It is possible to access the raw events
that happen on a form’s window via
fl_register_raw_callback()
discussed in Form Events.
fl_add_event_callback()
does not alter the window’s event
mask nor does it solicit events for you. That’s mainly for the reason
that an event type does not always correspond to a unique event mask,
also in this way, the user can solicit events at window’s creation and
use 0 to register all the event handlers.
To let XForms handle solicitation for you, call the following routine
void fl_activate_event_callbacks(Window win);
This function activates the default mapping of events to event masks
built-in in the Forms Library, and causes the system to solicit the
events for you. Note however, the mapping of events to masks are not
unique and depending on applications, the default mapping may or may not
be the one you want. For example, MotionNotify
event can be
mapped into ButtonMotionMask
or PointerMotionMask
. Forms
Library will use both.
It is possible to control the masks you want precisely by using the following function, which can also be used to add or remove solicited event masks on the fly without altering other masks already selected:
long fl_addto_selected_xevent(Window win, long mask); long fl_remove_selected_xevent(Window win, long mask);
Both functions return the resulting event masks that are currently
selected. If event callback functions are registered via both
fl_set_event_callback()
and
fl_add_event_callback()
, the callback via the latter is
invoked first and the callback registered via
fl_set_event_callback()
is called only if the first
attempt is unsuccessful, that is, the handler for the event is not
present. For example, after the following sequence
fl_add_event_callback(winID, Expose, expose_cb, 0); fl_set_event_callback(event_callback);
and all Expose
events on window winID
are consumed
by expose_cb
then event_callback()
would never be
invoked as a result of an Expose
event.
To remove a callback, use the following routine
void fl_remove_event_callback(Window win, int xev_type);
All parameters have the usual meaning. Again, this routine does not
modify the window’s event mask. If you like to change the events the
window is sensitive to after removing the callback, use
fl_activate_event_callbacks()
. If xev_type
is 0,
all callbacks for window win
are removed. This routine is
called automatically if fl_winclose()
is called to unmap
and destroy a window. Otherwise, you must call this routine explicitly
to remove all event callbacks before destroying a window using
XDestroyWindow()
.
A program using all of these has the following basic form:
void event_cb(XEvent *xev, void *mydata1) { /* Handles an X-event. */ } void expose_cb(XEvent *xev, void *mydata2) { /* handle expose */ } void form1_cb(FL_OBJECT *obj) { /* Handles object obj in form1. */ } void form2_cb(FL_OBJECT *obj) { /* Handles object obj in form2. */ } main(int argc, char *argv[]) { /* initialize */ /* create form1 and form2 and display them */ fl_set_form_callback(form1, form1cb); fl_set_form_callback(form2, form2cb); /* create your own window, winID and show it */ fl_addto_selected_xevent(winID, ExposureMask | ButtonPressMask |... ); fl_winshow(winID); fl_set_event_callback(event_cb, whatever); fl_add_event_callback(winID, Expose, expose_cb, data); fl_do_forms(); return 0; }
The routine fl_do_forms()
will never return in this case.
See demo27.c for a program that works this way.
It is recommended that you set up your programs using callback routines (either for the objects or for entire forms). This ensures that no events are missed, events are treated in the correct order, etc. Note that different event callback routines can be written for different stages of the program and they can be switched when required. This provides a progressive path for building up programs.
Another possibility is to use a free object so that the application window is handled automatically by the internal event processing mechanism just like any other forms.
Next: Handling Other Input Sources, Previous: Dealing With Multiple Windows, Up: Part I Doing Interaction [Contents][Index]