boppo_core/button_events.rs
1//! Events that occur when a top Button is pressed or released.
2//!
3//! This module provides the [`ButtonEvents`] and [`ButtonEvent`] structs.
4use std::{future::Future, pin::pin};
5
6use std::time::{Duration, Instant};
7
8use log::warn;
9use tokio::sync::broadcast::{self, error::TryRecvError};
10
11use crate::{Button, Buttons, internal};
12
13/// A notification that a top button has been pressed or released
14#[derive(Debug, Copy, Clone, PartialEq, Eq)]
15pub struct ButtonEvent {
16 // From least significant:
17 // 4 bits: the button index of the changed button
18 // 10 bits: the currently pressed bits (same as Buttons)
19 // 1 bit: if the changed button is pressed
20 // 1 bit: unused
21 data: u16,
22}
23
24impl ButtonEvent {
25 #[must_use]
26 #[expect(
27 clippy::cast_possible_truncation,
28 reason = "Button indices are always < u16::MAX"
29 )]
30 #[doc(hidden)]
31 pub fn new(changed: Button, pressed: bool, currently_pressed: Buttons) -> ButtonEvent {
32 let data =
33 changed.index() as u16 | u16::from(pressed) << 14 | currently_pressed.as_bitset() << 4;
34 ButtonEvent { data }
35 }
36
37 /// The button whose state changed (i.e. was pressed or released).
38 #[must_use]
39 pub fn button(&self) -> Button {
40 let index = self.data & 0b1111;
41 Button::from_index(index as usize)
42 }
43
44 /// Is this event a press event?
45 #[must_use]
46 pub fn is_pressed(&self) -> bool {
47 (self.data & (1 << 14)) != 0
48 }
49
50 /// Is this event a release event?
51 #[must_use]
52 pub fn is_released(&self) -> bool {
53 !self.is_pressed()
54 }
55
56 /// All buttons that were pressed at the time of the event
57 #[must_use]
58 pub fn all_currently_pressed(&self) -> Buttons {
59 Buttons::from_bitset((self.data >> 4) & 0b0011_1111_1111)
60 }
61
62 /// Represent this event as an u16 value.
63 ///
64 /// This is useful for making ButtonEvents cross FFIs.
65 #[must_use]
66 pub fn as_u16(&self) -> u16 {
67 self.data
68 }
69
70 /// Make a Button event from a u16 value.
71 ///
72 /// This is useful for making ButtonEvents cross FFIs.
73 pub fn from_u16(data: u16) -> Self {
74 Self { data }
75 }
76}
77
78enum EventTypeFilter {
79 Press,
80 Release,
81 Both,
82}
83
84/// A stream of top button press and release events.
85///
86/// Subscribers should generally poll for events frequently as events are stored
87/// in a circular buffer and a subscriber can lose events if too many are
88/// generated before being polled.
89///
90/// See also the following alternative ways to get the pressed/released state of
91/// buttons:
92/// * [`Buttons::currently_pressed()`]
93/// * [`Button::is_pressed()`]
94/// * [`Button::wait_for_press()`]
95/// * [`Button::wait_for_release()`]
96pub struct ButtonEvents {
97 raw_events: broadcast::Receiver<ButtonEvent>,
98 peeked_event: Option<ButtonEvent>,
99
100 /// Only report events for buttons that are included
101 buttons_filter: Buttons,
102 /// all events before this are ignored
103 ignore_events_until: Option<Instant>,
104 /// only report events that match
105 event_type_filter: EventTypeFilter,
106}
107
108impl ButtonEvents {
109 pub(crate) fn from_receiver(
110 button_event_receiver: broadcast::Receiver<ButtonEvent>,
111 ) -> ButtonEvents {
112 ButtonEvents {
113 raw_events: button_event_receiver,
114 peeked_event: None,
115 buttons_filter: Buttons::all(),
116 ignore_events_until: None,
117 event_type_filter: EventTypeFilter::Both,
118 }
119 }
120
121 /// Subscribe to all button events that take place after this call to subscribe.
122 ///
123 /// Events before subscribe will not be delivered.
124 ///
125 /// # Panics
126 ///
127 /// This function should ordinarily never panic, but may if the library has not been initialized.
128 ///
129 /// # Examples
130 ///
131 /// ```ignore
132 /// # use boppo_core::ButtonEvents;
133 /// let mut button_events = ButtonEvents::subscribe();
134 /// ```
135 pub fn subscribe() -> ButtonEvents {
136 ButtonEvents::from_receiver(internal::BUTTON_EVENTS.get().unwrap().subscribe())
137 }
138
139 /// Returns a [`Future`] yielding the next [`ButtonEvent`].
140 ///
141 /// # Panics
142 ///
143 /// This function should ordinarily never panic, but will if it is called after the internal
144 /// [`broadcast::Receiver`] is closed.
145 pub async fn next(&mut self) -> ButtonEvent {
146 if let Some(peeked_event) = self.peeked_event.take() {
147 return peeked_event;
148 }
149 loop {
150 let raw_event = self.raw_events.recv().await;
151 match raw_event {
152 Ok(raw_event) => {
153 if let Some(event) = self.handle_raw(raw_event) {
154 return event;
155 } // else get the next raw event
156 }
157
158 Err(broadcast::error::RecvError::Lagged(num_skipped)) => {
159 warn!("Button event receiver too slow. Dropped {num_skipped} button events.");
160 // Grab the next event. The client will NOT know anything happened.
161 // TODO should we expose a lapped flag or something?
162 }
163 Err(broadcast::error::RecvError::Closed) => {
164 panic!("Button receiver closed. This is unexpected.")
165 }
166 }
167 }
168 }
169
170 /// Waits for either a [`ButtonEvent`] to be received, or for `future` to complete, and returns
171 /// whichever is received first.
172 ///
173 /// # Errors
174 ///
175 /// This function will return `Err(F::Output)` if `future` completes before a [`ButtonEvent`]
176 /// is received.
177 ///
178 /// # Examples
179 ///
180 /// ```ignore
181 /// # use boppo_core::ButtonEvents;
182 /// let mut button_events = ButtonEvents::subscribe();
183 /// let my_future = async {
184 /// sleep_ms(100).await;
185 /// "there have been no button events for 100ms"
186 /// };
187 ///
188 /// match button_events.next_or_output(my_future).await {
189 /// Ok(event) => println!("{event:?}"),
190 /// Err(f_out) => println!("{f_out}"),
191 /// }
192 /// ```
193 pub async fn next_or_output<F>(&mut self, future: F) -> Result<ButtonEvent, F::Output>
194 where
195 F: Future,
196 {
197 let future = pin!(future);
198 let next_fut = pin!(self.next());
199 let either = futures::future::select(future, next_fut).await;
200 match either {
201 futures::future::Either::Left((output, _future)) => Err(output),
202 futures::future::Either::Right((button_event, _next_fut)) => Ok(button_event),
203 }
204 }
205
206 /// Returns [`Some`] if `self` receives an event before `timeout` elapses. Otherwise,
207 /// returns [`None`].
208 pub async fn next_or_timeout_after(&mut self, timeout: Duration) -> Option<ButtonEvent> {
209 embassy_time::with_timeout(
210 embassy_time::Duration::from_micros(timeout.as_micros() as u64),
211 self.next(),
212 )
213 .await
214 .ok()
215 }
216
217 /// Runs `future` while waiting for a new [`ButtonEvent`], and returns [`Some`] if an event
218 /// occurs before `future` completes. Otherwise, this function returns [`None`].
219 pub async fn next_or_completed<F>(&mut self, future: F) -> Option<ButtonEvent>
220 where
221 F: Future,
222 {
223 self.next_or_output(future).await.ok()
224 }
225
226 /// The same as [`ButtonEvents::next_or_output`], but does not consume an event if one is
227 /// received.
228 ///
229 /// # Errors
230 ///
231 /// This function will return `Err(F::Output)` if `future` completes before a [`ButtonEvent`]
232 /// is received.
233 ///
234 /// # Examples
235 ///
236 /// ```ignore
237 /// let mut button_events = ButtonEvents::subscribe();
238 /// let my_future = async {
239 /// sleep_ms(100).await;
240 /// "there have been no button events for 100ms"
241 /// };
242 ///
243 /// match button_events.peek_next_or_output(my_future).await {
244 /// Ok(event) => {
245 /// println!("{event:?}");
246 /// // We know there's an event here; unwrap it.
247 /// let ev = button_events.try_next().unwrap();
248 /// println!("{ev:?}");
249 /// assert_eq!(event == ev);
250 /// },
251 /// Err(f_out) => println!("{f_out}"),
252 /// }
253 /// ```
254 pub async fn peek_next_or_output<F>(&mut self, future: F) -> Result<ButtonEvent, F::Output>
255 where
256 F: Future,
257 {
258 let future = pin!(future);
259 let next_fut = pin!(self.peek_next());
260 let either = futures::future::select(future, next_fut).await;
261 match either {
262 futures::future::Either::Left((button_event, _future)) => Err(button_event),
263 futures::future::Either::Right((output, _next_fut)) => Ok(output),
264 }
265 }
266
267 /// Like [`next_or_timeout_after`][ButtonEvents::next_or_timeout_after], but peeks the event
268 /// instead of consuming it.
269 pub async fn peek_next_or_timeout_after(&mut self, timeout: Duration) -> Option<ButtonEvent> {
270 embassy_time::with_timeout(
271 embassy_time::Duration::from_micros(timeout.as_micros() as u64),
272 self.peek_next(),
273 )
274 .await
275 .ok()
276 }
277
278 /// Cancels the future if a button event is generated.
279 pub async fn peek_next_or_completed<F>(&mut self, future: F) -> Option<ButtonEvent>
280 where
281 F: Future,
282 {
283 self.peek_next_or_output(future).await.ok()
284 }
285
286 /// Waits for the next [`ButtonEvent`] and returns it without consuming it.
287 /// Repeated calls to this function will have the same return value.
288 ///
289 /// See also [`ButtonEvents::try_peek_next`]
290 pub async fn peek_next(&mut self) -> ButtonEvent {
291 if let Some(peeked_event) = self.peeked_event {
292 return peeked_event;
293 }
294 let next = self.next().await;
295 self.peeked_event = Some(next);
296 next
297 }
298
299 /// Returns the next [`ButtonEvent`] if one has been received, otherwise returns [`None`].
300 ///
301 /// # Panics
302 ///
303 /// This function will panic if the underlying [`broadcast::Receiver`] is closed.
304 pub fn try_next(&mut self) -> Option<ButtonEvent> {
305 if let Some(peeked_event) = self.peeked_event.take() {
306 return Some(peeked_event);
307 }
308 loop {
309 let raw_event = self.raw_events.try_recv();
310 match raw_event {
311 Ok(raw_event) => {
312 if let Some(event) = self.handle_raw(raw_event) {
313 return Some(event);
314 } // else get the next raw event
315 }
316
317 Err(broadcast::error::TryRecvError::Empty) => {
318 return None;
319 }
320
321 Err(broadcast::error::TryRecvError::Lagged(num_skipped)) => {
322 warn!("Button event receiver too slow. Dropped {num_skipped} button events.");
323 // Grab the next event. The caller will NOT know anything happened.
324 }
325 Err(broadcast::error::TryRecvError::Closed) => {
326 panic!("Button receiver closed. This is unexpected.")
327 }
328 }
329 }
330 }
331
332 /// Returns [`Some`] if an event has been received, without consuming it or waiting.
333 /// Otherwise returns [`None`].
334 ///
335 /// See also [`ButtonEvents::peek_next`]
336 pub fn try_peek_next(&mut self) -> Option<ButtonEvent> {
337 if let Some(peeked_event) = self.peeked_event {
338 return Some(peeked_event);
339 }
340 let next = self.try_next();
341 self.peeked_event = next;
342 next
343 }
344
345 // return None if the raw event should be filtered
346 fn handle_raw(&mut self, event: ButtonEvent) -> Option<ButtonEvent> {
347 if let Some(until) = self.ignore_events_until {
348 if Instant::now() < until {
349 return None;
350 }
351 self.ignore_events_until = None;
352 }
353 if !self.buttons_filter.contains(event.button()) {
354 return None;
355 }
356
357 match self.event_type_filter {
358 EventTypeFilter::Both => Some(event),
359 EventTypeFilter::Press => Some(event).filter(ButtonEvent::is_pressed),
360 EventTypeFilter::Release => Some(event).filter(ButtonEvent::is_released),
361 }
362 }
363
364 /// Clears all pending button events.
365 ///
366 /// Any [`ButtonEvent`] returned from a call to [`next`][ButtonEvents::next],
367 /// [`peek_next`][ButtonEvents::peek_next], etc via `self` are guaranteed to
368 /// have been received *after* this function returns.
369 pub fn clear_pending(&mut self) {
370 self.peeked_event.take();
371 while let Ok(_) | Err(TryRecvError::Lagged(_)) = self.raw_events.try_recv() {}
372 }
373
374 /// Only press events will be returned from next.
375 ///
376 /// Released events will still be relected in the state of `currently_pressed`.
377 pub fn only_report_press_events(&mut self) {
378 if let Some(event) = self.peeked_event
379 && event.is_released()
380 {
381 self.peeked_event.take();
382 }
383
384 self.event_type_filter = EventTypeFilter::Press;
385 }
386
387 /// Report both press and release events.
388 pub fn report_press_and_release_events(&mut self) {
389 self.event_type_filter = EventTypeFilter::Both;
390 }
391
392 /// Only release events will be returned from next.
393 pub fn only_report_release_events(&mut self) {
394 if let Some(event) = self.peeked_event
395 && event.is_pressed()
396 {
397 self.peeked_event.take();
398 }
399
400 self.event_type_filter = EventTypeFilter::Release;
401 }
402
403 /// If one or more press events (not release events) follows a press event within `duration`, do not report them.
404 pub fn ignore_presses_within(&mut self, duration: Duration) {
405 self.peeked_event.take();
406 self.ignore_events_until = Some(Instant::now() + duration);
407 }
408
409 /// Reset to the default behavior of not ignoring press events within a duration specified to `ignore_presses_within`.
410 pub fn remove_press_within_filter(&mut self) {
411 self.ignore_events_until = None;
412 }
413
414 /// Filter out events that don't come from a button in `buttons`.
415 pub fn filter_by_buttons(&mut self, buttons: Buttons) {
416 if let Some(event) = self.peeked_event
417 && !buttons.contains(event.button())
418 {
419 self.peeked_event.take();
420 }
421
422 self.buttons_filter = buttons;
423 }
424
425 /// Stop filtering by button. This is the same as `filter_by_buttons(Buttons::all())`.
426 pub fn remove_button_filter(&mut self) {
427 self.filter_by_buttons(Buttons::all());
428 }
429}
430
431#[cfg(test)]
432#[path = "./tests/button_events_test.rs"]
433mod test;