Many of you are familiar with my past X Window System writings in The X Journal, UNIX Review, and other publications. Most of these papers discuss the practical side of developing X-based application software. My new column in The X Advisor will continue this focus, with an emphasis on the needs of software engineers and programmers who use X to develop commercial-quality products.
Possible future topics, in no particular order, include:
Within each of the above topics, I will focus on the special needs of X application programmers. If there are any other topics you'd like to hear about, please send them to me by electronic mail to kenton @ rahul.net.
In this first column, I'll review what I have found to be the most effective X application programming model. As most professional X programmers have found, you can develop X applications in many ways, but some designs work much better than do others. Unfortunately, most of the X tutorial books spend little or no time on programming models for large X applications. My future columns will be based on this programming model, so I think it will be useful to detail it here.
The model presented here is that used by the original developers of the X Window System and the X Toolkit intrinsics. By using this model, you will be able to take full advantage of X's features, helping you create portable, robust code that is easy to maintain and enhance.
Most X tutorial books begin with a picture that looks like this:
Experienced X programmers should be familiar with the application programming interfaces (API) to all of these layers. The tutorial books usually do a pretty good job of explaining the functionality of the various layers. Unfortunately, they often don't do as good a job at explaining how to use the layers in real-world application programs.
Note that the diagram above also uses the Motif widget set. While other toolkits are available, Motif is by far the most popular among professional X application builders, so I will focus on it. Most of the techniques, however, will also apply to other tools.
While the diagram in Figure 1 is a good way to learn about the different
parts of X, it is not very useful for learning X application design. I
think this is a much better way to visualize the architecture of an X application:
Figure 2 more closely describes the object-oriented, run-time flow of control within a well written X applications. The application-specific part of the program should try hard to interact with X in only two ways:
In turn, the user interacts with the program in only two ways:
This programming model encourages several levels of modularity and data abstraction, all of which improve the design of your application:
Each of these separations improve the robustness of your program. Let's look at each in more detail.
Of course, the main reason for using any software toolkit is to take advantage of the functionality provided by that toolkit. The Motif widget set is built upon an object-oriented framework. This framework provides important functionality that is used or inherited by individual Motif widgets. For example, all applications built from Motif widgets automatically get this widget functionality:
If your application does not use Motif well, many of these features (as well as others that may be added in future releases of Motif) will be unavailable in your user interface. At best, you will confuse some regular Motif users. At worse, some key features that users require will not be available.
Our design model encourages application programmers to use widget interfaces whenever possible. Widgets provide a simple API to complex underlying functionality. By wisely using widgets, programmers can isolate their applications from the lower layers of X.
Widget programming is mostly specification. The application specifies the widget hierarchy, widget resources, and widget callbacks. The widgets then do all the work of managing the graphical user interface.
While many applications specify their widgets through C code, many others take the separation concept one step further by specifying them with Motif's user interface language (UIL). UIL is a language designed solely for efficiently specifying widget hierarchy, resources, and callback functions. Motif does not require application programs to use UIL, though it does provide many features that make UIL convenient.
Widgets internally deal with complex lower level X tasks such as rendering Xlib graphics on different types of hardware, laying out sub-windows, interacting with the window manager, collecting and processing X events, and managing cut-and-paste and drag-and-drop. Some X events will, of course, cause application callbacks to be activated. These should only be significant events that require application processing.
Callback functions may, of course, modify the application's widgets, e.g.,:
As we will see later, however, programs should try to avoid using most X functions in callback functions.
The previous section discussed how our programming model isolates application from the details of X. Related to this is isolating the application's user interface design from from X.
Application code often lives for many years (or decades), while graphical user interface technology has been changing relatively quickly. By maintaining a separation between your user interface design and your user interface toolkit, you can quickly adapt new user interface techniques.
With the X Toolkit and Motif, user interface functionality is encapsulated by the widgets. New user interface technology can easily be added to Motif by simply adding new widgets and, perhaps, a few new convenience functions. Since new widgets will support the standard widget API (resources and callback functions), they can be easily added to any Motif-based application.
Also, new releases of widgets sets often enhance the widgets' functionality, but normally preserve the semantics and syntax of the widgets' resources. Thus, any functional enhancements to the widgets can be incorporated into your application with little or no development effort required.
Finally, because the widget API is so simple, programmers often create formal separations between the two in their designs. You may, for example, separate your user interface modules into application-specific parts and widget-specific parts with a simple, well define API separating the two. Such a design allows applications to be relatively efficiently ported from Motif to other, possibly significantly different, user interface toolkits by simply replacing the widget-specific modules.
The application/widget interface is only half of your user interface design. The other, and more important, half is the actual presentation to the user. The user interface is usually developed through a mix of human factors studies and rapid prototyping. By using a widget-oriented design, the development of the user interface is greatly simplified.
In most applications, the syntax of the user interface should map cleanly to the application's widget hierarchy. Individual primitive widgets usually map to specific user functions. A hierarchy of user functions is usually implemented as a hierarchy of manager and primitive widgets.
By closely mapping your functionality to your widgets, you can you can easily modify the two together. Modifications to your user interface should require changes to only the related parts of your widget hierarchy. A simple changes such as moving a group of widgets from one part of the screen to another, is simply a matter of changing the widget parent of the top of the tree. More complex changes are often not much more difficult.
Many Motif-based user interface builder tools are now on the market. Many of these are very good. All of these tools are heavily widget based. In order to really take advantage of such a tool, you must use a widget-oriented design, such as the one we are discussing here.
I've seen many applications that violate our widget-oriented design model. In all cases, I disagree with their design decisions and feel that these products would have been improved by following our model. There are two common ways that these applications violate the model:
The reason the X Toolkit provides explicit support for only one event loop is that graphical user interfaces generally must be able to respond to any of a wide variety of user or system inputs at any point in time. A single event loop can easily monitor all event streams and efficiently distribute them to the various widgets.
You may think that you can more efficiently handle input by putting separate event loops into different parts of your application. In the long run, this is not likely to work robustly since even minor changes to your event handling may involve modifying a number of event loops. The only exception to this that I have seen work well is very short lived event loops that handle blocking popup dialog windows. I advise, however, avoiding even these when possible.
The Xlib issue is probably a more common failing in many application programs. Some programmers regularly use Xlib graphics functions in their applications, usually in callback functions related to a drawing area widget. The problem with a design like this is that it violates the tight modularity that widgets provide. In order to deal with Xlib, applications must understand and deal with lot of their low level run-time environment (e.g., graphics contexts, visual types, various event types), making them much more complex and fragile. I recommend that you avoid using Motif's drawing area widget whenever possible.
How, you might ask, can your application code avoid dealing with Xlib? Your application may need a custom graphical display that can not be produced by the standard Motif widget cases. The answer is to develop your own widget class to produce your graphics.
Aside from the modularity issue mentioned in the previous section, there are other good reasons for encapsulating your graphics with custom widgets. First, you will have access to a wide range of X Toolkit intrinsics functionality that is designed to support widget internals. These functions provide:
Widgets are object-oriented, so your widget class will be able to take advantage of services provided by its superclasses. Some of the above functionality is actually inherited from the Core widget class. Other widget classes, such as Motif's XmPrimitiveand XmManager base classes, may provide additional features for their subclasses, including:
Most of this functionality improves the efficiency and interoperability of your program. It also provides your widget with a standard programming interface, increasing its reusability, both within your program and within other programs your company might develop later.
As you can see, the X Toolkit intrinsics and Motif's base widget classes do most of the work of dealing with Xlib for you. Writing a new primitive widget is often simply a matter of filling in the pre-defined creation, resource handling, exposure handling, and destruction methods. The rest of the widget's functionality is inherited from it's superclass.
Future columns will discuss some of the details of writing your own widgets. I also recommend the excellent tutorial by Asente and Swick. Motif 1.X users may also want to read Writing Your Own OSF/Motif Widgets by McMinds and Whitty. Motif 2.0 users should also read the widget writing manual included with that release.
This column discusses the most effective X application programming model. These guidelines are not hard and fast rules. Programs that violate this model will probably compile and may even work reasonably well. On the other hand, programs that follow the model will usually be significantly more robust and maintainable than those that do not. I've used this model in a wide variety of X-based application programs and have found it to work very well.
While this programming model is simple, it is not simplistic. The majority of X applications can (and should) be written this way. Many need use only the standard Motif widgets. Others may want to add some of their own.
This is probably the only model that takes full advantage of the functionality of X. If you cannot think of a way to map your application's design to the above figure, you are probably not using X well. Straying from this model will almost always get you into trouble, either immediately or later when someone else tries to maintain your code.
I hope that you've found this first Advanced Application Development column to be useful. Future columns will cover other subjects that should interest the professional X programmer. If there are any in particular that you'd like me to cover in the future, please drop me a line.
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.