Skip to main content

loam_runtime/
input.rs

1use std::ops::Range;
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
4pub struct ActionId(pub u32);
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub struct ActionEvent {
8    pub action: ActionId,
9    pub pressed: bool,
10}
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub enum PointerPhase {
14    Began,
15    Moved,
16    Ended,
17    Cancelled,
18}
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21pub enum PointerButton {
22    Primary,
23    Secondary,
24    Middle,
25}
26
27/// Positions use NDC; deltas use logical pixels; both are y-up.
28#[derive(Clone, Copy, Debug, PartialEq)]
29pub struct Pointer {
30    pub id: u32,
31    pub button: Option<PointerButton>,
32    pub ndc: [f32; 2],
33    pub delta: [f32; 2],
34    pub phase: PointerPhase,
35    pub time: f64,
36}
37
38/// Messages the embedding page sent since the previous boundary, in arrival order.
39#[derive(Clone, Debug, Default)]
40pub struct HostMessages {
41    text: String,
42    entries: Vec<(Range<usize>, Range<usize>)>,
43    values: Vec<f32>,
44}
45
46impl HostMessages {
47    pub fn push(&mut self, topic: &str, values: &[f32]) {
48        let text = self.text.len()..self.text.len() + topic.len();
49        let range = self.values.len()..self.values.len() + values.len();
50        self.text.push_str(topic);
51        self.values.extend_from_slice(values);
52        self.entries.push((text, range));
53    }
54
55    pub fn clear(&mut self) {
56        self.text.clear();
57        self.entries.clear();
58        self.values.clear();
59    }
60
61    pub fn iter(&self) -> impl Iterator<Item = (&str, &[f32])> {
62        self.entries
63            .iter()
64            .map(|(text, values)| (&self.text[text.clone()], &self.values[values.clone()]))
65    }
66
67    pub fn latest(&self, topic: &str) -> Option<&[f32]> {
68        self.entries
69            .iter()
70            .rev()
71            .find(|(text, _)| &self.text[text.clone()] == topic)
72            .map(|(_, values)| &self.values[values.clone()])
73    }
74}
75
76/// Everything the host converted since the previous boundary.
77#[derive(Clone, Debug, Default)]
78pub struct Input {
79    pub pointers: Vec<Pointer>,
80    pub actions: Vec<ActionEvent>,
81    pub held: Vec<ActionId>,
82    pub scroll: [f32; 2],
83    /// Captured mouse motion in logical pixels, y-up.
84    pub look: [f32; 2],
85    pub cursor_locked: bool,
86    /// Seconds on the pointer clock when the host gathered this boundary.
87    pub time: f64,
88    pub host: HostMessages,
89}
90
91impl Input {
92    pub fn pressed(&self, action: ActionId) -> bool {
93        self.actions
94            .iter()
95            .any(|event| event.action == action && event.pressed)
96    }
97
98    pub fn is_held(&self, action: ActionId) -> bool {
99        self.held.contains(&action)
100    }
101
102    pub fn began(&self) -> Option<&Pointer> {
103        self.pointers
104            .iter()
105            .find(|pointer| pointer.phase == PointerPhase::Began)
106    }
107
108    pub fn drag(&self, button: PointerButton) -> [f32; 2] {
109        self.pointers
110            .iter()
111            .filter(|pointer| {
112                pointer.phase == PointerPhase::Moved && pointer.button == Some(button)
113            })
114            .fold([0.0; 2], |sum, pointer| {
115                [sum[0] + pointer.delta[0], sum[1] + pointer.delta[1]]
116            })
117    }
118}
119
120#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
121pub enum Key {
122    Space,
123    Escape,
124    Letter(char),
125    Digit(u8),
126}
127
128#[derive(Clone, Debug, Default)]
129pub struct Bindings {
130    keys: Vec<(Key, ActionId)>,
131}
132
133impl Bindings {
134    pub fn new() -> Self {
135        Self::default()
136    }
137
138    pub fn key(mut self, key: Key, action: ActionId) -> Self {
139        self.keys.push((key, action));
140        self
141    }
142
143    pub fn action(&self, key: Key) -> Option<ActionId> {
144        self.keys
145            .iter()
146            .find(|(bound, _)| *bound == key)
147            .map(|(_, action)| *action)
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154
155    #[test]
156    fn host_messages_keep_arrival_order_and_latest_returns_the_newest_per_topic() {
157        let mut host = HostMessages::default();
158        host.push("scroll", &[0.25]);
159        host.push("reset", &[]);
160        host.push("scroll", &[0.5, 1.0]);
161
162        let seen: Vec<(&str, &[f32])> = host.iter().collect();
163        assert_eq!(
164            seen,
165            [
166                ("scroll", &[0.25][..]),
167                ("reset", &[][..]),
168                ("scroll", &[0.5, 1.0][..])
169            ]
170        );
171        assert_eq!(host.latest("scroll"), Some(&[0.5, 1.0][..]));
172        assert_eq!(host.latest("reset"), Some(&[][..]));
173        assert_eq!(host.latest("scrol"), None);
174    }
175}