Documentation Index
Fetch the complete documentation index at: https://mintlify.com/smithay/wayland-rs/llms.txt
Use this file to discover all available pages before exploring further.
EventQueue is the machinery that turns raw bytes from the Wayland socket into structured calls to your application logic. Every event your compositor sends travels through at least one event queue before reaching your Dispatch implementation. Understanding exactly how events flow through the queue—and how to drive that flow—is the key to writing responsive, correct Wayland clients.
How event delivery works
Receiving and processing Wayland events is a deliberate two-step process:- Read step — raw event bytes are read from the compositor socket. For each message,
wayland-clientdetermines whichEventQueueowns the target object and pushes the message into that queue’s internalVecDequebuffer. - Dispatch step — the queue drains its buffer, invoking
Dispatch::event()on your state struct for each message in order.
ReadEventsGuard), while the dispatch step always happens on the thread that owns &mut State. This design gives you &mut State access in every event handler without any locks.
Creating a queue
CallConnection::new_event_queue() with your state type as the generic parameter:
QueueHandle and use it when creating Wayland objects:
&qh will have its events delivered to event_queue.
Driving the queue
roundtrip — startup synchronization
EventQueue::roundtrip() sends a wl_display.sync request, blocks until the compositor acknowledges it, and dispatches all events received in the meantime. Because the compositor processes requests in order, a roundtrip guarantees that all requests you sent before it have been acted upon:
roundtrip during initialization to wait for the initial wl_registry.global flood, for surface configuration acknowledgements, and for any other “send requests, then wait for the server to catch up” patterns. In the main event loop, prefer blocking_dispatch instead.
blocking_dispatch — simple main loop
EventQueue::blocking_dispatch() is the workhorse of a single-queue application. It:
- Dispatches any events already buffered in the queue.
- If the queue was empty, flushes outgoing messages and sleeps until at least one new event arrives.
- Dispatches that newly arrived batch.
dispatch_pending — non-blocking drain
dispatch_pending processes whatever is already in the queue’s internal buffer without touching the socket. Use it in multi-source event loops where something else is responsible for reading the socket:
flush — explicit send
Force all outgoing Wayland requests to be written to the socket. Several dispatch methods do this automatically, but if you are building requests without immediately dispatching you should call flush manually:
QueueHandle
QueueHandle<State> is a lightweight, Cloneable handle to a queue. You pass it when creating new Wayland objects so the library knows which queue should receive their events:
QueueHandle is Send + Sync—you can clone it and share it across threads. Each clone refers to the same underlying queue.
QueueHandle::make_data()
For advanced cases where you need a raw ObjectData handle compatible with the wayland-backend API but still want events routed through Dispatch, use make_data():
QueueFreezeGuard — pausing dispatch
QueueHandle::freeze() returns a QueueFreezeGuard that prevents the queue from dispatching events until all guards are dropped. Events continue to accumulate in the internal buffer; they are released in order when the freeze ends:
Custom event-loop integration with ReadEventsGuard
For applications that multiplex Wayland with other I/O sources (timers, D-Bus, file watches), use prepare_read() to integrate the Wayland socket into your existing event loop:
prepare_read() returns None when events are already queued in the internal buffer (for example because another thread read from the socket). The example above handles this by calling continue and looping back to dispatch_pending().ReadEventsGuard is re-exported from wayland_client::backend:
Async dispatch with poll_dispatch_pending
EventQueue also exposes a poll_dispatch_pending() method compatible with std::task::Poll. It processes all currently buffered events and registers a waker so the current task is notified when new events arrive:
For a batteries-included async/calloop integration, the
calloop-wayland-source crate wraps an EventQueue as a calloop event source. This functionality was part of wayland-client itself up to version 0.30 but was extracted into a separate crate in 0.31.0.Multiple event queues
Using more than one event queue lets you separate concerns, process events on different threads, or isolate library code from application code.One queue per thread
Each thread creates its ownEventQueue and runs its own dispatch loop. Because blocking_dispatch reads from the socket internally, the two threads will independently block and wake as events arrive:
Guest library pattern
If your code is a library sitting on top of a shared connection managed by a host application, do not read the socket yourself. Use onlydispatch_pending() so your queue drains the events the host has already buffered, without interfering with the host’s event loop:
Error handling
All dispatch methods returnResult<usize, DispatchError>, where the usize is the number of events processed. DispatchError has two variants:
BadMessage means the server sent an event that does not match the expected wire format for the interface. Backend wraps a WaylandError (either an I/O error or a compositor-issued protocol error).