Use this file to discover all available pages before exploring further.
A Wayland compositor must accept incoming client connections, track the state of each connected client, and clean up gracefully when a client disconnects. In wayland-server, this is handled by three cooperating types: ListeningSocket manages the Unix-domain socket that clients connect to, Client represents an individual connected client with its credentials and per-client data, and the ClientData trait lets you attach custom state to each connection. This page covers all three, together with DisconnectReason and the credential-based access patterns that can_view() enables.
ListeningSocket creates and owns a named Unix-domain socket inside $XDG_RUNTIME_DIR. It also acquires an exclusive lockfile alongside the socket, preventing two compositor instances from accidentally sharing the same name. The socket file and lockfile are both deleted when the ListeningSocket is dropped.
use wayland_server::ListeningSocket;// Creates $XDG_RUNTIME_DIR/wayland-0 (and a wayland-0.lock file)let socket = ListeningSocket::bind("wayland-0")?;println!("Listening on {:?}", socket.socket_name());
socket_name() returns Some(&OsStr) only when the socket was created via bind() or bind_auto(). It returns None for sockets created with bind_absolute().
ListeningSocket::accept() is non-blocking. It returns Ok(Some(UnixStream)) when a new client is waiting, or Ok(None) if no client is ready. The returned UnixStream must be passed to DisplayHandle::insert_client() to register the client with the compositor — a stream that is never inserted will leave the client hanging indefinitely.
// In your event loop, poll the socket fd for readability first,// then call accept():if let Some(stream) = socket.accept()? { let client_data = std::sync::Arc::new(MyClientData::new()); let client = display.handle().insert_client(stream, client_data)?; println!("New client: {:?}", client.id());}
ListeningSocket implements both AsRawFd and AsFd, so you can poll it alongside the Display fd in your event loop:
use std::os::unix::io::AsFd;// Pass socket.as_fd() to epoll / calloop / mio / etc.let listening_fd = socket.as_fd();
Client represents a connected Wayland client. You receive &Client in Dispatch::request(), GlobalDispatch::bind(), and GlobalDispatch::can_view(). You can also retrieve a client from a DisplayHandle given an ObjectId.
use wayland_server::backend::ObjectId;// From a known ObjectId (e.g., the id of a resource):let client = dh.get_client(object_id)?;// From within a Dispatch impl — client is passed directly:fn request(/* … */, client: &Client, /* … */) { println!("Request from {:?}", client.id());}
Credentials can be spoofed by a sufficiently privileged process. Do not use them as the sole security mechanism for privileged protocols. See the Weston discussion for context.
Terminates the client connection immediately by sending a protocol error. The client’s ClientData::disconnected() callback is invoked with DisconnectReason::ProtocolError.
use wayland_server::backend::protocol::ProtocolError;client.kill( &dh, ProtocolError { code: 0, object_id: 1, object_interface: "wl_display".into(), message: "You are not welcome here".into(), },);
ClientData is defined in wayland_backend and re-exported from wayland_server::backend. Implement it on a type to attach custom per-client state and receive lifecycle notifications.
DisconnectReason describes why a client connection ended. It is passed to ClientData::disconnected().
pub enum DisconnectReason { /// The connection was closed by the client or the OS (clean shutdown, SIGPIPE, etc.) ConnectionClosed, /// The server terminated the connection with a protocol error ProtocolError(ProtocolError),}
ProtocolError carries the error code, the numeric object_id of the affected resource, the object_interface name, and a human-readable message string.
A typical use of client credentials is restricting a privileged global (e.g. an XWayland-only protocol) to connections whose pid matches a known process. The can_view() method of GlobalDispatch is the right place for this check.
use wayland_server::{ Client, DataInit, DisplayHandle, GlobalDispatch, New, protocol::wl_compositor::WlCompositor,};pub struct XWaylandGlobal { dh: DisplayHandle, xwayland_pid: u32,}impl GlobalDispatch<WlCompositor, AppState> for XWaylandGlobal { fn bind( &self, _state: &mut AppState, _handle: &DisplayHandle, _client: &Client, resource: New<WlCompositor>, data_init: &mut DataInit<'_, AppState>, ) { data_init.init(resource, ()); } fn can_view(&self, client: &Client) -> bool { // Only the XWayland process (identified by PID) may see this global. client .get_credentials(&self.dh) .map(|creds| creds.pid == self.xwayland_pid) .unwrap_or(false) }}
A more complete pattern stores the DisplayHandle and per-compositor policy in the global data: