-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
1,660 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
//! Actuator definition. | ||
//! | ||
//! The actuator provides different mechanisms to determine when a key is considered actuated. Due | ||
//! to the unstable signals generated by an analog source, the actuator uses thresholds to | ||
//! determine whether the user intends to release or press a given key. The actuator can be | ||
//! customized to change the acutation points for each switch, along with the mode that determines | ||
//! when it actuates. | ||
|
||
use crate::layout::Event; | ||
|
||
/// Errors when working with the [`AnalogActuator`] | ||
pub enum AnalogActuatorError { | ||
/// The provided row and column pair does not exist in the analog matrix. | ||
InvalidLocation, | ||
} | ||
|
||
/// Different modes to determine when a switch is actuated | ||
#[derive(Default, Clone, Copy)] | ||
pub enum AnalogAcutationMode { | ||
/// Key is considered actuated when the key is past the actuation point, and released when it | ||
/// is above the actuation point. | ||
#[default] | ||
Static, | ||
/// Actuations are registered any time the user represses a key below the actuation point. | ||
/// Releases are registered as soon as the user starts to release. | ||
Rapid, | ||
/// Similar to dynamic, but once the user goes past the actuation point, subsequent actuations | ||
/// ignore the actuation point. So, a user can repress a key to register an actuation regardless | ||
/// of whether they are above or below the actuation point. Once the user fully releases the | ||
/// key, the user will need to go past the actuation point again. | ||
ContinuousRapid, | ||
} | ||
|
||
/// Analog matrix actuator | ||
pub struct AnalogActuator<const CS: usize, const RS: usize> { | ||
press_threshold: u8, | ||
release_threshold: u8, | ||
modes: [[AnalogAcutationMode; CS]; RS], | ||
actuation_points: [[u8; CS]; RS], | ||
cur_state: [[u8; CS]; RS], | ||
new_state: [[u8; CS]; RS], | ||
cur_actuated: [[bool; CS]; RS], | ||
} | ||
|
||
impl<const CS: usize, const RS: usize> AnalogActuator<CS, RS> { | ||
/// Create a new actuator. | ||
/// | ||
/// You must provide the default actuation mode for each key position, and their actuation points. | ||
/// Note that 255 represents a fully pressed switch, while 0 represents an unpressed switch. | ||
pub const fn new( | ||
modes: [[AnalogAcutationMode; CS]; RS], | ||
actuation_points: [[u8; CS]; RS], | ||
) -> Self { | ||
Self { | ||
modes, | ||
press_threshold: 5, | ||
release_threshold: 5, | ||
cur_state: [[0; CS]; RS], | ||
new_state: [[0; CS]; RS], | ||
cur_actuated: [[false; CS]; RS], | ||
actuation_points, | ||
} | ||
} | ||
|
||
/// Update the actuation mode for a given key. | ||
pub fn set_mode( | ||
&mut self, | ||
row: usize, | ||
col: usize, | ||
mode: AnalogAcutationMode, | ||
) -> Result<(), AnalogActuatorError> { | ||
self.modes | ||
.get_mut(row) | ||
.and_then(|row| { | ||
row.get_mut(col).map(|key| { | ||
*key = mode; | ||
}) | ||
}) | ||
.ok_or(AnalogActuatorError::InvalidLocation) | ||
} | ||
|
||
/// Set the actuation point for a given key. A value of 0 represents an unpressed state, | ||
/// and a value of 255 should represent a fully pressed switch. | ||
pub fn set_actuation_point( | ||
&mut self, | ||
row: usize, | ||
col: usize, | ||
value: u8, | ||
) -> Result<(), AnalogActuatorError> { | ||
self.actuation_points | ||
.get_mut(row) | ||
.and_then(|row| { | ||
row.get_mut(col).map(|key| { | ||
*key = value; | ||
}) | ||
}) | ||
.ok_or(AnalogActuatorError::InvalidLocation) | ||
} | ||
|
||
/// Update the threshold to register a key press | ||
pub fn set_press_threshold(&mut self, press_threshold: u8) { | ||
self.press_threshold = press_threshold; | ||
} | ||
|
||
/// Update the threshold to register a key release | ||
pub fn set_release_threshold(&mut self, release_threshold: u8) { | ||
self.release_threshold = release_threshold; | ||
} | ||
|
||
/// Iterates on the `Event`s generated by the update. | ||
/// | ||
/// `T` must be some kind of array of array of u8. | ||
/// | ||
/// # Example | ||
/// | ||
/// ``` | ||
/// use keyberon::analog::{AnalogAcutationMode, AnalogActuator}; | ||
/// use keyberon::layout::Event; | ||
/// let mut actuator = AnalogActuator::new( | ||
/// [[AnalogAcutationMode::Static; 2]; 2], | ||
/// [[127; 2]; 2], | ||
/// ); | ||
/// | ||
/// // no changes | ||
/// assert_eq!(0, actuator.events([[0, 0], [0, 0]]).count()); | ||
/// | ||
/// // `(0, 1)` is pressed. | ||
/// assert_eq!( | ||
/// vec![Event::Press(0, 1)], | ||
/// actuator.events([[0, 255], [0, 0]]).collect::<Vec<_>>(), | ||
/// ); | ||
/// ``` | ||
pub fn events(&mut self, new: [[u8; CS]; RS]) -> impl Iterator<Item = Event> + '_ { | ||
self.new_state = new; | ||
|
||
let press_threshold = self.press_threshold; | ||
let release_threshold = self.release_threshold; | ||
|
||
self.cur_state | ||
.iter_mut() | ||
.zip(self.cur_actuated.iter_mut()) | ||
.zip(self.actuation_points.iter()) | ||
.zip(self.modes.iter()) | ||
.zip(self.new_state.iter()) | ||
.enumerate() | ||
.flat_map(move |(row, ((((o, a), p), m), n))| { | ||
o.iter_mut() | ||
.zip(a.iter_mut()) | ||
.zip(p.iter()) | ||
.zip(m.iter()) | ||
.zip(n.iter()) | ||
.enumerate() | ||
.filter_map( | ||
move |(col, ((((cur, actuated), actuation_point), mode), new))| { | ||
let mut event = None; | ||
|
||
match mode { | ||
AnalogAcutationMode::Static => { | ||
if *actuated | ||
&& *new < actuation_point.saturating_sub(release_threshold) | ||
{ | ||
*actuated = false; | ||
event = Some(Event::Release(row as u8, col as u8)); | ||
} else if !*actuated | ||
&& *new >= actuation_point.saturating_add(press_threshold) | ||
{ | ||
*actuated = true; | ||
event = Some(Event::Press(row as u8, col as u8)); | ||
}; | ||
*cur = *new; | ||
} | ||
AnalogAcutationMode::Rapid => { | ||
if *actuated { | ||
if *new < cur.saturating_sub(release_threshold) { | ||
// Check for releases | ||
*actuated = false; | ||
event = Some(Event::Release(row as u8, col as u8)); | ||
*cur = *new; | ||
} else if *new > *cur { | ||
// If the user presses the key further, update cur | ||
*cur = *new; | ||
} | ||
} else if *new > cur.saturating_add(press_threshold) | ||
&& *new >= *actuation_point | ||
{ | ||
// Check for presses | ||
*actuated = true; | ||
event = Some(Event::Press(row as u8, col as u8)); | ||
*cur = *new; | ||
} else if *new < *cur { | ||
// If the user releases the key further, update cur | ||
*cur = *new | ||
}; | ||
} | ||
AnalogAcutationMode::ContinuousRapid => { | ||
if *actuated { | ||
if *new < cur.saturating_sub(release_threshold) { | ||
// Check for releases | ||
*actuated = false; | ||
event = Some(Event::Release(row as u8, col as u8)); | ||
*cur = *new; | ||
} else if *new > *cur { | ||
// If the user presses the key further, update cur | ||
*cur = *new; | ||
} | ||
} else if *cur == 0 { | ||
// If the key was fully released, only register an actuation if | ||
// we go past the actuation point. | ||
if *new >= *actuation_point { | ||
*actuated = true; | ||
event = Some(Event::Press(row as u8, col as u8)); | ||
*cur = *new; | ||
}; | ||
} else if *new > cur.saturating_add(press_threshold) { | ||
// Check for presses | ||
*actuated = true; | ||
event = Some(Event::Press(row as u8, col as u8)); | ||
*cur = *new; | ||
} else if *new < *cur { | ||
// If the user releases the key further, update cur | ||
*cur = *new; | ||
} | ||
} | ||
} | ||
|
||
event | ||
}, | ||
) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.