1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! Debouncer definition.
//!
//! When pressed, switches don't give a clear state change: they
//! bounce. A debouncer filter these bounces. The current
//! implementation validate the state change when the state is stable
//! during a configurable number of update. 5 ms is the recommended
//! duration for keyboard switches.

use crate::layout::Event;
use core::convert::TryInto;
use either::Either::*;

/// The debouncer type.
pub struct Debouncer<T> {
    cur: T,
    new: T,
    since: u16,
    nb_bounce: u16,
}

impl<T> Debouncer<T> {
    /// Create a new debouncer.
    ///
    /// `cur` and `new` corresponds to the initial state, they should
    /// be equal at start. taking the 2 states allow `new` to be a
    /// `const fn` and allow non clonable types to be used.
    ///
    /// `nb_bounce` correspond to the number of update with same state
    /// needed to validate the new state.
    pub const fn new(cur: T, new: T, nb_bounce: u16) -> Self {
        Self {
            cur,
            new,
            since: 0,
            nb_bounce,
        }
    }
}

impl<T: PartialEq> Debouncer<T> {
    /// Gets the current state.
    pub fn get(&self) -> &T {
        &self.cur
    }

    /// Updates the current state.  Returns `true` if the state changes.
    pub fn update(&mut self, new: T) -> bool {
        if self.cur == new {
            self.since = 0;
            return false;
        }

        if self.new != new {
            self.new = new;
            self.since = 1;
        } else {
            self.since += 1;
        }

        if self.since > self.nb_bounce {
            core::mem::swap(&mut self.cur, &mut self.new);
            self.since = 0;
            true
        } else {
            false
        }
    }

    /// Iterates on the `Event`s generated by the update.
    ///
    /// `T` must be some kind of array of array of bool.
    ///
    /// Panics if the coordinates doesn't fit in a `(u8, u8)`.
    ///
    /// # Example
    ///
    /// ```
    /// use keyberon::debounce::Debouncer;
    /// use keyberon::layout::Event;
    /// let mut debouncer = Debouncer::new(
    ///     [[false, false], [false, false]],
    ///     [[false, false], [false, false]],
    ///     2,
    /// );
    ///
    /// // no changes
    /// assert_eq!(0, debouncer.events([[false, false], [false, false]]).count());
    ///
    /// // `(0, 1)` pressed, but debouncer is filtering
    /// assert_eq!(0, debouncer.events([[false, true], [false, false]]).count());
    /// assert_eq!(0, debouncer.events([[false, true], [false, false]]).count());
    ///
    /// // `(0, 1)` stable enough, event appear.
    /// assert_eq!(
    ///     vec![Event::Press(0, 1)],
    ///     debouncer.events([[false, true], [false, false]]).collect::<Vec<_>>(),
    /// );
    /// ```
    pub fn events<'a, U>(&'a mut self, new: T) -> impl Iterator<Item = Event> + 'a
    where
        &'a T: IntoIterator<Item = U>,
        U: IntoIterator<Item = &'a bool>,
        U::IntoIter: 'a,
    {
        if self.update(new) {
            Left(
                self.new
                    .into_iter()
                    .zip(self.cur.into_iter())
                    .enumerate()
                    .flat_map(move |(i, (o, n))| {
                        o.into_iter().zip(n.into_iter()).enumerate().filter_map(
                            move |(j, bools)| match bools {
                                (false, true) => {
                                    Some(Event::Press(i.try_into().unwrap(), j.try_into().unwrap()))
                                }
                                (true, false) => Some(Event::Release(
                                    i.try_into().unwrap(),
                                    j.try_into().unwrap(),
                                )),
                                _ => None,
                            },
                        )
                    }),
            )
        } else {
            Right(core::iter::empty())
        }
    }
}