re-arrange input profiles (#1150)

This commit is contained in:
Logan McNaughton
2026-07-05 12:43:23 +02:00
committed by GitHub
parent 3bc7c96191
commit 1bfb50de8a
11 changed files with 1121 additions and 888 deletions
Generated
-21
View File
@@ -2796,7 +2796,6 @@ dependencies = [
"reqwest", "reqwest",
"rfd", "rfd",
"rustc-hash 2.1.2", "rustc-hash 2.1.2",
"sdl3-image-sys",
"sdl3-src", "sdl3-src",
"sdl3-sys", "sdl3-sys",
"sdl3-ttf-sys", "sdl3-ttf-sys",
@@ -6441,26 +6440,6 @@ dependencies = [
"tiny-skia 0.11.4", "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]] [[package]]
name = "sdl3-src" name = "sdl3-src"
version = "3.4.10" version = "3.4.10"
-2
View File
@@ -45,14 +45,12 @@ dirs = "6.0"
open = { version = "5.3", optional = true } open = { version = "5.3", optional = true }
sdl3-sys = { version = "0.6", features = ["build-from-source-static"] } 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-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] [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 } slint = { version = "1.16", default-features = false, features = ["compat-1-2", "std", "backend-android-activity-06", "accessibility"], optional = true }
jni = "0.22" jni = "0.22"
sdl3-sys = { version = "0.6", features = ["build-from-source"] } 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-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] [build-dependencies]
winresource = "0.1" winresource = "0.1"
@@ -24,7 +24,6 @@ class N64Activity : SDLActivity() {
override fun getLibraries(): Array<String> = arrayOf( override fun getLibraries(): Array<String> = arrayOf(
"SDL3", "SDL3",
"SDL3_ttf", "SDL3_ttf",
"SDL3_image",
"gopher64" "gopher64"
) )
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

+2 -2
View File
@@ -284,11 +284,11 @@ pub fn run(args: Args, arg_count: usize) -> std::io::Result<()> {
let mut config = ui::config::Config::new(); let mut config = ui::config::Config::new();
if let Some(profile) = args.configure_input_profile { if let Some(profile) = args.configure_input_profile {
ui::input::configure_input_profile( ui::input_profile::configure_input_profile(
&mut config, &mut config,
profile, profile,
args.use_dinput, args.use_dinput,
args.deadzone.unwrap_or(ui::input::DEADZONE_DEFAULT), args.deadzone.unwrap_or(ui::input_profile::DEADZONE_DEFAULT),
); );
ui::sdl_close(); ui::sdl_close();
+1
View File
@@ -7,6 +7,7 @@ pub mod config;
#[cfg(feature = "gui")] #[cfg(feature = "gui")]
pub mod gui; pub mod gui;
pub mod input; pub mod input;
pub mod input_profile;
#[cfg(feature = "gui")] #[cfg(feature = "gui")]
pub mod netplay; pub mod netplay;
#[cfg(feature = "gui")] #[cfg(feature = "gui")]
+19 -14
View File
@@ -1,34 +1,36 @@
use crate::ui; use crate::ui;
#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)] #[derive(Clone, serde::Serialize, serde::Deserialize)]
pub struct InputKeyButton { pub struct InputKeyButton {
pub enabled: bool,
pub id: i32, pub id: i32,
} }
#[derive(PartialEq, Copy, Clone, serde::Serialize, serde::Deserialize)] #[derive(PartialEq, Clone, serde::Serialize, serde::Deserialize)]
pub struct InputControllerAxis { pub struct InputControllerAxis {
pub enabled: bool,
pub id: i32, pub id: i32,
pub axis: i16, pub axis: i16,
pub initial_state: i16, pub initial_state: i16,
} }
#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)] #[derive(Clone, serde::Serialize, serde::Deserialize)]
pub struct InputJoystickHat { pub struct InputJoystickHat {
pub enabled: bool,
pub id: i32, pub id: i32,
pub direction: u8, 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 struct InputProfile {
pub keys: [InputKeyButton; ui::input::PROFILE_SIZE], pub inputs: [[Option<InputItem>; 2]; ui::input_profile::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 dinput: bool, pub dinput: bool,
pub deadzone: i32, pub deadzone: i32,
} }
@@ -160,7 +162,10 @@ impl Config {
} }
let mut input_profiles = std::collections::BTreeMap::new(); 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 { Config {
input: input_data.unwrap_or(Input { input: input_data.unwrap_or(Input {
input_profile_binding: [ input_profile_binding: [
+1 -1
View File
@@ -347,7 +347,7 @@ fn controller_window(app: &AppWindow, config: &ui::config::Config) {
app.on_input_profile_button_clicked(move || { app.on_input_profile_button_clicked(move || {
weak_app weak_app
.upgrade_in_event_loop(move |handle| { .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_profile_name(String::new().into());
handle.set_input_dinput(false); handle.set_input_dinput(false);
handle.set_show_input_profile(true); handle.set_show_input_profile(true);
+159 -803
View File
File diff suppressed because it is too large Load Diff
+929
View File
@@ -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<std::time::Instant>) {
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<std::time::Instant>) {
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<std::time::Instant>,
) -> 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<ui::config::InputItem>) -> 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<ui::config::InputItem>; 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,
}
}
-34
View File
@@ -274,37 +274,3 @@ pub fn onscreen_message(message: &str, milliseconds: MESSAGE_LENGTH) {
rdp_onscreen_message(c_message.as_ptr(), milliseconds) 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);
}
}