rws@expo.lcs.mit.EDU (Bob Scheifler) (02/21/91)
The following changes to the MIT X Consortium's Xlib and Xt standards have been proposed, and have passed internal review. The changes are now being circulated for Public Review. Public Review is scheduled to end May 20, 1991. The X community is encouraged to review the changes and submit comments by electronic mail to xchanges@expo.lcs.mit.edu Comments sent to other addresses are not guaranteed to be considered. Problem: Users with multi-screen displays want a way to tailor resources for each screen. This is particularly true when one screen is monochrome and one is color, but it is also true, e.g., when the screens have different resolutions. While it is nice to think about solving the general problem of permitting each individual widget to identify specific attributes to affect resource lookup, the complexity and interface changes are too large to justify the incremental benefit. A simple solution that deals with multiple screens is adequate. Solution: In addition to the RESOURCE_MANAGER property that currently exists on screen zero, define a new SCREEN_RESOURCES property that can exist on each screen. Xt will load this property, if it exists, between loading the RESOURCE_MANAGER property and loading the per-host user environment resource file (i.e., SCREEN_RESOURCES entries override RESOURCE_MANAGER entries). Xlib should have a new function for retrieving these strings: char *XScreenResourceString(screen) Screen *screen; Returns the SCREEN_RESOURCES property from the root window of the specified screen. If no property exists, NULL is returned. The caller is responsible for freeing the returned string, using XFree. Compatibility: Xt will now have to maintain a separate resource database for each screen, not just one for each display. The Xt function XtDatabase becomes a problem. I suggest that XtDatabase becomes the database for the default screen, which will preserve compatibility. A new function should be defined, XrmDatabase XtScreenDatabase(screen) Screen *screen; Xt currently uses a non-portable kludge to ram the (currently single) database into the Xlib Display structure, although this application-visible side-effect (via XGetDefault) is not stated in the specification. I am willing to say that it should continue to do this for the database of the default screen. (Note that the Xlib internationalization work has proposed a XrmSetDatabase function to do this portably.) Sub-Problem: For top-level shells, Xt must determine the screen of the shell. It is possible for the screen specification to come from a resource. There is a chicken and egg problem here, since the screen is required in order to know which resource database to use. Solution: The resource database of the default screen will be used when resolving the screen for a top-level shell. Once the screen is resolved, the resource database for that screen will be used for the remaining resources of the shell. Compatibility: Should be source and binary compatible, except as follows. The existing Xt specification is unclear about where shell resources come from when the display argument passed to (e.g.) XtAppCreateShell is different from the display that the shell is actually created on. This can happen when when a screen on a different display is explicitly passed in the arglist, or when a resource converter (e.g. String to Screen) has been registered that is capable of mapping to a screen on another screen. In current Xt implementations, the resource database of the display argument is used for all shell resources. In the new proposed semantics, the resource database of the destination screen will be used instead. As such, this is an incompatible change, but it should be a change that produces more sensible resource values in the normal case. Problem: Application designers cannot place display-dependent customizations in app-defaults files. The biggest problem here is not being able to provide reasonable defaults for both monochrome and color, but there are other kinds of customization that would also be useful (e.g. choosing between a 2D and 3D look, or choosing between two different GUIs). Solution: Use resources to control loading of defaults files. Define a new substitution variable, %C, to be used by Xt when looking for defaults files. The default value of XFILESEARCHPATH and XUSERFILESEARCHPATH should be changed to search for customization files first (for details, see following Problem and Solution). Xt will obtain the customization string by looking up the resource name: <appname>.customization class: <appclass>.Customization Where and when does Xt look for this resource? Xt will be changed to construct the database in "reverse order", starting from the application command line and working up to the application class resource file. This reverse order construction will not alter the semantics of the database, i.e. the resulting database will be identical to one loaded in forward order. Before loading the application-specific user resource file, Xt probes the database constructed so far for the customization. Before loading the application-specific class resource file, Xt probes the database again (so that a customization can be specified in any of the user files or properties). Compatibility: I believe this change is backward compatible, both source and binary. Old applications can continue to read old app-defaults files. New customizations can be placed in new files. As an example, an application like xterm might provide the normal "XTerm" app-defaults file, as well as an "XTerm-color" app-defaults file. The user could place something like this in their file passed to xrdb: #ifdef COLOR XTerm.Customization: -color #endif Sub-Problem: How can Xt efficiently create the database in reverse order? XrmMergeDatabases is not adequate for this. Solution: XrmMergeDatabases is like "override" in Xt translations. We need a similar function, but one that does "augment" instead. While we're at it, it is inefficient to do XrmGetFileDatabase followed by XrmMergeDatabases; one should be able to load and merge directly. Provide the following functions: void XrmCombineDatabase(source, target, override) XrmDatabase source; XrmDatabase *target; Bool override; Like XrmMergeDatabases, but if the source and target both have a given identical left-hand-side and override is False, the source value is not placed in the target. Status XrmCombineFileDatabase(filename, target, override) char *filename; XrmDatabase *target; Bool override; Equivalent to: XrmCombineDatabase(XrmGetFileDatabase(filename), target, override); But returns 0 if failed to open/read the file. Compatibility: Source and binary compatible. Sub-Problem: It is undesirable to replicate common resources across multiple customization files. Some way of sharing resources is required. Solution: Permit a resource file to have lines of the form: # <whitespace> include <whitespace> "<filename>" <whitespace> <whitespace> is zero or more spaces or tabs. Lines of this form would be interpreted by XrmGetFileDatabase and by the new XrmCombineFileDatabase. The filename would be interpreted relative to the directory of the file in which the line occurred (i.e., relative to the directory of the filename argument to the loading function). Compatibility: I believe this will be source and binary compatible. For compatibility, base app-defaults files should not contain #include, the customized versions should. That way, old binaries still read sensible defaults files. Problem: The currently specified defaults for XFILESEARCHPATH and XUSERFILESEARCHPATH encourage application-specific files to be spread across the file hierarchy. This is undesirable from the perspective of a system maintainer trying to figure out how to deinstall, copy, or backup an application, or even from the point of view of an end-user trying to browse around. In addition, there are filename length limitations on some systems that may force a different kind of structure to accommodate these limitations. Specification of these paths should remain somewhat implementation dependent, to permit vendors to choose a configuration best suited for their environment. Solution: A reasonable minimum of path entries can be specified. Use of links on POSIX-based systems, or system-dependent techniques on other systems, can be used as necessary to handle special needs. The default XFILESEARCHPATH must contain at least six entries. These entries must contain the following substitutions: 1. %C, %N, %S, %T, %L or %C, %N, %S, %T, %l, %t, %c 2. %C, %N, %S, %T, %l 3. %C, %N, %S, %T 4. %N, %S, %T, %L or %N, %S, %T, %l, %t, %c 5. %N, %S, %T, %l 6. %N, %S, %T The order of these entries within the path must be as given above. The order and use of substitutions within a given entry is implementation dependent. If XAPPLRESDIR is defined, the default XUSERFILESEARCHPATH must contain at least seven entries. These entries must contain the following directory prefixes and substitutions: 1. $XAPPLRESDIR with %C, %N, %L or %C, %N, %l, %t, %c 2. $XAPPLRESDIR with %C, %N, %l 3. $XAPPLRESDIR with %C, %N 4. $XAPPLRESDIR with %N, %L or %N, %l, %t, %c 5. $XAPPLRESDIR with %N, %l 6. $XAPPLRESDIR with %N 7. $HOME with %N If XAPPLRESDIR is not defined, the default XUSERFILESEARCHPATH must contain at least six entries. These entries must contain $HOME as the directory prefix, plus the following substitutions: 1. %C, %N, %L or %C, %N, %l, %t, %c 2. %C, %N, %l 3. %C, %N 4. %N, %L or %N, %l, %t, %c 5. %N, %l 6. %N The order of these entries within the path must be as given above. The order and use of substitutions within a given entry is implementation dependent. Compatibility: Vendors are free to choose default paths that preserve compatibility. Problem: Users cannot easily apply global overrides to resources that are specified application-specific class defaults files. For example, *ShapeStyle: oval is not guaranteed to apply uniformly to all widgets, if the defaults file has more specific references. Solution: Introduce a new reserved ComponentName for resources. I'll propose the single character "?". It is to be interpreted as "any". (I would have suggested using "ANY" as the ComponentName, but that might conflict with some existing resource.) The ComponentName "?" lies below both instance name and class name in precedence, and matches any class/instance pair. It cannot be used as the final component in a resource name. Since defaults files should not have instance or class prefixes on their resources (i.e. they should be written "*stuff:" instead of "XTerm*stuff:"), the user can now easily override defaults by using: "?*ShapeStyle:" instead of "*ShapeStyle". A side benefit of this change is that it now becomes possible to specify resources that affect everything at a given level in the widget hierarchy, regardless of class/instance differences. For example, one can specify "cadapp.?.?.foreground: red" to get everything at that level red, even if the things at that level are a mixture of classes. Compatibility: I believe this will be source and binary compatible. Problem: If an application defaults file has a translation table specified, and the user wants to augment/override it in their user defaults file, the application translations get completely lost. For example, if the application defaults file has foo.bar.translations: <stuff> and the user tries foo.bar.translations: #augment <more stuff> The two sets will not be merged, because only one entry is retained in the resource database. Users should not know or care whether application resource defaults are specified in code or in resource files, the application programmer should be free to migrate resources between code application defaults freely, without affecting the user. Solution: When obtaining a translation table from the resource database, do two lookups into the database, looking for both "baseTranslations" and "translations" resources. If the "translations" resource does not exist, then the "baseTranslations" resource is used. If the "baseTranslations" resource does not exist or the "translations" resource contains "#replace", then the "translations" resource alone is used. If both resources exist, both are used in sequence (first "baseTranslations" and then "translations") to augment or override (as specified in each resource) the class translations. Applications wishing to take advantage of this would change their defaults file, e.g., from foo.bar.translations: <stuff> to foo.bar.baseTranslations: <stuff> Compatibility: I believe this will be source and binary compatible (unless someone is currently using a resource named "baseTranslations", which I doubt). The change to defaults files suggested above will not work for a defaults file shared between an R4 and an R5 application. If it is important to the application to preserve complete compatibility, the full translations can be replicated in both the "translations" and the "baseTranslations" resource. This is unsightly, but doable. Problem: There is no way to enumerate the elements of a resource database. Various applications want this ability. For example, to find a resource with a given value (e.g. the way XKeysymToString does), or to dump the database in some format other than what XrmPutFileDatabase creates (e.g., to deal with non-string values), or to search for completions of a given resource prefix. Solution: Add the following procedure: #define XrmEnumAllLevels 0 #define XrmEnumOneLevel 1 Bool XrmEnumerateDatabase(db, name_prefix, class_prefix, mode, proc, closure) XrmDatabase db; XrmNameList name_prefix; XrmClassList class_prefix; int mode; Bool (*proc)(); void *closure; Calls the supplied procedure for each resource in the database that would match some completion of the given name/class resource prefix. The order in which resources are found is implementation-dependent. If mode is XrmEnumOneLevel, then a resource must match the given name/class prefix with just a single name and class appended. If mode is XrmEnumAllLevels, the resource must match the given name/class prefix with one or more names and classes appended. If the procedure returns True, the enumeration terminates and the function returns True. If the procedure always returns False, all matching resources are enumerated and the function returns False. The procedure is defined as follows: proc(db, bindings, quarks, type, value, closure) XrmDatabase *db; XrmBindingList bindings; XrmQuarkList quarks; XrmRepresentation *type; XrmValue *value; void *closure; The bindings and quarks lists are terminated by NULLQUARK. Note that pointers to the database and type are passed, but these values should not be modified. Compatibility: Source and binary compatible. Problem: XrmStringToQuark must always make a copy of its string argument, because there is no requirement that the string argument last longer than the call (and indeed, it often doesn't). However, it is quite often the case that the string argument is in fact permanently allocated (e.g. coming from a string constant compiled into the program), and copying such strings wastes a fair amount of space. Solution: Add a new Xlib function: XrmQuark XrmPermStringToQuark(string) char *string; This function acts just like XrmStringToQuark, except that Xlib is permitted to assume that the string argument is permanently allocated, and hence that it can be used as the stored value to be returned by XrmQuarkToString. Revise the Xt specification to state that Xt is permitted to assume that the following strings in class records are permanently allocated: In Object (and all subclasses): class_name in resources: resource_name, resource_class, resource_type, and default_type In Core (and all subclasses): in actions: string In Constraint (and all subclasses): in resources: resource_name, resource_class, resource_type, and default_type Compatibility: Xlib is clearly source and binary compatible. Xt is source and binary compatible providing no one is creating classes on the fly and later destroying them. I think this is a safe assumption, and that it is an acceptable long-term requirement that the strings remain permanently allocated. Problem: All core X errors, in wire format, contain only a resource identifier or other value (at most) as additional data. Since a wire error is always 32 bytes long, this leaves 21 bytes of pad. It is possible for an extension to define its own errors. These errors may contain more information than a single 32-bit value. Unfortunately, Xlib does not allow the extension's client library or an application program using the extension to access the additional information in the error. Neither the routine registered by calling XESetErrorString for returning the extension's error messages nor the routine registered by calling XSetErrorHandler have access to the original wire error. When the routine registered calling XSetErrorHandler is called, it is passed an XErrorEvent structure. This is an error that has already been converted by Xlib. The XErrorEvent structure contains only the fields in the current X errors, with no room for additional error values that may be in an extension's wire error. Thus, the application program has no access to the additional information in the wire format error. When the routine registered by calling XESetErrorString is called, it is passed only the error code, not the entire xError structure. It cannot format an error message to return to Xlib which contains the additional information in the wire error packet. The specific problem shows up with the PEX extension's OutputCommand error. This error contains, in addition to the fields in a core X error, the opcode of the bad output command and the number of output commands which were successfully processed. The only officially supported API for the PEX extension is PHIGS. It is very important to PHIGS to know this information, so it can do something reasonable when it gets back an error of this type from the server. Using the current extension interface for errors, there is no supported way for the PHIGS API to find out this information. Solution: Bool (*XESetWireToError (display, error_number, proc)() Display *display; int error_number; Bool (*proc)(); This function allows you to define a procedure to be called when an extension error needs to be converted from wire format (xError) to host format (XErrorEvent). The error number defines which protocol error number to install a conversion routine for. This procedure returns any previously defined procedure. Use this function for extension errors that contain additional error values beyond those in a core X error. This function also allows you to intercept an X error before it is otherwise examined. When Xlib needs to convert an error from wire format to host format, the routine is called with these arguments: Bool (*proc) (display, he, we) Display *display; XErrorEvent *he; xError *we; The he argument is a pointer to where the host format error should be stored. The structure pointed at by he is guaranteed to be as large as an XEvent structure, and so can be cast to a type larger than an XErrorEvent, in order to store additional values. If the error is to be completely ignored by Xlib (e.g. several protocol error structures will be combined into one Xlib error), then the function should return False, otherwise it should return True. void (*XESetPrintErrorValues (display, extension, proc)) () Display *display; int extension; void (*proc)(); This function allows you to define a procedure to be called when an extension error is printed, to print the error values. Use this function for extension errors that contain additional error values beyond those in a core X error. This function returns any previously defined procedure. When Xlib needs to print an error, the routine is called with these arguments: void (*proc) (display, ev, fp) Display *display; XErrorEvent *ev; void *fp; The structure pointed at by ev is guaranteed to be as large as an XEvent structure, and so can be cast to a type larger than an XErrorEvent, in order to obtain additional values set by using XESetWireToError. The underlying type of the fp argument is system dependent; on a POSIX system fp should be cast to type FILE*.