This new version of wayland-rs
brings several changes to the library API, in order to improve general ergonomics in writing Wayland clients and server programs. The main change is the introduction of the Filters API.
Filters instead of Implementations
Until now wayland-rs
had followed the design of the C libwayland in that each Wayland object must be given an implementation, under the form of some kind of callback that is invoked every time this object receives a message (an event client-side or a request server-side).
Right from the start this posed the question of state sharing, as Rust's constraints on this are much stronger than C on this subject. So sharing state between the callbacks of different objects requires using Rc
and RefCell
to wrap the state. But sharing state between objects is actually something very common for Wayland projects, and having to explicitly manage it like that is not ergonomic at all.
This is why this new version discontinues the "one-implementation-per-object" setup and rather introduces the concept of Filter. A Filter is again a wrapper around a callback, however more than one object can be assigned to a given filter, allowing much easier state sharing.
Creating a Filter requires you to provide a closure of type FnMut(E, Filter<E>)
. E
is the event type your Filter processes and it can be used for any object whose messages implement Into<E>
. wayland-server
and wayland-client
provide macros automating the creation of enums and their appropriate From
implementations.
As the second argument, your closure is given an handle to the Filter itself, allowing you to assign newly created object to the same filer from within the closure.
This way, you can assign all objects that need sharing state into the same Filter, removing any need for Rc
or RefCell
everywhere.
Wayland objects thus gain two methods: assign
, which assigns them to a Filter, and assign_mono
, which allows you to just assign a closure to an object, when you don't need to share state.
// Create an enum joining pointer and keyboard events, so that all input events are
// handled by a single filter
event_enum!(
InputEvents |
Pointer => WlPointer,
Keyboard => WlKeyboard,
);
// Create a filter with the input processing logic
let input_filter = Filter::<InputEvents>::new(|event, _| { /* process the input event */ });
// Once created, assign the objects to this filter
keyboard.assign(input_filter.clone());
pointer.assign(input_filter.clone());
Moreover, objects can be assigned to an other filter several times if necessary, as opposed to the implementation that could only be set one time.
Main & Attached handles
Two new kind of proxy handle are introduced client side, Main<I>
and Attached<I>
, splitting the capability of managing filters & event queue into new types.
To change the filter assignation of an object, you need a Main<I>
handle to it. However these handles cannot be send across threads.
To send it somewhere else, you first need to retrieve the I
itself (by dereferencing the Main<I>
and cloning) which is threadsafe, but cannot be used to change the filter assignation.
A bare I
can be used to send requests to the server, but not requests that create new objects. For these, you need to use the Main<I>
or attach the I
to an event queue (via Proxy::attach
). Once this is done, the newly created objects will be managed by this event queue. An Attached<I>
is not threadsafe either, as event queues are not.
Server-side also introduces the Main<I>
wrapper, but not Attached<I>
, as there are no event queues.
User-Data mechanism
The user-data mechanism, which allows you to attach an arbitrary value to a Wayland object and retrieve it from any handle to it, has been redesigned. It now revolves around an UserData
type which acts as an anonymous container.
Its contents can be sent only once, and then accessed back as long as you know the expected type. It also is thread-aware, and will prevent a non-threadsafe payload from being accessed from an other thread than the one that set it.
No more event loop integration
calloop
is no longer a dependency of wayland-rs
, and instead the structures of the library provide basic methods (get_fd()
and dispatch()
) allowing you to proceed with the integration into an other event loop as you prefer.
Feature rename
The native_lib
feature has been renamed into use_system_lib
, to be more explicit about what it does: disable the rust implementation of the protocol and use the system libwayland
instead (which is required if you need any kind of wayland-ffi interaction, such as using OpenGL).
Reducing allocations
The internal representation of messages no longer allocates for messages with less than 4 arguments, which notably covers the quite spammy events that wl_pointer.motion
and wl_touch.motion
are. This drastically reduce the number of allocations done by the library when using the use_system_lib
feature.