1use std::collections::{BTreeMap, BTreeSet};
2
3use pfx_gpu::window::Viewport;
4use serde::{Deserialize, Serialize};
5
6use crate::action::{ActionId, ActionKind, Actions, Binding, BindingMap, Set, Source};
7use crate::backend::Backend;
8use crate::device::{
9 Axis, Button, Control, DeviceKind, Key, Motors, MouseButton, PadId, PadInfo, Sign, Wheel,
10};
11use crate::event::InputEvent;
12use crate::family::{Family, GlyphStyle};
13use crate::record::{Recording, Start};
14use crate::rumble::{MotorCommand, Rumble, Rumbler};
15
16pub const PRESS: f32 = 0.5;
17pub const POINTER_SWITCH: f32 = 4.0;
18
19#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
20pub struct ActionState {
21 pub held: bool,
22 pub pressed: bool,
23 pub released: bool,
24 pub repeated: bool,
25 pub held_steps: u32,
26 pub value: f32,
27 pub vector: [f32; 2],
28}
29
30impl ActionState {
31 pub fn fired(&self) -> bool {
32 self.pressed || self.repeated
33 }
34}
35
36#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
37pub struct Active {
38 pub kind: DeviceKind,
39 pub pad: Option<PadId>,
40 pub family: Option<Family>,
41}
42
43impl Active {
44 pub fn keyboard() -> Self {
45 Self {
46 kind: DeviceKind::Keyboard,
47 pad: None,
48 family: None,
49 }
50 }
51
52 fn mouse() -> Self {
53 Self {
54 kind: DeviceKind::Mouse,
55 pad: None,
56 family: None,
57 }
58 }
59
60 pub fn set(&self) -> Set {
61 if self.kind.is_gamepad() {
62 Set::Gamepad
63 } else {
64 Set::Keyboard
65 }
66 }
67}
68
69#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
70pub struct Pointer {
71 pub layout: Option<[f32; 2]>,
72 pub inside: bool,
73}
74
75#[derive(Clone, Debug, PartialEq)]
76pub struct Step {
77 pub index: u64,
78 pub changed: Option<Active>,
79 pub style: Option<GlyphStyle>,
80 pub motors: Vec<MotorCommand>,
81}
82
83#[derive(Clone, Copy, Debug, Default, PartialEq)]
84struct Level {
85 value: f32,
86 tapped: bool,
87}
88
89impl Level {
90 fn tap(&self) -> bool {
91 self.tapped && self.value < PRESS
92 }
93
94 fn set(&mut self, value: f32) -> bool {
95 let rising = self.value < PRESS && value >= PRESS;
96 self.value = value;
97 if rising {
98 self.tapped = true;
99 }
100 rising
101 }
102}
103
104#[derive(Clone, Debug)]
105struct Pad {
106 info: PadInfo,
107 buttons: BTreeMap<Button, Level>,
108 axes: BTreeMap<Axis, f32>,
109 actions: BTreeMap<String, [f32; 2]>,
110 origins: BTreeMap<String, Vec<Control>>,
111}
112
113impl Pad {
114 fn new(info: PadInfo) -> Self {
115 Self {
116 info,
117 buttons: BTreeMap::new(),
118 axes: BTreeMap::new(),
119 actions: BTreeMap::new(),
120 origins: BTreeMap::new(),
121 }
122 }
123
124 fn source(&self, source: Source) -> Level {
125 match source {
126 Source::Button(button) => self.buttons.get(&button).copied().unwrap_or_default(),
127 Source::Axis(axis, sign) => {
128 let value = self.axes.get(&axis).copied().unwrap_or(0.0);
129 let value = match sign {
130 Sign::Positive => value,
131 Sign::Negative => -value,
132 };
133 Level {
134 value: value.max(0.0),
135 tapped: false,
136 }
137 }
138 _ => Level::default(),
139 }
140 }
141
142 fn axis(&self, axis: Axis) -> f32 {
143 self.axes.get(&axis).copied().unwrap_or(0.0)
144 }
145}
146
147pub struct Input {
148 actions: Actions,
149 map: BindingMap,
150 resolved: Vec<[Vec<Binding>; 2]>,
151 keys: BTreeMap<Source, Level>,
152 wheel_pending: [f32; 2],
153 wheel: [f32; 2],
154 pads: BTreeMap<PadId, Pad>,
155 states: Vec<ActionState>,
156 pointer: Pointer,
157 outside: BTreeSet<MouseButton>,
158 anchor: Option<[f32; 2]>,
159 viewport: Option<Viewport>,
160 active: Active,
161 reported: Active,
162 reported_style: GlyphStyle,
163 last_pad: Option<PadId>,
164 step: u64,
165 step_us: u32,
166 rumbler: Rumbler,
167 recording: Option<Recording>,
168 pending: Vec<InputEvent>,
169}
170
171impl Input {
172 pub fn new(actions: Actions, step_us: u32) -> Self {
173 let map = BindingMap::defaults(&actions);
174 let states = vec![ActionState::default(); actions.len()];
175 let mut input = Self {
176 actions,
177 map,
178 resolved: Vec::new(),
179 keys: BTreeMap::new(),
180 wheel_pending: [0.0; 2],
181 wheel: [0.0; 2],
182 pads: BTreeMap::new(),
183 states,
184 pointer: Pointer::default(),
185 outside: BTreeSet::new(),
186 anchor: None,
187 viewport: None,
188 active: Active::keyboard(),
189 reported: Active::keyboard(),
190 reported_style: GlyphStyle::Standard,
191 last_pad: None,
192 step: 0,
193 step_us: step_us.max(1),
194 rumbler: Rumbler::new(step_us, 100),
195 recording: None,
196 pending: Vec::new(),
197 };
198 input.resolve();
199 input
200 }
201
202 pub fn actions(&self) -> &Actions {
203 &self.actions
204 }
205
206 pub fn bindings(&self) -> &BindingMap {
207 &self.map
208 }
209
210 pub fn set_bindings(&mut self, mut map: BindingMap) {
211 map.fit(&self.actions);
212 self.map = map;
213 self.resolve();
214 }
215
216 pub fn edit_bindings<R>(&mut self, edit: impl FnOnce(&mut BindingMap, &Actions) -> R) -> R {
217 let result = edit(&mut self.map, &self.actions);
218 self.map.fit(&self.actions);
219 self.resolve();
220 result
221 }
222
223 fn resolve(&mut self) {
224 self.resolved = self
225 .actions
226 .iter()
227 .map(|(_, spec)| {
228 [
229 self.map.bindings(Set::Keyboard, &spec.name).to_vec(),
230 self.map.bindings(Set::Gamepad, &spec.name).to_vec(),
231 ]
232 })
233 .collect();
234 }
235
236 pub fn set_viewport(&mut self, viewport: Option<Viewport>) {
237 self.viewport = viewport;
238 }
239
240 pub fn step_index(&self) -> u64 {
241 self.step
242 }
243
244 pub fn step_us(&self) -> u32 {
245 self.step_us
246 }
247
248 pub fn state(&self, id: ActionId) -> ActionState {
249 self.states[usize::from(id.0)]
250 }
251
252 pub fn action(&self, name: &str) -> ActionState {
253 self.actions
254 .id(name)
255 .map_or_else(ActionState::default, |id| self.state(id))
256 }
257
258 pub fn states(&self) -> &[ActionState] {
259 &self.states
260 }
261
262 pub fn pointer(&self) -> Pointer {
263 self.pointer
264 }
265
266 pub fn wheel(&self) -> [f32; 2] {
267 self.wheel
268 }
269
270 pub fn active(&self) -> Active {
271 self.active
272 }
273
274 pub fn last_family(&self) -> Option<Family> {
275 self.last_pad
276 .and_then(|pad| self.pads.get(&pad))
277 .map(|pad| pad.info.family)
278 }
279
280 pub fn pads(&self) -> impl Iterator<Item = (PadId, &PadInfo)> {
281 self.pads.iter().map(|(id, pad)| (*id, &pad.info))
282 }
283
284 pub fn key_held(&self, key: Key) -> bool {
285 self.keys
286 .get(&Source::Key(key))
287 .is_some_and(|level| level.value >= PRESS)
288 }
289
290 pub fn mouse_held(&self, button: MouseButton) -> bool {
291 self.keys
292 .get(&Source::Mouse(button))
293 .is_some_and(|level| level.value >= PRESS)
294 }
295
296 pub fn mouse_outside(&self, button: MouseButton) -> bool {
297 self.outside.contains(&button)
298 }
299
300 pub fn feed(&mut self, event: InputEvent) {
301 let event = self.normalize(event);
302 self.apply(&event);
303 if self.recording.is_some() {
304 self.pending.push(event);
305 }
306 }
307
308 fn apply(&mut self, event: &InputEvent) {
309 match event {
310 InputEvent::Key { key, pressed } => {
311 let rising = self
312 .keys
313 .entry(Source::Key(*key))
314 .or_default()
315 .set(if *pressed { 1.0 } else { 0.0 });
316 if rising {
317 self.active = Active::keyboard();
318 }
319 }
320 InputEvent::MouseButton { button, pressed } => {
321 if *pressed && self.pointer.layout.is_some() && !self.pointer.inside {
322 self.outside.insert(*button);
323 } else {
324 self.outside.remove(button);
325 }
326 let rising = self
327 .keys
328 .entry(Source::Mouse(*button))
329 .or_default()
330 .set(if *pressed { 1.0 } else { 0.0 });
331 if rising {
332 self.active = Active::mouse();
333 }
334 }
335 InputEvent::Wheel { x, y } => {
336 if x.is_finite() && y.is_finite() {
337 self.wheel_pending[0] += x;
338 self.wheel_pending[1] += y;
339 for (wheel, on) in [
340 (Wheel::Up, *y > 0.0),
341 (Wheel::Down, *y < 0.0),
342 (Wheel::Left, *x < 0.0),
343 (Wheel::Right, *x > 0.0),
344 ] {
345 if on {
346 self.keys.entry(Source::Wheel(wheel)).or_default().tapped = true;
347 self.active = Active::mouse();
348 }
349 }
350 }
351 }
352 InputEvent::PointerMoved { .. } => {}
353 InputEvent::PointerAt { layout, inside } => {
354 match self.anchor {
355 Some(anchor) if self.active.kind != DeviceKind::Mouse => {
356 let dx = layout[0] - anchor[0];
357 let dy = layout[1] - anchor[1];
358 if dx * dx + dy * dy >= POINTER_SWITCH * POINTER_SWITCH {
359 self.active = Active::mouse();
360 self.anchor = Some(*layout);
361 }
362 }
363 _ => self.anchor = Some(*layout),
364 }
365 self.pointer = Pointer {
366 layout: Some(*layout),
367 inside: *inside,
368 };
369 }
370 InputEvent::PointerLeft => {
371 self.pointer = Pointer::default();
372 self.anchor = None;
373 }
374 InputEvent::FocusLost => {
375 for level in self.keys.values_mut() {
376 level.value = 0.0;
377 }
378 self.outside.clear();
379 }
380 InputEvent::PadConnected { pad, info } => {
381 self.pads.insert(*pad, Pad::new(info.clone()));
382 if self.active.pad == Some(*pad) {
383 self.active.family = Some(info.family);
384 }
385 }
386 InputEvent::PadDisconnected { pad } => {
387 self.pads.remove(pad);
388 self.rumbler.forget(*pad);
389 if self.last_pad == Some(*pad) {
390 self.last_pad = None;
391 }
392 if self.active.pad == Some(*pad) {
393 self.active = Active::keyboard();
394 }
395 }
396 InputEvent::PadButton { pad, button, value } => {
397 let value = finite_unit(*value);
398 let rising = self
399 .pad_mut(*pad)
400 .buttons
401 .entry(*button)
402 .or_default()
403 .set(value);
404 if rising {
405 self.touch_pad(*pad);
406 }
407 }
408 InputEvent::PadAxis { pad, axis, value } => {
409 let value = if value.is_finite() {
410 value.clamp(-1.0, 1.0)
411 } else {
412 0.0
413 };
414 let entry = self.pad_mut(*pad);
415 let before = entry.axis(*axis).abs();
416 entry.axes.insert(*axis, value);
417 if before < PRESS && value.abs() >= PRESS {
418 self.touch_pad(*pad);
419 }
420 }
421 InputEvent::PadAction { pad, action, value } => {
422 let value = [finite_signed(value[0]), finite_signed(value[1])];
423 let entry = self.pad_mut(*pad);
424 let before = entry.actions.get(action).copied().unwrap_or([0.0; 2]);
425 entry.actions.insert(action.clone(), value);
426 if magnitude(before) < PRESS && magnitude(value) >= PRESS {
427 self.touch_pad(*pad);
428 }
429 }
430 InputEvent::PadOrigins {
431 pad,
432 action,
433 controls,
434 } => {
435 self.pad_mut(*pad)
436 .origins
437 .insert(action.clone(), controls.clone());
438 }
439 }
440 }
441
442 fn normalize(&self, event: InputEvent) -> InputEvent {
443 match event {
444 InputEvent::PointerMoved { window } => match self.viewport {
445 Some(viewport) => InputEvent::PointerAt {
446 layout: viewport.pointer_to_layout(window[0], window[1]),
447 inside: viewport.pointer_inside(window[0], window[1]),
448 },
449 None => InputEvent::PointerAt {
450 layout: window,
451 inside: true,
452 },
453 },
454 InputEvent::Key { .. }
455 | InputEvent::MouseButton { .. }
456 | InputEvent::Wheel { .. }
457 | InputEvent::PointerAt { .. }
458 | InputEvent::PointerLeft
459 | InputEvent::FocusLost
460 | InputEvent::PadConnected { .. }
461 | InputEvent::PadDisconnected { .. }
462 | InputEvent::PadButton { .. }
463 | InputEvent::PadAxis { .. }
464 | InputEvent::PadAction { .. }
465 | InputEvent::PadOrigins { .. } => event,
466 }
467 }
468
469 fn pad_mut(&mut self, pad: PadId) -> &mut Pad {
470 self.pads.entry(pad).or_insert_with(|| {
471 Pad::new(PadInfo {
472 name: String::new(),
473 vendor: None,
474 product: None,
475 family: Family::Generic,
476 motors: Motors::None,
477 resolves_actions: false,
478 })
479 })
480 }
481
482 fn touch_pad(&mut self, pad: PadId) {
483 let family = self.pads.get(&pad).map(|pad| pad.info.family);
484 self.last_pad = Some(pad);
485 self.active = Active {
486 kind: DeviceKind::Gamepad,
487 pad: Some(pad),
488 family,
489 };
490 }
491
492 pub fn step(&mut self) -> Step {
493 for index in 0..self.states.len() {
494 let id = ActionId(index as u16);
495 let (held, value, vector) = self.evaluate(id);
496 let spec = self.actions.spec(id);
497 let repeat = spec.repeat;
498 let state = &mut self.states[index];
499 let was = state.held;
500 state.pressed = held && !was;
501 state.released = !held && was;
502 state.held_steps = if held && was {
503 state.held_steps.saturating_add(1)
504 } else {
505 0
506 };
507 state.repeated = held
508 && was
509 && repeat.is_some_and(|repeat| {
510 state.held_steps >= repeat.delay
511 && (state.held_steps - repeat.delay).is_multiple_of(repeat.interval)
512 });
513 state.held = held;
514 state.value = value;
515 state.vector = vector;
516 }
517 for level in self.keys.values_mut() {
518 level.tapped = false;
519 }
520 for pad in self.pads.values_mut() {
521 for level in pad.buttons.values_mut() {
522 level.tapped = false;
523 }
524 }
525 self.wheel = self.wheel_pending;
526 self.wheel_pending = [0.0; 2];
527 let changed = (self.active != self.reported).then_some(self.active);
528 self.reported = self.active;
529 let style = self.glyph_style();
530 let style_changed = (style != self.reported_style).then_some(style);
531 self.reported_style = style;
532 let motors = self.rumbler.step();
533 if let Some(recording) = &mut self.recording {
534 recording.steps.push(std::mem::take(&mut self.pending));
535 }
536 let index = self.step;
537 self.step += 1;
538 Step {
539 index,
540 changed,
541 style: style_changed,
542 motors,
543 }
544 }
545
546 pub fn update(&mut self, backend: &mut impl Backend) -> Step {
547 let mut events = Vec::new();
548 backend.poll(&mut events);
549 for event in events {
550 self.feed(event);
551 }
552 let step = self.step();
553 for command in &step.motors {
554 backend.set_motors(command);
555 }
556 step
557 }
558
559 fn level(&self, source: Source, set: Set) -> Level {
560 match set {
561 Set::Keyboard => match source {
562 Source::Mouse(button) if self.outside.contains(&button) => Level::default(),
563 _ => self.keys.get(&source).copied().unwrap_or_default(),
564 },
565 Set::Gamepad => {
566 let mut best = Level::default();
567 for pad in self.pads.values() {
568 if pad.info.resolves_actions {
569 continue;
570 }
571 let level = pad.source(source);
572 best.value = best.value.max(level.value);
573 best.tapped |= level.tapped;
574 }
575 best
576 }
577 }
578 }
579
580 fn stick_axes(&self, x: Axis, y: Axis) -> [f32; 2] {
581 let mut best = [0.0f32; 2];
582 for pad in self.pads.values() {
583 if pad.info.resolves_actions {
584 continue;
585 }
586 let v = [pad.axis(x), pad.axis(y)];
587 if magnitude(v) > magnitude(best) {
588 best = v;
589 }
590 }
591 best
592 }
593
594 fn evaluate(&self, id: ActionId) -> (bool, f32, [f32; 2]) {
595 let spec = self.actions.spec(id);
596 let dead_zone = spec.dead_zone;
597 let mut held = false;
598 let mut best = [0.0f32; 2];
599 for (set, bindings) in [Set::Keyboard, Set::Gamepad]
600 .into_iter()
601 .zip(&self.resolved[usize::from(id.0)])
602 {
603 for binding in bindings {
604 let (tapped, vector) = self.binding_value(binding, set, dead_zone);
605 held |= tapped;
606 if magnitude(vector) > magnitude(best) {
607 best = vector;
608 }
609 }
610 }
611 for pad in self.pads.values() {
612 if !pad.info.resolves_actions {
613 continue;
614 }
615 if let Some(value) = pad.actions.get(&spec.name) {
616 let vector = match spec.kind {
617 ActionKind::Digital | ActionKind::Axis => [value[0], 0.0],
618 ActionKind::Stick => radial(*value, dead_zone),
619 };
620 if magnitude(vector) > magnitude(best) {
621 best = vector;
622 }
623 }
624 }
625 held |= magnitude(best) >= PRESS;
626 match spec.kind {
627 ActionKind::Digital => {
628 let value = magnitude(best).min(1.0);
629 (held, value, [value, 0.0])
630 }
631 ActionKind::Axis => (held, best[0], [best[0], 0.0]),
632 ActionKind::Stick => (held, magnitude(best), best),
633 }
634 }
635
636 fn binding_value(&self, binding: &Binding, set: Set, dead_zone: f32) -> (bool, [f32; 2]) {
637 match *binding {
638 Binding::Press(source) => {
639 let level = self.level(source, set);
640 (level.tap(), [axial(level.value, dead_zone), 0.0])
641 }
642 Binding::Axis { negative, positive } => {
643 let negative = self.level(negative, set);
644 let positive = self.level(positive, set);
645 (
646 negative.tap() || positive.tap(),
647 [axial(positive.value - negative.value, dead_zone), 0.0],
648 )
649 }
650 Binding::PadAxis(axis) => {
651 if set != Set::Gamepad {
652 return (false, [0.0; 2]);
653 }
654 let (x, y) = axis.stick().axes();
655 let v = self.stick_axes(x, y);
656 let value = if axis == x { v[0] } else { v[1] };
657 let vector = match axis {
658 Axis::LeftX | Axis::RightX => [axial(value, dead_zone), 0.0],
659 Axis::LeftY | Axis::RightY => [0.0, axial(value, dead_zone)],
660 };
661 (false, vector)
662 }
663 Binding::Stick {
664 up,
665 down,
666 left,
667 right,
668 } => {
669 let up = self.level(up, set);
670 let down = self.level(down, set);
671 let left = self.level(left, set);
672 let right = self.level(right, set);
673 let vector = radial([right.value - left.value, up.value - down.value], dead_zone);
674 (up.tap() || down.tap() || left.tap() || right.tap(), vector)
675 }
676 Binding::PadStick(stick) => {
677 if set != Set::Gamepad {
678 return (false, [0.0; 2]);
679 }
680 let (x, y) = stick.axes();
681 (false, radial(self.stick_axes(x, y), dead_zone))
682 }
683 }
684 }
685
686 pub fn prompt(&self, name: &str) -> Vec<Control> {
687 let set = self.active.set();
688 if set == Set::Gamepad
689 && let Some(pad) = self.active.pad.and_then(|pad| self.pads.get(&pad))
690 && pad.info.resolves_actions
691 {
692 return pad.origins.get(name).cloned().unwrap_or_default();
693 }
694 let bindings = self.map.bindings(set, name);
695 let chosen = match self.active.kind {
696 DeviceKind::Mouse => bindings
697 .iter()
698 .find(|binding| binding.sources().iter().any(|source| source.is_mouse()))
699 .or_else(|| bindings.first()),
700 DeviceKind::Keyboard => bindings
701 .iter()
702 .find(|binding| binding.sources().iter().all(|source| source.is_keyboard()))
703 .or_else(|| bindings.first()),
704 DeviceKind::Gamepad => bindings.first(),
705 };
706 chosen.map(Binding::controls).unwrap_or_default()
707 }
708
709 pub fn prompt_family(&self) -> Family {
710 self.active
711 .family
712 .or_else(|| self.last_family())
713 .unwrap_or(Family::Generic)
714 }
715
716 pub fn glyph_style(&self) -> GlyphStyle {
717 self.prompt_family().style()
718 }
719
720 pub fn rumble_intensity(&self) -> u8 {
721 self.rumbler.intensity()
722 }
723
724 pub fn set_rumble_intensity(&mut self, percent: u8) {
725 self.rumbler.set_intensity(percent);
726 }
727
728 pub fn rumble(&mut self, rumble: Rumble) -> bool {
729 match (self.active.kind, self.active.pad) {
730 (DeviceKind::Gamepad, Some(pad)) => self.rumble_pad(pad, rumble),
731 _ => false,
732 }
733 }
734
735 pub fn rumble_pad(&mut self, pad: PadId, rumble: Rumble) -> bool {
736 let Some(motors) = self.pads.get(&pad).map(|pad| pad.info.motors) else {
737 return false;
738 };
739 self.rumbler.play(pad, motors, rumble)
740 }
741
742 pub fn start_recording(&mut self) {
743 let mut prelude = Vec::new();
744 for (pad, state) in &self.pads {
745 prelude.push(InputEvent::PadConnected {
746 pad: *pad,
747 info: state.info.clone(),
748 });
749 for (button, level) in &state.buttons {
750 if level.value != 0.0 {
751 prelude.push(InputEvent::PadButton {
752 pad: *pad,
753 button: *button,
754 value: level.value,
755 });
756 }
757 }
758 for (axis, value) in &state.axes {
759 if *value != 0.0 {
760 prelude.push(InputEvent::PadAxis {
761 pad: *pad,
762 axis: *axis,
763 value: *value,
764 });
765 }
766 }
767 for (action, value) in &state.actions {
768 prelude.push(InputEvent::PadAction {
769 pad: *pad,
770 action: action.clone(),
771 value: *value,
772 });
773 }
774 for (action, controls) in &state.origins {
775 prelude.push(InputEvent::PadOrigins {
776 pad: *pad,
777 action: action.clone(),
778 controls: controls.clone(),
779 });
780 }
781 }
782 for (source, level) in &self.keys {
783 if level.value < PRESS {
784 continue;
785 }
786 match source {
787 Source::Key(key) => prelude.push(InputEvent::Key {
788 key: *key,
789 pressed: true,
790 }),
791 Source::Mouse(button) => prelude.push(InputEvent::MouseButton {
792 button: *button,
793 pressed: true,
794 }),
795 Source::Wheel(_) | Source::Button(_) | Source::Axis(..) => {}
796 }
797 }
798 if let Some(layout) = self.pointer.layout {
799 prelude.push(InputEvent::PointerAt {
800 layout,
801 inside: self.pointer.inside,
802 });
803 }
804 self.pending.clear();
805 self.recording = Some(Recording::new(
806 self.step_us,
807 Start {
808 events: prelude,
809 states: self.states.clone(),
810 active: self.active,
811 last_pad: self.last_pad,
812 anchor: self.anchor,
813 outside: self.outside.iter().copied().collect(),
814 },
815 ));
816 }
817
818 pub fn begin_replay(&mut self, recording: &Recording) {
819 let start = &recording.start;
820 for event in &start.events {
821 self.apply(event);
822 }
823 if start.states.len() == self.states.len() {
824 self.states.clone_from(&start.states);
825 }
826 self.active = start.active;
827 self.reported = start.active;
828 self.last_pad = start.last_pad;
829 self.reported_style = self.glyph_style();
830 self.anchor = start.anchor;
831 self.outside = start.outside.iter().copied().collect();
832 for level in self.keys.values_mut() {
833 level.tapped = false;
834 }
835 for pad in self.pads.values_mut() {
836 for level in pad.buttons.values_mut() {
837 level.tapped = false;
838 }
839 }
840 }
841
842 pub fn stop_recording(&mut self) -> Option<Recording> {
843 self.pending.clear();
844 self.recording.take()
845 }
846
847 pub fn recording(&self) -> Option<&Recording> {
848 self.recording.as_ref()
849 }
850
851 pub fn is_recording(&self) -> bool {
852 self.recording.is_some()
853 }
854}
855
856fn finite_unit(value: f32) -> f32 {
857 if value.is_finite() {
858 value.clamp(0.0, 1.0)
859 } else {
860 0.0
861 }
862}
863
864fn finite_signed(value: f32) -> f32 {
865 if value.is_finite() {
866 value.clamp(-1.0, 1.0)
867 } else {
868 0.0
869 }
870}
871
872fn magnitude(v: [f32; 2]) -> f32 {
873 v[0].hypot(v[1])
874}
875
876pub fn axial(value: f32, dead_zone: f32) -> f32 {
877 let value = finite_signed(value);
878 let dead_zone = dead_zone.clamp(0.0, 0.95);
879 let size = value.abs();
880 if size <= dead_zone {
881 return 0.0;
882 }
883 value.signum() * ((size - dead_zone) / (1.0 - dead_zone)).min(1.0)
884}
885
886pub fn radial(v: [f32; 2], dead_zone: f32) -> [f32; 2] {
887 let v = [finite_signed(v[0]), finite_signed(v[1])];
888 let dead_zone = dead_zone.clamp(0.0, 0.95);
889 let size = magnitude(v);
890 if size <= dead_zone {
891 return [0.0; 2];
892 }
893 let scaled = ((size - dead_zone) / (1.0 - dead_zone)).min(1.0);
894 [v[0] / size * scaled, v[1] / size * scaled]
895}