Previous: X Animations, Up: C Programming


9.2.9 Advanced X Window System programming

Applications that run under the X Window System are often built using Xt, the X Toolkit. In Xt, an application is constructed from `widgets' such as text entry fields, buttons, sliders, drawing areas, etc. When the application starts up, each widget is configured to respond appropriately to `events', which include key presses and mouse clicks. After the widgets are configured, control is transferred to the Xt event loop.

GNU libplot can be used within the Xt event loop to draw vector graphics. For this, it would use one or more X Drawable Plotters. An X Drawable Plotter is a Plotter that can plot into an off-screen pixmap or an on-screen window, such as a window associated with a widget.

The following sample application shows how an X Drawable Plotter would be used. The application draws a `C' curve, as defined in a previous section, in a popped-up window. The usual Xt command-line options may be used: the window background color is specified with the ‘-bg’ option, the window geometry with ‘-geometry’, etc. The curve is initially drawn in red, but clicking once with the mouse will redraw it in green. A second mouse click will redraw it in red, and so forth. The application will terminate when ‘q’ is typed.

     #include <stdio.h>
     #include <stdlib.h>
     #include <plot.h>
     #include <X11/Xlib.h>
     #include <X11/Intrinsic.h>
     #include <X11/Shell.h>
     #include <X11/StringDefs.h>
     #include <X11/Core.h>
     
     plPlotter *plotter;
     int green = 0;                  /* draw in green, not red? */
     
     #define MAXORDER 12
     void draw_c_curve (double dx, double dy, int order)
     {
       if (order >= MAXORDER)
         /* continue path along (dx, dy) */
         pl_fcontrel_r (plotter, dx, dy);
       else
         {
           draw_c_curve (0.5 * (dx - dy), 0.5 * (dx + dy), order + 1);
           draw_c_curve (0.5 * (dx + dy), 0.5 * (dy - dx), order + 1);
         }
     }
     
     void Redraw (Widget w, XEvent *ev, String *params, Cardinal *n_params)
     {
       /* draw C curve */
       pl_erase_r (plotter);
       pl_pencolorname_r (plotter, green ? "green" : "red");
       pl_fmove_r (plotter, 600.0, 300.0);
       draw_c_curve (0.0, 400.0, 0);
       pl_endpath_r (plotter);
     }
     
     void Toggle (Widget w, XEvent *ev, String *params, Cardinal *n_params)
     {
       green = (green ? 0 : 1);
       Redraw (w, ev, params, n_params);
     }
     
     void Quit (Widget w, XEvent *ev, String *params, Cardinal *n_params)
     {
       exit (0);
     }
     
     /* mapping of events to actions */
     static const String translations =
     "<Expose>:      redraw()\n\
     <Btn1Down>:     toggle()\n\
     <Key>q:         quit()";
     
     /* mapping of actions to subroutines */
     static XtActionsRec actions[] =
     {
       {"redraw",            Redraw},
       {"toggle",            Toggle},
       {"quit",              Quit},
     };
     
     /* default parameters for widgets */
     static String default_resources[] =
     {
       "Example*geometry:      250x250",
       (String)NULL
     };
     
     int main (int argc, char *argv[])
     {
       plPlotterParams *plotter_params;
       Arg wargs[10];                /* storage of widget args */
       Display *display;             /* X display */
       Widget shell, canvas;         /* toplevel widget; child */
       Window window;                /* child widget's window */
       XtAppContext app_con;         /* application context */
       int i;
       char *bg_colorname = "white";
     
       /* take background color from command line */
       for (i = 0; i < argc - 1; i++)
         if (strcmp (argv[i], "-bg") == 0)
           bg_colorname = argv[i + 1];
       /* create toplevel shell widget */
       shell = XtAppInitialize (&app_con,
                                (String)"Example", /* app class */
                                NULL,              /* options */
                                (Cardinal)0,       /* num of options */
                                &argc,             /* command line */
                                argv,              /* command line */
                                default_resources,
                                NULL,              /* ArgList */
                                (Cardinal)0        /* num of Args */
                                );
       /* set default widget parameters (including window size) */
       XtAppSetFallbackResources (app_con, default_resources);
       /* map actions to subroutines */
       XtAppAddActions (app_con, actions, XtNumber (actions));
       /* create canvas widget as child of shell widget; realize both */
       XtSetArg(wargs[0], XtNargc, argc);
       XtSetArg(wargs[1], XtNargv, argv);
       canvas = XtCreateManagedWidget ((String)"", coreWidgetClass,
                                       shell, wargs, (Cardinal)2);
       XtRealizeWidget (shell);
       /* for the canvas widget, map events to actions */
       XtSetArg (wargs[0], XtNtranslations,
                 XtParseTranslationTable (translations));
       XtSetValues (canvas, wargs, (Cardinal)1);
     
       /* initialize GNU libplot */
       plotter_params = pl_newplparams ();
       display = XtDisplay (canvas);
       window = XtWindow (canvas);
       pl_setplparam (plotter_params, "XDRAWABLE_DISPLAY", display);
       pl_setplparam (plotter_params, "XDRAWABLE_DRAWABLE1", &window);
       pl_setplparam (plotter_params, "BG_COLOR", bg_colorname);
       plotter = pl_newpl_r ("Xdrawable", NULL, NULL, stderr,
                             plotter_params);
       pl_openpl_r (plotter);
       pl_fspace_r (plotter, 0.0, 0.0, 1000.0, 1000.0);
       pl_flinewidth_r (plotter, 0.25);
     
       /* transfer control to X Toolkit event loop (doesn't return) */
       XtAppMainLoop (app_con);
     
       return 1;
     }

Even if you are not familiar with X Window System programming, the structure of this application should be clear. It defines three callbacks: Redraw, Toggle, and Quit. They are invoked respectively in response to (1) a window expose event or mouse click, (2) a mouse click, and (3) a typed ‘q. The first drawing of the `C' curve (in red) takes place because the window receives an initial expose event.

This example could be extended to take window resizing into account. Actually, X Drawable Plotters are usually used to draw vector graphics in off-screen pixmaps rather than on-screen windows. Pixmaps, unlike windows, are never resized.