From 2581970811ec70ada4a8d8c3c15b89d2bac77880 Mon Sep 17 00:00:00 2001 From: mitchellcairns Date: Sun, 25 Jan 2026 23:21:07 -0800 Subject: [PATCH 1/2] Radial scaling joystick deadzone handling in input_state.cpp The joystick deadzone as applied currently locks the axis to the X or Y axis rather than only applying the deadzone according to a magnitude from center. This results in games being rather hard to control how we'd remember them performing on original hardware unless we disable deadzones. This will allow more gamepads to use a well-adjusted deadzone without impacting precise movement. --- recompinput/src/input_state.cpp | 40 ++++++++++----------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/recompinput/src/input_state.cpp b/recompinput/src/input_state.cpp index cbb0a1b..45a83ea 100644 --- a/recompinput/src/input_state.cpp +++ b/recompinput/src/input_state.cpp @@ -310,38 +310,22 @@ void recompinput::get_mouse_deltas(float* x, float* y) { } void recompinput::apply_joystick_deadzone(float x_in, float y_in, float* x_out, float* y_out) { - float joystick_deadzone = (float)recompui::config::general::get_joystick_deadzone() / 100.0f; + float deadzone = (float)recompui::config::general::get_joystick_deadzone() / 100.0f; + + float magnitude = sqrtf(x_in * x_in + y_in * y_in); - if (fabsf(x_in) < joystick_deadzone) { - x_in = 0.0f; - } - else { - if (x_in > 0.0f) { - x_in -= joystick_deadzone; - } - else { - x_in += joystick_deadzone; - } - - x_in /= (1.0f - joystick_deadzone); - } - - if (fabsf(y_in) < joystick_deadzone) { - y_in = 0.0f; + if (magnitude <= deadzone || magnitude <= 0.0f) { + *x_out = 0.0f; + *y_out = 0.0f; + return; } - else { - if (y_in > 0.0f) { - y_in -= joystick_deadzone; - } - else { - y_in += joystick_deadzone; - } - y_in /= (1.0f - joystick_deadzone); - } + float scaled_magnitude = (magnitude - deadzone) / (1.0f - deadzone); + if (scaled_magnitude > 1.0f) scaled_magnitude = 1.0f; - *x_out = x_in; - *y_out = y_in; + float ratio = scaled_magnitude / magnitude; + *x_out = x_in * ratio; + *y_out = y_in * ratio; } void recompinput::get_right_analog(int controller_num, float* x, float* y) { From 60b6140be9fdee40fce91ffacc502caaa92cb3c6 Mon Sep 17 00:00:00 2001 From: Mitch Cairns Date: Mon, 26 Jan 2026 10:40:09 -0800 Subject: [PATCH 2/2] Deadzone/Input Range Adjustment Options - N64 typically only uses about 66% of the real input range (-85 to +85 rather than the full -127 to +127) - Games often feel too sensitive as a result - This change implements a separate option for joystick input range to allow customization of this feature (Some N64 controllers are as low as (-65 to +65) - This update also splits out the deadzone for left/right joystick respectively --- recompinput/include/recompinput/input_state.h | 2 +- recompinput/src/input_state.cpp | 14 ++--- recompinput/src/profiles.cpp | 6 ++- recompui/include/recompui/config.h | 10 +++- recompui/src/config/ui_config_tab_general.cpp | 51 +++++++++++++++++-- 5 files changed, 68 insertions(+), 15 deletions(-) diff --git a/recompinput/include/recompinput/input_state.h b/recompinput/include/recompinput/input_state.h index 1961e29..7ea50df 100644 --- a/recompinput/include/recompinput/input_state.h +++ b/recompinput/include/recompinput/input_state.h @@ -38,7 +38,7 @@ namespace recompinput { void get_gyro_deltas(int controller_num, float* x, float* y); void get_mouse_deltas(float* x, float* y); void get_right_analog(int controller_num, float* x, float* y); - void apply_joystick_deadzone(float x_in, float y_in, float* x_out, float* y_out); + void apply_joystick_deadzone_range(float x_in, float y_in, float* x_out, float* y_out, double deadzone, double range); void set_right_analog_suppressed(bool suppressed); bool game_input_disabled(); bool all_input_disabled(); diff --git a/recompinput/src/input_state.cpp b/recompinput/src/input_state.cpp index 45a83ea..7f32643 100644 --- a/recompinput/src/input_state.cpp +++ b/recompinput/src/input_state.cpp @@ -309,9 +309,7 @@ void recompinput::get_mouse_deltas(float* x, float* y) { *y = cur_mouse_delta[1] * sensitivity; } -void recompinput::apply_joystick_deadzone(float x_in, float y_in, float* x_out, float* y_out) { - float deadzone = (float)recompui::config::general::get_joystick_deadzone() / 100.0f; - +void recompinput::apply_joystick_deadzone_range(float x_in, float y_in, float* x_out, float* y_out, double deadzone, double range) { float magnitude = sqrtf(x_in * x_in + y_in * y_in); if (magnitude <= deadzone || magnitude <= 0.0f) { @@ -324,18 +322,22 @@ void recompinput::apply_joystick_deadzone(float x_in, float y_in, float* x_out, if (scaled_magnitude > 1.0f) scaled_magnitude = 1.0f; float ratio = scaled_magnitude / magnitude; - *x_out = x_in * ratio; - *y_out = y_in * ratio; + *x_out = x_in * ratio * range; + *y_out = y_in * ratio * range; } void recompinput::get_right_analog(int controller_num, float* x, float* y) { + double deadzone = (float)recompui::config::general::get_joystick_deadzone_r() / 100.0f; + double range = (float)recompui::config::general::get_joystick_range_r() / 100.0f; + float x_val = controller_axis_state(controller_num, (SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX + 1), false) - controller_axis_state(controller_num, -(SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX + 1), false); float y_val = controller_axis_state(controller_num, (SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY + 1), false) - controller_axis_state(controller_num, -(SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY + 1), false); - apply_joystick_deadzone(x_val, y_val, x, y); + + apply_joystick_deadzone_range(x_val, y_val, x, y, deadzone, range); } void recompinput::set_right_analog_suppressed(bool suppressed) { diff --git a/recompinput/src/profiles.cpp b/recompinput/src/profiles.cpp index c89956e..584b309 100644 --- a/recompinput/src/profiles.cpp +++ b/recompinput/src/profiles.cpp @@ -3,6 +3,7 @@ #include #include #include "recompinput/profiles.h" +#include "recompui/config.h" #include "./json.h" #include "xxHash/xxh3.h" @@ -403,7 +404,10 @@ namespace recompinput { check_buttons(profile_index_kb); check_joystick(profile_index_cont); - recompinput::apply_joystick_deadzone(cur_x, cur_y, &cur_x, &cur_y); + + double deadzone = (float)recompui::config::general::get_joystick_deadzone_l() / 100.0f; + double range = (float)recompui::config::general::get_joystick_range_l() / 100.0f; + recompinput::apply_joystick_deadzone_range(cur_x, cur_y, &cur_x, &cur_y, deadzone, range); check_joystick(profile_index_kb); } diff --git a/recompui/include/recompui/config.h b/recompui/include/recompui/config.h index 38b84f5..bbe39aa 100644 --- a/recompui/include/recompui/config.h +++ b/recompui/include/recompui/config.h @@ -16,7 +16,10 @@ namespace recompui { inline const std::string rumble_strength = "rumble_strength"; inline const std::string gyro_sensitivity = "gyro_sensitivity"; inline const std::string mouse_sensitivity = "mouse_sensitivity"; - inline const std::string joystick_deadzone = "joystick_deadzone"; + inline const std::string joystick_deadzone_l = "joystick_deadzone_l"; + inline const std::string joystick_deadzone_r = "joystick_deadzone_r"; + inline const std::string joystick_range_l = "joystick_range_l"; + inline const std::string joystick_range_r = "joystick_range_r"; inline const std::string background_input_mode = "background_input_mode"; inline const std::string debug_mode = "debug_mode"; @@ -28,7 +31,10 @@ namespace recompui { double get_gyro_sensitivity(); bool has_mouse_sensitivity_option(); double get_mouse_sensitivity(); - double get_joystick_deadzone(); + double get_joystick_range_l(); + double get_joystick_range_r(); + double get_joystick_deadzone_l(); + double get_joystick_deadzone_r(); bool get_background_input_mode_enabled(); bool get_debug_mode_enabled(); } diff --git a/recompui/src/config/ui_config_tab_general.cpp b/recompui/src/config/ui_config_tab_general.cpp index 296ce0e..ab8357e 100644 --- a/recompui/src/config/ui_config_tab_general.cpp +++ b/recompui/src/config/ui_config_tab_general.cpp @@ -62,9 +62,21 @@ namespace config { double general::get_mouse_sensitivity() { return get_general_config_number_value(general::options::mouse_sensitivity); } + + double general::get_joystick_range_l() { + return get_general_config_number_value(general::options::joystick_range_l); + } + + double general::get_joystick_range_r() { + return get_general_config_number_value(general::options::joystick_range_r); + } - double general::get_joystick_deadzone() { - return get_general_config_number_value(general::options::joystick_deadzone); + double general::get_joystick_deadzone_l() { + return get_general_config_number_value(general::options::joystick_deadzone_l); + } + + double general::get_joystick_deadzone_r() { + return get_general_config_number_value(general::options::joystick_deadzone_r); } bool general::get_background_input_mode_enabled() { @@ -123,9 +135,38 @@ namespace config { } config.add_percent_number_option( - general::options::joystick_deadzone, - "Joystick Deadzone", - "Applies a deadzone to joystick inputs.", + general::options::joystick_range_l, + "Left Joystick Range", + "Controls the max output range percent for the left joystick." + "
" + "The default min/max range of an ideal N64 gamepad is -85 to +85" + "
" + "The default value is 66% (170 units / 255 units)", + 66.0 + ); + + config.add_percent_number_option( + general::options::joystick_range_r, + "Right Joystick Range", + "Controls the max output range percent for the right joystick." + "
" + "The default min/max range of an ideal N64 gamepad is -85 to +85" + "
" + "The default value is 66% (170 units / 255 units)", + 66.0 + ); + + config.add_percent_number_option( + general::options::joystick_deadzone_l, + "Left Joystick Deadzone", + "Applies a deadzone to the left joystick input.", + 5.0 + ); + + config.add_percent_number_option( + general::options::joystick_deadzone_r, + "Right Joystick Deadzone", + "Applies a deadzone to the right joystick input.", 5.0 );