RFC 1013
X Window System Protocol, version 11: Alpha update April 1987
X WINDOW SYSTEM PROTOCOL, VERSION 11 ๐
Okay, bestie, let's get you set up ๐ This is RFC 1013 by Robert W. Scheifler, filed under the Network Working Group in June 1987. It's labelled an "Alpha Update" dated April 1987, and it's Informational, so it's for info only and doesn't set an Internet standard ๐ธ The document doesn't say it obsoletes or updates anything, so there's nothing to swap out. Scheifler was at the Laboratory for Computer Science, 545 Technology Square, Room 418, Cambridge, MA 02139. The copyright is Massachusetts Institute of Technology, 1986 and 1987, and "X Window System" is an M.I.T. trademark โจ
Status of This Memo
This RFC is shared with the internet community for information only ๐ง It doesn't establish an Internet standard. The X window system has been widely reviewed and tested, and the community is encouraged to experiment with it ๐ฅ Distribution is unlimited (check the copyright notice on page 2).
Permission to use, copy, modify, and distribute this document is granted for any purpose and without fee, as long as the copyright notice shows up in every copy and both that notice and the permission notice stay attached. You also can't use M.I.T.'s name in any advertising or publicity about this document without specific, written prior permission ๐ M.I.T. makes no promises about how suitable this document or the protocol is for any purpose. It comes "as is," with no express or implied warranty ๐
Author: Robert W. Scheifler Laboratory for Computer Science 545 Technology Square, Room 418 Cambridge, MA 02139
Contributors:
- Dave Carver (Digital HPW)
- Branko Gerovac (Digital HPW)
- Jim Gettys (MIT/Project Athena, Digital)
- Phil Karlton (Digital WSL)
- Scott McGregor (Digital SSG)
- Ram Rao (Digital UEG)
- David Rosenthal (Sun)
- Dave Winchell (Digital UEG)
Implementors of initial server who provided useful input:
- Susan Angebranndt (Digital)
- Raymond Drewry (Digital)
- Todd Newman (Digital)
Invited reviewers who provided useful input:
- Andrew Cherenson (Berkeley)
- Burns Fisher (Digital)
- Dan Garfinkel (HP)
- Leo Hourvitz (Next)
- Brock Krizan (HP)
- David Laidlaw (Stellar)
- Dave Mellinger (Interleaf)
- Ron Newman (MIT)
- John Ousterhout (Berkeley)
- Andrew Palay (ITC CMU)
- Ralph Swick (MIT)
- Craig Taylor (Sun)
- Jeffery Vroom (Stellar)
This document doesn't try to explain the rationale or practical details you'd need to fully get the protocol or see how it fits into a complete system ๐ Knowing X Version 10 will definitely help you follow along.
The protocol has a lot of management mechanisms that normal apps aren't meant to use, and you don't need all of them to build a particular user interface. Keep in mind the protocol is meant to provide mechanism, not policy ๐
This document doesn't attempt to define precise formats or bit encodings ๐ฆ
1. Terminology
Access control list
X keeps a list of hosts that are allowed to run client programs ๐ By default, only programs on the local host can use the display, plus any hosts in an initial list the server reads. Clients on the local host can change this "access control list." Some server implementations may also have other authorization mechanisms.
Active grab
A grab is "active" when the grabbing client actually owns the pointer or keyboard, and it's the only one with that ownership ๐
Ancestors
If W is an inferior of A, then A is an "ancestor" of W ๐ธ
Atom
An "atom" is a unique ID that matches a string name โจ Atoms are used to identify properties, types, and selections.
Backing store
When a server keeps the contents of a window, the off-screen saved pixels are called a "backing store" ๐
Bit gravity
When a window gets resized, its contents don't necessarily get thrown away ๐ฆ You can ask the server to move the old contents to some region of the window. The server doesn't guarantee it'll do this. Pulling the contents toward a specific spot in a window is called "bit gravity" ๐
Bitmap
A "bitmap" is a pixmap with a depth of one ๐ง
Button grabbing
A client can passively "grab" buttons on the pointer ๐ซถ When one of those buttons gets pressed, the client actively grabs the pointer.
Byte order
For image data (pixmaps and bitmaps), the server defines the byte order, so clients with a different native byte ordering must swap bytes as needed ๐ฅ For every other part of the protocol, the client defines the byte order, and the server swaps bytes as needed ๐
Children
The "children" of a window are its first-level subwindows ๐
Client
An application program connects to the window system server through some interprocess communication (IPC) path, like a TCP connection or a shared memory buffer ๐ The client is that IPC path itself. A program with several paths open to the server counts as several clients to the protocol ๐ญ Resource lifetimes are controlled by connection lifetimes, not by program lifetimes.
Clipping regions
In a graphics context, you can specify a bitmap or a list of rectangles to limit output to a particular region of the window โจ The shape defined by that bitmap or those rectangles is called a "clipping region."
Color cell
A colormap entry is called a "color cell" ๐ Each entry holds three values for red, green, and blue intensities. These values are always treated as 16-bit unsigned numbers, where zero is the minimum intensity. The server scales them to match the display hardware. The components of a cell line up with the components of other cells in DirectColor and TrueColor colormaps ๐
Colormap
A "colormap" is made up of a set of color cells ๐ธ A pixel value indexes the colormap to produce the intensities that get displayed. Depending on hardware limits, one or more colormaps can be installed at a time, so that windows linked to those maps show true colors ๐
Connection
The IPC path between the server and a client program is called a "connection" ๐ฆ A client program usually (but not always) has one connection to the server, and requests and events get sent over it.
Containment
A window "contains" the pointer when the window is viewable and the hotspot of the cursor is inside a visible region of the window, or inside a visible region of one of its inferiors ๐ง The window's border counts as part of the window for containment. The pointer is "in" a window if that window contains the pointer and none of its inferiors do ๐
Coordinate system
The coordinate system has X running horizontally and Y running vertically, with the origin [0, 0] at the upper left ๐ Coordinates are discrete and measured in pixels. Every window and pixmap has its own coordinate system. For a window, the origin sits at the inside upper left, just inside the border โจ
Cursor
A "cursor" is the visible shape of the pointer on a screen ๐ It has a hot spot, a source bitmap, a shape bitmap, and a pair of colors. The cursor defined for a window controls how the pointer looks while it's inside that window ๐ฅ
Depth
The "depth" of a window or pixmap is the number of bits per pixel it has ๐ The depth of a gcontext is the depth of the gcontext's root ๐
Device
Keyboards, mice, tablets, trackballs, button boxes, and so on are all called input "devices" ๐ The core protocol only deals with two of them: "the keyboard" and "the pointer" ๐
Drawable
Windows and pixmaps can both be used as sources and destinations in graphics operations, and they're collectively called "drawables" ๐ซถ An InputOnly window, though, can't be used as a source or destination in a graphics operation ๐
Event
Clients get told about things asynchronously through "events" ๐ธ Events can come from devices asynchronously, or as side effects of client requests. They're grouped by type, and the server never sends a client an event type unless that client has specifically asked to hear about it ๐ Other clients can, however, force events to be sent to other clients. Events are usually reported relative to a window ๐ฆ
Event mask
Events get requested relative to a window โจ The set of event types a client asks for relative to a window is described using an "event mask" ๐
Event synchronization
There are some race conditions that can happen when device events get demultiplexed to clients. For example, figuring out where pointer and keyboard events should go in the middle of window management operations can be tricky ๐ญ The event synchronization mechanism lets device events get processed synchronously ๐ง
Event propagation
Device-related events "propagate" from the source window up through ancestor windows, until some client has shown interest in handling that type of event, or until the event gets discarded explicitly ๐
Event source
The smallest window containing the pointer is the "source" of a device-related event ๐
Exposure event
Servers don't guarantee they'll keep window contents when windows are covered or reconfigured. Some regions of windows can lose their contents ๐
Extension
Clients can define named "extensions" to the core protocol to extend the system ๐ Extensions can add output requests, resources, and event types, and that's totally expected โจ
Font
A "font" is an array of glyphs, which are usually characters ๐ซถ The protocol doesn't translate or interpret character sets. The client just gives the values used to index the glyph array ๐ฅ A font also carries extra metric information for working out spacing between glyphs and between lines ๐
Glyph
A "glyph" is an image, usually of a character, inside a font ๐
Grab
Keyboard keys, the keyboard itself, pointer buttons, the pointer, and the server can all be "grabbed" for exclusive use by a client ๐ Normal apps generally aren't supposed to use these facilities. They're meant for input and window managers to build different styles of user interface ๐ธ
Graphics context
Information for graphics output is stored in "GC"s, such as the foreground pixel, background pixel, line width, clipping region, and so on ๐ฆ
Hotspot ๐ฏ okay so a cursor has this thing called a "hot spot," which is the exact point inside the cursor that gets reported as the pointer's coordinates. bestie, that's the tip of the arrow, lowkey the whole point ๐
Identifier ๐ชช every resource has an "identifier," a unique value that clients use to name it. the best part? it works over any connection, so you can name the same resource from wherever you are ๐ฆ
Inferiors ๐ณ the "inferiors" of a window are all of its subwindows nested under it: the children, the children's children, and so on. family tree energy, no cap ๐ง
Input focus โจ๏ธ the "input focus" is nominally where keyboard input goes. by default, keyboard events go to the client that's interested in whichever window the pointer is sitting in. this is called a "real estate driven" input focus, and I'm obsessed with the name ๐ญ you can also attach keyboard input to one specific window, and then events go to that window's client no matter where the pointer is ๐
Input manager ๐๏ธ control over keyboard input is typically handled by an "input manager" client. it's giving gatekeeper โจ
InputOnly window ๐ป a window that can't be used for graphics requests. InputOnly windows are "invisible," but they're still useful for controlling cursors, generating input events, and grabbing ๐ซถ
InputOutput window ๐ช the "normal" kind of opaque window, used for both input and output. the classic, the icon, the one everyone thinks of ๐
Key grabbing ๐ a key on the keyboard can be passively "grabbed" by a client. when that key gets pressed, the keyboard is then actively grabbed by that client. the grab is set up in advance and kicks in on the press ๐
Keyboard grabbing โจ๏ธ a client can actively "grab" control of the keyboard. key events then go to that client instead of the client they'd normally have gone to. main character energy, literally ๐
Mapping ๐บ๏ธ a window is "mapped" once a map call has been performed on it. unmapped windows are never viewable or visible. they are simply not showing up, periodt ๐
Modifier keys โก Shift, Control, Meta, Super, Hyper, ALT, Compose, Apple, CapsLock, ShiftLock, and similar keys are all called "modifier" keys. the whole squad of keys that change what the other keys do ๐
Obscures ๐ซฃ window A "obscures" window B if both are viewable InputOutput windows, A is higher in the global stacking order, and the rectangle defined by the outside edges of intersects the rectangle defined by the outside edges of B. the spec leaves a word out right there, so we're just gonna stay faithful to what's written ๐ the big deal is the (fine) difference from "occludes," and window borders are included in the calculation โจ
โ ๏ธ bestie heads up: the wording in "Obscures" is missing a word, so the sentence reads a little off in the original too. the rules are the rules, so we keep it exactly as it is.
Occludes ๐ช window A "occludes" window B if both are mapped, A is higher in the global stacking order, and the rectangle defined by the outside edges of A intersects the rectangle defined by the outside edges of B. note the (fine) distinction with "obscures," and yep, window borders count here too ๐ธ
Padding ๐งธ some padding bytes get inserted into the data stream so protocol requests stay aligned on natural boundaries. this makes it easier to port to some machine architectures. the unsung hero, ngl ๐ซถ
Parent window ๐ช if C is a child of P, then P is the "parent" of C. simple, iconic, we love a clear relationship ๐
Passive grab ๐ชค grabbing a key or button is a "passive" grab. the grab activates only when that key or button is actually pressed. patient and sneaky, slay ๐
Pixel value ๐จ a "pixel" is an N-bit value, where N is the number of bit planes used in a particular window or pixmap. for a window, a pixel value indexes a colormap, and the colormap is what gives you the actual color that gets displayed ๐
Pixmap ๐ง a "pixmap" is a three dimensional array of bits. it's normally thought of as a two dimensional array of pixels, where each pixel can be a value from 0 to (2^N)-1, and N is the depth (z axis) of the pixmap. you can also picture it as a stack of N bitmaps. it's literally layers on layers ๐ฐ
Plane mask ๐ญ graphics operations can be restricted so they only affect a subset of a destination's bit planes. a "plane mask" is the bit mask that describes which planes are going to be modified, and it's stored in a graphics context ๐งโโ๏ธ
Pointer ๐ฑ๏ธ the "pointer" is the pointing device attached to the cursor, and it's tracked on the screens. the cursor's little sidekick ๐ฆ
Pointer grabbing ๐ค a client can actively "grab" control of the pointer. button and motion events then go to that client rather than the client they would normally have gone to. the pointer is hers now ๐
Pointing device ๐ญ a "pointing device" is typically a mouse or tablet, or some other device with effective dimensional motion. only one visible cursor is defined by the core protocol, and it tracks whichever pointing device is attached as the pointer. one cursor, one vibe โจ
Property ๐ windows may have associated "properties," each made up of a name, a type, a data format, and some data. the protocol doesn't interpret properties at all. they're a general-purpose naming mechanism for clients. for example, clients might share resize hints, program names, and icon formats with a window manager through properties. it's basically sticky notes for windows ๐๏ธ๐
Property list ๐ the "property list" of a window is the list of properties that have been defined for that window. the guest list, if you will ๐
Redirecting control ๐ง window managers (or client programs) may want to enforce window layout policy in various ways. when a client tries to change the size or position of a window, that operation can be "redirected" to a specified client, so it doesn't actually get performed. the bouncer of window layouts ๐ซ
Reply ๐ information a client asked for gets sent back with a "reply." events and replies are both multiplexed on the same connection, and most requests don't generate replies at all ๐งต
Request ๐ฎ a command to the server is called a "request." it's a single block of data sent over a connection. one message, one ask, very clean ๐ท
Resource ๐ windows, pixmaps, cursors, fonts, graphics contexts, and colormaps are all known as "resources." they each have unique identifiers for naming purposes. a resource's lifetime is bounded by the lifetime of the connection it was created over, so when that connection goes, the resource goes too ๐ฅฒ
Root ๐ฑ the "root" of a pixmap or gcontext is the same as the root of whatever drawable was used when it was created. the "root" of a window is the root window it was created under ๐ณ
Root window ๐ฅ๏ธ each screen has a "root window" covering it. it can't be reconfigured or unmapped, but otherwise it acts like a full fledged window. a root window has no parent, so it's the top of the family line ๐
Save set ๐ the "save set" of a client is a list of other clients' windows. if those windows are inferiors of one of the client's windows when the connection closes, they shouldn't be destroyed. and if they've been unmapped, they should be remapped. save sets are typically used by window managers to avoid lost windows if the manager ends abnormally. it's the safety net, bestie ๐ซถ
Screen ๐บ a server may provide several independent "screens," which typically have physically independent monitors. that's the expected setup when there's only a single keyboard and pointer shared among the screens ๐
Server ๐ the "server" provides the basic windowing mechanism. it handles IPC connections from clients, demultiplexes graphics requests onto the screens, and multiplexes input back to the right clients. the hub of the whole operation ๐ง โจ
Server grabbing ๐ the server can be "grabbed" by a single client for exclusive use. while the grab is in place, no requests from other client connections get processed, until the grab is complete. this is typically only a transient state, used for things like rubber-banding and pop-up menus, or to execute requests indivisibly. a brief lockdown, not a lifestyle ๐ค
Sibling ๐ฏ children of the same parent window are known as "sibling" windows. sisters, basically ๐
Stacking order ๐ฅ sibling windows may "stack" on top of each other. windows above both obscure and occlude lower windows. it's a lot like paper on a desk. the relationship between sibling windows is known as the "stacking order" ๐
Stipple ๐ฆ๏ธ a "stipple pattern" is a bitmap used to tile a region. it works as an additional clip mask for a fill operation with the foreground color. fancy texture, lowkey iconic ๐จ
Tile ๐งฑ a pixmap can be replicated in two dimensions to "tile" a region. the pixmap itself is also known as a "tile." tiles all the way down ๐ซ ๐งฉ
Timestamp โฑ๏ธ a time value, expressed in milliseconds, typically counted since the last server reset. timestamp values wrap around after about 49.7 days. the server, given that its current time is represented by timestamp T, always interprets timestamps from clients by treating half of the timestamp space as earlier in time than T, and the other half as later in time than T. one timestamp value, named CurrentTime, is never generated by the server. it's reserved for requests that want to represent the current server time. the clock that forgets, but in a really organized way ๐ฐ๏ธ๐
Type ๐ท๏ธ a type is an arbitrary atom used to identify how property data should be interpreted. the server doesn't interpret types at all, they're purely for the benefit of clients ๐
Unviewable ๐ a window is "unviewable" if it's mapped but some ancestor is unmapped. mapped, yes, but stuck behind a closed door ๐ช
Viewable ๐ a window is "viewable" if it and all of its ancestors are mapped. this does not mean any portion of the window is actually visible, so don't get it twisted ๐
Visible ๐ซง a region of a window is "visible" if someone looking at the screen can actually "see" it. the window has to be viewable, and the region can't be occluded by any other window. seen, not just there ๐๏ธโจ
Window gravity ๐งฒ when windows are resized, subwindows may be repositioned automatically relative to some position in the window. this attraction of a subwindow to some part of its parent is known as "window gravity." it's giving magnetic pull ๐
Window manager ๐ฉ manipulation of windows on the screen, plus much of the user interface (policy), is typically provided by a "window manager" client. the stylist of the whole setup ๐ ๐
XYFormat ๐ง the data for a pixmap is said to be in "XYFormat" if it's organized as a set of bitmaps representing individual bit planes. planes, all stacked up nicely ๐ฅ
ZFormat ๐ญ the data for a pixmap is said to be in "ZFormat" if it's organized as a set of pixel values in scanline order. one row at a time, very orderly, very cute ๐
2. PROTOCOL FORMATS โจ
Request Format ๐
okay so every request starts with an 8-bit major opcode and a 16-bit length field, and the length is counted in units of 4 bytes ๐ the 4-byte header holds the major opcode, the length field, and one spare data byte, and then zero or more extra bytes of data come after it. the length field covers the whole request, header included.
the length field has to match the minimum length the request needs. if it's smaller or larger than that, an error is generated ๐ unused bytes in a request don't have to be zero, so don't panic if they're random โจ major opcodes 128 through 255 are reserved for extensions. extensions usually bundle several requests together, so their requests typically carry a minor opcode in that spare data byte. where that minor opcode goes, how it's read, and everything else about extension requests are left to the extension itself, not the core protocol ๐ง finally, every request gets an implicit sequence number, starting at one, which replies, errors, and events use to point back at it ๐ฆ
(no diagram in this section)
Reply Format ๐ธ
replies carry a 32-bit length field, also counted in units of 4 bytes. every reply is 32 bytes, followed by however many extra bytes the length field says โจ unused bytes in a reply aren't guaranteed to be zero, so again, not a thing to rely on ๐ every reply also includes the least significant 16 bits of the sequence number of the request it answers. main character energy for the request-reply matching ๐
Error Format ๐
errors are 32 bytes long, and each one has an 8-bit error code. error codes 128 through 255 are reserved for extensions ๐ every error also tells you the major and minor opcodes of the request that failed, plus the least significant 16 bits of that request's sequence number.
for these errors (see Section 5), the failing resource id is returned too: Colormap, Cursor, Drawable, Font, GContext, IDChoice, Pixmap, and Window. for Atom errors you get the failing atom, and for Value errors you get the failing value. every other core error returns no extra data ๐ซถ same as replies, unused bytes in an error aren't guaranteed to be zero ๐ญ
Event Format ๐ฆ
events are 32 bytes long, and unused bytes in them aren't guaranteed to be zero either. every event has an 8-bit type code, and the most significant bit of that code gets set when the event came from a SendEvent request โจ event codes 64 through 127 are reserved for extensions, though the core protocol doesn't give you a way to ask for those events ๐
every core event (except KeymapNotify) also has the least significant 16 bits of the sequence number of the last request the client sent that the server processed, or is still processing right now ๐
3. Syntax โจ
the syntax {...} wraps a set of alternatives, and the syntax [...] wraps a set of structure components ๐
in general, TYPEs are written in upper case and AlternativeValues are capitalized ๐
requests in Section 10 are described like this:
RequestName
arg1: type1
...
argN: typeN
=>
result1: type1
...
resultM: typeM
Errors: kind1, ..., kindK
Description.
if there's no => in the description, the request has no reply (it's asynchronous), but errors can still show up ๐
events in Section 12 are described like this:
EventName
value1: type1
...
valueN: typeN
Description.
4. Common Types ๐
LISTofFOO
a type name like LISTofFOO means a counted list of elements of type FOO ๐ง the size of its length field can vary (it isn't necessarily the same size as a FOO), it may be implicit in some cases, and this document doesn't fully specify it ๐ซ
BITMASK and LISTofVALUE
BITMASK and LISTofVALUE are kinda special ๐ฅน lots of requests take arguments like this:
value-mask: BITMASK
value-list: LISTofVALUE
these let the client pick a subset of a mixed bag of "optional" arguments. the value-mask says which arguments are being provided, and each argument gets its own unique bit position ๐ the BITMASK usually has more bits than there are defined arguments. unused bits in the value-mask must be zero, or the server generates a Value error โ ๏ธ
the value-list has one value for each one bit in the mask, read from least significant bit to most significant bit ๐ฆ each value takes up 4 bytes, but the real value only uses the least significant bytes it needs. the values of the unused bytes don't matter ๐ธ
Or Types
a type written as "T1 or ... or Tn" means the union of those types ๐ a single-element type is just written as the element, without braces โจ
the basic types look like this:
DEVICE: 32-bit id (<class,model,manufacturer,unit> 8 bits each)
WINDOW: 32-bit id
PIXMAP: 32-bit id
CURSOR: 32-bit id
FONT: 32-bit id
GCONTEXT: 32-bit id
COLORMAP: 32-bit id
DRAWABLE: WINDOW or PIXMAP
ATOM: 32-bit id (top 3 bits guaranteed to be zero)
VISUALID: 32-bit id (top 3 bits guaranteed to be zero)
VALUE: 32-bit quantity (used only in LISTofVALUE)
INT8: 8-bit signed integer
INT16: 16-bit signed integer
INT32: 32-bit signed integer
CARD8: 8-bit unsigned integer
CARD16: 16-bit unsigned integer
CARD32: 32-bit unsigned integer
TIMESTAMP: CARD32
BITGRAVITY: {Forget, Static,
NorthWest, North, NorthEast,
West, Center, East,
SouthWest, South, SouthEast}
WINGRAVITY: {Unmap, Static,
NorthWest, North, NorthEast,
West, Center, East,
SouthWest, South, SouthEast}
BOOL: {True, False}
EVENT: {KeyPress, KeyRelease,
OwnerGrabButton,
ButtonPress, ButtonRelease, EnterWindow, LeaveWindow,
PointerMotion, PointerMotionHint,
Button1Motion, Button2Motion, Button3Motion,
Button4Motion, Button5Motion, ButtonMotion
Exposure, VisibilityChange,
StructureNotify, ResizeRedirect,
SubstructureNotify, SubstructureRedirect,
FocusChange,
PropertyChange, ColormapChange,
KeymapState}
POINTEREVENT: {ButtonPress, ButtonRelease, EnterWindow, LeaveWindow,
PointerMotion, PointerMotionHint,
Button1Motion, Button2Motion, Button3Motion,
Button4Motion, Button5Motion, ButtonMotion
KeymapState}
DEVICEEVENT: {KeyPress, KeyRelease,
ButtonPress, ButtonRelease,
PointerMotion,
Button1Motion, Button2Motion, Button3Motion,
Button4Motion, Button5Motion, ButtonMotion}
KEYCODE: CARD8
BUTTON: CARD8
KEYMASK: {Shift, CapsLock, Control, Mod1, Mod2, Mod3, Mod4, Mod5}
BUTMASK: {Button1, Button2, Button3, Button4, Button5}
KEYBUTMASK: KEYMASK or BUTMASK
STRING8: LISTofCARD8
STRING16: LISTofCHAR2B
CHAR2B: [byte1, byte2: CARD8]
POINT: [x, y: INT16]
RECTANGLE: [x, y: INT16,
width, height: CARD16]
ARC: [x, y: INT16,
width, height: CARD16,
angle1, angle2: INT16]
HOST: [family: {Internet, NS, ECMA, Datakit, DECnet}
address: LISTofCARD8]
the [x,y] coordinates of a RECTANGLE give the upper left corner ๐
the main idea behind "large" characters in a STRING16 is that each one is two bytes used as an index into a 2-D matrix ๐งฉ that's why it's CHAR2B and not CARD16. this matches the JIS/ISO way of indexing two-byte characters, and it's expected that most "large" fonts will use two-byte matrix indexing ๐ for large fonts built with linear indexing, a CHAR2B can be read as a 16-bit number with byte1 as the most significant byte. so clients should always send those 16-bit character values most significant byte first, because the server never byte-swaps CHAR2B quantities ๐ซ๐
the length, format, and meaning of a HOST address depend on its family ๐
5. Errors ๐ฅ
generally, when a request ends in an error, it has no side effects (no partial execution) ๐ โโ๏ธ the only requests where that isn't true are ChangeWindowAttributes, ChangeGC, PolyText8, PolyText16, FreeColors, StoreColors, and ChangeKeyboardControl ๐
these are the error codes the various requests can return:
Access ๐
- An attempt to grab a key/button combination that another client has already grabbed.
- An attempt to free a colormap entry that the client didn't allocate.
- An attempt to store into a read-only or an unallocated colormap entry.
- An attempt to modify the access control list from somewhere other than the local (or otherwise authorized) host.
- An attempt to select an event type that at most one client can select at a time, when another client has already selected it.
Alloc ๐ง
The server failed to allocate the requested resource ๐ฉ
Note that this only covers allocation errors at a very coarse level. It isn't meant to cover (and in practice can't hope to cover) every case of a server running out of allocation space in the middle of service โจ
The semantics when a server runs out of allocation space are left unspecified ๐คทโโ๏ธ
Atom ๐ค
A value for an ATOM argument doesn't name a defined ATOM ๐
Colormap ๐จ
A value for a COLORMAP argument doesn't name a defined COLORMAP ๐
Cursor ๐ฑ๏ธ
A value for a CURSOR argument doesn't name a defined CURSOR ๐ค
Drawable ๐ผ๏ธ
A value for a DRAWABLE argument doesn't name a defined WINDOW or PIXMAP ๐ซ
Font ๐ก
A value for a FONT or <FONT or GCONTEXT> argument doesn't name a defined FONT ๐
GContext โ๏ธ
A value for a GCONTEXT argument doesn't name a defined GCONTEXT ๐ต
IDChoice ๐
The value chosen for a resource identifier is either outside the range assigned to the client, or it's already in use ๐ฌ
Implementation ๐ ๏ธ
The server doesn't implement some part of the request. A server that generates this error for a core request is deficient ๐ so this error isn't listed for any of the requests, but clients should still be ready to receive these errors and handle or discard them ๐ง
Length ๐
The length of a request is shorter or longer than what's needed to minimally contain the arguments ๐ฅด
Match ๐ค
An InputOnly window is used as a DRAWABLE ๐ซ
Some argument (or pair of arguments) has the correct type and range, but doesn't "match" in some other way the request requires ๐
Name ๐ท๏ธ
A font or color of the specified name doesn't exist ๐ญ
Pixmap ๐บ๏ธ
A value for a PIXMAP argument doesn't name a defined PIXMAP ๐
Property ๐ฆ
The requested property doesn't exist for the specified window ๐ฅฒ
Request ๐จ
The major or minor opcode doesn't specify a valid request ๐คจ
Value ๐ข
Some numeric value falls outside the range the request accepts. Unless a specific range is given for an argument, the full range defined by the argument's type is accepted. Any argument defined as a set of alternatives can generate this error ๐จ
Window ๐ช
A value for a WINDOW argument doesn't name a defined WINDOW ๐
Note: the Atom, Colormap, Cursor, Drawable, Font, GContext, Pixmap, and Window errors are also used when the argument type is extended
by union with a set of fixed alternatives, e.g., <Window or PointerRoot or None> ๐
6. KEYBOARDS ๐
Keycodes are always in the inclusive range [8,255] ๐.
Okay, so for keyboards with both left-side and right-side modifier keys (like Shift and Control), the mask bits in the protocol always define the OR of the keys โจ. If the keys are electronically distinguishable, they can have separate up/down events generated, and clients that want to tell them apart can track the individual states manually ๐ซถ.
Here's the core bit: we need a universal association between keycaps and keycodes ๐ฅ. A keycap is the graphical info printed on a keyboard key, like"$ 4","T", or"+ =".
7. POINTERS ๐
Buttons are always numbered starting with one, no cap ๐.
8. PREDEFINED ATOMS ๐ฆ
Predefined atoms aren't strictly necessary, and they might not be useful in every environment. But they'll eliminate a ton of InternAtom requests in most apps, so lowkey iconic ๐. The core protocol doesn't attach any semantics to these names, except where they're used in FONTPROP structures (see QueryFont). Also, ngl, upper and lower case matter here ๐.
BITMAP ICON_SIZE RGB_GREEN_MAP
COMMAND ITALIC_ANGLE RGB_RED_MAP
COPYRIGHT MAX_SPACE SECONDARY
CUT_BUFFER0 MIN_SPACE SIZE_HINTS
CUT_BUFFER1 NAME STRIKEOUT_ASCENT
CUT_BUFFER2 NORMAL_HINTS STRIKEOUT_DESCENT
CUT_BUFFER3 NORM_SPACE STRING
CUT_BUFFER4 PIXMAP SUBSCRIPT_X
CUT_BUFFER5 POINT_SIZE SUBSCRIPT_Y
CUT_BUFFER6 PRIMARY SUPERSCRIPT_X
CUT_BUFFER7 QUAD_WIDTH SUPERSCRIPT_Y
DEFAULT_CHAR RECTANGLE UNDERLINE_POSITION
END_SPACE RESIZE_HINT UNDERLINE_THICKNESS
FACE_NAME RESOLUTION WEIGHT
FAMILY_NAME RGB_BEST_MAP WINDOW
FONT_ASCENT RGB_BLUE_MAP WM_HINTS
FONT_DESCENT RGB_COLOR_MAP X_HEIGHT
ICON RGB_DEFAULT_MAP ZOOM_HINTS
ICON_NAME
9. CONNECTION SETUP ๐
For remote clients, the X protocol can be built on top of any reliable byte stream ๐ฆ. For TCP connections, displays on a given host are numbered starting from 0, and the server for display N listens and accepts connections on port 6000+N โจ.
The client has to send an initial byte of data to identify the byte order it's going to use ๐
. That byte must be octal 102 or 154. The value 102 (ASCII uppercase B) means values are transmitted most significant byte first, and the value 154 (ASCII lowercase l) means values are transmitted least significant byte first ๐. Except where the protocol explicitly notes otherwise, all 16-bit and 32-bit quantities sent by the client must be transmitted with this byte order, and all 16-bit and 32-bit quantities returned by the server will be transmitted with this byte order ๐ซถ.
Following the byte-order byte, the client sends the following information at connection setup:
protocol-major-version: CARD16
protocol-minor-version: CARD16
authorization-protocol-name: STRING8
authorization-protocol-data: STRING8
The version numbers show which version of the protocol the client expects the server to implement. There's more on that below ๐ง. The authorization name shows which authorization protocol the client expects the server to use, and the data is specific to that protocol. Specifying valid authorization mechanisms isn't part of the core X protocol, bestie ๐. It's hoped that eventually one authorization protocol will be agreed upon. In the meantime, a server that implements a different protocol than the client expects, or a server that only implements the host-based mechanism, will simply ignore this information ๐.
The client receives the following at connection setup:
success: BOOL
protocol-major-version: CARD16
protocol-minor-version: CARD16
length: CARD16
length is the amount of additional data to follow, in units of 4 bytes ๐. The version numbers are an escape hatch in case future revisions of the protocol are necessary ๐ฅ. In general, the major version increments for incompatible changes, and the minor version increments for small upward compatible changes. Barring changes, the major version will be eleven, and the minor version will be zero ๐. The protocol version numbers returned indicate the protocol the server actually supports, and that might not equal the version the client sent ๐ญ. The server can (but doesn't have to) refuse connections from clients that offer a different version than the server supports. A server can (but doesn't have to) support more than one version at the same time ๐.
If authorization fails, the client receives this additional data:
reason: STRING8
If authorization is accepted, the client receives this additional data:
vendor: STRING8
release-number: CARD32
resource-id-base, resource-id-mask: CARD32
image-byte-order: {LSBFirst, MSBFirst}
bitmap-format-scanline-unit: {8, 16, 32}
bitmap-format-scanline-pad: {8, 16, 32}
bitmap-format-bit-order: {LeastSignificant, MostSignificant}
pixmap-formats: LISTofFORMAT
roots: LISTofSCREEN
keyboard: DEVICE
pointer: DEVICE
motion-buffer-size: CARD32
maximum-request-length: CARD16
where
FORMAT: [depth: CARD8,
bits-per-pixel: {4, 8, 16, 24, 32}
scanline-pad: {8, 16, 32}]
SCREEN: [root: WINDOW
device: DEVICE
width-in-pixels, height-in-pixels: CARD16
width-in-millimeters,height-in-millimeters:CARD16
allowed-depths: LISTofDEPTH
root-depth: CARD8
root-visual: VISUALID
default-colormap: COLORMAP
white-pixel, black-pixel: CARD32
min-installed-maps, max-installed-maps: CARD16
backing-stores: {Never, WhenMapped, Always}
save-unders: BOOL
current-input-masks: SETofEVENT]
DEPTH: [depth: CARD8
visuals: LISTofVISUALTYPE]
VISUALTYPE: [visual-id: VISUALID
class: {StaticGray, StaticColor,
TrueColor,GrayScale, PseudoColor,
DirectColor}
red-mask, green-mask, blue-mask: CARD32
bits-per-rgb-value: CARD8
colormap-entries: CARD16]
Per server information:
The vendor string gives some identification of the owner of the server implementation ๐. The semantics of the release-number are controlled by the vendor ๐ .
The resource-id-mask contains a single contiguous set of bits (at least 18). The client allocates resource ids by choosing a value with (only) some subset of these bits set, and ORing it with resource-id-base. Only values built this way can be used to name newly created resources over this connection ๐. Resource ids never have the top 3 bits set ๐ โโ๏ธ. The client isn't restricted to linear or contiguous allocation of resource ids โจ. Once an id has been freed, it can be reused, but that shouldn't be necessary. An id must be unique with respect to the ids of all other resources, not just other resources of the same type ๐ซถ.
Although the server is generally responsible for byte swapping data to match the client, images are always transmitted and received in formats (including byte order) specified by the server ๐ง. The byte order for images is given by image-byte-order, and it applies to each scanline unit in XYFormat (bitmap) format, and to each pixel value in ZFormat.
A bitmap is represented in scanline order. Each scanline is padded to a multiple of bits as given by bitmap-format-scanline-pad. The pad bits are of arbitrary value. The scanline is quantized in multiples of bits as given by bitmap-format-scanline-unit. Within each unit, the leftmost bit in the bitmap is either the least or most significant bit in the unit, as given by bitmap-format-bit-order. If a pixmap is represented in XYFormat, each plane is represented as a bitmap, and the planes appear from most to least significant in bit order ๐ธ.
For each pixmap depth supported by some screen, pixmap-formats lists the ZFormat used to represent images of that depth. In ZFormat, the pixels are in scanline order, left to right within a scanline. The number of bits used to hold each pixel is given by bits-per-pixel, and it may be larger than strictly required by the depth. When bits-per-pixel is 4, the order of nibbles in the byte is the same as the image byte-order. Each scanline is padded to a multiple of bits as given by scanline-pad ๐.
How a pointing device roams the screens is up to the server implementation, and it's transparent to the protocol. No geometry among screens is defined ๐.
The server may keep the recent history of pointer motion, and at a finer granularity than MotionNotify events report. That history is available via the GetPointerMotions request. The approximate size of the history buffer is given by motion-buffer-size ๐ฆ.
Maximum-request-length specifies the maximum length of a request, in 4-byte units, that the server accepts. In other words, it's the biggest value that can show up in the length field of a request. Requests larger than this generate a Length error, and the server will read and simply discard the entire request ๐ญ. Maximum-request-length will always be at least 4096 (so requests of length up to and including 16384 bytes will be accepted by all servers) ๐.
Per screen information:
The allowed-depths specifies which pixmap and window depths are supported. Pixmaps are supported for each depth listed, and windows of that depth are supported if at least one visual type is listed for the depth. A pixmap depth of one is always supported and listed, but windows of depth one might not be supported. A depth of zero is never listed, but zero-depth InputOnly windows are always supported ๐.
Root-depth and root-visual specify the depth and visual type of the root window. Width-in-pixels and height-in-pixels specify the size of the root window, which can't be changed. The class of the root window is always InputOutput. Width-in-millimeters and height-in-millimeters can be used to figure out the physical size and the aspect ratio ๐.
The default-colormap is the one initially associated with the root window. Clients with minimal color requirements that create windows of the same depth as the root may want to allocate from this map by default ๐จ.
Black-pixel and white-pixel can be used to implement a "monochrome" application ๐ค๐ค. These pixel values are for permanently allocated entries in the default-colormap. The actual RGB values may be settable on some screens โจ.
The border of the root window starts out as a pixmap filled with the black-pixel ๐ค, and the initial background is a pixmap filled with some unspecified two-color pattern made from black-pixel and white-pixel โจ. Very cute, very intentional ๐
Min-installed-maps is the number of maps that can be guaranteed to be installed at the same time (with InstallColormap), no matter how many entries each map has allocated ๐บ๏ธ. Max-installed-maps is the most maps that might possibly be installed at once, depending on how they were allocated ๐ฅ. For the usual case of a single hardware colormap, both values are one, so it's giving simple ๐
Backing-stores tells you whether the server supports backing stores for this screen ๐ธ. It might still be limited in how many windows it can handle at once, so don't get too ambitious ๐ If save-unders is True, the server can support the save-under mode in CreateWindow and ChangeWindowAttributes, but again, storage limits can apply ๐ง
The current-input-events is whatever GetWindowAttributes would return for the all-event-masks of the root window ๐
Per visual-type information:
A given visual type might be listed for more than one depth, or for more than one screen. Lowkey it shows up everywhere ๐ฆ
For PseudoColor, a pixel value indexes a colormap to produce independent RGB values, and those RGB values can be changed dynamically ๐ GrayScale is treated the same as PseudoColor, except that which primary drives the screen is undefined, so the client should always store the same value for red, green, and blue in colormaps ๐ฉถ For DirectColor, a pixel value is split into separate RGB subfields, and each subfield separately indexes the colormap for its corresponding value. The RGB values can be changed dynamically too ๐ TrueColor is treated the same as DirectColor, except the colormap has predefined read-only RGB values. Those are server-dependent, but they provide (near-)linear ramps in each primary โจ StaticColor is treated the same as PseudoColor, except its colormap has predefined read-only RGB values, which are also server-dependent ๐ซถ StaticGray is treated the same as StaticColor, except the red, green, and blue values are equal for any single pixel value, which gives you shades of gray ๐ซ๏ธ A StaticGray with a two-entry colormap can be thought of as "monochrome" ๐ค๐ค
The red-mask, green-mask, and blue-mask are only defined for DirectColor and TrueColor ๐จ Each one has one contiguous set of bits, and none of them intersect ๐ซ
The bits-per-rgb-value gives the log base 2 of the approximate number of distinct color values (individually) of red, green, and blue ๐ข Actual RGB values are always passed in the protocol within a 16-bit spectrum, which is a big, gorgeous range ๐
The colormap-entries defines the number of available colormap entries in a newly created colormap ๐ For DirectColor and TrueColor, this will usually be the size of an individual pixel subfield ๐
10. REQUESTS โจ
CreateWindow โจ
CreateWindow
wid, parent: WINDOW
class: {InputOutput, InputOnly, CopyFromParent}
depth: CARD8
visual: VISUALID or CopyFromParent
x, y: INT16
width, height, border-width: CARD16
value-mask: BITMASK
value-list: LISTofVALUE
Errors: IDChoice, Window, Pixmap, Colormap, Cursor, Match,
Value, Alloc
okay so CreateWindow makes a brand new window ๐ช that starts out unmapped (aka not showing up yet ๐) and gives it the identifier wid. bestie, that's the whole vibe โจ
A class of CopyFromParent means the class gets copied from the parent ๐ฏ. A depth of zero for class InputOutput or CopyFromParent means the depth comes from the parent too. A visual of CopyFromParent means the visual type is taken from the parent ๐
. For class InputOutput, the visual type and depth have to be a combination the screen supports (or you get a Match error ๐). The depth doesn't need to match the parent's, but the parent cannot be of class InputOnly (otherwise, Match error). For class InputOnly, the depth must be zero (else Match error), and the visual must be one the screen supports (else Match error). The parent can have any depth and any class though ๐ฆ
The server basically acts like InputOnly windows don't exist for graphics requests, exposure processing, and VisibilityNotify events ๐ซฅ. An InputOnly window can't be used as a drawable (so it can't be a source or destination for graphics requests), but InputOnly and InputOutput windows act exactly the same in every other way: properties, grabs, input control, and so on ๐
The window gets placed on top in the stacking order with respect to its siblings ๐. The x and y coordinates are relative to the parent's origin, and they specify the position of the upper left outer corner of the window (not its origin) ๐. The width and height give the inside size, not counting the border, and they must be non-zero ๐ธ. The border-width for an InputOnly window must be zero (else Match error) โจ
The value-mask and value-list set the window attributes you want to explicitly initialize. The possible values are:
background-pixmap: PIXMAP or None or ParentRelative
background-pixel: CARD32
border-pixmap: PIXMAP or CopyFromParent
border-pixel: CARD32
bit-gravity: BITGRAVITY
win-gravity: WINGRAVITY
backing-store: {NotUseful, WhenMapped, Always}
backing-bit-planes: CARD32
backing-pixel: CARD32
save-under: BOOL
event-mask: SETofEVENT
do-not-propagate-mask: SETofDEVICEEVENT
override-redirect: BOOL
colormap: COLORMAP or CopyFromParent
cursor: CURSOR or None
And when attributes don't get explicitly initialized, these are the defaults ๐:
background-pixmap: None
border-pixmap: CopyFromParent
bit-gravity: Forget
win-gravity: NorthWest
backing-store: NotUseful
backing-bit-planes: all ones
backing-pixel: zero
save-under: False
event-mask: {} (empty set)
do-not-propagate-mask: {} (empty set)
override-redirect: False
colormap: CopyFromParent
cursor: None
Only these attributes are defined for InputOnly windows: win-gravity, event-mask, do-not-propagate-mask, and cursor ๐ง. Specifying any other attribute for an InputOnly window is a Match error, so be careful, bestie ๐
โโ๏ธ
If background-pixmap is given, it overrides the default background-pixel ๐ผ๏ธ. The background pixmap and the window must have the same root and the same depth (else Match error). Any pixmap size works, although some sizes are faster than others ๐๏ธ. If background is None, the window has no defined background ๐ซง. If background is ParentRelative, the parent's background gets used, but the window must have the same depth as the parent (else Match error). If the parent has a background of None, the window ends up with a background of None as well ๐คทโโ๏ธ. No copy of the parent's background gets made. Instead, the parent's background is reexamined every time the window background is needed, which is so iconic ๐ช. If background-pixel is given, it overrides the default AND any background-pixmap that was given, and a pixmap of undefined size filled with background-pixel is used for the background ๐. For a ParentRelative background, the background tile origin always lines up with the parent's background tile origin. Otherwise, the background tile origin is always the window origin ๐
When regions of the window get exposed and the server hasn't kept the contents, the server automatically tiles those regions with the window's background ๐งฑ. The exception is a background of None, where the previous screen contents just get left in place. Exposure events still get generated for those regions even when the background is None ๐
The border tile origin is always the same as the background tile origin ๐ฏ. If border-pixmap is given, it overrides the default border-pixel. The border pixmap and the window must have the same root and the same depth (else Match error). Any pixmap size works, though some sizes may be faster than others ๐. If CopyFromParent is given, the parent's border pixmap gets copied (later changes to the parent don't affect the child ๐
โโ๏ธ), but the window must have the same depth as the parent (else Match error). If border-pixel is given, it overrides the default AND any border-pixmap that was given, and a pixmap of undefined size filled with border-pixel gets used for the border ๐๏ธ
Output to a window is always clipped to the inside of the window, so the border is never affected ๐ก๏ธ
The bit-gravity decides which region of the window should be kept if the window gets resized, and win-gravity decides how the window gets repositioned if its parent gets resized. Check out ConfigureWindow for more on that ๐
A backing-store of WhenMapped tells the server that keeping the contents of obscured regions when the window is mapped would be beneficial ๐. A backing-store of Always tells the server that keeping contents even when the window is unmapped would be beneficial. Also, even if the window is bigger than its parent, the server should keep complete contents, not just the part inside the parent's boundaries ๐ง . If the server keeps contents, Exposure events won't be generated, but the server may stop keeping contents at any time ๐ญ. A value of NotUseful tells the server that keeping contents is unnecessary, though a server may still choose to keep them anyway ๐คทโโ๏ธ
backing-bit-planes says (with one bits) which bit planes of the window hold dynamic data that must be preserved in backing stores ๐จ. backing-pixel specifies what value to use in the planes that aren't covered by backing-bit-planes. The server is totally free to only save the specified bit planes in the backing store, and regenerate the remaining planes with the specified pixel value ๐ง
If save-under is True, the server is advised that saving the contents of the windows this one obscures would be beneficial when this window is mapped ๐ซง
The event-mask defines which events the client is interested in for this window (or, for some event types, its inferiors) ๐. The do-not-propagate-mask defines which events should NOT be propagated to ancestor windows when no client has that event type selected on this window ๐ซ
override-redirect specifies whether map and configure requests on this window should override a SubstructureRedirect on the parent. This is typically used to tell a window manager not to tamper with the window ๐ช๐
The colormap specifies the colormap that best reflects the "true" colors of the window ๐. Servers that can support hardware colormaps may use this info, and window managers may use it for InstallColormap requests. The colormap must have the same visual type as the window (else match error). If CopyFromParent is specified, the parent's colormap gets copied (later changes to the parent don't affect the child). But the window must have the same visual type as the parent (else Match error), AND the parent must not have a colormap of None (else Match error) ๐
If a cursor is specified, it gets used whenever the pointer is in the window ๐ฑ๏ธ. If None is specified, the parent's cursor gets used when the pointer is in the window, and any change in the parent's cursor causes an immediate change in the display cursor. Very dramatic, very cohesive ๐
This request generates a CreateNotify event ๐
The background and border pixmaps and the cursor can be freed immediately if you don't plan on making any more explicit references to them ๐งน. Finally, drawing into the background or border pixmap afterwards has an undefined effect on the window state. The server might or might not make a copy of the pixmap, so don't rely on it ๐ซ
ChangeWindowAttributes ๐
ChangeWindowAttributes
window: WINDOW
value-mask: BITMASK
value-list: LISTofVALUE
Errors: Window, Pixmap, Colormap, Cursor, Match, Value,
Access
okay bestie, this one lets you change a window's attributes ๐ The value-mask and value-list say which attributes get changed. The values and restrictions are the same as for CreateWindow, so no surprises there โจ
Changing the background does not change the window contents ๐ธ Setting the border, or changing the background so the border tile origin moves, makes the border get repainted ๐๏ธ Changing the background of a root window to None or ParentRelative brings back the default background pixmap. Changing the border of a root window to CopyFromParent brings back the default border pixmap. It's giving reset energy ๐ซถ
Changing the back-store of an obscured window to WhenMapped or Always, or changing the backing-bit-planes, backing-pixel, or save-under of a mapped window, might have no immediate effect ๐
Multiple clients can select input on the same window, and their event-masks have to be disjoint ๐ฆ When an event is generated, it gets reported to all the interested clients. BUT (lowkey chaotic) at most one client at a time can select for SubstructureRedirect, at most one client at a time can select for ResizeRedirectr, and at most one client at a time can select for ButtonPress ๐ญ
There is only one do-not-propagate-mask for a window, not one per client ๐ง
Changing the colormap of a window (so, defining a new map, not changing the contents of the existing map) generates a ColormapNorify event ๐ Changing the colormap of a visible window might have no immediate effect on the screen. See InstallColormap for the deets ๐
Changing the cursor of a root window to None restores the default cursor ๐
The order in which attributes get verified and altered is server dependent ๐ If an error is generated, a subset of the attributes may already have been altered. Not me being shocked ๐ญ
GetWindowAttributes ๐ฅ
GetWindowAttributes
window: WINDOW
=>
visual: VISUALID
class: {InputOutput, InputOnly}
bit-gravity: BITGRAVITY
win-gravity: WINGRAVITY
backing-store: {NotUseful, WhenMapped, Always}
backing-bit-planes: CARD32
backing-pixel: CARD32
save-under: BOOL
colormap: COLORMAP or None
map-is-installed: BOOL
map-state: {Unmapped, Unviewable, Viewable}
all-event-masks, your-event-mask: SETofEVENT
do-not-propagate-mask: SETofDEVICEEVENT
override-redirect: BOOL
Errors: Window
this one just asks for the current attributes of a window, and it's so simple it's iconic ๐ All-event-masks is the inclusive-OR of every event mask that clients have selected on the window. Your-event-mask is the event mask selected by the client doing the querying ๐
DestroyWindow ๐
DestroyWindow
window: WINDOW
Errors: Window
okay this is the dramatic exit ๐ If the window is mapped, an UnmapWindow request gets performed automatically first. Then the window and all its inferiors get destroyed, and a DestroyNotify event is generated for each window. They go in order from the argument window downwards, and the order among siblings at each level is unspecified ๐ฆ Normal exposure processing on formerly obscured windows also happens ๐ธ
If the window is a root window, this request has no effect. Root windows are not getting destroyed today ๐ซถ
DestroySubwindows ๐ง
DestroySubwindows
window: WINDOW
Errors: Window
this one performs a DestroyWindow on all the children of the window, going in bottom to top stacking order ๐ฅ Main character clean-up energy โจ
ChangeSaveSet ๐
ChangeSaveSet
window: WINDOW
mode: {Insert, Delete}
Errors: Window, Match, Value
this adds or removes the specified window from the client's "save-set" ๐ The window must have been created by some other client, or you get a Match error ๐ The use of the save-set is described in Section 11, so go check that out ๐
The server removes windows from the save-set automatically when they're destroyed ๐
ReparentWindow ๐ฆ
ReparentWindow
window, parent: WINDOW
x, y: INT16
Errors: Window, Match
okay so if the window is mapped, an UnmapWindow request happens automatically first ๐ซถ Then the window gets removed from its current spot in the hierarchy and inserted as a child of the specified parent ๐ The x and y coordinates are relative to the parent's origin, and they say the new position of the upper left outer corner of the window โจ The window gets placed on top in the stacking order with respect to its siblings.
A ReparentNotify event is generated next ๐ฅ The override-redirect attribute of the window is passed along in this event. A value of True means a window manager should not tamper with this window ๐ Finally, if the window was originally mapped, a MapWindow request happens automatically ๐
Normal exposure processing on formerly obscured windows also happens ๐ธ The server might not generate exposure events for regions from the initial unmap that are immediately obscured by the final map ๐
A Match error is generated if the new parent is not on the same screen as the old parent ๐ It's also a Match error if the new parent is the window itself or one of its inferiors, or if the window has a ParentRelative background and the new parent is not the same depth as the window ๐ญ
MapWindow ๐
MapWindow
window: WINDOW
Errors: Window
if the window is already mapped, this request has no effect ๐ง
If the override-redirect attribute of the window is False and some other client has selected SubstructureRedirect on the parent, then a MapRequest event is generated, but the window stays unmapped ๐ Otherwise, the window gets mapped and a MapNotify event is generated ๐ฆ
If the window is now viewable and its contents had been discarded, then the window is tiled with its background ๐ธ If no background is defined, the existing screen contents are not altered. One or more exposure events are generated after that ๐๏ธ If a backing-store was maintained while the window was unmapped, no exposure events are generated. If a backing-store will now be maintained, a full-window exposure is always generated. Otherwise, only visible regions may be reported ๐ Similar tiling and exposure happen for any newly viewable inferiors too โจ
MapSubwindows ๐
MapSubwindows
window: WINDOW
Errors: Window
this performs a MapWindow request on all unmapped children of the window, in top to bottom stacking order ๐
UnmapWindow ๐
UnmapWindow
window: WINDOW
Errors: Window
if the window is already unmapped, this request has no effect ๐ Otherwise, the window gets unmapped and an UnmapNotify event is generated ๐ฅ Normal exposure processing on formerly obscured windows is performed too ๐
UnmapSubwindows ๐ซถ
UnmapSubwindows
window: WINDOW
Errors: Window
this performs an UnmapWindow request on all mapped children of the window, in bottom to top stacking order ๐ฅ Okay that's the whole vibe for this part, and more is coming, so stay tuned bestie ๐๐ง
ConfigureWindow
ConfigureWindow
window: WINDOW
value-mask: BITMASK
value-list: LISTofVALUE
Errors: Window, Match, Value
okay bestie, ConfigureWindow is the request that changes a window's whole configuration ๐ โจ The value-mask and value-list say which values you're setting. The possible values are:
x: INT16
y: INT16
width: CARD16
height: CARD16
border-width: CARD16
sibling: WINDOW
stack-mode: {Above, Below, TopIf, BottomIf, Opposite}
x and y are relative to the parent's origin, and they place the upper left outer corner of the window ๐ฆ width and height are the inside size, so the border doesn't count, and they must be non-zero ๐ Trying to make the border-width of an InputOnly window non-zero is a Match error, so don't do that ๐ญ
If the override-redirect attribute is False and some other client has selected SubstructureRedirect on the parent, then a ConfigureRequest event gets generated and nothing else happens ๐ซถ Otherwise, here's the rest of the tea:
If some other client has selected ResizeRedirect on the window and the width or height is changing, then a ResizeRequest event gets generated, and the current width and height get used in everything that follows ๐ธ
The geometry changes as you asked, the window gets restacked among its siblings (more on that below), and a ConfigureNotify event is generated. If the width or height has actually changed, the children get affected too, as described below. Exposure processing also happens on formerly obscured windows ๐
Changing the width or height makes the contents move or disappear, depending on the window's bit-gravity, and makes children get reconfigured, depending on their win-gravity. For a change of width and height of W and H, the [x, y] pairs are:
NorthWest: [0, 0]
North: [W/2, 0]
NorthEast: [W, 0]
West: [0, H/2]
Center: [W/2, H/2]
East: [W, H/2]
SouthWest: [0, H]
South: [W/2, H]
SouthEast: [W, H]
When a window with one of these bit-gravities gets resized, the matching pair tells you how far each pixel in the window moves ๐ง When a window with one of these win-gravities sees its parent resized, the matching pair tells you how far the window moves inside the parent. Whenever a window gets repositioned like this, a GravityNotify event is generated ๐
A gravity of Static means the contents or origin shouldn't move relative to the origin of the root window. If the size change comes with a change in position of [X, Y], then for bit-gravity each pixel moves by [-X, -Y], and for win-gravity a child moves by [-X, -Y] when its parent gets resized. Static gravity only kicks in when the width or height changes, not when the window is simply moved ๐
A bit-gravity of Forget means the window contents are always thrown away after a size change. The window gets tiled with its background (if no background is defined, the existing screen contents stay as they are), and one or more exposure events are generated ๐ซ A server may also ignore the bit-gravity you specified and use Forget instead.
A win-gravity of Unmap works like NorthWest, except the child also gets unmapped when the parent is resized, and an UnmapNotify event is generated ๐
If a sibling and a stack-mode are both specified, the window gets restacked like this:
Above: window is placed just above sibling
Below: window is placed just below sibling
TopIf: if sibling occludes window, then window is placed
at the top of the stack
BottomIf: if window occludes sibling, then window is
placed at the bottom of the stack
Opposite: if sibling occludes window, then window is
placed at the top of the stack, else if window
occludes sibling, then window is placed at the
bottom of the stack
If a stack-mode is specified but no sibling is, the window gets restacked like this:
Above: window is placed at the top of the stack
Below: window is placed at the bottom of the stack
TopIf: if any sibling occludes window, then window is
placed at the top of the stack
BottomIf: if window occludes any sibling, then window is
placed at the bottom of the stack
Opposite: if any sibling occludes window, then window is
placed at the top of the stack, else if window
occludes any sibling, then window is placed at
the bottom of the stack
It's a Match error if a sibling is specified without a stack-mode, or if the window isn't actually a sibling. Not it ๐
Note that the computations for BottomIf, TopIf, and Opposite use the window's final geometry (the one set by the other arguments to the request), not its initial geometry ๐
CirculateWindow
CirculateWindow
window: WINDOW
direction: {RaiseLowest, LowerHighest}
Errors: Window, Value
If some other client has selected SubstructureRedirect on the window, then a CirculateRequest event is generated and nothing else happens. Otherwise, the steps below run, and then a CirculateNotify event is generated if the window is actually restacked ๐ฆ
For RaiseLowest, the lowest mapped child (if there is one) that's occluded by another child goes to the top of the stack โญ For LowerHighest, the highest mapped child (if there is one) that occludes another child goes to the bottom of the stack. Exposure processing happens on formerly obscured windows too ๐ธ
GetGeometry
GetGeometry
drawable: DRAWABLE
=>
root: WINDOW
depth: CARD8
x, y: INT16
width, height, border-width: CARD16
Errors: Drawable
This one returns the root and the (current) geometry of the drawable ๐ง depth is the number of bits per pixel for the object. x, y, and border-width will always be zero for pixmaps. For a window, x and y give the upper left outer corner of the window relative to its parent's origin, and width and height give the inside size (the border isn't included) ๐
It is legal to pass an InputOnly window as a drawable to this request ๐ซถ
QueryTree
QueryTree
window: WINDOW
=>
root: WINDOW
parent: WINDOW or None
children: LISTofWINDOW
Errors: Window
QueryTree gives you the root, the parent, and the children of the window ๐ณ The children come back listed in bottom-to-top stacking order ๐ฅ
InternAtom
InternAtom
name: STRING8
only-if-exists: BOOL
=>
atom: ATOM or None
Errors: Value, Alloc
InternAtom returns the atom for the given name ๐ If only-if-exists is False, the atom gets created if it doesn't already exist. The string should use ASCII encoding, and upper and lower case both matter, so be careful, bestie ๐
The lifetime of an atom isn't tied to the client that interned it. Atoms stay defined until server reset (see Section 11) ๐
GetAtomName
GetAtomName
atom: ATOM
=>
name: STRING8
Errors: Atom
GetAtomName hands back the name for the given atom โจ Easy, iconic ๐
ChangeProperty
ChangeProperty
window: WINDOW
property, type: ATOM
format: {8, 16, 32}
mode: {Replace, Prepend, Append}
data: LISTofINT8 or LISTofINT16 or LISTofINT32
Errors: Window, Atom, Value, Match, Alloc
ChangeProperty alters the property on the specified window ๐ The type is uninterpreted by the server, so it's just a label to the server. The format says whether the data should be read as a list of 8-bit, 16-bit, or 32-bit quantities, so the server can byte-swap correctly when it needs to ๐ฅ
If mode is Replace, the previous property value gets thrown out ๐ If mode is Prepend or Append, the type and format have to match the existing property value (otherwise it's a Match error). If the property is undefined, it's treated as defined with the correct type and format and zero-length data. Prepend tacks the data onto the beginning of the existing data, and Append tacks it onto the end ๐
This generates a PropertyNotify event on the window ๐ฆ
The lifetime of a property isn't tied to the client that stored it. Properties stay until they're explicitly deleted, or the window is destroyed, or the server resets (see Section 11) ๐
The maximum size of a property depends on the server ๐ง
DeleteProperty
DeleteProperty
window: WINDOW
property: ATOM
Errors: Window, Atom
DeleteProperty deletes the property from the specified window, as long as the property exists ๐๏ธ It generates a PropertyNotify event on the window, unless the property didn't exist to begin with ๐
GetProperty
GetProperty
window: WINDOW
property: ATOM
type: ATOM or AnyPropertyType
long-offset, long-length: CARD32
delete: BOOL
=>
type: ATOM
format: {8, 16, 32}
bytes-after: CARD32
value: LISTofINT8 or LISTofINT16 or LISTofINT32
Errors: Window, Atom, Property, Match, Value
If the property you asked for doesn't exist on the specified window, you get a Property error ๐ญ Otherwise, if type AnyPropertyType is specified, (part of) the property comes back no matter what its type is. If you specify a type, (part of) the property only comes back when its type matches, and otherwise you get a Match error ๐ซถ The actual type and format of the property are returned too โจ
Here's the math for the offsets. Define the following values:
N = actual length of the stored property in bytes
(even if the format is 16 or 32)
I = 4 * long-offset
T = N - I
L = MINIMUM(T, 4 * long-length)
A = N - (I + L)
The returned value starts at byte index I in the property (indexing from 0), and its length in bytes is L. If long-offset is given so that L is negative, that's a Value error ๐ The value of bytes-after is A, which tells you how many trailing unread bytes are left in the stored property ๐ง
If delete is True and bytes-after is zero, the property is also deleted from the window, and a PropertyNotify event is generated on the window ๐๐ฅ
RotateProperties โจ
RotateProperties
window: WINDOW
delta: INT8
properties: LISTofATOM
Errors: Window, Atom, Match
okay bestie, picture the property names as a little circle ๐ numbered from zero. If there are N names, the value for name I moves over to name (I + delta) mod N, for every I from zero to N - 1. Positive delta spins everything to the right, negative delta spins it to the left, so it's a full ring of values just sliding around ๐ฆ
The server sends a PropertyNotify event for each property, in the order they were listed ๐
But here's the tea โ If an atom shows up more than once in the list, or a property with that name isn't defined on the window, you get a Match error. And if you get an Atom or Match error, NOTHING changes. Zero properties get touched. Totally all-or-nothing energy ๐
ListProperties
ListProperties
window: WINDOW
=>
atoms: LISTofATOM
Errors: Window
this one is so chill ๐ธ You give it a window and it hands back the atoms for every property currently defined on that window. That's literally it. Errors: Window if the window is bad.
SetSelectionOwner
SetSelectionOwner
selection: ATOM
owner: WINDOW or None
time: TIMESTAMP or CurrentTime
Error: Atom, Window
this is where the selection drama happens ๐ It changes the owner AND the last-change time of the specified selection. Now the timing is the vibe check โฐ If the time you send is earlier than the selection's current last-change time, or later than the current server time, the request just does nothing. Otherwise the last-change time gets set to your time, and if you sent CurrentTime, the server swaps in its own current time instead ๐ง
Also, if the new owner is different from the current owner AND the current owner is a window, that old owner gets a SelectClear event. Rude to get kicked out like that, but that's the rule ๐
Some more fun facts ๐ If the owner is a window and that window gets destroyed later, the owner quietly flips back to None. The last-change time doesn't change though. The selection atom is totally uninterpreted by the server (it doesn't care what it means), and selections are global to the whole server ๐
GetSelectionOwner
GetSelectionOwner
selection: ATOM
=>
owner: WINDOW or None
Errors: Atom
tell me who's holding the selection ๐ It returns the current owner, if there is one. Errors: Atom for a bad atom, obvi.
ConvertSelection
ConvertSelection
selection, target: ATOM
property: ATOM or None
requestor: WINDOW
time: TIMESTAMP or CurrentTime
Error: Atom, Window
okay so if the selection has a window as its owner, the server sends a SelectionRequest event to that owner ๐จ If there's no owner at all, the server sends a SelectionNotify event back to the requestor with property set to None ๐ Either way, the arguments get passed along exactly as they came in. No edits, no glow-ups, just vibes unchanged โจ
SendEvent
SendEvent
destination: WINDOW or PointerWindow or InputFocus
propagate: BOOL
event-mask: SETofEVENT
event: <normal-event-format>
Errors: Window, Value
this one is giving main character energy ๐ฌ First, the destination can be fancy. If you say PointerWindow, the destination gets swapped for whichever window the pointer is sitting in. If you say InputFocus, it's a little more complicated: if the focus window contains the pointer, the destination becomes the window the pointer is in. If not, the destination becomes the focus window ๐ฆ
Now, if propagate is False, the event goes to every client that's selecting on the destination for any of the event types in event-mask. Simple ๐ธ
If propagate is True and nobody has selected on the destination for any of those event types, the destination moves up to the closest ancestor that some client has selected a type in event-mask on, as long as no window in between has that type in its do-not-propagate-mask. If there's no such window, or if the window is an ancestor of the focus window and InputFocus was originally specified, the event isn't sent to any clients at all ๐ญ Otherwise, the event gets reported to every client selecting on the final destination for any of the types in event-mask ๐
A couple more things ๐ The event code has to be one of the core events, or one of the events defined by an extension, so the server can byte swap the contents properly when it needs to. Other than that, the server leaves the event contents alone and doesn't check them, except that it forces on the most significant bit of the event code. And active grabs get ignored for this request, so no drama there ๐ โโ๏ธ
GrabPointer
GrabPointer
grab-window: WINDOW
owner-events: BOOL
event-mask: SETofPOINTEREVENT
pointer-mode, keyboard-mode: {Synchronous, Asynchronous}
confine-to: WINDOW or None
cursor: CURSOR or None
time: TIMESTAMP or CurrentTime
=>
status: {Success, AlreadyGrabbed, Frozen, InvalidTime,
NotViewable}
Errors: Cursor, Window, Value
okay this is the big one, bestie ๐ฅ GrabPointer actively grabs control of the pointer. After that, only the grabbing client gets the pointer events. A new grab from this same client overrides any active pointer grab it already has ๐
The event-mask always gets ButtonPress and ButtonRelease added in, no matter what you send ๐ If owner-events is False, every pointer event gets reported relative to grab-window, and only the ones selected by event-mask come through. If owner-events is True, then any pointer event that would normally go to this client gets reported normally ๐ For anything else, it's reported relative to grab-window, and again only if selected by event-mask. Either way, events that don't get reported are just discarded ๐๏ธ
Now the modes ๐ pointer-mode controls what happens next with pointer events, and keyboard-mode controls what happens next with keyboard events. If the mode is Asynchronous, events keep processing normally. If the device is frozen by this client, processing for that device starts back up again. If the mode is Synchronous, the device looks frozen to the protocol, and the server doesn't generate any more events for it until the grabbing client sends a releasing AllowEvents request. The real device changes don't disappear while it's frozen, though. They just get queued up for later ๐ง
For the cursor, if you specify one, it's shown no matter which window the pointer is in โจ If you don't, then when the pointer is in grab-window or one of its subwindows, the normal cursor for that window shows up, and otherwise the cursor for grab-window is displayed instead ๐ฆ
If you specify a confine-to window, the pointer gets restricted so it stays inside that window ๐ The confine-to window doesn't have to be related to grab-window at all. If the pointer isn't inside the confine-to window to begin with, it gets warped automatically to the closest edge (with enter and leave events generated as usual) right before the grab turns on. And if the confine-to window gets reconfigured later, the pointer is warped automatically whenever it's needed to keep it inside ๐ซถ
This request generates EnterNotify and LeaveNotify events ๐
The request fails with status AlreadyGrabbed if another client already has the pointer actively grabbed ๐ โโ๏ธ It fails with status Frozen if another client's active grab has the pointer frozen. It fails with status NotViewable if grab-window or the confine-to window isn't viewable. And it fails with status InvalidTime if the specified time is earlier than the last-pointer-grab time, or later than the current server time. Otherwise, the last-pointer-grab time gets set to your specified time, with CurrentTime swapped out for the current server time ๐ฐ๏ธ
UngrabPointer
UngrabPointer
time: TIMESTAMP or CurrentTime
this is the "let go" request ๐ซ It releases the pointer if this client has it actively grabbed, whether that came from GrabPointer, GrabButton, or just a normal button press. It also releases any queued events. And it has no effect if the specified time is earlier than the last-pointer-grab time, or later than the current server time.
This request generates EnterNotify and LeaveNotify events too ๐
Also, an UngrabPointer happens automatically if the event window or confine-to window for an active pointer grab becomes not viewable. So it lets go on its own when that happens ๐ธ
GrabButton
GrabButton
modifiers: SETofKEYMASK or AnyModifier
button: BUTTON or AnyButton
grab-window: WINDOW
owner-events: BOOL
event-mask: SETofPOINTEREVENT
pointer-mode, keyboard-mode: {Synchronous, Asynchronous}
confine-to: WINDOW or None
cursor: CURSOR or None
Errors: Cursor, Window, Value, Access
GrabButton sets up a passive grab, which is basically a standby trap ๐ชค Later on, if the specified button gets pressed while the specified modifier keys are down (and no other buttons or modifier keys are down), and grab-window contains the pointer, and the confine-to window (if there is one) is viewable, and none of these constraints are satisfied for any ancestor, then the pointer gets actively grabbed just like in GrabPointer ๐ฏ The last-pointer-grab time gets set to the time the button was pressed (as sent in the ButtonPress event), and the ButtonPress event gets reported. The rest of the arguments work the same as they do for GrabPointer. The active grab ends automatically once all the buttons are released, no matter what the modifier keys are doing ๐
A modifiers of AnyModifier is the same as issuing the request for every possible modifier combination โจ A button of AnyButton is the same as issuing it for every possible button ๐ฆ
Heads up ๐จ An Access error happens if some other client already issued a GrabButton with the same button/key combination on the same window. When you use AnyModifier or AnyButton, the request fails completely (so no grabs get established at all) if there's a conflict. And this request has no effect on an active grab.
UngrabButton
UngrabButton
modifiers: SETofKEYMASK or AnyModifier
button: BUTTON or AnyButton
grab-window: WINDOW
Errors: Window
this one undoes the standby trap ๐งน It releases the passive button/key combination on the specified window, but only if this client was the one who grabbed it. AnyModifier means every possible modifier combination, and AnyButton means every possible button, same as before. It has no effect on an active grab ๐
ChangeActivePointerGrab ๐
ChangeActivePointerGrab
event-mask: SETofPOINTEREVENT
cursor: CURSOR or None
time: TIMESTAMP or CurrentTime
Errors: Cursor
okay so this one is bestie's glow-up request ๐ If you already have the pointer actively grabbed, you can switch up its dynamic settings, but only if the time you send is no earlier than the last-pointer-grab time and no later than the current server time โฐ event-mask and cursor work exactly like they do in GrabPointer ๐ฆ And the event-mask always gets ButtonPress and ButtonRelease added in automatically, no cap ๐ซถ It has zero effect on the passive settings of a GrabButton, so don't expect it to touch those ๐
GrabKeyboard โจ๏ธ
GrabKeyboard
grab-window: WINDOW
owner-events: BOOL
pointer-mode, keyboard-mode: {Synchronous, Asynchronous}
time: TIMESTAMP or CurrentTime
=>
status: {Success, AlreadyGrabbed, Frozen, InvalidTime,
NotViewable}
Errors: Window, Value
okay this is the main character move ๐ GrabKeyboard actively grabs the keyboard, so from now on, further key events only go to the grabbing client ๐ It overrides any active keyboard grab that this same client already has, so you're basically taking the spotlight back ๐
owner-events decides where the key events land ๐ธ If it's False, every key event that gets generated is reported with respect to grab-window. If it's True, then any key event that would normally go to this client gets reported normally, and anything else gets reported with respect to the grab-window. Also, KeyPress and KeyRelease events are always reported, no matter what event selection the client made, which is so iconic ๐ญ
pointer-mode and keyboard-mode control what happens to further processing of pointer events and keyboard events, respectively ๐ง In Asynchronous mode, event processing just keeps going as normal, and if the device is frozen by this client, its event processing gets resumed. In Synchronous mode, the device looks frozen from the protocol's point of view, and the server won't generate any more events for that device until the grabbing client sends a releasing AllowEvents request ๐ง The actual device changes don't get lost while it's frozen, they're just queued up for later ๐
This request also generates FocusIn and FocusOut events โจ
Now for the failure cases, which are kind of a lot ๐ The request fails with status AlreadyGrabbed if some other client has the keyboard actively grabbed. It fails with status Frozen if another client's active grab has the keyboard frozen. It fails with status NotViewable if grab-window isn't viewable. And it fails with status InvalidTime if the time you sent is earlier than the last-keyboard-grab time or later than the current server time. If none of those happen, the last-keyboard-grab time gets set to your specified time, except CurrentTime gets swapped out for the current server time ๐ฐ๏ธ
UngrabKeyboard ๐
UngrabKeyboard
time: TIMESTAMP or CurrentTime
bye bye keyboard grab ๐ UngrabKeyboard releases the keyboard if this client has it actively grabbed, whether that came from GrabKeyboard or from GrabKey, and it also releases any queued events ๐ซง It does nothing if the specified time is earlier than the last-keyboard-grab time or later than the current server time ๐
This request also generates FocusIn and FocusOut events ๐ฆ
Also, if the event window for an active keyboard grab stops being viewable, an UngrabKeyboard happens automatically, so the server cleans up after you ๐งน
GrabKey ๐๏ธ
GrabKey
key: KEYCODE or AnyNonModifier
modifiers: SETofKEYMASK or AnyModifier
grab-window: WINDOW
owner-events: BOOL
pointer-mode, keyboard-mode: {Synchronous, Asynchronous}
Errors: Window, Value, Access
ooh this one is a passive grab on the keyboard, so it's like setting a trap that waits for the moment ๐ชค Later on, if the specified key gets pressed (and it can even be a modifier key) while the specified modifier keys are down and no other modifier keys are down, and the KeyPress event would be generated in grab-window or one of its inferiors, and none of those constraints are satisfied by any ancestor, then the keyboard gets actively grabbed the way GrabKeyboard describes ๐ The last-keyboard-grab time gets set to the time the key was pressed, which is the same time shown in the KeyPress event, and then the KeyPress event is reported โจ The rest of the arguments work just like they do for GrabKeyboard ๐ The active grab ends automatically once the specified key gets released, no matter what the modifier keys are doing ๐ซถ
If you use AnyModifier for modifiers, it's the same as making this request for every possible modifier combination ๐ง If you use AnyNonModifier for key, it's the same as making the request for every possible non-modifier key code ๐
An Access error happens if some other client already issued a GrabKey with the same key combination on the same window ๐ โโ๏ธ And if you're using AnyModifier or AnyNonModifier, the request fails completely, meaning no grabs get established at all, if there's a conflicting grab for any combination ๐ญ
UngrabKey ๐
UngrabKey
key: KEYCODE or AnyNonModifier
modifiers: SETofKEYMASK or AnyModifier
grab-window: WINDOW
Errors: Window
UngrabKey lets go of the key combination on the specified window, but only if this client grabbed it ๐ธ Same deal as before: AnyModifier means every possible modifier combination, and AnyNonModifier means every possible non-modifier key code ๐ง It has no effect on an active grab, so an already-running grab stays put ๐
AllowEvents ๐
AllowEvents
mode: {AsyncPointer, SyncPointer, ReplayPointer,
AsyncKeyboard, SyncKeyboard, ReplayKeyboard}
time: TIMESTAMP or CurrentTime
Errors: Value
this is the release valve for when a client has frozen a device ๐ AllowEvents lets go of some queued events. It does nothing if the specified time is earlier than the last-grab time of the client's most recent active grab, or if the specified time is later than the current server time โฐ
Let's go through each mode, because they're all a little different ๐ฆ
AsyncPointer: if the pointer is frozen by the client, pointer event processing just continues normally ๐โโ๏ธ If the pointer got frozen twice by the client, on behalf of two separate grabs, AsyncPointer "thaws" it for both ๐ It has no effect if the pointer isn't frozen by the client, but the pointer doesn't need to be grabbed by the client for this to work ๐ซง
SyncPointer: if the pointer is frozen and actively grabbed by the client, pointer event processing continues normally until the next ButtonPress or ButtonRelease event gets reported to the client, and then the pointer appears to freeze again ๐ง But if that reported event makes the pointer grab get released, then the pointer doesn't freeze at all ๐ SyncPointer has no effect if the pointer isn't frozen by the client, or if the pointer isn't grabbed by the client ๐
ReplayPointer: if the pointer is actively grabbed by the client, and it's frozen because an event got sent to the client (either from a GrabButton activating, or from a previous AllowEvents with mode SyncPointer, but not from a GrabPointer), then the pointer grab gets released and that event gets completely reprocessed ๐ This time, though, passive grabs at or above the grab-window of the grab that was just released get ignored, which is wild ๐คฏ The request does nothing if the pointer isn't grabbed by the client, or if the pointer isn't frozen as the result of an event ๐
AsyncKeyboard: if the keyboard is frozen by the client, keyboard event processing continues normally ๐น If the pointer is frozen twice by the client on behalf of two separate grabs, AsyncPointer "thaws" for both ๐ AsyncKeyboard has no effect if the keyboard isn't frozen by the client, but the keyboard doesn't need to be grabbed by the client either ๐ซถ
SyncKeyboard: if the keyboard is frozen and actively grabbed by the client, keyboard event processing continues normally until the next KeyPress or KeyRelease event gets reported to the client, and then the keyboard appears to freeze again ๐ง But if that reported event makes the keyboard grab get released, the keyboard doesn't freeze ๐ SyncKeyboard has no effect if the keyboard isn't frozen by the client, or if the keyboard isn't grabbed by the client ๐
ReplayKeyboard: if the keyboard is actively grabbed by the client and is frozen because an event got sent to the client (either from a GrabKey activating, or from a previous AllowEvents with mode SyncKeyboard, but not from a GrabKeyboard), then the keyboard grab gets released and that event gets completely reprocessed ๐ Passive grabs at or above the grab-window of the grab that was just released get ignored this time too ๐คฏ The request does nothing if the keyboard isn't grabbed by the client, or if the keyboard isn't frozen as the result of an event ๐
AsyncPointer, SyncPointer, and ReplayPointer have no effect on keyboard event processing ๐ And AsyncKeyboard, SyncKeyboard, and ReplayKeyboard have no effect on pointer event processing ๐
It's totally possible for a pointer grab and a keyboard grab to be active at the same time, whether by the same client or by different ones ๐ If a device is frozen on behalf of either grab, no event processing happens for that device at all ๐ง A single device can also be frozen because of both grabs at once, and in that case the freeze has to be released on behalf of both grabs before events can get processed again ๐ซ
GrabServer ๐
GrabServer
GrabServer disables request processing and close-downs on all the other connections, meaning every connection except the one this request came in on ๐ โโ๏ธ Think of it as putting up a velvet rope for everyone else ๐
UngrabServer ๐
UngrabServer
UngrabServer restarts request processing and close-downs on the other connections ๐ฅณ The velvet rope is gone, everybody's back in ๐ฆ
QueryPointer ๐
QueryPointer
window: WINDOW
=>
root: WINDOW
child: WINDOW or None
same-screen: BOOL
root-x, root-y, win-x, win-y: INT16
mask: SETofKEYBUTMASK
Errors: Window
QueryPointer is basically asking "where's the pointer rn?" ๐ It returns the root window the pointer is currently on, plus the pointer coordinates relative to the root's origin โจ
If same-screen is False, the pointer isn't on the same screen as the window you passed in, so child comes back as None and win-x and win-y are both zero ๐ If same-screen is True, then win-x and win-y are the pointer coordinates relative to that window's origin, and child is the child window that contains the pointer, if there is one ๐ง The current state of the modifier keys and the buttons also comes back in the reply, so you get the full vibe check ๐ ๐
Okay bestie, the motion history is up first ๐ฆ GetMotionEvents pulls every recorded pointer event between two times, but only the ones that land inside a window (borders count!) ๐
GetMotionEvents
start, stop: TIMESTAMP or CurrentTime
window: WINDOW
=>
events: LISTofTIMECOORD
where
TIMECOORD: {x, y: CARD16
time: TIMESTAMP}
Error: Window
So it hands back every event in the motion history buffer between start and stop, both inclusive, whose coordinates fall within the window at its current placement ๐ The x and y values come back relative to the window's origin, so no mental math needed ๐ง
TranslateCoordinates is the coordinate converter we all needed ๐
TranslateCoordinates
src-window, dst-window: WINDOW
src-x, src-y: INT16
=>
same-screen: BOOL
child: WINDOW or None
dst-x, dst-y: INT16
Errors: Window
You give it src-x and src-y, measured from src-window's origin, and it gives back dst-x and dst-y, measured from dst-window's origin ๐ธ If same-screen comes back False, the two windows live on different screens and dst-x and dst-y are zero, so don't go looking for numbers that aren't there ๐ And if the point sits inside a mapped child of dst-window, that child gets returned in child โจ
WarpPointer is giving teleport ๐งโจ
WarpPointer
src-window: WINDOW or None
dst-window: WINDOW
src-x, src-y: INT16
src-width, src-height: CARD16
dst-x, dst-y: INT16
Errors: Window
It moves the pointer to [dst-x, dst-y], relative to dst-window's origin ๐ฆ If src-window is None, the jump doesn't care where the pointer already is ๐ But if you name a window, the warp only happens when the pointer is currently inside a visible part of the rectangle you described on src-window ๐
The src-x and src-y values are relative to src-window's origin ๐ Two fun fallbacks here: if src-height is zero, it gets swapped for the current height of src-window minus src-y, and if src-width is zero, it gets swapped for the current width of src-window minus src-x ๐
Bestie, this one has a boundary. The request can't push the pointer outside theconfine-towindow of an active pointer grab. If you try, the pointer only goes as far as the closest edge of thatconfine-towindow ๐ง
SetInputFocus decides who gets the keyboard, and it's lowkey dramatic ๐
SetInputFocus
focus: WINDOW or PointerRoot or None
revert-to: {Parent, PointerRoot, None}
time: TIMESTAMP or CurrentTime
Errors: Window, Value
It changes the input focus and the last-focus-change time ๐ Timing matters here. If the time you pass is earlier than the current last-focus-change time, or later than the current server time, the request does absolutely nothing ๐ญ Otherwise the last-focus-change time becomes your time, and if you passed CurrentTime, it becomes the current server time instead โญ
If focus is None, every keyboard event gets tossed until a new focus window shows up ๐ In that case revert-to is ignored, so don't stress about it ๐
If focus is a window, that window becomes the keyboard's focus window ๐ A keyboard event that would normally go to this window or one of its inferiors gets reported normally. Anything else gets reported with respect to the focus window ๐ซถ
If focus is PointerRoot, the focus window is decided fresh at every keyboard event, as the root window of whatever screen the pointer is on at that moment ๐ revert-to is ignored here too โจ
This request generates FocusIn and FocusOut events ๐ฆ
What happens when the focus window stops being viewable? It depends on revert-to. If it's Parent, the focus goes to the parent (or the closest viewable ancestor), and the new revert-to value is taken to be None ๐ง If it's PointerRoot or None, the focus goes to that value instead ๐ Whenever focus reverts, FocusIn and FocusOut events get generated, but the last-focus-change time doesn't move ๐
GetInputFocus is the "wait, who has focus right now?" request ๐ซถ
GetInputFocus
=>
focus: WINDOW or PointerRoot or None
revert-to: {Parent, PointerRoot, None}
It returns the current focus state ๐ธ
QueryKeymap shows which keys are pressed right this second, in a bit vector ๐
QueryKeymap
=>
keys: LISTofCARD8
Each one bit says whether its key is currently pressed โจ The vector is 32 bytes long. Byte N (counting from 0) holds the bits for keys 8N through 8N+7, and the least significant bit in that byte stands for key 8N ๐ Once you get the byte math, it's honestly so satisfying ๐ฅ
OpenFont loads a font and gives it an id ๐
OpenFont
fid: FONT
name: STRING8
Errors: IDChoice, Name, Alloc
It loads the named font if it isn't already loaded, then links fid to it ๐ Once it's open, the font can be used as a source for any drawable ๐ฆ For the font name, you should use ASCII encoding, and upper and lower case don't matter, so FIXED and fixed are the same vibe ๐
CloseFont breaks the link between the resource id and the font ๐ซถ
CloseFont
font: FONT
Errors: Font
It deletes the association between the resource id and the font ๐ The font itself isn't freed right away. It gets freed once no other resource is still referencing it ๐ง Clean breakup, no drama ๐โจ
QueryFont โจ
Okay bestie, this is where we ask the server about a font ๐
You send it a FONT or GCONTEXT, and it sends back the font's logical info plus a list of per-character info. Here's the request and reply, exactly as the spec lays them out:
QueryFont
font: FONT or GCONTEXT
=>
font-info: FONTINFO
char-infos: LISTofCHARINFO
where
FONTINFO: [draw-direction: {LeftToRight, RightToLeft}
min-char-or-byte2,max-char-or-byte2:CARD16
min-byte1, max-byte1: CARD8
all-chars-exist: BOOL
default-char: CARD16
min-bounds: CHARINFO
max-bounds: CHARINFO
font-ascent: INT16
font-descent: INT16
properties: LISTofFONTPROP]
FONTPROP: [name: ATOM
value: INT32 or CARD32]
CHARINFO: [left-side-bearing: INT16
right-side-bearing: INT16
character-width: INT16
ascent: INT16
descent: INT16
attributes: CARD16]
Errors: Font
Returns logical information about a font.
The draw-direction is basically just a hint ๐ง. It tells you whether most char-infos have a positive (LeftToRight) or a negative (RightToLeft) character-width metric. The core protocol has no support for vertical text, so don't go looking for it ๐
Character indexing ๐ข
Indexing has two modes, and the rules are a little spicy. If min-byte1 and max-byte1 are both zero, then min-char-or-byte2 is the linear character index of the first element of char-infos, and max-char-or-byte2 is the linear index of the last element.
If either min-byte1 or max-byte1 is non-zero, then both min-char-or-byte2 and max-char-or-byte2 are less than 256. In that case the two-byte character index for char-infos element N (counting from 0) is worked out like this:
byte1 = N/D + min-byte1
byte2 = N\D + min-char-or-byte2
where
D = max-char-or-byte2 - min-char-or-byte2 + 1
/ = integer division
\ = integer modulus
Here's the fun bit ๐ If char-infos has length zero, then min-bounds and max-bounds are identical, and the effective char-infos is one filled with that char-info. Its length is:
L = D * (max-byte1 - min-byte1 + 1)
So every glyph in the linear or matrix range has the same info, as given by min-bounds (and max-bounds). And if all-chars-exist is True, then every character in char-infos has a non-zero bounding box ๐
Default characters and bounds ๐ธ
The default-char is the character used whenever someone asks for an undefined or non-existent one. It's a CARD16, not a CHAR2B. For a font using the two-byte matrix format, byte1 sits in the most significant byte and byte2 in the least significant byte. If the default-char is itself undefined or non-existent, then nothing gets printed for undefined or non-existent characters at all ๐
The min-bounds and max-bounds hold the minimum and maximum of each individual CHARINFO component across all the char-infos. Non-existent characters are ignored for this.
The font's bounding box is the smallest rectangle that encloses all the characters superimposed at the same origin [x,y]. Its upper left corner is:
[x + min-bounds.left-side-bearing, y - max-bounds.
ascent]
Its width is:
max-bounds.right-side-bearing - min-bounds.
left-side-bearing
And its height is:
max-bounds.ascent + max-bounds.descent
Ascent, descent and the baseline ๐
The font-ascent is the logical extent of the font above the baseline, used for working out line spacing. Individual characters can poke out past it, and that's totally allowed. The font-descent is the logical extent at or below the baseline, also for line spacing, and it can be exceeded by specific characters too. If the baseline sits at Y-coordinate y, then the logical extent of the font is inclusive between (y - font-ascent) and (y + font-descent - 1) ๐ฆ
Font properties ๐ง
A font isn't guaranteed to have any properties at all. Whether a property value is signed or unsigned has to come from prior knowledge of that property. When possible, fonts should have at least the properties below. The trailing colon isn't part of the name, and upper and lower case matter, so be careful ๐ค
MIN_SPACE: CARD32
The minimum interword spacing, in pixels.
NORM_SPACE: CARD32
The normal interword spacing, in pixels.
MAX_SPACE: CARD32
The maximum interword spacing, in pixels
SUBSCRIPT_X: INT32
SUBSCRIPT_Y: INT32
Offsets from the character origin where subscripts
should begin, in pixels. If the origin is at [x,y],
then subscripts should begin at [x + SubscriptX,
y + SubscriptY].
UNDERLINE_POSITION: INT32
Y offset from the baseline to the top of an underline,
in pixels. If the baseline is Y-coordinate y, then
the top of the underline is at (y +
UnderlinePosition).
UNDERLINE_THICKNESS: CARD32
Thickness of the underline, in pixels.
STRIKEOUT_ASCENT: INT32
STRIKEOUT_DESCENT: INT32
Vertical extents for boxing or voiding characters, in
pixels. If the baseline is at Y-coordinate y, then
the top of the strikeout box is at (y -
StrikeoutAscent), and the height of the box is
(StrikeoutAscent + StrikeoutDescent).
ITALIC_ANGLE: INT32
The angle of characters in the font, in degrees
scaled by 64, relative to the three-oclock position
from the character origin, with positive indicating
counterclockwise motion (as in Arc requests).
X_HEIGHT: INT32
"1 ex" as in TeX, but expressed in units of pixels.
Often the height of lowercase x.
QUAD_WIDTH: INT32
"1 em" as in TeX, but expressed in units of pixels.
Often the width of the digits 0-9.
WEIGHT: CARD32
The weight or boldness of the font, expressed as a
value between 0 and 1000.
POINT_SIZE: CARD32
The point size, expressed in 1/10ths, of this font at
the ideal resolution. There are 72.27 points to the
inch.
RESOLUTION: CARD32
The number of pixels per point, expressed in 1/100ths,
at which this font was created.
Per-character boxes ๐งธ
For a character origin at [x,y], the character's own bounding box (the smallest rectangle around its shape, described by CHARINFO) has its upper left corner at:
[x + left-side-bearing, y - ascent]
Its width is:
right-side-bearing - left-side-bearing
Its height is:
ascent + descent
The origin for the next character is:
[x + character-width, y]
Two little notes ๐ The baseline is logically just below non-descending characters, so when descent is zero, only pixels with Y-coordinates less than y get drawn. And the origin is logically coincident with the left edge of a non-kerned character, so when left-side-bearing is zero, no pixels with X-coordinate less than x get drawn.
CHARINFO metric values can be negative, so don't be shocked if you see one ๐ญ A non-existent character is represented with all CHARINFO components set to zero. And the interpretation of the per-character attributes field is undefined by the core protocol, so the server isn't deciding that for you ๐
QueryTextExtents ๐
Next up, we're asking how big a whole string is. You send a FONT or GCONTEXT plus a STRING16, and you get back the logical extents of that string:
QueryTextExtents
font: FONT or GCONTEXT
items: STRING16
=>
draw-direction: {LeftToRight, RightToLeft}
font-ascent: INT16
font-descent: INT16
overall-ascent: INT16
overall-descent: INT16
overall-width: INT32
overall-left: INT32
overall-right: INT32
Errors: Font
The reply gives you draw-direction, font-ascent and font-descent (all as described in QueryFont). Then it gets juicy โจ overall-ascent is the maximum ascent metric of all the characters in the string, and overall-descent is the maximum descent metric. overall-width is the sum of all the character-width metrics in the string.
For each character, let W be the sum of the character-width metrics of all the characters before it. Let L be that character's left-side-bearing plus W, and let R be its right-side-bearing plus W. Then overall-left is the minimum L across all the characters, and overall-right is the maximum R ๐
For fonts defined with linear indexing (rather than two-byte matrix indexing), the server reads each CHAR2B as a 16-bit number sent most significant byte first. In other words, byte1 of the CHAR2B is the most significant byte. Also, if the font has no defined default-char, then undefined characters in the string count as having all-zero metrics ๐ซถ
ListFonts ๐
This one's a font search ๐ You give it a pattern and a max-names limit:
ListFonts
pattern: STRING8
max-names: CARD16
=>
names: LISTofSTRING8
You get back a list of at most max-names names, all matching the pattern. The pattern should use ASCII encoding, and upper and lower case don't matter. Inside the pattern, the ? character (octal value 77) matches any single character, and the * character (octal value 52) matches any number of characters ๐ท The names that come back are in lower case, so don't be surprised by that either ๐
ListFontsWithInfo ๐ฆ
This is ListFonts, but with extra goodies. It takes the same inputs, and returns the matching fonts along with their info:
ListFontsWithInfo
pattern: STRING8
max-names: CARD16
=>
fonts: LISTofFONTDATA
where
FONTDATA: [name: STRING8
info: FONTINFO]
FONTINFO: <same type definition as in QueryFont>
Each FONTDATA entry has a name and an info field, and the FONTINFO is the same type definition you saw in QueryFont ๐งฉ The information returned for each font is identical to what QueryFont would return, with one catch: the per-character metrics aren't included here ๐ธ
SetFontPath โจ
path: LISTofSTRING8
Errors: Value
Okay bestie, this one sets the search path for font lookup ๐ธ There's only one search path per server, not one per client, so everybody shares it ๐ซถ How the strings get interpreted depends on the operating system, but they're meant to be directories that get searched in the order you list them ๐
Setting the path to the empty list puts it back to the default path for the server ๐
Side quest: running this request also guarantees the server flushes all cached font info for fonts that don't currently have any explicit resource ids allocated ๐งน
What an error from this request means is system specific, so it depends on the setup ๐
GetFontPath โจ
=>
path: LISTofSTRING8
This one just returns the current search path for fonts ๐ Simple and iconic ๐
CreatePixmap โจ
pid: PIXMAP
drawable: DRAWABLE
depth: CARD8
width, height: CARD16
Errors: IDChoice, Drawable, Value, Alloc
Making a pixmap! ๐จ This request creates one and assigns the identifier pid to it. The width and height must be non-zero ๐
โโ๏ธ The depth must be one of the depths supported by the root of the specified drawable ๐ The initial contents of the pixmap are undefined, so don't count on anything being in there yet ๐ซ
It is legal to pass an InputOnly window as a drawable to this request ๐ฆ
FreePixmap โจ
pixmap: PIXMAP
Errors: Pixmap
This one deletes the association between the resource id and the pixmap ๐๏ธ The pixmap storage gets freed once no other resource is referencing it anymore, so it's not gone instantly, it waits until nobody needs it ๐ซถ
CreateGC โจ
cid: GCONTEXT
drawable: DRAWABLE
value-mask: BITMASK
value-list: LISTofVALUE
Errors: IDChoice, Drawable, Pixmap, Font, Match, Value, Alloc
Okay the graphics context era is here ๐ This request creates a graphics context and assigns the identifier cid to it. The gcontext can be used with any destination drawable that has the same root and depth as the specified drawable ๐
The value-mask and value-list say which components get explicitly initialized. The context components are:
alu-function: {Clear, And, AndReverse, Copy, AndInverted,
Noop, Xor, Or, Nor, Equiv, Invert,
OrReverse, CopyInverted, OrInverted,
Nand, Set}
plane-mask: CARD32
foreground: CARD32
background: CARD32
line-width: CARD16
line-style: {Solid, OnOffDash, DoubleDash}
cap-style: {NotLast, Butt, Round, Projecting}
join-style: {Miter, Round, Bevel}
fill-style: {Solid, Tiled, OpaqueStippled, Stippled}
fill-rule: {EvenOdd, Winding}
arc-mode: {Chord, PieSlice}
tile: PIXMAP
stipple: PIXMAP
tile-stipple-x-origin: INT16
tile-stipple-y-origin: INT16
font: FONT
subwindow-mode: {ClipByChildren, IncludeInferiors}
graphics-exposures: BOOL
clip-x-origin: INT16
clip-y-origin: INT16
clip-mask: PIXMAP or None
dash-offset: CARD16
dash-list: CARD8
How the Bits Combine ๐ง
In graphics operations, a source pixel and a destination pixel get combined bitwise, so the boolean operation happens on each corresponding bit plane ๐ฌ The plane-mask restricts the operation to a subset of those planes. The result is:
((src FUNC dst) AND plane-mask) OR (dst AND (NOT plane-mask))
foreground, background, and plane-mask don't get range checked. They're just truncated to the right number of bits โ๏ธ
The meanings of the alu-functions are:
Clear 0
And src AND dst
AndReverse src AND (NOT dst)
Copy src
AndInverted (NOT src) AND dst
NoOp dst
Xor src XOR dst
Or src OR dst
Nor (NOT src) AND (NOT dst)
Equiv (NOT src) XOR dst
Invert NOT dst
OrReverse src OR (NOT dst)
CopyInverted NOT src
OrInverted (NOT src) OR dst
NAnd (NOT src) OR (NOT dst)
Set 1
Line Width ๐
Line-width is measured in pixels. It can be greater than or equal to one (a "wide" line), or it can be the special value zero (a "thin" line) ๐ฆ
Wide lines are drawn centered on the path described by the graphics request. Unless the join or cap style says otherwise, the bounding box of a wide line with endpoints [x1, y1], [x2, y2], and width w is a rectangle with vertices at these real coordinates:
[x1-(w*sn/2), y1+(w*cs/2)], [x1+(w*sn/2), y1-(w*cs/2)],
[x2-(w*sn/2), y2+(w*cs/2)], [x2+(w*sn/2), y2-(w*cs/2)]
Here sn is the sine of the angle of the line and cs is the cosine of the angle of the line ๐ A pixel is part of the line (so it gets drawn) if the center of the pixel is fully inside the bounding box, which is treated as having infinitely thin edges. If the center of the pixel is exactly on the bounding box, it's part of the line if and only if the interior is immediately to its right (x increasing direction). Pixels with centers on a horizontal edge are a special case: they're part of the line if and only if the interior is immediately below (y increasing direction) ๐
Note that this is a mathematical model describing which pixels get drawn for a wide line. It does not mean trigonometry is required to implement it ๐ค Real or fixed point arithmetic is recommended for computing the corners of the line endpoints for lines greater than one pixel in width.
Thin lines (zero line-width) are "one pixel wide" lines drawn with an unspecified, device dependent algorithm (for example, Bresenham) ๐ง There are only two constraints on that algorithm. First, if a line is drawn unclipped from [x1,y1] to [x2,y2], and another line is drawn unclipped from [x1+dx, y1+dy] to [x2+dx,y2+dy], then a point [x,y] is touched by drawing the first line if and only if the point [x+dx,y+dy] is touched by drawing the second line. Second, the effective set of points making up a line can't be affected by clipping. In other words, a point is touched in a clipped line if and only if the point lies inside the clipping region and the point would be touched by the line when drawn unclipped ๐ซถ
A wide line drawn from [x1,y1] to [x2,y2] always draws the same pixels as a wide line drawn from [x2,y2] to [x1,y1], not counting cap and join styles. That property is not guaranteed for thin lines though ๐ Also, "jags" in adjacent wide lines always line up properly, but that's also not guaranteed for thin lines. A line-width of zero is different from a line-width of one in which pixels are drawn. In general, drawing a thin line is faster than drawing a wide line of width one, but thin lines may not mix well aesthetically. If you want precise and uniform results across all displays, a client should always use a line-width of one, rather than a line-width of zero โจ
Line Style ๐
The line-style defines which segments of a line get drawn:
Solid: the full path of the line is drawn
DoubleDash: the full path of the line is drawn, but the
segments defined by the even dashes are
filled differently than the segments defined
by the odd dashes (see fill-style)
OnOffDash: only the segments defined by the even dashes
are drawn, and cap-style applies to each
individual segment (except NotLast is treated
as Butt for internal caps)
Cap Style ๐
The cap-style defines how the endpoints of a path are drawn:
NotLast: equivalent to Butt, except that for a
line-width of zero or one the final endpoint is
not drawn
Butt: square at the endpoint, with no projection beyond
Round: a circular arc with diameter equal to the
line-width, centered on the endpoint; equivalent
to Butt for line-width zero or one
Projecting: square at the end, but the path continues
beyond the endpoint for a distance equal to
half the line-width; equivalent to Butt for
line-width zero or one
Join Style ๐
The join-style defines how corners are drawn for wide lines:
Miter: the outer edges of the two lines extend to meet at
an angle
Round: a circular arc with diameter equal to the
line-width, centered on the joinpoint
Bevel: Butt endpoint styles, and then the triangular
"notch" filled
Origins ๐
The tile, stipple, and clip origins are interpreted relative to the origin of whatever destination drawable is specified in a graphics request.
The tile pixmap must have the same root and depth as the gcontext, or else you get a Match error โ The stipple pixmap must have depth one and the same root as the gcontext, or else you also get a Match error. For stipple operations, the stipple pattern is tiled in a single plane, and it acts as an additional clip mask that gets ANDed with the clip-mask. Any size pixmap can be used for tiling or stippling, although some sizes may be faster to use than others ๐งฉ
Fill Style ๐๏ธ
The fill-style defines the contents of the source for line, text, and fill requests.
For all text and fill requests (PolyText8, PolyText16, PolyFillRectangle, FillPoly, PolyFillArc), for line requests (PolyLine, PolySegment, PolyRectangle, PolyArc) with line-style Solid, and for the even dashes of line requests with line-style OnOffDash or DoubleDash:
Solid: foreground
Tiled: tile
OpaqueStippled: a tile with the same width and height as
stipple, but with background everywhere
stipple has a zero and with foreground
everywhere stipple has a one
Stippled: foreground masked by stipple
For the odd dashes of line requests with line-style DoubleDash:
Solid: background
Tiled: same as for even dashes
OpaqueStippled: same as for even dashes
Stippled: background masked by stipple
The dash-list value allowed here is actually a simplified version of the fancier dash patterns you can set with SetDashes ๐ฆ. Giving a value of N here is the same as giving the two-element list [N, N] in SetDashes. The value must be non-zero ๐
โโ๏ธ. The meanings of dash-offset and dash-list are explained in the SetDashes request โจ.
The clip-mask is basically a bouncer for your destination drawable ๐ . Only pixels where the clip-mask has a one bit get drawn, and it affects all graphics requests. It does not clip sources ๐. The clip-mask origin is read relative to the origin of whatever destination drawable you name in a graphics request. If you use a pixmap as the clip-mask, it must have depth one and have the same root as the gcontext, or you'll get a Match error ๐. You can also set the clip-mask with the SetClipRectangles request ๐.
For ClipByChildren, both the source and destination windows also get clipped by all viewable InputOutput children ๐ธ. For IncludeInferiors, neither the source nor the destination window gets clipped by inferiors, which means your drawing goes right through subwindow boundaries ๐ฆ. Using IncludeInferiors on a window of one depth with mapped inferiors of a different depth isn't illegal, but the core protocol leaves the semantics undefined ๐ญ.
The fill-rule decides which pixels count as inside (so, which ones get drawn) for paths given in FillPoly requests ๐. EvenOdd means a point is inside if an infinite ray starting at that point crosses the path an odd number of times. For Winding, a point is inside if an infinite ray starting at that point crosses an unequal number of clockwise and counterclockwise directed path segments ๐. For both rules, a "point" is infinitely small and the path is an infinitely thin line ๐ง. A pixel is inside if its center point is inside and the center point isn't on the boundary. If the center point is on the boundary, the pixel is inside if and only if the polygon interior is immediately to its right (x increasing direction) ๐ . Pixels with centers along a horizontal edge are a special case: they're inside if and only if the polygon interior is immediately below (y increasing direction) โจ.
The arc-mode controls filling in the PolyFillArc request ๐ธ.
The graphics-exposures flag controls GraphicsExposure event generation for CopyArea and CopyPlane requests, plus any similar requests defined by extensions ๐ซถ.
Here are the default component values, verbatim:
function: Copy
plane-mask: all ones
foreground: 0
background: 1
line-width: 0
line-style: Solid
cap-style: Butt
join-style: Miter
fill-style: Solid
full-rule: EvenOdd
arc-mode: PieSlice
tile: pixmap of unspecified size filled with forground
pixell (i.e., client specified pixel if any,
else 0)
stipple: pixmap of unspecified size filled with ones
tile-stipple-x-origin: 0
tile-stipple-y-origin: 0
font: <implementation dependent>
subwindow-mode: ClipByChildren
graphics-exposures: True
clip-x-origin: 0
clip-y-origin: 0
clip-mask: None
dash-offset: 0
dash-list: 4 (i.e., the list [4, 4])
Storing a pixmap in a gcontext might or might not result in a copy being made ๐คทโโ๏ธ. If the pixmap gets used later as the destination for a graphics request, the change might or might not show up in the gcontext. If the pixmap is used at the same time in a graphics request as both a destination and a tile or stipple, the results are not defined ๐.
It's quite likely that some gcontext information gets cached in display hardware, and that hardware can only cache a small number of gcontexts ๐ญ. Given how many components there are and how complex they are, clients should treat switching between gcontexts with nearly identical state as significantly more expensive than making minor changes to a single gcontext ๐ง .
ChangeGC โจ
gc: GCONTEXT
value-mask: BITMASK
value-list: LISTofVALUE
Errors: GContext, Pixmap, Font, Match, Value, Alloc
ChangeGC changes components in gc ๐ฅ. The value-mask and value-list say which components to change, and the values and restrictions are the same as for CreateGC ๐.
Changing the clip-mask also overrides any previous SetClipRectangles request on the context ๐. Changing the dash-offset or dash-list overrides any previous SetDashes request on the context ๐ฆ.
The order in which components get verified and altered is server dependent. If an error is generated, a subset of the components may already have been altered ๐ฌ.
CopyGC ๐ธ
src-gc, dst-gc: GCONTEXT
value-mask: BITMASK
Errors: GContext, Value, Match, Alloc
CopyGC copies components from src-gc to dst-gc ๐ซถ. The value-mask says which components to copy, just like in CreateGC. The two gcontexts must have the same root and the same depth, or you get a Match error ๐ .
SetDashes ๐ฆ
gc: GCONTEXT
dash-offset: CARD16
dash-list: LISTofCARD8
Errors: GContext, Value, Alloc
SetDashes sets the dash-offset and dash-list in gc for dashed line styles ๐. The initial and alternating elements of the dash-list are the "even" dashes, and the others are the "odd" dashes. All of the elements must be non-zero ๐ โโ๏ธ. The dash-offset sets the phase of the pattern, meaning how many pixels into the dash-list the pattern actually begins in any single graphics request ๐ง. Dashing is continuous through path segments combined with a join-style, but it resets to the dash-offset each time a cap-style is applied ๐.
SetClipRectangles ๐
gc: GCONTEXT
clip-x-origin, clip-y-origin: INT16
rectangles: LISTofRECTANGLE
ordering: {UnSorted, YSorted, YXSorted, YXBanded}
Errors: GContext, Value, Alloc, Match
SetClipRectangles changes the clip-mask in gc to the specified list of rectangles and sets the clip origin โจ. Output gets clipped so it stays inside those rectangles ๐ . The clip origin is read relative to the origin of whatever destination drawable you name in a graphics request, and the rectangle coordinates are read relative to the clip origin ๐ธ. The rectangles should not intersect, or the graphics results are undefined ๐ญ.
If the client knows the ordering relations on the rectangles, it can say so with the ordering argument, which may make the server faster ๐. If you give an incorrect ordering, the server may generate a Match error, but it isn't required to ๐คทโโ๏ธ. If no error is generated, the graphics results are undefined.
Here's what each ordering means. UnSorted means the rectangles are in arbitrary order ๐ฒ. YSorted means the rectangles are non-decreasing in their Y origin ๐. YXSorted adds to YSorted by requiring that rectangles with an equal Y origin are non-decreasing in their X origin ๐ฆ. YXBanded adds to YXSorted by requiring that, for every possible Y scanline, all rectangles that include that scanline have identical Y origins and Y extents ๐ซถ.
FreeGC ๐ซง
gc: GCONTEXT
Errors: GContext
FreeGC deletes the association between the resource id and the gcontext, and destroys the gcontext ๐.
ClearToBackground ๐ท
window: WINDOW
x, y: INT16
width, height: CARD16
exposures: BOOL
Errors: Window, Value, Match
The x and y coordinates are relative to the window's origin, and they specify the upper left corner of the rectangle ๐. If width is zero, it gets replaced with the current width of the window minus x ๐. If height is zero, it gets replaced with the current height of the window minus y ๐.
If the window has a defined background tile, the rectangle gets tiled with a plane-mask of all ones and an alu-function of Copy ๐. If the window has background None, the contents of the window don't change ๐. In either case, if exposures is True, one or more exposure events get generated for regions of the rectangle that are either visible or being retained in a backing store ๐.
It's a Match error to use an InputOnly window in this request ๐ .
CopyArea ๐ฆ
src-drawable, dst-drawable: DRAWABLE
gc: GCONTEXT
src-x, src-y: INT16
width, height: CARD16
dst-x, dst-y: INT16
Errors: Drawable, GContext, Match
CopyArea combines the specified rectangle of src-drawable with the specified rectangle of dst-drawable ๐ซถ. The src-x and src-y coordinates are relative to src-drawable's origin, and dst-x and dst-y are relative to dst-drawable's origin. Each pair specifies the upper left corner of its rectangle ๐. Src-drawable must have the same root and the same depth as dst-drawable, or you get a Match error ๐ค.
If some regions of the source rectangle are obscured and haven't been retained by the server, or if regions outside the boundaries of the source drawable are specified, here's what happens ๐. If dst-drawable is a window with a background other than None, the corresponding regions of the destination are tiled with that background (with a plane-mask of ones and alu-function Copy) ๐จ. No matter what, if graphics-exposures in gc is True, GraphicsExposure events get generated for the corresponding destination regions ๐ฅ.
If graphics-exposures is True but no regions are exposed, a NoExposure event gets generated instead ๐.
GC components:
alu-function, plane-mask, foreground,
subwindow-mode, clip-x-origin, clip-y-origin, clip-mask
CopyPlane ๐
scr-drawable, dst-drawable: DRAWABLE
GC:Gcontext
src-x, src-y: INT16
width, height: CARD16
dst-x, dst-y: INT16
bit-plane: CARD32
Errors: Drawable, GContext, Value, Match
Src-drawable must have the same root as dst-drawable, or you get a Match error ๐ โโ๏ธ. It doesn't need to have the same depth, though ๐. Bit-plane must have exactly one bit set ๐ง. Effectively, that plane of the src-drawable and the foreground and background pixels in gc get combined to form a pixmap with the same depth as dst-drawable. Then the equivalent of a CopyArea is performed, with all the same exposure semantics ๐ฆ.
GC components:
alu-function, plan-mask, foreground,
background, subwindow-mode, graphics-exposures,
clip-x-origin, clip-y-origin, clip-mask
PolyPoint โจ
PolyPoint
drawable: DRAWABLE
gc: GCONTEXT
coordinate-mode: {Origin, Previous}
points: LISTofPOINT
Errors: Drawable, GContext, Value, Match
Okay bestie, PolyPoint is giving "paint these dots, one by one, in order" ๐. It mixes the foreground pixel from your gc with the pixel at every point on the drawable, and it draws them in exactly the order you list them ๐ . The first point is always measured from the drawable's origin. After that, each point is measured from either that same origin or the point right before it, depending on coordinate-mode ๐ฆ.
GCcomponents: alu-function, plane-mask, foreground,
subwindow-mode, clip-x-origin, clip-y-origin, clip-mask
PolyLine ๐
PolyLine
drawable: DRAWABLE
gc: GCONTEXT
coordinate-mode: {Origin, Previous}
points: LISTofPOINT
Errors: Drawable, GContext, Value, Match
PolyLine draws a line between every pair of neighbouring points, so point[i] connects to point[i+1] ๐ง. The lines are drawn in the order listed. They join nicely at every middle point, and if your first and last points line up, the first and last lines join up too, which is so iconic ๐ญ๐ซถ.
For any single line, no pixel gets drawn twice. Thin lines (zero line-width) that cross each other will draw their overlapping pixels more than once. Wide lines that cross each other only draw the overlap once, as if the whole PolyLine were one filled shape. Main character energy ๐.
The first point is always relative to the drawable's origin. Every other point is relative to either that origin or the previous point, depending on coordinate-mode ๐ธ.
GC components: alu-function, plane-mask, line-width,
line-style, cap-style, join-style, fill-style,
subwindow-mode, clip-x-origin, clip-y-origin, clip-mask
GC mode-dependent components: foreground, background, tile,
stipple, tile-stipple-x-origin, tile-stipple-y-origin,
dash-offset,dash-list
PolySegment ๐ฆ
PolySegment
drawable: DRAWABLE
gc: GCONTEXT
segments: LISTofSEGMENT
where SEGMENT: [x1, y1, x2, y2: INT16]
Errors: Drawable, GContext, Match
PolySegment is the no-drama cousin of PolyLine ๐. For each segment, it draws a line from [x1, y1] to [x2, y2], and the lines come out in the order listed. There is zero joining at coincident endpoints, so nothing gets stitched together ๐. No pixel is drawn twice on any single line, but if lines cross, the crossing pixels get drawn multiple times. Iykyk ๐.
GC components: alu-function, plane-mask, line-width,
line-style, cap-style, fill-style, subwindow-mode,
clip-x-origin, clip-y-origin,clip-mask
GC mode-dependent components: foreground, background, tile,
stipple,tile-stipple-x-origin, tile-stipple-y-origin,
dash-offset, dash-list
PolyRectangle ๐
PolyRectangle
drawable: DRAWABLE
gc: GCONTEXT
rectangles: LISTofRECTANGLE
Errors: Drawable, GContext, Match
This one draws the outlines of rectangles, and it's literally like drawing a five-point PolyLine for each one ๐. The x and y coordinates of each rectangle are relative to the drawable's origin, and they mark the upper left corner of that rectangle โจ.
Rectangles get drawn in the order listed. Within any one rectangle, no pixel is drawn twice. If rectangles overlap, the overlapping pixels get drawn multiple times, which is a whole mood ๐.
GC components: alu-function, plane-mask, line-width,
line-style, join-style, fill-style, subwindow-mode,
clip-x-origin, clip-y-origin, clip-mask
GC mode-dependent components: foreground, background, tile,
stipple, tile-stipple-x-origin, tile-stipple-y-origin,
dash-offset, dash-list
PolyArc ๐
PolyArc
drawable: DRAWABLE
gc: GCONTEXT
arcs: LISTofARC
Errors: Drawable, GContext, Match
PolyArc draws circles and ellipses, bestie, and they're so pretty ๐ญ๐ฆ. Each arc is described by a rectangle and two angles ๐ . The x and y coordinates are relative to the drawable's origin and mark the upper left corner of that rectangle. The center of the circle or ellipse is the center of the rectangle. The width and height give the major and minor axes, in that order ๐ธ.
The angles are signed integers in degrees, scaled by 64. Positive means counterclockwise and negative means clockwise ๐ฅ. The start of the arc is angle1, measured from the three-o'clock position around the center. The path and extent come from angle2, measured from where the arc starts. If the magnitude of angle2 is more than 360 degrees, it gets truncated down to 360 degrees โจ.
Arcs are drawn in the order listed. If the last point of one arc lines up with the first point of the next, they join correctly. The same goes for the first arc and the last arc if their points match up ๐ซถ. For any given arc, no pixel is drawn twice. If two arcs join correctly, the line-width is greater than zero, and the arcs intersect, then no pixel is drawn more than once. Otherwise, the overlapping pixels of intersecting arcs get drawn multiple times ๐.
Here's a fun one: specifying an arc from one endpoint with a clockwise extent draws the same pixels as starting from the other endpoint with an equivalent counterclockwise extent. The only difference is how it affects joins ๐ง.
If you make one axis zero, you get a horizontal or vertical line ๐.
Angles are computed purely from the coordinate system, so the aspect ratio gets ignored ๐.
GC components: alu-function, plane-mask, line-width,
line-style, cap-style, join-style, fill-style,
subwindow-mode, clip-x-origin, clip-y-origin, clip-mask
GC mode-dependent components: foreground, background, tile,
stipple,tile-stipple-x-origin, tile-stipple-y-origin,
dash-offset, dash-list
FillPoly ๐ซง
FillPoly
drawable: DRAWABLE
gc: GCONTEXT
shape: {Complex, Nonconvex, Convex}
coordinate-mode: {Origin, Previous}
points: LISTofPOINT
Errors: Drawable, GContext, Match, Value
FillPoly fills in the region enclosed by a path ๐จ. The path closes itself automatically if the last point doesn't match the first one ๐. No pixel in the region gets drawn more than once, which is so clean ๐ .
Same deal with coordinate-mode: the first point is relative to the drawable's origin, and the rest are relative to either that origin or the previous point ๐ธ.
The shape value is a hint the server can use to go faster ๐. Complex means the path might cross itself. Nonconvex means the path doesn't cross itself, but it isn't totally convex. If the client knows that, picking Nonconvex over Complex might boost performance ๐ฆ. But if you say Nonconvex for a path that crosses itself, the graphics results are undefined, so don't do that ๐. Convex means the path is completely convex. If the client knows it, Convex can help performance too. If you claim Convex for a path that isn't convex, the graphics results are undefined ๐ญ.
GC components: alu-function, plane-mask, fill-style,
fill-rule, subwindow-mode, clip-x-origin, clip-y-origin,
clip-mask
GC mode-dependent components: foreground, tile, stipple,
tile-stipple-x-origin, tile-stipple-y-origin
PolyFillRectangle ๐ฌ
PolyFillRectangle
drawable: DRAWABLE
gc: GCONTEXT
rectangles: LISTofRECTANGLE
Errors: Drawable, GContext, Match
This one fills in rectangles, and the x and y coordinates of each one are relative to the drawable's origin, marking its upper left corner ๐.
Rectangles get filled in the order listed. No pixel within any single rectangle is drawn twice. If rectangles overlap, the overlapping pixels get drawn multiple times ๐โจ.
GC components: alu-function, plane-mask, fill-style,
fill-rule, subwindow-mode, clip-x-origin, clip-y-origin,
clip-mask
GC mode-dependent components: foreground, tile, stipple,
tile-stipple-x-origin, tile-stipple-y-origin
PolyFillArc ๐ท
PolyFillArc
drawable: DRAWABLE
gc: GCONTEXT
arcs: LISTofARC
Errors: Drawable, GContext, Match
For each arc, PolyFillArc fills the region enclosed by the arc and one or two line segments, depending on the arc-mode ๐ฐ. With Chord, a single line segment joins the two endpoints of the arc. With PieSlice, two line segments join each endpoint to the center point, so it looks like a slice of pie ๐ฅง๐. The arcs are the same ones described in PolyArc ๐ฆ.
Arcs get filled in the order listed. No pixel is drawn twice for any given arc. If regions overlap, the overlapping pixels get drawn multiple times ๐ธ.
GC components: alu-function, plane-mask, fill-style,
fill-rule, arc-mode, subwindow-mode, clip-x-origin,
clip-y-origin, clip-mask
GC mode-dependent components: foreground, tile, stipple,
tile-stipple-x-origin, tile-stipple-y-origin
PutImage ๐ธ
PutImage
drawable: DRAWABLE
gc: GCONTEXT
depth: CARD8
width, height: CARD16
dst-x, dst-y: INT16
left-pad: CARD8
format: {Bitmap, XYPixmap, ZPixmap}
bits: <bits>
Errors: Drawable, GContext, Match, Value, Alloc
PutImage combines an image with a rectangle of the drawable, so it's basically how you drop a picture onto the canvas ๐ผ๏ธโจ. dst-x and dst-y are relative to the drawable's origin ๐
.
Bitmap format is a bit picky ๐. If you use it, depth must be one, or you get a Match error, and the image must be in XYFormat. The foreground pixel in gc decides what the one bits look like, and the background pixel decides what the zero bits look like ๐ฆ.
For XYPixmap and ZPixmap, depth must match the drawable's depth, or you get a Match error ๐. XYPixmap images must be sent in XYFormat. ZPixmap images must be sent in the ZFormat defined for the given depth ๐.
left-pad must be zero for ZPixmap format ๐ซถ. For Bitmap and XYPixmap, left-pad must be less than bitmap-format-scanline-pad, which is listed in the server connection setup info ๐ง. The server ignores the first left-pad bits in every scanline, so the real image starts that many bits into the data ๐ญ. The width argument is the width of the actual image and does not include left-pad ๐ธ.
GC components: alu-function, plane-mask, subwindow-mode,
clip-x-origin, clip-y-origin, clip-mask
GC mode-dependent components: foreground, background
GetImage โจ
GetImage
drawable: DRAWABLE
x, y: INT16
width, height: CARD16
plane-mask: CARD32
format: {XYFormat, ZFormat}
=>
depth: CARD8
visual: VISUALID or None
bits: <bits>
Errors: Drawable, Value, Match
okay so GetImage is how you grab the contents of a rectangle from a drawable, in whatever format you ask for ๐ธ The x and y coordinates are relative to the drawable's origin, and they mark the upper left corner of the rectangle ๐
If you pick XYFormat, only the bit planes listed in plane-mask get sent back. If you pick ZFormat, every plane that isn't in plane-mask gets sent back as zero ๐ฆ The depth you get back tells you how many bits per pixel the image has ๐ธ And the visual part is the fun bit: if the drawable is a window, you get its visual type back, but if it's a pixmap, the visual is None ๐
If the drawable is a window, the window must be mapped ๐ช It also has to be the case that, if there were no inferiors or overlapping windows, the rectangle you asked for would be fully visible on the screen. The result will include any visible bits of inferiors or overlapping windows that sit inside that rectangle. BUT if those windows have a different depth than your window, what gets returned for them isn't defined by the core protocol ๐ that's a whole vibe, not a guarantee โจ
PolyText8 ๐
PolyText8
drawable: DRAWABLE
gc: GCONTEXT
x, y: INT16
items: LISTofTEXTITEM8
where
TEXTITEM8: TEXTELT8 or FONT
TEXTELT8: [delta: INT8
string: STRING8]
Errors: Drawable, GContext, Match, Font
PolyText8 draws text, and the x and y coordinates are relative to the drawable's origin ๐ง They give you the baseline starting position, which is the initial character origin. Every text item gets processed in turn, in order ๐
A FONT item stores that font in the gc and uses it for all the text that comes after it ๐ Switching between fonts with different draw-directions is totally allowed, no drama ๐
A text element's delta is an extra shift along the x axis before its string gets drawn. The delta is always added to the character origin, and it isn't added or subtracted based on the draw-direction of the current font, so don't let that one trip you up ๐
Each character image, as defined by the font in the gc, gets treated as an extra mask for a fill operation on the drawable ๐๏ธ Literally so iconic โจ
All the FONTs in the request always get sent most significant byte first ๐
If a Font error comes up for an item, the items before it may already have been drawn ๐ so don't assume a clean slate.
For fonts with two-byte matrix indexing, each STRING8 byte gets read as the byte2 value of a CHAR2B, and the byte1 value is zero ๐ฆ
GC components: alu-function, plane-mask, fill-style, font, subwindow-mode, clip-x-origin, clip-y-origin, clip-mask
GC mode-dependent components: foreground, tile, stipple, tile-stipple-x-origin, tile-stipple-y-origin
PolyText16 ๐
PolyText16
drawable: DRAWABLE
gc: GCONTEXT
x, y: INT16
items: LISTofTEXTITEM16
where
TEXTITEM16: TEXTELT16 or FONT
TEXTELT16: [delta-x: INT8
string: STRING16]
Errors: Drawable, GContext, Match, Font
this is PolyText8 but with two-byte (16-bit) characters, so it's just bigger and better ๐ธ Same deal, just with the wider characters. For fonts that use linear indexing instead of two-byte matrix indexing, the server reads each CHAR2B as a 16-bit number sent most significant byte first. That means byte1 of the CHAR2B is the most significant byte ๐ฅ
ImageText8 ๐ท
ImageText8
drawable: DRAWABLE
gc: GCONTEXT
x, y: INT16
string: STRING8
Errors: Drawable, GContext, Match
ImageText8 is the one that paints a little background box behind your words ๐ The x and y coordinates are relative to the drawable's origin, and they give the baseline starting position (the initial character origin) ๐ง
First it fills a destination rectangle with the background pixel from the gc, then it paints the text with the foreground pixel โจ The upper left corner of that filled rectangle sits at:
[x + overall-left, y - font-ascent]
Its width is:
overall-right - overall-left
and its height is:
font-ascent + font-descent
Here, overall-left, overall-right, font-ascent, and font-descent are the values you'd get back from a QueryTextExtents call using the gc and the string ๐
The alu-function and fill-style in the gc get ignored for this request, so the effective alu-function is Copy and the effective fill-style is Solid ๐ no cap, it's a fixed look ๐ซถ
For fonts with two-byte matrix indexing, each STRING8 byte gets read as the byte2 value of a CHAR2B, with byte1 set to zero ๐ฆ
GC components: plane-mask, foreground, background, font, subwindow-mode, clip-x-origin, clip-y-origin, clip-mask
ImageText16 ๐
ImageText16
drawable: DRAWABLE
gc: GCONTEXT
x, y: INT16
string: STRING16
Errors: Drawable, GContext, Match
Same as ImageText8, just with two-byte (16-bit) characters ๐ For fonts with linear indexing rather than two-byte matrix indexing, the server reads each CHAR2B as a 16-bit number sent most significant byte first, so byte1 of the CHAR2B is the most significant byte โจ
CreateColormap ๐ฆ
CreateColormap
mid: COLORMAP
visual: VISUALID
window: WINDOW
alloc: {None, All}
Errors: IDChoice, Window, Value, Match, Alloc
CreateColormap makes a colormap of the given visual type for the screen the window lives on, and links the identifier mid to it ๐ช The visual type has to be one the screen actually supports, and it can't be TrueColor (or you get a Match error) ๐ โโ๏ธ
The starting values of the colormap entries are undefined for GrayScale, PseudoColor, and DirectColor ๐ธ For StaticGray, StaticColor, and TrueColor, the entries do have defined values, but those values belong to the visual and aren't defined by the core protocol ๐
For StaticGray, StaticColor, and TrueColor, alloc must be None, or you get a Match error ๐ For the other classes, if alloc is None, the colormap starts with no allocated entries, and clients can allocate entries themselves ๐ If alloc is All, the whole colormap is "allocated" writable. Entries can't be freed with FreeColors, and no relationships among entries are defined. The client has to understand whether the colormap is GrayScale, PseudoColor, or DirectColor so it knows how to store into the entries ๐ฅ very main character energy, you gotta know your visual ๐
FreeColormap ๐ง
FreeColormap
cmap: COLORMAP
Errors: Colormap
FreeColormap deletes the link between the resource id and the colormap ๐ If the colormap is an installed map for a screen, it gets uninstalled (see UninstallColormap) ๐ซถ If the colormap is set as the colormap for a window (through CreateWindow or ChangeWindowAttributes), that window's colormap changes to None, and a ColormapNotify event gets generated ๐
The colors shown for a window with a colormap of None aren't defined by the protocol ๐
It has no effect on a default colormap for a screen, so the defaults are safe ๐
CopyColormapAndFree ๐ท
CopyColormapAndFree
mid, src-cmap: COLORMAP
Errors: Colormap, Alloc
This one creates a colormap for the same screen as src-cmap and links the identifier mid to it โจ It moves all of the client's existing allocations from src-cmap over to the new colormap, and then frees those entries in src-cmap ๐ฆ Values in the other entries of the new colormap are undefined ๐
InstallColormap ๐
InstallColormap
cmap: COLORMAP
Errors: Colormap
InstallColormap makes this colormap an installed map for its screen ๐ All the windows associated with this colormap immediately display with true colors ๐ As a side effect, previously installed colormaps might get uninstalled, and other windows might show false colors ๐
Which colormaps get uninstalled is up to the server, EXCEPT that the M-1 most recently client-installed colormaps are guaranteed not to be uninstalled. Here M is the min-installed-maps value set for the screen in the connection setup ๐
If cmap isn't already an installed map, a ColormapNotify event gets generated on every window that has cmap as an attribute ๐ If a colormap gets uninstalled because of this install, a ColormapNotify event gets generated on every window that has that colormap as an attribute too.
Initially, only the default colormap for a screen is installed ๐ธ
UninstallColormap ๐ง
UninstallColormap
cmap: COLORMAP
Errors: Colormap
If cmap is an installed map for its screen, one or more colormaps get installed in its place. Which ones is up to the server, but if the screen's default colormap isn't installed and can be installed without forcing other colormaps out, then the default colormap is used ๐ซถ
If cmap is an installed map, a ColormapNotify event gets generated on every window that has this colormap as an attribute ๐ If a colormap gets installed because of the uninstall, a ColormapNotify event gets generated on every window that has that colormap as an attribute too.
ListInstalledColormaps ๐
ListInstalledColormaps
window: WINDOW
=>
cmaps: LISTofCOLORMAP
Errors: Window
This one returns a list of the currently installed colormaps for the screen of the specified window ๐
AllocColor ๐
AllocColor
cmap: COLORMAP
red, green, blue: CARD16
=>
pixel: CARD32
red, green, blue: CARD16
Errors: Colormap, Alloc
AllocColor allocates a read-only colormap entry that matches the closest RGB values the hardware can provide โจ It returns the pixel along with the RGB values that were actually used ๐
AllocNamedColor ๐
AllocNamedColor
cmap: COLORMAP
name: STRING8
=>
pixel: CARD32
exact-red, exact-green, exact-blue: CARD16
screen-red, screen-green, screen-blue: CARD16
Errors: Colormap, Name, Alloc
AllocNamedColor looks up the named color for the screen associated with the colormap, and then does an AllocColor on cmap ๐ The name should use ASCII encoding, and upper or lower case doesn't matter ๐ธ The exact RGB values are the "true" values for the color, and the screen values are the values actually used in the colormap ๐ฆ bestie, that's the whole difference, and it's iconic ๐ซถ
AllocColorCells ๐
cmap: COLORMAP
colors, planes: CARD16
contiguous: BOOL
=>
pixels, masks: LISTofCARD32
Errors: Colormap, Value, Alloc
okay so the number of colors has to be positive and the number of planes has to be non-negative ๐ If you ask for C colors and P planes, you get back C pixels and P masks. No mask shares any bits with another mask or with any of the pixels. If you OR masks and pixels together in every combo, you get C*(2^P) distinct pixels, and they're all allocated as writable by this request ๐ฆ For GrayScale or PseudoColor, each mask has exactly one bit. For DirectColor, each mask has exactly three bits. If contiguous is True, then ORing all the masks together gives you one contiguous run of bits for GrayScale or PseudoColor, or three contiguous runs (one inside each pixel subfield) for DirectColor. The RGB values of the allocated entries are undefined โจ
AllocColorPlanes ๐
cmap: COLORMAP
colors, reds, greens, blues: CARD16
contiguous: BOOL
=>
pixels: LISTofCARD32
red-mask, green-mask, blue-mask: CARD32
Errors: Errors; Colormap, Value, Alloc
this one is giving planes ๐ง The number of colors has to be positive, and the reds, greens, and blues have to be non-negative. Ask for C colors, R reds, G greens, and B blues, and you get C pixels back, with the masks carrying R, G, and B bits respectively. If contiguous is True, each mask gets a contiguous run of bits. Same deal as before: no mask overlaps another mask or any pixel. For DirectColor, each mask has to sit inside its matching pixel subfield. ORing subsets of masks with pixels gives you C*(2^(R+G+B)) distinct pixels, and the request allocates all of them ๐ธ The initial RGB values are undefined. Here's the twist though: the colormap only has C*(2^R) independent red entries, C*(2^G) independent green entries, and C*(2^B) independent blue entries. That's true even for PseudoColor ๐คฏ When you change a pixel's colormap entry with StoreColors or StoreNamedColor, the pixel gets split up using the masks and the matching independent entries get updated ๐
FreeColors ๐
cmap: COLORMAP
pixels: LISTofCARD32
plane-mask: CARD32
Errors: Colormap, Access, Value
so the plane-mask shouldn't share any bits with the pixels ๐ The full set of pixels comes from ORing subsets of plane-mask with the pixels, and this request frees every one of them. ngl, freeing one pixel from AllocColorPlanes might not let it get reused until all of its "related" pixels are freed too ๐ซถ
Every pixel you name that the client has allocated in cmap gets freed, even if one or more of them throws an error. A Value error happens when a pixel isn't a valid index into cmap. An Access error happens when a pixel isn't allocated by this client (meaning it's unallocated, or only allocated by some other client) ๐ญ If more than one pixel is in error, the server can report whichever one it wants.
StoreColors ๐
cmap: COLORMAP
items: LISTofCOLORITEM
where
COLORITEM: [pixel: CARD32
do-red, do-green, do-blue: BOOL
red, green, blue: CARD16]
Errors: Colormap, Access, Value
this changes the colormap entries for the pixels you pick ๐ The do-red, do-green, and do-blue fields tell it which components to actually change. If the colormap is an installed map for its screen, the changes show up right away โจ
Every specified pixel that's allocated writable in cmap (by any client) gets changed, even if one or more of them errors. A Value error shows up when a pixel isn't a valid index into cmap. An Access error shows up when a pixel is unallocated or is allocated read-only ๐ If more than one pixel is in error, the server can report whichever one it wants.
StoreNamedColor ๐ฆ
cmap: COLORMAP
pixel: CARD32
name: STRING8
do-red, do-green, do-blue: BOOL
Errors: Colormap, Name, Access, Value
this one looks up a named color for the screen linked to cmap, then does a StoreColors in cmap ๐ง The name should use ASCII encoding, and upper and lower case don't matter ๐ธ
QueryColors ๐
cmap: COLORMAP
pixels: LISTofCARD32
=>
colors: LISTofRGB
where
RGB: [red, green, blue: CARD16]
Errors: Colormap, Value
this gives you back the color values stored in cmap for the pixels you ask about ๐ The values for an unallocated entry are undefined. A Value error happens when a pixel isn't a valid index into cmap. If more than one pixel is in error, the server can report whichever one it wants ๐ซถ
LookupColor ๐
cmap: COLORMAP
name: STRING8
=>
exact-red, exact-green, exact-blue: CARD16
screen-red, screen-green, screen-blue: CARD16
Errors: Colormap, Name
this looks up a color by its string name for the screen linked to cmap, and gives you back both the exact color values and the closest values the hardware can actually show ๐ The name should use ASCII encoding, and upper and lower case don't matter ๐
CreateCursor โจ
cid: CURSOR
source: PIXMAP
mask: PIXMAP or None
fore-red, fore-green, fore-blue: CARD16
back-red, back-green, back-blue: CARD16
x, y: CARD16
Errors: IDChoice, Bitmap, Match, Value, Alloc
this makes a cursor and ties the identifier cid to it ๐ฆ You have to give foreground and background RGB values, even if the server only has a monochrome screen ๐ค The foreground is used for the one bits in the source, and the background is used for the zero bits. Both source and mask (if you give one) have to be depth one, or you get a Match error. They can have any root though ๐ The mask pixmap sets the shape of the cursor: the one bits in the mask decide which source pixels get displayed. If there's no mask, all of the source pixels are shown ๐ธ If you give a mask, it has to be the same size as the source, or it's a Match error. The x and y coordinates set the hotspot, relative to the source's origin, and that point has to be inside the source, or it's a Match error ๐ง
The cursor's components can get transformed however needed to fit display limits ๐ You can free the pixmaps right away if you don't plan to reference them again ๐ซถ If you draw into the source or mask pixmap afterwards, the effect on the cursor is undefined, since the server might or might not have made a copy of the pixmap ๐
CreateGlyphCursor ๐
cid: CURSOR
source-font: FONT
mask-font: FONT or None
source-char, mask-char: CARD16
fore-red, fore-green, fore-blue: CARD16
back-red, back-green, back-blue: CARD16
Errors: IDChoice, Font, Value, Alloc
this is basically CreateCursor, except the source and mask bitmaps come from font glyphs ๐ The mask font and character are optional. The origin of the source glyph sets the hotspot, and the mask lines up so its origin sits on the source's origin โจ The source and mask don't need matching bounding box metrics. If there's no mask, all of the source pixels are displayed ๐ฆ Heads up: source-char and mask-char are CARD16, not CHAR2B ๐ For two-byte matrix fonts, the 16-bit value should be built with byte1 in the most significant byte and byte2 in the least significant byte ๐
FreeCursor ๐ธ
cursor: CURSOR
Errors: Cursor
this deletes the link between the resource id and the cursor ๐ง The cursor storage gets freed once no other resource is referencing it ๐
RecolorCursor ๐
cursor: CURSOR
fore-red, fore-green, fore-blue: CARD16
back-red, back-green, back-blue: CARD16
Errors: Cursor
this changes a cursor's color ๐ If the cursor is being shown on a screen, the change shows up immediately โญ
QueryBestSize ๐
class: {Cursor, Tile, Stipple}
drawable: DRAWABLE
width, height: CARD16
=>
width, height: CARD16
Errors: Drawable, Value, Match
this returns the "best" size, which is the one "closest" to the size you asked for ๐ซถ For Cursor, that's the largest size that can be fully displayed. For Tile, it's the size that tiles "fastest". For Stipple, it's the size that stipples "fastest" ๐
For Cursor, the drawable tells the server which screen you want. For Tile and Stipple, the drawable tells it the screen, and maybe the window class and depth too ๐ฆ An InputOnly window can't be the drawable for Tile or Stipple, or you get a Match error ๐
QueryExtension ๐ฆ
name: STRING8
=>
present: BOOL
major-opcode: CARD8
first-event: CARD8
first-error: CARD8
this checks whether the named extension is around ๐ง If it is, you get back its major opcode (if it has one), otherwise zero comes back. Any minor opcodes and request formats belong to the extension itself ๐ธ If the extension adds event types, you get back the base event type code, otherwise zero. The event format is up to the extension ๐ If the extension adds error codes, you get back the base error code, otherwise zero. The format of any extra data in those errors is up to the extension too โจ
The extension name should be in ASCII encoding, and upper and lower case does matter here ๐
ListExtensions ๐
=>
names: LISTofSTRING8
this returns a list of every extension the server supports โญ
SetKeyboardMapping ๐
SetKeyboardMapping
map: LISTofCARD8
=>
status: {Success, Busy}
Errors: Value
Okay so this one sets the mapping of the keyboard ๐น Elements of the list are indexed starting from one โจ The list must be of length 255. The index is a "core" keycode, and the element at that index defines the "effective" keycode.
A zero element disables a key, no elements can have values 1 through 7, and no two elements (with index larger than 7) can have the same non-zero value. If the keyboard doesn't really generate a given keycode, specifying a non-zero value for that core keycode has no effect ๐
Elements 6 and 7 of the map must always be zero. The first five elements are special ๐ They specify the keycodes (if any) that correspond to the Mod1 through Mod5 modifiers. Setting one of these entries to zero disables use of that modifier bit. No two of the first five elements can have the same non-zero value.
A server can impose restrictions on how keyboards get remapped, e.g., if certain keys don't generate up transitions in hardware ๐
If any of the keys or modifiers to be altered are currently in the down state, the status reply is Busy and the mapping is not changed. Not me being the one who gets blocked ๐ญ
GetKeyboardMapping
GetKeyboardMapping
=>
map: LISTofCARD8
Errors: Value
This one returns the current mapping of the keyboard ๐ฆ Elements of the list are indexed starting from one, and the length of the list is 255.
The nominal mapping is almost the identity mapping, with a few exceptions: map[i]=0 for keycodes that have no corresponding physical key, and the first five entries show the keycodes (if any) for the Mod1 through Mod5 modifier bits. Iconic ๐ธ
ChangeKeyboardControl
ChangeKeyboardControl
value-mask: BITMASK
value-list: LISTofVALUE
Errors: Match Value
This is where you control the vibes of the keyboard ๐ The value-mask and value-list specify which controls are being changed. The possible values are:
key-click-percent: INT8
bell-percent: INT8
bell-pitch: INT16
bell-duration: INT16
led: CARD8
led-mode: {On, Off}
key: KEYCODE
auto-repeat-mode: {On, Off, Default}
key-click-percent sets the volume for key clicks between 0 (off) and 100 (loud) inclusive, if possible. Setting it to -1 restores the default. Other negative values generate a Value error. โจ
bell-percent sets the base volume for the bell between 0 (off) and 100 (loud) inclusive, if possible. Setting it to -1 restores the default. Other negative values generate a Value error. ๐
bell-pitch sets the pitch (in Hz) of the bell, if possible. Setting it to -1 restores the default. Other negative values generate a Value error. ๐ถ
bell-duration sets the duration (in milliseconds) of the bell, if possible. Setting it to -1 restores the default. Other negative values generate a Value error. โฑ๏ธ
If both led-mode and led are specified, the state of that LED is changed, if possible. If only led-mode is specified, the state of all LEDs is changed, if possible. At most 32 LEDs are supported, numbered from one. It's a Match error if an led is specified without an led-mode. Main character energy for the LEDs ๐ก
If both auto-repeat-mode and key are specified, the auto-repeat mode of that key is changed, if possible. If only auto-repeat-mode is specified, the global auto-repeat mode for the entire keyboard is changed, if possible, without affecting the per-key settings. It's a Match error if a key is specified without an auto-repeat-mode.
A bell generator connected with the console but not directly on the keyboard is treated as if it were part of the keyboard ๐ง
The order in which controls are verified and altered is server dependent. If an error is generated, a subset of the controls may already have been altered. So be careful, bestie ๐ฌ
GetKeyboardControl
GetKeyboardControl
=>
key-click-percent: CARD8
bell-percent: CARD8
bell-pitch: CARD16
bell-duration: CARD16
led-mask: CARD32
global-auto-repeat: {On, Off}
auto-repeats: LISTofCARD8
Errors: Match
This returns the current control values for the keyboard ๐ For the LEDs, the least significant bit of led-mask corresponds to LED one, and each one bit in led-mask indicates an LED that is lit.
Auto-repeats is a bit vector, where each one bit means auto-repeat is enabled for the corresponding key. The vector is represented as 32 bytes. Byte N (counting from 0) contains the bits for keys 8N to 8N+7, with the least significant bit in the byte representing key 8N. It's giving bit-level detail ๐คโจ
Bell
Bell
percent: INT8
Errors: Match, Value
This rings the bell on the keyboard at the specified volume, relative to the base volume for the keyboard, if possible ๐ The percent can range from -100 to 100 inclusive. It gets added to the base volume, and the sum is limited to the range 0 to 100 inclusive. Volume math is literally so serious ๐งฎ
SetPointerMapping
SetPointerMapping
map: LISTofCARD8
=>
status: {Success, Busy}
Errors: Value
This sets the mapping of the pointer ๐ฑ๏ธ Elements of the list are indexed starting from one. The length of the list must be the same as what GetPointerMapping would return. The index is a "core" button number, and the element of the list defines the "effective" number.
A zero element disables a button. Elements are not restricted in value by the number of physical buttons, but no two elements can have the same non-zero value.
If any of the buttons to be altered are currently in the down state, the status reply is Busy and the mapping is not changed. Blocked again, huh ๐
GetPointerMapping
GetPointerMapping
=>
map: LISTofCARD8
Errors: Value
This returns the current mapping of the pointer ๐ Elements of the list are indexed starting from one, and the length of the list indicates the number of physical buttons.
The nominal mapping for a pointer is the identity mapping, where map[i]=i. Simple, clean, iconic โจ
ChangePointerControl
ChangePointerControl
do-acceleration, do-threshold: BOOL
acceleration-numerator, acceleration-denominator: INT16
threshold: INT16
Errors: Match, Value
This defines how the pointer moves ๐๏ธ The acceleration is a multiplier for movement, expressed as a fraction. For example, specifying 3/1 means the pointer moves three times as fast as normal. The fraction may be rounded arbitrarily by the server.
Acceleration only takes effect if the pointer moves more than threshold pixels at once, and it only applies to the amount beyond the threshold. Setting a value to -1 restores the default. Other negative values generate a Value error, and so does a zero value for acceleration-denominator. Don't divide by zero, babe ๐
GetPointerControl
GetPointerControl
=>
acceleration-numerator, acceleration-denominator: CARD16
threshold: CARD16
Errors: Match
This returns the current acceleration and threshold for the pointer ๐ฆ
SetScreenSaver
SetScreenSaver
timeout, interval: INT16
prefer-blanking: {Yes, No, Default}
allow-exposures: {Yes, No, Default}
Errors: Value
Okay this one is a whole storyline ๐บ Timeout and interval are specified in minutes, and setting a value to -1 restores the default. Other negative values generate a Value error.
If the timeout value is zero, screen-saver is disabled. If the timeout value is non-zero, screen-saver is enabled. Once screen-saver is enabled, if no input from the keyboard or pointer is generated for timeout minutes, screen-saver is activated.
For each screen, if blanking is preferred and the hardware supports video blanking, the screen will simply go blank ๐ค Otherwise, if either exposures are allowed or the screen can be regenerated without sending exposure events to clients, the screen is tiled with the root window background tile. That tile is randomly re-origined each interval minutes, as long as the interval value is non-zero. Otherwise, the state of the screen does not change and screen-saver is not activated. Plot twist ๐ซฃ
Screen-saver is deactivated, and all screen states are restored, at the next keyboard or pointer input, or at the next ForceScreenSaver with mode Reset. Welcome back, bestie ๐
GetScreenSaver
GetScreenSaver
=>
timeout, interval: CARD16
prefer-blanking: {Yes, No}
allow-exposures: {Yes, No}
This returns the current screen-saver control values ๐ธ
ForceScreenSaver
ForceScreenSaver
mode: {Activate, Reset}
If the mode is Activate and screen-saver is currently deactivated, then screen-saver is activated, even if it was disabled with a timeout value of zero. Wild ๐ฅ
If the mode is Reset and screen-saver is currently enabled, then screen-saver is deactivated (if it was activated). After that, the activation timer is reset to its initial state, as if device input had just been received. Full reset energy ๐งโโ๏ธ
ChangeHosts
ChangeHosts
mode: {Insert, Delete}
host: HOST
Errors: Access, Value
This adds or removes the specified host from the access control list ๐ช When the access control mechanism is enabled and a host tries to establish a connection to the server, the host must be in this list, or the server will refuse the connection. Bouncer vibes ๐
The client must reside on the same host as the server, and/or have been granted permission in the initial authorization at connection setup ๐
An initial access control list can be specified, typically by naming a file that the server reads at startup and reset. โจ
ListHosts
ListHosts
=>
mode: {Enabled, Disabled}
hosts: LISTofHOST
This returns the hosts on the access control list, plus whether use of the list at connection setup is currently enabled or disabled ๐
Each HOST is padded to a multiple of four bytes. Alignment is everything ๐งฑ
ChangeAccessControl
ChangeAccessControl
mode: {Enable, Disable}
Errors: Value, Access
This enables or disables the use of the access control list at connection setups ๐
The client must reside on the same host as the server, and/or have been granted permission in the initial authorization at connection setup. Same rule, still iconic ๐
ChangeCloseDownMode
ChangeCloseDownMode
mode: {Destroy, RetainPermanent, RetainTemporary}
Errors: Value
This defines what happens to the client's resources when the connection closes ๐ซถ A connection starts in Destroy mode. The meaning of the close-down mode is described in Section 11 ๐
11. Connection Close โจ
What happens at connection close:
All event selections the client made get thrown out ๐๏ธ. If the client has the pointer actively grabbed, an UngrabPointer gets performed. If it has the keyboard actively grabbed, an UngrabKeyboard gets performed. All of its passive grabs get released ๐ฆ. If the client has the server grabbed, UngrabServer gets performed. If the close-down mode (see ChangeCloseDownMode) is RetainPermanent or RetainTemporary, then every resource the client allocated (colormap entries included) gets marked as "permanent" or "temporary" respectively. Other clients can still explicitly destroy them, though ๐. If the mode is Destroy, then all of the client's resources get destroyed, as described below ๐.
What happens when a client's resources are destroyed:
For each window in the client's save-set, if the window was created by the client, that save-set window gets reparented to the closest ancestor where the save-set window is not an inferior of a window the client created ๐ง. If the save-set window is unmapped, a MapWindow request gets performed on it. After save-set processing, all windows the client created are destroyed. For each non-window resource the client created, the matching Free request gets performed. Every color and colormap entry the client allocated gets freed ๐จ.
What happens when the last connection to a server closes:
A server cycles between having no connections and having some. Every time it hits the "no connections" state, it "resets" itself, like it just got started up ๐. First it destroys all the lingering resources from clients that left in RetainPermanent or RetainTemporary mode. The reset also covers deleting all atoms except the predefined ones, deleting all properties on all root windows, and resetting all device maps and attributes (key click, bell volume, acceleration). It also resets the access control list, restores the standard root tiles and cursors, restores the default font path, and puts the input focus back to PointerRoot ๐ธ.
12. Events ๐ฆ
When a button is pressed with the pointer in some window W, and no active pointer grab is in progress, the server searches the ancestors of W from the root down, looking for a passive grab to activate ๐. If there's no matching passive grab on that button, an active grab starts automatically for the client receiving the event, and the last-pointer-grab time gets set to the current server time โฐ. That's basically the same as a GrabButton with these arguments:
event-window: the event window
event-mask: the client's selected events on the event window
pointer-mode and keyboard-mode: Asynchronous
owner-events: True if the client has OwnerGrabButton selected
on the event window, else False
confine-to: None
cursor: None
The grab ends automatically once all the buttons are released. UngrabPointer and ChangeActiveGrab can both be used to modify the active grab ๐ .
KeyPress, KeyRelease, ButtonPress, ButtonRelease, and MotionNotify
These events all share the same format ๐:
root, event: WINDOW
child: WINDOW or None
same-screen: BOOL
root-x, root-y, event-x, event-y: INT16
detail: <see below>
state: SETofKEYBUTMASK
time: TIMESTAMP
They get generated when a key or button changes state, or when the pointer moves. The "source" is the window the pointer is in. The window the event is normally reported with respect to is found by climbing the hierarchy, starting at the source window, until you hit the first window where any client has selected interest in the event. This only works as long as no window in between blocks the event type by including it in its do-not-propagate-mask ๐ซ. Active grabs and the focus window can change which window the event is actually reported on. The window the event gets reported with respect to is called the "event" window ๐.
Root is the root window of the "source" window. root-x and root-y are the pointer coordinates relative to root's origin at the time of the event. Event is the "event" window. If the event window is on the same screen as root, then event-x and event-y are the pointer coordinates relative to the event window's origin. If it isn't, then event-x and event-y are zero ๐. If the source window is an inferior of the event window, then child gets set to the child of the event window that is an ancestor of the source window. The state component tells you the state of the buttons and modifier keys right before the event. The detail component changes depending on the event type:
KeyPress, KeyRelease: KEYCODE
ButtonPress, ButtonRelease: BUTTON
MotionNotify: {Normal, Hint}
MotionNotify events only get generated when the motion begins and ends in the window. The granularity of motion events isn't guaranteed, BUT a client that selects for motion events is guaranteed at least one event when the pointer moves and comes to rest ๐. Selecting PointerMotion gets you events no matter what state the pointer buttons are in. If you select some subset of Button[1-5]Motion instead, you only get MotionNotify events when one or more of the specified buttons are pressed. Selecting ButtonMotion gets you MotionNotify events only when at least one button is pressed. The event is always of type MotionNotify, no matter which selection you made ๐ซถ. If PointerMotionHint is selected, the server is free to send only one MotionNotify event (with detail Hint) to the client for the event window. It can keep doing that until the key or button state changes, the pointer leaves the event window, or the client issues a QueryPointer or GetMotionEvents request ๐ญ.
EnterNotify and LeaveNotify
These two share a format too ๐:
root, event: WINDOW
child: WINDOW or None
same-screen: BOOL
root-x, root-y, event-x, event-y: INT16
mode: {Normal, Grab, Ungrab}
detail: {Ancestor, Virtual, Inferior, Nonlinear,
NonlinearVirtual}
focus: BOOL
state: SETofKEYBUTMASK
time: TIMESTAMP
If pointer motion moves the pointer into a different window than before, you get EnterNotify and LeaveNotify events instead of a MotionNotify event ๐. Only clients that select EnterWindow on a window receive EnterNotify events, and only clients that select LeaveNotify receive LeaveNotify events. The pointer position in the event is always the "final" position, never the "initial" one ๐. In a LeaveNotify event, if a child of the event window contains the "initial" pointer position, then child is set to that child. Otherwise it's None. For an EnterNotify event, if a child of the event window contains the "final" pointer position, then child is set to that child. Otherwise it's None. If the event window is the focus window or an inferior of the focus window, then focus is True. Otherwise focus is False ๐ฏ.
Normal pointer motion events have mode Normal. Pseudo-motion events when a grab activates have mode Grab. Pseudo-motion events when a grab deactivates have mode Ungrab ๐ธ.
Normal events get generated like this:
- When the pointer moves from window A to window B, and A is an inferior of B:
- LeaveNotify with detail Ancestor is generated on A
- LeaveNotify with detail Virtual is generated on each window between A and B exclusive (in that order)
- EnterNotify with detail Inferior is generated on B
- When the pointer moves from window A to window B, and B is an inferior of A:
- LeaveNotify with detail Inferior is generated on A
- EnterNotify with detail Virtual is generated on each window between A and B exclusive (in that order)
- EnterNotify with detail Ancestor is generated on B
- When the pointer moves from window A to window B, with window C being their least common ancestor:
- LeaveNotify with detail Nonlinear is generated on A
- LeaveNotify with detail NonlinearVirtual is generated on each window between A and C exclusive (in that order)
- EnterNotify with detail NonlinearVirtual is generated on each window between C and B exclusive (in that order)
- EnterNotify with detail Nonlinear is generated on B
- When the pointer moves from window A to window B, on different screens:
- LeaveNotify with detail Nonlinear is generated on A
- LeaveNotify with detail NonlinearVirtual is generated on each window above A up to and including its root (in order)
- EnterNotify with detail NonlinearVirtual is generated on each window from B's root down to but not including B (in order)
- EnterNotify with detail Nonlinear is generated on B
- When a pointer grab activates (but after any initial warp into a confine-to window), with G the grab-window for the grab and P the window the pointer is in:
- EnterNotify and LeaveNotify events with mode Grab are generated (as for Normal above) as if the pointer were to suddenly warp from its current position in P to some position in G. However, the pointer does not warp, and the pointer position is used as both the "initial" and "final" positions for the events ๐ช.
- When a pointer grab deactivates, with G the grab-window for the grab and P the window the pointer is in:
Okay so the Ungrab version of EnterNotify and LeaveNotify is basically the same energy as Normal, bestie ๐ฆ. They get generated as if the pointer suddenly warped from some spot in G over to where it is now, in P. But it doesn't actually warp ๐ . The current pointer position just gets used as both the "initial" and "final" positions for the events โจ
FocusIn and FocusOut ๐
These two are the focus-change drama queens ๐ They're generated whenever the input focus changes, and they get reported to clients selecting FocusChange on the window. Here's the full spec:
FocusIn
and
FocusOut
event: WINDOW
mode: {Normal, WhileGrabbed, Grab, Ungrab}
detail: {Ancestor, Virtual, Inferior, Nonlinear,
NonlinearVirtual, Pointer, PointerRoot, None}
So the mode depends on what caused it ๐ SetInputFocus with the keyboard not grabbed gives mode Normal. SetInputFocus with the keyboard grabbed gives WhileGrabbed. A keyboard grab activating gives Grab, and a keyboard grab deactivating gives Ungrab.
Normal and WhileGrabbed events go like this ๐ธ
When focus moves from window A to window B, A is an inferior of B, and the pointer is in window P:
FocusOut with detail Ancestor is generated on A
FocusOut with detail Virtual is generated on each window
between A and B exclusive (in that order)
FocusIn with detail Inferior is generated on B
If P is an inferior of B, but P is not A or an inferior of A
or an ancestor of A, FocusIn with detail Pointer is
generated on each window below B down to and
including P (in order)
When focus moves from window A to window B, B is an inferior of A, and the pointer is in window P:
If P is an inferior of A, but P is not A or an inferior of B
or an ancestor of B, FocusOut with detail Pointer is
generated on each window from P up to but not
including A (in order)
FocusOut with detail Inferior is generated on A
FocusIn with detail Virtual is generated on each window
between A and B exclusive (in that order)
FocusIn with detail Ancestor is generated on B
When focus moves from window A to window B, with window C being their least common ancestor, and the pointer is in window P:
If P is an inferior of A, FocusOut with detail Pointer is
generated on each window from P up to but not
including A (in order)
FocusOut with detail Nonlinear is generated on A
FocusOut with detail NonlinearVirtual is generated on each
window between A and C exclusive (in that order)
FocusIn with detail NonlinearVirtual is generated on each
window between C and B exclusive (in that order)
FocusIn with detail Nonlinear is generated on B
If P is an inferior of B, FocusIn with detail Pointer is
generated on each window below B down to and
including P (in order)
When focus moves from window A to window B, on different screens, and the pointer is in window P:
If P is an inferior of A, FocusOut with detail Pointer is
generated on each window from P up to but not
including A (in order)
FocusOut with detail Nonlinear is generated on A
FocusOut with detail NonlinearVirtual is generated on each
window above A up to and including its root (in
order)
FocusIn with detail NonlinearVirtual is generated on each
window from B's root down to but not including B
(in order)
FocusIn with detail Nonlinear is generated on B
If P is an inferior of B, FocusIn with detail Pointer is
generated on each window below B down to and
including P (in order)
When focus moves from window A to PointerRoot (or None):
If P is an inferior of A, FocusOut with detail Pointer is
generated on each window from P up to but not
including A (in order)
FocusOut with detail Nonlinear is generated on A
FocusOut with detail NonlinearVirtual is generated on each
window above A up to and including its root (in
order)
FocusIn with detail PointerRoot (or None) is generated on
all root windows
When focus moves from PointerRoot (or None) to window A:
FocusOut with detail PointerRoot (or None) is generated on
all root windows
FocusIn with detail NonlinearVirtual is generated on each
window from A's root down to but not including A
(in order)
FocusIn with detail Nonlinear is generated on A
If P is an inferior of A, FocusIn with detail Pointer is
generated on each window below A down to and
including P (in order)
When focus moves from PointerRoot to None (or vice versa):
FocusOut with detail PointerRoot (or None) is generated on
all root windows
FocusIn with detail None (or PointerRoot) is generated on
all root windows
When a keyboard grab activates, with G the grab-window for the grab and F the current focus, FocusIn and FocusOut events with mode Grab are generated (as for Normal above) as if the focus were to change from F to G.
When a keyboard grab deactivates, with G the grab-window for the grab and F the current focus, FocusIn and FocusOut events with mode Ungrab are generated (as for Normal above) as if the focus were to change from G to F.
KeymapNotify ๐
The keys field is a LISTofCARD8, and it's a bit vector, same as what you get from QueryKeymap ๐ It's reported to clients selecting KeymapState on a window, and it's generated immediately after every EnterNotify and FocusIn.
KeymapNotify
keys: LISTofCARD8
Expose ๐ท
Expose
window: WINDOW
x, y, width, height: CARD16
last-in-series: BOOL
Expose is reported to clients selecting Exposure on the window. It's possibly generated when a region of the window becomes viewable, but it might only be generated when a region becomes visible, so don't count on one or the other ๐ All the regions exposed by a given "action" are guaranteed to be reported contiguously. If last-in-series is False, another exposure follows.
The x and y coordinates are relative to the drawable's origin, and they specify the upper left corner of a rectangle. The width and height give the extent of that rectangle.
Expose events are never generated on InputOnly windows ๐ซ
GraphicsExposure ๐ฆ
GraphicsExposure
drawable: DRAWABLE
x, y, width, height: CARD16
last-in-series: BOOL
major-opcode: CARD8
minor-opcode: CARD16
GraphicsExposure is reported to clients selecting graphics-exposures in a graphics context. It's generated when a destination region couldn't be computed because the source region was obscured or out of bounds. All the regions exposed by a given graphics request are guaranteed to be reported contiguously. If last-in-series is False, another exposure follows.
The x and y coordinates are relative to the drawable's origin, and they specify the upper left corner of a rectangle. The width and height give the extent of that rectangle.
The major and minor opcodes tell you which graphics request was used. For the core protocol, major-opcode is always CopyArea or CopyPlane, and minor-opcode is always zero.
NoExposure ๐ง
NoExposure
drawable: DRAWABLE
major-opcode: CARD8
minor-opcode: CARD16
NoExposure is reported to clients selecting graphics-exposures in a graphics context. It's generated when a graphics request that might produce GraphicsExposure events doesn't produce any. The drawable is the destination used for that graphics request.
The major and minor opcodes tell you which graphics request was used. For the core protocol, major-opcode is always CopyArea or CopyPlane, and minor-opcode is always zero.
VisibilityNotify ๐
VisibilityNotify
window: WINDOW
state: {Unobscured, PartiallyObscured, FullyObscured}
VisibilityNotify is reported to clients selecting VisibilityChange on the window. The window's state gets worked out while ignoring all of its subwindows, so the subwindows don't count ๐
Here's how the state changes trigger events โจ When a window goes from partially or fully obscured, or not viewable, to viewable and completely unobscured, you get an event with Unobscured. When a window goes from (a) viewable and completely unobscured, or (b) not viewable, to viewable and partially obscured, you get an event with PartiallyObscured. When a window goes from (a) viewable and completely unobscured, (b) viewable and partially obscured, or (c) not viewable, to viewable and fully obscured, you get an event with FullyObscured.
VisibilityNotify events are never generated on InputOnly windows ๐ซถ
CreateNotify ๐
CreateNotify
parent, window: WINDOW
x, y: INT16
width, height, border-width: CARD16
override-redirect: BOOL
CreateNotify is reported to clients selecting SubstructureNotify on the parent. It's generated when the window is created, and its arguments are the same as in the CreateWindow request ๐ธ
DestroyNotify ๐
DestroyNotify
event, window: WINDOW
DestroyNotify is reported to clients selecting StructureNotify on the window, and to clients selecting SubstructureNotify on the parent. It's generated when the window is destroyed. "Event" is the window the event was generated on, and "window" is the window that got destroyed ๐ญ
UnmapNotify ๐
UnmapNotify
event, window: WINDOW
from-configure: BOOL
UnmapNotify is reported to clients selecting StructureNotify on the window, and to clients selecting SubstructureNotify on the parent. It's generated when the window changes state from mapped to unmapped. "Event" is the window the event was generated on, and "window" is the window that got unmapped. The from-configure flag is True if the event was generated because the window's parent was resized while the window itself had a win-gravity of Unmap โจ
MapNotify ๐ฆ
MapNotify
event, window: WINDOW
override-redirect: BOOL
MapNotify is reported to clients selecting StructureNotify on the window, and to clients selecting SubstructureNotify on the parent. It's generated when the window changes state from unmapped to mapped. "Event" is the window the event was generated on, and "window" is the window that got mapped. The override-redirect flag comes from the window's attribute ๐
MapRequest ๐
MapRequest
parent, window: WINDOW
MapRequest is reported to the client selecting SubstructureRedirect on the parent. It's generated when a MapWindow request is issued on an unmapped window whose override-redirect attribute is False ๐
ReparentNotify ๐ฆ
event, window: WINDOW
x, y: INT16
override-redirect: BOOL
Okay so, this one's the "your window just moved house" event ๐ โจ Clients who select SubstructureNotify on the old or new parent get it, and so do clients who select StructureNotify on the window itself. The event field is the window where the event got generated, window is the one that's been re-rooted, and parent tells you the new parent. The x and y values are measured from the new parent's origin and mark the upper left outer corner of the window. And override-redirect just copies the window's attribute ๐
ConfigureNotify ๐ธ
event, window: WINDOW
x, y: INT16
width, height, border-width: CARD16
above-sibling: WINDOW or None
override-redirect: BOOL
This is the main "something about my size or stacking changed" event ๐ It goes to clients selecting StructureNotify on the window and clients selecting SubstructureNotify on the parent. The catch is that it only fires when a ConfigureWindow request actually changes the window's state. Like, if nothing really changed, no event, periodt. event is where it was generated, and window is the one that got changed. If above-sibling is None, the window is sitting at the bottom of the stack with respect to its siblings. Otherwise it's chilling right on top of the sibling you named โญ And yep, override-redirect comes from the window's attribute.
GravityNotify ๐ง
event, window: WINDOW
x, y: INT16
This one's lowkey dramatic ๐ญ It shows up when a window gets moved because its parent changed size. It's reported to clients selecting SubstructureNotify on the parent, and to clients selecting StructureNotify on the window. event is where it was generated, and window is the one that got moved.
ResizeRequest ๐
window: WINDOW
width, height: CARD16
Think of this as the window politely raising its hand and saying "hey, someone's trying to resize me" โ๐ It's reported to the one client selecting ResizeRedirect on the window. It's generated when a ConfigureWindow request from some other client tries to change the window's size. Also, width and height are the inside size, so the border doesn't count ๐
ConfigureRequest ๐
parent, window: WINDOW
x, y: INT16
width, height, border-width: CARD16
above-sibling: WINDOW or None
This is the bossy cousin of ConfigureNotify ๐
It goes to the client selecting SubstructureRedirect on the parent, and it's generated when some other client issues a ConfigureWindow request on the window. The geometry is whatever the request says. The above-sibling is the sibling the window should end up directly on top of, and if it's None, the window should go on the bottom ๐
CirculateNotify โจ
event, window: WINDOW
place: {Top, Bottom}
Okay this is the "I've been restacked" event ๐ฅ It's reported to clients selecting StructureNotify on the window, and to clients selecting SubstructureNotify on the parent. It's generated when the window actually gets restacked because of a CirculateWindow request. event is where it was generated, and window is the one that got restacked. If place is Top, the window is now on top of all its siblings. Otherwise it's below all of them ๐ฆ
CirculateRequest ๐ท
parent, window: WINDOW
place: {Top, Bottom}
The bossy version of CirculateNotify ๐ It's reported to the client selecting SubstructureRedirect on the parent, and it's generated when a CirculateWindow request gets issued on the parent and a window actually needs to be restacked. window tells you which window to restack, and place tells you where it should go in the stacking order ๐
PropertyNotify ๐ฅ
window: WINDOW
atom: ATOM
state: {NewValue, Deleted}
time: TIMESTAMP
Clients selecting PropertyChange on the window get this one whenever a property of that window changes ๐ง The time is the server time of the change, so you always know exactly when the drama happened โฐ
SelectionClear ๐
owner: WINDOW
selection: ATOM
time: TIMESTAMP
This goes to the current owner of a selection, and it's generated on the window that's losing ownership when a new owner is being defined. Basically you're getting kicked off the throne ๐๐ญ The time is the last-change time recorded for the selection.
SelectionRequest ๐ซถ
owner: WINDOW
selection: ATOM
target: ATOM
property: ATOM or None
requestor: WINDOW
time: TIMESTAMP or CurrentTime
This goes to the owner of a selection, and it gets generated when a client issues a ConvertSelection request. The arguments are the same as the ones in that request โจ
Now the owner should convert the selection based on the target type. If a property is specified, the owner should store the result as that property on the requestor window, and then send a SelectionNotify event to the requestor using SendEvent. And if the selection can't be converted the way the requestor asked for, the owner should send a SelectionNotify with property set to None. Not me being dramatic, it's just how it works ๐
SelectionNotify ๐
requestor: WINDOW
selection, target: ATOM
property: ATOM or None
time: TIMESTAMP or CurrentTime
This one's only generated by clients using SendEvent ๐ The owner of a selection should send it to a requestor in two situations: when a selection has been converted and stored as a property, or when a conversion couldn't be performed, which is shown by property being None ๐
ColormapNotify ๐ธ
window: WINDOW
colormap: COLORMAP or None
new: BOOL
state: {Installed, Uninstalled}
Reported to clients selecting ColormapChange on the window โจ It's generated with new set to True when the window's colormap attribute changes. It's generated with new set to False when a colormap gets installed or uninstalled. Either way, state tells you whether the colormap is currently installed ๐
ClientMessage ๐ฆ
window: WINDOW
type: ATOM
format: {8, 16, 32}
data: LISTofINT8 or LISTofINT16 or LISTofINT32
This one's only generated by clients using SendEvent ๐ The type says how the receiving client should interpret the data. The server doesn't add any interpretation of its own, not on the type or the data ๐ง The format says whether the data should be read as a list of 8-bit, 16-bit, or 32-bit quantities, so the server can byte-swap correctly when it needs to ๐ The data always has either 20 8-bit values, 10 16-bit values, or 5 32-bit values, though some message types won't use all of them ๐ซถ
13. FLOW CONTROL AND CONCURRENCY โจ
Whenever the server is writing to a connection, it's totally fine for the server to stop reading from that connection. But if the writing would block, the server must keep servicing the other connections ๐ The server is not required to buffer more than a single request per connection at a time ๐ฆ
For a given connection, a client can block while reading from it. But it should undertake to read events and errors whenever writing would block ๐ซถ If a client doesn't follow this rule, it could end up with a deadlocked connection. Deadlock is probably unlikely, though, unless the transport layer has very little buffering, or the client sends a huge pile of requests without ever reading replies or checking for errors and events ๐ญ
Warning, bestie: if you flood the server with requests and never read anything back, you're basically setting up your own deadlock. Read your replies and events! ๐
If a server is implemented with internal concurrency, the overall effect must be as if each individual request was executed to completion in some serial order. Requests from a given connection must be executed in delivery order, so the total execution order is a shuffle of the individual streams ๐ The "execution" of a request includes validating all the arguments, collecting all the data for any reply, and generating (and queueing) all required events. It does not include actually transmitting the reply and the events ๐ฎ
On top of that, the effect of any other "cause," like a grab activating or the pointer moving, that can generate multiple events must effectively generate (and queue) all of its required events indivisibly with respect to all other causes and requests ๐โจ
๐ฆ where this page comes from
- ietf: rfc1013 1987-06
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms