Kenton Lee

Adding External Input Sources to the X Toolkit Event Loop

by Kenton Lee

December, 1995

Copyright © 1995 Kenton Lee, All Rights Reserved.

In my June Advanced Application Development column [Lee6-95], I discussed my programming model for X Window System applications. This model relies heavily on the X Toolkit[McCormack]. The X Toolkit is a powerful tool, largely because it uses two simple, but powerful and well integrated, programming frame works. The first, and most visible, frame work is the widget class hierarchy that I've discussed in past columns [Lee3-95]. The second, and just as important, frame work is the run-time event loop.

While the event loop usually requires little code in your program, it actually is the most important run-time component of the X Toolkit. It manages all of your application program's input and distributes it to the appropriate widgets and application callback functions.

Some applications use only minimal event loop functionality: processing X protocol events, such as keyboard, pointer, and window events. Many applications, however, need to react to other input sources, including:

Fortunately, the X Toolkit can process these types of input just as easily as it can handle X protocol events. In this month's Advanced Application Development column, I'll discuss techniques for adding these input sources to your application program's event loop.

Event Loop Programming Model

The event loop can be as simple as one line of code in your program:

XtAppEventLoop(app);

The event loop is actually one of the busiest part of your program, receiving X protocol events from the X server and distributing them to your program's widgets. The widgets then process the events internally, usually through their translation tables. The widgets may modify their user interfaces in response to the events, e.g., echoing key strokes as text or drawing new graphics. The widgets may also call application callback functions, e.g., the push button widget's activate callbacks.

The X Toolkit provides a number of hooks so that the application programmer can easily handle non-X input sources as well. These mechanisms all provide similar application programming interfaces (APIs). The programmer provides:

  1. information about the input source
  2. a callback function to be executed when the input occurs
  3. client data that is passed to the callback function in addition to standard call data
  4. the input source may be canceled at any time (some are canceled automatically)

In the following sections, we'll detail the APIs for the types of non-event input supported by the X Toolkit.

Note that these mechanisms are implemented very efficiently. There is little or no impact on performance when there is no input of the specified type.

Note also that the mechanisms are not preemptive. The event loop will not interrupt a callback function to check for other input sources.

UNIX File Descriptor Input

Multi-program application designs are fairly common in multi-tasking operating systems like UNIX. The multiple programs often share data or control information, generally through UNIX's socket or pipe features. The multiple programs may be executing on the same computer or on separate computers connected to a network (such as the Internet). The communication is often intermittent: the source programs usually write to the sockets or pipes whenever they have data and the destination programs then read the data.

X application programs must be able to process socket and pipe input streams simultaneously with their normal X input event streams. If the two streams are not integrated properly, performance of one or the other will degrade.

The X Toolkit's XtAppAddInput() function adds file descriptor input handling to the event loop. To use this function, simply specify the file descriptor identifying the socket or pipe, the socket condition in which you're interested (usually XtInputReadMask, indicating that data is available to read from the file descriptor), the callback function, and (optional) client data. You may register as many input handlers as you need; one per call to XtAppAddInput().

The event loop automatically checks your file descriptor for the specified condition and calls your callback function when the condition occurs. The file descriptor is monitored continuously until the input handler is canceled with XtRemoveInput(). Note that UNIX-based implementations of the X Toolkit use the select() system call, so there is no affect on performance when the file descriptor is not active.

Note also that the socket or pipe need not be used to pass the actual data. Some programmers find shared memory or shared files more convenient for passing data between processes. The socket or pipe can be used for a simple control protocol; notifying the receiving program that the data has been written to the shared memory or shared file and is now available to be read.

Timers and Time Outs

Another important control stream that the X Toolkit's event loop supports is timers or time outs. Timers allow the application program to request a callback some number of milliseconds in the future. Timers are useful for a wide variety of user interface mechanisms, including:

The API for timers is similar to that of input handlers. XtAppAddTimeOut() registers the timer and XtRemoveTimeOut() cancels it. One major difference in the APIs, however, is that the X Toolkit automatically cancels timers when their callback function is called. If you wish to implement a continuous timer loop, simply call XtAppAddTimeOut() again in your callback function.

You may register multiple timers and the X Toolkit event loop handles the intervals properly. You may distinguish different timers via the identifier passed in the callback function or simply by using different callback functions for each timer.

Timers need not be used only for graphical effects. They can be used any time you want to implement a delay or a time out. For example, in one application I recently developed, I used timers in my auto-save feature. After a configurable number of minutes, my timer callback would save the user's work to a file.

Timers can also be used in conjunction with the socket handlers we discussed in the previous section. For example, in a client-server model, the client program sends requests to a server and waits for responses. If the server program does not respond within a certain time period, the client may want to try a different server. This design can easily be implemented by registering both a socket handler and a time out. If input is received on the socket handler, the client can read the data and cancel (or ignore) the time out. If the time out callback occurs before data is received on the socket, the client can cancel the socket handler and send a request to a different server.

Asynchronous Input

Some applications communicate with asynchronous signals rather than with synchronous sockets or pipes. In the X Consortium's X11R6 release, the X Toolkit added support for asynchronous input. This feature can be used with many operating systems' asynchronous input mechanisms, including POSIX signals[POSIX].

The X Toolkit's asynchronous input handling facility is slightly more complex than the file descriptor and timer mechanisms that we discussed in the last two sections. The asynchronous input handling facility has two levels. One level is operating-system-dependent and the other is OS-independent. The OS-dependent level is necessary, because operating systems vary considerably in the types of asynchronous input they handle and because application programmers may need to take advantage of OS-specific features.

To process asynchronous input through the event loop, the application programmer should:

  1. Specify an X Toolkit event loop callback with XtAppAddSignal(). The API is similar to that of the other input handlers we covered earlier. The handler may be canceled with XtRemoveSignal().
  2. Register an operating system-level handler. For POSIX-style signals, use the operating system's sigaction() system call. This handler should call XtNoticeSignal() and no other X functions.

The X Toolkit event loop will check to see if XtNoticeSignal() has been called one or more times asynchronously (since the last time the event loop checked). If so, it calls the Xt-level callback function.

The following program demonstrates asynchronous input handlers. The signalCB() callback function is called whenever the program receives the POSIX SIGUSR1 signal:


/*
 * Kenton Lee:  X11R6 X Toolkit signal handler demo
 * cc signaldemo.c -lXm -lXt -lX11 -o signaldemo
 */
#include <Xm/Xm.h>
#include <Xm/Label.h>
#include <signal.h>

/* Xt signal handler ID */
static XtSignalId signalID;

/* Xt signal callback */
static void signalCB(XtPointer clientData, XtSignalId *id)
{
    if (*id == signalID)
        printf("received signal\n");
}

/* POSIX signal handler */
static void handler(int sig)
{
    if (sig == SIGUSR1)
        XtNoticeSignal(signalID);
}

void main(int argc, char **argv)
{
    struct sigaction act, oact;
    XtAppContext app;
    Widget top;

    /* create user interface */
    top = XtAppInitialize(&app, "Signal", 0, 0, &argc, argv,
        0, 0, 0);
    XtCreateManagedWidget("label", xmLabelWidgetClass, top,
        0, 0);
    XtRealizeWidget(top);

    /* Xt signal callback */
    signalID = XtAppAddSignal(app, signalCB, 0);

    /* POSIX signal handler */
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGUSR1, &act, &oact);

    /* Xt event loop */
    XtAppMainLoop(app);
}

Figure 1: Example X Toolkit Signal Handler (X11R6 and later)


Because the X Toolkit event loop is not preemptive, its semantics are slightly different from those of asynchronous signals. See the X Toolkit manual for information on some possible problem areas.[McCormack]

Work Procedures

Most interactive programs spend most of their time waiting for user and system input. The X Toolkit event loop simply blocks and waits when it has nothing else to do. If you wish, you may take advantage of this dead time with work procedures. Work procedures are registered with XtAppAddWorkProc(). If there is any user or system input to process, the X Toolkit event loop processes it normally. If there is no input to process, the event loop will execute your work procedure.

Work procedures are a convenient way to execute small pieces of code without blocking your user interface. Two cases where work procedures are very useful are:

Your work procedure must return a Boolean value. False indicates that the work procedure should be called again when the X Toolkit has more time available. True indicates that the work procedure is finished and should not be called again (unless it is registered again). Alternatively, you may explicitly cancel a work procedure with XtRemoveWorkProc().


Conclusion

To beginners, the X Toolkit event loop may appear to be very simple and restrictive. It actually gives you quite a bit of flexibility, however. In this month's Advanced Application Development column, I've presented an overview of the major mechanisms that you can use to extend the event loop to cover input sources other than X protocol events. Hopefully, you'll be able to use these mechanisms within your applications.

Some programmers are tempted to bypass the X Toolkit event loop and use separate mechanisms for dealing with non-X input. This is generally a poor design as coordinating separate input streams is difficult to implement robustly. In most cases, your user interface's responsiveness will suffer, possibly significantly. Fortunately, the X Toolkit's input handling features remove most of the need for separate input handling mechanisms.

The X Toolkit event loop's flexibility can support some very interesting application features. In future columns, I plan to present more detailed examples that use these mechanisms.


References

[Asente]
Paul Asente and Ralph Swick, X Window System Toolkit: The Complete Programmer's Guide and Specification, Digital Press, 1990.
[Lee3-95]
Kenton Lee, "Indirect Motif Widget Resources," The X Journal, March, 1995.
[Lee6-95]
Kenton Lee, "The X Programming Model," The X Advisor, June, 1995.
[McCormack]
Joel McCormack, et al, X Toolkit Intrinsics - C Language Reference, included with the X Consortium's X11R6 release.
[POSIX]
IEEE Std. 1003.1-1990, Standard for information technology - Portable Operating System Interface (POSIX) - Part 1: System Application Programming Interface (API).


Ken Lee is an independent software consultant specializing in X Window System application software. He has been developing UNIX graphical user interface software since 1981. Ken may be reached by Internet electronic mail at kenton @ rahul.net or on the World Wide Web at http://www.rahul.net/kenton/.

Ken has published over two dozen technical papers on X software development. Most are available over the World Wide Web at http://www.rahul.net/kenton/bib.html.