Xlib Programming Manual (O'Reilly & Associates, Inc.) |
This chapter covers a few obscure but occasionally necessary programming techniques. The routines and techniques described here will not be needed in most programs.
The end of the chapter contains information about porting
and portability.
Reading a property
#include "Xatom.h"
Status XFetchName (dpy, w, name)
register Display *dpy;
Window w;
char **name;
{
Atom actual_type;
int actual_format;
unsigned long nitems;
unsigned long leftover;
unsigned char *data = NULL;
if (XGetWindowProperty(dpy, w, XA_WM_NAME, 0L, (long)BUFSIZ,
False, XA_STRING, &actual_type, &actual_format,
&nitems, &leftover, &data) != Success) {
*name = NULL;
return (0);
}
if ( (actual_type == XA_STRING) && (actual_format == 8) ) {
/* The data returned by XGetWindowProperty is guaranteed
* to contain one extra byte that is null terminated to
* make retrieving string properties easy */
*name = (char *)data;
return(1);
}
if (data) Xfree ((char *)data);
*name = NULL;
return(0);
}
Using XChangeProperty() is easier than reading a property
with XGetWindowProperty(), since there are many fewer arguments.
Example 15-2 shows the companion function to XFetchName(), XStoreName().
Writing a property
XStoreName (dpy, w, name)
register Display *dpy;
Window w;
char *name;
{
XChangeProperty(dpy, w, XA_WM_NAME, XA_STRING,
8, PropModeReplace, (unsigned char *)name,
name ? strlen(name) : 0);
}
At this writing, the X Consortium is working on an X extension
that will support more advanced screen saving abilities.
There are several kinds of security that can prevent connections from being made by clients running on other machines. First, to provide a minimal level of protection, connections are only permitted from machines which are listed on a host access list. This is adequate on single-user workstations but obviously breaks down on machines running more than one server.
In X11R4, per-user control was added with the MIT-MAGIC-COOKIE-1MIT-MAGIC-COOKIE-1 is not too secure, however, because it passes its secret key ("cookie") between client and server without encryption.
X11R5 defines, and the MIT release implements, two new mechanisms that can be used for secure access control. XDM-AUTHORIZATION-1 is similar to MIT-MAGIC-COOKIE-1, but uses DES (Data Encryption Standard) encryption to encrypt the authorization data that is passed between client and server. To compile this authorization scheme, you need an implementation of DES in the file mit/lib/Xdmcp/Wraphelp.c. Due to U.S. export regulations, this file may not appear in your distribution. If you do not plan to export the file outside of the U.S., you may legally obtain it over the network from the X Consortium. Ftp to the host export.lcs.mit.edu and see the file pub/R5/xdm-auth/README. Outside the U.S. you may be able to obtain a compatible version of this file from the directory /pub/X11R5 on the machine ftp.psy.uq.oz.au (130.102.32.1). If you have this file, but this security mechanism is not automatically built on your system, you can add the following line to the file mit/config/site.def before building X11R5:
The other R5 authorization mechanism is named SUN-DES-1, and is based on the public key Sun Secure RPC system included with recent version of SunOS. If your system provides this secure RPC system, then the .cf file for your system in mit/config should define the variable HasSecureRPC, which will cause this security mechanism to be automatically built. The forthcoming (late 1992) X Window System Administrator's Guide from O'Reilly & Associates explains the issues of X security and these X11R5 security mechanisms in detail.#define HasXdmAuth YES
You can add, get, or remove hosts with XAddHost(), XAddHosts(), XListHosts(), XRemoveHost(), and XRemoveHosts(). All the host access control functions use the XHostAddress structure. The members in this structure are:
The XHostAddress structure
typedef struct {
int family; /* For example FamilyInternet */
int length; /* Length of address, in bytes */
char *address; /* Pointer to where to find the bytes */
} XHostAddress;
One possible use of XQueryTree() is to find out
whether your application's top-level window has been reparented by the
window manager, and it returns the ID of the new parent.
Clients in the default DestroyAll close down mode will have all their resources killed when the connection to the server dies. XSetCloseDownMode() can set two other modes, RetainPermanent and RetainTemporary, which allow client resources to live on for a time. A client may want its resources to live on to assist in the process of recovering from a broken connection with the server, usually caused by a network failure. When next run after the problem has been corrected, the application could somehow determine which resources were its own and continue operating where it left off. The "somehow" is the crux of the problem. The only way we can think of to allow the client to find out the IDs of its resources after the client is resurrected is for the client to save all the resource IDs in a file (or perhaps in a property, but this would not survive a server crash) immediately after they are created. Then upon startup, it can read this information and see if the specified resources still exist. If they do, it can skip creating them.
A dying connection between the server and client raises other problems, too. Even if a client's resources are put on life support, there is no longer any "brain" behind them. The user's instructions will go unanswered, and there will be no visible warning on the screen that the client is no longer connected. The window manager or some other program, if running on the same machine as the server, could conceivably detect this situation and print a message. However, this kind of functionality in a window manager has not been demonstrated up to now. Otherwise, the user can only be warned that the connection could die and that this would cause the window to freeze (if the client's resources were preserved; the window would disappear if the close down mode had not been set). The user could then restart the client from an xterm window to reactivate the window.
XKillClient() can kill resources that remain alive
after the connection closes. It can kill resources associated with a single
client by specifying any resource ID associated with that client, or it
can kill all resources of all clients that terminated with mode RetainTemporary
if given the argument AllTemporary. XKillClient() might be
used by the window manager or conceivably by a separate client to save
space in the server by cleaning up resources after clients die that have
requested that their resources be kept alive. This should not be done unless
the user agrees with it, because it could upset an application's attempt
to recover from a broken connection with the server.
First, you call XUniqueContext() to obtain an ID for a particular type of information you want to assign to windows. XUniqueContext() just provides a unique integer ID every time you call it (you can also make up your own if you wish). This ID indicates to the application what type of information is stored, but none of the calls require you to specify the data type. Then use XSaveContext() to store information into the context manager and XFindContext() to read it. If you plan to rewrite a particular piece of data corresponding to a window ID and context ID, it is better in terms of time and space to erase the current entry with XDeleteContext() before calling XSaveContext() again. XDeleteContext() does not make the context ID invalid.
If you have many different pieces of data of the same
type, such as an array, that must be associated with each window, you have
the option of packing it in a single chunk of data and storing it by context
or creating a different context ID for each member of the array. The context
ID indicates the meaning of the data (how you interpret it), not
necessarily the C language type.
Because the window-based coordinate system is so convenient,
this function is rarely needed. Since XTranslateCoordinates() makes
a round-trip request, it cannot be used heavily to port to X programs that
use global coordinates.
An example of using X_NOT_STDC_ENV might be to know when the system declares getenv:
It is convention in the R5 code from MIT is to put the standard case first using #ifndef.#ifndef X_NOT_STDC_ENV #include <stdlib.h> #else extern char *getenv(); #endif
Lack of the symbol X_NOT_STDC_ENV does not mean that the system has <stdarg.h>. This header file is part of ANSI-C, but the X Consortium found it more useful to check for it separately because many systems have all the ANSI-C files except this one. The symbol __STDC__ is used to control inclusion of this file.
X_NOT_POSIX means the system does not have POSIX.1 header files. Lack of this symbol does not mean that the POSIX environment is the default. You may still have to define _POSIX_SOURCE before including the header file to get POSIX definitions.
An example of using X_NOT_POSIX might be to determine what return type would be declared for getuid in <pwd.h>:
Note that both X_NOT_STDC_ENV and X_NOT_POSIX, when declared, state a noncompliance. This was chosen so that porting to a new, standard platform would be easier. Only non-standard platforms need to add themselves to <X11/Xosdefs.h> to turn on the appropriate symbols.#include <pwd.h> #ifndef X_NOT_POSIX uid_t uid; #else int uid; extern int getuid(); #endif uid = getuid();
Not all systems for which the X Consortium leaves these symbols undefined strictly adhere to the relevant standards. Thus you will sometimes see checks for a specific operating system near a check for one of the Xosdefs.h symbols. The X Consortium found it most useful to label systems as conforming even if they had some holes in their compliance. Presumably these holes will become fewer as time goes on.
<X11/Xosdefs.h> is automatically included by the header <X11/Xos.h>.
Unfortunately, there is not a header file for declaring malloc correctly, and it can be a bit tricky. The MIT R5 distribution uses lines like the following (from mit/lib/Xt/Alloc.c) to declare malloc and related functions:
Note that because index may be a macro declared in this header, you should be sure to avoid this identifier in variable and structure field names.#ifndef X_NOT_STDC_ENV #include <stdlib.h> #else char *malloc(), *realloc(), *calloc(); #endif #if defined(macII) && !defined(__STDC__) char *malloc(), *realloc(), *calloc(); #endif /* macII */
For external header files that might get used from C++, you should wrap all of your function declarations like this:
When in doubt, assume that the header file might get used from C++._XFUNCPROTOBEGIN... ...function declarations... _XFUNCPROTOEND...
A typical function declaration uses NeedFunctionPrototypes, like this:
If there are const parameters, use the symbol _Xconst instead, as above. This symbol will be defined only if the compiler supports const parameters. If it is plausible to pass a string constant to a char* parameter, then it is a good idea to declare the parameter with _Xconst, so that literals can be passed in C++.extern Atom XInternAtom( #if NeedFunctionPrototypes Display* /* display */, _Xconst char* /* atom_name */, Bool /* only_if_exists */ #endif );
If there are nested function prototypes, use NeedNestedPrototypes:
If there is a variable argument list, use NeedVarargsPrototypes:extern Bool XCheckIfEvent( #if NeedFunctionPrototypes Display* /* display */, XEvent* /* event_return */, Bool (*) ( #if NeedNestedPrototypes Display* /* display */, XEvent* /* event */, XPointer /* arg */ #endif ) /* predicate */, XPointer /* arg */ #endif );
If you have parameter types in library functions that will widen (be silently cast to a larger type) in traditional C, then you should use NeedWidePrototypes so that functions compiled with an ANSI-C compiler may be called from code compiled with a traditional C compiler, and vice versa.extern char *XGetIMValues( #if NeedVarargsPrototypes XIM /* im */, ... #endif );
If you use _Xconst, NeedNestedPrototypes, NeedVarargsPrototypes, or NeedWidePrototypes, then your function implementation also has to have a function prototype. For example:extern XModifierKeymap *XDeleteModifiermapEntry( #if NeedFunctionPrototypes XModifierKeymap* /* modmap */, #if NeedWidePrototypes unsigned int /* keycode_entry */, #else KeyCode /* keycode_entry */, #endif int /* modifier */ #endif );
#if NeedFunctionPrototypes
Atom XInternAtom (
Display *dpy,
_Xconst char *name,
Bool onlyIfExists)
#else
Atom XInternAtom (dpy, name, onlyIfExists)
Display *dpy;
char *name;
Bool onlyIfExists;
#endif
{
...
}
Actually, whenever you use a function prototype in a header
file, you should use a function prototype in the implementation, as required
by ANSI-C.
The following system-specific symbols are commonly used in X sources where OS dependencies intrude:
For other system-specific symbols, look at the StandardDefines parameters in the mit/config/*.cf files.USG Based on System V Release 2. SYSV Based on System V Release 3. SVR4 System V Release 4.
If you have a Berkeley 4.3-compatible tty driver, xterm sets the tty driver's row and column attributes when its top-level window is resized. vi and more and several other programs also look at those attributes when figuring out the terminal size. Also, xterm will send a SIGWINCH signal to the controlling process, which, if it is vi or more, will understand this signal and change its own notion of screen size, repainting the window in the process. This is the best way to deal with window resizing under xterm.
Graphics programs face a more difficult porting path. They must be rewritten to use the X library. It is a good idea to use a toolkit rather than trying to write completely in Xlib.
Programs written for single-user systems such as PCs will be a little more difficult, since they must be converted to respond to events instead of asking for one type of input at a time. They must also be modified to work in a multitasking environment.
Byte order is another traditionally thorny issue in porting. Byte order refers to the order in which bytes of data are stored in memory. There are actually four ways for two-byte data to be ordered, since the direction of each byte has two variations and the position of the most significant byte is also variable.
For X pixmaps, byte order is defined by the server and
clients with different native byte ordering must swap bytes as necessary.
For all other parts of the protocol, the byte order is defined by the client
and the server swaps bytes as necessary.
Using the XlibSpecificationRelease symbol
#ifdef XlibSpecificationRelease if (XlibSpecificationRelease == 5) ;/* R5 */ else if (XlibSpecificationRelease == 6) ;/* R6 */ else ;/* R7 or error */ #elseif /* R4 */ #endif
Extensions to X are not second-class citizens, and there should be very little to distinguish the use of an extension from that of the core protocol. The only difference is that the application should check to make sure the extension exists and then query the extension to find out the major opcode, additional event types, and additional error types so that the extension can be integrated properly. If the extensions have been written properly so that they initialize themselves when first called, they should be usable just like other X library functions.
XListExtensions() returns a list of all extensions supported by the server. Once the name of the desired extension is known, XQueryExtension() should be called to get specific information about the extension. XFreeExtensionList() should then be used to free the memory allocated by XListExtensions().
The standard extensions as of this writing are the Shape
extension, which supports non-rectangular windows, the X Input extension,
which supports input devices other than the single mouse and keyboard normally
connected to an X server, and PEX, a 3-D graphics extension. All extensions
are optional, however. Only the Shape extension is available in virtually
all X servers as of this writing.
Xlib Programming Manual (O'Reilly & Associates, Inc.) |