Documentation
¶
Overview ¶
Package x11 is a from-scratch, pure-Go (CGO-free, no non-stdlib dependency) implementation of the pieces of the X Window System protocol, version 11.0, that every X client needs before it can say anything of its own: the wire encoder and decoder, the .Xauthority parser, the connection-setup exchange and its reply, the anonymous shared-memory segment MIT-SHM attaches, and the unix-domain transport that hands the server a descriptor over SCM_RIGHTS.
It deliberately stops there. It has no connection type, no event loop and no opinion about what a client does with a connection: a screen capture and a window toolkit want very different request/reply machines on top of the same bytes, and this package is the bytes. A consumer builds its own connection type over Handshake's result.
The one exception is Monitors, which enumerates the displays laid out inside an X screen over RANDR and XINERAMA. It is here because the answer does not depend on what the client is FOR — a capture and a toolkit want the same rectangles — and it does not need a connection type to give it: it asks through Requester, two methods any request/reply machine already has.
Everything above the socket is transport-agnostic: Handshake takes any io.ReadWriteCloser, so the whole codec is exercisable in-process over a net.Pipe against a scripted fake server. That is what lets the wire format be tested to 100% on darwin and on windows, with no X server anywhere — a protocol bug is caught on every platform, not only on the one that can run an X server.
Index ¶
- Constants
- func AuthFilePath() string
- func DialUnix(path string) (io.ReadWriteCloser, error)
- func EDIDModelName(edid []byte) string
- func EncodeAuthEntry(e AuthEntry) []byte
- func LoadAuthCookie(authFile, host, display string) (name string, data []byte, err error)
- func Pad4(n int) int
- func Padding(n int) int
- func ReadFull(r io.Reader, b []byte) error
- func TrimNul(b []byte) string
- func WrapUnix(c *net.UnixConn) io.ReadWriteCloser
- type AuthEntry
- type ByteOrder
- type Decoder
- func (d *Decoder) Get8() byte
- func (d *Decoder) Get16() uint16
- func (d *Decoder) Get16s() int16
- func (d *Decoder) Get32() uint32
- func (d *Decoder) GetBytes(n int) []byte
- func (d *Decoder) GetString(n int) string
- func (d *Decoder) OK() bool
- func (d *Decoder) Order() ByteOrder
- func (d *Decoder) Skip(n int)
- type Depth
- type Encoder
- func (e *Encoder) Bytes() []byte
- func (e *Encoder) Order() ByteOrder
- func (e *Encoder) Pad(n int)
- func (e *Encoder) Put8(v byte)
- func (e *Encoder) Put16(v uint16)
- func (e *Encoder) Put32(v uint32)
- func (e *Encoder) PutBytes(b []byte)
- func (e *Encoder) PutString(s string)
- func (e *Encoder) Skip(n int)
- type FDSender
- type Format
- type Monitor
- type Randr
- type Requester
- type Screen
- type Segment
- type Setup
- type SetupError
- type VisualType
- type Waiter
- type Xinerama
Constants ¶
const ( RandrName = "RANDR" XineramaName = "XINERAMA" )
Extension names, as QueryExtension spells them.
const ( VisualStaticGray = 0 VisualGrayScale = 1 VisualStaticColor = 2 VisualPseudoColor = 3 VisualTrueColor = 4 VisualDirectColor = 5 )
Visual classes. Only TrueColor and DirectColor decompose a pixel through the masks; the palette classes would need the colormap read back, which no display built this millennium presents.
const ( ImageOrderLSB = 0 ImageOrderMSB = 1 )
Image byte-order values reported by Setup.ImageByteOrder.
const ( OrderLSB = 'l' OrderMSB = 'B' )
The two byte-order sentinels sent as the first byte of the setup request: 'l' little-endian (LSB first), 'B' big-endian (MSB first).
const ( FamilyInternet = 0 FamilyLocal = 256 FamilyWild = 65535 FamilyInternet6 = 6 )
Xauthority address families (X11/Xauth.h). Lengths in the file are always big-endian, independent of the machine.
const AtomNone = 0
AtomNone is the atom that names nothing (X11/Xatom.h).
const AuthMITCookie = "MIT-MAGIC-COOKIE-1"
AuthMITCookie is the MIT-MAGIC-COOKIE-1 authorization protocol name sent in the setup request when a matching cookie is found in the authority file.
Variables ¶
This section is empty.
Functions ¶
func AuthFilePath ¶
func AuthFilePath() string
AuthFilePath returns the authority file to consult: $XAUTHORITY if set, otherwise $HOME/.Xauthority, otherwise "".
func DialUnix ¶
func DialUnix(path string) (io.ReadWriteCloser, error)
DialUnix connects to the X server's unix-domain socket at path and returns it wrapped by WrapUnix.
func EDIDModelName ¶ added in v0.2.0
EDIDModelName returns the display product name carried by a base EDID block — the string a user recognises, "DELL U2720Q" rather than "DP-2" — or "" if the blob is not an EDID or carries no name descriptor.
The name is a 13-byte field terminated by a line feed and padded with spaces, so both the terminator and the padding are trimmed. Only the base block is read: the name descriptor is required to be there, and an extension block cannot move it.
func EncodeAuthEntry ¶
EncodeAuthEntry serializes an AuthEntry back to the Xauthority file format. It is the exact inverse of ParseXauthority, and is exported because the only honest way to test a client against a cookie is to write one: a test builds the fixture with this, and a round-trip through the parser proves the two agree.
func LoadAuthCookie ¶
LoadAuthCookie resolves the MIT-MAGIC-COOKIE-1 for (host, display) from the given authority file. A missing file, or no match, is not an error: it returns an empty name and data so the caller falls back to an unauthenticated setup, exactly as Xlib does. host defaults to the machine hostname when empty.
func Pad4 ¶
Pad4 returns n rounded up to the next multiple of four. X11 pads every variable-length field to a four-byte boundary.
func ReadFull ¶
ReadFull reads exactly len(b) bytes or returns the first error. It is kept here so the codec's callers have one spelling of "read a whole packet".
Types ¶
type AuthEntry ¶
type AuthEntry struct {
Family uint16
Address []byte
Number string // display number as ASCII; "" is a wildcard
Name string // authorization protocol name
Data []byte // the cookie
}
AuthEntry is one record parsed from an Xauthority file.
type ByteOrder ¶
ByteOrder is the wire byte order negotiated at connection setup. X11 lets the client pick; the server then speaks the client's order for the session.
type Decoder ¶
type Decoder struct {
// contains filtered or unexported fields
}
Decoder reads a fixed-order byte slice. Every read is bounds-checked; once the Decoder is not OK it stays that way, so a truncated buffer degrades to a clean error at the call site rather than a panic.
func NewDecoder ¶
NewDecoder wraps b for reading in the given order.
func (*Decoder) OK ¶
OK reports whether every read so far stayed inside the buffer. A parser checks it once, at the end, rather than after every field.
type Depth ¶
type Depth struct {
Depth uint8
Visuals []VisualType
}
Depth groups the visuals available at a given colour depth.
type Encoder ¶
type Encoder struct {
// contains filtered or unexported fields
}
Encoder builds a request body in a chosen byte order. Every multi-byte integer goes through the negotiated ByteOrder, so the same code emits a correct little- or big-endian stream.
func NewEncoder ¶
NewEncoder starts an Encoder in the given order.
func (*Encoder) Bytes ¶
Bytes returns the bytes written so far. The slice aliases the Encoder's own buffer, so a caller that keeps it past the next write must copy it.
type FDSender ¶
type FDSender interface {
// SendFD writes one already-framed request with fd attached as a single
// SCM_RIGHTS control message.
SendFD(msg []byte, fd int) error
}
FDSender is implemented by a transport that can pass a file descriptor alongside a request over the same socket (a unix-domain stream, via SCM_RIGHTS). The transport WrapUnix returns implements it; an in-process net.Pipe used by a test does not, so a client's MIT-SHM fd-passing path degrades to the plain socket path when it is absent.
It is an interface rather than a concrete type because that is what keeps the connection above it transport-agnostic: a client type-asserts on this and never on a socket.
type Format ¶
Format is one entry of the server's pixmap-format list: for a given colour depth it fixes the bits-per-pixel and the scanline padding a ZPixmap image of that depth uses on the wire. It is what turns a width into a STRIDE.
type Monitor ¶ added in v0.2.0
type Monitor struct {
// Name is the RANDR monitor name, which is normally the CONNECTOR —
// "HDMI-1", "DP-2", "eDP-1". It is stable and it is what xrandr prints,
// but it does not say what is plugged in. It is "" from XINERAMA, which
// carries no names at all.
Name string
// Model is the display's own product name, read out of its EDID: "VITURE
// Beast", "DELL U2720Q". It is what a user recognises, and the only field
// that can tell two identical connectors apart by what is on the end of
// them — so an application that identifies a headset by name wants this
// one. It is "" when the output publishes no EDID (a virtual server such
// as Xvfb, or a driver that does not export the property).
Model string
// NameAtom is the atom Name was resolved from, kept because a caller that
// re-queries can compare atoms without a round trip.
NameAtom uint32
// Primary marks the monitor the desktop treats as its origin.
Primary bool
// Automatic marks a monitor RANDR synthesised from the outputs rather than
// one configured by hand with `xrandr --setmonitor`.
Automatic bool
// X, Y, Width, Height are the monitor's rectangle inside the screen's
// coordinate space, top-left origin, Y growing downwards.
X, Y int16
Width, Height uint16
// WidthMM, HeightMM are the physical size, which is what a DPI is computed
// from. Both are 0 on a display that reports none.
WidthMM uint32
HeightMM uint32
// Outputs are the RANDR output ids driving this monitor. Empty from
// XINERAMA and from the whole-screen fallback.
Outputs []uint32
}
Monitor is one physical output's rectangle inside an X screen.
func Monitors ¶ added in v0.2.0
Monitors lists the monitors of screen sc, trying RANDR 1.5 first — with the displays' own model names filled in where they publish an EDID — then XINERAMA, and falling back to the whole screen as a single nameless monitor.
It never returns an empty list without an error: a screen always has at least itself. An extension that is absent, that refuses, or that answers nothing is not an error either; it just means the next way of asking gets a turn. The one error is a screen that does not exist.
func (Monitor) DisplayName ¶ added in v0.2.0
DisplayName is the best human-readable name for the monitor: the EDID model when the display published one, the connector otherwise. It is what to show a user choosing an output.
type Randr ¶ added in v0.2.0
Randr is a queried RANDR handle: the extension's major opcode plus the version the server agreed to speak.
func QueryRandr ¶ added in v0.2.0
QueryRandr queries RANDR and negotiates version 1.5, which is the one that carries RRGetMonitors. It returns (nil, nil) when the server has no RANDR, because "the server does not offer it" is an answer, not a failure.
func (*Randr) GetMonitors ¶ added in v0.2.0
GetMonitors lists the monitors of the screen rooted at root. Names are resolved from their atoms, so a monitor comes back as "HDMI-1" rather than as a number; models are NOT read here, because that costs one round trip per output and a caller that only wants rectangles should not pay for it. Use Randr.ResolveModels or Monitors for those.
func (*Randr) HasMonitors ¶ added in v0.2.0
HasMonitors reports whether the negotiated version carries RRGetMonitors, which arrived in RANDR 1.5.
func (*Randr) OutputProperty ¶ added in v0.2.0
func (rr *Randr) OutputProperty(output, property uint32, maxWords uint32) (format byte, value []byte, err error)
OutputProperty reads up to maxWords 32-bit words of a RANDR output property. A property the output does not have comes back with format 0 and no value, which is not an error.
func (*Randr) ResolveModels ¶ added in v0.2.0
ResolveModels fills in the Model of each monitor from its outputs' EDID.
It is best-effort by construction: an output with no EDID property, a server that refuses the request, and a blob that is not an EDID all leave Model empty rather than failing the enumeration — the rectangles are still right, and a caller that wanted them should not lose them because a display declines to introduce itself. It reports an error only when the EDID atom itself cannot be looked up, which means the connection is in trouble.
type Requester ¶ added in v0.2.0
type Requester interface {
// Order returns the byte order negotiated at connection setup.
Order() ByteOrder
// Request sends one request and returns its reply. op names the request
// for error messages; opcode is the major opcode — a core one, or an
// extension's as QueryExtension reported it; data is the byte the protocol
// packs into the request header. body must already be 4-byte padded.
//
// The reply is the 32-byte fixed part followed by whatever additional data
// the request carries, as one slice. An error reply comes back as an
// error, not as a packet.
Request(op string, opcode, data byte, body []byte) ([]byte, error)
}
Requester is what the monitor enumeration needs of a connection, and all it needs: the negotiated byte order, and the ability to send a request and be handed its reply.
This package deliberately owns no connection type (see the package comment), because a capture loop and an event pump want genuinely different request/reply machines over the same socket. Both of them can satisfy this, though, and that is what lets one enumeration serve both.
type Screen ¶
type Screen struct {
Root uint32
DefaultColmap uint32
WhitePixel uint32
BlackPixel uint32
Width uint16
Height uint16
WidthMM uint16
HeightMM uint16
RootVisual uint32
RootDepth uint8
Depths []Depth
}
Screen is one root screen: its root window, size, root visual and the allowed depths (each carrying its visuals).
func (*Screen) DepthOfVisual ¶
DepthOfVisual returns the colour depth the given visual lives at, and whether it was found.
func (*Screen) FindVisual ¶
func (sc *Screen) FindVisual(id uint32) (VisualType, bool)
FindVisual returns the VisualType with the given id on screen sc, and whether it was found.
func (*Screen) RootVisualType ¶
func (sc *Screen) RootVisualType() VisualType
RootVisualType returns the screen's root visual descriptor, falling back to a synthesized 24-bit TrueColor BGRX visual if the root visual id is somehow absent from the depth list (defensive; real servers always list it).
type Segment ¶
Segment is a mapped anonymous shared-memory region backing a MIT-SHM attachment: Data is the pixel store both peers see, FD is the descriptor handed to the X server by the extension's AttachFd request, and Seg is the resource id the server knows it by.
It is the memory, not the protocol. Which AttachFd/PutImage/GetImage requests a client sends over it is the client's business — a capture asks the server to WRITE into the segment, a window writes into it and asks the server to read — and those requests live in the consumer, not here.
The lifecycle is portable; the shared-memory syscalls themselves sit behind createAnonFile/mmapRegion/munmapRegion/closeFD, which are provided per platform. Off Linux there is no X server to attach to, so createAnonFile reports [ErrNoSharedMemory] and no segment is ever created.
func NewSegment ¶
NewSegment allocates and maps a shared-memory segment of size bytes and assigns it the resource id seg. The caller registers it with the server via the MIT-SHM AttachFd request and frees it with Segment.Close.
type Setup ¶
type Setup struct {
ProtoMajor uint16
ProtoMinor uint16
Release uint32
ResourceIDBase uint32
ResourceIDMask uint32
Vendor string
MaxRequestLen uint16 // in 4-byte units
ImageByteOrder uint8 // 0 = LSBFirst, 1 = MSBFirst
BitmapBitOrder uint8
BitmapUnit uint8
BitmapPad uint8
MinKeycode uint8
MaxKeycode uint8
Formats []Format
Screens []Screen
}
Setup is the parsed server connection-setup reply: everything a client needs to allocate resource IDs, pick a visual, size images correctly and map keycodes.
func Handshake ¶
func Handshake(rw io.ReadWriteCloser, order ByteOrder, authName string, authData []byte) (*Setup, error)
Handshake runs the client connection setup over rw: it sends the byte-order sentinel, protocol 11.0 and the authorization name and data, then reads and parses the server's reply. order selects the wire byte order; both are valid and the server adopts the client's choice for the whole session.
It returns only the parsed Setup. Framing requests and demultiplexing replies, errors and events is the caller's business, and the two things a client does with a connection — pump events, or push frames — want different machines over the same socket. What is common is everything up to this point, which is what this function is.
On return, rw is positioned exactly at the first byte the server sends after the setup reply, so the caller can start reading packets immediately. On error rw is left as it is: the caller closes it.
func (*Setup) FormatFor ¶
FormatFor returns the pixmap Format matching depth, and whether one exists. It is what sizes each pixel and pads each scanline of an image at that depth.
type SetupError ¶
type SetupError struct {
Reason string
Authenticate bool // the server asked for further authentication
}
SetupError is the connection-setup refusal: the server would not talk to us at all. Reason is the server's own wording, which for a missing or wrong cookie is "No protocol specified" or "Authorization required".
type VisualType ¶
type VisualType struct {
ID uint32
Class uint8
BitsPerRGB uint8
ColormapEnt uint16
RedMask uint32
GreenMask uint32
BlueMask uint32
}
VisualType describes a visual: its class and the RGB channel masks a TrueColor or DirectColor visual packs a pixel with. The masks are what convert a pixel to or from any other layout.
func (VisualType) Direct ¶
func (v VisualType) Direct() bool
Direct reports whether the visual's pixels decompose through its masks.
type Waiter ¶
type Waiter interface {
// WaitReadable reports whether the server sent anything within d. An
// implementation must not consume a partial packet: see the note on the
// unix transport for why a read deadline is not a substitute.
WaitReadable(d time.Duration) bool
}
Waiter is implemented by a transport that can say whether the server sent anything, without reading a whole packet. The transport WrapUnix returns implements it.
It exists because parts of the X11 protocol have no timeout of their own — a selection paste asks whoever owns the clipboard and waits for an event that arrives only if that owner is alive and still answering. A client that cannot bound that wait freezes.
type Xinerama ¶ added in v0.2.0
Xinerama is a queried XINERAMA handle.
func QueryXinerama ¶ added in v0.2.0
QueryXinerama queries XINERAMA. It returns (nil, nil) when the server has none.
func (*Xinerama) QueryScreens ¶ added in v0.2.0
QueryScreens lists the Xinerama screens, which are the same rectangles RANDR would call monitors, without names.