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}