Added dead zone configuration (#857)

User can define dead zone by device identifier and a radius.
This commit is contained in:
Rikard Bengtsson
2023-12-01 10:51:05 +01:00
committed by GitHub
parent 470c0ab629
commit 542b6f4d73
4 changed files with 232 additions and 66 deletions

View File

@@ -175,6 +175,29 @@ jamma2_pid=0x2222
;no_merge_vidpid=0x12345678
;no_merge_vidpid=0x11112222
; Dead zone radius definitions.
; Joystick movements smaller than a defined radius will be neglected.
; This is good for worn or poorly made joysticks and converters.
; Devices that match the identifier part of the string will be affected.
; You can add multiple devices (one entry per identifier).
; The identifier part is case-insensitive, and the radius can be up to 64 units.
; Identifier and radius are separated by a whitespace (' ') and/or a comma (',').
; Accepted formats are:
;
; - VIDPID as an eight digit hex number ("0x" can be omitted), then the radius (not hex).
;deadzone=0x1E8F1603, 25
;
; - vid:VID as a four digit hex, then the radius.
;deadzone=vid:0x1e8f, 25
;
; - pid:PID as a four digit hex, then the radius.
;deadzone=PID:1603 25
;
; - The following formats are explained a bit further down:
;deadzone=usb-1.2/, 10
;deadzone=7c:10:c9:15:22:33/df:47:3a:12:44:55, 8
;deadzone=1e8f_1603_55c4dd0c, 5
; Permanently assign specific controller to specific player.
; Normally you don't need to use this option, but if you use arcade cabinet with integrated controllers then
; you may want to use it for specific player regardless which controller is used first.

45
cfg.cpp
View File

@@ -64,6 +64,7 @@ static const ini_var_t ini_vars[] =
{ "OSD_TIMEOUT", (void*)(&(cfg.osd_timeout)), INT16, 0, 3600 },
{ "DIRECT_VIDEO", (void*)(&(cfg.direct_video)), UINT8, 0, 1 },
{ "OSD_ROTATE", (void*)(&(cfg.osd_rotate)), UINT8, 0, 2 },
{ "DEADZONE", (void*)(&(cfg.controller_deadzone)), STRINGARR, sizeof(cfg.controller_deadzone) / sizeof(*cfg.controller_deadzone), sizeof(*cfg.controller_deadzone) },
{ "GAMEPAD_DEFAULTS", (void*)(&(cfg.gamepad_defaults)), UINT8, 0, 1 },
{ "RECENTS", (void*)(&(cfg.recents)), UINT8, 0, 1 },
{ "CONTROLLER_INFO", (void*)(&(cfg.controller_info)), UINT8, 0, 10 },
@@ -97,35 +98,35 @@ static const ini_var_t ini_vars[] =
{ "BT_AUTO_DISCONNECT", (void*)(&(cfg.bt_auto_disconnect)), UINT32, 0, 180 },
{ "BT_RESET_BEFORE_PAIR", (void*)(&(cfg.bt_reset_before_pair)), UINT8, 0, 1 },
{ "WAITMOUNT", (void*)(&(cfg.waitmount)), STRING, 0, sizeof(cfg.waitmount) - 1 },
{ "RUMBLE", (void *)(&(cfg.rumble)), UINT8, 0, 1},
{ "RUMBLE", (void *)(&(cfg.rumble)), UINT8, 0, 1 },
{ "WHEEL_FORCE", (void*)(&(cfg.wheel_force)), UINT8, 0, 100 },
{ "WHEEL_RANGE", (void*)(&(cfg.wheel_range)), UINT16, 0, 1000 },
{ "HDMI_GAME_MODE", (void *)(&(cfg.hdmi_game_mode)), UINT8, 0, 1},
{ "VRR_MODE", (void *)(&(cfg.vrr_mode)), UINT8, 0, 3},
{ "VRR_MIN_FRAMERATE", (void *)(&(cfg.vrr_min_framerate)), UINT8, 0, 255},
{ "VRR_MAX_FRAMERATE", (void *)(&(cfg.vrr_max_framerate)), UINT8, 0, 255},
{ "VRR_VESA_FRAMERATE", (void *)(&(cfg.vrr_vesa_framerate)), UINT8, 0, 255},
{ "HDMI_GAME_MODE", (void *)(&(cfg.hdmi_game_mode)), UINT8, 0, 1 },
{ "VRR_MODE", (void *)(&(cfg.vrr_mode)), UINT8, 0, 3 },
{ "VRR_MIN_FRAMERATE", (void *)(&(cfg.vrr_min_framerate)), UINT8, 0, 255 },
{ "VRR_MAX_FRAMERATE", (void *)(&(cfg.vrr_max_framerate)), UINT8, 0, 255 },
{ "VRR_VESA_FRAMERATE", (void *)(&(cfg.vrr_vesa_framerate)), UINT8, 0, 255 },
{ "VIDEO_OFF", (void*)(&(cfg.video_off)), INT16, 0, 3600 },
{ "PLAYER_1_CONTROLLER", (void*)(&(cfg.player_controller[0])), STRINGARR, sizeof(cfg.player_controller[0]) / sizeof(cfg.player_controller[0][0]), sizeof(cfg.player_controller[0][0])},
{ "PLAYER_2_CONTROLLER", (void*)(&(cfg.player_controller[1])), STRINGARR, sizeof(cfg.player_controller[0]) / sizeof(cfg.player_controller[0][0]), sizeof(cfg.player_controller[0][0])},
{ "PLAYER_3_CONTROLLER", (void*)(&(cfg.player_controller[2])), STRINGARR, sizeof(cfg.player_controller[0]) / sizeof(cfg.player_controller[0][0]), sizeof(cfg.player_controller[0][0])},
{ "PLAYER_4_CONTROLLER", (void*)(&(cfg.player_controller[3])), STRINGARR, sizeof(cfg.player_controller[0]) / sizeof(cfg.player_controller[0][0]), sizeof(cfg.player_controller[0][0])},
{ "PLAYER_5_CONTROLLER", (void*)(&(cfg.player_controller[4])), STRINGARR, sizeof(cfg.player_controller[0]) / sizeof(cfg.player_controller[0][0]), sizeof(cfg.player_controller[0][0])},
{ "PLAYER_6_CONTROLLER", (void*)(&(cfg.player_controller[5])), STRINGARR, sizeof(cfg.player_controller[0]) / sizeof(cfg.player_controller[0][0]), sizeof(cfg.player_controller[0][0])},
{ "DISABLE_AUTOFIRE", (void *)(&(cfg.disable_autofire)), UINT8, 0, 1},
{ "VIDEO_BRIGHTNESS", (void *)(&(cfg.video_brightness)), UINT8, 0, 100},
{ "VIDEO_CONTRAST", (void *)(&(cfg.video_contrast)), UINT8, 0, 100},
{ "VIDEO_SATURATION", (void *)(&(cfg.video_saturation)), UINT8, 0, 100},
{ "VIDEO_HUE", (void *)(&(cfg.video_hue)), UINT16, 0, 360},
{ "VIDEO_GAIN_OFFSET", (void *)(&(cfg.video_gain_offset)), STRING, 0, sizeof(cfg.video_gain_offset)},
{ "PLAYER_1_CONTROLLER", (void*)(&(cfg.player_controller[0])), STRINGARR, sizeof(cfg.player_controller[0]) / sizeof(cfg.player_controller[0][0]), sizeof(cfg.player_controller[0][0]) },
{ "PLAYER_2_CONTROLLER", (void*)(&(cfg.player_controller[1])), STRINGARR, sizeof(cfg.player_controller[0]) / sizeof(cfg.player_controller[0][0]), sizeof(cfg.player_controller[0][0]) },
{ "PLAYER_3_CONTROLLER", (void*)(&(cfg.player_controller[2])), STRINGARR, sizeof(cfg.player_controller[0]) / sizeof(cfg.player_controller[0][0]), sizeof(cfg.player_controller[0][0]) },
{ "PLAYER_4_CONTROLLER", (void*)(&(cfg.player_controller[3])), STRINGARR, sizeof(cfg.player_controller[0]) / sizeof(cfg.player_controller[0][0]), sizeof(cfg.player_controller[0][0]) },
{ "PLAYER_5_CONTROLLER", (void*)(&(cfg.player_controller[4])), STRINGARR, sizeof(cfg.player_controller[0]) / sizeof(cfg.player_controller[0][0]), sizeof(cfg.player_controller[0][0]) },
{ "PLAYER_6_CONTROLLER", (void*)(&(cfg.player_controller[5])), STRINGARR, sizeof(cfg.player_controller[0]) / sizeof(cfg.player_controller[0][0]), sizeof(cfg.player_controller[0][0]) },
{ "DISABLE_AUTOFIRE", (void *)(&(cfg.disable_autofire)), UINT8, 0, 1 },
{ "VIDEO_BRIGHTNESS", (void *)(&(cfg.video_brightness)), UINT8, 0, 100 },
{ "VIDEO_CONTRAST", (void *)(&(cfg.video_contrast)), UINT8, 0, 100 },
{ "VIDEO_SATURATION", (void *)(&(cfg.video_saturation)), UINT8, 0, 100 },
{ "VIDEO_HUE", (void *)(&(cfg.video_hue)), UINT16, 0, 360 },
{ "VIDEO_GAIN_OFFSET", (void *)(&(cfg.video_gain_offset)), STRING, 0, sizeof(cfg.video_gain_offset) },
{ "HDR", (void*)(&cfg.hdr), UINT8, 0, 2 },
{ "HDR_MAX_NITS", (void*)(&(cfg.hdr_max_nits)), UINT16, 100, 10000},
{ "HDR_AVG_NITS", (void*)(&(cfg.hdr_avg_nits)), UINT16, 100, 10000},
{ "HDR_MAX_NITS", (void*)(&(cfg.hdr_max_nits)), UINT16, 100, 10000 },
{ "HDR_AVG_NITS", (void*)(&(cfg.hdr_avg_nits)), UINT16, 100, 10000 },
{ "VGA_MODE", (void*)(&(cfg.vga_mode)), STRING, 0, sizeof(cfg.vga_mode) - 1 },
{ "NTSC_MODE", (void *)(&(cfg.ntsc_mode)), UINT8, 0, 2},
{ "NTSC_MODE", (void *)(&(cfg.ntsc_mode)), UINT8, 0, 2 },
{ "CONTROLLER_UNIQUE_MAPPING", (void *)(cfg.controller_unique_mapping), UINT32ARR, 0, 0xFFFFFFFF },
{ "OSD_LOCK", (void*)(&(cfg.osd_lock)), STRING, 0, sizeof(cfg.osd_lock) - 1 },
{ "OSD_LOCK_TIME", (void*)(&(cfg.osd_lock_time)), UINT16, 0, 60},
{ "OSD_LOCK_TIME", (void*)(&(cfg.osd_lock_time)), UINT16, 0, 60 },
};
static const int nvars = (int)(sizeof(ini_vars) / sizeof(ini_var_t));

1
cfg.h
View File

@@ -72,6 +72,7 @@ typedef struct {
char shmask_default[1023];
char preset_default[1023];
char player_controller[6][8][256];
char controller_deadzone[8][256];
uint8_t rumble;
uint8_t wheel_force;
uint16_t wheel_range;

229
input.cpp
View File

@@ -37,7 +37,6 @@
#define NUMPLAYERS 6
#define UINPUT_NAME "MiSTer virtual input"
char joy_bnames[NUMBUTTONS][32] = {};
int joy_bcount = 0;
static struct pollfd pool[NUMDEV + 3];
@@ -1199,9 +1198,12 @@ typedef struct
char id[80];
char name[128];
char sysfs[512];
int ss_range[2];
int max_cardinal[2];
float max_range[2];
uint32_t deadzone;
} devInput;
static devInput input[NUMDEV] = {};
@@ -1778,6 +1780,65 @@ static void mouse_btn_req()
if (grabbed) mouse_req |= 2;
}
static inline void joy_clamp(int* value, const int min, const int max)
{
if (*value < min) {
*value = min;
}
else if (*value > max) {
*value = max;
}
}
static inline float boxradf(const float angle)
{
return 1.0f / fmaxf(fabsf(sinf(angle)), fabsf(cosf(angle)));
}
static void joy_apply_deadzone(int* x, int* y, const devInput* dev, const int stick) {
// Don't be fancy with such a small deadzone.
if (dev->deadzone <= 2)
{
if (dev->deadzone && (abs((*x > *y) == (*x > -*y) ? *x : *y) <= dev->deadzone))
*x = *y = 0;
return;
}
const float radius = hypotf(*x, *y);
if (radius <= (float)dev->deadzone)
{
*x = *y = 0;
return;
}
const float angle = atan2f(*y, *x);
const float box_radius = boxradf(angle);
/* A measure of how "cardinal" the angle is,
i.e closeness to [0, 90, 180, 270] degrees (0.0 - 1.0). */
const float cardinality = (1.4142136f - box_radius) * 2.4142136f;
// Expected range for the given angle.
const float max_cardinal = dev->max_cardinal[stick] > (2.0f * dev->deadzone) ? dev->max_cardinal[stick] : 127.0f;
const float max_diagonal = dev->max_range[stick] > (2.0f * dev->deadzone) ? dev->max_range[stick] : 127.0f;
const float range = cardinality * max_cardinal + (1.0f - cardinality) * max_diagonal;
const float weight = 1.0f - fmaxf(range - radius, .0f) / (range - dev->deadzone);
const float adjusted_radius = fminf(weight * range, max_cardinal * box_radius);
/* Don't ever return a larger magnitude than that was given.
The whole point of this function is to subtract some magnitude, not add. */
if (adjusted_radius > radius) return;
*x = nearbyintf(adjusted_radius * cosf(angle));
*y = nearbyintf(adjusted_radius * sinf(angle));
// Just to be sure.
const int min_range = is_psx() ? -128 : -127;
joy_clamp(x, min_range, INT8_MAX);
joy_clamp(y, min_range, INT8_MAX);
}
static uint32_t osdbtn = 0;
static void joy_digital(int jnum, uint32_t mask, uint32_t code, char press, int bnum, int dont_save = 0)
{
@@ -2078,42 +2139,62 @@ static void joy_digital(int jnum, uint32_t mask, uint32_t code, char press, int
}
}
static bool joy_dir_is_diagonal(const int x, const int y)
{
static const float JOY_DIAG_THRESHOLD = .85f;
return
((x == 0) || (y == 0)) ? false :
((x == y) || (x == -y)) ? true :
abs((x > y) == (x > -y) ? (float)y / x : (float)x / y) >= JOY_DIAG_THRESHOLD;
}
static void joy_analog(int dev, int axis, int offset, int stick = 0)
{
int num = input[dev].num;
static int pos[2][NUMPLAYERS][2] = {};
if (grabbed && num > 0 && num < NUMPLAYERS+1)
if (grabbed && num > 0 && --num < NUMPLAYERS)
{
num--;
pos[stick][num][axis] = offset;
int x = pos[stick][num][0];
int y = pos[stick][num][1];
if (is_n64())
int x = pos[stick][num][0], y = pos[stick][num][1];
if (joy_dir_is_diagonal(x, y))
{
// Update maximum observed cardinal distance
const int abs_x = abs(x);
const int abs_y = abs(y);
if (abs_x > input[dev].max_cardinal[stick]) input[dev].max_cardinal[stick] = abs_x;
if (abs_y > input[dev].max_cardinal[stick]) input[dev].max_cardinal[stick] = abs_y;
// Update maximum observed diag
// Use sum of squares and only calc sqrt() when necessary
const int ss_range_curr = x*x + y*y;
// compare to max ss_range and update if larger
if ((ss_range_curr > input[dev].ss_range[stick]) & (abs(abs_x - abs_y) <= 3))
const int ss_range_curr = x * x + y * y;
if ((ss_range_curr > input[dev].ss_range[stick]))
{
input[dev].ss_range[stick] = ss_range_curr;
input[dev].max_range[stick] = sqrt(ss_range_curr);
input[dev].max_range[stick] = sqrtf(ss_range_curr);
}
// emulate n64 joystick range and shape for regular -127-+127 controllers
n64_joy_emu(x, y, &x, &y, input[dev].max_cardinal[stick], input[dev].max_range[stick]);
stick_swap(num,stick,&num,&stick);
}
if(stick) user_io_r_analog_joystick(num, (char)x, (char)y);
else user_io_l_analog_joystick(num, (char)x, (char)y);
// Update maximum observed cardinal distance
const int c_dist = abs((x > y) == (x > -y) ? x : y);
if (c_dist > input[dev].max_cardinal[stick])
{
input[dev].max_cardinal[stick] = c_dist;
}
joy_apply_deadzone(&x, &y, &input[dev], stick);
if (is_n64())
{
// Emulate N64 joystick range and shape for regular -127-+127 controllers
n64_joy_emu(x, y, &x, &y, input[dev].max_cardinal[stick], input[dev].max_range[stick]);
stick_swap(num, stick, &num, &stick);
}
if (stick)
{
user_io_r_analog_joystick(num, (char)x, (char)y);
}
else
{
user_io_l_analog_joystick(num, (char)x, (char)y);
}
}
}
@@ -2573,6 +2654,81 @@ static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int
map_joystick_show(input[dev].map, input[dev].mmap, input[dev].num);
}
}
// Analog joystick dead zone
{
// Lightgun/wheel has no dead zone
if (ev->type != EV_ABS || (ev->code <= 1 && (input[dev].lightgun || input[dev].quirk == QUIRK_WHEEL)))
{
input[dev].deadzone = 0U;
}
// Dual Shock 3/4
else if (input[dev].quirk == QUIRK_DS3 || input[dev].quirk == QUIRK_DS4)
{
input[dev].deadzone = 10U;
}
// Default dead zone
else
{
input[dev].deadzone = 2U;
}
char cfg_format[32];
char cfg_uid[sizeof(*cfg.controller_deadzone)];
snprintf(cfg_format, sizeof(cfg_format), "%%%u[^ \t,]%%*[ \t,]%%u%%n", (size_t)(sizeof(cfg_uid) - 1));
const char* dev_uid = get_unique_mapping(dev, 1);
for (size_t i = 0; i < sizeof(cfg.controller_deadzone) / sizeof(*cfg.controller_deadzone); i++)
{
const char* cfg_line = cfg.controller_deadzone[i];
if (!cfg_line || !strlen(cfg_line)) break;
uint32_t cfg_vidpid, cfg_deadzone;
size_t scan_pos;
char vp;
if ((sscanf(cfg_line, cfg_format, cfg_uid, &cfg_deadzone, &scan_pos) < 2) ||
(scan_pos != strlen(cfg_line))) continue;
if ((
sscanf(cfg_uid, "0%*[Xx]%08x%n", &cfg_vidpid, &scan_pos) ||
sscanf(cfg_uid, "%08x%n", &cfg_vidpid, &scan_pos)) &&
(scan_pos == strlen(cfg_uid)))
{
const uint32_t vidpid = (input[dev].vid << 16) | input[dev].pid;
if (vidpid != cfg_vidpid) continue;
}
else if ((
(sscanf(cfg_uid, "%[VvPp]%*[Ii]%*[Dd]:0%*[Xx]%04x%n", &vp, &cfg_vidpid, &scan_pos) == 2) ||
(sscanf(cfg_uid, "%[VvPp]%*[Ii]%*[Dd]:%04x%n", &vp, &cfg_vidpid, &scan_pos) == 2)) &&
(scan_pos == strlen(cfg_uid)))
{
if (vp == 'V' || vp == 'v')
{
if (input[dev].vid != cfg_vidpid) continue;
}
else
{
if (input[dev].pid != cfg_vidpid) continue;
}
}
else if (
!strcasestr(input[dev].id, cfg_uid) &&
!strcasestr(input[dev].sysfs, cfg_uid) &&
!strcasestr(dev_uid, cfg_uid))
{
continue;
}
if (cfg_deadzone > 64) cfg_deadzone = 64;
printf("Analog device %s was given a dead zone of %u\n", input[dev].id, cfg_deadzone);
input[dev].deadzone = cfg_deadzone;
break;
}
}
}
int old_combo = input[dev].osd_combo;
@@ -3294,29 +3450,14 @@ static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int
}
int hrange = (absinfo->maximum - absinfo->minimum) / 2;
int dead = hrange/63;
if (input[sub_dev].quirk == QUIRK_DS3 || input[sub_dev].quirk == QUIRK_DS4)
{
dead = 10;
}
// normalize to -range/2...+range/2
value = value - (absinfo->minimum + absinfo->maximum) / 2;
if (ev->code > 1 || (!input[dev].lightgun && input[dev].quirk != QUIRK_WHEEL)) //lightgun/wheel has no dead zone
{
// check the dead-zone and remove it from the range
hrange -= dead;
if (value < -dead) value += dead;
else if (value > dead) value -= dead;
else value = 0;
}
value -= (absinfo->minimum + absinfo->maximum) / 2;
int range = is_psx() ? 128 : 127;
value = (value * range) / hrange;
//final check to eliminate additive error
// final check to eliminate additive error
if (value < -range) value = -range;
else if (value > 127) value = 127;
@@ -3326,14 +3467,14 @@ static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int
if (ev->code == (input[dev].mmap[SYS_AXIS_MX] & 0xFFFF) && mouse_emu)
{
mouse_emu_x = 0;
if (value < -1 || value>1) mouse_emu_x = value;
if (value < -1 || value > 1) mouse_emu_x = value;
mouse_emu_x /= 12;
return;
}
else if (ev->code == (input[dev].mmap[SYS_AXIS_MY] & 0xFFFF) && mouse_emu)
{
mouse_emu_y = 0;
if (value < -1 || value>1) mouse_emu_y = value;
if (value < -1 || value > 1) mouse_emu_y = value;
mouse_emu_y /= 12;
return;
}
@@ -3389,7 +3530,7 @@ static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int
}
else
{
int offset = (value < -1 || value>1) ? value : 0;
int offset = (value < -1 || value > 1) ? value : 0;
if (input[dev].stick_l[0] && ev->code == (uint16_t)input[dev].mmap[input[dev].stick_l[0]])
{
joy_analog(dev, 0, offset, 0);
@@ -5300,7 +5441,7 @@ int input_test(int getchar)
if (!noabs) input_cb(&ev, &absinfo, i);
//sumulate digital directions from analog
// simulate digital directions from analog
if (ev.type == EV_ABS && !(mapping && mapping_type <= 1 && mapping_button < -4) && !(ev.code <= 1 && input[dev].lightgun) && input[dev].quirk != QUIRK_PDSP && input[dev].quirk != QUIRK_MSSP)
{
input_absinfo *pai = 0;