Widgets 102

by John Cwikla

Copyright © 1995,1996,1997,1998,1999,2000,2001,2002 John Cwikla, All Rights Reserved.

Last month, I began my journey into the world of widget internals by examining at the Core widget's header files. This month the adventure continues, the Core widget's source file being our target.

To recap the important points from last month:



Dissection

Since I am hoping to give to you a beginning glimpse into the guts of a widget, the best way, I think, is a line by line dissection of Core's widget class record. But what is the widget class record, you ask? The widget class record is the actual, single instance of a widget class. The widget class record is the data structure that resides in memory when you use one or more widgets of that class. When you specify a widget class in XtCreateWidget(), you are passing in a reference to this structure, as we will see later one. For now, let's jump right in!

Again we will use the Core widget class as it is the superclass of most of the widgets you will use. (The parts of the Core widget class are being taken directly from the Core.c file that is part of the X distribution, and it may be beneficial to following along ther as I continue). The Core widget class record, line by line:

externaldef(widgetclassrec) WidgetClassRec widgetClassRec = {

This is the initialization of the widget class variable. When linked into your program widgetClassRec becomes a global variable that can be accessed from your program. You do not access this variable directly, but instead another variable that is a pointer to this one (ie, &widgetClassRec) will be the widget class pointer. Simply put, when you use a widget class, you are actually using a pointer to a global variable (in this case widgetClassRec).

/* superclass */ (WidgetClass)&unNamedObjClassRec,

The superclass field contains a pointer to the parent classes class record. This is how chaining is performed. The coreWidgetClass superclass is the daunting unNamedObjClassRec. This class was put there in case some hierarchical change needed to be made without breaking things. But I thought you said the coreWidgetClass was the superclass of all widget classes? What's the deal with this unNamedObjClassRec? Remember, I said most. If you chase up the hierarchy you see the following class records:

	widgetClassRec
	unNamedObjClassRec
	rectObjClassRec
	objectClassRec

So as it turns out objectClass is the real grand-daddy of all widgets and gadgets. Alright, so gadgets have shown up again, so I'll get them out of the way with a little explanation. One of the major features of the coreWidgetClass is that it creates a window for its instance. Gadgets on the other hand do not have a window, they are basically windowless widgets. However both do share some things, like x and y coordinates, and a destroy callback. These resources are defined higher up in the class hierarchy. Thus most gadgets are subclassed from the retObjClassRec instead of widgetClassRec. For day to day widget thinking, unless you are writing non-widget objects, you do not need to worry about these superclasses. If you want to see the division of resources among these superclasses, the Xt source for these objects should be relatively straightforward enough to understand. Enough said, let's move on!

/* class_name */ "Core",

Each class should have a unique name. This name is mainly used for getting resources. It is possible to define resources for an entire widget class, for instance by specifying in a resource file:

*Core.background: blue

A more useful resource to set would be the translation table that you would want all widgets of a class to share.

/* widget_size */ sizeof(WidgetRec),

The size in bytes of an instance record. Every Widget instance is a malloc'd block of memory, so the Intrinsics need to know how big to make the block. Remember, widgets are subclassed by chaining, so each subclass widget_size field will be the value of the parent class's widget_size field, plus the size of the new widget's additional instance fields.

/* class_initialize */ NULL,

This function (if non-NULL) is called once before any widget instances of the class are created. A widget writer can use this function to complete tasks that need to be done before any widgets are created. Resource converters, for example, can be registered in this function since resource converters only need be registered once. This function does not get any parameters so anything performed here cannot expect to use any information in the widget class.

/* class_part_initialize*/ CoreClassPartInitialize,

The class_part_initialize routine (if non-NULL) when called gives the widget writer the opportunity to modify fields in the widget class record. However, not just this class_part_initialize routine is called, but all superclass class_part_initialize routines also, from super to subclass order. Thus, if you subclass a widget from Core that has a function BobClassPartInitialize, CoreClassPartInitialize will be called before BobClassPartInitialize. Why do this? This is how the intrinsics perform inheritance. Since the superclass has first dibs at the class part record, they can modify them as they please. This will be more clear if we actually look at some of CoreClassPartInitialize():

static void CoreClassPartInitialize(wc)
    register WidgetClass wc;
{
    /* We don't need to check for null super since we'll get to object
       eventually, and it had better define them!  */

    register WidgetClass super = wc->core_class.superclass;

    if (wc->core_class.realize == XtInheritRealize) {
    wc->core_class.realize = super->core_class.realize;
    }

[ snip... ]

    if (wc->core_class.tm_table == (char *) XtInheritTranslations) {
    wc->core_class.tm_table =
        wc->core_class.superclass->core_class.tm_table;
    } else if (wc->core_class.tm_table != NULL) {
    wc->core_class.tm_table =
          (String)XtParseTranslationTable(wc->core_class.tm_table);
    }


    if (wc->core_class.actions != NULL) {
    Boolean inPlace;

    if (wc->core_class.version == XtVersionDontCheck)
        inPlace = True;
    else
        inPlace = (wc->core_class.version < XtVersion) ? False : True;

    /* Compile the action table into a more efficient form */
        wc->core_class.actions = (XtActionList) _XtInitializeActionData(
        wc->core_class.actions, wc->core_class.num_actions, inPlace);
    }
}

The CoreClassPartInitialize() function is doing some special things. First, it checks some fields for special inheritance flags, like XtInheritRealize. If the class has defined the appropriate flag instead of an actual value, the Core class sets the value to the superclass's value at that field. If we look at:

    if (wc->core_class.tm_table == (char *) XtInheritTranslations) {
    wc->core_class.tm_table =
        wc->core_class.superclass->core_class.tm_table;
    } else if (wc->core_class.tm_table != NULL) {
    wc->core_class.tm_table =
          (String)XtParseTranslationTable(wc->core_class.tm_table);
    }

the CoreClassPartInitialize checks to see if the new widget class has defined any new translations for this widget. If it has then it parses them into an internal form. If instead, XtInheritTranslations has been used, the superclass translations are installed. Suppose your widget has a translation table with

< Btn1Down > : beep()

as part of it. If you subclassed the widget and installed new translations the new translations would be used. If the field was NULL, no translations would exist in the new widget class. If you used XtInheritTranslations this subclass (and any of its subclasses that were chained accordingly) would inherit that translation.

/* class_inited */ FALSE,

Since some things for a widget class are done only once there needs to be a flag set to indicate whether the widget class has been initialized. This is it.

/* initialize */ CoreInitialize,

The initialize function is called when a new widget instance is created. This routine allows widget programmers a chance to check the values in the widget instance record, initialize variables, compile resources, or allocate server resources like GC's and pixmaps. Initialize procedures are also chained, called in super-to-subclass order, allowing overriding of resource values set by the super-class.

/* initialize_hook */ NULL,

This is for backward compatibility with earlier releases of the Intrinsics. This function used to be called before the initialize routine. Now all the same information is available to the initialize routine, and the initialize_hook routine is no longer needed.

/* realize */ CoreRealize,

The realize procedure is called when XtRealizeWidget() is called for a widget (or a parent of the widget). In the realize procedure the Window is created for a widget. Since most widgets do not create any special type of window, they can use XtInheritRealize, which will chase up the superclass chain until they reach a parent that will create the window, ie, whose realize procedure is non-NULL and not XtInheritRealize. For most widget classes the chain goes up the the coreWidgetClass calling CoreRealize. CoreRealize is a simple function:

static void CoreRealize(widget, value_mask, attributes)
    Widget       widget;
    XtValueMask      *value_mask;
    XSetWindowAttributes *attributes;
{
    XtCreateWindow(widget, (unsigned int) InputOutput,
    (Visual *) CopyFromParent, *value_mask, attributes);
} /* CoreRealize */

A widget class may want to override the realize procedure if there were some special attributes needed for the widgets window. An example may be using the Shape Extension for non-rectangular widget windows.

/* actions */ NULL,
/* num_actions */ 0,

I'll take these two fields together. You should already be familiar with translations. From our example above:

< Btn1Down > : beep()

The part left of the colon is the translation event, in this case an event consisting of mouse button 1 being pressed. The right side of the colon is the action to be performed. Somewhere this action procedure needs to be listed, and it is, at the actions field. This field is a structure that consists of a string-to-function mapping, and would have a field:

{ "beep", (XtActionProc)beep() }

The num_actions field is the number of string-to-function pairs in your action list.

/* resources */ resources, /* num_resources */ XtNumber(resources),

The resources field holds a list of XtResource structures. As one of its resources, Core has:

    {XtNmappedWhenManaged, XtCMappedWhenManaged, XtRBoolean, sizeof(Boolean),
         XtOffsetOf(CoreRec,core.mapped_when_managed),
     XtRImmediate, (XtPointer)True},

When you use XtSetArg(), or a name/value pair in a resource file, the intrinsics use this table (and the tables of all the widget superclasses) to set the value to the named resource in the widget. This resource has the following pieces:

typedef struct _XtResource {
    String  resource_name;  /* Resource name                */
    String  resource_class; /* Resource class               */
    String  resource_type;  /* Representation type desired          */
    Cardinal    resource_size;  /* Size in bytes of representation      */
    Cardinal    resource_offset;/* Offset from base to put resource value   */
    String  default_type;   /* representation type of specified default */
    XtPointer   default_addr;   /* Address of default resource          */
} XtResource, *XtResourceList;

/* xrm_class */ NULLQUARK,

A private field for the intrinsics. Should always be initialized to NULLQUARK.

/* compress_motion */ FALSE,

A boolean that instructs the Intrinsics to take multiple mouse motion events and turn them into one event before passing them onto the widget.

/* compress_exposure */ TRUE,

Like compress_motion, this field instructs the Intrinsics on how to handle exposure events.

/* compress_enterleave*/ FALSE,

Another event compression boolean.

/* visible_interest */ FALSE,

If TRUE, the visible field in the Core instance record is kept up-to-date.

/* destroy */ CoreDestroy,

This routine is the complement of the initialize routine. Remember all of the things we allocated in the initialize routine? Here we have a chance to free them.

/* resize */ NULL,

Unlike Core, most widgets would have a resize procedure, or have it set to XtInheritResize. When XtResizeWidget() is used on a widget, if this field is non-NULL, the function is called.

/* expose */ NULL,

The exposure routine, can be NULL, XtInheritExpose called when the widget's window receives an expose event.

/* set_values */ CoreSetValues,

This routine is called whenever XtSetValues() or XtVaSetValues() are used on the widget. It passes in an old copy of the widget, a widget with the requested values change, and a copy of the return widget. The Intrinisics have already used the XtResource structure to (hopefully) convert the resources and copy them into the appropriate are in the widget. The widget writer now has a chance to verify these new values, modify them, and inform the intrinsics that the widget should be forcefully redrawn if necessary.

/* set_values_hook */ NULL,

Set_values_hook is another deprecated routine, there for backward compatibility.

/* set_values_almost */ CoreSetValuesAlmost,

A widget needs to have a set_values_almost routine if changing a resource value could result in a geometry change. If a widget does not have its own routine, it should use XtInheritSetValuesAlmost.

/* get_values_hook */ NULL,

Some widgets need a get_values_hook procedure if the value of a resource they keep internally in a widget, is not the same value they would give back when XtGetValues() is called on a widget. The widget may have compiled a list into an internal form for quicker access and needs to decompile the resource before returning the value. Otherwise, XtGetValues() just copies from the resource offset, resource size number of bytes. This is why it is absolutely necessary to give the appropriate datatype's address to the XtGetValues() routine!

/* accept_focus */ NULL,

The Intrinsics call a widget's accept_focus routine (if non-NULL) to notify the widget that it can take the input focus. This field can also be XtInheritAcceptFocus().

/* version */ XtVersion,

The version number of the widget. If you have ever gotten the message to the effect of, "Version type mismatch" when running an X program, then this field does not match the current XtVersion.

/* callback_offsets */ NULL,

A private field used by the Intrinsics. Should be set to NULL.

/* tm_table */ NULL,

If this widget has its own translation table, this field should point to it. Can be NULL or XtInheritTranslations.

/* query_geometry */ NULL,

Widget layout is usually based on the geometry of child widgets. Sometimes a widget has the opportunity to allow their children to be whatever size they would like. If a widget can calculate a preferred size it will have a query_geometry routine. If this field is NULL, the Intrinsics will assume the childs current width and height are its preferred geometry. This field can also be set to XtInheritQueryGeometry.

/* display_accelerator */ NULL,

Sometimes a widget needs to modify its appearance if it has accelerators that get installed onto another widget. If so, the widget should have a display_accelerator procedure.

/* extension */ NULL
}
};

An extension record allowing future versions to be backward compatible.

And finally!

externaldef (WidgetClass) WidgetClass widgetClass = &widgetClassRec;

externaldef (WidgetClass) WidgetClass coreWidgetClass = &widgetClassRec;

These are the global variables I mentioned early in the article. The variables are the pointers to the class records, and are the WidgetClass's used when creating a widget.


Here endeth today's lesson. This may seem like alot of information, but hopefully you can use it as a guide. If you ever need to look at widget source, I think you will now have a place to start. At the least you should be able to see that there is no magic going on in the Intrinsics, (ok not much!) and widget just consist of things every C programmer is used to -- structures, lists and pointers.


References

Asente, Paul J., and Swick, Ralph R., X Window System Toolkit, The Complete Programmer's Guide and Specification, Digital Press, 1990.

O'Reilly and Associates, Inc. X Toolkit Intrinsics Reference Manual, Second Edition, 1991. A