1 | //! Calloop, a Callback-based Event Loop |
2 | //! |
3 | //! This crate provides an [`EventLoop`] type, which is a small abstraction |
4 | //! over a polling system. The main difference between this crate |
5 | //! and other traditional rust event loops is that it is based on callbacks: |
6 | //! you can register several event sources, each being associated with a callback |
7 | //! closure that will be invoked whenever the associated event source generates |
8 | //! events. |
9 | //! |
10 | //! The main target use of this event loop is thus for apps that expect to spend |
11 | //! most of their time waiting for events and wishes to do so in a cheap and convenient |
12 | //! way. It is not meant for large scale high performance IO. |
13 | //! |
14 | //! ## How to use it |
15 | //! |
16 | //! Below is a quick usage example of calloop. For a more in-depth tutorial, see |
17 | //! the [calloop book](https://smithay.github.io/calloop). |
18 | //! |
19 | //! For simple uses, you can just add event sources with callbacks to the event |
20 | //! loop. For example, here's a runnable program that exits after five seconds: |
21 | //! |
22 | //! ```no_run |
23 | //! use calloop::{timer::{Timer, TimeoutAction}, EventLoop, LoopSignal}; |
24 | //! |
25 | //! fn main() { |
26 | //! // Create the event loop. The loop is parameterised by the kind of shared |
27 | //! // data you want the callbacks to use. In this case, we want to be able to |
28 | //! // stop the loop when the timer fires, so we provide the loop with a |
29 | //! // LoopSignal, which has the ability to stop the loop from within events. We |
30 | //! // just annotate the type here; the actual data is provided later in the |
31 | //! // run() call. |
32 | //! let mut event_loop: EventLoop<LoopSignal> = |
33 | //! EventLoop::try_new().expect("Failed to initialize the event loop!" ); |
34 | //! |
35 | //! // Retrieve a handle. It is used to insert new sources into the event loop |
36 | //! // It can be cloned, allowing you to insert sources from within source |
37 | //! // callbacks. |
38 | //! let handle = event_loop.handle(); |
39 | //! |
40 | //! // Create our event source, a timer, that will expire in 2 seconds |
41 | //! let source = Timer::from_duration(std::time::Duration::from_secs(2)); |
42 | //! |
43 | //! // Inserting an event source takes this general form. It can also be done |
44 | //! // from within the callback of another event source. |
45 | //! handle |
46 | //! .insert_source( |
47 | //! // a type which implements the EventSource trait |
48 | //! source, |
49 | //! // a callback that is invoked whenever this source generates an event |
50 | //! |event, _metadata, shared_data| { |
51 | //! // This callback is given 3 values: |
52 | //! // - the event generated by the source (in our case, timer events are the Instant |
53 | //! // representing the deadline for which it has fired) |
54 | //! // - &mut access to some metadata, specific to the event source (in our case, a |
55 | //! // timer handle) |
56 | //! // - &mut access to the global shared data that was passed to EventLoop::run or |
57 | //! // EventLoop::dispatch (in our case, a LoopSignal object to stop the loop) |
58 | //! // |
59 | //! // The return type is just () because nothing uses it. Some |
60 | //! // sources will expect a Result of some kind instead. |
61 | //! println!("Timeout for {:?} expired!" , event); |
62 | //! // notify the event loop to stop running using the signal in the shared data |
63 | //! // (see below) |
64 | //! shared_data.stop(); |
65 | //! // The timer event source requires us to return a TimeoutAction to |
66 | //! // specify if the timer should be rescheduled. In our case we just drop it. |
67 | //! TimeoutAction::Drop |
68 | //! }, |
69 | //! ) |
70 | //! .expect("Failed to insert event source!" ); |
71 | //! |
72 | //! // Create the shared data for our loop. |
73 | //! let mut shared_data = event_loop.get_signal(); |
74 | //! |
75 | //! // Actually run the event loop. This will dispatch received events to their |
76 | //! // callbacks, waiting at most 20ms for new events between each invocation of |
77 | //! // the provided callback (pass None for the timeout argument if you want to |
78 | //! // wait indefinitely between events). |
79 | //! // |
80 | //! // This is where we pass the *value* of the shared data, as a mutable |
81 | //! // reference that will be forwarded to all your callbacks, allowing them to |
82 | //! // share some state |
83 | //! event_loop |
84 | //! .run( |
85 | //! std::time::Duration::from_millis(20), |
86 | //! &mut shared_data, |
87 | //! |_shared_data| { |
88 | //! // Finally, this is where you can insert the processing you need |
89 | //! // to do do between each waiting event eg. drawing logic if |
90 | //! // you're doing a GUI app. |
91 | //! }, |
92 | //! ) |
93 | //! .expect("Error during event loop!" ); |
94 | //! } |
95 | //! ``` |
96 | //! |
97 | //! ## Event source types |
98 | //! |
99 | //! The event loop is backed by an OS provided polling selector (epoll on Linux). |
100 | //! |
101 | //! This crate also provide some adapters for common event sources such as: |
102 | //! |
103 | //! - [MPSC channels](channel) |
104 | //! - [Timers](timer) |
105 | //! - [unix signals](signals) on Linux |
106 | //! |
107 | //! As well as generic objects backed by file descriptors. |
108 | //! |
109 | //! It is also possible to insert "idle" callbacks. These callbacks represent computations that |
110 | //! need to be done at some point, but are not as urgent as processing the events. These callbacks |
111 | //! are stored and then executed during [`EventLoop::dispatch`](EventLoop#method.dispatch), once all |
112 | //! events from the sources have been processed. |
113 | //! |
114 | //! ## Async/Await compatibility |
115 | //! |
116 | //! `calloop` can be used with futures, both as an executor and for monitoring Async IO. |
117 | //! |
118 | //! Activating the `executor` cargo feature will add the [`futures`] module, which provides |
119 | //! a future executor that can be inserted into an [`EventLoop`] as yet another [`EventSource`]. |
120 | //! |
121 | //! IO objects can be made Async-aware via the [`LoopHandle::adapt_io`](LoopHandle#method.adapt_io) |
122 | //! method. Waking up the futures using these objects is handled by the associated [`EventLoop`] |
123 | //! directly. |
124 | //! |
125 | //! ## Custom event sources |
126 | //! |
127 | //! You can create custom event sources can will be inserted in the event loop by |
128 | //! implementing the [`EventSource`] trait. This can be done either directly from the file |
129 | //! descriptors of your source of interest, or by wrapping an other event source and further |
130 | //! processing its events. An [`EventSource`] can register more than one file descriptor and |
131 | //! aggregate them. |
132 | //! |
133 | //! ## Platforms support |
134 | //! |
135 | //! Currently, calloop is tested on Linux, FreeBSD and macOS. |
136 | //! |
137 | //! The following platforms are also enabled at compile time but not tested: Android, NetBSD, |
138 | //! OpenBSD, DragonFlyBSD. |
139 | //! |
140 | //! Those platforms *should* work based on the fact that they have the same polling mechanism as |
141 | //! tested platforms, but some subtle bugs might still occur. |
142 | |
143 | #![warn (missing_docs, missing_debug_implementations)] |
144 | #![allow (clippy::needless_doctest_main)] |
145 | #![cfg_attr (docsrs, feature(doc_cfg))] |
146 | #![cfg_attr (feature = "nightly_coverage" , feature(coverage_attribute))] |
147 | |
148 | mod sys; |
149 | |
150 | pub use sys::{Interest, Mode, Poll, Readiness, Token, TokenFactory}; |
151 | |
152 | pub use self::loop_logic::{EventLoop, LoopHandle, LoopSignal, RegistrationToken}; |
153 | pub use self::sources::*; |
154 | |
155 | pub mod error; |
156 | pub use error::{Error, InsertError, Result}; |
157 | |
158 | pub mod io; |
159 | mod list; |
160 | mod loop_logic; |
161 | mod macros; |
162 | mod sources; |
163 | mod token; |
164 | |