core: Introduce gamepad button mapping support

This commit is contained in:
Tom Schuster 2024-02-24 20:29:18 +01:00
parent 9bc6700531
commit 0cea5a0a9e
2 changed files with 66 additions and 0 deletions

View File

@ -30,6 +30,12 @@ pub enum PlayerEvent {
MouseWheel { MouseWheel {
delta: MouseWheelDelta, delta: MouseWheelDelta,
}, },
GamepadButtonDown {
button: GamepadButton,
},
GamepadButtonUp {
button: GamepadButton,
},
TextInput { TextInput {
codepoint: char, codepoint: char,
}, },
@ -381,6 +387,7 @@ impl TextControlCode {
} }
/// Flash virtual keycode. /// Flash virtual keycode.
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, FromPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, FromPrimitive)]
pub enum KeyCode { pub enum KeyCode {
Unknown = 0, Unknown = 0,
@ -674,3 +681,22 @@ pub fn key_code_to_button_key_code(key_code: KeyCode) -> Option<ButtonKeyCode> {
}; };
Some(out) Some(out)
} }
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
pub enum GamepadButton {
South,
East,
North,
West,
LeftTrigger,
LeftTrigger2,
RightTrigger,
RightTrigger2,
Select,
Start,
DPadUp,
DPadDown,
DPadLeft,
DPadRight,
}

View File

@ -29,6 +29,7 @@ use crate::display_object::{
EditText, InteractiveObject, Stage, StageAlign, StageDisplayState, StageScaleMode, EditText, InteractiveObject, Stage, StageAlign, StageDisplayState, StageScaleMode,
TInteractiveObject, WindowMode, TInteractiveObject, WindowMode,
}; };
use crate::events::GamepadButton;
use crate::events::{ButtonKeyCode, ClipEvent, ClipEventResult, KeyCode, MouseButton, PlayerEvent}; use crate::events::{ButtonKeyCode, ClipEvent, ClipEventResult, KeyCode, MouseButton, PlayerEvent};
use crate::external::{ExternalInterface, ExternalInterfaceProvider, NullFsCommandProvider}; use crate::external::{ExternalInterface, ExternalInterfaceProvider, NullFsCommandProvider};
use crate::external::{FsCommandProvider, Value as ExternalValue}; use crate::external::{FsCommandProvider, Value as ExternalValue};
@ -356,6 +357,9 @@ pub struct Player {
/// Any compatibility rules to apply for this movie. /// Any compatibility rules to apply for this movie.
compatibility_rules: CompatibilityRules, compatibility_rules: CompatibilityRules,
/// A map from gamepad buttons to key codes.
gamepad_button_mapping: HashMap<GamepadButton, KeyCode>,
/// Debug UI windows /// Debug UI windows
#[cfg(feature = "egui")] #[cfg(feature = "egui")]
debug_ui: Rc<RefCell<crate::debug_ui::DebugUi>>, debug_ui: Rc<RefCell<crate::debug_ui::DebugUi>>,
@ -842,6 +846,7 @@ impl Player {
/// Event handling is a complicated affair, involving several different /// Event handling is a complicated affair, involving several different
/// concerns that need to resolve with specific priority. /// concerns that need to resolve with specific priority.
/// ///
/// 0. Transform gamepad button events into key events.
/// 1. (In `avm_debug` builds) /// 1. (In `avm_debug` builds)
/// If Ctrl-Alt-V is pressed, dump all AVM1 variables in the player. /// If Ctrl-Alt-V is pressed, dump all AVM1 variables in the player.
/// If Ctrl-Alt-D is pressed, toggle debug output for AVM1 and AVM2. /// If Ctrl-Alt-D is pressed, toggle debug output for AVM1 and AVM2.
@ -861,6 +866,33 @@ impl Player {
/// 8. Mouse state is updated. This triggers button rollovers, which are a /// 8. Mouse state is updated. This triggers button rollovers, which are a
/// second wave of event processing. /// second wave of event processing.
pub fn handle_event(&mut self, event: PlayerEvent) { pub fn handle_event(&mut self, event: PlayerEvent) {
// Optionally transform gamepad button events into key events.
let event = match event {
PlayerEvent::GamepadButtonDown { button } => {
if let Some(key_code) = self.gamepad_button_mapping.get(&button) {
PlayerEvent::KeyDown {
key_code: *key_code,
key_char: None,
}
} else {
// Just ignore this event.
return;
}
}
PlayerEvent::GamepadButtonUp { button } => {
if let Some(key_code) = self.gamepad_button_mapping.get(&button) {
PlayerEvent::KeyUp {
key_code: *key_code,
key_char: None,
}
} else {
// Just ignore this event.
return;
}
}
_ => event,
};
let prev_is_mouse_down = self.input.is_mouse_down(); let prev_is_mouse_down = self.input.is_mouse_down();
self.input.handle_event(&event); self.input.handle_event(&event);
let is_mouse_button_changed = self.input.is_mouse_down() != prev_is_mouse_down; let is_mouse_button_changed = self.input.is_mouse_down() != prev_is_mouse_down;
@ -2113,6 +2145,7 @@ pub struct PlayerBuilder {
load_behavior: LoadBehavior, load_behavior: LoadBehavior,
spoofed_url: Option<String>, spoofed_url: Option<String>,
compatibility_rules: CompatibilityRules, compatibility_rules: CompatibilityRules,
gamepad_button_mapping: HashMap<GamepadButton, KeyCode>,
player_version: Option<u8>, player_version: Option<u8>,
player_runtime: PlayerRuntime, player_runtime: PlayerRuntime,
quality: StageQuality, quality: StageQuality,
@ -2163,6 +2196,7 @@ impl PlayerBuilder {
load_behavior: LoadBehavior::Streaming, load_behavior: LoadBehavior::Streaming,
spoofed_url: None, spoofed_url: None,
compatibility_rules: CompatibilityRules::default(), compatibility_rules: CompatibilityRules::default(),
gamepad_button_mapping: HashMap::new(),
player_version: None, player_version: None,
player_runtime: PlayerRuntime::default(), player_runtime: PlayerRuntime::default(),
quality: StageQuality::High, quality: StageQuality::High,
@ -2362,6 +2396,11 @@ impl PlayerBuilder {
self self
} }
pub fn with_gamepad_button_mapping(mut self, mapping: HashMap<GamepadButton, KeyCode>) -> Self {
self.gamepad_button_mapping = mapping;
self
}
#[cfg(feature = "known_stubs")] #[cfg(feature = "known_stubs")]
/// Sets the output path for the stub report. When set, the player /// Sets the output path for the stub report. When set, the player
/// will write the report to this path and exit the process. /// will write the report to this path and exit the process.
@ -2507,6 +2546,7 @@ impl PlayerBuilder {
load_behavior: self.load_behavior, load_behavior: self.load_behavior,
spoofed_url: self.spoofed_url.clone(), spoofed_url: self.spoofed_url.clone(),
compatibility_rules: self.compatibility_rules.clone(), compatibility_rules: self.compatibility_rules.clone(),
gamepad_button_mapping: self.gamepad_button_mapping,
stub_tracker: StubCollection::new(), stub_tracker: StubCollection::new(),
#[cfg(feature = "egui")] #[cfg(feature = "egui")]
debug_ui: Default::default(), debug_ui: Default::default(),