Xlib Programming Manual (O'Reilly & Associates, Inc.) |
Communication is necessary to make sure that all applications running under X cooperate properly with the window manager and share the system resources politely. Communication also allows applications to interchange data. Most applications in an integrated computing environment should have the ability to transfer data to and accept data from other applications.
Communication between clients takes place through properties. Sometimes properties are set directly by one application and read by another. This is the case with most communication between the window manager and the clients.
There is also a simple but limited means of communication through properties called cut buffers. But the preferred and most powerful method of general communication between clients is called selections. Selections actually establish a dialog between the two applications, not just a one-way communication. Both cut buffers and selections are ways of using properties for communication.
Successful communication depends on conventions for the meanings of the data communicated through properties and selections. The conventions in this area were established initially in Release 4, with the adoption as an X Consortium standard of the Inter-Client Communication Conventions Manual (ICCCM), which is reprinted in Appendix L, Interclient Communcation Conventions, of Volume Zero, X Protocol Reference Manual (as of the second printing). The current version of the ICCCM is version 1.
Several R3 routines are now obsolete because of new routines
added to Xlib in R4. This edition describes only the currently valid interfaces.
The outdated routines formerly used by applications are XSetStandardProperties(),
XSetWMHints(), XSetZoomHints(), XSetNormalHints(),
XStoreName() and XSetIconName().
Each property has a unique integer ID, called an atom. An atom is just a nickname for a property, so that arbitrary length property name strings do not have to be transferred back and forth between Xlib and the server. The atom is assigned by the server and will remain defined in the server even after the client that defined it terminates. The atoms for the predefined properties are constants defined in <X11/Xatom.h>; all of them begin with the prefix XA_. This naming convention avoids name clashes with user-defined symbols.
A property is uniquely identified by an atom and a window. Therefore, there may be one property on each window identified by a given atom. In other words, there can be a XA_WM_NAME property on each and every window, even though by convention this property is only set or read on the top-level windows of each application. A property on a window takes up space only once it is set.
Each property also has a name, which is an ASCII string. For the predefined properties, the name is never used in code. That is why we have chosen for this manual to refer to all predefined properties by their atoms. But for properties defined by convention between related clients (not predefined), the property name string is used so that the applications can determine the correct atom for the property. The first client to call XInternAtom() with the property name string as an argument gets a new atom. Subsequent clients that call XInternAtom() with the same string will get the same atom. After each client has called XInternAtom(), they use the atom rather than the string to refer to the property. They use this process because for properties defined by clients, the actual number used for an atom may differ between invocations of the server.
Once created, an atom remains defined in the server even after the client that created it has exited. A server remembers all atoms that were ever defined since the server started up. This means that one client can refer to an atom first interned by another client even if that other client has already exited.
Each property has a type, which itself is a property. There are several predefined properties for use as some of the more often needed types.
The data associated with a property can be stored as an array of 8-bit quantities, 16-bit quantities, or 32-bit quantities only. Properties can contain structures or raw data, but if one is to contain a structure of complex type, it must be encoded into one of the three byte formats by the program before being sent to the server and decoded when read from the server. The predefined property types have been carefully designed to match one of the data formats so that encoding and decoding are not necessary.
Properties remain set until the window to which they are attached is destroyed, which happens automatically when the client that created the window exits. However, properties set on the root window remain defined even after the client that set them has exited, since the root window is never destroyed.
There are 68 predefined properties for window manager communication, selections, standard colormaps, and font specifications. The properties used for window manager communication and selections are described in this chapter. The standard colormap properties are described in Chapter 7, "Color," and the font properties are described in Chapter 6, "Drawing Graphics and Text."
Properties are set with XChangeProperty() and read
with XGetWindowProperty(). Whenever XChangeProperty() is
called, a PropertyNotify event is generated.
The format is based on ISO standards for encoding and combining character sets. Compound Text is intended to be used in three main contexts: inter-client communication using selections; window properties (e.g., window manager hints); and resources (e.g., as defined in Xlib and the Xt Intrinsics). All of the standard routines for setting window manager hints that set text properties support the compound text encoding. If you are only concerned with your program operating in English on a system where the window manager also uses English, these routines are easy to use.
The target type for selections in the Compound Text encoding
is COMPOUND_TEXT.
It is a fundamental principle of client-window manager communication that a general client should not care which window manager is running or, indeed, if one is running at all. The choice of window manager is up to the user or perhaps the system administrator, not the client.
The fact that window managers need information about the clients they are managing and yet that window managers vary and might not be running lead to the concept of the hint. A hint is a suggestion to the window manager about a preference of the application made by setting a property. Xlib makes this easy by providing routines that conveniently set the right properties. The window manager is encouraged to honor as many of the hints as possible, but it is not required to honor any of them. Therefore, the application must not depend on its hints being honored; it must be capable of operating when any of its hints are ignored or denied.
In general, the object of the X11 design is that clients should, as far as possible, do exactly what they would do in the absence of a window manager, except for:
Clients create one or more windows that are children of the root window. All these windows are known as top-level windows. It is these windows that the window manager controls, and it is also these windows on which the application sets window manager hint properties.
It is important to remember that the version 1 conventions are the accepted X Consortium standard and will continue to be valid in R5 and later; there will be additions but not incompatible changes.
Some of the properties that a client sets for the window manager are mandatory (the standard properties), and some are optional. XSetWMProperties() which was introduced and used in basicwin in Chapter 3, "Basic Window Program," sets all the required properties. The purpose of XSetWMProperties() is to provide a simple interface for the programmer who wants to code an application quickly. Other functions are provided to communicate more optional information to the window manager.
In order to work well with most window managers, every program should call XSetWMProperties() for each top-level window. These provide the window manager with the following information:
The following sections describe the properties set by
the client that indicate its preferences to the window manager. Table 12-1
shows all the predefined properties that clients can set and the section
in this chapter where they are described.
| Property | Property Type | C Type | Description | See |
|---|---|---|---|---|
| For window manager: | ||||
| XA_WM_CLASS | XA_STRING | XClassHint | Application class and name for resource database lookup. | Section 12.3.1.5 |
| XA_WM_HINTS | XA_WM_HINTS | XWMHints | Additional hints set by client for use by window manager. | Section 12.3.1 |
| XA_WM_ICON_NAME | "TEXT" | XTextProper ty | Name to be used in icon. | Section 12.3.1.2 |
| XA_WM_NAME | "TEXT" | XTextProper ty | Application name. | Section12.3.1.1 |
| XA_WM_NORMAL_HINTS | XA_WM_SIZE_ HINTS | XSizeHints | Size hints for window in normal state (not iconified or zoomed). | Section12.3.1.3 |
| XA_WM_TRANSIENT_FO R_HINT | XA_STRING | char * | Tells window manager which window is the real main window with which a temporary window is associated. | Section 12.3.1.4.6 |
| XA_WM_ZOOM_HINTS | XA_WM_SIZE_ HINTS | XSizeHints | Size hints for zoomed window. | Section 12.3.1.3 |
| For session manager: | ||||
| WM_CLIENT_MACHINE | "TEXT" | XTextProper ty | The name of machine running the client, as seen from the machine running the server. | Section12.3.1 |
| XA_WM_COMMAND | "TEXT" | XTextProper ty | Command and arguments, separated by ASCII 0's, used to invoke application. | Section 12.3.2.1 |
In addition to the functions mentioned above that set all of the standard properties, Xlib also provides separate functions for setting and getting each property. These are referenced in the sections below describing each property. See the relevant pages in Volume Two, Xlib Reference Manual, for full details on each function. Applications set these properties and never read them, and the window manager gets them but never sets them. Therefore, if you are writing an application, you will only use the routines that set these properties.
Window managers are expected to make an effort to display this information; simply ignoring XA_WM_NAME is not acceptable window manager behavior. Clients can assume that at least the first part of this string is visible to the user, unless the user has made an explicit decision to make it invisible by placing the headline off-screen or covering it by other windows. But XA_WM_NAME should not be used for application-critical information nor to announce changes of application state that require timely user response. The expected uses are:
XSetWMName() and XGetWMName() set and get the XA_WM_NAME property. These interfaces support the use of the Compound Text Encoding.
If an icon pixmap has been specified in the standard properties or XA_WM_HINTS, it may be displayed in the icon in addition to or instead of the icon name. XSetWMIconName() and XGetWMIconName() set the XA_WM_NAME property.
XSetWMProperties() normally sets the XA_WM_NORMAL_HINTS property for a window in normal state. XSetWMNormalHints() is also available if for some reason XSetWMProperties() is not suitable.
The XA_WM_NORMAL_HINTS property is an XSizeHints structure, shown in Example 12-1.
The XSizeHints structure
typedef struct {
long flags; /* Marks defined members in
* structure */
int x, y; /* Obsolete */
int width, height; /* Obsolete */
int min_width, min_height;
int max_width, max_height;
int width_inc, height_inc;
struct {
int x; /* Numerator */
int y; /* Denominator */
} min_aspect, max_aspect;
int base_width, base_height;
int win_gravity;
} XSizeHints;
| Flag | Description |
|---|---|
| USPosition | User-specified x, y |
| USSize | User-specified width, height |
| PPosition | Program-specified position |
| PSize | Program-specified size |
| PMinSize | Program-specified minimum size |
| PMaxSize | Program-specified maximum size |
| PResizeInc | Program-specified resize increments |
| PAspect | Program-specified min and max aspect ratios |
| PBaseSize | Program-specified base size |
| PWinGravity | Program-specified window gravity |
| PAllHints | Program-specified all hints |
XSetWMSizeHints() is only useful if an application and a window manager agree on a private protocol that defines a new type of size hint atom beyond the one defined by the version 1 conventions or if a new type of size hint is added in later conventions.
XAllocSizeHints() allocates an XSizeHints structure and zeros all the fields. This function should be used so that new fields can be added in later releases while maintaining binary compatibility of applications written with earlier releases. In other words, using this function avoids compiling in the size of this structure, which may change. You declare only a pointer to this structure and then set it by calling XAllocSizeHints(). XAllocSizeHints() is used in the example program in Chapter 3.
XGetWMNormalHints() is normally used by the window manager to read the hints.
The XWMHints structure
typedef struct {
long flags; /* Marks defined members in structure */
Bool input; /* Does application need window
* manager for keyboard input */
int initial_state; /* See below */
Pixmap icon_pixmap; /* Pixmap to be used as icon */
Window icon_window; /* Window to be used as icon */
int icon_x, icon_y; /* Initial position of icon */
Pixmap icon_mask; /* Pixmap to be used as mask
* for icon_pixmap */
XID window_group; /* ID of related window group */
/* This structure may be extended in the future */
} XWMHints;
The following sections describe each member of XWMHints
and how it should be set.
| Member | Flag | Bit |
|---|---|---|
| input | InputHint | 0 |
| initial_state | StateHint | 1 |
| icon_pixmap | IconPixmapHint | 2 |
| icon_window | IconWindowHint | 3 |
| icon_x, icon_y | IconPositionHint | 4 |
| icon_mask | IconMaskHint | 5 |
| window_group | WindowGroupHint | 6 |
| All of the above | AllHints | 0-6 |
There are four input models:
If your application requires keyboard input and you neglect to set the input flag to True, you application will not get keyboard events under some window managers, such as olwm.
Under version 1 conventions, clients that use the Locally Active or Globally Active focus models must participate in one of the WM_PROTOCOLS called WM_TAKE_FOCUS, as described in Section 12.3.3.2, "Window Manager Protocols - WM_PROTOCOLS."
| Flag | Description |
|---|---|
| IconicState | Client wants to be iconified. |
| NormalState | Client wants top-level normal window visible. |
When setting the initial_state member of XWMHints, you must OR the StateHint constant set into the flags member of XWMHints to indicate that the field is to be set.
Even though there is have no flag for an inactive state, a window manager might implement a concept of inactive state in which an infrequently used client's window would be represented as a string in a menu. But this state is invisible to the client, which would see itself merely as being in IconicState.
The icon hints elements of the XWMHints structure
typedef struct {
.
.
Pixmap icon_pixmap; /* Pixmap for icon */
Pixmap icon_mask; /* Pixmap to be used as mask for
* icon_pixmap */
Window icon_window; /* Window to be used as icon */
int icon_x, icon_y; /* Initial position of icon */
.
.
} XWMHints;
icon_pixmap is the pattern to be used to distinguish
the icon from other clients. This pixmap should be:
icon_window is a window created but not mapped by the client. Clients that need to know their icon's ID or want to draw more than a simple two-color bitmap on the icon should set this hint. For example, xbiff and xmh change their icon pixmap when mail arrives, and they need to know their icon's ID to do this. Therefore, they must supply an icon window.
The icon_window hint should not be used unless needed. When it is not specified, the window manager creates the icon window itself.
You do not know which of the hints the window manager will honor. With current window managers, you can be confident that if icon_window is set, the window it names will be visible. If not, if icon_pixmap is set, the pixmap it names is visible. Otherwise, the window's XA_WM_ICON_NAME string is visible.
The conventions specify that the window manager must use the specified icon window. Therefore the application can read events from that icon window if desired.
An application that sets an icon_window is responsible for redrawing the window in case of Expose events. One way to set the icon design to be displayed is to set the background pixmap attribute of the icon window. The advantage of this approach is that there is no need to handle Expose events for the icon, because the background is automatically redrawn by the server. The disadvantage is that there is no way to apply a mask to generate a nonrectangular icon.
The icon window:
Applications with only one top-level window need not set this hint.
One of the top-level windows is known as the group leader. The window_group member of the hints for each of the other top-level windows should be set to the window ID of the group leader.
The group leader may be a window which exists only for that purpose and may never be mapped. Its window_group member should contain its own ID. The properties of the window group leader are those for the group as a whole (for example, the icon to be shown when the entire group is iconified). Every other top-level window may also have its own hints applicable only to itself.
It is important not to confuse XA_WM_TRANSIENT_FOR with the override_redirect window attribute. The override_redirect attribute specifies that the window manager does not get the chance to intercept the mapping request and thus no chance at all to decorate the window. This should be done only on the most temporary of windows, such as menus, or on windows that the programmer wants to be mapped without window manager intervention, such as automated demonstration programs. XA_WM_TRANSIENT_FOR should be used when other windows are allowed to be active while the transient window is visible, such as when the pointer is not grabbed while the window is popped up. If other windows must be frozen, use override_redirect and grab the pointer while the window is mapped.
Temporary windows that are popped up frequently should also set the save_under window attribute so that windows beneath the window may not need to redraw themselves quite so often.
To summarize, clients wishing to pop up a window should do one of three things:
The application should normally specify res_class as the application name with an initial capital.
If the res_name field is NULL, then the following is used:
An application should look up its own resources in the resource database using XGetDefault() or with the resource manager routines described in Chapter 13, "Managing User Preferences." If the user defaults are not found under res_name, the application should use res_class.
The XA_WM_CLASS property should only be written once and must be present when the window is mapped; window managers will ignore changes to it while the window is mapped.
The XA_WM_CLASS property contains a structure of type XClassHint. Example 12-4 shows the XClassHint structure.
The XClassHint structure
typedef struct {
char *res_name;
char *res_class;
} XClassHint;
The XAllocClassHint() function should be used to allocate
and zero the XClassHint structure. An example of doing this is presented
in Chapter 3.
XA_WM_CLASS can be set by the client with XSetClassHint() and read by the window manager with XGetClassHint().
Note that session managers are rare or nonexistent at the present time. Nonetheless, these conventions should be honored because it is only a matter of time before session managers become available. And the session manager does not necessarily need to be a separate client from the window manager. A window manager may have session-management capabilities.
There are two properties that need setting for the benefit of session managers, WM_COMMAND and WM_CLIENT_MACHINE. These supply the command and arguments needed to invoke an application in its current state and the machine on which it should be run. Together they supply enough information to restart the application. These are described in the following sections.
Applications use XSetCommand() function to set the command property. Window managers use XGetCommand() to read it.
Clients should ensure, by resetting this property as often as necessary, that it always reflects a command that will restart them in their current state.
This property should be set to a string forming the name of the machine running the client as seen from the machine running the server.
The WM_PROTOCOLS property contains a list of atoms, each
identifying a communication protocol between the application and the window
manager in which the application wants to participate. Atoms can identify
both standard protocols and private protocols specific to individual window
managers. All the protocols in which a client can volunteer to take part
involve the window manager sending the client a ClientMessage event.
The message_type field of the event will be the atom for WM_PROTOCOLS,
and the data field will contain the atom for one of the protocols
listed in Table 12-5.
| Protocol | Purpose |
|---|---|
| WM_TAKE_FOCUS | Assignment of keyboard focus. |
| WM_SAVE_YOURSELF | Save client state warning. |
| WM_DELETE_WINDOW | Request to delete top-level window. |
| More to come... |
Note that none of the above properties are represented by predefined atoms. Therefore, you will need to call XInternAtom() once for each one that you intend to use.
An application sets the WM_PROTOCOLS property using XSetWMProtocols(), and the window or session manager reads it with XGetWMProtocols().
To applications that specify WM_TAKE_FOCUS, the window manager may send a ClientMessage event with the atom corresponding to WM_TAKE_FOCUS in their data[0] field. If the application wants the keyboard focus, it should respond by calling XSetInputFocus() with its window argument set to the window of theirs that last had the keyboard focus or to their default input window and with the time argument set to the timestamp in the message. The revert_to argument should be set to RevertToParent.
A client could receive WM_TAKE_FOCUS when opening from an icon or when the user has clicked outside the top-level window in an area that indicates to the window manager that it should assign the focus (for example, clicking in the headline bar can be used to assign the focus).
The goal of WM_TAKE_FOCUS is to support window managers that want to assign the keyboard focus to a top-level window in such a way that the top-level window can either assign it to one of its subwindows or decline the offer of the focus. A clock, for example, or a text editor with no currently open frames might not want to take focus even though the window manager generally believes that clients should take the keyboard focus after being deiconified or raised.
Clients that call XSetInputFocus() must set the time argument to the timestamp of the event that caused them to make the attempt. Note that this cannot be a FocusIn event, since they do not have timestamps, and that clients may acquire the focus without a corresponding EnterNotify. Clients must not use CurrentTime in the time field.
Clients using the Globally Active model can only use XSetInputFocus() to acquire the input focus when they do not already have it on receipt of one of the following events: ButtonPress, ButtonRelease, passive-grabbed KeyPress, and passive-grabbed KeyRelease.
In general, clients should avoid using passive-grabbed key events for this purpose except when they are unavoidable (as, for example, a selection tool that establishes a passive grab on the keys that cut, copy, or paste).
Clients that set the input focus should set the revert_to argument of the XSetInputFocus() request to the parent of the window that is to be the new focus window. This determines the behavior of the input focus if the window the focus has been set to becomes not viewable. All other settings lead to problems, as described in Appendix L, Interclient Communcation Conventions, of Volume Zero, X Protocol Reference Manual (as of the second printing).
Clients should not give up the input focus of their own volition. They should ignore input that they receive instead.
Applications that express interest in this protocol may receive a ClientMessage event the atom for WM_SAVE_YOURSELF in its data[0] field.
Clients receiving WM_SAVE_YOURSELF should place themselves in a state from which they can be restarted and should update XA_WM_COMMAND (by calling XSetCommand()) to be a command that will restart them in this state. The session manager will be waiting for a PropertyNotify event on XA_WM_COMMAND as a confirmation that the client has saved its state, so that XA_WM_COMMAND should be updated (perhaps with a zero-length append) even if its contents are correct. No interactions with the user are permitted during this process.
After receiving the WM_SAVE_YOURSELF message through the event, saving its state, and updating XA_WM_COMMAND, the client should not change its state (in the sense of doing anything that would require a change to XA_WM_COMMAND) until it receives a mouse or keyboard event. Once it does so, it can assume that the danger is over. The session manager will ensure that these events do not reach clients until the danger is over or until the clients have been killed.
Clients with multiple top-level windows should ensure that only one of their top-level windows has a nonzero-length XA_WM_COMMAND property. They should also respond to a WM_SAVE_YOURSELF message by (in this order):
Once an application has expressed interest in this protocol, if one of the top-level windows is deleted, the application will receive a ClientMessage event whose data[0] field is the atom for WM_DELETE_WINDOW.
Clients receiving a WM_DELETE_WINDOW message should behave as if the user selected "delete window" from a (hypothetical) menu. They should perform any confirmation dialogue with the user, and if they decide to complete the deletion, either:
If the client aborts a destroy and the user then attempts to delete the window again, the window manager should start the WM_DELETE_WINDOW protocol again. Window managers should not use XDestroyWindow() on a window that has WM_DELETE_WINDOW in its WM_PROTOCOLS property.
Note that the WM_SAVE_YOURSELF and WM_DELETE_WINDOW protocols are orthogonal to each other and may be selected independently.
The other property, WM_STATE, stores the current state (normal, iconic, or withdrawn) of the application. This is mostly for communication between the window manager and session manager but may also be used by some applications.
The XIconSize structure
typedef struct {
int min_width, min_height;
int max_width, max_height;
int width_inc, height_inc;
} XIconSize;
The width_inc and height_inc members define
an arithmetic progression of sizes, from the minimum size to the maximum
size, representing the supported icon sizes. XGetIconSizes() actually
returns a list of these structures, in case the window manager needs more
than one to specify all of its accepted icon sizes.
Some commercial window managers set this property. Clients should be prepared to create an icon pixmap to fit the hint of each of the standard window managers and can even use the hint to determine which window manager is in operation.
Window managers use XSetIconSize to set this property for clients.
XAllocIconSize() function should be used to allocate and zero the XIconSize structure.
Xlib currently provides no routines for reading or writing this property, but of course, you can use XChangeProperty() or XGetWindowProperty().
This property does not have a predefined atom--to read or write this property you will need to call XInternAtom() to get the atom for this property.
You will need to convert strings into XTextProperty structures before you can call XSetWMProperties(), XGetWMClientMachine(), XGetWMIconName(), XGetWMName(), XSetWMClientMachine(), XSetWMIconName(), or XSetWMName().
These routines use the XTextProperty structure, which contains enough information to read and write the property in any format (8-bit, 16-bit, or 32-bit). The XTextProperty structure is shown in Example 12-6.
The XTextProperty structure
typedef struct {
unsigned char *value; /* Property data */
Atom encoding; /* Type of property */
int format; /* 8, 16, or 32 */
unsigned long nitems; /* Number of items in value */
} XTextProperty;
You need to set the fields in two copies of this structure
before calling XSetWMProperties(), in order to set the window name
and icon name properties, as was done in basicwin in Chapter 3,
"Basic Window Program." There are two ways to do this: one is to set the
fields directly one at a time, and the other is to use XStringListToTextProperty().
The latter is easier and better, because it does not require hardcoding
the format or encoding. See Example 3-9 for a demonstration of how to do
this.
Four more routines are provided to manipulate the XTextProperty structure:
The client may receive notification that its window has been reparented, moved, resized, raised, or lowered or that its border width has been changed by selecting StructureNotifyMask on its top-level window. It should not respond to these events by trying to change any of these characteristics, however.
An application can call XIconifyWindow() to have one of its top-level windows iconified. This function sends a ClientMessage event to the window manager, telling it to iconify this application. There is no equivalent routine to have the window manager return the application to normal state.
It is also possible to tell the window manager to unmap both the top-level window and its icon. This is done by calling XWithdrawWindow(). This is useful because the window manager rereads all the standard properties when the window returns from withdrawn to normal state.
There is no routine to tell the window manager to change a withdrawn application back into normal state. The technique under version 1 conventions is for client to set the initial_state field of the XWMHints structure to NormalState, then call XSetWMHints() to reset this property, and then map the top-level window.
To change from withdrawn state to iconic state, the application should follow the same procedure but set the initial_state field to IconicState.
To change from iconic state to normal state, the client needs only to map the window--it need not reset the property.
If a client selects StructureNotifyMask on the top-level window, it will receive an UnmapNotify event when it moves to iconic state and a MapNotify when it moves to normal state.
Clients can also select VisibilityChangeMask on their top-level or icon windows. They will then receive a VisibilityNotify event (with the state field set to VisibilityFullyObscured) when the window concerned becomes completely obscured even though mapped (and thus perhaps a waste of time to update) and a VisibilityNotify event (with state field not set to VisibilityFullyObscured) when it becomes even partly viewable.
Even when the client is not attempting to change the stacking order, the entire reconfigure request is sent by the server to the window manager for approval, and the window manager has the opportunity to honor, modify, or deny the request. The client finds out the window manager's decision through ConfigureNotify events.
Most applications do not need to specify or even suggest the position of their top-level windows. However, when doing so, the position the client specifies should be relative to the root window regardless of reparenting.
Client requests to reconfigure the top-level window are interpreted by the window manager in the same manner as the initial window geometry mapped from withdrawn state. There is no guarantee that the window manager will allocate the requested size or location, and clients must be prepared to deal with any size and location.
The window manager has several options in deciding how to respond to a request by the application to reconfigure a top-level window:
Clients should be aware that their borders may not be visible. Most window managers use reparenting techniques to decorate client's top-level windows with titles, controls, and other details. Ones that do are likely to override the client's attempts to set the border width and set it to zero. Clients should, therefore, not depend on the top-level window's border being visible nor use it to display any critical information. Other window managers will allow the top-level windows' borders to be visible.
Clients should ignore the above field of all ConfigureNotify
events that they receive on their top-level windows, since they cannot
be guaranteed to contain useful information.
The user may want to transfer information from an application and, at other times, to the application. Many applications need to be able to assume either role. In particular, clients should not display text in a permanent window without allowing the user to select it and convert it into a string, and any application that requires the user to type extensively should allow the user to paste in text from other applications.
Selections communicate between an owner client and a requestor client. The owner has the data representing the value of a selection, and the requestor wants it. The selection mechanism provides a way to notify other clients when useful data is placed in a property and to allow the owner of the data to convert it to a type asked for by the requestor.
Note that in the X11 environment, all data transferred between clients must go via the server (unless they are running on the same host, but that is a special case). An X11 client can neither assume that another client can open the same files nor communicate directly through IPC channels. The other client may be talking to the server via a completely different networking mechanism (for example, one client might be DECnet and the other TCP/IP). Thus, passing indirect references to data such as file names, hostnames, port numbers, and so on is permitted only if both clients specifically agree.
The application in which the text or graphics is being selected must first of all figure out what information is being selected and be able to convert it into a format that can be transferred to other applications. If the selection is text (usually the selection is a string) and the selected area is highlighted, by having the user drag the pointer over the area, then this application has to become the owner of a selection atom.
There are two built-in selection atoms: XA_PRIMARY and XA_SECONDARY. Unless the client foresees needing two simultaneous selections, it should use XA_PRIMARY. It calls XSetSelectionOwner(), specifying the selection atom, any window that it created (this window is used by other applications to identify the owner), and the time. The time used should be from the event that triggered the bid to own the selection (not CurrentTime) because of race conditions that can otherwise occur. If the client does not already own the selection atom, then this call will generate a SelectionClear event for the old owner, telling it to unhighlight the old selection.
Each client that wants to be able to have a selection pasted into it must set aside a key or button combination to indicate that the user wishes to paste in the current selection. In response to the event that occurs when that key or button combination is pressed, the client calls XConvertSelection(). This call specifies which selection the application wants (XA_PRIMARY until other conventions are established), the property to place the data in, the window on which to set this property, and the time. These arguments are quite clear. But the XConvertSelection() call also specifies a target type that the application wants the data in. You need to understand what happens after the XConvertSelection() call to understand the purpose of the target type property.
The server places all the arguments of the XConvertSelection() call into an XSelectionRequestEvent and sends the event to the selection owner. The owner then tries to convert the selection data into the format specified in the target type property. If the selection owner knows how to convert the data into the requested type, it puts the data in the property specified in the event and returns the atom of this property in the property member of a SelectionNotify event. If the selection owner cannot convert the selection into the requested type, it returns None as the property member in the SelectionNotify event. The owner sends this SelectionNotify event using XSendEvent().
When the requestor receives the SelectionNotify event, it either reads the property if it is set, repeats the request with a different target type if the owner returned None, or gives up on pasting data from that selection owner. It could be that the user is trying to do something like paste graphics into a text-only application.
Now you should understand the selection mechanism in general, so let's look at a more tangible example of how it takes place.
Assuming the text editor already uses the selection mechanism to transfer text to other applications, adding the line number capability should be easy. It would simply need to look for a new target type that indicated to it to figure out what line number the selected text is on. It might choose the first line, if more than one line were selected, or simply display an error message telling the user to select a single line.
The debugger application would then make the call shown in Example 12-7.
Setting the primary selection to a line number
Display display;
Atom target;
Window debugger_window;
Time time;
Bool only_if_exists;
Atom data_prop;
/* We create atom for data to be put into */
data_prop = XInternAtom(display, "STOP_LINE_NUM",
only_if_exists = False);
/* Target type atom must have been created by owner */
target_type = XInternAtom(display, "LINE_NUMBER",
only_if_exists = True);
if (target_type == None) {
fprintf(stderr, "%s: selection owner did not create \
LINE_NUMBER atom", argv[0]);
return(False);
}
XConvertSelection(display, XA_PRIMARY, target_type,
data_prop, debugger_window, time = triggering_event_time)
/* Wait for a SelectionNotify event and, if the property
* member is the same as data_prop, the conversion went fine;
* if the property member is None, the conversion failed */
The server sends all of the above information in a SelectionRequest
event to the text editor client (which had previously made itself the owner
of the selection with XSetSelectionOwner()).
The text editor stores the data in the property specified in the SelectionRequest event on debugger_window, then sends a SelectionNotify event (using XSendEvent()) to the requesting application. Upon receiving this event, the debugger reads this property and uses its value to place a break point in the C program.
Now that you have seen a more practical application of selections, we'll move on to a more precise description of each step in the selection transfer process.
Note that if the time in the XSetSelectionOwner() request is in the future relative to the server's current time or if it is in the past relative to the last time the selection concerned changed hands, the XSetSelectionOwner() request appears to the client to succeed, but ownership is not actually transferred. To ensure that ownership has been transferred, a client must perform the sequence shown in Example 12-8.
Code to ensure transfer of selection ownership
XSetSelectionOwner(display, selection_atom, owner, time);
if (XGetSelectionOwner(display, selection_atom) != owner) {
/* We didn't get the selection */
}
If XGetSelectionOwner() returns a window ID rather
than None, then the selection ownership was successfully transferred.
The XSelectionRequestEvent structure
typedef struct {
int type;
unsigned long serial; /* # of last request processed by
* server */
Bool send_event; /* True if this came from SendEvent
* request */
Display *display; /* Display the event was read from */
Window owner;
Window requestor;
Atom selection;
Atom target;
Atom property;
Time time;
} XSelectionRequestEvent;
The owner and the selection members will be
the values specified in the XSetSelectionOwner() request, and therefore,
the selection owner is interested in them only if it owns more than one
selection.
The owner should convert the selection into the type specified by the target member and set the property specified by the property member of the SelectionRequest event. Current conventions hold that all properties used to reply to SelectionRequest events should be placed on the requestor window. If the data comprising the selection cannot be stored on the requestor window (for example, because the server cannot provide sufficient memory), the owner must refuse the selection request as above.
The owner should also send the requestor a SelectionNotify event using XSendEvent() with an event_mask of 0. The members of the SelectionNotify event should be set to the same values received in the SelectionRequest event, except that if the selection could not be converted to the requested type, the property member should be set to None. Example 12-10 shows the XSelectionEvent structure which is used for SelectionNotify events.
The XSelectionEvent structure
typedef struct {
int type;
unsigned long serial; /* # of last request processed by
* server */
Bool send_event; /* True if this came from SendEvent
* request */
Display *display; /* Display the event was read from */
Window requestor;
Atom selection;
Atom target;
Atom property; /* Atom or None */
Time time;
} XSelectionEvent;
The selection, target, and property
members should be set to the values received in the SelectionRequest
event. Setting the property member to None indicates that
the conversion requested could not be made.
The data stored in the property must eventually be deleted. According to the current conventions, selection requestors are responsible for deleting the converted properties whose names they receive in SelectionNotify events. Owners are responsible for deleting all other properties involved in communicating selections.
A selection owner may need confirmation that the data comprising the selection has actually been transferred. They should express interest in PropertyNotify events for the requestor window and wait until the property in the SelectionNotify event has been deleted before assuming that the selection data has been transferred.
The XSelectionClearEvent structure
typedef struct {
int type;
unsigned long serial; /* # of last request processed by
* server */
Bool send_event; /* True if from a SendEvent request */
Display *display; /* Display the event was read from */
Window window;
Atom selection;
Time time;
} XSelectionClearEvent;
The time member is the time at which the ownership
changed hands, and the owner member is the window the new owner
specified in its XSetSelectionOwner() request.
If an owner loses ownership while it has a transfer in progress, that is to say before it receives notification that the requestor has received all the data, it must continue to service the ongoing transfer to completion.
To relinquish ownership of a selection voluntarily, a client should execute a XSetSelectionOwner() call for that selection atom, with owner specified as None and time specified as CurrentTime. Alternatively, the client may destroy the window used as the owner argument of the XSetSelectionOwner() call, or it may exit. In both cases, the ownership of the selection involved will revert to None.
The client that calls XConvertSelection() call will get a SelectionNotify event sent to it from the selection owner. The requestor, selection, time, and target arguments of this event will be the same as those on the XConvertSelection() request.
If the property member is None, the conversion has been refused. This can mean that there is no owner for the selection, that the owner does not support the conversion implied by target, or that the server did not have sufficient space to accommodate the data.
If the property member is not None, then that property will exist on the requestor window. The value of the selection can be retrieved from this property using XGetWindowProperty(). When using XGetWindowProperty() to retrieve the value of a selection, the property argument should be set to the property member in the SelectionNotify event. The type member should be set to AnyPropertyType, because the requestor has no way of knowing beforehand what type the selection owner will use.
The property in the SelectionNotify should be deleted by invoking XGetWindowProperty() with the delete argument set to True. As discussed above, the owner has no way of knowing when the data has been transferred to the requestor unless the property is removed.
It is important to observe that defining a new atom consumes resources in the server, and they are not released until the server reinitializes. Thus, it must be a goal to reduce the need for newly minted atoms.
The selection named by XA_PRIMARY is used for all commands which take only a single argument. It is the principal means of communication between clients which use the selection mechanism.
It is suggested that the selection named by XA_SECONDARY be used:
Target properties describe types of data. They contain
the C language types of the structures that are used for many of the Xlib
functions. The predefined target atoms are shown in Table 12-6.
| Type Atom | C Language Type |
|---|---|
| XA_ARC | XArc |
| XA_POINT | XPoint |
| XA_ATOM | Atom |
| XA_RGB_COLOR_MAP | Atom (standard colormap) |
| XA_BITMAP | Pixmap (of depth 1) |
| XA_RECTANGLE | XRectangle |
| XA_CARDINAL | int (dimensionless) |
| XA_STRING | char * |
| XA_COLORMAP | Colormap |
| XA_VISUALID | VisualID |
| XA_CURSOR | Cursor |
| XA_WINDOW | Window |
| XA_DRAWABLE | Drawable |
| XA_WM_HINTS | XWMHints |
| XA_FONT | Font |
| XA_INTEGER | int |
| XA_WM_SIZE_HINTS | XSizeHints |
| XA_PIXMAP | Pixmap |
The owner should not translate the selection into some
arbitrary fallback target type (such as XA_STRING) and return the
fallback target to the requestor in the SelectionNotify event, because
this might confuse the requestor. The conversion should simply fail. The
requestor then has the option of requesting another type. The requestor
can supply the target TARGETS to get a list of target types the owner supports.
The selection mechanism is superior for many applications, since it allows communication regarding the type of the data transferred. Selections are described in Section 12.4, "Selections." It is also possible for an application to use both cut buffers and selections.
The cut buffers are eight properties on the root window of screen 0 of a server. The buffers are numbered 0 to 7. Cut buffers rely on a prior agreement between the two clients regarding the format of the data to be placed in the cut buffers. The data that can be placed in a single cut buffer is limited to the maximum size of a single property, which is server-dependent.
Because the cut buffers are properties, it is possible to be notified when they have been written into. PropertyNotify events can assist applications in timing their communication. These are selected with PropertyChangeMask.
The functions that are used to read and write to cut buffers are XFetchBuffer(), XFetchBytes(), XStoreBuffer(), and XStoreBytes(). The routines with Bytes in the name use cut buffer 0 only, while the others may use any of the eight. XRotateBuffers() moves the contents of the eight buffers any number of positions.
The cut buffer properties are named by the predefined atoms XA_CUT_BUFFER0 to XA_CUT_BUFFER7.
The cut buffers can let applications implement a first-in, last-out stack of data. A client using this cut buffer mechanism must initially ensure that all eight buffer properties exist, using XChangeProperty() to append zero-length data to each. A client storing data in the cut buffers (an owner) must first rotate the ring of buffers by +1, using XRotateWindowProperties to rename XA_CUT_BUFFER0 to XA_CUT_BUFFER1 to .... to XA_CUT_BUFFER7 to XA_CUT_BUFFER0. It must then store the data into XA_CUT_BUFFER0, using XStoreBytes().
A client obtaining data from the cut buffers should use XFetchBytes() to retrieve the contents of XA_CUT_BUFFER0.
A client may, in response to a specific user request, rotate the cut buffers by -1, using XRotateWindowProperties to rename XA_CUT_BUFFER7 to XA_CUT_BUFFER6 .... and so on and XA_CUT_BUFFER0 to XA_CUT_BUFFER7.
Data should be stored to the cut buffers and the ring rotated only when requested by explicit user action. Users depend on their mental model of cut buffer operation and need to be able to identify operations that transfer data to and from the buffers.
Note that there is nothing magic about the properties
used by Xlib's cut buffer routines or those routines themselves. If an
application needs more buffers, it can intern additional atoms for CUT_BUFFER8
and so on and write its own equivalent of XStoreBuffer() and XFetchBuffer()
that can write and read these properties.
Xlib Programming Manual (O'Reilly & Associates, Inc.) |