Overview of Main Functions
**************************

In this chapter we give a brief overview of all the main functions that are available. For an overview of all routines related to specific object classes see Part III.

Version Information
===================

The header file forms.h defines three symbolic constants which you can use to conditionally compile your application. They are

FL_VERSION
     The major version number.

FL_REVISION
     Revision number.

FL_INCLUDE_VERSION
     1000 * FL_VERSION + FL_REVISION

There is also a routine that can be used to obtain the library version at run time:

     int fl_library_version(int *version, int *revision)

The function returns a consolidated version information, computed as 1000 * version + revision. For example, for library version 1 revision 21 (1.21), the function returns a value of 1021 with version and revision (if not NULL) set to 1 and 21, respectively.

It is always a good idea to check if the header and the run time library are of the same version and take appropriate actions when they are not. This is especially important for versions less than 1.

To obtain the version number of the library used in an executable, run the command with -flversion option, which will print the complete version information.

Initialization
==============

The routine

     Display *fl_initialize(int *argc, char *argv[], const char *appclass,
                            XrmOptionDescList app_opt, int n_app_opt);

initializes the Forms Library and returns a pointer to the Display structure if a connection could be made, otherwise NULL. This function must be called before any other calls to the Forms Library (except fl_set_defaults() and a few other functions that alter some of the defaults of the library).

The meaning of the arguments is as follows

argc, argv
     Number and array of the command line arguments the application was started with. The application name is derived from argv[0] by stripping leading path names and trailing period and extension, if any. Due to the way the X resources (and command line argument parsing) work, the executable name should not contain a dot . or a star *.

appclass
     The application class name, which typically is the generic name for all instances of this application. If no meaningful class name exists, it is typically given (or converted to if non given) as the application name with the first letter capitalized (second if the first letter is an X).

app_opt
     Specifies how to parse the application-specific resources.

n_app_opt
     Number of entries in the option list.

The fl_initialize() function builds the resource database, calls the Xlib XrmParseCommand() function to parse the command line arguments and performs other per display initialization. After the creation of the database, it is associated with the display via XrmSetDatabase(), so the application can get at it if necessary.

All recognized options are removed from the argument list and their corresponding values set. The XForms library provides appropriate defaults for all options. The following are recognized by the library:

Option
Type
Meaning
Default

-fldebug level
int
Print debug information
0 (off)

-name appname
string
Change application name
none

-flversion

Print version of the library

-sync

Synchronous X11 mode (debug)
false

-display host:dpy
string
Set (remote) host
$DISPLAY

-visual class
string
TrueColor, PseudoColor...
best

-depth depth
int
Set prefered visual depth
best

-vid id
long
Set prefered visual ID
0

-private

Force use of private colormap
false

-shared

Force use of shared colormap
false

-stdcmap

Force use of standard colormap
false

-double

Enable double buffering for forms
false

-bw width
int
Set object border width
1

-rgamma gamma
float
Set red gamma
1.0

-ggamma gamma
float
Set green gamma
1.0

-bgamma gamma
float
Set blue gamma
1.0

In the above table "best" means the visual that has the most colors, which may or may not be the server's default. There is a special command option -visual Default that sets both the visual and depth to the X servers default. If a visual ID is requested, it overrides depth or visual if specified. The visual ID can also be requested programmatically (before fl_initialize() is called) via the function

     void fl_set_visualID(long id);

Note that all command line options can be abbreviated, thus if the application program uses single character options, they might clash with the built-ins. For example, if you use -g as a command line option to indicate geometry, it might not work as -g matches -ggamma in the absence of -ggamma. Thus you should avoid using single character command line options.

If the border width is set to a negative number, all objects appear to have a softer appearance. Older version of the library used a larger default for the border width of 3.

As mentioned the fl_initialize() function removes all the above listed values from the command line arguments, leaving you with a cleaned-up list. To get again at the complete list you can use the function

     char **fl_get_cmdline_args( int *arg_cnt );

returning a copy to the values from the original list and their number via the arg_cnt argument.

Depending on your application XForms defaults may or may not be appropriate. E.g., on machines capable of 24 bits visuals, Forms Library always selects the deeper 24 bits visual. If your application only uses a limited number of colors, it might be faster if a visual other than 24 bits is selected.

There are a couple of ways to override the default settings. You can provide an application specific resource database distributed with your program. The easiest way, however, is to set up your own program defaults programmatically without affecting the users' ability to override them with command line options. For this, you can use the following routine before calling fl_initialize():

     void fl_set_defaults(unsigned long mask, FL_IOPT *flopt);

In addition to setting a preferred visual, this function can also be used to set other program defaults, such as label font size, unit of measure for form sizes etc.

The following table lists the fields, masks and their meanings of FL_IOPT:

Structure
Mask Name
Meaning
typedef struct {

int debug;
FL_PDDebug
Debug level (0-5)

int depth;
FL_PDDepth
Preferred visual depth

int vclass;
FL_PDVisual
Prefered visual, TrueColor etc.

int doubleBuffer;
FL_PDDouble
Simulate double buffering

int buttonFontSize;
FL_PDButtonFontSize
Default button label font size

int menuFontSize;
FL_PDMenuFontSize
Menu label font size

int choiceFontSize;
FL_PDChoiceFontSize
Choice label and choice text font size

int browserFontSize;
FL_PDBrowserFontSize
Browser label and text font size

int inputFontSize;
FL_PDInputFontSize
Input label and text font size

int labelFontSize;
FL_PDLabelFontSize
Label font size for all other objects (box, pixmap etc.)

int pupFontSize;
FL_PDPupFontSize
Font size for pop-ups

int privateColormap;
FL_PDPrivateMap
Select private colormap if appropriate

int sharedColormap;
FL_PDSharedMap
Force use of shared colormap

int standardColormap;
FL_PDStandardMap
Force use of standard colormap
int scrollbarType;

FL_PDScrollbarType
Scrollbar type to use for browser and input
int ulThickness;

FL_PDULThickness
Underline thickness
int ulPropWidth;

FL_PDULPropWidth
Underline width, 0 for const. width fonts
int backingStore;
FL_PDBS
Turn BackingStore on or off

int coordUnit;
FL_PDCoordUnit
Unit of measure: pixel, mm, point

int borderWidth;
FL_PDBorderWidth
Default border width

} FL IOPT;

A special visual designation, FL_DefaultVisual and a command line option equivalent, -visual Default are provided to set the program default to the server's default visual class and depth.

If you set up your resource specifications to use class names instead of instance names, users can then list instance resources under an arbitrary name that is specified with the -name option.

Coordinate units can be in pixels, points (1/72 inch), mm (millimeters), cp (centi-point, i.e., 1/100 of a point) or cmm (centi-millimeter). The the type of unit in use can be queried or set via the functions

     int fl_get_coordunit(void);
     void fl_set_coordunit(int coordUnit);

coordUnit can have the following values: FL_COORD_PIXEL, FL_COORD_POINT, FL_COORD_MM, FL_COORD_centiPOINT and FL_COORD_centiMM.

The unit in use can be changed anytime, but typically you would do this prior to creating a form, presumably to make the size of the form screen resolution independent. The basic steps in doing this may look something like the following:

     int oldcoordUnit = fl_get_coordunit();
     fl_set_coordunit(FL_COORD_POINT);
     fl_bgn_form(...);    /* add more objects */
     fl_end_form();
     fl_set_coordunit(oldcoordunit);

Some of the defaults are "magic" in that their exact values depend on the context or platform. For example, the underline thickness by default is 1 for normal fonts and 2 for bold fonts.

There exists a convenience function to set the application default border width

     void fl_set_border_width(int border_width)

which is equivalent to

     FL_IOPT fl_cntl;
     fl_cntl.borderWidth = border_width;
     fl_set_defaults(FL_PDBorderWidth, &fl_cntl);

Typically this function, if used, should appear before fl_initialize() is called so the user has the option to override the default via resource or command line options.

The cirrent setting of the borderwidth can also tested via

     int fl_get_border_width(void);

To change the default scrollbar type (which is THIN_SCROLLBAR) used in browser and input object, the following convenience function can be used:

     void fl_set_scrollbar_type(int type);

where type can be one of the following

FL_NORMAL_SCROLLBAR
     Basic scrollbar

FL_THIN_SCROLLBAR
     Thin scrollbar

FL_NICE_SCROLLBAR
     Nice scrollbar

FL_PLAIN_SCROLLBAR
     Similar to thin scrollbar, but not as fancy

Setting the scrollbar type before calling fl_initialize() is equivalent to

     FL_IOPT fl_cntl;
     fl_cntl.scrollbarType = type;
     fl_set_defaults(FL_PDScrollbarType, &fl_cntl);

It is recommended that this function be used before fl_initialize() so the user has the option to override the default through application resources.

Prior to version 0.80 the origin of XForms' coordinate system was at the lower left-hand corner of the form. The new Form Designer will convert the form definition file to the new coordinate system, i.e., with the origin at the upper left-hand corner, so no manual intervention is required. To help those who lost the .fd files or otherwise can't use a newer version of fdesign, a compatibility function is provided

     void fl_flip_yorigin(void);

Note however that this function must be called prior to fl_initialize() and is a no-op after that.

If this function has been called functions like fl_get_object_position() or fl_get_object_bbox(), reporting an objects positions and bounding box, will return y-coordinates in the old-fashioned coordinate system with the origin at the left bottom corner of the form. Similarly, the functions for setting or changing an objects position (fl_set_object_position() and fl_move_object()) then expect to receive arguments for the y-coordinates in this system. The y-coordinate stored in the object itself (i.e., obj->y) is always for the normal coordinate system with the origin at the top left corner.

For proportional font, substituting tabs with spaces is not always appropriate because this most likely will fail to align text properly. Instead, a tab is treated as an absolute measure of distance, in pixels, and a tab stop will always end at multiples of this distance. Application program can adjust this distance by setting the tab stops using the following routine

     void fl_set_tabstop(const char *s);

where s is a string whose width in pixels is to be used as the tab length. The font used to calculate the width is the same font that is used to render the string in which the tab is embedded. The default "aaaaaaaa", i.e., eight 'a's.

Before we proceed further, some comments about double buffering are in order. Since Xlib does not support double buffering, Forms Library simulates this functionality with pixmap bit-bliting. In practice, the effect is hardly distinguishable from double buffering and performance is on par with multi-buffering extensions (It is slower than drawing into a window directly on most workstations however). Bear in mind that a pixmap can be resource hungry, so use this option with discretion.

In addition to using double buffering throughout an application, it is also possible to use double buffering on a per-form or per-object basis by using the following routines:

     void fl_set_form_dblbuffer(FL_FORM *form, int yes_no);
     void fl_set_object_dblbuffer(FL_OBJECT *obj, int yes_no);

Currently double buffering for objects having a non-rectangular box might not work well. A nonrectangular box means that there are regions within the bounding box that should not be painted, which is not easily done without complex and expensive clipping and unacceptable inefficiency. XForms gets around this by painting these regions with the form's backface color. In most cases, this should prove to be adequate. If needed, you can modify the background of the pixmap by changing obj->dbl_background after switching to double buffer.

Normally the Forms Library reports errors to stderr. This can be avoided or modified by registering an error handling function

     void fl_set_error_handler(void (*user_handler)(const char *where,
                                                    const char *fmt,...));

The library will call the user_handler function with a string indicating in which function an error occured and a formatting string (see sprintf()) followed by zero or more arguments. To restore the default handler, call the function again with user_handler set to NULL. You can call this function anytime and as many times as you wish.

You can also instruct the default message handler to log the error to a file instead of printing to stderr

     void fl_set_error_logfp(FILE *fp);

For example

     fl_set_error_logfp(fopen("/dev/null","w"));

redirects all error messages to /dev/null, effectively turning off the default error reporting to stderr.

In XForms versions older than 1.0.01 for some error messages, in addition to being printed to stderr, a dialog box were shown that requires actions from the user. This could be turned off and on with the function

     void fl_show_errors(int show);

where show indicates whether to show (1) or not show (0) the errors. With newer versions of the Forms Library this function has no effect.

The fonts used in all forms can be changed using the routines

     int fl_set_font_name(int n, const char *name);
     int fl_set_font_name_f(int n, const char *fmt, ,,,);

The first function just accepts a simple string while the second constructs the font name from a format string just as it's used for printf() etc. and the following arguments. The first argument, n, must be a number between 0 and FL_MAXFONTS-1. The function returns 0 on success, 1 if called before proper initialization of the library and -1 for either invalid arguments (name or the result of the expansion of the format string doesn't name an available font, n negative or not less than FL_MAXFONTS). See Label Attributes and Fonts, for details. A redraw of all forms is required to actually see the change for visible forms.

Since the dimension of an object is typically given in pixels, depending on the server resolution and the font used, this can lead to unsatisfactory user interfaces. For example, a button designed to (just) contain a label in a 10 pt font on a 75 DPI monitor will have the label overflow the button on a 100 DPI monitor. This comes about because a character of a 10 pt font when rendered with 75 DPI resolution may have 10 pixels while the same character in the same 10 pt font with 100 DPI resolution may have 14 pixels. Thus, when designing the interfaces, leave a few extra pixels for the object. Or use a resolution independent unit, such as point, or centi-point etc.

Using a resolution independent unit for the object size should solve the font problems, theoretically. In practice, this approach may still prove to be vulnerable. The reason is the discreteness of both the font resolution and the monitor/server resolutions. The standard X fonts only come in two discrete resolutions, 75 DPI and 100 DPI. Due to the variations in monitor resolutions, the theoretically identical sized font, say a 10 pt font, can vary in sizes (pixels) by up to 30%, depending on the server (rendering a font on a 80 DPI monitor will cause errors in sizes regardless if a 75 DPI or 100 DPI font is used.) This has not even taken into account the fact that a surprising number of systems have wrong font paths (e.g., a 90 DPI monitor using 75 DPI fonts etc.).

With the theoretical and practical problems associated with X fonts, it is not practical for XForms to hard-code default font resolution and it is not practical to use the resolution information obtained from the server either as information obtained from the server regarding monitor resolution is highly unreliable. Thus, XForms does not insist on using fonts with specific resolutions and instead it leaves the freedom to select the default fonts of appropriate resolutions to the system administrators.

Given all these uncertainties regarding fonts, as a workaround, XForms provides a function that can be used to adjust the object size dynamically according to the actual fonts loaded:

     double fl_adjust_form_size(FL_FORM *form);

This function works by computing the size (in pixels) of every object on the form that has an inside label and compares it to the size of the object. Scaling factors are computed for all object labels that don't fit. The maximum scaling factor found is then used to scale the form so every object label fits inside the object. It will never shrink a form. The function returns the resulting scaling factor. In scaling the aspect ratio of the form is left unmodified and all object gravity specifications are ignored. Since this function is meant to compensate for font size and server display resolution variations, scaling is limited to 125% per invocation. The best place to use this function is right after the creation of the forms. If the forms are properly designed this function should be a no-op on the machine the forms were designed on. Form Designer has a special option -compensate and resource compensate to request the emission of this function automatically for every form created. It is likely that this will become the default once the usefulness of it has been established.

There is a similar function that works the same way, but on an object-by-object basis and further allows explicit margin specifications:

     void fl_fit_object_label(FL_OBJECT *obj, FL_Coord hm, FL_Coord vm);

where hm and vm are the horizontal and vertical margins to leave on each side of the object, respectively. This function works by computing the object labels size and comparing it to the object size. If the label does not fit inside the object with the given margin, the entire form the object is on is scaled so the object label fits. In scaling the form, all gravity specification is ignored but the aspect ratio of the form (and thus of all objects) is kept. This function will not shrink a form. You can use this function on as many objects as you choose. Of course the object has to have a label inside the object for this function to work.

All colors with indices smaller than FL_FREE_COL1 are used (or can potentially be used) by the Forms Library. If you wish they can be changed using the following function prior to fl_initialize():

     void fl_set_icm_color(FL_COLOR index, int r, int g, int b);

Using this function you can actually change all entries in the internal colormap (with index going up to FL_MAX_COLORS-1). You may also inspect the internal colormap using

     void fl_get_icm_color(FL_COLOR index, int *r, int *g, int *b);

In some situations Forms Library may modify some of the server defaults. All modified defaults are restored as early as possible by the main loop and in general, when the application exits, all server defaults are restored. The only exception is when exiting from a callback that is activated by shortcuts. Thus it is recommended that the cleanup routine fl_finish() is called prior to exiting an application or register it via atexit().

     void fl_finish(void);

In addition to restoring all server defaults, fl_finish() also shuts down the connection and frees dynamically allocated memory.

Creating Forms
==============

To start the definition of a form call

     FL_FORM *fl_bgn_form(int type, FL_Coord w, FL_Coord h);

When the form is created it automatically acquires one object, a box object covering the full area of the form, which is used as the background of the form. The type argument is the type of this box object, so you can "style" the look of your forms (but don't use any non-rectangular box types). w and h are the width and height of the new form. The function returns a pointer to the new form.

Note: if you look at the code generated by fdesign for the creation of a form you may notice that the type of this automatically assigned box is FL_NO_BOX (which is invisible) and that for the background another box of the same size but a different (visible) type is added. This is because in fdesign the very first object can't be accessed and thus its properties can not be adjusted (like the box type or its color that then becomes the background color of the form). By using an extra box, which can be accessed from within fdesign, that problem is circumvented.

There also exist functions for setting and requesting the background color of a form

     void fl_set_form_background_color(FL_FORM *form, FL_COLOR col);
     FL_COLOR fl_get_form_background_color(FL_FORM *form);

These functions use the color of the very first object of the form, or, if this is a box of type FL_NO_BOX as it is the case with forms created via code generated by fdesign, the color of the second object. If these object(s) don't exist the function can't work properly.

Once all objects required have been added to a form call

     void fl_end_form(void);

Between these two calls objects and groups of objects are added to the form with functions like fl_add_button().

To start a new group of objects use

     FL_OBJECT *fl_bgn_group(void);

The function returns a pointer to the group (actually to an invisible pseudo-object of class FL_BEGIN_GROUP). Groups can't be nested.

When all objects that are supposed to belong to the group are added call

     void fl_end_group(void);

Also this function creates an (invisible) pseudo-object, belonging to class FL_END_GROUP, but since it can't be used its address isn ot returned.

Groups are useful for two reasons. First of all, it is possible to hide or deactivate groups of objects with a single function call. This is often very handy to dynamically change the appearance of a form depending on the context or selected options. In addition it can also be used as a shortcut to set some particular attributes of several objects. It is not uncommon that you want several objects to maintain their relative positioning upon form resizing. This requires to set the gravity for each object. If these objects are placed inside a group, setting the gravity attributes of the group will suffice.

The second reason for use of groups is radio buttons. Radio buttons are considered related only if they belong to the same group. Using groups is the only way to place unrelated groups of radio buttons on a single form without interference from each other.

Both forms and groups that have been ended by fl_end_form() or fl_end_group() can be "reopened" by using

     FL_FORM *fl_addto_form(FL_FORM *form)
     FL_OBJECT *fl_addto_group(FL_OBJECT *group);

Both functions return their argument on success and NULL on failure (e.g., because a different group or form is still open). On success further objects can be appended to the form or group.

To remove an object from a form use

     void fl_delete_object(FL_OBJECT *obj);

This does not yet destroy the object, it just breaks its connection to the form it did belong to, so it can still be referenced and added to the same form again or some other form using

     void fl_add_object(FL_FORM *form, FL_OBJECT *obj);

even without "reopening" the form using fl_addto_form().

To finally destroy an object use

     void fl_free_object(FL_OBJECT *obj);

If fl_delete_object() hadn't been called for the object this will happen now. The object receives a final event of type FL_FREEMEM to allow it to free memory it did allocate and do whatever other clean-up required. Finally all memory allocated for the object is freed. After being freed an object can not be referenced anymore.

A form as a whole, together with all the objects it contains can be deleted by calling

     void fl_free_form(FL_FORM *form);

This will first hide the form (emitting warning if this is necessary), then free all of its objects and finally release memory allocated for the form.

Object Attributes
=================

A number of general routines are available for setting and querying attributes. Unless stated otherwise, all attributes altering routines affect the appearance or geometry of the object immediately if the object is visible.

Since the object class and type of an object can't be changed anymore once an object has been created there are only functions for querying these attributes:

     int fl_get_object_objclass(FL_OBJECT *obj);
     int fl_get_object_type(FL_OBJECT *obj);

Receiving a negative value indicates that a NULL pointer was passed to the functions.

To set the two colors that influence the appearance of the object use

     void fl_set_object_color(FL_OBJECT *obj, FL_COLOR col1, FL_COLOR col2);

and to find out about the colors of an object use

     void fl_get_object_color(FL_OBJECT *obj,
                              FL_COLOR *col1, FL_COLOR *col2);

     void fl_set_object_boxtype(FL_OBJECT *obj, int boxtype);

Changes the shape of the box of the object. Please note that not all possible boxtypes are suitable for all types of objects, see the documentation for the different objects for limitations.

To find out the current boxtype of an object use

     int fl_get_object_boxtype(FL_OBJECT *obj);

Receiving a negative value indicates that a NULL pointer was passed to the function.

There are also functions to change or query the border width of an object:

     void fl_set_object_bw(FL_OBJECT *obj, int bw);
     void fl_get_object_bw(FL_OBJECT *obj, int *bw);

If the requested border width is 0, -1 is used.

To change or inquire the objects position (relative to the form it belongs to) the functions

     void fl_set_object_position(FL_OBJECT *obj, FL_Coord x, FL_Coord y);
     void fl_get_object_position(FL_OBJECT *obj, FL_Coord *x, FL_Coord *y);

exist. If the object is visible it's redrawn at the new position.

An object can also be moved relative to its current position using the function

     void fl_move_object(FL_OBJECT *obj, FL_Coord dx, FL_Coord dy);

where dx and dy are the amounts by which the object is moved to the right and down.

To change or inquire about the size of an object use

     void fl_set_object_size(FL_OBJECT *obj, FL_Coord w, FL_Coord h);
     void fl_get_object_size(FL_OBJECT *obj, FL_Coord *w, FL_Coord *h);

When changing the size of the object the position of its upper left hand corner remains unchanged.

To set or query both the position and the size of an object the functions

     void fl_set_object_geometry(FL_OBJECT *obj, FL_Coord x, FL_Coord y,
                                 FL_Coord w, FL_Coord h);
     void fl_get_object_geometry(FL_OBJECT *obj, FL_Coord *x, FL_Coord *y,
                                 FL_Coord (*w, FL_Coord *h);

can be used.

Please note: always use one of the above functions to change the position and/or size of an object and don't try to change the information stored in the object directly. There's some double bookkeeping going on under the hood that makes sure that the objects position and size won't change due to rounding errors when the whole form gets resized and changing the internal information kept in the objects structure would interfere with this.

There's a second function for calculation an objects geometry:

     void fl_get_object_bbox(FL_OBJECT *obj, FL_Coord *x, FL_Coord *y,
                             FL_Coord *w, FL_Coord *h);

The difference between this functions and fl_get_object_geometry() is that fl_get_object_bbox() returns the bounding box size that has the label, which could be drawn outside of the object figured in.

Some objects in the library are composite objects that consist of other objects. For example, the scrollbar object is made of a slider and two scroll buttons. To get a handle to one of the components of the composite object, the following routine is available:

     FL_OBJECT *fl_get_object_component(FL_OBJECT *obj, int objclass,
                                        int type, int number);

where obj is the composite object, objclass and type are the component object's class ID and type; and number is the sequence number of the desired object in case the composite has more than one object of the same class and type. You can use a constant -1 for type to indicate any type of class objclass. The function returns the object handle if the requested object is found, otherwise NULL. For example to obtain the object handle to the horizontal scrollbar in a browser, code similiar to the following can be used

     hscrollbar = fl_get_object_component(browser, FL_SCROLLBAR,
                                          FL_HOR_THIN_SCROLLBAR, 0)

To influence change the color, font size, font style, alignment and text of the label of an object use

     void fl_set_object_lcolor(FL_OBJECT *obj, FL_COLOR lcol);
     void fl_set_object_lsize(FL_OBJECT *obj, int lsize);
     void fl_set_object_lstyle(FL_OBJECT *obj, int lstyle);
     void fl_set_object_lalign(FL_OBJECT *obj, int align);
     void fl_set_object_label(FL_OBJECT *obj, const char *label);
     void fl_set_object_label(FL_OBJECT *obj, const char *fmt, ...);

To find out about the object labels color, font size, style, alignment and the string itself use

     FL_COLOR fl_get_object_lcolor(FL_OBJECT *obj);
     int fl_get_object_lsize(FL_OBJECT *obj);
     int fl_get_object_lstyle(FL_OBJECT *obj);
     int fl_get_object_lalign(FL_OBJECT *obj);
     const char * fl_get_object_label(FL_OBJECT *obj);

To set a tool-tip text for an object use the following routines

     void fl_set_object_helper(FL_OBJECT *obj, const char *helpmsg);
     void fl_set_object_helper_f(FL_OBJECT *obj, const char *fmt, ...);

where helpmsg is a text string (with possible embedded newlines in it) that will be shown when the mouse hovers over the object for nore than about 600 msec. A copy of the string is made internally. The second functions accepts instead of a simple string a format string just as it's used for printf() etc., followed by as many further arguments as the format string contains format specifiers.

The boxtype, color and font for the tool-tip message displayed can be customized further using the following routines:

     void fl_set_tooltip_boxtype(int boxtype);
     void fl_set_tooltip_color(FL_COLOR textcolor, FL_COLOR background);
     void fl_set_tooltip_font(int style, int size);

where boxtype is the backface of the form that displays the text. The default is FL_BORDER_BOX. textcolor and background specify the color of the text and the color of the backface. The defaults for these are FL_BLACK and FL_YELLOW. style and size are the font style and size of the text.

There are four function for controlling how an object reacts to resizing the form it belongs to or to find out what its current settings are:

     void fl_set_object_resize(FL_OBJECT *obj, unsigned int howresize);
     void fl_get_object_resize(FL_OBJECT *obj, unsigned int *howresize);
     void fl_set_object_gravity(FL_OBJECT *obj, unsigned int NWgravity,
                                unsigned int SEgravity);
     void fl_get_object_gravity(FL_OBJECT *obj, unsigned int *NWgravity,
                                unsigned int *SEgravity);

See Doing Interaction, for more details on the resizing behaviour of objects.

If you change many attributes of a single object or many objects in a visible form the changed object is redrawn after each change. To avoid this put the changes between calls of the two functions

     void fl_freeze_form(FL_FORM *form);
     void fl_unfreeze_form(FL_FORM *form);

The form is automatically redrawn once it is "unfrozen", so a call of fl_redraw_form() isn't required (and, while the form is "frozen", calling this function as well as fl_redraw_object() has no effects).

You may also freeze and unfreeze all forms at once by using

     void fl_freeze_all_forms(void);
     void fl_unfreeze_all_forms(void);

There are also routines that influence the way events are dispatched. These routines are provided mainly to facilitate the development of (unusual) new objects where attributes need to be changed on the fly. These routines should not be used on the built-in ones.

To enable or disable an object to receive the FL_STEP event, use the following routine

     void fl_set_object_automatic(FL_OBJECT *obj, int yes_no);

To determine if an object receives FL_STEP events use

     int fl_object_is_automatic(FL_OBJECT *obj);

To enable or disable an object to receive the FL_DBLCLICK event use the following routine

     void fl_set_object_dblclick(FL_OBJECT *obj, unsigned long timeout);

where timeout specifies the maximum time interval (in msec) between two clicks for them to be considered a double-click (using 0 disables double-click detection). To determine the current setting of the timeout use

     unsigned fl_get_object_dblclick(FL_OBJECT *obj);

To make an object or a group invisible or visible use the following two functions

     void fl_hide_object(FL_OBJECT *obj);
     void fl_show_object(FL_OBJECT *obj);

obj can be the pseudo-object returned by fl_bgn_group() and then allows to hide or show whole groups of objects.

To determine if an object is visible (given that the form it belongs to is also visible) use

     int fl_object_is_visible(FL_OBJECT *obj);

     void fl_trigger_object(FL_OBJECT *obj);

returns obj to the application program after calling its callback if one exists.

     void fl_set_focus_object(FL_FORM *form, FL_OBJECT *obj);

sets the input focus in form form to object obj. Note however, if this routine is used as a response to an FL_UNFOCUS event, i.e., as an attempt to override the focus assignment by the main loop from within an objects event handler, this routine will not work as the main loop assigns a new focus object upon return from the object event handler, which undoes the focus change inside the event handler. To override the FL_UNFOCUS event the following routine should be used:

     void fl_reset_focus_object(FL_OBJECT *obj);

Use the following routine to obtain the object that has the focus on a form

     FL_OBJECT *fl_get_focus_object(FL_FORM *form);

The routine

     void fl_set_object_callback(FL_OBJECT *obj,
                                 void (*callback)(FL_OBJECT *, long),
                                 long argument);

binds a callback routine to an object.

To invoke the callback manually (as opposed to invocation by the main loop), use the following function

     void fl_call_object_callback(FL_OBJECT *obj);

If the object obj does not have a callback associated with it, this call has not effect.

     void fl_set_form_callback(FL_FORM *form,
                               void (*callback)(FL_OBJECT *, void *),
                               void *data);

binds a callback routine to an entire form.

It is sometimes useful to obtain the last X event from within a callback function, e.g., to implement different functionalities depending on which button triggers the callback. For this, the following routine can be used from within a callback function.

     const XEvent *fl_last_event(void);

In other rare circumstances one might not be interested not in the X event but instead the internal XForms event resulting in the invocation of an object or form callback. This information can be obtained by calling

     int fl_current_event(void);

A callback invocation resulting from a call of fl_call_object_callback() will return FL_TRIGGER. For other possible return value see the chapter about XForms internal events. Calling this function is only useful while within an object or form callback, at all other times it returns just FL_NOEVENT.

Also in objects callback it might be of interest to find out if the mouse is on top of a certain letter of the (inside) label (one trivial use of this can be found in the program demo/strange_button.c. To find out about this use

     int fl_get_label_char_at_mouse(FL_OBJECT *obj);

The function returns the index of the character in the label of the object the mouse is on or -1 if it's not over the label. Note that this function has some limitations: it can only be used on labels inside of the object and the label string may not contain underline characters (and the label can't be a symbol) - if you try to use it on labels that don't satisfy these requirements -1 is returned.

Sometimes, it may be desirable to obtain hardcopies of some objects in a what-you-see-is-what-you-get (WYSISYG) way, especially those that are dynamic and of vector-graphics in nature. To this end, the following routine exists:

     int fl_object_ps_dump(FL_OBJECT *obj, const char *fname);

The function will output the specified object in PostScript. If fname is NULL, a file selector will be shown to ask the user for a file name. The function returns a negative number if no output is generated due to errors. At the moment, only the FL_XYPLOT object is supported. Nothe that this function isn't part of the statndard XForms library (libforms) but the XForms image library (libflimage discussed in Part VI Images.

The object must be visible at the time of the function call. The hardcopy should mostly be WYSIWYG and centered on the printed page. The orientation is determined such that a balanced margin results, i.e., if the width of the object is larger than the height, landscape mode will be used. Further, if the object is too big to fit on the printed page, a scale factor will be applied so the object fits. The box underneath the object is by default not drawn and in the default black&white mode, all curves are drawn in black. See demo program xyplotover.c for an example output.

It is possible to customize the output by changing the PostScript output control parameters via the function

     FLPS_CONTROL *flps_init(void);

A typical use is to call this routine to obtain a handle to the PostScript output control structure and change the control structure members to suit your needs before calling fl_object_ps_dump(). You should not free the returned buffer.

The control structure has the following members

int ps_color
     The choices are full color (FLPS_COLOR), grayscale (FLPS_GRAYSCALE) and black&white (FLPS_BW). The default for xyplot is black and white. In this mode, all drawings are black, on a white background. If drawbox (see below) is true, the drawing color can be either white or black depending on the specified color.

int orientation
     Valid choices are FLPS_AUTO, FLPS_PORTRAIT and FLPS_LANDSCAPE. The default is FLPS_AUTO.

auto_fit
     By default, this is true so the object always fits the printed page. Set it to false (0) to turn off auto-scaling.

int eps
     Set this to 1 if output in EPS format is required.

int drawbox
     Set this to 1 if the box of the object is to be drawn.

float xdpi, ydpi
     These two are the screen resolution. The default is to use the actual resolution of the display. Note by setting a dpi number smaller or larger than the actual resolution, the output object is in effect being enlarged or shrunken.

float paper_w
     The paper width in inches. The default is 8.5 in.

float paper_h
     The paper height in inches. The default is 11 in.

To generate a PostScript output of a form or forms, use the fd2ps program documented in Part II Generating Hardcopies.

Doing Interaction
=================

To display the form form on the screen use one of

     Window fl_show_form(FL_FORM *form, int place, int border,
                         const char *title);
     Window fl_show_form(FL_FORM *form, int place, int border,
                         const char *fmt, ...);

place controls the position and size of the form. border indicates whether a border (window manager's decoration) should be drawn around the form. If a border is to be drawn title is the name of the window (and its associated icon). The routine returns the window identifier of the form. For resource and identification purposes, the form name is taken to be the title with spaces removed and the first character lower-cased. E.g., if a form has a title "Foo Bar the forms name is derived as "fooBar". The only difference between the two functions is that the first one accepts a simple string for the title while the second expects a format string like printf(), followed by the appropriate number of arguments.

For the the location and size of the window controlled by place the following possibilities exist:

FL_PLACE_SIZE
     The user can control the position but the size is fixed. Interactive resizing is not allowed once the form becomes visible.

FL_PLACE_POSITION
     Initial position used will be the one set via fl_set_form_position(). Interactive resizing is allowed.

FL_PLACE GEOMETRY
     Place at the latest position and size (see also below) or the geometry set via fl_set_form_geometry() etc. A form so shown will have a fixed size and interactive resizing is not allowed.

FL_PLACE_ASPECT
     Allows interactive resizing but any new size will have the aspect ratio as that of the initial size.

FL_PLACE_MOUSE
     The form is placed centered below the mouse. Interactive resizing will not be allowed unless this option is accompanied by FL_FREE_SIZE as in FL_PLACE_MOUSE|FL_FREE_SIZE.

FL_PLACE_CENTER
     The form is placed in the center of the screen. If FL_FREE_SIZE is also specified, interactive resizing will be allowed.

FL_PLACE_FULLSCREEN
     The form is scaled to cover the full screen. If FL_FREE_SIZE is also specified, interative resizing will be allowed.

FL_PLACE_FREE
     Both the position and size are completely free. The initial size used is the designed size. Initial position, if set via fl_set_form_position(), will be used, otherwise interactive positioning may be possible if the window manager allows it.

FL_PLACE_HOTSPOT
     The form is so placed that mouse is on the "hotspot". If FL_FREE_SIZE is also specified, interactive resizing will be allowed.

FL_PLACE_CENTERFREE
     Same as FL_PLACE_CENTER|FL_FREE_SIZE, i.e., place the form at the center of the screen and allow resizing.

FL_PLACE ICONIC
     The form is shown initially iconified. The size and location used are the window manager's default.

If no size is specified, the designed (or later scaled) size will be used. Note that the initial position is dependent upon the window manager used. Some window managers will allow interactive placement of the windows and some will not.

There are three values that can be passed for border:

FL_FULLBORDER
     Draw full border with title

FL_TRANSIENT
     Draw borders with possibly less decoration (depends on the window managers behaviour)

FL_NOBORDER
     Draw no border at all

Since multiple forms can be displayed at the same time note that using FL_NOBORDER might have adverse effect on keyboard focus and is not very friendly to other applications (it is close to impossible to move a form that has no border). Thus use this feature with discretion. The only situation where FL_NOBORDER is appropriate is for automated demonstration suites or when the application program must obtain an input or a mouse click from the user, and even then all other forms should be deactivated while a borderless form is active. For almost all situations where the application must demand an action from the user FL_TRANSIENT is preferable. Also note that you can't iconify a form that has no borders and under most window managers forms displayed with FL_TRANSIENT can't be iconified either.

One additional property (under almost all window managers) of a transient window is that it will stay on top of the main form, which the application program can designate using

     void fl_set_app_mainform(FL_FORM *form);

By default, the main form is set automatically by the library to the first full-bordered form shown.

To obtain the current main form, use the following routine

     FL_FORM *fl_get_app_mainform(void);

In some situations, either because the concept of an application main form does not apply (for example, an application might have multiple full-bordered windows), or under some (buggy) window managers, the designation of a main form may cause stacking order problems. To workaround these, the following routine can be used to disable the designation of a main form (must be called before any full-bordered form is shown):

     void fl_set_app_nomainform(int yes_no);

with a true flag.

All visible forms will have the properties WM_CLASS, WM_CLIENT_MACHINE and WM_NAME set. In addition, the first full-bordered form will have the WM_COMMAND property set and is by default the applications main form.

Sometimes it is necessary to have access to the window resource ID before the window is mapped (shown). For this, the following routines can be used

     Window fl_prepare_form_window(FL_FORM *form, int place, int border,
                                   const char *name);
     Window fl_prepare_form_window_f(FL_FORM *form, int place, int border,
                                     const char *fmt, ...);

These routines create a window that obeys any and all constraints just as fl_show_form() does but remains unmapped. The only difference between the two functions is that the first one takes a simple string for the forms name while the second expects a format string like printf(), followed by the appropriate number of further arguments.

To map such a window, the following must be used

     Window fl_show_form_window(FL_FORM *form);

Between these two calls, the application program has full access to the window and can set all attributes, such as icon pixmaps etc., that are not set by fl_show_form().

The application program can raise a form to the top of the screen so no other forms obscures it by calling

     void fl_raise_form(FL_FORM *form);

To instead lower a form to the bottom of the stack use

     void fl_lower_form(FL_FORM *form);

When placing a form on the screen using FL_PLACE_GEOMETRY for the place argument to fl_show_form() the position and size can be set before by using the routines

     void fl_set_form_position(FL_FORM *form, FL_Coord x, FL_Coord y);
     void fl_set_form_size(FL_FORM *form, FL_Coord w, FL_Coord h);
     void fl_set_form_geometry(FL_FORM form*, FL_Coord x, FL_Coord y,
                               FL_Coord w, FL_Coord h);
     void fl_scale_form(FL_FORM *form, double xsc, double ysc);

where fl_set_form_geometry() combines the functionality of fl_set_form_position() and fl_set_form_size() and the last routine, fl_scale_form(), scales the form in horizontal and vertical direction by the factors passed to the function. These routines can also be used when the form is visible.

Sometimes it is desirable to know how large the decoration are the window manager puts around a forms window. They can be obtained by a call of

     void fl_get_decoration_sizes(FL_FORM *form, int *top, int *right,
                                  int *bottom, int *left);

This is especially useful if it is necessary to open a window at some previously stored position since in that case one needs the position of of the window, which deviates from the position reported for the form by the window manager's decorations. Obviously, the above function can't be used for forms that are embedded into another form.

The function

     int fl_form_is_iconified(FL_FORM *form);

allows to test if the (visible) window of a form is in iconified state.

If interactive resizing is allowed (e.g., by showing the form with FL_PLACE_POSITION) it can be useful to limit the range of the size of a form can take. To this end, the following functions are available

     void fl_set_form_minsize(FL_FORM *form, FL_Coord minw, FL_Coord minh);
     void fl_set_form_maxsize(FL_FORM *form, FL_Coord maxw, FL_Coord maxh);

Although these two routines can be used before or after a form becomes visible, not all window managers honor such requests once the window is visible. Also note that the constraints only apply to the next call of fl_show_form() for the form.

To set or change the icon shown when a form is iconified use the following routine

     void fl_set_form_icon(FL_FORM *form, Pixmap icon, Pixmap mask);

where icon can be any valid pixmap ID. (see Pixmap Object for some of the routines that can be used to create pixmaps.) Note that a previously set icon if not freed or modified in anyway.

If, for any reason, you would like to change the form title after the form has been made visible, the following calls can be used (they will also change the icon title)

     void fl_set_form_title(FL_FORM *form, const char *name);
     void fl_set_form_title_f(FL_FORM *form, const char *fmt, ...);

(While the first function expects a simple string, the second has to be called with a format string as printf() etc., followed by the corresponding number of arguments.)

The routine

     void fl_hide_form(FL_FORM *form);

hides the particular form, i.e., closes its window and all subwindows.

To check if a form is visible or not, the following function can be used

     int fl_form_is_visible(FL_FORM *form)'

The function can return that the form is visible (FL_VISIBLE), is invisible (FL_INVISIBLE) or is in the processing of becoming invisible (FL_BEING_HIDDEN).

The most important function for doing the actual interaction with forms is

     FL_OBJECT *fl_do_forms(void);

It starts the main loop of the program and returns only when either the state of an object changes that has no callback bound to it or fl_finish() is called in a callback. In the first case the address of the object is returned, in the latter NULL.

A second way of doing interaction with the currently displayed forms is using

     FL_OBJECT *fl_check_forms(void);

This routine returns NULL immediately unless the state of one of the object (without a callback bound to it) changed. In that case a pointer to this object gets returned. NULL also gets returned after a call of fl_finish().

Then there are two more functions:

     FL_OBJECT *fl_do_only_forms(void);
     FL_OBJECT *fl_check_only_forms(void);

Both functions do the same as fl_do_forms() and fl_check_forms() except that they do not handle user events generated by application windows opened via fl_winopen() or similar routines.

To activate or deactivate a form for user interaction you can use

     void fl_activate_form(FL_FORM *form);
     void fl_deactivate_form(FL_FORM *form);

The same can also be done for all forms at once using

     void fl_deactivate_all_forms(void)
     void fl_activate_all_forms(void)

To find out if a form is currently active call

     int fl_form_is_activated(FL_FORM *form);

A return value of 0 tells you that the form is currently deactivated.

You can also register callbacks for a form that are invoked whenever the activation status of the form is changed:

     typedef void (*FL_FORM_ATACTIVATE)(FL_FORM *, void *);
     FL_FORM_ACTIVATE fl_set_form_atactivate(FL_FORM *form,
                                             FL_FORM_ATACTIVATE callback,
                                             void *data);

     typedef void (*FL_FORM_ATDEACTIVATE)(FL_FORM *, void *);
     FL_FORM_ACTIVATE fl_set_form_atdeactivate(FL_FORM *form,
                                               FL_FORM_ATACTIVATE callback,
                                               void *data);

Also individual objects (or groups of objects if the argument of the function is an object returned by fl_bgn_group()) can be activated and deactivated to enable or disable user interaction:

     void fl_activate_object(FL_OBJECT *obj);
     void fl_deactivate_object(FL_OBJECT *obj);

It is normally useful to give the user a visual clue when an object gets deactivated, e.g., by graying out its label etc.

To find out if an object is active use

     int fl_object_is_active(FL_OBJECT *obj);

     void fl_redraw_object(FL_OBJECT *obj);

This routine redraws the particular object. If obj is a group it redraws the complete group. Normally you should never need this routine because all library routines take care of redrawing objects when necessary, but there might be situations in which an explicit redraw is required.

To redraw an entire form use

     void fl_redraw_form(FL_FORM *form);

For non-form windows, i.e., those created with fl_winopen() or similar routines by the application program, the following means of interaction are provided (note that these do not work on form windows, for which a different set of functions exist, see Windowing Support for details.)

You may set up a callback routine (of type FL_APPEVENT_CB for all user events using

     typedef int (*FL_APPEVENT_CB)(XEvent *, void *);
     FL_APPEVENT_CB fl_set_event_callback(FL_APPEVENT_CB callback, void *data);

The function returns the previously set callback (or NULL).

It is also possible to set up callback functions on a per window/event basis using the following routines:

     typedef int (*FL_APPEVENT_CB)(XEvent *xev, void *user_data);
     FL_APPEVENT_CB fl_add_event_callback(Window win, int xevent_type,
                                          FL_APPEVENT_CB callback,
                                          void *user_data);
     void fl_remove_event_callback(Window win, int xevent_type);

These functions manipulate the event callback functions for the window specified, which will be called when an event of type xevent_type is pending for the window. If xevent_type is 0 it signifies a callback for all event for window win. Note that the Forms Library does not solicit any event for the caller, i.e., the Forms Library assumes the caller opens the window and solicits all events before calling these routines.

To let the Forms Library handle event solicitation, the following function may be used

     void fl_activate_event_callbacks(Window win);

Signals
=======

Typically, when a signal is delivered, the application does not know what state the application is in, thus limiting the tasks a signal handler can do. In a GUI system and with a main loop inside the library, it's even harder to know what's safe or unsafe to do in a signal handler. Given all these difficulties, the Forms Library's main loop is made to be aware of signal activities and invoke signal handlers only when it's appropriate to do so, thus removing most limitations on what a signal handler can do.

The application program can elect to handle the receipt of a signal by registering a callback function that gets called when a signal is caught

     typedef void (*FL_SIGNAL_HANDLER)(int, void *);

     void fl_add_signal_callback(int signal, FL_SIGNAL_HANDLER sh,
                                 void *data);

Only one callback per signal is permitted. By default, fl_add_signal_callback() will store the callback function and initiate a mechanism for the OS to deliver the signal when it occurs. When the signal is received by the library, the main loop will invoke the registered callback function when it is appropriate to do so. The callback function can make use of all of XForms's functions as well as Xlib functions as if they were reentrant. Further, a signal callback registered his way is persistent and will cease to function only when explicitly removed.

It is very simple to use this routine. For example, to prevent a program from exiting prematurely due to signals, a code fragment similar to the following can be used:

     void clean_up(int signum, void *data) {
         /* clean up, of course */
     }

     /* call this somewhere after fl_initialize() */
     fl_add_signal_callback(SIGINT, clean_up, &mydata);

After this, whenever a SIGINT signal is received, clean_up() is called.

To remove a signal callback, the following routine should be used

     void fl_remove_signal_callback(int signal);

Although very easy to use, there are limitations with the default behavior outlined above. For example on some platforms there is no blocking of signals of any kind while handling a signal. In addition, use of fl_add_signal_callback() prevents the application program from using any, potentially more flexible, system signal handling routines on some platforms. Also there might be perceptible delays from the time a signal is delivered by the OS and the time its callback is invoked by XForms' main loop. This delay can be particular troublesome for timing sensitive tasks (playing music for example).

In light of these limitations, provisions are made so an application program may choose to take over the initial signal handling setup and receipt via various system dependent methods (sigaction() for example).

To change the default behavior of the built-in signal facilities, the following routine should be called prior to any use of fl_add_signal_callback() with a true value for flag:

     void fl_app_signal_direct(int flag);

After this call fl_add_signal_callback() will not initiate any actions to receive a signal. The application program should handle the receipt and blocking of signals (via e.g., signal(2), sigaction(2), sigprocmask(2) etc.) When the signal is received by the application program, it should call the following routine to inform the main loop of the delivery of the signal signum, possibly after performing some timing sensitive tasks:

     void fl_signal_caught(int signum);

This routine is the only one in the library that can be safely called from within a direct application signal handler. If multiple invocations of fl_signal_caught() occur before the main loop is able to call the registered callback, the callback is called only once.

The following example illustrates how to handle a timing critical situation (for most application, idle callback, timeouts or FL_TIMER object should be sufficient).

First, you need to define the function that will handle the timing critical tasks. The function will be registered with the OS to be invoked directly by it. There are limitations on what you can do within a (OS) signal handler, in particular, GUI activity is not safe.

     void timing_critical_task(int sig) {
         /* handle timing critical tasks that does not involve GUI */
         ...
         /* Now tell the library the signal has been delivered by the OS.
          * The library will invoke the xforms signal handler when it's
          * appropriate to do so */
         fl_signal_caught(sig);
     }

Now define a (XForms) signal handler that will be responsible for handling the response of the GUI upon receipt of the signal

     void gui_signal_handler(int sig, void *data) {
         /* within an XForms signal handler, there is no limitation
          * on GUI activitity */
         fl_set_object_color(....);
         ...
     }

To make all this work, a set-up similar to the following can be used

     /* setup the signal */
     fl_app_signal_direct(1);
     setitimer(ITIMER_REAL, interval);

     /* setup the OS signal handler */
     signal(SIGALRM, timing_critical_tasks);

     /* setup the XForms signal handler */
     fl_add_signal_callback(SIGALRM, gui_signal_handler, &myData);

Idle Callbacks and Timeouts
===========================

For application programs that need to perform some light, but semi-continuous or periodic tasks, idle callback and timeouts (also FL_TIMER objects) can be utilized.

To register an idle callback with the system, use the following routine

     typedef int (*FL_APPEVENT_CB)(XEvent *, void *);
     FL_APPEVENT_CB fl_set_idle_callback(FL_APPEVENT_CB callback,
                                         void *user_data);

where callback is the function that will get called whenever the main loop is idle. The time interval between invocations of the idle callback can vary considerably depending on interface activity and other factors. A range between 50 and 300 msec should be expected. While the idle callback is executed it won't be called again (i.e., no call of any XForms function from within the idle callback function will call the idle callback function), so it does not need to be reentrant.

It is possible to change what the library considers to be "idle" with the following function:

     void fl_set_idle_delta(long msec);

Here msec is the minimum time interval of inactivity after which the main loop is considered to be in an idle state. However it should be noted that under some conditions an idle callback can be called sooner than the minimum interval.

If the timing of the idle callback is of concern, timeouts should be used. Timeouts are similar to idle callbacks but with the property that the user can specify a minimum time interval that must elapse before the callback is called. The precision of timeouts tends to be quite a bit better than that of idle callbacks since they internally get prefered treatent. To register a timeout callback, the following routine can be used

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

The function returns the timeout ID (note: the function will not return 0 and -1, so the application can use these values to mark invalid or expired timeouts). When the time interval specified by the msec argument (in milli-second) is elapsed, the timeout is removed and the callback function is called with the timeout ID as the first argument. Although a timeout offers some control over the timing, due to performance and CPU load compromises, while the resolution can be better than 10 ms under favourable conditions, it can also be much worse, occasionally up to 150 ms.

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(). See Timer Object, for the usage of FL_TIMER object. For tasks that need more accurate timing the use of signal should be considered.

Global Variables
================

For convenience the library exports a number of global variables. These are:

FL_OBJECT *FL_EVENT
     This is a special object returned by fl_do_forms() etc. when an X event is received that isn't coming from a form under the control of the library, e.g., for a  window that was opened directly via Xlib functions. Upon receiving this special event the application program can and must remove the pending event from the queue using fl_XNextEvent().

FL_FORM *fl_current_form
     This variable is always set to the currently active form.

Display *fl_display
     This variable is set to the display (X server) the program is connected to and is needed as an argument for many Xlib functions. It's recommended not to use this global variable but instead either the function fl_get_display() or FL_FormDisplay() (the latter accepts a form pointer as its argument and will also be safe in future versions of the library that may support multiple connections).

int fl_screen
     This variable is set to the default screen of the display connection.

Window fl_root
     This variable is set to the root window.

Window fl_vroot
     Some window managers have problems with obtaining the corrent root window and applications don't work with the normal root windows. In this case fl_vroot can be used instead.

int fl_scrw, fl_scrh
     These variables contain the screens width and height.

int fl_mode
     The variable contains the visual mode in use, it should be one of the Xlib constants PseudoColor, TrueColor, DirectColor, StaticColor, GrayScale or StaticGray. Alternatively, the functions fl_get_vclass() or fl_get_form_vclass() can be used (the latter accepts a form pointer as its argument and is thus also safe for future versions that may allow multiple connections).

FL_State fl_state[ 6 ]
     This array of structure of type FL_State contains a lot of information about the graphics mode, where each structure has the information for each of the cisual modes. Interesting is only the entry for the visual mode used, fl_vmode.

char *fl_ul_magic_char
     This variable points to the character used to indicate underlining in labels and other texts. If it appears as the very first character of a string all characters in that string are underlined, otherwise the character direct in front of it. Per default it's set to '\b'.