Skip to main content

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;