by John Cwikla

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

Here is something interesting to try: put *depth: 3 in your .Xdefaults file, and run your favorite X program. Next, you might want to try *visual: DirectColor.

If the program uses widgets you probably received an error:

X Error of failed request:  BadMatch (invalid parameter attributes)
  Major opcode of failed request:  1 (X_CreateWindow)
  Serial number of failed request:  23
  Current serial number in output stream:  31

While these examples may seem odd, the same thing happens for most X programs even if you used a display capable of a 3 bit depth, or one capable of using a DirectColor visual class, if that depth or that visual was not the default of the server. (A more likely scenario is trying to use *depth: 24 on a 24 bit capable display with a default 8 bit visual, or perhaps *depth: 8 on a 24 bit capable display with a default 24 bit visual.) In any case, most Xt programs fail to deal with non-default visuals.

Some Background

A visual describes how a pixel should be interpreted, the depth specifies the number of planes in a window or colormap, and the colormap is a table the pixel uses to get red, green and blue values. When a window is created, two requirements must be met otherwise a BadMatch error results.

First, the depth must match the visual's depth. Second, the visual must be the same visual used to create the colormap. To meet these requirements we must carefully pick our depth, visual and colormap. Since one of the attributes of a visual is its depth, when we choose a visual we also choose a depth. A colormap is created with a visual, so once we have our visual we can easily get a compatible colormap. The real obstacle is picking an appropriate visual to suit our needs.

At initialization, each supported visual is given a unique ID on the display. (To check which visuals your display supports you can use the xdpyinfo program.) One of the visuals is chosen as the default, and used when the root window is created. We can choose our visual by ID, or, given a visual class and/or depth, we can try to find an appropriate match. There are two X calls we use to obtain information about supported visuals -- XMatchVisualInfo() and XGetVisualInfo().

XMatchVisualInfo() takes depth and visual class parameters, and if successful, returns an XVisualInfo **, a pointer to a structure containing a visual's attributes. XGetVisualInfo() takes a mask and template and returns a list of all matching visuals the display supports If our selected visual is not the same visual used by the root window a colormap must be created with that visual. Once we have the depth, visual and colormap we can create a window.

To create a window, call either XCreateWindow() or XCreateSimpleWindow(). One of the main differences between XCreateWindow() and XCreateSimpleWindow() is XCreateSimpleWindow() uses CopyFromParent for the depth, colormap and visual. XCreateWindow() requires the depth, visual and colormap as parameters, or, instead of actual values, CopyFromParent may still be used. CopyFromParent means the value should be the same value used to create the parent window. For the top window in your hierarchy (or your shell) CopyFromParent takes the value from the root window (set when the X server was initialized). For a shell to use a non-default visual we cannot use CopyFromParent for the depth, visual and colormap, we will need to specify the values.

BadMatch Errors

So why did the examples produce an error? The problem stems from the fact that the visual and colormap resource converters act independently of one another. It is possible to put correct resources in your .Xdefaults file and have a window successfully created on a non-default visual, however, this requires both the visual and colormap to already exist on the server. While you can choose a valid visual using xpyinfo to get its ID, colormaps usually need to be created. For widgets, the colormap and depth values exist in the core instance record, while the visual is part of the shell instance record. The depth and colormap are set in the core initialize method, and the visual later in the shell initialize method. Again, the resources are initialized independently of each other.

The default converter for the visual resource checks the value against the six visual classes (StaticGray, GrayScale, StaticColor, PseudoColor, DirectColor, or TrueColor). If the class is a valid value (converted to an integer between 0 and 5 inclusive), XMatchVisualInfo() is called, and if it succeeds, returns the visual ID. If more than one visual with the same class exists, the one with the widget's depth is returned. If there are multiple visuals with that class and depth, it is undefined which one will be returned. The colormap converter does a string to integer conversion, and may or may not return a valid colormap ID.

When a shell widget is created, the core initialize method is called first, and the depth and colormap are set. (In the intrinsics, initialize procedures are called from super-to-subclass order.) Next, the shell initialize procedure is called. Unfortunately no check is made to ensure that the visual, depth and colormap are correctly matched. Since a visual is needed to create a colormap, and a colormap has already been chosen, a non-default visual will likely cause a BadMatch error when the window is realized.

Matching the Visual, Depth and Colormap

A General Xt Solution

The first part of our solution is to decide upon some algorithm for selecting a visual. The algorithm should be as flexible as possible, allowing both new and advanced users to take advantage of their display's capabilities. The following resources will be added:

The algorithm is as follows:

  1. if XtNvisualID is set, try to find the visual with this ID, if it exists.
  2. if XtNapplicationDepth is set, try to find a visual with that depth, otherwise set it to DefaultDepth(). This allows users to have:

     *applicationDepth: 24
    

    and know that if a 24 bit visual ever exists, it will be found and used.

  3. if XtNvisualClass is set then find the visual with that class and XtNapplicationDepth if set, otherwise the greatest depth.

  4. if all else fails use the default visual and default depth.

  5. if the resultant visual's ID == default visual's ID, and XtNusePrivateColormap is FALSE, then use the default colormap, otherwise, create an appropriate one.

The first thing to do is to add to our application resources:


typedef struct _OurResourceStruct
{
	int visualID;
	int applicationDepth;
	int visualClass;
	Boolean usePrivateColormap;
} OurResourceStruct, *OurResourcePtr;

OurResourceStruct ourResources;

#define UNDEFINED_DEFAULT -1
#define TheOffset(a) XtOffset(OurResourcePtr, (a))

static XtResource AppResources[] =
{
	{XtNvisualID, XtCVisualID, XtRInt, sizeof(int),
		TheOffset(visualID), XtRImmediate, (XtPointer)UNDEFINED_DEFAULT},
	{XtNapplicationDepth, XtCApplicationDepth, XtRInt, sizeof(int),
		TheOffset(applicationDepth), XtRImmediate, (XtPointer)UNDEFINED_DEFAULT},
	{XtNvisualClass, XtCVisualClass, XtRVisualClass, sizeof(int),
		TheOffset(visualClass), XtRImmediate, (XtPointer)UNDEFINED_DEFAULT},
	{XtNusePrivateColormap, XtCUsePrivateColormap, XtRBoolean, sizeof(Boolean),
		TheOffset(usePrivateColormap), XtRImmediate, (XtPointer)FALSE},
};

Next we need to call XtGetApplicationResources() at startup to obtain their values. However the first argument to XtGetApplicationResources() is a widget, so what do we use? The answer, unfortunately, is that we need a mini-hack. First we create a shell, but don't realize it. (Because the window is not created until the realize stage, as long as you don't realize the widget, there is no chance of a BadMatch since XCreateWindow() has not been called.)

This allows us to get the resource values and modify them if necessary. We now come to the hack, and we have two choices. Once we have our modified resources we could destroy the shell and create a new shell using the new values. Or, we can set the XtNbackground and XtNborderPixel values to 0, a valid entry in any colormap, with XtSetValues(), and at the same time set our new resource values. This is not as bad as it may seem. 99 percent of the time a shell widget has a child using its entire window space making the shell's background color irrelevant. The border pixel color, while possibly useful, is easily traded for not having to destroy and create a new shell. My choice then is to use the second option.




	shell = XtAppCreateShell(name, className, applicationShellWidgetClass, 
					display, NULL, 0);

	XtGetApplicationResources(shell, &ourResources, AppResources, 
					XtNumber(AppResources), NULL, 0);


After XtGetApplicationResources() is called, we have all the information we need to step through our algorithm.

  1. First check if visualID has been set (not UNDEFINED_DEFAULT). If it is, use XGetVisualInfo() to verify that it is a valid visual, along with getting the visual info.

    
    	success = FALSE;
    
    	if (ourResources.visualID != UNDEFINED_DEFAULT)
    	{
    		XVisualInfo *vtemp;
    		int vitems;
    
    		vtemp.visualid = ourResources.visualID;
    		vinfos = XGetVisualInfo(display, VisualIDMask, &vtemp, &vitems);
    
    		if (vinfos != NULL)
    		{
    /*
    ** Better only be one match!
    */
    			theVisual = vinfos[0].visual;
    			theApplicationDepth = vinfos[0].depth;
    			theVisualClass = vinfos[0].class;
    
    			XFree(vinfos);
    			success = TRUE;
    		}
    	}
    
    

  2. If 1 above fails, check to see if (applicationDepth == UNDEFINED_DEFAULT) and (visualClass == UNDEFINED_DEFAULT). If this is true, get the default visual's information.

  3. If 2 above fails,
    1. if applicationDepth == UNDEFINED_DEFAULT, initialize it to the default depth, else leave it alone

    2. if visualClass == UNDEFINED_DEFAULT, initialize it to the default visual's class, else leave it alone

    3. Use XMatchVisualInfo() to see if an appropriate visual exists.

  4. If 3 fails, use XGetVisualInfo() and the applicationDepth value to find a visual of that depth.

  5. If 4 fails, use XGetVisualInfo() and the visual class to find a visual. In this case, if we get back a list of visuals, we use the one with the greatest depth.

  6. If 5 fails, fall back to the default visual.


    screen = DefaultScreen(display); if (!success) { /* Step 2 */ if ( (ourResources.applicationDepth == UNDEFINED_DEFAULT) && (ourResources.visualClass == UNDEFINED_DEFAULT)) { theVisual = DefaultVisual(display, screen); theApplicationDepth = DefaultDepth(display, screen); theVisualClass = theVisual->class; } else { /* Step 3 */ if (ourResources.applicationDepth == UNDEFINED_DEFAULT) theApplicationDepth = DefaultDepth(display, screen); else theApplicationDepth = ourResources.applicationDepth; if (ourResources.visualClass == UNDEFINED_DEFAULT) theVisualClass = DefaultVisual(display, screen)->class; else theVisualClass = ourResources.visualClass; if (XMatchVisualInfo(display, screen, theApplicationDepth, theVisualClass, &theVisualInfo) != 0) { theVisual = theVisualInfo.visual; theApplicationDepth = theVisualInfo.depth; theVisualClass = theVisualInfo.class; } else { /* Step 4 */ XVisualInfo *visTemplate; XVisualInfo **visReturn; int numVis, n; visReturn = (XVisualInfo **)NULL; n = 0; /* ** See if we can find a visual at the depth they ask for. */ if (ourResources.applicationDepth != UNDEFINED_DEFAULT) { visTemplate.depth = ourResources.applicationDepth; visReturn = XGetVisualInfo(display, VisualDepthMask, &visTemplate, &numVis); /* ** If numVis > 1 you may want to have it pick your favorite visual. This is not ** necessary since the user still has finer control by setting XtNvisualClass ** or XtNvisualID. */ /* Step 5 */ if (visReturn != (XVisualInfo **)NULL) { visTemplate.class = theVisualClass; visReturn = XGetVisualInfo(display, VisualClassMask, &visTemplate, &numVis); if (visReturn != (XVisualInfo **)NULL) { int i; for (i=1;i visReturn[n].depth) n = i; } } /* Step 6 */ if (visReturn == (XVisualInfo **)NULL) { theVisual = DefaultVisual(display, screen); theApplicationDepth = DefaultDepth(display, screen); theVisualClass = theVisual->class; } else { theVisual = visReturn[n].visual; theApplicationDepth = visReturn[n].depth; theVisualClass = visReturn[n].class; } } } } }

    Finally, if the visual we end up with is not the default visual, or XtNusePrivateColormap is TRUE, create a private colormap. The resource XtNusePrivateColormap is necessary in case the default visual uses a read-write colormap. It is possible a user or programmer may want a colormap of the same class and depth as the default colormap, except with all the cells unallocated. Imagine an 8 bit PseudoColor default visual, and a background image that uses 255 colors. If you do not want your application to run in black and white when XAllocColor() fails, you need to create your own colormap.


    if ( (theVisual->visualid == DefaultVisual(display, screen)->visualid) && !ourResources.usePrivateColormap) theColormap = DefaultColormap(display, screen); else theColormap = XCreateColormap(display, RootWindow(display, screen), theVisual, AllocNone);

    Finally we use XtSetValues() to change the values in our shell.

    
    	n = 0;
    	XtSetArg(warg[n], XtNdepth, theApplicationDepth); n++;
    	XtSetArg(warg[n], XtNvisual, theVisualDepth); n++;
    	XtSetArg(warg[n], XtNcolormap, theColormap); n++;
    	XtSetArg(warg[n], XtNbackground, 0); n++;
    	XtSetArg(warg[n], XtNborderPixel, 0); n++;
    	XtSetValues(shell, warg, n);
    

    Subsequent toplevel shells can be quickly initialized by setting the Arg list at creation.

    The last problem is to make subshells use the same visual, depth and colormap as the toplevel shells. Remember, a shell's parent window is the root window with CopyFromParent as the default value for the visual, depth and colormap. To avoid making our values global we introduce the following function:


    Widget XtGetShell(Widget _w) { while(_w && !XtIsShell(_w)) _w = XtParent(_w); return _w; }

    Now when creating a subshell, use XtGetShell() on the subshell's parent widget. This will return the supershell that you can use XtGetValues() on to obtain the depth, visual and colormap. Set these in the Arg list of the popup at creation time. Matching sub- and supershells alleviates much of the colormap flashing for menus and dialogs. Our application can now exist beyond the default visual. Better yet, it does this under resource control.

    A Widget Solution

    While the above solution works for an Xt application, one of the drawbacks is that it must be added to each program you write. It would be better to write it once as a widget, and use it in all applications. Since the visual is part of the shell widget class, we can subclass the topmost shell a program would have, the applicationShellWidgetClass. We call ours the appPlusShellWidgetClass.

    First we take advantage of the fact that we can change the core instance's depth and colormap any time before the widget is realized (when the window is created). Second, all the appPlusShells share any created colormaps, and have the same visual and depth. While this seems limiting, it has the same effect as the original solution. It would be possible to allow each appPlusShell to have different visuals or separate colormaps, but problems, such as when to share a colormap, arise. In my own programs I have found it to be reasonable to have the entire application under the same visual and colormap.

    If we share the visual and colormap, we need a common place to store the information. Since all the instances can access the class part of the appPlusShell, we use that. We initialize the fields in the widget class record, flagging them as being unset. The first time the initialize method is called, the new resource values are checked, and most of the original solution utilized. After we find the appropriate visual, depth and colormap we store these values in the appPlusShell class part of the widget. Each new instance checks the class part to see if the fields are initialized. If the fields are initialized, the values are copied from the class part to the instance fields, otherwise it is the first instance and we must run through our algorithm.

    The other two procedures that need to be implemented are the realize method and the destroy method. In the realize method, we need to set the colormap, background_pixel and border_pixel values of the XSetWindowAttributes variable. We use the colormap we have set in the core instance record, and the pixel values are both set to 0.


    static void realize(AppPlusShellWidget _apsw, XtValueMask *_xvm, XSetWindowAttributes *_xswa) { _xswa->colormap = _apsw->core.colormap; _xswa->background_pixel = _xswa->border_pixel = 0; *_xvm |= CWColormap | CWBorderPixel | CWBackPixel; (*appPlusShellWidgetClass->core_class.superclass->core_class.realize)((Widget)_apsw, _xvm, _xswa); }

    The destroy method need not be implemented. It is possible to reference count our colormap and if we created it, release it when the count is 0. In practice, many users are prone to closing all the appPlusShells and then opening a new one. It is not necessary to go through the initialization routine again, so instead, I won't implement a destroy method. This provides the ability to use the values previously stored in the class part. Anything we have allocated is left to be free'd automatically when the connection to the display is closed.

    Past the Default Visual

    The Intrinsics do not fully handle non-default visuals, so most programs also do not. We can overcome this lack by using a simple algorithm to match a depth, visual and colormap. As a bonus, users can decide which visual to use through resources. The resource names have changed to avoid using default converters. Users should no longer put "*depth" or "*visual" in resource files (remember to document that), and instead should use the new names: XtNapplicationDepth, XtNvisualID, XtNvisualClass and XtNusePrivateColormap.

    Now that you have the visual of your choice, how can you use it effectively? In my column, I examine how different visuals can be gathered into a common color model.

    In the mean time, the code presented in this article can be found as part of the freely available AppPlusShell package at ftp.x.org:/contrib/widgets/AppPlusS.tar.Z.