Joystick support fixes.

I did this because I wanted to fly around maps using a Spaceball
4000FLX, or any other 6DOF controller.  These fixes help it work.

Various fixes to joystick support:
  - joyaxiscallback() used strtol() to check to see if the supplied
    string was an integer, then didn't assign the parsed integer to
    the cvar.
  - Wrong multiplier for left/turnleft values.
  - Delete `axismap[]` from J_JoystickAxis().  It was causing problems,
    and smells like it was trying to do what the cvars
    joyadvaxis[xyzruv] are doing now.
  - Fix compiler error by adding case statements for:
	SDL_SENSOR_ACCEL_L
	SDL_SENSOR_ACCEL_R
	SDL_SENSOR_GYRO_L
	SDL_SENSOR_GYRO_R

New cvar: "joyonly".

"Joystick" axes are typically return-to-center affairs; their deflection
values are therefore reported as -MAX - MAX, with zero in the center.
"Game controllers" are similar, but also often have analog left and
right "triggers" which are reported as 0 - MAX, with zero at one end
(fully released to fully depressed).

Unfortunately, SDL will try its darndest to make a joystick look like a
"game controller."  It does this by reinterpreting certain of the axes
to report the range 0 - MAX, as if they were triggers.  This is not a
thing to do with 6DOF controllers, where all axes are return-to-center.
While it may be remotely possible to put together an SDL2 controller
mapping that reports -MAX - MAX on all axes, for me it was simpler to
hack on FTEQW.

Coupled with that is FTEQW's giving preference to "game controllers,"
i.e. if SDL_IsGameController() returns true, FTEQW will treat it as one.

"joyonly" is a boolean cvar.  If true, FTE will ignore "game
controllers" and treat everything as a joystick.  The default is false.
"joyonly" must be set at startup to be effective, when the controllers
are being enumerated by SDL.
This commit is contained in:
Leo L. Schwab 2023-01-19 20:05:25 -08:00
parent ebbc6c0930
commit 1fda671b9a
2 changed files with 34 additions and 8 deletions

View file

@ -31,9 +31,15 @@ void QDECL joyaxiscallback(cvar_t *var, char *oldvalue)
{ {
int sign; int sign;
char *end; char *end;
strtol(var->string, &end, 0); sign = strtol(var->string, &end, 0);
if (!*end) //okay, its missing or an actual number. if (!*end)
{
//okay, its missing or an actual number.
if (sign >= -6 && sign <= 6) {
var->ival = sign;
}
return; return;
}
end = var->string; end = var->string;
if (*end == '-') if (*end == '-')
@ -63,7 +69,7 @@ void QDECL joyaxiscallback(cvar_t *var, char *oldvalue)
else if (!Q_strcasecmp(end, "right") || !Q_strcasecmp(end, "turnright")) else if (!Q_strcasecmp(end, "right") || !Q_strcasecmp(end, "turnright"))
var->ival = 4*sign; var->ival = 4*sign;
else if (!Q_strcasecmp(end, "left") || !Q_strcasecmp(end, "turnleft")) else if (!Q_strcasecmp(end, "left") || !Q_strcasecmp(end, "turnleft"))
var->ival = 4*sign*1; var->ival = 4*sign*-1;
else if (!Q_strcasecmp(end, "up") || !Q_strcasecmp(end, "moveup")) else if (!Q_strcasecmp(end, "up") || !Q_strcasecmp(end, "moveup"))
var->ival = 5*sign; var->ival = 5*sign;
else if (!Q_strcasecmp(end, "down") || !Q_strcasecmp(end, "movedown")) else if (!Q_strcasecmp(end, "down") || !Q_strcasecmp(end, "movedown"))

View file

@ -95,6 +95,9 @@ static uint32_t SDL_GiveFinger(SDL_JoystickID jid, SDL_TouchID tid, SDL_FingerID
#if SDL_MAJOR_VERSION >= 2 #if SDL_MAJOR_VERSION >= 2
#define MAX_JOYSTICKS 16 #define MAX_JOYSTICKS 16
#ifndef MAXJOYAXIS
#define MAXJOYAXIS 6
#endif
static struct static struct
{ {
@ -154,6 +157,8 @@ static struct sdljoy_s
float buttonrepeat[countof(gpbuttonmap)]; float buttonrepeat[countof(gpbuttonmap)];
} sdljoy[MAX_JOYSTICKS]; } sdljoy[MAX_JOYSTICKS];
static cvar_t joy_only = CVARD("joyonly", "0", "If true, treats \"game controllers\" as regular joysticks.\nMust be set at startup.");
//the enumid is the value for the open function rather than the working id. //the enumid is the value for the open function rather than the working id.
static void J_AllocateDevID(struct sdljoy_s *joy) static void J_AllocateDevID(struct sdljoy_s *joy)
{ {
@ -184,6 +189,10 @@ static void J_ControllerAdded(int enumid)
{ {
const char *cname; const char *cname;
int i; int i;
if (joy_only.ival)
return;
for (i = 0; i < MAX_JOYSTICKS; i++) for (i = 0; i < MAX_JOYSTICKS; i++)
if (sdljoy[i].controller == NULL && sdljoy[i].joystick == NULL) if (sdljoy[i].controller == NULL && sdljoy[i].joystick == NULL)
break; break;
@ -213,7 +222,7 @@ static void J_JoystickAdded(int enumid)
if (i == MAX_JOYSTICKS) if (i == MAX_JOYSTICKS)
return; return;
if (SDL_IsGameController(enumid)) //if its reported via the gamecontroller api then use that instead. don't open it twice. if (!joy_only.ival && SDL_IsGameController(enumid)) //if its reported via the gamecontroller api then use that instead. don't open it twice.
return; return;
sdljoy[i].joystick = SDL_JoystickOpen(enumid); sdljoy[i].joystick = SDL_JoystickOpen(enumid);
@ -283,11 +292,17 @@ static void J_ControllerAxis(SDL_JoystickID jid, int axis, int value)
} }
static void J_JoystickAxis(SDL_JoystickID jid, int axis, int value) static void J_JoystickAxis(SDL_JoystickID jid, int axis, int value)
{ {
static const int axismap[] = {0,1,3,4,2,5};
struct sdljoy_s *joy = J_DevId(jid); struct sdljoy_s *joy = J_DevId(jid);
if (joy && !joy->controller && axis < sizeof(axismap)/sizeof(axismap[0]) && joy->qdevid != DEVID_UNSET)
IN_JoystickAxisEvent(joy->qdevid, axismap[axis], value / 32767.0); if (joy->qdevid == DEVID_UNSET)
{
if (abs(value) < 0x1000)
return;
J_AllocateDevID(joy);
}
if (joy && !joy->controller && axis < MAXJOYAXIS && joy->qdevid != DEVID_UNSET)
IN_JoystickAxisEvent(joy->qdevid, axis, value / 32767.0);
} }
//we don't do hats and balls and stuff. //we don't do hats and balls and stuff.
static void J_ControllerButton(SDL_JoystickID jid, int button, qboolean pressed) static void J_ControllerButton(SDL_JoystickID jid, int button, qboolean pressed)
@ -499,6 +514,10 @@ static void J_ControllerSensor(SDL_JoystickID jid, SDL_SensorType sensor, float
case SDL_SENSOR_GYRO: case SDL_SENSOR_GYRO:
IN_Gyroscope(joy->qdevid, data[0], data[1], data[2]); IN_Gyroscope(joy->qdevid, data[0], data[1], data[2]);
break; break;
case SDL_SENSOR_ACCEL_L:
case SDL_SENSOR_ACCEL_R:
case SDL_SENSOR_GYRO_L:
case SDL_SENSOR_GYRO_R:
case SDL_SENSOR_INVALID: case SDL_SENSOR_INVALID:
case SDL_SENSOR_UNKNOWN: case SDL_SENSOR_UNKNOWN:
safedefault: safedefault:
@ -1454,6 +1473,7 @@ void INS_Init (void)
#ifdef HAVE_SDL_TEXTINPUT #ifdef HAVE_SDL_TEXTINPUT
Cvar_Register(&sys_osk, "input controls"); Cvar_Register(&sys_osk, "input controls");
#endif #endif
Cvar_Register (&joy_only, "input controls");
#ifdef HAVE_SDL_TEXTINPUT #ifdef HAVE_SDL_TEXTINPUT
#ifdef __linux__ #ifdef __linux__