Xt, or "X Toolkit Intrinsics", assists in the construction and use
of
widget sets under the X Window System. Xt doesn't directly provide any widget functionality; instead, it provides some depth of abstraction above Xlib, and above C itself, for creating and using high-level widgets. Xt widgets are written using Xlib with complicated Xt functions and data structures, and can be used with Xt calls or wrapper functions (a la Motif). There are a few major Xt widget sets still in major use, namely Athena (Xaw) and Motif, and a few others that are obsolete.
Most modern X widget sets are not made with Xt, partly because other languages and styles have proved to be a better foundation, especially as regards object orientation. GTK+, for instance, is built on it's own Xlib wrapper called GDK. In fact, the abstractive parts of Xt are analogous to those parts of GDK and GObject. QT uses C++ classes and its extension of C++ for similar purposes. (As a side not, GTK+ and QT completely hide XLib; XT does not.)
The original generation of X applications and utilities were created using the Xt widget sets Athena and Motif, and occasionally something obscure like OLIT. Some of these applications, like xterm and xclock, are still in use today. Motif and the CDE have historically been the X11 environment of choice for commercial Unix, though they are slowly being edged out by the newer GNOME, KDE, and others; and are rarely used on modern OSS systems.
The Xt specification states quite clearly that it is supposed to be somewhat object-oriented. Most OO nuts would balk at this, but it is true; Xt allows definition of classes (ostensibly all widgets), instantiation of classes as widgets, inheritance, encapsulation, and polymorphism. That said, it does not have the clarity or ease of use one would expect. Thus, it is Object Orientation proper and not the proverbial language-specific easy-as-pie OO that was supposed to be All That Is Programming by now 10 years ago. A diagram of that sentence is left to the reader as an excercise, and so is trying to figure out if I'm advocating C or eastern religion.
Literature
The definitive source of information about Xt is the specification, available in PostScript with the X11R6 distribution or available on the web (see below). There are a great number of books, mostly from the late 80's, with information on Xlib, Xt and Motif, most notably the Animal Books. It should be noted that the X manuals were some of O'Reilly's earliest publications.
Athena
The Athena widget set is included in the X Window System
distribution and is used to build the popular client programs from that
package. Xterm, xedit, xman, xload and so forth are all programmed with
Xt using Athena. Athena is not visually impressive, but it is free,
standard and well documented. I've written my examples in athena because I like it and it mainly uses Xt calls instead of wrappers, and because you are you are guaranteed to have it if you have X.
Big, scary example program
Here's an example program for your perusal. It's not really a tutorial, though I've included a simpler "Hello, world!" program below. Browse my Athena writeup for a slower intro. Real knowledge of the C language is expected (Pointers, function pointers, struct pointers...).
This program will let you enter text into a text box, and echo to
standard output when you press the button. This program is not very
useful, as it is just a simplified 'echo' with an unwieldy X interface. (It could be used as a hideous text editor via redirection, though.) If I added CLIPBOARD support to it, it could pass as a deranged xclipboard.
#include <stdio.h>
#include <X11/Intrinsic.h>
#include <X11/Shell.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Box.h>
#include <X11/Xaw/AsciiText.h>
// Truly, you need a great number of include files for any Athena
// program of size. The three under X11 are for Xt, and the three
// under X11/Xaw are each for an Athena widget.
Widget window, box, command, clear_command, quit_command, ascii;
/* Each top level window, widget, and widget container is a 'Widget' */
XtAppContext app;
/* And one of these for each application... */
Arg args[2];
int nargs = 0;
/* These two variables will be used for passing Xt arguments the 'hard' way */
String app_resources[] = {
"*command.Label: Write text to stdout",
"*clear_command.Label: Clear",
"*quit_command.Label: Quit",
"*window.Title: Hello, world in Xt/Athena",
"*window.Geometry: 300x200+10+10",
"*ascii.Width: 280",
"*ascii.Height: 150",
NULL };
/* These are the 'fallback resources'; it's naive way of
initially setting some widget properties. */
void print_text(Widget w, XtPointer data, XtPointer call_data) {
/* All functions used as callbacks have this signature.
If you need to pass them special info, you have to do it
through 'data', which is really just a (void *). It's typical
to have a few well named globals so that this is trivial. */
String string = NULL;
/* This is the 'interactive' way to set and get resources.
* We could set more than one at a time this way.
* In this case, we only need to set one value (to get).
* (The easy way is the XtVaGetValues, which you can look up yourself)
*/
nargs = 0;
XtSetArg(args[nargs], XtNstring, &string); nargs++;
XtGetValues(ascii, args, nargs);
printf("%s\n", string);
}
void clear_text(Widget w, XtPointer data, XtPointer call_data) {
String string = NULL;
/* This is the aforementioned 'easy way'.
* It can't be used to give a dynamically varying number of
* arguments, since the arguments are given at compile time.
*/
XtVaSetValues(ascii, XtNstring, &string, NULL);
}
void quit_app(Widget w, XtPointer data, XtPointer call_data) {
/* The manual gives this as the "right way" to end the application.
* Just exiting will work, but that may cause problems on some systems.
*/
XtUnmapWidget(window);
XtDestroyApplicationContext(app);
exit(0);
}
int main(int argc, char **argv) {
window = XtOpenApplication(&app, "window",
// AppContext represents the whole program, and
// we need a name for the top level widget
NULL, 0, // XrmOptions; not used in this example
&argc, argv,
// This allows things like the -geometry or -display options
app_resources, // Pass the fallback resource array
sessionShellWidgetClass, // Indicates we're making
//a normal, top level window.
NULL, 0); // For extra options set on the top level widget
box = XtCreateManagedWidget(
"box", // These names are used for setting resources via
// app_resources, et cetera
boxWidgetClass, // The 'type' of the widget; this symbol is
// from the widget header file
window, // The parent of this widget
NULL, 0); // No arguments being passed, so these are empty.
command = XtCreateManagedWidget("command", commandWidgetClass, box, NULL, 0);
clear_command = XtCreateManagedWidget("clear_command",
commandWidgetClass, box, NULL, 0);
quit_command = XtCreateManagedWidget("quit_command",
commandWidgetClass, box, NULL, 0);
nargs = 0;
XtSetArg(args[nargs], XtNeditType, XawtextEdit); nargs++;
ascii = XtCreateManagedWidget("ascii", asciiTextWidgetClass, box,
args, nargs); // Set some initial values on this widget
XtAddCallback(
command, // Widget who's callback we want
XtNcallback, // Just a plain callback; there are others
print_text, // Function to call
NULL ); // Data pointer to send the function; in our case, none
XtAddCallback(clear_command, XtNcallback, clear_text, NULL );
XtAddCallback(quit_command, XtNcallback, quit_app, NULL );
XtRealizeWidget(window); // Maps window and contained widgets
XtAppMainLoop(app); // Roll the event looop
return (0);
}
/*
Save this as xt-example.c and try
gcc -o xt-ex -L/usr/X11R6/lib -lX11 -lXt -lXaw xt-example.c
for compiling this.
*/
This program is not quite trivial (by tutorial standards), but not useful. It
seperates into initializing the tookit, creating basic elements, and
running the event loop. Each of the buttons calls a function, and the text box takes care of itself. The non-trivial parts of the program mostly have to do with figuring out which widget resource does what, and how to use them. If you have trouble understanding this program, here is a "Hello, world!" to get you started a little slower. Understanding of the rest of the program will probably have to be derived from reading the manual and modifying the source yourself.
#include <X11/Intrinsic.h>
#include <X11/Xaw/Label.h>
#include <X11/Shell.h>
// A handy array for setting some properties initially.
// Forget the NULL and you'll segfault, probably.
String app_resources[] = {
"*label.Label: Hello, world!",
NULL
};
int main(int argc, char **argv)
{
Widget top_level, label;
XtAppContext app;
top_level = XtOpenApplication(&app, "Hello, world!",
NULL, 0, &argc, argv,
app_resources, sessionShellWidgetClass, NULL, 0);
// Consider most of this magic.
// Note app_resources and &app.
label = XtCreateManagedWidget("label", labelWidgetClass,
top_level, NULL, 0);
// No "container widget", since we just need the one
XtRealizeWidget(top_level);
XtAppMainLoop(app);
return 0;
}
In contrast to a great many widget sets, Xt handles all widgets using the same set of functions. Widget sets may (and do) implement wrapper functions and special widget functionality, but they are in many cases fungible.
Xt/Motif
The Motif library was for a long time the de facto standard in Unix
GUIs. Motif, owned by The Open Group, is distributed with most Unices, and there are open source alternatives such as lesstif; still, it
is not quite an open technology. The Motif widget set is more advanced,
both technically and visually, than the Athena widget set. Motif is
widely used for building legacy Unix applications, but some
popular OSS programs like Netscape also use(d) it.
Motif, or Xm, is undoubtedly 'prettier' and more sophisticated than Athena. It is also somewhat abstracted away from Xt using wrapper functions, so Motif code is not as ideal for demonstrating Xt.
Writing widgets in Xt
Certainly, a really good writeup would have an example of how to create a widget in Xt. Unfortunately, even simple widgets like the label require a gigantic amount of boilerplate work just to get started, so I won't bother. If you would like to see it for yourself, browse to xc/lib/Xaw in the X Window System distribution source.
Quick guide to important Xt concepts
Here's a rundown of the basic things you need to construct an Xt program, emphasis on Athena. The first is a list of common data structures and data, the second is a list of functions, and last (which you may want to read first) is a description of passing arguments, both the easy way and the hard way.
Data structures
- XtAppContext: You'll need one of these for a given Xt program. It's passed to XtOpenApplication as a pointer (&app) and passed plain to mainloop.
- Widget: Every top level window, container widget, and widget is contained in a Widget. These are set from widget creation functions, and are passed directly. (They are actually typedef'd pointers.)
- app_resources: The type here is char**, NULL terminated. Called the fallback resources, these are used to set some initial resource values. Passed to XtOpenApplication.
- XrmOptionDescRec: Look the exact format of these up in the manual; each of these specifies a special command-line argument passed directly to a widget resource. A NULL-terminated list of these is passed to XtOpenApplication.
- Arg: An array of Arg's, and an integer counter, are integral in using XtSetValues and XtGetValues. Args are typically set with XtSetArg.
- Callback functions: The signature of these is void name(Widget w, XtPointer client_data, XtPointer call_data) and they are the only type of callback function in Xt. client_data is sent whatever data was specified in the callback, and call_data is a data structure specific to the callback.
Common functions
XtOpenApplication
This is the function you use to start any Xt app. It takes a whole list of things and returns the top-level widget. You can also start the app by calling four other functions manually, to do different tasks. (Most old code contains the function XtAppInitialize, which is 'deprecated' in the spec.) Arguments in order:
- XtAppContext*: Pass &app, your typical XtAppContext.
- String: The name of your application and top-level widget
- XrmOptionDescRec*, int: Array of XrmOptionDescRec entries, and how many there are. Acceptably NULL and 0.
- &argc, argv: Absolutely necessary. Enough said.
- String*: Usually named app_resources or similar, it's a NULL terminated list of Strings indicating initial widget settings.
- Widget: The type of top-level widget. Typically, use sessionShellWidgetClass.
- Arg*, int: Arguments and number, for top-level widget. See explanation below.
XtCreateManagedWidget: The canonical way of making a widget. You could theoretically make a widget with XtCreateWidget and then manage it later, but this is the easy way.
- Returns a Widget, representing the one you've just created.
- String name: Name used in fallback resources, et cetera.
- Class: Class of the widget, something like labelWidgetClass or boxWidgetClass (in Athena).
- Widget parent: The widget will show up inside this widget, if possible. Should be a container widget of some sort.
- Arg* list, int count: Argument list to pass. See below.
XtSetValues, XtGetValues: These are pretty simple. They are used for typical value setting and getting, in the same fashion.
- Widget: The widget to get/set values on
- Arg*, int: List of arguments and the number of items in the list. See below.
XtAddCallback: The function that holds the whole thing together, XtAddCallback can actually be done with XtSetValues but you don't want to do it that way, probably.
More to come. Work-in-progress. Sorry.
Setting and getting values
You should learn early on how to set and get widget resources, using both the XtSetArg macro with initializers and the set/get functions, as well as with the XtVa* functions. The fallback resources are also important.
The hard way is to pass normal initializers (XtOpenApplication, XtCreateManagedWidget) a special array, built up with XtSetArg macro. In contrast to regular argument passing in C functions, this is very laborious and inefficient, but it has advantages, such as being able to set or get a dynamic, arbitrary number of arguments with a single function call (since XtSetArg is a macro). Abstracting this process, though, is not a bad idea, as in the example function:
/* Example function for resizing a widget */
void resize(Widget my_widget, int width, int height)
{
Arg arg_list10; // Allocate more than we need, in this case
int arg_count = 0;
if( width > 0 )
XtSetArg(arg_list[arg_count], XtNwidth, width); arg_count++;
if( height > 0 )
XtSetArg(arg_list[arg_count], XtNheight, height); arg_count++;
if( width > 0 || height > 0 )
XtSetValues(my_widget, arg_list, arg_count);
}
Sources, etc.
- Much thanks to Linux Programming Unleashed
- The Xt and Xlib specs can be gotten with the X11R6 source distribution, or found at ftp://ftp.x.org/pub/R6.6/xc/doc/hardcopy/
- http://www.x.org