diff --git a/Cargo.lock b/Cargo.lock index a825a0eb..4656a473 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2796,7 +2796,6 @@ dependencies = [ "reqwest", "rfd", "rustc-hash 2.1.2", - "sdl3-image-sys", "sdl3-src", "sdl3-sys", "sdl3-ttf-sys", @@ -6441,26 +6440,6 @@ dependencies = [ "tiny-skia 0.11.4", ] -[[package]] -name = "sdl3-image-src" -version = "3.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe273101c7dab94551183212eee9adef1a7bf274d407f0b7bfe72482960ab25c" - -[[package]] -name = "sdl3-image-sys" -version = "0.6.4+SDL-image-3.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a445f781b39a1c1bc751f5f4612191e0402006e35ad5d02d9193281afad1cf4" -dependencies = [ - "cmake", - "pkg-config", - "rpkg-config", - "sdl3-image-src", - "sdl3-sys", - "vcpkg", -] - [[package]] name = "sdl3-src" version = "3.4.10" diff --git a/Cargo.toml b/Cargo.toml index 4bb8c324..ef1c908e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,14 +45,12 @@ dirs = "6.0" open = { version = "5.3", optional = true } sdl3-sys = { version = "0.6", features = ["build-from-source-static"] } sdl3-ttf-sys = { version = "0.6", features = ["build-static-vendored", "no-sdlttf-harfbuzz", "no-sdlttf-plutosvg"] } -sdl3-image-sys = { version = "0.6", features = ["build-static-vendored"] } [target.'cfg(target_os = "android")'.dependencies] slint = { version = "1.16", default-features = false, features = ["compat-1-2", "std", "backend-android-activity-06", "accessibility"], optional = true } jni = "0.22" sdl3-sys = { version = "0.6", features = ["build-from-source"] } sdl3-ttf-sys = { version = "0.6", features = ["build-from-source", "no-sdlttf-harfbuzz", "no-sdlttf-plutosvg"] } -sdl3-image-sys = { version = "0.6", features = ["build-from-source"] } [build-dependencies] winresource = "0.1" diff --git a/android-project/app/src/main/java/io/github/gopher64/gopher64/N64Activity.kt b/android-project/app/src/main/java/io/github/gopher64/gopher64/N64Activity.kt index bcd1a5c6..e7e37d4e 100644 --- a/android-project/app/src/main/java/io/github/gopher64/gopher64/N64Activity.kt +++ b/android-project/app/src/main/java/io/github/gopher64/gopher64/N64Activity.kt @@ -24,7 +24,6 @@ class N64Activity : SDLActivity() { override fun getLibraries(): Array = arrayOf( "SDL3", "SDL3_ttf", - "SDL3_image", "gopher64" ) diff --git a/data/ui/controller.png b/data/ui/controller.png deleted file mode 100644 index 9b8c031e..00000000 Binary files a/data/ui/controller.png and /dev/null differ diff --git a/src/lib.rs b/src/lib.rs index d9521857..3e9ffc8c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -284,11 +284,11 @@ pub fn run(args: Args, arg_count: usize) -> std::io::Result<()> { let mut config = ui::config::Config::new(); if let Some(profile) = args.configure_input_profile { - ui::input::configure_input_profile( + ui::input_profile::configure_input_profile( &mut config, profile, args.use_dinput, - args.deadzone.unwrap_or(ui::input::DEADZONE_DEFAULT), + args.deadzone.unwrap_or(ui::input_profile::DEADZONE_DEFAULT), ); ui::sdl_close(); diff --git a/src/ui.rs b/src/ui.rs index e673771c..408b0df2 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -7,6 +7,7 @@ pub mod config; #[cfg(feature = "gui")] pub mod gui; pub mod input; +pub mod input_profile; #[cfg(feature = "gui")] pub mod netplay; #[cfg(feature = "gui")] diff --git a/src/ui/config.rs b/src/ui/config.rs index 57be74b6..ccced72b 100644 --- a/src/ui/config.rs +++ b/src/ui/config.rs @@ -1,34 +1,36 @@ use crate::ui; -#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)] +#[derive(Clone, serde::Serialize, serde::Deserialize)] pub struct InputKeyButton { - pub enabled: bool, pub id: i32, } -#[derive(PartialEq, Copy, Clone, serde::Serialize, serde::Deserialize)] +#[derive(PartialEq, Clone, serde::Serialize, serde::Deserialize)] pub struct InputControllerAxis { - pub enabled: bool, pub id: i32, pub axis: i16, pub initial_state: i16, } -#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)] +#[derive(Clone, serde::Serialize, serde::Deserialize)] pub struct InputJoystickHat { - pub enabled: bool, pub id: i32, pub direction: u8, } -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(Clone, serde::Serialize, serde::Deserialize)] +pub enum InputItem { + Key(InputKeyButton), + ControllerButton(InputKeyButton), + ControllerAxis(InputControllerAxis), + JoystickButton(InputKeyButton), + JoystickHat(InputJoystickHat), + JoystickAxis(InputControllerAxis), +} + +#[derive(Clone, serde::Serialize, serde::Deserialize)] pub struct InputProfile { - pub keys: [InputKeyButton; ui::input::PROFILE_SIZE], - pub controller_buttons: [InputKeyButton; ui::input::PROFILE_SIZE], - pub controller_axis: [InputControllerAxis; ui::input::PROFILE_SIZE], - pub joystick_buttons: [InputKeyButton; ui::input::PROFILE_SIZE], - pub joystick_hat: [InputJoystickHat; ui::input::PROFILE_SIZE], - pub joystick_axis: [InputControllerAxis; ui::input::PROFILE_SIZE], + pub inputs: [[Option; 2]; ui::input_profile::PROFILE_SIZE], pub dinput: bool, pub deadzone: i32, } @@ -160,7 +162,10 @@ impl Config { } let mut input_profiles = std::collections::BTreeMap::new(); - input_profiles.insert("default".to_string(), ui::input::get_default_profile()); + input_profiles.insert( + "default".to_string(), + ui::input_profile::get_default_profile(), + ); Config { input: input_data.unwrap_or(Input { input_profile_binding: [ diff --git a/src/ui/gui.rs b/src/ui/gui.rs index 29304863..764dc00c 100644 --- a/src/ui/gui.rs +++ b/src/ui/gui.rs @@ -347,7 +347,7 @@ fn controller_window(app: &AppWindow, config: &ui::config::Config) { app.on_input_profile_button_clicked(move || { weak_app .upgrade_in_event_loop(move |handle| { - handle.set_input_deadzone(ui::input::DEADZONE_DEFAULT); + handle.set_input_deadzone(ui::input_profile::DEADZONE_DEFAULT); handle.set_input_profile_name(String::new().into()); handle.set_input_dinput(false); handle.set_show_input_profile(true); diff --git a/src/ui/input.rs b/src/ui/input.rs index 9abc0094..3cd49f11 100644 --- a/src/ui/input.rs +++ b/src/ui/input.rs @@ -2,34 +2,11 @@ use std::ops::Neg; use crate::ui; -const R_DPAD: usize = 0; -const L_DPAD: usize = 1; -const D_DPAD: usize = 2; -const U_DPAD: usize = 3; -const START_BUTTON: usize = 4; -const Z_TRIG: usize = 5; -const B_BUTTON: usize = 6; -const A_BUTTON: usize = 7; -const R_CBUTTON: usize = 8; -const L_CBUTTON: usize = 9; -const D_CBUTTON: usize = 10; -const U_CBUTTON: usize = 11; -const R_TRIG: usize = 12; -const L_TRIG: usize = 13; -const AXIS_LEFT: usize = 14; -const AXIS_RIGHT: usize = 15; -const AXIS_UP: usize = 16; -const AXIS_DOWN: usize = 17; -const HOTKEY: usize = 18; -pub const PROFILE_SIZE: usize = 19; - const X_AXIS_SHIFT: usize = 16; const Y_AXIS_SHIFT: usize = 24; const MAX_AXIS_VALUE: f64 = 85.0; -pub const DEADZONE_DEFAULT: i32 = 5; - pub const UNKNOWN_CONTROLLER_NAME: &str = "Unknown controller"; #[derive(Default)] @@ -83,196 +60,158 @@ fn normalize_axis_position(axis_position: i16) -> f64 { axis_position as f64 * MAX_AXIS_VALUE / i16::MAX as f64 } -fn set_axis_from_joystick( +fn set_axis( profile: &ui::config::InputProfile, joystick: *mut sdl3_sys::joystick::SDL_Joystick, -) -> (f64, f64) { - let mut x = 0.0; - let mut y = 0.0; - if profile.joystick_axis[AXIS_LEFT].enabled { - let axis_position = unsafe { - sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile.joystick_axis[AXIS_LEFT].id) - }; - if axis_position as isize * profile.joystick_axis[AXIS_LEFT].axis as isize > 0 { - x = normalize_axis_position(axis_position); - } - } - if profile.joystick_axis[AXIS_RIGHT].enabled { - let axis_position = unsafe { - sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile.joystick_axis[AXIS_RIGHT].id) - }; - if axis_position as isize * profile.joystick_axis[AXIS_RIGHT].axis as isize > 0 { - x = normalize_axis_position(axis_position); - } - } - if profile.joystick_axis[AXIS_DOWN].enabled { - let axis_position = unsafe { - sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile.joystick_axis[AXIS_DOWN].id) - }; - if axis_position as isize * profile.joystick_axis[AXIS_DOWN].axis as isize > 0 { - y = normalize_axis_position(axis_position).neg(); - } - } - if profile.joystick_axis[AXIS_UP].enabled { - let axis_position = unsafe { - sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile.joystick_axis[AXIS_UP].id) - }; - if axis_position as isize * profile.joystick_axis[AXIS_UP].axis as isize > 0 { - y = normalize_axis_position(axis_position).neg(); - } - } - (x, y) -} - -fn set_axis_from_controller( - profile: &ui::config::InputProfile, controller: *mut sdl3_sys::gamepad::SDL_Gamepad, -) -> (f64, f64) { - let mut x = 0.0; - let mut y = 0.0; - if profile.controller_axis[AXIS_LEFT].enabled { - let axis_position = unsafe { - sdl3_sys::gamepad::SDL_GetGamepadAxis( - controller, - sdl3_sys::gamepad::SDL_GamepadAxis(profile.controller_axis[AXIS_LEFT].id), - ) - }; - if axis_position as isize * profile.controller_axis[AXIS_LEFT].axis as isize > 0 { - x = normalize_axis_position(axis_position); - } - } - if profile.controller_axis[AXIS_RIGHT].enabled { - let axis_position = unsafe { - sdl3_sys::gamepad::SDL_GetGamepadAxis( - controller, - sdl3_sys::gamepad::SDL_GamepadAxis(profile.controller_axis[AXIS_RIGHT].id), - ) - }; - if axis_position as isize * profile.controller_axis[AXIS_RIGHT].axis as isize > 0 { - x = normalize_axis_position(axis_position); - } - } - if profile.controller_axis[AXIS_DOWN].enabled { - let axis_position = unsafe { - sdl3_sys::gamepad::SDL_GetGamepadAxis( - controller, - sdl3_sys::gamepad::SDL_GamepadAxis(profile.controller_axis[AXIS_DOWN].id), - ) - }; - if axis_position as isize * profile.controller_axis[AXIS_DOWN].axis as isize > 0 { - y = normalize_axis_position(axis_position).neg(); - } - } - if profile.controller_axis[AXIS_UP].enabled { - let axis_position = unsafe { - sdl3_sys::gamepad::SDL_GetGamepadAxis( - controller, - sdl3_sys::gamepad::SDL_GamepadAxis(profile.controller_axis[AXIS_UP].id), - ) - }; - if axis_position as isize * profile.controller_axis[AXIS_UP].axis as isize > 0 { - y = normalize_axis_position(axis_position).neg(); - } - } - (x, y) -} - -fn set_axis_from_keys( - profile: &ui::config::InputProfile, keyboard_state: *const bool, ) -> (f64, f64) { let mut x = 0.0; let mut y = 0.0; - if profile.keys[AXIS_LEFT].enabled - && unsafe { *keyboard_state.offset(profile.keys[AXIS_LEFT].id as isize) } - { - x = -MAX_AXIS_VALUE + let axes = [ + ui::input_profile::AXIS_LEFT, + ui::input_profile::AXIS_RIGHT, + ui::input_profile::AXIS_DOWN, + ui::input_profile::AXIS_UP, + ]; + let mut has_deadzone = false; + for axis in axes { + for input in profile.inputs[axis].iter() { + if !controller.is_null() + && let Some(ui::config::InputItem::ControllerAxis(controller_axis)) = input + { + let axis_position = unsafe { + sdl3_sys::gamepad::SDL_GetGamepadAxis( + controller, + sdl3_sys::gamepad::SDL_GamepadAxis(controller_axis.id), + ) + }; + if axis_position as isize * controller_axis.axis as isize > 0 { + let axis_value = if axis == ui::input_profile::AXIS_LEFT + || axis == ui::input_profile::AXIS_RIGHT + { + &mut x + } else { + &mut y + }; + *axis_value = normalize_axis_position(axis_position); + has_deadzone = true; + } + } else if !joystick.is_null() + && let Some(ui::config::InputItem::JoystickAxis(joystick_axis)) = input + { + let axis_position = + unsafe { sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, joystick_axis.id) }; + if axis_position as isize * joystick_axis.axis as isize > 0 { + let axis_value = if axis == ui::input_profile::AXIS_LEFT + || axis == ui::input_profile::AXIS_RIGHT + { + &mut x + } else { + &mut y + }; + *axis_value = normalize_axis_position(axis_position); + has_deadzone = true; + } + } else if let Some(ui::config::InputItem::Key(key)) = input + && unsafe { *keyboard_state.offset(key.id as isize) } + { + match axis { + ui::input_profile::AXIS_LEFT => x = -MAX_AXIS_VALUE, + ui::input_profile::AXIS_RIGHT => x = MAX_AXIS_VALUE, + ui::input_profile::AXIS_DOWN => y = MAX_AXIS_VALUE, + ui::input_profile::AXIS_UP => y = -MAX_AXIS_VALUE, + _ => unreachable!(), + } + } + } } - if profile.keys[AXIS_RIGHT].enabled - && unsafe { *keyboard_state.offset(profile.keys[AXIS_RIGHT].id as isize) } - { - x = MAX_AXIS_VALUE - } - if profile.keys[AXIS_DOWN].enabled - && unsafe { *keyboard_state.offset(profile.keys[AXIS_DOWN].id as isize) } - { - y = -MAX_AXIS_VALUE - } - if profile.keys[AXIS_UP].enabled - && unsafe { *keyboard_state.offset(profile.keys[AXIS_UP].id as isize) } - { - y = MAX_AXIS_VALUE + + y = y.neg(); + if has_deadzone { + apply_deadzone(&mut x, &mut y, profile.deadzone); } (x, y) } -fn set_buttons_from_joystick( - profile: &ui::config::InputProfile, - i: usize, +fn is_controller_button_pressed( + input: &Option, joystick: *mut sdl3_sys::joystick::SDL_Joystick, - keys: &mut u32, -) { - let profile_joystick_button = profile.joystick_buttons[i]; - if profile_joystick_button.enabled { - *keys |= (unsafe { - sdl3_sys::joystick::SDL_GetJoystickButton(joystick, profile_joystick_button.id) - } as u32) - << i; - } - - let profile_joystick_hat = profile.joystick_hat[i]; - if profile_joystick_hat.enabled - && (unsafe { sdl3_sys::joystick::SDL_GetJoystickHat(joystick, profile_joystick_hat.id) } - & profile_joystick_hat.direction) - != 0 - { - *keys |= 1 << i; - } - - let profile_joystick_axis = profile.joystick_axis[i]; - if profile_joystick_axis.enabled { - let axis_position = - unsafe { sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, profile_joystick_axis.id) }; - if (axis_position as isize * profile_joystick_axis.axis as isize > 0 - || profile_joystick_axis.initial_state != 0) - && axis_position.abs_diff(profile_joystick_axis.initial_state) > (u16::MAX / 4) + controller: *mut sdl3_sys::gamepad::SDL_Gamepad, +) -> bool { + if !controller.is_null() { + if let Some(ui::config::InputItem::ControllerButton(controller_button)) = input + && unsafe { + sdl3_sys::gamepad::SDL_GetGamepadButton( + controller, + sdl3_sys::gamepad::SDL_GamepadButton(controller_button.id), + ) + } { - *keys |= 1 << i; + return true; + } + if let Some(ui::config::InputItem::ControllerAxis(controller_axis)) = input { + let axis_position = unsafe { + sdl3_sys::gamepad::SDL_GetGamepadAxis( + controller, + sdl3_sys::gamepad::SDL_GamepadAxis(controller_axis.id), + ) + }; + if axis_position as isize * controller_axis.axis as isize > 0 + && axis_position.saturating_abs() > i16::MAX / 2 + { + return true; + } + } + } else if !joystick.is_null() { + if let Some(ui::config::InputItem::JoystickButton(joystick_button)) = input + && unsafe { sdl3_sys::joystick::SDL_GetJoystickButton(joystick, joystick_button.id) } + { + return true; + } + if let Some(ui::config::InputItem::JoystickHat(joystick_hat)) = input + && (unsafe { sdl3_sys::joystick::SDL_GetJoystickHat(joystick, joystick_hat.id) } + & joystick_hat.direction) + != 0 + { + return true; + } + if let Some(ui::config::InputItem::JoystickAxis(joystick_axis)) = input { + let axis_position = + unsafe { sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, joystick_axis.id) }; + if (axis_position as isize * joystick_axis.axis as isize > 0 + || joystick_axis.initial_state != 0) + && axis_position.abs_diff(joystick_axis.initial_state) > (u16::MAX / 4) + { + return true; + } } } + false } -fn set_buttons_from_controller( +fn set_buttons( profile: &ui::config::InputProfile, - i: usize, + joystick: *mut sdl3_sys::joystick::SDL_Joystick, controller: *mut sdl3_sys::gamepad::SDL_Gamepad, - keys: &mut u32, -) { - let profile_controller_button = profile.controller_buttons[i]; - if profile_controller_button.enabled { - *keys |= (unsafe { - sdl3_sys::gamepad::SDL_GetGamepadButton( - controller, - sdl3_sys::gamepad::SDL_GamepadButton(profile_controller_button.id), - ) - } as u32) - << i; - } - - let profile_controller_axis = profile.controller_axis[i]; - if profile_controller_axis.enabled { - let axis_position = unsafe { - sdl3_sys::gamepad::SDL_GetGamepadAxis( - controller, - sdl3_sys::gamepad::SDL_GamepadAxis(profile_controller_axis.id), - ) - }; - if axis_position as isize * profile_controller_axis.axis as isize > 0 - && axis_position.saturating_abs() > i16::MAX / 2 - { - *keys |= 1 << i; + keyboard_state: *const bool, + alt_pressed: bool, +) -> u32 { + let mut keys = 0; + for i in 0..14 { + for input in profile.inputs[i].iter() { + if let Some(ui::config::InputItem::Key(key)) = input + && !alt_pressed + && unsafe { *keyboard_state.offset(key.id as isize) } + { + keys |= 1 << i; + } + if is_controller_button_pressed(input, joystick, controller) { + keys |= 1 << i; + } } } + keys } pub fn set_rumble(ui: &ui::Ui, channel: usize, rumble: u8) { @@ -307,44 +246,12 @@ fn hotkey_pressed( joystick: *mut sdl3_sys::joystick::SDL_Joystick, controller: *mut sdl3_sys::gamepad::SDL_Gamepad, ) -> bool { - let controller_button = profile.controller_buttons[HOTKEY]; - let controller_axis = profile.controller_axis[HOTKEY]; - let joystick_button = profile.joystick_buttons[HOTKEY]; - let joystick_hat = profile.joystick_hat[HOTKEY]; - let joystick_axis = profile.joystick_axis[HOTKEY]; - - let mut pressed = false; - if controller_button.enabled && !controller.is_null() { - pressed = unsafe { - sdl3_sys::gamepad::SDL_GetGamepadButton( - controller, - sdl3_sys::gamepad::SDL_GamepadButton(controller_button.id), - ) - }; - } else if controller_axis.enabled && !controller.is_null() { - let axis_position = unsafe { - sdl3_sys::gamepad::SDL_GetGamepadAxis( - controller, - sdl3_sys::gamepad::SDL_GamepadAxis(controller_axis.id), - ) - }; - pressed = axis_position as isize * controller_axis.axis as isize > 0 - && axis_position.saturating_abs() > i16::MAX / 2 - } else if joystick_button.enabled && !joystick.is_null() { - pressed = - unsafe { sdl3_sys::joystick::SDL_GetJoystickButton(joystick, joystick_button.id) }; - } else if joystick_hat.enabled && !joystick.is_null() { - pressed = (unsafe { sdl3_sys::joystick::SDL_GetJoystickHat(joystick, joystick_hat.id) } - & joystick_hat.direction) - != 0; - } else if joystick_axis.enabled && !joystick.is_null() { - let axis_position = - unsafe { sdl3_sys::joystick::SDL_GetJoystickAxis(joystick, joystick_axis.id) }; - pressed = (axis_position as isize * joystick_axis.axis as isize > 0 - || joystick_axis.initial_state != 0) - && axis_position.abs_diff(joystick_axis.initial_state) > (u16::MAX / 4) + for input in profile.inputs[ui::input_profile::HOTKEY].iter() { + if is_controller_button_pressed(input, joystick, controller) { + return true; + } } - pressed + false } pub fn get_controller_names() -> Vec { @@ -446,7 +353,9 @@ fn handle_joystick_events(ui: &mut ui::Ui) { } fn handle_hotkeys(keys: u32, last_key_state: u32) { - if keys & (1 << L_TRIG) != 0 && last_key_state & (1 << L_TRIG) == 0 { + if keys & (1 << ui::input_profile::L_TRIG) != 0 + && last_key_state & (1 << ui::input_profile::L_TRIG) == 0 + { unsafe { sdl3_sys::events::SDL_PushEvent(&mut sdl3_sys::events::SDL_Event { user: sdl3_sys::events::SDL_UserEvent { @@ -457,7 +366,9 @@ fn handle_hotkeys(keys: u32, last_key_state: u32) { }) }; } - if keys & (1 << R_TRIG) != 0 && last_key_state & (1 << R_TRIG) == 0 { + if keys & (1 << ui::input_profile::R_TRIG) != 0 + && last_key_state & (1 << ui::input_profile::R_TRIG) == 0 + { unsafe { sdl3_sys::events::SDL_PushEvent(&mut sdl3_sys::events::SDL_Event { user: sdl3_sys::events::SDL_UserEvent { @@ -468,7 +379,9 @@ fn handle_hotkeys(keys: u32, last_key_state: u32) { }) }; } - if keys & (1 << START_BUTTON) != 0 && last_key_state & (1 << START_BUTTON) == 0 { + if keys & (1 << ui::input_profile::START_BUTTON) != 0 + && last_key_state & (1 << ui::input_profile::START_BUTTON) == 0 + { unsafe { sdl3_sys::events::SDL_PushEvent(&mut sdl3_sys::events::SDL_Event { user: sdl3_sys::events::SDL_UserEvent { @@ -479,7 +392,9 @@ fn handle_hotkeys(keys: u32, last_key_state: u32) { }) }; } - if keys & (1 << Z_TRIG) != 0 && last_key_state & (1 << Z_TRIG) == 0 { + if keys & (1 << ui::input_profile::Z_TRIG) != 0 + && last_key_state & (1 << ui::input_profile::Z_TRIG) == 0 + { unsafe { sdl3_sys::events::SDL_PushEvent(&mut sdl3_sys::events::SDL_Event { user: sdl3_sys::events::SDL_UserEvent { @@ -490,7 +405,9 @@ fn handle_hotkeys(keys: u32, last_key_state: u32) { }) }; } - if keys & (1 << L_CBUTTON) != 0 && last_key_state & (1 << L_CBUTTON) == 0 { + if keys & (1 << ui::input_profile::L_CBUTTON) != 0 + && last_key_state & (1 << ui::input_profile::L_CBUTTON) == 0 + { unsafe { sdl3_sys::events::SDL_PushEvent(&mut sdl3_sys::events::SDL_Event { user: sdl3_sys::events::SDL_UserEvent { @@ -514,7 +431,6 @@ pub fn get(ui: &mut ui::Ui, channel: usize) -> InputData { pak_change_pressed: false, }; }; - let mut keys = 0; let controller = ui.input.controllers[channel].game_controller; let joystick = ui.input.controllers[channel].joystick; @@ -529,37 +445,15 @@ pub fn get(ui: &mut ui::Ui, channel: usize) -> InputData { .offset(i32::from(sdl3_sys::scancode::SDL_SCANCODE_RALT) as isize) }; - for i in 0..14 { - if profile_name != "default" || channel == 0 { - let profile_key = profile.keys[i]; - if profile_key.enabled && !alt_pressed { - keys |= (unsafe { *ui.input.keyboard_state.offset(profile_key.id as isize) } - as u32) - << i; - } - } + let mut keys = set_buttons( + profile, + joystick, + controller, + ui.input.keyboard_state, + alt_pressed, + ); - if !controller.is_null() { - set_buttons_from_controller(profile, i, controller, &mut keys); - } else if !joystick.is_null() { - set_buttons_from_joystick(profile, i, joystick, &mut keys); - } - } - - let mut x: f64 = 0.0; - let mut y: f64 = 0.0; - - if profile_name != "default" || channel == 0 { - (x, y) = set_axis_from_keys(profile, ui.input.keyboard_state); - } - - if !controller.is_null() { - (x, y) = set_axis_from_controller(profile, controller); - apply_deadzone(&mut x, &mut y, profile.deadzone); - } else if !joystick.is_null() { - (x, y) = set_axis_from_joystick(profile, joystick); - apply_deadzone(&mut x, &mut y, profile.deadzone); - } + let (mut x, mut y) = set_axis(profile, joystick, controller, ui.input.keyboard_state); bound_axis(&mut x, &mut y); keys |= (x.round() as i8 as u8 as u32) << X_AXIS_SHIFT; @@ -572,14 +466,20 @@ pub fn get(ui: &mut ui::Ui, channel: usize) -> InputData { handle_hotkeys(keys, last_key_state); InputData { data: 0, - pak_change_pressed: keys & (1 << B_BUTTON) != 0, + pak_change_pressed: keys & (1 << ui::input_profile::B_BUTTON) != 0, } } else { - let key = profile.keys[HOTKEY]; + let mut pak_change_pressed = false; + for input in profile.inputs[ui::input_profile::HOTKEY].iter() { + if let Some(ui::config::InputItem::Key(key)) = input + && unsafe { *ui.input.keyboard_state.offset(key.id as isize) } + { + pak_change_pressed = true; + } + } InputData { data: keys, - pak_change_pressed: key.enabled - && unsafe { *ui.input.keyboard_state.offset(key.id as isize) }, + pak_change_pressed, } } } @@ -617,551 +517,7 @@ pub fn clear_bindings(config: &mut ui::config::Config) { } } -fn close_input_profile_window( - open_joysticks: Vec<*mut sdl3_sys::joystick::SDL_Joystick>, - open_controllers: Vec<*mut sdl3_sys::gamepad::SDL_Gamepad>, - renderer: *mut sdl3_sys::render::SDL_Renderer, - text_engine: *mut sdl3_ttf_sys::ttf::TTF_TextEngine, - font: *mut sdl3_ttf_sys::ttf::TTF_Font, - texture: *mut sdl3_sys::render::SDL_Texture, - window: *mut sdl3_sys::video::SDL_Window, -) { - for joystick in open_joysticks { - unsafe { sdl3_sys::joystick::SDL_CloseJoystick(joystick) } - } - for controller in open_controllers { - unsafe { sdl3_sys::gamepad::SDL_CloseGamepad(controller) } - } - - unsafe { - sdl3_sys::render::SDL_DestroyTexture(texture); - sdl3_ttf_sys::ttf::TTF_CloseFont(font); - sdl3_ttf_sys::ttf::TTF_DestroyRendererTextEngine(text_engine); - sdl3_sys::render::SDL_DestroyRenderer(renderer); - sdl3_sys::video::SDL_DestroyWindow(window); - } -} - -pub fn configure_input_profile( - config: &mut ui::config::Config, - profile: String, - dinput: bool, - deadzone: i32, -) { - ui::sdl_init(sdl3_sys::init::SDL_INIT_VIDEO); - ui::sdl_init(sdl3_sys::init::SDL_INIT_GAMEPAD); - ui::ttf_init(); - - if profile == "default" { - eprintln!("Profile name cannot be default"); - return; - } - if profile.is_empty() { - eprintln!("Profile name cannot be empty"); - return; - } - let mut open_joysticks: Vec<*mut sdl3_sys::joystick::SDL_Joystick> = Vec::new(); - let mut open_controllers: Vec<*mut sdl3_sys::gamepad::SDL_Gamepad> = Vec::new(); - - for joystick in get_joysticks().iter() { - if !dinput { - let controller = unsafe { sdl3_sys::gamepad::SDL_OpenGamepad(*joystick) }; - if !controller.is_null() { - open_controllers.push(controller); - } - } else { - let joystick = unsafe { sdl3_sys::joystick::SDL_OpenJoystick(*joystick) }; - if !joystick.is_null() { - open_joysticks.push(joystick); - } - } - } - - let title = std::ffi::CString::new("configure input profile").unwrap(); - let mut window: *mut sdl3_sys::video::SDL_Window = std::ptr::null_mut(); - let mut renderer: *mut sdl3_sys::render::SDL_Renderer = std::ptr::null_mut(); - #[cfg(target_os = "android")] - let window_flags = sdl3_sys::video::SDL_WINDOW_FULLSCREEN; - #[cfg(not(target_os = "android"))] - let window_flags = sdl3_sys::video::SDL_WindowFlags(0); - if !unsafe { - sdl3_sys::render::SDL_CreateWindowAndRenderer( - title.as_ptr(), - 852, - 480, - window_flags, - &mut window, - &mut renderer, - ) - } { - panic!("Could not create window and renderer") - } - if !unsafe { sdl3_sys::video::SDL_ShowWindow(window) } { - panic!("Could not show window") - } - - let key_labels: [(&str, usize); PROFILE_SIZE] = [ - ("A", A_BUTTON), - ("B", B_BUTTON), - ("Start", START_BUTTON), - ("D Up", U_DPAD), - ("D Down", D_DPAD), - ("D Left", L_DPAD), - ("D Right", R_DPAD), - ("C Up", U_CBUTTON), - ("C Down", D_CBUTTON), - ("C Left", L_CBUTTON), - ("C Right", R_CBUTTON), - ("L", L_TRIG), - ("R", R_TRIG), - ("Z", Z_TRIG), - ("Control Stick Up", AXIS_UP), - ("Control Stick Down", AXIS_DOWN), - ("Control Stick Left", AXIS_LEFT), - ("Control Stick Right", AXIS_RIGHT), - ("Hotkey Activator", HOTKEY), - ]; - - let mut new_keys = [ui::config::InputKeyButton { - enabled: false, - id: 0, - }; PROFILE_SIZE]; - let mut new_joystick_buttons = [ui::config::InputKeyButton { - enabled: false, - id: 0, - }; PROFILE_SIZE]; - let mut new_joystick_hat = [ui::config::InputJoystickHat { - enabled: false, - id: 0, - direction: 0, - }; PROFILE_SIZE]; - let mut new_joystick_axis = [ui::config::InputControllerAxis { - enabled: false, - id: 0, - axis: 0, - initial_state: 0, - }; PROFILE_SIZE]; - let mut new_controller_buttons = [ui::config::InputKeyButton { - enabled: false, - id: 0, - }; PROFILE_SIZE]; - let mut new_controller_axis = [ui::config::InputControllerAxis { - enabled: false, - id: 0, - axis: 0, - initial_state: 0, - }; PROFILE_SIZE]; - - let mut last_joystick_axis_result = ui::config::InputControllerAxis { - enabled: false, - id: 0, - axis: 0, - initial_state: 0, - }; - - let image_bytes = include_bytes!("../../data/ui/controller.png"); - let image = unsafe { - sdl3_image_sys::image::IMG_LoadPNG_IO(sdl3_sys::everything::SDL_IOFromConstMem( - image_bytes.as_ptr() as *const std::ffi::c_void, - image_bytes.len(), - )) - }; - let image_texture = unsafe { sdl3_sys::render::SDL_CreateTextureFromSurface(renderer, image) }; - unsafe { sdl3_sys::surface::SDL_DestroySurface(image) }; - let text_engine = unsafe { sdl3_ttf_sys::ttf::TTF_CreateRendererTextEngine(renderer) }; - let font = unsafe { - let font_bytes = include_bytes!("../../data/ui/RobotoMono-Regular.ttf"); - sdl3_ttf_sys::ttf::TTF_OpenFontIO( - sdl3_sys::everything::SDL_IOFromConstMem( - font_bytes.as_ptr() as *const std::ffi::c_void, - font_bytes.len(), - ), - true, - 35.0, - ) - }; - - for (key, value) in key_labels.iter() { - unsafe { - sdl3_sys::events::SDL_PumpEvents(); - sdl3_sys::events::SDL_FlushEvents( - u32::from(sdl3_sys::events::SDL_EVENT_FIRST), - u32::from(sdl3_sys::events::SDL_EVENT_LAST), - ); - } - - let mut key_set = false; - while !key_set { - ui::video::draw_text( - format!("Select binding for: {key}").as_str(), - renderer, - image_texture, - text_engine, - font, - ); - let mut event: sdl3_sys::events::SDL_Event = Default::default(); - while !key_set && unsafe { sdl3_sys::events::SDL_WaitEventTimeout(&mut event, 100) } { - if event.event_type() == sdl3_sys::events::SDL_EVENT_WINDOW_CLOSE_REQUESTED - || (event.event_type() == sdl3_sys::events::SDL_EVENT_KEY_DOWN - && unsafe { event.key.scancode } - == sdl3_sys::scancode::SDL_SCANCODE_AC_BACK) - { - close_input_profile_window( - open_joysticks, - open_controllers, - renderer, - text_engine, - font, - image_texture, - window, - ); - return; - } else if event.event_type() == sdl3_sys::events::SDL_EVENT_KEY_DOWN { - if unsafe { - !event.key.repeat - && event.key.scancode != sdl3_sys::scancode::SDL_SCANCODE_LALT - && event.key.scancode != sdl3_sys::scancode::SDL_SCANCODE_RALT - } { - new_keys[*value] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(unsafe { event.key.scancode }), - }; - key_set = true - } - } else if event.event_type() == sdl3_sys::events::SDL_EVENT_GAMEPAD_BUTTON_DOWN { - if !open_controllers.is_empty() { - new_controller_buttons[*value] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(unsafe { event.gbutton.button }), - }; - key_set = true - } - } else if event.event_type() == sdl3_sys::events::SDL_EVENT_GAMEPAD_AXIS_MOTION { - let axis_value = unsafe { event.gaxis.value }; - let axis = unsafe { event.gaxis.axis }; - if !open_controllers.is_empty() - && axis_value.saturating_abs() > (i16::MAX as i32 * 3 / 4) as i16 - { - let result = ui::config::InputControllerAxis { - enabled: true, - id: axis as i32, - axis: axis_value / axis_value.saturating_abs(), - initial_state: 0, - }; - if result != last_joystick_axis_result { - new_controller_axis[*value] = result; - last_joystick_axis_result = result; - key_set = true - } - } - } else if event.event_type() == sdl3_sys::events::SDL_EVENT_JOYSTICK_BUTTON_DOWN { - if !open_joysticks.is_empty() { - new_joystick_buttons[*value] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(unsafe { event.jbutton.button }), - }; - key_set = true - } - } else if event.event_type() == sdl3_sys::events::SDL_EVENT_JOYSTICK_HAT_MOTION { - let state = unsafe { event.jhat.value }; - let hat = unsafe { event.jhat.hat }; - if !open_joysticks.is_empty() && state != sdl3_sys::joystick::SDL_HAT_CENTERED { - new_joystick_hat[*value] = ui::config::InputJoystickHat { - enabled: true, - id: hat as i32, - direction: state, - }; - key_set = true - } - } else if event.event_type() == sdl3_sys::events::SDL_EVENT_JOYSTICK_AXIS_MOTION { - let axis_value = unsafe { event.jaxis.value }; - let axis = unsafe { event.jaxis.axis }; - - let mut initial_state = 0; - let has_initial_state = unsafe { - sdl3_sys::joystick::SDL_GetJoystickAxisInitialState( - sdl3_sys::joystick::SDL_GetJoystickFromID(event.jaxis.which), - axis as i32, - &mut initial_state, - ) - }; - initial_state = if has_initial_state { - if initial_state < i16::MIN / 2 { - i16::MIN - } else if initial_state > i16::MAX / 2 { - i16::MAX - } else { - 0 - } - } else { - 0 - }; - - if !open_joysticks.is_empty() - && axis_value.abs_diff(initial_state) > (u16::MAX / 4) - { - let result = ui::config::InputControllerAxis { - enabled: true, - id: axis as i32, - axis: axis_value / axis_value.saturating_abs(), - initial_state, - }; - let same_trigger = last_joystick_axis_result.id == result.id - && last_joystick_axis_result.initial_state != 0; - if result != last_joystick_axis_result && !same_trigger { - new_joystick_axis[*value] = result; - last_joystick_axis_result = result; - key_set = true - } - } - } - } - } - ui::video::draw_text( - "Binding set, please wait...", - renderer, - image_texture, - text_engine, - font, - ); - std::thread::sleep(std::time::Duration::from_millis(500)); - } - - close_input_profile_window( - open_joysticks, - open_controllers, - renderer, - text_engine, - font, - image_texture, - window, - ); - - let new_profile = ui::config::InputProfile { - keys: new_keys, - controller_buttons: new_controller_buttons, - controller_axis: new_controller_axis, - joystick_buttons: new_joystick_buttons, - joystick_hat: new_joystick_hat, - joystick_axis: new_joystick_axis, - dinput, - deadzone, - }; - config.input.input_profiles.insert(profile, new_profile); -} - -pub fn get_default_profile() -> ui::config::InputProfile { - let mut default_controller_buttons = [ui::config::InputKeyButton { - enabled: false, - id: 0, - }; PROFILE_SIZE]; - let mut default_controller_axis = [ui::config::InputControllerAxis { - enabled: false, - id: 0, - axis: 0, - initial_state: 0, - }; PROFILE_SIZE]; - let mut default_keys = [ui::config::InputKeyButton { - enabled: false, - id: 0, - }; PROFILE_SIZE]; - default_keys[R_DPAD] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_D), - }; - default_keys[L_DPAD] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_A), - }; - default_keys[D_DPAD] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_S), - }; - default_keys[U_DPAD] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_W), - }; - default_keys[START_BUTTON] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_RETURN), - }; - default_keys[Z_TRIG] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_Z), - }; - default_keys[B_BUTTON] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_LCTRL), - }; - default_keys[A_BUTTON] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_LSHIFT), - }; - default_keys[R_CBUTTON] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_L), - }; - default_keys[L_CBUTTON] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_J), - }; - default_keys[D_CBUTTON] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_K), - }; - default_keys[U_CBUTTON] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_I), - }; - default_keys[R_TRIG] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_C), - }; - default_keys[L_TRIG] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_X), - }; - default_keys[AXIS_LEFT] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_LEFT), - }; - default_keys[AXIS_RIGHT] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_RIGHT), - }; - default_keys[AXIS_UP] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_UP), - }; - default_keys[AXIS_DOWN] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_DOWN), - }; - default_keys[HOTKEY] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_COMMA), - }; - - default_controller_buttons[R_DPAD] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_RIGHT), - }; - default_controller_buttons[L_DPAD] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_LEFT), - }; - default_controller_buttons[D_DPAD] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_DOWN), - }; - default_controller_buttons[U_DPAD] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_UP), - }; - default_controller_buttons[START_BUTTON] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_START), - }; - default_controller_axis[Z_TRIG] = ui::config::InputControllerAxis { - enabled: true, - id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFT_TRIGGER), - axis: 1, - initial_state: 0, - }; - default_controller_buttons[B_BUTTON] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_WEST), - }; - default_controller_buttons[A_BUTTON] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_SOUTH), - }; - default_controller_axis[R_CBUTTON] = ui::config::InputControllerAxis { - enabled: true, - id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTX), - axis: 1, - initial_state: 0, - }; - default_controller_axis[L_CBUTTON] = ui::config::InputControllerAxis { - enabled: true, - id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTX), - axis: -1, - initial_state: 0, - }; - default_controller_axis[D_CBUTTON] = ui::config::InputControllerAxis { - enabled: true, - id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTY), - axis: 1, - initial_state: 0, - }; - default_controller_axis[U_CBUTTON] = ui::config::InputControllerAxis { - enabled: true, - id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTY), - axis: -1, - initial_state: 0, - }; - default_controller_buttons[R_TRIG] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER), - }; - default_controller_buttons[L_TRIG] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_LEFT_SHOULDER), - }; - default_controller_axis[AXIS_LEFT] = ui::config::InputControllerAxis { - enabled: true, - id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTX), - axis: -1, - initial_state: 0, - }; - default_controller_axis[AXIS_RIGHT] = ui::config::InputControllerAxis { - enabled: true, - id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTX), - axis: 1, - initial_state: 0, - }; - default_controller_axis[AXIS_UP] = ui::config::InputControllerAxis { - enabled: true, - id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTY), - axis: -1, - initial_state: 0, - }; - default_controller_axis[AXIS_DOWN] = ui::config::InputControllerAxis { - enabled: true, - id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTY), - axis: 1, - initial_state: 0, - }; - default_controller_buttons[HOTKEY] = ui::config::InputKeyButton { - enabled: true, - id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_BACK), - }; - - ui::config::InputProfile { - keys: default_keys, - controller_buttons: default_controller_buttons, - controller_axis: default_controller_axis, - joystick_buttons: [ui::config::InputKeyButton { - enabled: false, - id: 0, - }; PROFILE_SIZE], - joystick_hat: [ui::config::InputJoystickHat { - enabled: false, - id: 0, - direction: 0, - }; PROFILE_SIZE], - joystick_axis: [ui::config::InputControllerAxis { - enabled: false, - id: 0, - axis: 0, - initial_state: 0, - }; PROFILE_SIZE], - dinput: false, - deadzone: DEADZONE_DEFAULT, - } -} - -fn get_joysticks() -> Vec { +pub fn get_joysticks() -> Vec { unsafe { sdl3_sys::events::SDL_PumpEvents() }; let mut num_joysticks = 0; let sdl_joysticks = unsafe { sdl3_sys::joystick::SDL_GetJoysticks(&mut num_joysticks) }; diff --git a/src/ui/input_profile.rs b/src/ui/input_profile.rs new file mode 100644 index 00000000..2169e376 --- /dev/null +++ b/src/ui/input_profile.rs @@ -0,0 +1,929 @@ +use crate::ui; + +const R_DPAD: usize = 0; +const L_DPAD: usize = 1; +const D_DPAD: usize = 2; +const U_DPAD: usize = 3; +pub const START_BUTTON: usize = 4; +pub const Z_TRIG: usize = 5; +pub const B_BUTTON: usize = 6; +const A_BUTTON: usize = 7; +const R_CBUTTON: usize = 8; +pub const L_CBUTTON: usize = 9; +const D_CBUTTON: usize = 10; +const U_CBUTTON: usize = 11; +pub const R_TRIG: usize = 12; +pub const L_TRIG: usize = 13; +pub const AXIS_RIGHT: usize = 14; +pub const AXIS_LEFT: usize = 15; +pub const AXIS_DOWN: usize = 16; +pub const AXIS_UP: usize = 17; +pub const HOTKEY: usize = 18; +pub const PROFILE_SIZE: usize = 19; + +pub const DEADZONE_DEFAULT: i32 = 5; + +const GATHER_TIME: usize = 5; + +pub fn configure_input_profile( + config: &mut ui::config::Config, + profile_name: String, + dinput: bool, + deadzone: i32, +) { + ui::sdl_init(sdl3_sys::init::SDL_INIT_VIDEO); + ui::sdl_init(sdl3_sys::init::SDL_INIT_GAMEPAD); + ui::ttf_init(); + + if profile_name == "default" { + eprintln!("Profile name cannot be default"); + return; + } + if profile_name.is_empty() { + eprintln!("Profile name cannot be empty"); + return; + } + + let mut profile = if let Some(profile) = config.input.input_profiles.get(&profile_name) { + profile.clone() + } else { + ui::config::InputProfile { + inputs: [const { [None, None] }; PROFILE_SIZE], + dinput, + deadzone, + } + }; + + let mut open_joysticks: Vec<*mut sdl3_sys::joystick::SDL_Joystick> = Vec::new(); + let mut open_controllers: Vec<*mut sdl3_sys::gamepad::SDL_Gamepad> = Vec::new(); + + for joystick in ui::input::get_joysticks().iter() { + if !dinput { + let controller = unsafe { sdl3_sys::gamepad::SDL_OpenGamepad(*joystick) }; + if !controller.is_null() { + open_controllers.push(controller); + } + } else { + let joystick = unsafe { sdl3_sys::joystick::SDL_OpenJoystick(*joystick) }; + if !joystick.is_null() { + open_joysticks.push(joystick); + } + } + } + + let title = std::ffi::CString::new("configure input profile").unwrap(); + let mut window: *mut sdl3_sys::video::SDL_Window = std::ptr::null_mut(); + let mut renderer: *mut sdl3_sys::render::SDL_Renderer = std::ptr::null_mut(); + #[cfg(target_os = "android")] + let window_flags = sdl3_sys::video::SDL_WINDOW_FULLSCREEN; + #[cfg(not(target_os = "android"))] + let window_flags = sdl3_sys::video::SDL_WindowFlags(0); + if !unsafe { + sdl3_sys::render::SDL_CreateWindowAndRenderer( + title.as_ptr(), + 852, + 480, + window_flags, + &mut window, + &mut renderer, + ) + } { + panic!("Could not create window and renderer") + } + if !unsafe { sdl3_sys::video::SDL_ShowWindow(window) } { + panic!("Could not show window") + } + + let text_engine = unsafe { sdl3_ttf_sys::ttf::TTF_CreateRendererTextEngine(renderer) }; + let font = unsafe { + let font_bytes = include_bytes!("../../data/ui/RobotoMono-Regular.ttf"); + sdl3_ttf_sys::ttf::TTF_OpenFontIO( + sdl3_sys::everything::SDL_IOFromConstMem( + font_bytes.as_ptr() as *const std::ffi::c_void, + font_bytes.len(), + ), + true, + 12.0, + ) + }; + + set_font_size(window, font); + + if draw_profile_screen(window, renderer, text_engine, font, &mut profile, dinput) { + profile.deadzone = deadzone; + profile.dinput = dinput; + config.input.input_profiles.insert(profile_name, profile); + } + + for joystick in open_joysticks { + unsafe { sdl3_sys::joystick::SDL_CloseJoystick(joystick) } + } + for controller in open_controllers { + unsafe { sdl3_sys::gamepad::SDL_CloseGamepad(controller) } + } + + unsafe { + sdl3_ttf_sys::ttf::TTF_CloseFont(font); + sdl3_ttf_sys::ttf::TTF_DestroyRendererTextEngine(text_engine); + sdl3_sys::render::SDL_DestroyRenderer(renderer); + sdl3_sys::video::SDL_DestroyWindow(window); + } +} + +fn set_font_size(window: *mut sdl3_sys::video::SDL_Window, font: *mut sdl3_ttf_sys::ttf::TTF_Font) { + let mut rect: sdl3_sys::rect::SDL_Rect = Default::default(); + unsafe { + sdl3_ttf_sys::ttf::TTF_SetFontSize(font, 12.0); + sdl3_sys::video::SDL_GetWindowSafeArea(window, &mut rect) + }; + let num_rows = PROFILE_SIZE + 2; + let min_row_height = rect.h / num_rows as i32; + while unsafe { sdl3_ttf_sys::ttf::TTF_GetFontLineSkip(font) } < min_row_height { + unsafe { + let font_size = sdl3_ttf_sys::ttf::TTF_GetFontSize(font); + sdl3_ttf_sys::ttf::TTF_SetFontSize(font, font_size + 0.5); + } + } +} + +fn reset_gather(auto_advance: bool) -> (usize, Option) { + if auto_advance { + (GATHER_TIME, Some(std::time::Instant::now())) + } else { + (0, None) + } +} + +fn advance_cursor( + selected: &mut (usize, usize), + auto_advance: &mut bool, +) -> (usize, Option) { + selected.0 += 1; + if selected.0 == PROFILE_SIZE { + *auto_advance = false; + } + reset_gather(*auto_advance) +} + +fn draw_profile_screen( + window: *mut sdl3_sys::video::SDL_Window, + renderer: *mut sdl3_sys::render::SDL_Renderer, + text_engine: *mut sdl3_ttf_sys::ttf::TTF_TextEngine, + font: *mut sdl3_ttf_sys::ttf::TTF_Font, + profile: &mut ui::config::InputProfile, + dinput: bool, +) -> bool { + let mut selected: (usize, usize) = (0, 0); + let mut auto_advance = dinput && !unsafe { sdl3_sys::keyboard::SDL_HasKeyboard() }; + + let (mut gather, mut gather_timer) = reset_gather(auto_advance); + loop { + if let Some(timer) = gather_timer + && timer.elapsed() > std::time::Duration::from_secs(1) + { + gather -= 1; + if gather > 0 { + gather_timer = Some(std::time::Instant::now()); + } else { + (gather, gather_timer) = reset_gather(auto_advance); + } + } + let mut event: sdl3_sys::events::SDL_Event = Default::default(); + + if unsafe { sdl3_sys::events::SDL_WaitEventTimeout(&mut event, 20) } { + match event.event_type() { + sdl3_sys::events::SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED => { + set_font_size(window, font); + } + sdl3_sys::events::SDL_EVENT_WINDOW_CLOSE_REQUESTED => break, + sdl3_sys::events::SDL_EVENT_GAMEPAD_BUTTON_DOWN => { + if gather == 0 { + match sdl3_sys::gamepad::SDL_GamepadButton( + unsafe { event.gbutton.button } as i32 + ) { + sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_DOWN => { + move_down(&mut selected); + } + sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_UP => { + move_up(&mut selected); + } + sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_LEFT => { + move_left(&mut selected); + } + sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_RIGHT => { + move_right(&mut selected); + } + sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_WEST + | sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_NORTH => { + delete_binding(selected, profile); + } + sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_SOUTH + | sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_EAST + if select_or_save(selected, &mut gather, &mut gather_timer) => + { + return true; + } + _ => (), + } + } else if !dinput { + profile.inputs[selected.0][selected.1] = Some( + ui::config::InputItem::ControllerButton(ui::config::InputKeyButton { + id: i32::from(unsafe { event.gbutton.button }), + }), + ); + (gather, gather_timer) = advance_cursor(&mut selected, &mut auto_advance); + } + } + sdl3_sys::events::SDL_EVENT_KEY_DOWN => { + let scancode = unsafe { event.key }.scancode; + if gather == 0 || scancode == sdl3_sys::scancode::SDL_SCANCODE_AC_BACK { + match scancode { + sdl3_sys::scancode::SDL_SCANCODE_AC_BACK => break, + sdl3_sys::scancode::SDL_SCANCODE_DOWN => { + move_down(&mut selected); + } + sdl3_sys::scancode::SDL_SCANCODE_UP => { + move_up(&mut selected); + } + sdl3_sys::scancode::SDL_SCANCODE_LEFT => { + move_left(&mut selected); + } + sdl3_sys::scancode::SDL_SCANCODE_RIGHT => { + move_right(&mut selected); + } + sdl3_sys::scancode::SDL_SCANCODE_DELETE + | sdl3_sys::scancode::SDL_SCANCODE_BACKSPACE => { + delete_binding(selected, profile); + } + sdl3_sys::scancode::SDL_SCANCODE_RETURN + if select_or_save(selected, &mut gather, &mut gather_timer) => + { + return true; + } + _ => (), + } + } else { + if scancode != sdl3_sys::scancode::SDL_SCANCODE_LALT + && scancode != sdl3_sys::scancode::SDL_SCANCODE_RALT + && !unsafe { event.key }.repeat + { + profile.inputs[selected.0][selected.1] = + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(scancode), + })); + (gather, gather_timer) = + advance_cursor(&mut selected, &mut auto_advance); + } + } + } + sdl3_sys::events::SDL_EVENT_GAMEPAD_AXIS_MOTION => { + let axis_value = unsafe { event.gaxis }.value; + let axis = unsafe { event.gaxis }.axis; + if !dinput + && gather != 0 + && axis_value.saturating_abs() > (i16::MAX as i32 * 3 / 4) as i16 + { + profile.inputs[selected.0][selected.1] = + Some(ui::config::InputItem::ControllerAxis( + ui::config::InputControllerAxis { + id: i32::from(axis), + axis: axis_value.signum(), + initial_state: 0, + }, + )); + (gather, gather_timer) = advance_cursor(&mut selected, &mut auto_advance); + } + } + sdl3_sys::events::SDL_EVENT_JOYSTICK_BUTTON_DOWN => { + if dinput { + if gather == 0 { + if let Some(ui::config::InputItem::JoystickButton(joystick_button)) = + &profile.inputs[D_DPAD][0] + && joystick_button.id == unsafe { event.jbutton.button } as i32 + { + move_down(&mut selected); + } else if let Some(ui::config::InputItem::JoystickButton( + joystick_button, + )) = &profile.inputs[U_DPAD][0] + && joystick_button.id == unsafe { event.jbutton.button } as i32 + { + move_up(&mut selected); + } else if let Some(ui::config::InputItem::JoystickButton( + joystick_button, + )) = &profile.inputs[L_DPAD][0] + && joystick_button.id == unsafe { event.jbutton.button } as i32 + { + move_left(&mut selected); + } else if let Some(ui::config::InputItem::JoystickButton( + joystick_button, + )) = &profile.inputs[R_DPAD][0] + && joystick_button.id == unsafe { event.jbutton.button } as i32 + { + move_right(&mut selected); + } else if let Some(ui::config::InputItem::JoystickButton( + joystick_button, + )) = &profile.inputs[B_BUTTON][0] + && joystick_button.id == unsafe { event.jbutton.button } as i32 + { + delete_binding(selected, profile); + } else if let Some(ui::config::InputItem::JoystickButton( + joystick_button, + )) = &profile.inputs[A_BUTTON][0] + && joystick_button.id == unsafe { event.jbutton.button } as i32 + && select_or_save(selected, &mut gather, &mut gather_timer) + { + return true; + } + } else { + profile.inputs[selected.0][selected.1] = Some( + ui::config::InputItem::JoystickButton(ui::config::InputKeyButton { + id: i32::from(unsafe { event.jbutton.button }), + }), + ); + (gather, gather_timer) = + advance_cursor(&mut selected, &mut auto_advance); + } + } + } + sdl3_sys::events::SDL_EVENT_JOYSTICK_HAT_MOTION => { + let state = unsafe { event.jhat.value }; + let hat = unsafe { event.jhat.hat }; + if dinput { + if gather == 0 { + if let Some(ui::config::InputItem::JoystickHat(joystick_hat)) = + &profile.inputs[D_DPAD][0] + && joystick_hat.id == hat as i32 + && joystick_hat.direction == state + { + move_down(&mut selected); + } else if let Some(ui::config::InputItem::JoystickHat(joystick_hat)) = + &profile.inputs[U_DPAD][0] + && joystick_hat.id == hat as i32 + && joystick_hat.direction == state + { + move_up(&mut selected); + } else if let Some(ui::config::InputItem::JoystickHat(joystick_hat)) = + &profile.inputs[L_DPAD][0] + && joystick_hat.id == hat as i32 + && joystick_hat.direction == state + { + move_left(&mut selected); + } else if let Some(ui::config::InputItem::JoystickHat(joystick_hat)) = + &profile.inputs[R_DPAD][0] + && joystick_hat.id == hat as i32 + && joystick_hat.direction == state + { + move_right(&mut selected); + } else if let Some(ui::config::InputItem::JoystickHat(joystick_hat)) = + &profile.inputs[B_BUTTON][0] + && joystick_hat.id == hat as i32 + && joystick_hat.direction == state + { + delete_binding(selected, profile); + } else if let Some(ui::config::InputItem::JoystickHat(joystick_hat)) = + &profile.inputs[A_BUTTON][0] + && joystick_hat.id == hat as i32 + && joystick_hat.direction == state + && select_or_save(selected, &mut gather, &mut gather_timer) + { + return true; + } + } else if state != sdl3_sys::joystick::SDL_HAT_CENTERED { + profile.inputs[selected.0][selected.1] = Some( + ui::config::InputItem::JoystickHat(ui::config::InputJoystickHat { + id: hat as i32, + direction: state, + }), + ); + (gather, gather_timer) = + advance_cursor(&mut selected, &mut auto_advance); + } + } + } + sdl3_sys::events::SDL_EVENT_JOYSTICK_AXIS_MOTION => { + let axis_value = unsafe { event.jaxis }.value; + let axis = unsafe { event.jaxis }.axis; + let mut initial_state = 0; + let has_initial_state = unsafe { + sdl3_sys::joystick::SDL_GetJoystickAxisInitialState( + sdl3_sys::joystick::SDL_GetJoystickFromID(event.jaxis.which), + axis as i32, + &mut initial_state, + ) + }; + initial_state = if has_initial_state { + if initial_state < i16::MIN / 2 { + i16::MIN + } else if initial_state > i16::MAX / 2 { + i16::MAX + } else { + 0 + } + } else { + 0 + }; + if dinput && gather != 0 && axis_value.abs_diff(initial_state) > (u16::MAX / 4) + { + profile.inputs[selected.0][selected.1] = Some( + ui::config::InputItem::JoystickAxis(ui::config::InputControllerAxis { + id: i32::from(axis), + axis: axis_value.signum(), + initial_state, + }), + ); + (gather, gather_timer) = advance_cursor(&mut selected, &mut auto_advance); + } + } + _ => (), + } + } + unsafe { + sdl3_sys::everything::SDL_RenderClear(renderer); + let ttf_text = + sdl3_ttf_sys::ttf::TTF_CreateText(text_engine, font, std::ptr::null(), 0); + render_screen(window, renderer, ttf_text, font, profile, selected, gather); + sdl3_ttf_sys::ttf::TTF_DestroyText(ttf_text); + sdl3_sys::render::SDL_RenderPresent(renderer); + } + } + false +} + +fn move_left(selected: &mut (usize, usize)) { + selected.1 = selected.1.saturating_sub(1) +} + +fn move_right(selected: &mut (usize, usize)) { + selected.1 = selected.1.saturating_add(1).min(1) +} + +fn move_up(selected: &mut (usize, usize)) { + selected.0 = selected.0.saturating_sub(1) +} + +fn move_down(selected: &mut (usize, usize)) { + selected.0 = selected.0.saturating_add(1).min(PROFILE_SIZE) +} + +fn delete_binding(selected: (usize, usize), profile: &mut ui::config::InputProfile) { + if selected.0 < PROFILE_SIZE { + profile.inputs[selected.0][selected.1] = None; + } +} + +fn select_or_save( + selected: (usize, usize), + gather: &mut usize, + gather_timer: &mut Option, +) -> bool { + if selected.0 == PROFILE_SIZE { + return true; + } else { + *gather = GATHER_TIME; + *gather_timer = Some(std::time::Instant::now()); + } + false +} + +fn render_screen( + window: *mut sdl3_sys::video::SDL_Window, + renderer: *mut sdl3_sys::render::SDL_Renderer, + ttf_text: *mut sdl3_ttf_sys::ttf::TTF_Text, + font: *mut sdl3_ttf_sys::ttf::TTF_Font, + profile: &ui::config::InputProfile, + selected: (usize, usize), + gather: usize, +) { + let mut rect: sdl3_sys::rect::SDL_Rect = Default::default(); + unsafe { sdl3_sys::video::SDL_GetWindowSafeArea(window, &mut rect) }; + + let row_height = unsafe { sdl3_ttf_sys::ttf::TTF_GetFontLineSkip(font) } as usize; + let key_labels: [&str; PROFILE_SIZE] = [ + "D Right", + "D Left", + "D Down", + "D Up", + "Start", + "Z", + "B", + "A", + "C Right", + "C Left", + "C Down", + "C Up", + "R", + "L", + "Control Stick Right", + "Control Stick Left", + "Control Stick Down", + "Control Stick Up", + "Hotkey Activator", + ]; + + // headers + unsafe { + sdl3_ttf_sys::ttf::TTF_SetFontStyle( + font, + sdl3_ttf_sys::ttf::TTF_STYLE_BOLD | sdl3_ttf_sys::ttf::TTF_STYLE_UNDERLINE, + ); + let c_text = std::ffi::CString::new("Inputs").unwrap(); + sdl3_ttf_sys::ttf::TTF_SetTextString(ttf_text, c_text.as_ptr(), 0); + sdl3_ttf_sys::ttf::TTF_DrawRendererText(ttf_text, rect.x as f32, rect.y as f32); + + let c_text = std::ffi::CString::new("Binding 1").unwrap(); + sdl3_ttf_sys::ttf::TTF_SetTextString(ttf_text, c_text.as_ptr(), 0); + sdl3_ttf_sys::ttf::TTF_DrawRendererText( + ttf_text, + rect.x as f32 + (rect.w / 3) as f32 * 1.0, + rect.y as f32, + ); + + let c_text = std::ffi::CString::new("Binding 2").unwrap(); + sdl3_ttf_sys::ttf::TTF_SetTextString(ttf_text, c_text.as_ptr(), 0); + sdl3_ttf_sys::ttf::TTF_DrawRendererText( + ttf_text, + rect.x as f32 + (rect.w / 3) as f32 * 2.0, + rect.y as f32, + ); + sdl3_ttf_sys::ttf::TTF_SetFontStyle(font, sdl3_ttf_sys::ttf::TTF_STYLE_NORMAL); + } + + for (i, label) in key_labels.iter().enumerate() { + let c_text = std::ffi::CString::new(*label).unwrap(); + unsafe { + sdl3_ttf_sys::ttf::TTF_SetTextString(ttf_text, c_text.as_ptr(), 0); + sdl3_ttf_sys::ttf::TTF_DrawRendererText( + ttf_text, + rect.x as f32, + rect.y as f32 + ((i + 1) * row_height) as f32, + ); + + let binding_1 = + std::ffi::CString::new(get_binding_text(&profile.inputs[i][0])).unwrap(); + sdl3_ttf_sys::ttf::TTF_SetTextString(ttf_text, binding_1.as_ptr(), 0); + draw_binding_text(renderer, ttf_text, rect, row_height, selected, gather, i, 0); + + let binding_2 = + std::ffi::CString::new(get_binding_text(&profile.inputs[i][1])).unwrap(); + sdl3_ttf_sys::ttf::TTF_SetTextString(ttf_text, binding_2.as_ptr(), 0); + draw_binding_text(renderer, ttf_text, rect, row_height, selected, gather, i, 1); + } + } + let c_text = std::ffi::CString::new("Save and Exit").unwrap(); + unsafe { + sdl3_ttf_sys::ttf::TTF_SetFontStyle(font, sdl3_ttf_sys::ttf::TTF_STYLE_BOLD); + sdl3_ttf_sys::ttf::TTF_SetTextString(ttf_text, c_text.as_ptr(), 0); + let (mut text_w, mut text_h) = (0, 0); + sdl3_ttf_sys::ttf::TTF_GetTextSize(ttf_text, &mut text_w, &mut text_h); + + if selected.0 == PROFILE_SIZE { + draw_highlight( + renderer, + rect.x as f32 + (rect.w - text_w) as f32 / 2.0, + rect.y as f32 + ((PROFILE_SIZE + 1) * row_height) as f32, + text_w, + text_h, + ); + } + + sdl3_ttf_sys::ttf::TTF_DrawRendererText( + ttf_text, + rect.x as f32 + (rect.w - text_w) as f32 / 2.0, + rect.y as f32 + ((PROFILE_SIZE + 1) * row_height) as f32, + ); + }; +} + +fn draw_highlight( + renderer: *mut sdl3_sys::render::SDL_Renderer, + x: f32, + y: f32, + text_w: i32, + text_h: i32, +) { + unsafe { + sdl3_sys::render::SDL_SetRenderDrawColor(renderer, 50, 100, 180, 255); + + let highlight = sdl3_sys::rect::SDL_FRect { + x, + y, + w: text_w as f32, + h: text_h as f32, + }; + sdl3_sys::render::SDL_RenderFillRect(renderer, &highlight); + sdl3_sys::render::SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + } +} + +#[allow(clippy::too_many_arguments)] +fn draw_binding_text( + renderer: *mut sdl3_sys::render::SDL_Renderer, + ttf_text: *mut sdl3_ttf_sys::ttf::TTF_Text, + rect: sdl3_sys::rect::SDL_Rect, + row_height: usize, + selected: (usize, usize), + gather: usize, + i: usize, + binding: usize, +) { + unsafe { + if selected.0 == i && selected.1 == binding { + if gather > 0 { + let gather_text = std::ffi::CString::new(format!("{}...", gather)).unwrap(); + sdl3_ttf_sys::ttf::TTF_SetTextString(ttf_text, gather_text.as_ptr(), 0); + } + let (mut text_w, mut text_h) = (0, 0); + sdl3_ttf_sys::ttf::TTF_GetTextSize(ttf_text, &mut text_w, &mut text_h); + draw_highlight( + renderer, + rect.x as f32 + (rect.w / 3) as f32 * if selected.1 == 0 { 1.0 } else { 2.0 }, + rect.y as f32 + ((i + 1) * row_height) as f32, + text_w, + text_h, + ); + } + sdl3_ttf_sys::ttf::TTF_DrawRendererText( + ttf_text, + rect.x as f32 + (rect.w / 3) as f32 * (binding as f32 + 1.0), + rect.y as f32 + ((i + 1) * row_height) as f32, + ); + } +} + +fn get_binding_text(binding: &Option) -> String { + if let Some(binding) = binding { + match binding { + ui::config::InputItem::Key(key) => { + return unsafe { + std::ffi::CStr::from_ptr(sdl3_sys::keyboard::SDL_GetScancodeName( + sdl3_sys::scancode::SDL_Scancode(key.id), + )) + .to_str() + .unwrap() + .to_string() + }; + } + ui::config::InputItem::ControllerButton(controller_button) => { + return unsafe { + std::ffi::CStr::from_ptr(sdl3_sys::gamepad::SDL_GetGamepadStringForButton( + sdl3_sys::gamepad::SDL_GamepadButton(controller_button.id), + )) + .to_str() + .unwrap() + .to_string() + }; + } + ui::config::InputItem::ControllerAxis(controller_axis) => { + return unsafe { + let axis = + std::ffi::CStr::from_ptr(sdl3_sys::gamepad::SDL_GetGamepadStringForAxis( + sdl3_sys::gamepad::SDL_GamepadAxis(controller_axis.id), + )) + .to_str() + .unwrap() + .to_string(); + let direction = if controller_axis.axis > 0 { "+" } else { "-" }; + format!("{} {}", axis, direction) + }; + } + ui::config::InputItem::JoystickButton(joystick_button) => { + return format!("Joystick Button {}", joystick_button.id); + } + ui::config::InputItem::JoystickHat(joystick_hat) => { + let direction = match joystick_hat.direction { + 1 => "Up", + 2 => "Right", + 4 => "Down", + 8 => "Left", + _ => "Unknown", + }; + return format!("Joystick Hat {} {}", joystick_hat.id, direction); + } + ui::config::InputItem::JoystickAxis(joystick_axis) => { + let direction = if joystick_axis.axis > 0 { "+" } else { "-" }; + return format!("Joystick Axis {} {}", joystick_axis.id, direction); + } + } + } + "(unset)".to_string() +} + +pub fn get_default_profile() -> ui::config::InputProfile { + let mut inputs: [[Option; 2]; PROFILE_SIZE] = + [const { [None, None] }; PROFILE_SIZE]; + + inputs[R_DPAD] = [ + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_D), + })), + Some(ui::config::InputItem::ControllerButton( + ui::config::InputKeyButton { + id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_RIGHT), + }, + )), + ]; + inputs[L_DPAD] = [ + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_A), + })), + Some(ui::config::InputItem::ControllerButton( + ui::config::InputKeyButton { + id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_LEFT), + }, + )), + ]; + inputs[D_DPAD] = [ + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_S), + })), + Some(ui::config::InputItem::ControllerButton( + ui::config::InputKeyButton { + id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_DOWN), + }, + )), + ]; + inputs[U_DPAD] = [ + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_W), + })), + Some(ui::config::InputItem::ControllerButton( + ui::config::InputKeyButton { + id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_DPAD_UP), + }, + )), + ]; + inputs[START_BUTTON] = [ + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_RETURN), + })), + Some(ui::config::InputItem::ControllerButton( + ui::config::InputKeyButton { + id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_START), + }, + )), + ]; + inputs[Z_TRIG] = [ + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_Z), + })), + Some(ui::config::InputItem::ControllerAxis( + ui::config::InputControllerAxis { + id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFT_TRIGGER), + axis: 1, + initial_state: 0, + }, + )), + ]; + inputs[B_BUTTON] = [ + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_LCTRL), + })), + Some(ui::config::InputItem::ControllerButton( + ui::config::InputKeyButton { + id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_WEST), + }, + )), + ]; + inputs[A_BUTTON] = [ + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_LSHIFT), + })), + Some(ui::config::InputItem::ControllerButton( + ui::config::InputKeyButton { + id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_SOUTH), + }, + )), + ]; + inputs[R_CBUTTON] = [ + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_L), + })), + Some(ui::config::InputItem::ControllerAxis( + ui::config::InputControllerAxis { + id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTX), + axis: 1, + initial_state: 0, + }, + )), + ]; + inputs[L_CBUTTON] = [ + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_J), + })), + Some(ui::config::InputItem::ControllerAxis( + ui::config::InputControllerAxis { + id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTX), + axis: -1, + initial_state: 0, + }, + )), + ]; + inputs[D_CBUTTON] = [ + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_K), + })), + Some(ui::config::InputItem::ControllerAxis( + ui::config::InputControllerAxis { + id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTY), + axis: 1, + initial_state: 0, + }, + )), + ]; + inputs[U_CBUTTON] = [ + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_I), + })), + Some(ui::config::InputItem::ControllerAxis( + ui::config::InputControllerAxis { + id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_RIGHTY), + axis: -1, + initial_state: 0, + }, + )), + ]; + inputs[R_TRIG] = [ + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_C), + })), + Some(ui::config::InputItem::ControllerButton( + ui::config::InputKeyButton { + id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER), + }, + )), + ]; + inputs[L_TRIG] = [ + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_X), + })), + Some(ui::config::InputItem::ControllerButton( + ui::config::InputKeyButton { + id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_LEFT_SHOULDER), + }, + )), + ]; + inputs[AXIS_LEFT] = [ + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_LEFT), + })), + Some(ui::config::InputItem::ControllerAxis( + ui::config::InputControllerAxis { + id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTX), + axis: -1, + initial_state: 0, + }, + )), + ]; + inputs[AXIS_RIGHT] = [ + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_RIGHT), + })), + Some(ui::config::InputItem::ControllerAxis( + ui::config::InputControllerAxis { + id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTX), + axis: 1, + initial_state: 0, + }, + )), + ]; + inputs[AXIS_UP] = [ + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_UP), + })), + Some(ui::config::InputItem::ControllerAxis( + ui::config::InputControllerAxis { + id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTY), + axis: -1, + initial_state: 0, + }, + )), + ]; + inputs[AXIS_DOWN] = [ + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_DOWN), + })), + Some(ui::config::InputItem::ControllerAxis( + ui::config::InputControllerAxis { + id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_AXIS_LEFTY), + axis: 1, + initial_state: 0, + }, + )), + ]; + inputs[HOTKEY] = [ + Some(ui::config::InputItem::Key(ui::config::InputKeyButton { + id: i32::from(sdl3_sys::scancode::SDL_SCANCODE_COMMA), + })), + Some(ui::config::InputItem::ControllerButton( + ui::config::InputKeyButton { + id: i32::from(sdl3_sys::gamepad::SDL_GAMEPAD_BUTTON_BACK), + }, + )), + ]; + + ui::config::InputProfile { + inputs, + dinput: false, + deadzone: DEADZONE_DEFAULT, + } +} diff --git a/src/ui/video.rs b/src/ui/video.rs index 660ed50f..b567196c 100644 --- a/src/ui/video.rs +++ b/src/ui/video.rs @@ -274,37 +274,3 @@ pub fn onscreen_message(message: &str, milliseconds: MESSAGE_LENGTH) { rdp_onscreen_message(c_message.as_ptr(), milliseconds) }; } - -pub fn draw_text( - text: &str, - renderer: *mut sdl3_sys::render::SDL_Renderer, - image_texture: *mut sdl3_sys::render::SDL_Texture, - text_engine: *mut sdl3_ttf_sys::ttf::TTF_TextEngine, - font: *mut sdl3_ttf_sys::ttf::TTF_Font, -) { - unsafe { - let (mut w, mut h) = (0, 0); - sdl3_sys::render::SDL_GetRenderOutputSize(renderer, &mut w, &mut h); - - let c_text = std::ffi::CString::new(text).unwrap(); - let ttf_text = sdl3_ttf_sys::ttf::TTF_CreateText(text_engine, font, c_text.as_ptr(), 0); - - sdl3_sys::everything::SDL_RenderClear(renderer); - let rect = sdl3_sys::rect::SDL_FRect { - x: 0.0, - y: 0.0, - w: w as f32, - h: (h - sdl3_ttf_sys::ttf::TTF_GetFontHeight(font)) as f32, - }; - sdl3_sys::render::SDL_RenderTexture(renderer, image_texture, std::ptr::null(), &rect); - let (mut text_w, mut text_h) = (0, 0); - sdl3_ttf_sys::ttf::TTF_GetTextSize(ttf_text, &mut text_w, &mut text_h); - sdl3_ttf_sys::ttf::TTF_DrawRendererText( - ttf_text, - (w / 2 - text_w / 2) as f32, - (h - text_h) as f32, - ); - sdl3_sys::render::SDL_RenderPresent(renderer); - sdl3_ttf_sys::ttf::TTF_DestroyText(ttf_text); - } -}