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#[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#[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#[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 pub look: [f32; 2],
85 pub cursor_locked: bool,
86 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}