XForms: Review and Tutorial

Exploring XForms, a graphical user interface toolkit for X.
Code Setup

In general, an XForms program has two types of code: the user interface that uses the XForms toolkit and the code that performs some useful actions, i.e., what you want the program to do when, for instance, a button is pressed. The user interface part of a the program consists of the initializing code, the definition of the forms and commands to put the forms on-screen. Then, the program activates XForms' main loop. This loop does the event polling described above.

For example, a tiny program that only shows a “quit” button would look as follows:

#include <forms.h>
/* Call back function, called when `quit`
 * button is pressed
void quit_button_cb (FL_OBJECT *obj, long data)
    exit (0);
void main (int argc, char **argv)
        *myform;        /* form to display */
        *quitbutton;    /* temp variable */
    /* Initializer code: */
    fl_initialize (argv [0], "XMyprog", 0, 0,
                   &argc, argv);
    /* Form definition: */
    myform = fl_bgn_form (FL_FLAT_BOX, 200, 150);
    quitbutton = fl_add_button (FL_NORMAL_BUTTON,
                 50, 50, 100, 50, "Quit");
    fl_set_object_callback (quitbutton,
        quit_button_cb, 0);
    fl_end_form ();
    /* Putting the form on-screen: */
    fl_show_form (myform, FL_PLACE_FREE,
        FL_FULLBORDER, "Button program");
    /* Main XForms loop: */
    fl_do_forms ();
    /* Never reached.. */

The definition of a function quit_button_cb() can be seen in this listing. This function is the callback, which is activated when the user presses the quit button. All following code is in the main() function: the initialization, the form definition, the placing of the form on-screen, and the activation of the main XForms loop. The initialization part is done by the function fl_initialize(), which connects to the X server, parses the command line, etc. In this example, fl_initialize() doesn't need to parse any application-specific flags; hence the two zero arguments.

The form definition is enclosed by fl_bgn_form() and fl_end_form(). Normally you do not write this code by hand, but leave it to the designer (discussed below). The form is started by stating the box type and the size of the form. Between the fl_bgn_form() and fl_end_form() statements, objects can be added to the form. An object is added using fl_add_...(). In this example, only one button is put in the form at the x and y coordinates 50,50 (relative to the form's lower left corner) and with a size of 100 by 50 pixels. The button text is Quit.

Following the button definition, the callback of the button is assigned. Normally you leave even this definition to the designer. In our case, activation of the button leads to a call to quit_button_cb().

Note that to add the button, a local variable quitbutton is used. The same variable is used in the next statement to assign the callback. These are the only statements that require the variable; in other words, an object can be created (typically in a dedicated function generated by fdesign) without having to use a global variable. Readability and maintenance are usually improved by minimal use of global variables. Later on, you can always find the object when its callback function is invoked—the first argument to the callback is always a pointer to the object in question.

After the form definition (terminated by fl_end_form()), the form is placed on the screen using fl_show_form(). The placement type (resizing capabilities, etc.), border type and window title are stated here. After this, XForms' main loop fl_do_forms() is called. This function never returns; the program terminates via the callback.

The Designer

The XForms package contains an excellent designer called fdesign to create and maintain a graphical user interface. The designer lets you create forms, add objects to the forms, set the objects' properties, such as colors, etc., and define the callbacks of the objects.

The designer can be finely tuned. I strongly suggest that you read the chapter in the documentation about the subject. When tuning the designer, you specify the type of the generated code, whether the designer should emit a main() function and/or templates for your callbacks, the stepsize in pixels of the size and placement of objects when designing a form, and lots more.

In fact, you should never modify the code the designer generates; if you want to add special fl_...() calls to modify the objects' appearance, do so outside of the generated code. The reason for this is that later on you may want to change the look of your program, e.g., by changing some colors. All you have to do then is to start up the designer, perform your changes, save the files and re-make. If you modify the generated code, the changes are lost once you re-save the designer file. (Of course, I came to this conclusion the hard way—I made the mistake of modifying fdesign's output.)