input.rsannotatedinput.rssource1218 lines · 43.8 KB · raw
1use jgenesis_common::frontend::DisplayInfo;
2use jgenesis_common::input::Player;
3use jgenesis_native_config::input::{
4    AxisDirection, GamepadAction, GenericInput, HatDirection, Hotkey, KeyboardInput,
5};
6use rustc_hash::{FxHashMap, FxHashSet};
7use sdl3::event::{Event, WindowEvent};
8use sdl3::joystick::{HatState, Joystick};
9use sdl3::keyboard::{Keycode, Scancode};
10use sdl3::sys::everything::SDL_JoystickID;
11use sdl3::{IntegerOrSdlError, JoystickSubsystem};
12use std::array;
13use std::cell::RefCell;
14use std::fmt::Debug;
15use std::hash::Hash;
16use std::ops::Deref;
17use std::rc::Rc;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
20pub(crate) struct CanonicalInput(GenericInput);
21
22fn canonicalize_keycode(keycode: Keycode) -> Keycode {
23    match keycode {
24        Keycode::RShift => Keycode::LShift,
25        Keycode::RCtrl => Keycode::LCtrl,
26        Keycode::RAlt => Keycode::LAlt,
27        _ => keycode,
28    }
29}
30
31fn canonicalize_scancode(scancode: Scancode) -> Scancode {
32    match scancode {
33        Scancode::RShift => Scancode::LShift,
34        Scancode::RCtrl => Scancode::LCtrl,
35        Scancode::RAlt => Scancode::LAlt,
36        _ => scancode,
37    }
38}
39
40fn canonicalize_key(key: KeyboardInput) -> KeyboardInput {
41    match key {
42        KeyboardInput::Keycode(keycode) => KeyboardInput::Keycode(canonicalize_keycode(keycode)),
43        KeyboardInput::Scancode(scancode) => {
44            KeyboardInput::Scancode(canonicalize_scancode(scancode))
45        }
46    }
47}
48
49impl CanonicalInput {
50    pub(crate) fn canonicalize(input: GenericInput) -> Self {
51        match input {
52            GenericInput::Keyboard(key) => Self(GenericInput::Keyboard(canonicalize_key(key))),
53            _ => Self(input),
54        }
55    }
56
57    pub(crate) fn reverse_canonicalize(self) -> Option<&'static [GenericInput]> {
58        match self.0 {
59            GenericInput::Keyboard(KeyboardInput::Keycode(Keycode::LShift)) => Some(&[
60                GenericInput::Keyboard(KeyboardInput::Keycode(Keycode::LShift)),
61                GenericInput::Keyboard(KeyboardInput::Keycode(Keycode::RShift)),
62            ]),
63            GenericInput::Keyboard(KeyboardInput::Keycode(Keycode::LCtrl)) => Some(&[
64                GenericInput::Keyboard(KeyboardInput::Keycode(Keycode::LCtrl)),
65                GenericInput::Keyboard(KeyboardInput::Keycode(Keycode::RCtrl)),
66            ]),
67            GenericInput::Keyboard(KeyboardInput::Keycode(Keycode::LAlt)) => Some(&[
68                GenericInput::Keyboard(KeyboardInput::Keycode(Keycode::LAlt)),
69                GenericInput::Keyboard(KeyboardInput::Keycode(Keycode::RAlt)),
70            ]),
71            GenericInput::Keyboard(KeyboardInput::Scancode(Scancode::LShift)) => Some(&[
72                GenericInput::Keyboard(KeyboardInput::Scancode(Scancode::LShift)),
73                GenericInput::Keyboard(KeyboardInput::Scancode(Scancode::RShift)),
74            ]),
75            GenericInput::Keyboard(KeyboardInput::Scancode(Scancode::LCtrl)) => Some(&[
76                GenericInput::Keyboard(KeyboardInput::Scancode(Scancode::LCtrl)),
77                GenericInput::Keyboard(KeyboardInput::Scancode(Scancode::RCtrl)),
78            ]),
79            GenericInput::Keyboard(KeyboardInput::Scancode(Scancode::LAlt)) => Some(&[
80                GenericInput::Keyboard(KeyboardInput::Scancode(Scancode::LAlt)),
81                GenericInput::Keyboard(KeyboardInput::Scancode(Scancode::RAlt)),
82            ]),
83            _ => None,
84        }
85    }
86}
87
88pub const MAX_MAPPING_LEN: usize = 3;
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
91pub enum GenericButton<Button> {
92    Button(Button, Player),
93    TurboButton(Button, Player),
94    Hotkey(Hotkey),
95}
96
97#[derive(Debug, Clone, Copy)]
98pub enum InputEvent<Button> {
99    Button { button: Button, player: Player, pressed: bool },
100    AnalogValueChange { button: Button, player: Player, value: i16 },
101    MouseMotion { position: (f32, f32), delta: (f32, f32), display_info: DisplayInfo },
102    MouseLeave,
103    Hotkey { hotkey: Hotkey, pressed: bool },
104}
105
106pub struct InitialAxisDirections(pub Vec<(u8, AxisDirection)>);
107
108impl Deref for InitialAxisDirections {
109    type Target = Vec<(u8, AxisDirection)>;
110
111    fn deref(&self) -> &Self::Target {
112        &self.0
113    }
114}
115
116pub struct Joysticks {
117    subsystem: JoystickSubsystem,
118    open_joysticks: Vec<(Joystick, InitialAxisDirections)>,
119    joystick_id_to_device_id: FxHashMap<u32, u32>,
120    device_id_to_idx: FxHashMap<u32, usize>,
121}
122
123impl Joysticks {
124    #[must_use]
125    pub fn new(subsystem: JoystickSubsystem) -> Self {
126        Self {
127            subsystem,
128            open_joysticks: Vec::new(),
129            joystick_id_to_device_id: FxHashMap::default(),
130            device_id_to_idx: FxHashMap::default(),
131        }
132    }
133
134    #[allow(clippy::missing_errors_doc)]
135    pub fn handle_sdl_event(&mut self, event: &Event) -> Result<(), IntegerOrSdlError> {
136        match *event {
137            Event::JoyDeviceAdded { which: joystick_id, .. } => {
138                self.handle_device_added(joystick_id)?;
139            }
140            Event::JoyDeviceRemoved { which: joystick_id, .. } => {
141                self.handle_device_removed(joystick_id).map_err(IntegerOrSdlError::SdlError)?;
142            }
143            _ => {}
144        }
145
146        Ok(())
147    }
148
149    #[allow(clippy::missing_errors_doc)]
150    pub fn handle_device_added(&mut self, joystick_id: u32) -> Result<(), IntegerOrSdlError> {
151        if self.joystick_id_to_device_id.contains_key(&joystick_id) {
152            return Ok(());
153        }
154
155        let joystick = self.subsystem.open(SDL_JoystickID(joystick_id))?;
156
157        let name = joystick.name();
158
159        let initial_axis_directions = read_initial_axis_directions(&joystick);
160
161        self.open_joysticks.push((joystick, initial_axis_directions));
162        self.regenerate_id_maps().map_err(IntegerOrSdlError::SdlError)?;
163
164        if let Some(&device_id) = self.joystick_id_to_device_id.get(&joystick_id) {
165            log::info!("Added joystick ID {joystick_id}: '{name}' (Device ID {device_id})");
166        }
167
168        Ok(())
169    }
170
171    #[allow(clippy::missing_errors_doc)]
172    pub fn handle_device_removed(&mut self, joystick_id: u32) -> Result<(), sdl3::Error> {
173        let device_id = self.joystick_id_to_device_id.get(&joystick_id).copied();
174
175        self.open_joysticks.retain(|(joystick, _)| joystick.id() != joystick_id);
176        self.regenerate_id_maps()?;
177
178        if let Some(device_id) = device_id {
179            log::info!("Removed joystick ID {joystick_id} (Device ID {device_id})");
180        }
181
182        Ok(())
183    }
184
185    fn regenerate_id_maps(&mut self) -> Result<(), sdl3::Error> {
186        // Clear maps before making joysticks() call that could potentially return an error
187        self.joystick_id_to_device_id.clear();
188        self.device_id_to_idx.clear();
189
190        let joystick_ids = self.subsystem.joysticks()?;
191        for (device_id, joystick_id) in joystick_ids.into_iter().enumerate() {
192            let Some(idx) =
193                self.open_joysticks.iter().position(|(joystick, _)| joystick.id() == joystick_id.0)
194            else {
195                continue;
196            };
197
198            self.joystick_id_to_device_id.insert(joystick_id.0, device_id as u32);
199            self.device_id_to_idx.insert(device_id as u32, idx);
200        }
201
202        Ok(())
203    }
204
205    #[must_use]
206    pub fn map_to_device_id(&mut self, instance_id: u32) -> Option<u32> {
207        self.joystick_id_to_device_id.get(&instance_id).copied()
208    }
209
210    #[must_use]
211    pub fn device(&self, device_id: u32) -> Option<&Joystick> {
212        self.device_id_to_idx.get(&device_id).map(|&idx| &self.open_joysticks[idx].0)
213    }
214
215    #[must_use]
216    pub fn initial_axis_directions(
217        &self,
218        device_id: u32,
219    ) -> Option<impl Iterator<Item = (u8, AxisDirection)>> {
220        self.device_id_to_idx.get(&device_id).map(|&idx| self.open_joysticks[idx].1.iter().copied())
221    }
222
223    pub fn all_devices(
224        &self,
225    ) -> impl Iterator<Item = (u32, &'_ (Joystick, InitialAxisDirections))> + '_ {
226        self.device_id_to_idx
227            .iter()
228            .map(|(&device_id, &idx)| (device_id, &self.open_joysticks[idx]))
229    }
230}
231
232fn read_initial_axis_directions(joystick: &Joystick) -> InitialAxisDirections {
233    InitialAxisDirections(
234        (0..joystick.num_axes())
235            .filter_map(|axis_id| {
236                let value = joystick.axis(axis_id).ok()?;
237                if value > 30000 {
238                    Some((axis_id as u8, AxisDirection::Positive))
239                } else if value < -30000 {
240                    Some((axis_id as u8, AxisDirection::Negative))
241                } else {
242                    None
243                }
244            })
245            .collect(),
246    )
247}
248
249struct InputMapperState<Button> {
250    input_events: Rc<RefCell<Vec<InputEvent<Button>>>>,
251    mappings: FxHashMap<GenericButton<Button>, Vec<Vec<CanonicalInput>>>,
252    inputs_to_buttons: FxHashMap<CanonicalInput, Vec<GenericButton<Button>>>,
253    active_inputs: FxHashSet<GenericInput>,
254    active_canonical_inputs: FxHashSet<CanonicalInput>,
255    active_turbo_buttons: FxHashMap<(Button, Player), bool>,
256    active_hotkeys: FxHashSet<Hotkey>,
257    changed_button_buffers: [Vec<GenericButton<Button>>; MAX_MAPPING_LEN + 1],
258}
259
260impl<Button> InputMapperState<Button>
261where
262    Button: Debug + Copy + Hash + Eq,
263{
264    fn new() -> Self {
265        Self {
266            input_events: Rc::new(RefCell::new(Vec::with_capacity(10))),
267            mappings: FxHashMap::default(),
268            inputs_to_buttons: FxHashMap::default(),
269            active_inputs: FxHashSet::default(),
270            active_canonical_inputs: FxHashSet::default(),
271            active_turbo_buttons: FxHashMap::default(),
272            active_hotkeys: FxHashSet::default(),
273            changed_button_buffers: array::from_fn(|_| Vec::with_capacity(10)),
274        }
275    }
276
277    fn update_mappings(
278        &mut self,
279        button_mappings: &[((Button, Player), &Vec<GenericInput>)],
280        turbo_mappings: &[((Button, Player), &Vec<GenericInput>)],
281        hotkey_mappings: &[(Hotkey, &Vec<GenericInput>)],
282    ) {
283        self.mappings.clear();
284        self.inputs_to_buttons.clear();
285        self.active_inputs.clear();
286        self.active_canonical_inputs.clear();
287        self.active_turbo_buttons.clear();
288        self.active_hotkeys.clear();
289
290        for (mappings, turbo) in [(button_mappings, false), (turbo_mappings, true)] {
291            for &((button, player), mapping) in mappings {
292                if mapping.len() > MAX_MAPPING_LEN {
293                    log::error!("Ignoring mapping, too many inputs: {mapping:?}");
294                    continue;
295                }
296
297                let generic_button = if turbo {
298                    GenericButton::TurboButton(button, player)
299                } else {
300                    GenericButton::Button(button, player)
301                };
302                self.mappings
303                    .entry(generic_button)
304                    .or_default()
305                    .push(mapping.iter().copied().map(CanonicalInput::canonicalize).collect());
306
307                for &mapping_input in mapping {
308                    self.inputs_to_buttons
309                        .entry(CanonicalInput::canonicalize(mapping_input))
310                        .or_default()
311                        .push(generic_button);
312                }
313            }
314        }
315
316        for &(hotkey, mapping) in hotkey_mappings {
317            if mapping.len() > MAX_MAPPING_LEN {
318                log::error!("Ignoring mapping, too many inputs: {mapping:?}");
319                continue;
320            }
321
322            let generic_button = GenericButton::Hotkey(hotkey);
323            self.mappings
324                .entry(generic_button)
325                .or_default()
326                .push(mapping.iter().copied().map(CanonicalInput::canonicalize).collect());
327
328            for &mapping_input in mapping {
329                self.inputs_to_buttons
330                    .entry(CanonicalInput::canonicalize(mapping_input))
331                    .or_default()
332                    .push(generic_button);
333            }
334        }
335    }
336
337    fn handle_input(&mut self, raw_input: GenericInput, pressed: bool) {
338        if pressed && !self.active_inputs.insert(raw_input) {
339            // Input is already pressed
340            return;
341        } else if !pressed && !self.active_inputs.remove(&raw_input) {
342            // Input is already released
343            return;
344        }
345
346        let input = CanonicalInput::canonicalize(raw_input);
347        if let Some(raw_inputs) = input.reverse_canonicalize() {
348            for &other_raw_input in raw_inputs {
349                if other_raw_input == raw_input {
350                    continue;
351                }
352
353                if self.active_inputs.contains(&other_raw_input) {
354                    // Mapping will not change as a result of this press/release
355                    return;
356                }
357            }
358        }
359
360        if pressed {
361            self.active_canonical_inputs.insert(input);
362        } else {
363            self.active_canonical_inputs.remove(&input);
364        }
365
366        self.handle_canonical_input(input, pressed);
367    }
368
369    fn handle_canonical_input(&mut self, input: CanonicalInput, pressed: bool) {
370        let Some(buttons) = self.inputs_to_buttons.get(&input) else { return };
371
372        log::debug!("Input {input:?}, pressed={pressed}, buttons={buttons:?}");
373
374        for buffer in &mut self.changed_button_buffers {
375            buffer.clear();
376        }
377
378        for &button in buttons {
379            let Some(mappings) = self.mappings.get(&button) else { continue };
380
381            log::debug!("Mappings: {mappings:?}");
382
383            let other_mappings_pressed = mappings.iter().any(|mapping| {
384                mapping.iter().all(|&mapping_input| {
385                    mapping_input != input && self.active_canonical_inputs.contains(&mapping_input)
386                })
387            });
388            if other_mappings_pressed {
389                // Mappings that don't contain this input are still pressed; button state will not change
390                continue;
391            }
392
393            for mapping in mappings {
394                let mut contains_new_input = false;
395                let mut all_others_pressed = true;
396                for &mapping_input in mapping {
397                    contains_new_input |= mapping_input == input;
398                    all_others_pressed &= mapping_input == input
399                        || self.active_canonical_inputs.contains(&mapping_input);
400                }
401
402                if contains_new_input && all_others_pressed {
403                    // This was the only input in the mapping that was not already pressed, so
404                    // button state will change
405                    self.changed_button_buffers[mapping.len()].push(button);
406                }
407            }
408        }
409
410        // Iterate in reverse mapping length order
411        for changed_buttons in self.changed_button_buffers.iter().rev() {
412            if changed_buttons.is_empty() {
413                continue;
414            }
415
416            for &button in changed_buttons {
417                log::debug!("Button state changed! button={button:?} pressed={pressed}");
418
419                match button {
420                    GenericButton::Button(button, player) => {
421                        self.input_events.borrow_mut().push(InputEvent::Button {
422                            button,
423                            player,
424                            pressed,
425                        });
426                    }
427                    GenericButton::TurboButton(button, player) => {
428                        if pressed {
429                            self.active_turbo_buttons.insert((button, player), true);
430                        } else {
431                            self.active_turbo_buttons.remove(&(button, player));
432                        }
433                        self.input_events.borrow_mut().push(InputEvent::Button {
434                            button,
435                            player,
436                            pressed,
437                        });
438                    }
439                    GenericButton::Hotkey(hotkey) => {
440                        if pressed && self.active_hotkeys.insert(hotkey) {
441                            self.input_events
442                                .borrow_mut()
443                                .push(InputEvent::Hotkey { hotkey, pressed: true });
444                        } else if !pressed && self.active_hotkeys.remove(&hotkey) {
445                            self.input_events
446                                .borrow_mut()
447                                .push(InputEvent::Hotkey { hotkey, pressed: false });
448                        }
449                    }
450                }
451            }
452
453            if pressed {
454                // On input presses, only count a button/hotkey as pressed if the combination length
455                // is the maximum out of all combinations that changed state.
456                //
457                // This is to handle cases like e.g. Shift+F1 and F1 being mapped to different
458                // hotkeys, where only the Shift+F1 mapping should change state when Shift is pressed
459                // first and F1 is pressed second.
460                //
461                // There is probably a more robust way to do this - maybe making the input order significant?
462                break;
463            }
464        }
465    }
466
467    fn handle_axis_input(
468        &mut self,
469        axis_deadzone: i16,
470        gamepad_idx: u32,
471        axis_idx: u8,
472        value: i16,
473    ) {
474        let magnitude = value.saturating_abs();
475        let pressed = magnitude > axis_deadzone;
476        let pressed_direction = AxisDirection::from_value(value);
477
478        if pressed {
479            self.handle_input(
480                GenericInput::Gamepad {
481                    gamepad_idx,
482                    action: GamepadAction::Axis(axis_idx, pressed_direction.inverse()),
483                },
484                false,
485            );
486            self.handle_input(
487                GenericInput::Gamepad {
488                    gamepad_idx,
489                    action: GamepadAction::Axis(axis_idx, pressed_direction),
490                },
491                true,
492            );
493        } else {
494            for direction in [AxisDirection::Positive, AxisDirection::Negative] {
495                self.handle_input(
496                    GenericInput::Gamepad {
497                        gamepad_idx,
498                        action: GamepadAction::Axis(axis_idx, direction),
499                    },
500                    false,
501                );
502            }
503        }
504
505        // When a gamepad axis input is mapped to an emulated analog input, ensure that the emulator
506        // receives analog values instead of digital pressed vs. not pressed by pushing the analog
507        // value change events _after_ checking for a digital pressed change. This way the emulator's
508        // input mapping code will always see the analog change events last without needing to
509        // special case digital vs. analog inputs.
510        //
511        // For a similar reason, push an event for the inverse axis direction first in case both
512        // gamepad axis directions are mapped to the same emulated analog axis.
513        // TODO deadzone
514        for direction in [pressed_direction.inverse(), pressed_direction] {
515            let canonical_input = CanonicalInput::canonicalize(GenericInput::Gamepad {
516                gamepad_idx,
517                action: GamepadAction::Axis(axis_idx, direction),
518            });
519            let Some(buttons) = self.inputs_to_buttons.get(&canonical_input) else {
520                continue;
521            };
522
523            let direction_magnitude = if direction == pressed_direction { magnitude } else { 0 };
524
525            for &button in buttons {
526                let GenericButton::Button(button, player) = button else { continue };
527
528                self.input_events.borrow_mut().push(InputEvent::AnalogValueChange {
529                    button,
530                    player,
531                    value: direction_magnitude,
532                });
533            }
534        }
535    }
536
537    fn handle_hat_input(&mut self, gamepad_idx: u32, hat_idx: u8, state: HatState) {
538        for direction in HatDirection::ALL {
539            let pressed = is_hat_direction_pressed(direction, state);
540            self.handle_input(
541                GenericInput::Gamepad {
542                    gamepad_idx,
543                    action: GamepadAction::Hat(hat_idx, direction),
544                },
545                pressed,
546            );
547        }
548    }
549
550    fn toggle_turbo_states(&mut self) {
551        for (&(button, player), pressed) in &mut self.active_turbo_buttons {
552            self.input_events.borrow_mut().push(InputEvent::Button {
553                button,
554                player,
555                pressed: *pressed,
556            });
557
558            *pressed = !*pressed;
559        }
560    }
561
562    fn unset_all_gamepad_inputs(&mut self) {
563        // Allocation to avoid borrow checker issues is fine, this won't be called frequently
564        let gamepad_inputs: Vec<_> = self.inputs_to_buttons.keys().copied().collect();
565
566        for input in gamepad_inputs {
567            self.handle_input(input.0, false);
568        }
569    }
570}
571
572pub(crate) struct InputMapper<Button> {
573    joysticks: Rc<RefCell<Joysticks>>,
574    axis_deadzone: i16,
575    state: InputMapperState<Button>,
576}
577
578impl<Button> InputMapper<Button>
579where
580    Button: Debug + Copy + Hash + Eq,
581{
582    pub fn new(
583        joysticks: Rc<RefCell<Joysticks>>,
584        axis_deadzone: i16,
585        button_mappings: &[((Button, Player), &Vec<GenericInput>)],
586        turbo_mappings: &[((Button, Player), &Vec<GenericInput>)],
587        hotkey_mappings: &[(Hotkey, &Vec<GenericInput>)],
588    ) -> Self {
589        let mut state = InputMapperState::new();
590        state.update_mappings(button_mappings, turbo_mappings, hotkey_mappings);
591
592        Self { joysticks, axis_deadzone, state }
593    }
594
595    pub fn update_mappings(
596        &mut self,
597        axis_deadzone: i16,
598        button_mappings: &[((Button, Player), &Vec<GenericInput>)],
599        turbo_mappings: &[((Button, Player), &Vec<GenericInput>)],
600        hotkey_mappings: &[(Hotkey, &Vec<GenericInput>)],
601    ) {
602        self.axis_deadzone = axis_deadzone;
603        self.state.update_mappings(button_mappings, turbo_mappings, hotkey_mappings);
604    }
605
606    pub fn handle_event(
607        &mut self,
608        event: &Event,
609        emulator_window_id: u32,
610        display_info: Option<DisplayInfo>,
611    ) {
612        log::debug!("SDL event: {event:?}");
613
614        let mut joysticks = self.joysticks.borrow_mut();
615
616        match *event {
617            Event::KeyDown { keycode, scancode, window_id, .. }
618                if window_id == emulator_window_id =>
619            {
620                if let Some(keycode) = keycode {
621                    self.state.handle_input(
622                        GenericInput::Keyboard(KeyboardInput::Keycode(keycode)),
623                        true,
624                    );
625                }
626
627                if let Some(scancode) = scancode {
628                    self.state.handle_input(
629                        GenericInput::Keyboard(KeyboardInput::Scancode(scancode)),
630                        true,
631                    );
632                }
633            }
634            Event::KeyUp { keycode, scancode, window_id, .. }
635                if window_id == emulator_window_id =>
636            {
637                if let Some(keycode) = keycode {
638                    self.state.handle_input(
639                        GenericInput::Keyboard(KeyboardInput::Keycode(keycode)),
640                        false,
641                    );
642                }
643
644                if let Some(scancode) = scancode {
645                    self.state.handle_input(
646                        GenericInput::Keyboard(KeyboardInput::Scancode(scancode)),
647                        false,
648                    );
649                }
650            }
651            Event::MouseButtonDown { mouse_btn, window_id, .. }
652                if window_id == emulator_window_id =>
653            {
654                self.state.handle_input(GenericInput::Mouse(mouse_btn), true);
655            }
656            Event::MouseButtonUp { mouse_btn, window_id, .. }
657                if window_id == emulator_window_id =>
658            {
659                self.state.handle_input(GenericInput::Mouse(mouse_btn), false);
660            }
661            Event::MouseMotion { x, y, xrel, yrel, window_id, .. }
662                if window_id == emulator_window_id =>
663            {
664                if let Some(display_info) = display_info {
665                    self.state.input_events.borrow_mut().push(InputEvent::MouseMotion {
666                        position: (x, y),
667                        delta: (xrel, yrel),
668                        display_info,
669                    });
670                }
671            }
672            Event::Window { win_event: WindowEvent::MouseLeave, window_id, .. }
673                if window_id == emulator_window_id =>
674            {
675                self.state.input_events.borrow_mut().push(InputEvent::MouseLeave);
676            }
677            Event::JoyButtonDown { which, button_idx, .. } => {
678                let Some(gamepad_idx) = joysticks.map_to_device_id(which) else { return };
679                self.state.handle_input(
680                    GenericInput::Gamepad {
681                        gamepad_idx,
682                        action: GamepadAction::Button(button_idx),
683                    },
684                    true,
685                );
686            }
687            Event::JoyButtonUp { which, button_idx, .. } => {
688                let Some(gamepad_idx) = joysticks.map_to_device_id(which) else { return };
689                self.state.handle_input(
690                    GenericInput::Gamepad {
691                        gamepad_idx,
692                        action: GamepadAction::Button(button_idx),
693                    },
694                    false,
695                );
696            }
697            Event::JoyAxisMotion { which, axis_idx, value, .. } => {
698                let Some(gamepad_idx) = joysticks.map_to_device_id(which) else { return };
699                self.state.handle_axis_input(self.axis_deadzone, gamepad_idx, axis_idx, value);
700            }
701            Event::JoyHatMotion { which, hat_idx, state, .. } => {
702                let Some(gamepad_idx) = joysticks.map_to_device_id(which) else { return };
703                self.state.handle_hat_input(gamepad_idx, hat_idx, state);
704            }
705            Event::JoyDeviceAdded { which, .. } => {
706                if let Err(err) = joysticks.handle_device_added(which) {
707                    log::error!("Error opening joystick with joystick id {which}: {err}");
708                }
709                self.state.unset_all_gamepad_inputs();
710            }
711            Event::JoyDeviceRemoved { which, .. } => {
712                if let Err(err) = joysticks.handle_device_removed(which) {
713                    log::error!("Error closing joystick with joystick id {which}: {err}");
714                }
715                self.state.unset_all_gamepad_inputs();
716            }
717            _ => {}
718        }
719    }
720
721    pub fn frame_complete(&mut self) {
722        self.state.toggle_turbo_states();
723    }
724
725    #[must_use]
726    pub fn input_events(&self) -> Rc<RefCell<Vec<InputEvent<Button>>>> {
727        Rc::clone(&self.state.input_events)
728    }
729}
730
731impl<Button> InputMapper<Button> {
732    pub fn joysticks(&self) -> Rc<RefCell<Joysticks>> {
733        Rc::clone(&self.joysticks)
734    }
735}
736
737fn is_hat_direction_pressed(direction: HatDirection, state: HatState) -> bool {
738    use HatDirection as HD;
739    use HatState as HS;
740
741    match direction {
742        HD::Up => matches!(state, HS::Up | HS::LeftUp | HS::RightUp),
743        HD::Left => matches!(state, HS::Left | HS::LeftUp | HS::LeftDown),
744        HD::Right => matches!(state, HS::Right | HS::RightUp | HS::RightDown),
745        HD::Down => matches!(state, HS::Down | HS::LeftDown | HS::RightDown),
746    }
747}
748
749#[cfg(test)]
750mod tests {
751    use super::*;
752    use jgenesis_common::frontend::MappableInputs;
753    use smsgg_config::{SmsGgButton, SmsGgInputs};
754    use std::marker::PhantomData;
755
756    struct TestState<B, I: MappableInputs<B>> {
757        inputs: I,
758        hotkeys: FxHashSet<Hotkey>,
759        _marker: PhantomData<B>,
760    }
761
762    impl<B, I> TestState<B, I>
763    where
764        B: Debug + Copy + Eq + Hash,
765        I: Default + MappableInputs<B>,
766    {
767        fn new() -> Self {
768            Self { inputs: I::default(), hotkeys: FxHashSet::default(), _marker: PhantomData }
769        }
770
771        fn handle_input(
772            &mut self,
773            state: &mut InputMapperState<B>,
774            input: GenericInput,
775            pressed: bool,
776        ) {
777            state.handle_input(input, pressed);
778            take_events(&mut self.inputs, &mut self.hotkeys, state);
779        }
780    }
781
782    fn new_smsgg_state() -> TestState<SmsGgButton, SmsGgInputs> {
783        TestState::new()
784    }
785
786    fn take_events<I, B>(
787        inputs: &mut I,
788        hotkeys: &mut FxHashSet<Hotkey>,
789        state: &mut InputMapperState<B>,
790    ) where
791        I: MappableInputs<B>,
792    {
793        for event in state.input_events.borrow_mut().drain(..) {
794            match event {
795                InputEvent::Button { button, player, pressed } => {
796                    inputs.set_field(button, player, pressed);
797                }
798                InputEvent::Hotkey { hotkey, pressed } => {
799                    if pressed {
800                        hotkeys.insert(hotkey);
801                    } else {
802                        hotkeys.remove(&hotkey);
803                    }
804                }
805                _ => {}
806            }
807        }
808    }
809
810    fn into_hash_set<H: Eq + Hash>(iter: impl IntoIterator<Item = H>) -> FxHashSet<H> {
811        iter.into_iter().collect()
812    }
813
814    macro_rules! key_input {
815        ($keycode:ident) => {
816            GenericInput::Keyboard(KeyboardInput::Keycode(Keycode::$keycode))
817        };
818    }
819
820    #[test]
821    fn basic_mapping() {
822        let mut input_state = InputMapperState::new();
823        input_state.update_mappings(
824            &[
825                ((SmsGgButton::Button1, Player::One), &vec![key_input!(F)]),
826                ((SmsGgButton::Button1, Player::Two), &vec![key_input!(G)]),
827                ((SmsGgButton::Button2, Player::One), &vec![key_input!(Up)]),
828            ],
829            &[],
830            &[(Hotkey::FastForward, &vec![key_input!(H)])],
831        );
832
833        let mut state = new_smsgg_state();
834        let mut expected = SmsGgInputs::default();
835        assert_eq!(expected, state.inputs);
836        assert_eq!(state.hotkeys, FxHashSet::default());
837
838        state.handle_input(&mut input_state, key_input!(F), true);
839        expected.p1.button1 = true;
840        assert_eq!(expected, state.inputs);
841        assert_eq!(state.hotkeys, FxHashSet::default());
842
843        state.handle_input(&mut input_state, key_input!(G), true);
844        expected.p2.button1 = true;
845        assert_eq!(expected, state.inputs);
846        assert_eq!(state.hotkeys, FxHashSet::default());
847
848        state.handle_input(&mut input_state, key_input!(F), false);
849        expected.p1.button1 = false;
850        assert_eq!(expected, state.inputs);
851        assert_eq!(state.hotkeys, FxHashSet::default());
852
853        state.handle_input(&mut input_state, key_input!(H), true);
854        assert_eq!(expected, state.inputs);
855        assert_eq!(state.hotkeys, into_hash_set([Hotkey::FastForward]));
856
857        state.handle_input(&mut input_state, key_input!(H), false);
858        assert_eq!(expected, state.inputs);
859        assert_eq!(state.hotkeys, FxHashSet::default());
860    }
861
862    #[test]
863    fn one_mapping_button_and_hotkey() {
864        let mut input_state = InputMapperState::new();
865        input_state.update_mappings(
866            &[((SmsGgButton::Button1, Player::One), &vec![key_input!(F)])],
867            &[],
868            &[(Hotkey::SaveState, &vec![key_input!(F)])],
869        );
870
871        let mut state = new_smsgg_state();
872
873        let mut expected_inputs = SmsGgInputs::default();
874        let mut expected_hotkeys: FxHashSet<Hotkey> = FxHashSet::default();
875        assert_eq!(expected_inputs, state.inputs);
876        assert_eq!(expected_hotkeys, state.hotkeys);
877
878        state.handle_input(&mut input_state, key_input!(F), true);
879        expected_inputs.p1.button1 = true;
880        expected_hotkeys.insert(Hotkey::SaveState);
881        assert_eq!(expected_inputs, state.inputs);
882        assert_eq!(expected_hotkeys, state.hotkeys);
883
884        state.handle_input(&mut input_state, key_input!(F), false);
885        expected_inputs.p1.button1 = false;
886        expected_hotkeys.remove(&Hotkey::SaveState);
887        assert_eq!(expected_inputs, state.inputs);
888        assert_eq!(expected_hotkeys, state.hotkeys);
889    }
890
891    #[test]
892    fn two_mappings_same_button() {
893        let mut input_state = InputMapperState::new();
894        input_state.update_mappings(
895            &[
896                ((SmsGgButton::Button1, Player::One), &vec![key_input!(F)]),
897                ((SmsGgButton::Button1, Player::One), &vec![key_input!(G)]),
898            ],
899            &[],
900            &[],
901        );
902
903        let mut state = new_smsgg_state();
904
905        let mut expected = SmsGgInputs::default();
906        assert_eq!(expected, state.inputs);
907
908        state.handle_input(&mut input_state, key_input!(F), true);
909        expected.p1.button1 = true;
910        assert_eq!(expected, state.inputs, "one mapping pressed");
911
912        state.handle_input(&mut input_state, key_input!(G), true);
913        assert_eq!(expected, state.inputs, "two mappings pressed");
914
915        state.handle_input(&mut input_state, key_input!(G), false);
916        assert_eq!(expected, state.inputs, "one mapping released, one still pressed");
917
918        state.handle_input(&mut input_state, key_input!(F), false);
919        expected.p1.button1 = false;
920        assert_eq!(expected, state.inputs, "both mappings released");
921    }
922
923    #[test]
924    fn one_mapping_three_buttons() {
925        let mut input_state = InputMapperState::new();
926        input_state.update_mappings(
927            &[
928                ((SmsGgButton::Button1, Player::One), &vec![key_input!(F)]),
929                ((SmsGgButton::Button2, Player::One), &vec![key_input!(F)]),
930                ((SmsGgButton::Pause, Player::One), &vec![key_input!(F)]),
931            ],
932            &[],
933            &[],
934        );
935
936        let mut state = new_smsgg_state();
937
938        let mut expected = SmsGgInputs::default();
939        assert_eq!(expected, state.inputs);
940
941        state.handle_input(&mut input_state, key_input!(F), true);
942        expected.p1.button1 = true;
943        expected.p1.button2 = true;
944        expected.pause = true;
945        assert_eq!(expected, state.inputs, "mapping pressed");
946
947        state.handle_input(&mut input_state, key_input!(F), false);
948        expected.p1.button1 = false;
949        expected.p1.button2 = false;
950        expected.pause = false;
951        assert_eq!(expected, state.inputs, "mapping released");
952    }
953
954    #[test]
955    fn combination_mapping() {
956        let mut input_state = InputMapperState::new();
957        input_state.update_mappings(
958            &[(
959                (SmsGgButton::Button1, Player::One),
960                &vec![key_input!(F), key_input!(G), key_input!(H)],
961            )],
962            &[],
963            &[],
964        );
965
966        let mut state = new_smsgg_state();
967
968        let mut expected = SmsGgInputs::default();
969        assert_eq!(expected, state.inputs);
970
971        state.handle_input(&mut input_state, key_input!(F), true);
972        assert_eq!(expected, state.inputs, "1/3 pressed (1)");
973
974        state.handle_input(&mut input_state, key_input!(H), true);
975        assert_eq!(expected, state.inputs, "2/3 pressed (2)");
976
977        state.handle_input(&mut input_state, key_input!(F), false);
978        assert_eq!(expected, state.inputs, "1/3 pressed (3)");
979
980        state.handle_input(&mut input_state, key_input!(G), true);
981        assert_eq!(expected, state.inputs, "2/3 pressed (4)");
982
983        state.handle_input(&mut input_state, key_input!(F), true);
984        expected.p1.button1 = true;
985        assert_eq!(expected, state.inputs, "3/3 pressed (5)");
986
987        state.handle_input(&mut input_state, key_input!(H), false);
988        expected.p1.button1 = false;
989        assert_eq!(expected, state.inputs, "2/3 pressed (6)");
990
991        state.handle_input(&mut input_state, key_input!(H), true);
992        expected.p1.button1 = true;
993        assert_eq!(expected, state.inputs, "3/3 pressed (7)");
994
995        state.handle_input(&mut input_state, key_input!(G), false);
996        expected.p1.button1 = false;
997        assert_eq!(expected, state.inputs, "2/3 pressed (8)");
998    }
999
1000    #[test]
1001    fn combination_length_priority_basic() {
1002        let mut input_state = InputMapperState::new();
1003        input_state.update_mappings(
1004            &[],
1005            &[],
1006            &[
1007                (Hotkey::SaveState, &vec![key_input!(LShift), key_input!(F1)]),
1008                (Hotkey::LoadState, &vec![key_input!(F1)]),
1009            ],
1010        );
1011
1012        let mut state = new_smsgg_state();
1013
1014        let mut expected: FxHashSet<Hotkey> = FxHashSet::default();
1015        assert_eq!(expected, state.hotkeys);
1016
1017        state.handle_input(&mut input_state, key_input!(F1), true);
1018        expected.insert(Hotkey::LoadState);
1019        assert_eq!(expected, state.hotkeys, "single key pressed");
1020
1021        state.handle_input(&mut input_state, key_input!(F1), false);
1022        expected.remove(&Hotkey::LoadState);
1023        assert_eq!(expected, state.hotkeys, "single key pressed & released");
1024
1025        state.handle_input(&mut input_state, key_input!(LShift), true);
1026        assert_eq!(expected, state.hotkeys, "1/2 pressed");
1027
1028        state.handle_input(&mut input_state, key_input!(F1), true);
1029        expected.insert(Hotkey::SaveState);
1030        assert_eq!(expected, state.hotkeys, "2/2 pressed");
1031
1032        state.handle_input(&mut input_state, key_input!(F1), false);
1033        expected.remove(&Hotkey::SaveState);
1034        assert_eq!(expected, state.hotkeys, "1/2 released");
1035
1036        state.handle_input(&mut input_state, key_input!(LShift), false);
1037        assert_eq!(expected, state.hotkeys, "2/2 released");
1038    }
1039
1040    #[test]
1041    fn combination_length_priority_weird() {
1042        let mut input_state = InputMapperState::new();
1043        input_state.update_mappings(
1044            &[],
1045            &[],
1046            &[
1047                (Hotkey::SaveState, &vec![key_input!(LShift), key_input!(F1)]),
1048                (Hotkey::LoadState, &vec![key_input!(F1)]),
1049            ],
1050        );
1051
1052        let mut state = new_smsgg_state();
1053
1054        let mut expected: FxHashSet<Hotkey> = FxHashSet::default();
1055        assert_eq!(expected, state.hotkeys);
1056
1057        state.handle_input(&mut input_state, key_input!(F1), true);
1058        expected.insert(Hotkey::LoadState);
1059        assert_eq!(expected, state.hotkeys, "single key pressed");
1060
1061        state.handle_input(&mut input_state, key_input!(LShift), true);
1062        expected.insert(Hotkey::SaveState);
1063        assert_eq!(expected, state.hotkeys, "combination secondary key pressed");
1064
1065        state.handle_input(&mut input_state, key_input!(F1), false);
1066        expected.remove(&Hotkey::LoadState);
1067        expected.remove(&Hotkey::SaveState);
1068        assert_eq!(expected, state.hotkeys, "single key + combination released");
1069
1070        state.handle_input(&mut input_state, key_input!(F1), true);
1071        expected.insert(Hotkey::SaveState);
1072        assert_eq!(expected, state.hotkeys, "combination pressed second time");
1073
1074        state.handle_input(&mut input_state, key_input!(F1), false);
1075        expected.remove(&Hotkey::SaveState);
1076        assert_eq!(expected, state.hotkeys, "combination released second time");
1077
1078        state.handle_input(&mut input_state, key_input!(LShift), false);
1079        assert_eq!(expected, state.hotkeys, "combination secondary key released");
1080    }
1081
1082    #[test]
1083    fn shift_canonicalization_basic() {
1084        let mut input_state = InputMapperState::new();
1085        input_state.update_mappings(
1086            &[
1087                ((SmsGgButton::Button1, Player::One), &vec![key_input!(RShift)]),
1088                ((SmsGgButton::Button2, Player::One), &vec![key_input!(LShift)]),
1089            ],
1090            &[],
1091            &[],
1092        );
1093
1094        let mut state = new_smsgg_state();
1095
1096        let mut expected = SmsGgInputs::default();
1097        assert_eq!(expected, state.inputs);
1098
1099        state.handle_input(&mut input_state, key_input!(LShift), true);
1100        expected.p1.button1 = true;
1101        expected.p1.button2 = true;
1102        assert_eq!(expected, state.inputs, "Pressing LShift should trigger both Shift mappings");
1103
1104        state.handle_input(&mut input_state, key_input!(LShift), false);
1105        expected.p1.button1 = false;
1106        expected.p1.button2 = false;
1107        assert_eq!(expected, state.inputs, "Releasing LShift should trigger both Shift mappings");
1108
1109        state.handle_input(&mut input_state, key_input!(RShift), true);
1110        expected.p1.button1 = true;
1111        expected.p1.button2 = true;
1112        assert_eq!(expected, state.inputs, "Pressing RShift should trigger both Shift mappings");
1113
1114        state.handle_input(&mut input_state, key_input!(RShift), false);
1115        expected.p1.button1 = false;
1116        expected.p1.button2 = false;
1117        assert_eq!(expected, state.inputs, "Releasing RShift should trigger both Shift mappings");
1118    }
1119
1120    #[test]
1121    fn shift_canonicalization_simultaneous() {
1122        let mut input_state = InputMapperState::new();
1123        input_state.update_mappings(
1124            &[
1125                ((SmsGgButton::Button1, Player::One), &vec![key_input!(RShift)]),
1126                ((SmsGgButton::Button2, Player::One), &vec![key_input!(LShift)]),
1127            ],
1128            &[],
1129            &[],
1130        );
1131
1132        let mut state = new_smsgg_state();
1133
1134        let mut expected = SmsGgInputs::default();
1135        assert_eq!(expected, state.inputs);
1136
1137        state.handle_input(&mut input_state, key_input!(LShift), true);
1138        state.handle_input(&mut input_state, key_input!(RShift), true);
1139        expected.p1.button1 = true;
1140        expected.p1.button2 = true;
1141        assert_eq!(expected, state.inputs);
1142
1143        state.handle_input(&mut input_state, key_input!(RShift), false);
1144        assert_eq!(
1145            expected, state.inputs,
1146            "Releasing RShift while LShift is held should not change mapping"
1147        );
1148
1149        state.handle_input(&mut input_state, key_input!(RShift), true);
1150        assert_eq!(
1151            expected, state.inputs,
1152            "Pressing RShift while LShift is held should not change mapping"
1153        );
1154
1155        state.handle_input(&mut input_state, key_input!(LShift), false);
1156        assert_eq!(
1157            expected, state.inputs,
1158            "Releasing LShift while RShift is held should not change mapping"
1159        );
1160
1161        state.handle_input(&mut input_state, key_input!(RShift), false);
1162        expected.p1.button1 = false;
1163        expected.p1.button2 = false;
1164        assert_eq!(
1165            expected, state.inputs,
1166            "Releasing RShift while LShift is not held should change mapping"
1167        );
1168    }
1169
1170    #[test]
1171    fn turbo() {
1172        let mut input_state = InputMapperState::new();
1173        input_state.update_mappings(
1174            &[],
1175            &[((SmsGgButton::Button1, Player::One), &vec![key_input!(D)])],
1176            &[],
1177        );
1178
1179        let mut state = new_smsgg_state();
1180
1181        let mut expected = SmsGgInputs::default();
1182        assert_eq!(expected, state.inputs);
1183
1184        state.handle_input(&mut input_state, key_input!(D), false);
1185        assert_eq!(expected, state.inputs);
1186        input_state.toggle_turbo_states();
1187        assert_eq!(expected, state.inputs);
1188
1189        state.handle_input(&mut input_state, key_input!(D), true);
1190        expected.p1.button1 = true;
1191        assert_eq!(expected, state.inputs);
1192
1193        // First toggle won't change state
1194        // This is intentional - the pressed state should immediately change to true once the turbo
1195        // mapping is pressed, then it should remain true until the second "frame complete" event
1196        input_state.toggle_turbo_states();
1197        take_events(&mut state.inputs, &mut state.hotkeys, &mut input_state);
1198        assert_eq!(expected, state.inputs);
1199
1200        for _ in 0..50 {
1201            input_state.toggle_turbo_states();
1202            take_events(&mut state.inputs, &mut state.hotkeys, &mut input_state);
1203            expected.p1.button1 = !expected.p1.button1;
1204            assert_eq!(expected, state.inputs);
1205        }
1206
1207        assert_eq!(state.inputs.p1.button1, true);
1208        state.handle_input(&mut input_state, key_input!(D), false);
1209        expected.p1.button1 = false;
1210        assert_eq!(expected, state.inputs);
1211
1212        for _ in 0..51 {
1213            input_state.toggle_turbo_states();
1214            take_events(&mut state.inputs, &mut state.hotkeys, &mut input_state);
1215            assert_eq!(expected, state.inputs);
1216        }
1217    }
1218}