[comp.windows.x] Proposed Xlib/Xt Changes Available For Public Review

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*.