The code found in Listing 1 defines the user interface that’s used throughout the remainder of this article to illustrate the methods of requesting and processing events. The widget set that is used in the example is Athena. Athena was chosen because it is likely to be available on most systems. This sample has been tested under Linux using both Red Hat 6.1 and Solaris version 2.6.
Listing 2 provides a makefile for compiling and testing the sample. Figure 1 shows the results that are anticipated from the execution of the compiled code.
Figure 1: xevnts Application Interface
When authoring an X Window system application that employs the X Toolkit Intrinsics library, control of the program is quickly relinquished to the XtAppMainLoop function, as seen in Listing 1.
322: XtAppMainLoop( appContext );
This loop is responsible for monitoring the application's event queue and dispatching events that the X Server sends to any registered procedure.
The process of extracting and dispatching events defines the core of the X-based application's program flow.
When the XtAppMainLoop function is entered, it never returns. Within an infinite loop, events are continually removed from the application's queue using XtAppNextEvent, and are sent to the event handler functions specified by the program's author using XtDispatchEvent.
Writing an application to follow this method of program execution is known as event-driven programming because the events dictate the branches taken during the program's flow.
The user’s navigation habits largely influence the generation of events. Therefore, it will never be known which procedure that was registered by the programmer will be called at any given moment, nor is it possible to predict the order in which the procedures will be invoked. For example, consider the assignment of callback functions to the buttons created in Listing 1:
130: XtAddCallback( btn, XtNcallback, 131: callbackProc, data->func );
The function pointer that is stored in the second field of the menuButtons array determines which procedure is registered for each of the buttons created. In the example, if each button were assigned a unique procedure, it would be impossible to know the order in which they would be invoked because you could not predict the user's actions when selecting from the available menu items. Therefore, writing event-driven programs requires more resilient practices than linear or iterative methods, in which the possible branches of execution are known because they are dictated by the author.
A final caution to apply when constructing event-driven applications using the X Window system is the asynchronous communication that takes place between the client and the X Server.
The X Server generates and communicates events asynchronously to the applications it serves. This implies that events may occur in any order, having no relationship to the number of events generated in response to a single user action.
With an understanding of the components used in the development of X Window applications and the unique nature of their event-driven program flow, we now shift our focus to the methods provided by X for registering procedures in support of an application's functionality.