Skip to main content

rmk_types/
fork.rs

1//! Shared fork-related types used by firmware and protocol layers.
2
3use core::ops::{BitAnd, BitOr, Not};
4
5use postcard::experimental::max_size::MaxSize;
6use serde::{Deserialize, Serialize};
7
8use crate::action::KeyAction;
9use crate::led_indicator::LedIndicator;
10use crate::modifier::ModifierCombination;
11use crate::mouse_button::MouseButtons;
12
13/// Bitset state used by fork matching logic.
14///
15/// A zero (default) value means "match nothing" — no modifiers, LEDs, or mouse buttons.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, MaxSize)]
17#[cfg_attr(feature = "defmt", derive(defmt::Format))]
18#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
19#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
20pub struct StateBits {
21    /// Active modifier combination to match.
22    pub modifiers: ModifierCombination,
23    /// LED indicator state to match (Num/Caps/Scroll Lock, etc.).
24    pub leds: LedIndicator,
25    /// Mouse button state to match.
26    pub mouse: MouseButtons,
27}
28
29impl BitOr for StateBits {
30    type Output = Self;
31
32    fn bitor(self, rhs: Self) -> Self::Output {
33        Self {
34            modifiers: self.modifiers | rhs.modifiers,
35            leds: self.leds | rhs.leds,
36            mouse: self.mouse | rhs.mouse,
37        }
38    }
39}
40
41impl BitAnd for StateBits {
42    type Output = Self;
43
44    fn bitand(self, rhs: Self) -> Self::Output {
45        Self {
46            modifiers: self.modifiers & rhs.modifiers,
47            leds: self.leds & rhs.leds,
48            mouse: self.mouse & rhs.mouse,
49        }
50    }
51}
52
53impl Not for StateBits {
54    type Output = Self;
55
56    fn not(self) -> Self::Output {
57        Self {
58            modifiers: !self.modifiers,
59            leds: !self.leds,
60            mouse: !self.mouse,
61        }
62    }
63}
64
65impl StateBits {
66    pub const fn new_from(modifiers: ModifierCombination, leds: LedIndicator, mouse: MouseButtons) -> Self {
67        Self { modifiers, leds, mouse }
68    }
69}
70
71/// Fork (key override) configuration.
72///
73/// A fork overrides a key's output based on the current modifier/LED/mouse state.
74/// When the trigger key is pressed, the fork checks current state against `match_any`
75/// and `match_none` to decide between `positive_output` and `negative_output`.
76#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, MaxSize)]
77#[cfg_attr(feature = "defmt", derive(defmt::Format))]
78#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
79#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
80pub struct Fork {
81    /// The key action that activates this fork. Should not be `KeyAction::Transparent`.
82    pub trigger: KeyAction,
83    /// Output when the state condition is NOT met.
84    pub negative_output: KeyAction,
85    /// Output when the state condition IS met.
86    pub positive_output: KeyAction,
87    /// If any of these state bits are active, the positive branch is taken.
88    pub match_any: StateBits,
89    /// If any of these state bits are active, the fork is suppressed.
90    pub match_none: StateBits,
91    /// Modifiers to keep active when the fork fires.
92    pub kept_modifiers: ModifierCombination,
93    /// Whether this fork can be rebound via protocol.
94    /// This is a firmware-enforced policy — the protocol itself does not
95    /// reject writes to non-bindable forks; enforcement happens in the
96    /// firmware's SetFork handler.
97    pub bindable: bool,
98}
99
100impl Default for Fork {
101    fn default() -> Self {
102        Self::empty()
103    }
104}
105
106impl Fork {
107    pub fn new(
108        trigger: KeyAction,
109        negative_output: KeyAction,
110        positive_output: KeyAction,
111        match_any: StateBits,
112        match_none: StateBits,
113        kept_modifiers: ModifierCombination,
114        bindable: bool,
115    ) -> Self {
116        Self {
117            trigger,
118            negative_output,
119            positive_output,
120            match_any,
121            match_none,
122            kept_modifiers,
123            bindable,
124        }
125    }
126
127    pub fn empty() -> Self {
128        Self::new(
129            KeyAction::No,
130            KeyAction::No,
131            KeyAction::No,
132            StateBits::default(),
133            StateBits::default(),
134            ModifierCombination::default(),
135            false,
136        )
137    }
138}