(Note: This crate is the extraction of the event loop machinery of wayland-server into its own crate.)
Calloop is an event loop center around event sources associated with callbacks: a source generates events, which are then provided to the callback associated with this source.
What it is
It is built on mio to track readiness of sources and the run part of a program based on it is centered on the EventLoop::dispatch()
method:
loop {
event_loop.dispatch(Some(Duration::from_millis(20)).unwrap();
}
This method blocks during at most the supplied time until any event source becomes ready, and then dispatches all the generated events to their associated callbacks before returning.
From an EventLoop
you can get a LoopHandle
which can be cloned, and is used to insert new event sources into the event loop, it is thus notably possible to insert a new event source into the loop from within the callback of an other event source.
Currently the following kind of event sources are supported:
- timers (generates events on timeouts)
- MPSC channels (the event source is the receiver end)
- Unix signals (generates an event when chosen signal is received)
- Generic
mio::Evented
types (the generated events are just a forward of themio
readiness)
It is also possible to create new kinds of event sources by implementing the mio::Evented
and calloop::EventSource
traits.
calloop
also provides "idle callbacks": callback that are fired only once, when all pending events of the sources have been processed. This makes it possible to schedule tasks for "later, when the loop is no longer busy".
Why this format
This kind of event loop handling, similar to the GLib event loop for example (but simpler), proved to be very adequate for wayland workflow, notably on smithay.
The main take-away is that it allows easy sharing of the event loop between different modules without needing these modules to know about each other. Having a single method fetching the next event would require creating a large enum of all possible events, which would completely breaks encapsulation.
A wayland compositor based on smithay actually has several modules that are mostly independent (session management, input processing, client protocol), but all require to wait on event sources. This callback-based approach allows all of them to share the same event loop without any need to have common code gluing them together, allowing much easier modularity.
I decided to extract this into a new crate because I felt this event-loop logic was actually quite independent of the wayland logic, and could probably be re-used by others. Typically, it might be used for client-side applications, that want to juggle between their connection to the display server and other event sources without necessarily spawning new threads.
Trade-offs
calloop
makes large internal use of Rc
, RefCell
and trait objects, and you will too need to do it if you need to share data between some of your callbacks. A fundamental assumption is that the considered workload from event sources will be sufficiently low that this will not be a performance bottleneck.
(This is typically the case on a wayland server, were most of the work is spent in compositing the windows and drawing on the screen.)