Skip to main content

a7n_core/hotkey/
trigger.rs

1// SPDX-License-Identifier: GPL-3.0-or-later
2
3use super::keys::{key_usage, trigger_name};
4use super::{HotkeyError, Mods, modifier_bit};
5
6pub const HOLD_DEFAULT: u32 = 800;
7pub const HOLD_MIN: u32 = 300;
8pub const HOLD_MAX: u32 = 3000;
9
10#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
11pub enum TriggerMode {
12    #[default]
13    DoubleTap,
14    Hold,
15    Both,
16}
17
18impl TriggerMode {
19    /// # Errors
20    /// Rejects modes other than `double_tap`, `hold` and `both`.
21    pub fn parse(text: &str) -> Result<Self, HotkeyError> {
22        match text {
23            "double_tap" => Ok(Self::DoubleTap),
24            "hold" => Ok(Self::Hold),
25            "both" => Ok(Self::Both),
26            _ => Err(HotkeyError(format!("unknown trigger mode '{text}'"))),
27        }
28    }
29}
30
31/// Trigger selected by the backwards-compatible `double_tap` config key.
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct DoubleTap(TriggerKey);
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36enum TriggerKey {
37    None,
38    Modifiers(u8),
39    Key(u32),
40}
41
42impl DoubleTap {
43    /// # Errors
44    /// Rejects keys that type text or are unsuitable as safety triggers.
45    pub fn parse(text: &str) -> Result<Self, HotkeyError> {
46        let name = text.trim().to_ascii_lowercase();
47        if name == "none" {
48            return Ok(Self(TriggerKey::None));
49        }
50        if let Some(key) = key_usage(&name).filter(|key| trigger_name(*key).is_some()) {
51            return Ok(Self(TriggerKey::Key(key)));
52        }
53        if let Some(modifier) = name.strip_prefix("either_").and_then(Mods::token) {
54            return Ok(Self(TriggerKey::Modifiers(modifier.0 | (modifier.0 << 4))));
55        }
56        Err(HotkeyError(format!("unknown trigger key '{text}'")))
57    }
58
59    #[must_use]
60    pub fn matches(self, usage: u32) -> bool {
61        match self.0 {
62            TriggerKey::None => false,
63            TriggerKey::Key(key) => key == usage,
64            TriggerKey::Modifiers(mask) => modifier_bit(usage).is_some_and(|bit| mask & bit != 0),
65        }
66    }
67
68    #[must_use]
69    pub fn collides(self, chord: super::Chord) -> bool {
70        chord.mods == Mods::default() && self.matches(chord.key)
71    }
72}