wlturbo

package module
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 30, 2026 License: MIT Imports: 14 Imported by: 0

README

WLTurbo - Wayland Client Library for Go

A performance-focused Wayland client library that provides the foundational protocol implementation for building Wayland applications and libraries.

Overview

WLTurbo is a low-level Wayland client library with generated core and extension bindings. Applications and higher-level libraries use it to communicate with any compositor that advertises the required protocols.

Architecture

WLTurbo provides the foundational Wayland client infrastructure:

  • Core Protocol Objects: Display, Registry, Compositor, Surface, Seat, Region
  • Event Dispatching: Efficient routing of Wayland events to handlers
  • Connection Management: Unix socket communication with the compositor
  • Memory Management: Shared memory support via file descriptor passing

Bindings handle wire messages and object lifecycle. Applications own rendering, input interpretation, capability fallbacks and desktop policy. WLTurbo provides client bindings, not compositor-side server implementations.

Generated protocols

Packages under protocol/ cover:

Area Packages
Core and windows core, xdgshell, xdgdecoration, xdgactivation
Buffers and scaling linuxdmabuf, drmsyncobj, viewporter, fractionalscale
Presentation and color presentation, tearingcontrol, fifo, committiming, contenttype, alphamodifier, colormanagement, colorrepresentation, drmlease
Input cursorshape, tablet, textinput, relativepointer, pointerconstraints, pointerwarp, shortcutsinhibit, virtualkeyboard, inputmethod
Clipboard Core data-device, primaryselection, datacontrol (ext-data-control)
Desktop and outputs layershell, xdgoutput, outputmanagement, outputpower, workspace, foreigntoplevel (wlr), extforeigntoplevel, extsessionlock, kdeserverdecoration
Idle idleinhibit, idlenotify
Capture screencopy (wlr), imagecapturesource, imagecopycapture

Each package contains the upstream XML, generated Go bindings and a generation command. Protocol sources records pinned upstream revisions and checksums. A binding's presence does not imply that the compositor implements it.

After display.Roundtrip() discovers globals, call display.Registry().BindNegotiated(interfaceName, supportedVersion, proxy) to bind at the lesser of the advertised and supported versions. It returns the negotiated version; use errors.Is(err, wlturbo.ErrGlobalNotFound) to handle absent optional globals.

Features

  • Stream framing: messages split across or coalesced within socket reads are framed exactly; event bodies are not copied.
  • Descriptor ownership: received FDs belong to the event that declares them and are closed if a handler does not take them.
  • Object lifecycle: destroyed objects stay as zombies until delete_id, so events already in flight are dropped instead of failing the connection.
  • Version checks: generated requests newer than the bound object version return ErrVersionTooLow before anything is sent.
  • Allocation-free numeric paths: numeric generated requests without object creation or descriptors, and typed numeric events, do not allocate after warm-up; representative cases have allocation regression tests. Object creation, received strings and descriptors have separate allocation costs.

Quick Start

package main

import (
    "github.com/bnema/wlturbo/wl"
)

func main() {
    // Connect to Wayland display
    display, err := wl.Connect("")
    if err != nil {
        panic(err)
    }
    defer display.Close()

    // Basic event loop. Handlers run inside Dispatch and must not call
    // Dispatch or Roundtrip on the same display.
    for {
        if err := display.Dispatch(); err != nil {
            break
        }
    }
}

For higher-level device control, libwldevices-go builds on WLTurbo.

Development checks

GOWORK=off go generate ./...
GOWORK=off go test ./...
GOWORK=off go test -race ./...
GOWORK=off go vet ./...
GOWORK=off go test ./protocol -run '^$' -bench . -benchmem

Tests verify that checked-in bindings reproduce from the vendored XML. Socketpair tests exercise wire messages, versions, object lifecycle and file descriptors without a compositor.

For isolated integration tests, build NeferWL with its headless backend and pass its executable path:

env GOWORK=off WLTURBO_HEADLESS=/path/to/neferwl go test ./protocol -run Headless -v -count=1 -timeout=90s

The tests start a separate compositor with temporary runtime/config directories and no terminal or Xwayland. They do not use the running desktop session. Headless tests do not validate physical display timing, DRM leasing or hardware HDR output.

Requirements

  • Go 1.27+
  • Linux with Wayland compositor
  • Unix sockets support

License

The transport and scanner are MIT-licensed; see LICENSE. Vendored protocol XML and generated bindings retain upstream notices and terms. In particular, kdeserverdecoration is LGPL-2.1-or-later. See protocol sources, the generated file headers and the included LGPL text.

Contributing

Contributions welcome! Areas of focus:

  • Performance improvements
  • Additional protocol object support
  • Documentation and examples
  • Benchmark suite

Documentation

Overview

Package wlturbo is a Wayland client transport for Go: connection, framing, descriptor passing, object lifecycle and the bootstrap registry. Protocol bindings are generated into the protocol/ packages.

Index

Constants

View Source
const (
	// 32-bit formats
	FormatARGB8888 = 0
	FormatXRGB8888 = 1

	// 24-bit formats
	FormatRGB888 = 0x34324752 // 'RG24'
	FormatBGR888 = 0x34324742 // 'BG24'

	// 16-bit formats
	FormatRGB565   = 0x36314752 // 'RG16'
	FormatXRGB1555 = 0x35315258 // 'XR15'

	// 8-bit formats
	FormatY8 = 0x20203859 // 'Y8  '
)

Wayland pixel formats

View Source
const (
	// HeaderSize is the fixed size of a Wayland message header in bytes.
	HeaderSize = 8

	// DefaultMaxMessageSize bounds a single Wayland message, including its
	// header. Larger messages are rejected instead of allocating.
	DefaultMaxMessageSize = 1 << 20
)

Variables

View Source
var (
	// ErrMalformedFrame reports a message whose header violates the wire
	// protocol: too short, misaligned, or larger than the accepted maximum.
	ErrMalformedFrame = errors.New("malformed wayland frame")

	// ErrUnknownObject reports an event for an object this client never
	// created or bound.
	ErrUnknownObject = errors.New("wayland event for unknown object")

	// ErrUnknownOpcode reports an event opcode the target object cannot receive.
	ErrUnknownOpcode = errors.New("wayland event with unknown opcode")

	// ErrDisplayError reports that the compositor sent wl_display.error.
	ErrDisplayError = errors.New("wayland display error")
)

Sentinel errors reported by the transport. Wire-level failures wrap one of them so callers can classify with errors.Is, while ProtocolError carries the offending object, opcode and size.

View Source
var ErrGlobalNotFound = errors.New("wlturbo: global not announced")

ErrGlobalNotFound reports that the compositor does not announce a global.

View Source
var ErrVersionTooLow = errors.New("wlturbo: request needs a newer object version")

ErrVersionTooLow reports a request the bound object version does not have.

Functions

func CheckVersion added in v0.3.0

func CheckVersion(version, since uint32, request string) error

CheckVersion rejects a request introduced in version since when the object's known version is lower. An unknown version (0) is not checked.

func CloseSentFD added in v0.3.0

func CloseSentFD(fd int) error

CloseSentFD releases a descriptor after a complete successful Wayland send.

func CreateAnonymousFile

func CreateAnonymousFile(size int64) (fd int, err error)

CreateAnonymousFile creates an anonymous file for shared memory

func MapMemory

func MapMemory(fd int, size int) ([]byte, error)

MapMemory maps a file descriptor into memory

func UnmapMemory

func UnmapMemory(data []byte) error

UnmapMemory unmaps memory

Types

type Arg added in v0.4.0

type Arg struct {
	// contains filtered or unexported fields
}

Arg is one typed request argument. It carries native values (no interface boxing), so building and passing Args to Context.RequestArgs does not allocate. The zero Arg is invalid and makes the request fail before any write. Build Args with the ArgXxx constructors.

func ArgArray added in v0.4.0

func ArgArray(v []byte) Arg

ArgArray is an array argument.

func ArgFD added in v0.4.0

func ArgFD() Arg

ArgFD marks a file descriptor position. Descriptors travel out of band in Request.FDs, so it contributes no bytes to the message body.

func ArgFixed added in v0.4.0

func ArgFixed(v Fixed) Arg

ArgFixed is a fixed-point argument.

func ArgInt added in v0.4.0

func ArgInt(v int32) Arg

ArgInt is an int argument.

func ArgObject added in v0.4.0

func ArgObject(v Object) Arg

ArgObject is an object or new_id argument; a nil Object is sent as 0.

func ArgString added in v0.4.0

func ArgString(v string) Arg

ArgString is a string argument.

func ArgUint added in v0.4.0

func ArgUint(v uint32) Arg

ArgUint is a uint argument.

type BaseProxy

type BaseProxy struct {
	// contains filtered or unexported fields
}

BaseProxy provides base implementation for protocol objects

func (*BaseProxy) Context

func (p *BaseProxy) Context() *Context

Context returns the proxy's context

func (*BaseProxy) Dispatch

func (p *BaseProxy) Dispatch(event *Event)

Dispatch default implementation (does nothing)

func (*BaseProxy) ID

func (p *BaseProxy) ID() uint32

ID returns the proxy's object ID

func (*BaseProxy) SetContext

func (p *BaseProxy) SetContext(ctx *Context)

SetContext sets the proxy's context

func (*BaseProxy) SetID

func (p *BaseProxy) SetID(id uint32)

SetId sets the proxy's object ID

func (*BaseProxy) SetVersion added in v0.3.0

func (p *BaseProxy) SetVersion(v uint32)

SetVersion records the object's protocol version. Registry.Bind sets it for globals and generated requests copy it from parent to child.

func (*BaseProxy) Version added in v0.3.0

func (p *BaseProxy) Version() uint32

Version returns the protocol version the object was bound or created at, or 0 when it is unknown (for example after Registry.BindID).

type Context

type Context struct {
	// contains filtered or unexported fields
}

Context provides a compatibility layer for wl.Context

func NewContext

func NewContext(display *Display) *Context

NewContext creates a new context from a display

func (*Context) AllocateID

func (c *Context) AllocateID() uint32

AllocateID allocates a new object ID

func (*Context) CheckProxy added in v0.3.0

func (c *Context) CheckProxy(proxy Proxy) error

CheckProxy rejects stale and foreign proxies before any bytes are written.

func (*Context) Close

func (c *Context) Close() error

Close closes the context

func (*Context) Register

func (c *Context) Register(proxy Proxy)

Register registers a proxy object

func (*Context) Request added in v0.3.0

func (c *Context) Request(r Request, args ...interface{}) error

Request runs one request with its whole lifecycle: it checks the proxy and version, allocates and registers Child (inheriting the parent's version), sends, and then either closes the sent descriptors or, on error, unregisters Child and leaves the descriptors with the caller. Child must already have this context; it is passed in args at its wire position.

Every argument is boxed into an interface, and a descriptor is a uintptr placeholder that r.FDs is not checked against. Generated bindings use RequestArgs instead, which marshals the same bytes without allocating.

func (*Context) RequestArgs added in v0.4.0

func (c *Context) RequestArgs(r Request, args ...Arg) error

RequestArgs is Request with typed arguments: same lifecycle, same wire bytes, no per-argument interface boxing. Child, when set, must be passed as an ArgObject at its wire position. The number of ArgFD markers must equal len(r.FDs); a mismatch fails before the child is allocated, the send guard runs or anything is written, and leaves the descriptors with the caller.

func (*Context) RunTill

func (c *Context) RunTill(callback Object) error

RunTill runs the event loop until the callback fires

func (*Context) SendDestructor added in v0.3.0

func (c *Context) SendDestructor(proxy Proxy, opcode uint32, args ...interface{}) error

SendDestructor sends a destructor request exactly once. The proxy is claimed (unregistered) under the connection send lock, after marshaling and immediately before the write: concurrent destructors and later requests on the proxy fail without writing. A marshaling error leaves the proxy registered. A write error after the claim leaves the proxy unregistered; such an error means the connection is broken and must be closed.

func (*Context) SendDestructorWithFDs added in v0.3.0

func (c *Context) SendDestructorWithFDs(proxy Proxy, opcode uint32, fds []int, args ...interface{}) error

SendDestructorWithFDs is SendDestructor for requests carrying FDs. On error the FDs remain owned by the caller.

func (*Context) SendRequest

func (c *Context) SendRequest(proxy Proxy, opcode uint32, args ...interface{}) error

SendRequest sends a request through the context. The proxy is validated under the connection send lock, so no request reaches the wire after a destructor for the same proxy.

func (*Context) SendRequestWithFDs

func (c *Context) SendRequestWithFDs(proxy Proxy, opcode uint32, fds []int, args ...interface{}) error

SendRequestWithFDs sends a request with file descriptors through the context. On error the FDs remain owned by the caller.

func (*Context) Unregister

func (c *Context) Unregister(proxy Proxy)

Unregister removes a proxy object Only this proxy is removed: a zombie or a newer object that took the ID is left in place.

func (*Context) UnregisterID

func (c *Context) UnregisterID(id uint32)

UnregisterID removes a proxy object by ID (overloaded for compatibility)

type Display

type Display struct {
	// contains filtered or unexported fields
}

Display represents a connection to the Wayland display

func Connect

func Connect(socketPath string) (*Display, error)

Connect connects to the Wayland display and requests the registry.

func ConnectFromConn added in v0.3.0

func ConnectFromConn(conn net.Conn) (*Display, error)

ConnectFromConn attaches to an established Wayland socket and requests its registry.

func (*Display) AllocateID

func (d *Display) AllocateID() uint32

AllocateID allocates a new object ID (public method)

func (*Display) Close

func (d *Display) Close() error

Close closes the display connection. It is safe to call more than once; later calls are no-ops. After Close, Dispatch returns net.ErrClosed.

func (*Display) Closed added in v0.2.0

func (d *Display) Closed() bool

Closed reports whether the connection has been closed.

func (*Display) Context

func (d *Display) Context() *Context

Context returns a context for this display

func (*Display) Dispatch

func (d *Display) Dispatch() error

Dispatch reads one complete Wayland message and delivers it to the object that owns it.

Read boundaries never define message boundaries: a header split across several reads and several messages arriving in one read are both handled. Dispatch returns net.ErrClosed after Close, a typed ProtocolError for a malformed frame, io.ErrUnexpectedEOF when the peer closes mid-frame, and os.ErrDeadlineExceeded (not sticky) when a read deadline expires.

Event handlers run while Dispatch holds the dispatch lock, so a handler must not call Dispatch or Roundtrip on the same Display: that call deadlocks. Hand work that needs a roundtrip to another goroutine instead.

func (*Display) GetRegistry

func (d *Display) GetRegistry() *Registry

GetRegistry returns the registry (compatibility)

func (*Display) ID

func (d *Display) ID() uint32

ID returns the display's object ID (always 1)

func (*Display) RegisterEventSignature added in v0.3.0

func (d *Display) RegisterEventSignature(object uint32, opcode uint16, signature string)

RegisterEventSignature defines a signature for a legacy proxy. Generated proxies supply it themselves and cannot be overridden.

func (*Display) Registry

func (d *Display) Registry() *Registry

Registry returns the global registry

func (*Display) Roundtrip

func (d *Display) Roundtrip() error

Roundtrip waits for one wl_display.sync callback. Concurrent event loops must not call Roundtrip while another goroutine dispatches this connection.

func (*Display) SendRequest

func (d *Display) SendRequest(objectID uint32, opcode uint16, args ...interface{}) error

SendRequest sends a request to the compositor

func (*Display) SendRequestWithFDs

func (d *Display) SendRequestWithFDs(objectID uint32, opcode uint16, fds []int, args ...interface{}) error

SendRequestWithFDs sends a request with file descriptors

func (*Display) Sync

func (d *Display) Sync() (Object, error)

Sync creates a sync callback

type DisplayError added in v0.2.0

type DisplayError struct {
	ObjectID uint32
	Code     uint32
	Message  string
}

DisplayError is returned when the compositor reports wl_display.error.

func (*DisplayError) Error added in v0.2.0

func (e *DisplayError) Error() string

func (*DisplayError) Unwrap added in v0.2.0

func (e *DisplayError) Unwrap() error

Unwrap reports DisplayError as ErrDisplayError.

type Event

type Event struct {
	ProxyID uint32
	Opcode  uint16
	// contains filtered or unexported fields
}

Event represents a Wayland protocol event

func (*Event) Array

func (e *Event) Array() []byte

Array reads a byte array from the event

func (*Event) Data

func (e *Event) Data() []byte

Data returns the raw event body. It aliases the connection's receive buffer and is only valid until the handler returns; copy it to keep it.

func (*Event) FD added in v0.3.0

func (e *Event) FD() *OwnedFD

FD transfers ownership of the next descriptor assigned to this event. An unclaimed descriptor is closed when dispatch completes.

func (*Event) Fd

func (e *Event) Fd() uintptr

Fd is the legacy raw descriptor API; prefer FD for explicit ownership.

func (*Event) Fixed

func (e *Event) Fixed() Fixed

Fixed reads a fixed-point value from the event

func (*Event) Int32

func (e *Event) Int32() int32

Int32 reads an int32 from the event

func (*Event) NewID

func (e *Event) NewID() Proxy

NewId reads a new object ID from the event

func (*Event) Offset

func (e *Event) Offset() int

Offset returns the current read offset

func (*Event) Proxy

func (e *Event) Proxy() Proxy

Proxy reads an existing proxy reference from the event

func (*Event) String

func (e *Event) String() string

String reads a string from the event

func (*Event) Uint32

func (e *Event) Uint32() uint32

Uint32 reads a uint32 from the event

type Fixed

type Fixed int32

Fixed represents a 24.8 fixed-point number

func NewFixed

func NewFixed(v float64) Fixed

NewFixed creates a Fixed from float64

func (Fixed) Float64

func (f Fixed) Float64() float64

Float64 converts Fixed to float64

type Global

type Global struct {
	Name      uint32
	Interface string
	Version   uint32
}

Global represents a global object

type GlobalHandler

type GlobalHandler func(registry *Registry, name uint32, version uint32)

GlobalHandler is called when a global is announced

type Object

type Object interface {
	ID() uint32
}

Object represents a Wayland object

type OwnedFD added in v0.3.0

type OwnedFD struct {
	// contains filtered or unexported fields
}

OwnedFD owns one received descriptor. Close is idempotent; Take transfers ownership to the caller (who must close it). Descriptor zero is valid.

func (*OwnedFD) Close added in v0.3.0

func (f *OwnedFD) Close() error

func (*OwnedFD) Take added in v0.3.0

func (f *OwnedFD) Take() (int, error)

type ProtocolError added in v0.2.0

type ProtocolError struct {
	Kind   string
	Object uint32
	Opcode uint16
	Size   uint32
	Err    error
}

ProtocolError describes a wire-level protocol violation.

func (*ProtocolError) Error added in v0.2.0

func (e *ProtocolError) Error() string

func (*ProtocolError) Unwrap added in v0.2.0

func (e *ProtocolError) Unwrap() error

Unwrap returns the sentinel error classifying this violation.

type Proxy

type Proxy interface {
	Object
	SetID(uint32)
	Context() *Context
	Dispatch(*Event)
}

Proxy interface for Wayland protocol objects

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry represents the global registry

func (*Registry) AddGlobalHandler

func (r *Registry) AddGlobalHandler(handler RegistryGlobalHandler)

AddGlobalHandler adds a global handler to the registry

func (*Registry) AddGlobalRemoveHandler

func (r *Registry) AddGlobalRemoveHandler(handler RegistryGlobalRemoveHandler)

AddGlobalRemoveHandler registers a handler for registry removals.

func (*Registry) AddHandler

func (r *Registry) AddHandler(iface string, handler GlobalHandler)

AddHandler adds a handler for a specific interface, or "*" for every interface. Handlers accumulate; each announced global calls all of them in registration order.

func (*Registry) Bind

func (r *Registry) Bind(name uint32, iface string, version uint32, proxy Proxy) error

Bind binds to a global object and returns a typed proxy

func (*Registry) BindID

func (r *Registry) BindID(name uint32, iface string, version uint32) (uint32, error)

BindID binds to a global object and returns just the ID (compatibility method)

func (*Registry) BindNegotiated added in v0.3.0

func (r *Registry) BindNegotiated(iface string, supported uint32, proxy Proxy) (uint32, error)

BindNegotiated binds the announced global for iface at min(announced version, supported) and returns that version. It returns ErrGlobalNotFound when the global is absent, so callers can decide whether the capability is optional.

func (*Registry) Dispatch added in v0.3.0

func (r *Registry) Dispatch(event *Event)

Dispatch handles wl_registry.global and wl_registry.global_remove. The transport has already validated the body against EventSignature.

func (*Registry) EventSignature added in v0.3.0

func (r *Registry) EventSignature(opcode uint16) (string, bool)

EventSignature reports the wl_registry event signatures.

func (*Registry) FindGlobal

func (r *Registry) FindGlobal(iface string) (Global, bool)

FindGlobal finds a global by interface name

func (*Registry) FindGlobalByName

func (r *Registry) FindGlobalByName(name uint32) (Global, bool)

FindGlobalByName finds a global by its name ID

func (*Registry) GetGlobals

func (r *Registry) GetGlobals() map[uint32]Global

GetGlobals returns all announced globals

func (*Registry) ID

func (r *Registry) ID() uint32

ID returns the registry's object ID

type RegistryGlobalEvent

type RegistryGlobalEvent struct {
	Registry  *Registry
	Name      uint32
	Interface string
	Version   uint32
}

RegistryGlobalEvent represents a registry global announcement

type RegistryGlobalHandler

type RegistryGlobalHandler interface {
	HandleRegistryGlobal(event RegistryGlobalEvent)
}

RegistryGlobalHandler interface

type RegistryGlobalRemoveEvent

type RegistryGlobalRemoveEvent struct {
	Registry *Registry
	Name     uint32
}

RegistryGlobalRemoveEvent represents a registry global removal

type RegistryGlobalRemoveHandler

type RegistryGlobalRemoveHandler interface {
	HandleRegistryGlobalRemove(event RegistryGlobalRemoveEvent)
}

RegistryGlobalRemoveHandler interface

type Request added in v0.3.0

type Request struct {
	Proxy      Proxy
	Opcode     uint32
	Name       string // interface.request, used in errors
	Since      uint32 // version that introduced the request; 0 or 1 means always
	Destructor bool   // claim the proxy exactly once, as SendDestructor does
	Child      Proxy  // new_id object created by this request, or nil
	FDs        []int  // descriptors to attach; closed after a successful send
}

Request describes one protocol request for Context.Request and Context.RequestArgs.

type ShmBuffer

type ShmBuffer struct {
	// contains filtered or unexported fields
}

ShmBuffer represents a buffer allocated from a pool

func (*ShmBuffer) Data

func (b *ShmBuffer) Data() []byte

Data returns the buffer's data slice

func (*ShmBuffer) Offset

func (b *ShmBuffer) Offset() int

Offset returns the buffer's offset in the pool

type ShmPool

type ShmPool struct {
	// contains filtered or unexported fields
}

ShmPool represents a shared memory pool

func CreateShmPool

func CreateShmPool(size int) (*ShmPool, error)

CreateShmPool creates a new shared memory pool

func (*ShmPool) AllocateBuffer

func (p *ShmPool) AllocateBuffer(width, height, stride int, format uint32) (*ShmBuffer, error)

AllocateBuffer allocates a buffer from the pool

func (*ShmPool) Close

func (p *ShmPool) Close() error

Close closes the shared memory pool

func (*ShmPool) Data

func (p *ShmPool) Data() []byte

Data returns the memory-mapped data

func (*ShmPool) FD

func (p *ShmPool) FD() int

FD returns the file descriptor

func (*ShmPool) Size

func (p *ShmPool) Size() int

Size returns the pool size

Directories

Path Synopsis
cmd
wlturbo-scanner command
Command wlturbo-scanner generates Go bindings from Wayland protocol XML files.
Command wlturbo-scanner generates Go bindings from Wayland protocol XML files.
internal
scanner
Package scanner generates WLTurbo-backed Go bindings from Wayland protocol XML files.
Package scanner generates WLTurbo-backed Go bindings from Wayland protocol XML files.
core
Package core contains canonical Wayland core protocol bindings generated from pinned wayland.xml.
Package core contains canonical Wayland core protocol bindings generated from pinned wayland.xml.
Package wl provides type aliases for easy migration from neurlang/wayland
Package wl provides type aliases for easy migration from neurlang/wayland

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL