One of the things I do in my spare time is maintain the Widget FAQ. The Widget FAQ is a 100+ item list of various widgets of different flavors and functionality. We are all familiar with widgets, many of us use them in our work, but what is going on in there? What are these resources that I'm setting? Why do I have to call XtRealizeWidget()? In the next 2 months' columns I will answer these questions and more as we crack some light into this black-box. For any of you who have peeked at widget source, or even just the private header file of a widget, this column is for you. This months discussion will remain limited to what can be discovered by searching header files. Next month, in Widgets 201, I will dissect an actual widget, but the header files make an excellent primer.
To keep things simple (ok -- relatively simple) we explore the ancestor of most widget classes, the Core widget class. (I say most since the widget-like objects gadgets are not subclassed from the Core widget class, but instead are subclassed from RectObj -- but that's a topic for another column.) The Core widget class, coreWidgetClass, contains the basic code that most widgets need: creating windows, handling resources, setting up translation tables and actions, etc. Since most widgets need some or all of these properties, the Core is a good place to start. Plus, as we continue, you should begin to see why the Core is the only place to start.
When a programmer runs into a widget, it is usually in two places, either in a declaration like:
Widget myWidget;
or at widget creation time:
myWidget = XtCreateManagedWidget("Widget1", coreWidgetClass, parentWidget, NULL, 0);
So what is a Widget? And what is a coreWidgetClass? And how are they related?
The first two questions can be answered by taking a peek into header files. Widgets, by convention, have two header files, one that is for public use (for the programmer) and one that is for private use (for the widget writer). The Core widget's header files are Core.h and CoreP.h. Before we look into them however, we need to dive into Intrinsic.h which has the following typedefs:
typedef struct _WidgetRec *Widget; typedef Widget *WidgetList; typedef struct _WidgetClassRec *WidgetClass;
Whenever we are referring to a Widget we actually mean a struct _WidgetRec*. So we now know that a Widget is actually a pointer to a structure defined somewhere. I say somewhere because this typedef is taking advantage of C's opaque type declaration. The compiler knows that a Widget should be the size of the pointer, and the definition of a struct _WidgetRec can be kept elsewhere. A WidgetList is an array of Widget's, (an array of pointers to struct _WidgetRec). And a WidgetClass is another opaque type, a pointer to a struct _WidgetClassRec. Keep this last one in mind, as we will use it later on.
Now we can take a look at Core.h
/* * $XConsortium: Core.h,v 1.10 89/12/12 19:30:32 swick Exp $ * $oHeader: Core.h,v 1.2 88/08/18 15:54:32 asente Exp $ */ /*********************************************************** Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts, and the Massachusetts Institute of Technology, Cambridge, Massachusetts. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the names of Digital or MIT not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ******************************************************************/ #ifndef _XtCore_h #define _XtCore_h typedef struct _WidgetClassRec *CoreWidgetClass; typedef struct _WidgetRec *CoreWidget; externalref WidgetClass coreWidgetClass; #ifndef _XT_CORE_C externalref WidgetClass widgetClass; #endif #endif /* _XtCore_h */ /* DON'T ADD STUFF AFTER THIS #endif */
Besides the intimidating warning at the end of the first comment, there does not seem to be much going on here. Except of course the thing the programmer does need, the definition of the widget class coreWidgetClass of type WidgetClass. Above we discovered that a WidgetClass was an opaque pointer to some structure. The other two typedef's, CoreWidgetClass and CoreWidget I'll skip over for now, and just note quickly that they have to do with subclassing.
You now know that a Widget is an opaque pointer. You also know that coreWidgetClass is of type WidgetClass another opaque pointer. Excited yet? I didn't think so. We seem to still be outside the black box. Looking into CoreP.h will help:
/* * $XConsortium: CoreP.h,v 1.16 89/10/04 12:22:50 swick Exp $ * $oHeader: CoreP.h,v 1.2 88/08/18 15:54:37 asente Exp $ */ /*********************************************************** Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts, and the Massachusetts Institute of Technology, Cambridge, Massachusetts. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the names of Digital or MIT not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ******************************************************************/ #ifndef XtCoreP_h #define XtCoreP_h #includeexternalref int _XtInheritTranslations; #define XtInheritTranslations ((String) &_XtInheritTranslations) #define XtInheritRealize ((XtRealizeProc) _XtInherit) #define XtInheritResize ((XtWidgetProc) _XtInherit) #define XtInheritExpose ((XtExposeProc) _XtInherit) #define XtInheritSetValuesAlmost ((XtAlmostProc) _XtInherit) #define XtInheritAcceptFocus ((XtAcceptFocusProc) _XtInherit) #define XtInheritQueryGeometry ((XtGeometryHandler) _XtInherit) #define XtInheritDisplayAccelerator ((XtStringProc) _XtInherit) /*************************************************************** * Widget Core Data Structures * * **************************************************************/ typedef struct _CorePart { Widget self; /* pointer to widget itself */ WidgetClass widget_class; /* pointer to Widget's ClassRec */ Widget parent; /* parent widget */ XrmName xrm_name; /* widget resource name quarkified */ Boolean being_destroyed; /* marked for destroy */ XtCallbackList destroy_callbacks; /* who to call when widget destroyed */ XtPointer constraints; /* constraint record */ Position x, y; /* window position */ Dimension width, height; /* window dimensions */ Dimension border_width; /* window border width */ Boolean managed; /* is widget geometry managed? */ Boolean sensitive; /* is widget sensitive to user events*/ Boolean ancestor_sensitive; /* are all ancestors sensitive? */ XtEventTable event_table; /* private to event dispatcher */ XtTMRec tm; /* translation management */ XtTranslations accelerators; /* accelerator translations */ Pixel border_pixel; /* window border pixel */ Pixmap border_pixmap; /* window border pixmap or NULL */ WidgetList popup_list; /* list of popups */ Cardinal num_popups; /* how many popups */ String name; /* widget resource name */ Screen *screen; /* window's screen */ Colormap colormap; /* colormap */ Window window; /* window ID */ Cardinal depth; /* number of planes in window */ Pixel background_pixel; /* window background pixel */ Pixmap background_pixmap; /* window background pixmap or NULL */ Boolean visible; /* is window mapped and not occluded?*/ Boolean mapped_when_managed;/* map window if it's managed? */ } CorePart; typedef struct _WidgetRec { CorePart core; } WidgetRec, CoreRec; /****************************************************************** * * Core Class Structure. Widgets, regardless of their class, will have * these fields. All widgets of a given class will have the same values * for these fields. Widgets of a given class may also have additional * common fields. These additional fields are included in incremental * class structures, such as CommandClass. * * The fields that are specific to this subclass, as opposed to fields that * are part of the superclass, are called "subclass fields" below. Many * procedures are responsible only for the subclass fields, and not for * any superclass fields. * ********************************************************************/ typedef struct _CoreClassPart { WidgetClass superclass; /* pointer to superclass ClassRec */ String class_name; /* widget resource class name */ Cardinal widget_size; /* size in bytes of widget record */ XtProc class_initialize; /* class initialization proc */ XtWidgetClassProc class_part_initialize; /* dynamic initialization */ XtEnum class_inited; /* has class been initialized? */ XtInitProc initialize; /* initialize subclass fields */ XtArgsProc initialize_hook; /* notify that initialize called */ XtRealizeProc realize; /* XCreateWindow for widget */ XtActionList actions; /* widget semantics name to proc map */ Cardinal num_actions; /* number of entries in actions */ XtResourceList resources; /* resources for subclass fields */ Cardinal num_resources; /* number of entries in resources */ XrmClass xrm_class; /* resource class quarkified */ Boolean compress_motion; /* compress MotionNotify for widget */ XtEnum compress_exposure; /* compress Expose events for widget*/ Boolean compress_enterleave;/* compress enter and leave events */ Boolean visible_interest; /* select for VisibilityNotify */ XtWidgetProc destroy; /* free data for subclass pointers */ XtWidgetProc resize; /* geom manager changed widget size */ XtExposeProc expose; /* rediplay window */ XtSetValuesFunc set_values; /* set subclass resource values */ XtArgsFunc set_values_hook; /* notify that set_values called */ XtAlmostProc set_values_almost; /* set_values got "Almost" geo reply */ XtArgsProc get_values_hook; /* notify that get_values called */ XtAcceptFocusProc accept_focus; /* assign input focus to widget */ XtVersionType version; /* version of intrinsics used */ XtPointer callback_private; /* list of callback offsets */ String tm_table; /* state machine */ XtGeometryHandler query_geometry; /* return preferred geometry */ XtStringProc display_accelerator;/* display your accelerator */ XtPointer extension; /* pointer to extension record */ } CoreClassPart; typedef struct _WidgetClassRec { CoreClassPart core_class; } WidgetClassRec, CoreClassRec; externalref WidgetClassRec widgetClassRec; #define coreClassRec widgetClassRec #endif /* _XtCoreP_h */ /* DON'T ADD STUFF AFTER THIS #endif */
Ahh! Now we are excited! We come across the definition:
typedef struct _WidgetRec { CorePart core; } WidgetRec, CoreRec;
A Widget is a pointer to this structure. Which contains another structure, the CorePart, which contains many things, some which should look familiar, x, y, width, etc. (For educational purposes, you might want to try including your favorite widget's private file into your source code and dereferencing the now unopaque Widget pointer. This is now where the CoreWidget typedef comes in. To see the innards of a core widget, you can cast a widget to a CoreWidget type, and include the CoreP.h file. If you do this, you should only examine and not change any of these values. As you will probably be bypassing some code inside the widget that sets/retrieves these values and will quite possibly break your program!)
A WidgetClass is defined as:
typedef struct _WidgetClassRec { CoreClassPart core_class; } WidgetClassRec, CoreClassRec;
Once again, the innards, the CoreClassPart contains some fields that should look familiar. The only connection between the Widget and the WidgetClass seems to be in the CorePart where ther is:
WidgetClass widget_class; /* pointer to Widget's ClassRec */
At least we have found a connection, but what is it?
myWidget = XtCreateManagedWidget("Widget1", coreWidgetClass, parentWidget, NULL, 0);
myWidget is the instance. coreWidgetClass is the class.
In terms of a Core widget, everything within the CorePart belongs to the instance of a widget. Everything within the CoreClassPart belongs to the class, shared among each instance, thus the:
WidgetClass widget_class; /* pointer to Widget's ClassRec */
in the CorePart. This pointer allows each instance access to the class part of the widget.
Browsing other widgets' private header files you could see structures of the form:
typedef struct _CompositeRec { CorePart core; CompositePart composite; } CompositeRec; typedef struct _CompositeClassRec { CoreClassPart core_class; CompositeClassPart composite_class; } CompositeClassRec;
Each of these structs include a part from the Core widget. This is known as subclassing. By having the Core parts in the structure, the new widget inherits all the functionality of both the Core instance and class parts. For example, the new widget will automatically have x and y position resources, since the Core instance has them. The same is true for the class record.
For each new subclass, a widget writer decides which superclass has the functionality that resembles closest what will be needed in the new widget class. The Shell widget uses:
typedef struct _ShellClassRec { CoreClassPart core_class; CompositeClassPart composite_class; ShellClassPart shell_class; } ShellClassRec; typedef struct { CorePart core; CompositePart composite; ShellPart shell; } ShellRec, *ShellWidget;
Note two things here. First widgets are subclassed by chaining the parts of previous widget subclasses. Since the Shell is a subclass of Composite, and Composite is a subclass of Core, the Shell widget records must contain parts from both. Second, since the Xt library, where widgets are defined, is written in C, the superclass parts must be in super-to-subclass order. This is due to how the fields are stored in memory. To be able to access fields in superclass parts, they must be at the same offset in the record structure. This also means that a ShellWidget can be casted to a CompositeWidget or a CoreWidget without any problems. Since the CoreWidget is the superclass of all widgets, and a Widget is the same as a CoreWidget, any new subclass can also be passed around as a Widget.
So ends Widget 101, more properly called "Widget Header Diving." Just by looking through header files we can learn alot about widgets -- but not everything. Next month, the Core source file will be explored and the answers to such questions like, "Why do I have to call XtRealizeWidget()?" will be presented. For now, you should be able to see how Widget can be the generic type for so many different widgets, and also a first look at the differences between a widget and its class.
Asente, Paul J., and Swick, Ralph R., X Window System Toolkit, The Complete Programmer's Guide and Specification, Digital Press, 1990.