RFC 1013

X Window System Protocol, version 11: Alpha update April 1987

๐Ÿ“… published June 1987, from ietf

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 the confine-to window of an active pointer grab. If you try, the pointer only goes as far as the closest edge of that confine-to window ๐Ÿšง

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