Skip to main content

pawkit_input/
state.rs

1use std::{collections::HashMap, fmt::Debug};
2
3use bitvec::{array::BitArray, slice::BitSlice};
4use pawkit_crockford::Ulid;
5use serde::Serialize;
6
7use crate::{
8    DeviceId,
9    binding::{
10        AnalogBinding, AnalogBindingKind, BoundAxis, BoundButton, DigitalBinding, VectorBinding,
11        VectorBindingKind,
12        axis::{GamepadAxis, MouseAxis},
13        button::{GamepadButton, KeyboardButton, MouseButton},
14    },
15    length_squared,
16};
17
18enum DeviceState {
19    Keyboard(BitArray<[u8; 15]>),
20    Mouse(BitArray<[u8; 1]>, [f32; 4]),
21    Gamepad(BitArray<[u8; 4]>, [f32; 6]),
22}
23
24#[repr(u8)]
25#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
26pub enum InputFamily {
27    Keyboard,
28    Mouse,
29    Gamepad,
30}
31
32pub struct InputState {
33    devices: HashMap<Ulid, DeviceState>,
34}
35
36impl DeviceState {
37    const EMPTY: [f32; 0] = [];
38
39    fn family(&self) -> InputFamily {
40        return match self {
41            Self::Keyboard(_) => InputFamily::Keyboard,
42            Self::Mouse(_, _) => InputFamily::Mouse,
43            Self::Gamepad(_, _) => InputFamily::Gamepad,
44        };
45    }
46
47    fn digital(&self) -> &BitSlice<u8> {
48        return match self {
49            Self::Keyboard(b) => b,
50            Self::Mouse(b, _) => b,
51            Self::Gamepad(b, _) => b,
52        };
53    }
54
55    fn analog(&self) -> &[f32] {
56        return match self {
57            Self::Keyboard(_) => &Self::EMPTY,
58            Self::Mouse(_, a) => a,
59            Self::Gamepad(_, a) => a,
60        };
61    }
62}
63
64impl InputState {
65    pub fn new() -> Self {
66        return Self {
67            devices: HashMap::new(),
68        };
69    }
70
71    #[inline(always)]
72    fn connect_device(&mut self, state: DeviceState) -> DeviceId {
73        let id = Ulid::new();
74
75        self.devices.insert(id, state);
76
77        return DeviceId(id);
78    }
79
80    pub fn device_family(&self, device: &DeviceId) -> Option<InputFamily> {
81        let device = self.devices.get(&device.0)?;
82
83        match device {
84            DeviceState::Keyboard(_) => return Some(InputFamily::Keyboard),
85            DeviceState::Mouse(_, _) => return Some(InputFamily::Mouse),
86            DeviceState::Gamepad(_, _) => return Some(InputFamily::Gamepad),
87        }
88    }
89
90    pub fn connect_keyboard(&mut self) -> DeviceId {
91        return self.connect_device(DeviceState::Keyboard(BitArray::new([0; 15])));
92    }
93
94    pub fn connect_mouse(&mut self) -> DeviceId {
95        return self.connect_device(DeviceState::Mouse(BitArray::new([0]), [0f32; 4]));
96    }
97
98    pub fn connect_gamepad(&mut self) -> DeviceId {
99        return self.connect_device(DeviceState::Gamepad(BitArray::new([0; 4]), [0f32; 6]));
100    }
101
102    pub fn disconnect_device(&mut self, device: &DeviceId) -> bool {
103        return self.devices.remove(&device.0).is_some();
104    }
105
106    pub fn set_keyboard_button(
107        &mut self,
108        device: &DeviceId,
109        button: KeyboardButton,
110        value: bool,
111    ) -> bool {
112        let Some(device) = self.devices.get_mut(&device.0) else {
113            return false;
114        };
115
116        let DeviceState::Keyboard(buttons) = device else {
117            return false;
118        };
119
120        buttons.set(button as usize, value);
121
122        return true;
123    }
124
125    pub fn set_mouse_button(
126        &mut self,
127        device: &DeviceId,
128        button: MouseButton,
129        value: bool,
130    ) -> bool {
131        let Some(device) = self.devices.get_mut(&device.0) else {
132            return false;
133        };
134
135        let DeviceState::Mouse(buttons, _) = device else {
136            return false;
137        };
138
139        buttons.set(button as usize, value);
140
141        return true;
142    }
143
144    pub fn set_gamepad_button(
145        &mut self,
146        device: &DeviceId,
147        button: GamepadButton,
148        value: bool,
149    ) -> bool {
150        let Some(device) = self.devices.get_mut(&device.0) else {
151            return false;
152        };
153
154        let DeviceState::Gamepad(buttons, _) = device else {
155            return false;
156        };
157
158        buttons.set(button as usize, value);
159
160        return true;
161    }
162
163    pub fn set_mouse_axis(&mut self, device: &DeviceId, axis: MouseAxis, value: f32) -> bool {
164        let Some(device) = self.devices.get_mut(&device.0) else {
165            return false;
166        };
167
168        let DeviceState::Mouse(_, axes) = device else {
169            return false;
170        };
171
172        axes[axis as usize] = value;
173
174        return true;
175    }
176
177    pub fn set_gamepad_axis(&mut self, device: &DeviceId, axis: GamepadAxis, value: f32) -> bool {
178        let Some(device) = self.devices.get_mut(&device.0) else {
179            return false;
180        };
181
182        let DeviceState::Gamepad(_, axes) = device else {
183            return false;
184        };
185
186        axes[axis as usize] = value;
187
188        return true;
189    }
190
191    fn get_digital_single<TButton, TAxis>(
192        &self,
193        digital: &BitSlice<u8>,
194        analog: &[f32],
195        button: BoundButton<TButton, TAxis>,
196    ) -> bool
197    where
198        TButton: Debug + Copy + PartialEq + Serialize + Into<usize>,
199        TAxis: Debug + Copy + PartialEq + Serialize + Into<usize>,
200    {
201        match button {
202            BoundButton::Digital { button } => return digital[button.into()],
203            BoundButton::Analog {
204                axis,
205                threshold,
206                scale,
207            } => return (analog[axis.into()] * scale) > threshold,
208        }
209    }
210
211    pub(crate) fn get_digital(
212        &self,
213        device: &DeviceId,
214        bindings: &[DigitalBinding],
215    ) -> Option<bool> {
216        let device = self.devices.get(&device.0)?;
217        let family = device.family();
218        let digital = device.digital();
219        let analog = device.analog();
220
221        for binding in bindings {
222            match binding {
223                DigitalBinding::Keyboard(button) if family == InputFamily::Keyboard => {
224                    if self.get_digital_single(digital, analog, *button) {
225                        return Some(true);
226                    }
227                }
228
229                DigitalBinding::Mouse(button) if family == InputFamily::Mouse => {
230                    if self.get_digital_single(digital, analog, *button) {
231                        return Some(true);
232                    }
233                }
234
235                DigitalBinding::Gamepad(button) if family == InputFamily::Gamepad => {
236                    if self.get_digital_single(digital, analog, *button) {
237                        return Some(true);
238                    }
239                }
240
241                _ => continue,
242            }
243        }
244
245        return Some(false);
246    }
247
248    pub(crate) fn get_analog_single<TButton, TAxis>(
249        &self,
250        digital: &BitSlice<u8>,
251        analog: &[f32],
252        axis: BoundAxis<TButton, TAxis>,
253    ) -> f32
254    where
255        TButton: Debug + Copy + PartialEq + Serialize + Into<usize>,
256        TAxis: Debug + Copy + PartialEq + Serialize + Into<usize>,
257    {
258        match axis {
259            BoundAxis::Analog { axis } => return analog[axis.into()],
260            BoundAxis::Digital { button } => {
261                return if digital[button.into()] { 1f32 } else { 0f32 };
262            }
263            BoundAxis::MultiDigital { negative, positive } => {
264                let mut value = 0f32;
265
266                if digital[positive.into()] {
267                    value += 1f32;
268                }
269
270                if digital[negative.into()] {
271                    value -= 1f32;
272                }
273
274                return value;
275            }
276        }
277    }
278
279    pub(crate) fn get_analog(&self, device: &DeviceId, bindings: &[AnalogBinding]) -> Option<f32> {
280        let device = self.devices.get(&device.0)?;
281        let family = device.family();
282        let digital = device.digital();
283        let analog = device.analog();
284
285        let mut value = 0f32;
286
287        for binding in bindings {
288            match binding.axis {
289                AnalogBindingKind::Keyboard(axis) if family == InputFamily::Keyboard => {
290                    let mut current = self.get_analog_single(digital, analog, axis);
291
292                    if current.abs() < binding.deadzone {
293                        current = 0.;
294                    }
295
296                    let scaled = current * binding.scale;
297
298                    if scaled.abs() > value.abs() {
299                        value = scaled;
300                    }
301                }
302
303                AnalogBindingKind::Mouse(axis) if family == InputFamily::Mouse => {
304                    let mut current = self.get_analog_single(digital, analog, axis);
305
306                    if current.abs() < binding.deadzone {
307                        current = 0.;
308                    }
309
310                    let scaled = current * binding.scale;
311
312                    if scaled.abs() > value.abs() {
313                        value = scaled;
314                    }
315                }
316
317                AnalogBindingKind::Gamepad(axis) if family == InputFamily::Gamepad => {
318                    let mut current = self.get_analog_single(digital, analog, axis);
319
320                    if current.abs() < binding.deadzone {
321                        current = 0.;
322                    }
323
324                    let scaled = current * binding.scale;
325
326                    if scaled.abs() > value.abs() {
327                        value = scaled;
328                    }
329                }
330
331                _ => continue,
332            }
333        }
334
335        return Some(value);
336    }
337
338    pub(crate) fn get_vector(
339        &self,
340        device: &DeviceId,
341        bindings: &[VectorBinding],
342    ) -> Option<[f32; 2]> {
343        let device = self.devices.get(&device.0)?;
344        let family = device.family();
345        let digital = device.digital();
346        let analog = device.analog();
347
348        let mut value = [0f32; 2];
349        let mut value_len_sqr = 0f32;
350
351        for binding in bindings {
352            match binding.axes {
353                VectorBindingKind::Keyboard { x, y } if family == InputFamily::Keyboard => {
354                    let mut current = [
355                        self.get_analog_single(digital, analog, x) * binding.scale.0,
356                        self.get_analog_single(digital, analog, y) * binding.scale.1,
357                    ];
358
359                    for i in 0..2 {
360                        if current[i].abs() < binding.deadzone {
361                            current[i] = 0.;
362                        }
363                    }
364
365                    let current_len_sqr = length_squared(current);
366
367                    if current_len_sqr > value_len_sqr {
368                        value = current;
369                        value_len_sqr = current_len_sqr;
370                    }
371                }
372
373                VectorBindingKind::Mouse { x, y } if family == InputFamily::Mouse => {
374                    let mut current = [
375                        self.get_analog_single(digital, analog, x) * binding.scale.0,
376                        self.get_analog_single(digital, analog, y) * binding.scale.1,
377                    ];
378
379                    for i in 0..2 {
380                        if current[i].abs() < binding.deadzone {
381                            current[i] = 0.;
382                        }
383                    }
384
385                    let current_len_sqr = length_squared(current);
386
387                    if current_len_sqr > value_len_sqr {
388                        value = current;
389                        value_len_sqr = current_len_sqr;
390                    }
391                }
392
393                VectorBindingKind::Gamepad { x, y } if family == InputFamily::Gamepad => {
394                    let mut current = [
395                        self.get_analog_single(digital, analog, x) * binding.scale.0,
396                        self.get_analog_single(digital, analog, y) * binding.scale.1,
397                    ];
398
399                    for i in 0..2 {
400                        if current[i].abs() < binding.deadzone {
401                            current[i] = 0.;
402                        }
403                    }
404
405                    let current_len_sqr = length_squared(current);
406
407                    if current_len_sqr > value_len_sqr {
408                        value = current;
409                        value_len_sqr = current_len_sqr;
410                    }
411                }
412
413                _ => continue,
414            }
415        }
416
417        return Some(value);
418    }
419}