Implemented sdl stubs and improved controller reconnection

This commit is contained in:
Marcus Minhorst 2025-07-07 22:02:53 -04:00 committed by Ricardo Luís Vaz Silva
commit d7bb21e2e3
3 changed files with 267 additions and 149 deletions

View file

@ -32,13 +32,13 @@
*/
#include <SDL.h>
#include <SDL_events.h>
#include "i_input.h"
#include "c_cvars.h"
#include "dobject.h"
#include "m_argv.h"
#include "m_joy.h"
#include "v_video.h"
#include "d_eventbase.h"
#include "d_gui.h"
#include "c_buttons.h"
#include "c_console.h"
@ -50,10 +50,6 @@
#include "engineerrors.h"
#include "i_interface.h"
static void I_CheckGUICapture ();
static void I_CheckNativeMouse ();
bool GUICapture;
static bool NativeMouse = true;
@ -529,12 +525,12 @@ void MessagePump (const SDL_Event &sev)
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
if (SDL_IsGameController(sev.jdevice.which))
if (SDL_GameControllerFromInstanceID(sev.jdevice.which))
break; // let SDL_CONTROLLERBUTTON* handle this
event.type = sev.type == SDL_JOYBUTTONDOWN ? EV_KeyDown : EV_KeyUp;
event.data1 = KEY_FIRSTJOYBUTTON + sev.jbutton.button;
if(event.data1 != 0)
D_PostEvent(&event);
I_JoyConsumeEvent(sev.jdevice.which, &event);
break;
case SDL_CONTROLLERBUTTONDOWN:
@ -566,14 +562,22 @@ void MessagePump (const SDL_Event &sev)
default: event.data1 = 0;
}
if(event.data1 != 0)
D_PostEvent(&event);
I_JoyConsumeEvent(sev.cbutton.which, &event);
break;
case SDL_JOYDEVICEADDED:
case SDL_JOYDEVICEREMOVED:
case SDL_CONTROLLERDEVICEADDED:
if (sev.type == SDL_JOYDEVICEADDED && SDL_IsGameController(sev.jdevice.which)) // DeviceIndex Here
break; // skip double event
I_UpdateDeviceList();
event.type = EV_DeviceChange;
D_PostEvent (&event);
break;
case SDL_JOYDEVICEREMOVED:
case SDL_CONTROLLERDEVICEREMOVED:
if ((sev.type == SDL_JOYDEVICEADDED || sev.type == SDL_JOYDEVICEREMOVED) && SDL_IsGameController(sev.jdevice.which))
case SDL_CONTROLLERDEVICEREMAPPED:
if (sev.type == SDL_JOYDEVICEREMOVED && SDL_GameControllerFromInstanceID(sev.jdevice.which))
break; // skip double event
I_UpdateDeviceList();
event.type = EV_DeviceChange;

View file

@ -0,0 +1,13 @@
#ifndef I_INPUT_H
#define I_INPUT_H
#include "d_eventbase.h"
extern int WaitingForKey;
static void I_CheckGUICapture ();
static void I_CheckNativeMouse ();
void I_JoyConsumeEvent(int instanceID, event_t * event);
#endif

View file

@ -32,28 +32,32 @@
*/
#include <SDL.h>
#include <SDL_gamecontroller.h>
#include <cstdlib>
#include "basics.h"
#include "cmdlib.h"
#include "d_eventbase.h"
#include "i_input.h"
#include "m_joy.h"
#include "keydef.h"
class SDLInputJoystick: public IJoystickConfig
{
public:
SDLInputJoystick(int DeviceIndex) :
DeviceIndex(DeviceIndex),
Multiplier(JOYSENSITIVITY_DEFAULT) ,
Enabled(true)
InstanceID(SDL_JoystickGetDeviceInstanceID(DeviceIndex)),
Multiplier(JOYSENSITIVITY_DEFAULT),
Enabled(true),
SettingsChanged(false)
{
if (SDL_IsGameController(DeviceIndex))
{
Mapping = SDL_GameControllerOpen(DeviceIndex);
Device = NULL;
DefaultAxes = DefaultControllerAxes;
DefaultAxesCount = sizeof(DefaultControllerAxes) / sizeof(EJoyAxis);
DefaultAxesCount = sizeof(DefaultControllerAxes) / sizeof(DefaultAxisConfig);
if(Mapping != NULL)
{
@ -66,8 +70,10 @@ public:
else
{
Device = SDL_JoystickOpen(DeviceIndex);
Mapping = NULL;
DefaultAxes = DefaultJoystickAxes;
DefaultAxesCount = sizeof(DefaultJoystickAxes) / sizeof(EJoyAxis);
DefaultAxesCount = sizeof(DefaultJoystickAxes) / sizeof(DefaultAxisConfig);
if(Device != NULL)
{
@ -77,13 +83,16 @@ public:
SetDefaultConfig();
}
}
M_LoadJoystickConfig(this);
}
~SDLInputJoystick()
{
if(Device != NULL || Mapping != NULL)
if(IsValid() && SettingsChanged)
M_SaveJoystickConfig(this);
SDL_GameControllerClose(Mapping);
SDL_JoystickClose(Device);
if (Mapping)
SDL_GameControllerClose(Mapping);
if (Device)
SDL_JoystickClose(Device);
}
bool IsValid() const
@ -103,6 +112,7 @@ public:
}
void SetSensitivity(float scale)
{
SettingsChanged = true;
Multiplier = scale;
}
@ -128,37 +138,55 @@ public:
}
float GetAxisDigitalThreshold(int axis)
{
return 0;
return Axes[axis].DigitalThreshold;
}
EJoyCurve GetAxisResponseCurve(int axis)
{
return JOYCURVE_DEFAULT;
return Axes[axis].ResponseCurvePreset;
}
float GetAxisResponseCurvePoint(int axis, int point)
{
return 0;
return unsigned(point) < 4
? Axes[axis].ResponseCurve.pts[point]
: 0;
};
void SetAxisDeadZone(int axis, float zone)
{
SettingsChanged = true;
Axes[axis].DeadZone = clamp(zone, 0.f, 1.f);
}
void SetAxisMap(int axis, EJoyAxis gameaxis)
{
SettingsChanged = true;
Axes[axis].GameAxis = gameaxis;
}
void SetAxisScale(int axis, float scale)
{
SettingsChanged = true;
Axes[axis].Multiplier = scale;
}
void SetAxisDigitalThreshold(int axis, float threshold)
{
SettingsChanged = true;
Axes[axis].DigitalThreshold = threshold;
}
void SetAxisResponseCurve(int axis, EJoyCurve preset)
{
if (preset >= NUM_JOYCURVE || preset < JOYCURVE_CUSTOM) return;
SettingsChanged = true;
Axes[axis].ResponseCurvePreset = preset;
if (preset == JOYCURVE_CUSTOM) return;
Axes[axis].ResponseCurve = JOYCURVE[preset];
}
void SetAxisResponseCurvePoint(int axis, int point, float value)
{
if (unsigned(point) < 4)
{
SettingsChanged = true;
Axes[axis].ResponseCurvePreset = JOYCURVE_CUSTOM;
Axes[axis].ResponseCurve.pts[point] = value;
}
}
// Used by the saver to not save properties that are at their defaults.
@ -168,58 +196,85 @@ public:
}
bool IsAxisDeadZoneDefault(int axis)
{
return Axes[axis].DeadZone <= JOYDEADZONE_DEFAULT;
if(axis >= DefaultAxesCount)
return Axes[axis].DeadZone == JOYDEADZONE_DEFAULT;
return Axes[axis].DeadZone == DefaultAxes[axis].DeadZone;
}
bool IsAxisMapDefault(int axis)
{
if(axis >= DefaultAxesCount)
return Axes[axis].GameAxis == JOYAXIS_None;
return Axes[axis].GameAxis == DefaultAxes[axis];
return Axes[axis].GameAxis == DefaultAxes[axis].GameAxis;
}
bool IsAxisScaleDefault(int axis)
{
return Axes[axis].Multiplier == JOYSENSITIVITY_DEFAULT;
if(axis >= DefaultAxesCount)
return Axes[axis].Multiplier == JOYSENSITIVITY_DEFAULT;
return Axes[axis].Multiplier == DefaultAxes[axis].Multiplier;
}
bool IsAxisDigitalThresholdDefault(int axis)
{
return true;
if(axis >= DefaultAxesCount)
return Axes[axis].DigitalThreshold == JOYTHRESH_DEFAULT;
return Axes[axis].DigitalThreshold == DefaultAxes[axis].DigitalThreshold;
}
bool IsAxisResponseCurveDefault(int axis)
{
return true;
}
if(axis >= DefaultAxesCount)
return Axes[axis].ResponseCurvePreset == JOYCURVE_DEFAULT;
return Axes[axis].ResponseCurvePreset == DefaultAxes[axis].ResponseCurvePreset;
}
void SetDefaultConfig()
{
if (Axes.size() == 0)
{
for(int i = 0;i < GetNumAxes();i++)
{
Axes.Push({});
}
}
for(int i = 0;i < GetNumAxes();i++)
{
AxisInfo info;
if (Mapping) {
switch(i) {
case SDL_CONTROLLER_AXIS_LEFTX: info.Name = "Left Stick X"; break;
case SDL_CONTROLLER_AXIS_LEFTY: info.Name = "Left Stick Y"; break;
case SDL_CONTROLLER_AXIS_RIGHTX: info.Name = "Right Stick X"; break;
case SDL_CONTROLLER_AXIS_RIGHTY: info.Name = "Right Stick Y"; break;
case SDL_CONTROLLER_AXIS_TRIGGERLEFT: info.Name = "Left Trigger"; break;
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: info.Name = "Right Trigger"; break;
default: info.Name.Format("Axis %d", i+1); break;
case SDL_CONTROLLER_AXIS_LEFTX: Axes[i].Name = "Left Stick X"; break;
case SDL_CONTROLLER_AXIS_LEFTY: Axes[i].Name = "Left Stick Y"; break;
case SDL_CONTROLLER_AXIS_RIGHTX: Axes[i].Name = "Right Stick X"; break;
case SDL_CONTROLLER_AXIS_RIGHTY: Axes[i].Name = "Right Stick Y"; break;
case SDL_CONTROLLER_AXIS_TRIGGERLEFT: Axes[i].Name = "Left Trigger"; break;
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: Axes[i].Name = "Right Trigger"; break;
default: Axes[i].Name.Format("Axis %d", i+1); break;
}
} else {
if(i < NumAxes)
info.Name.Format("Axis %d", i+1);
Axes[i].Name.Format("Axis %d", i+1);
else
info.Name.Format("Hat %d (%c)", (i-NumAxes)/2 + 1, (i-NumAxes)%2 == 0 ? 'x' : 'y');
Axes[i].Name.Format("Hat %d (%c)", (i-NumAxes)/2 + 1, (i-NumAxes)%2 == 0 ? 'x' : 'y');
}
info.DeadZone = JOYDEADZONE_DEFAULT;
info.Multiplier = JOYSENSITIVITY_DEFAULT;
info.Value = 0.0;
info.ButtonValue = 0;
Axes[i].Value = 0.0;
Axes[i].ButtonValue = 0;
info.GameAxis = (i < DefaultAxesCount)? DefaultAxes[i]: JOYAXIS_None;
Axes.Push(info);
if (i < DefaultAxesCount)
{
Axes[i].GameAxis = DefaultAxes[i].GameAxis;
Axes[i].DeadZone = DefaultAxes[i].DeadZone;
Axes[i].Multiplier = DefaultAxes[i].Multiplier;
Axes[i].DigitalThreshold = DefaultAxes[i].DigitalThreshold;
Axes[i].ResponseCurvePreset = DefaultAxes[i].ResponseCurvePreset;
Axes[i].ResponseCurve = JOYCURVE[DefaultAxes[i].ResponseCurvePreset];
}
else
{
Axes[i].GameAxis = JOYAXIS_None;
Axes[i].DeadZone = JOYDEADZONE_DEFAULT;
Axes[i].Multiplier = JOYSENSITIVITY_DEFAULT;
Axes[i].DigitalThreshold = JOYTHRESH_DEFAULT;
Axes[i].ResponseCurvePreset = JOYCURVE_DEFAULT;
Axes[i].ResponseCurve = JOYCURVE[JOYCURVE_DEFAULT];
}
}
}
@ -230,6 +285,7 @@ public:
void SetEnabled(bool enabled)
{
SettingsChanged = true;
Enabled = enabled;
}
@ -254,7 +310,104 @@ public:
}
}
void ProcessInput();
void ProcessInput() {
uint8_t buttonstate;
if (Mapping)
{
// GameController API available
auto lastTriggerL = Axes[SDL_CONTROLLER_AXIS_TRIGGERLEFT].Value > Axes[SDL_CONTROLLER_AXIS_TRIGGERLEFT].DigitalThreshold;
auto lastTriggerR = Axes[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].Value > Axes[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].DigitalThreshold;
for (auto i = 0; i < SDL_CONTROLLER_AXIS_MAX && i < NumAxes; ++i)
{
buttonstate = 0;
Axes[i].Value = SDL_GameControllerGetAxis(Mapping, static_cast<SDL_GameControllerAxis>(i))/32767.0;
Axes[i].Value = Joy_RemoveDeadZone(Axes[i].Value, Axes[i].DeadZone, &buttonstate);
Axes[i].Value = Joy_ApplyResponseCurveBezier(Axes[i].ResponseCurve, Axes[i].Value);
}
auto currTriggerL = Axes[SDL_CONTROLLER_AXIS_TRIGGERLEFT].Value > Axes[SDL_CONTROLLER_AXIS_TRIGGERLEFT].DigitalThreshold;
auto currTriggerR = Axes[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].Value > Axes[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].DigitalThreshold;
if (lastTriggerL != currTriggerL) Joy_GenerateButtonEvent(currTriggerL, KEY_PAD_LTRIGGER);
if (lastTriggerR != currTriggerR) Joy_GenerateButtonEvent(currTriggerR, KEY_PAD_RTRIGGER);
// todo: right stick
buttonstate = Joy_XYAxesToButtons(
abs(Axes[0].Value) < Axes[0].DigitalThreshold ? 0 : Axes[0].Value,
abs(Axes[1].Value) < Axes[1].DigitalThreshold ? 0 : Axes[1].Value
);
Joy_GenerateButtonEvents(Axes[0].ButtonValue, buttonstate, 4, KEY_JOYAXIS1PLUS);
Axes[0].ButtonValue = buttonstate;
}
else
{
// Joystick API fallback
for (int i = 0; i < NumAxes; ++i)
{
buttonstate = 0;
Axes[i].Value = SDL_JoystickGetAxis(Device, i)/32767.0;
Axes[i].Value = Joy_RemoveDeadZone(Axes[i].Value, Axes[i].DeadZone, &buttonstate);
Axes[i].Value = Joy_ApplyResponseCurveBezier(Axes[i].ResponseCurve, Axes[i].Value);
// Map button to axis
// X and Y are handled differently so if we have 2 or more axes then we'll use that code instead.
if (NumAxes == 1 || (i >= 2 && i < NUM_JOYAXISBUTTONS))
{
Joy_GenerateButtonEvents(Axes[i].ButtonValue, buttonstate, 2, KEY_JOYAXIS1PLUS + i*2);
Axes[i].ButtonValue = buttonstate;
}
}
if(NumAxes > 1)
{
buttonstate = Joy_XYAxesToButtons(
abs(Axes[0].Value) < Axes[0].DigitalThreshold ? 0 : Axes[0].Value,
abs(Axes[1].Value) < Axes[1].DigitalThreshold ? 0 : Axes[1].Value
);
Joy_GenerateButtonEvents(Axes[0].ButtonValue, buttonstate, 4, KEY_JOYAXIS1PLUS);
Axes[0].ButtonValue = buttonstate;
}
// Map POV hats to buttons and axes. Why axes? Well apparently I have
// a gamepad where the left control stick is a POV hat (instead of the
// d-pad like you would expect, no that's pressure sensitive). Also
// KDE's joystick dialog maps them to axes as well.
for (int i = 0; i < NumHats; ++i)
{
AxisInfo &x = Axes[NumAxes + i*2];
AxisInfo &y = Axes[NumAxes + i*2 + 1];
buttonstate = SDL_JoystickGetHat(Device, i);
// If we're going to assume that we can pass SDL's value into
// Joy_GenerateButtonEvents then we might as well assume the format here.
if(buttonstate & 0x1) // Up
y.Value = -1.0;
else if(buttonstate & 0x4) // Down
y.Value = 1.0;
else
y.Value = 0.0;
if(buttonstate & 0x2) // Left
x.Value = 1.0;
else if(buttonstate & 0x8) // Right
x.Value = -1.0;
else
x.Value = 0.0;
if(i < 4)
{
Joy_GenerateButtonEvents(x.ButtonValue, buttonstate, 4, KEY_JOYPOV1_UP + i*4);
x.ButtonValue = buttonstate;
}
}
}
}
protected:
struct AxisInfo
@ -262,16 +415,28 @@ protected:
FString Name;
float DeadZone;
float Multiplier;
float DigitalThreshold;
EJoyCurve ResponseCurvePreset;
CubicBezier ResponseCurve;
EJoyAxis GameAxis;
double Value;
uint8_t ButtonValue;
};
static const EJoyAxis DefaultJoystickAxes[5];
static const EJoyAxis DefaultControllerAxes[6];
const EJoyAxis * DefaultAxes;
struct DefaultAxisConfig
{
float DeadZone;
EJoyAxis GameAxis;
float Multiplier;
float DigitalThreshold;
EJoyCurve ResponseCurvePreset;
};
static const DefaultAxisConfig DefaultJoystickAxes[5];
static const DefaultAxisConfig DefaultControllerAxes[6];
const DefaultAxisConfig * DefaultAxes;
int DefaultAxesCount;
int DeviceIndex;
int InstanceID;
SDL_Joystick *Device;
SDL_GameController *Mapping;
@ -280,24 +445,36 @@ protected:
TArray<AxisInfo> Axes;
int NumAxes;
int NumHats;
bool SettingsChanged;
friend class SDLInputJoystickManager;
};
// [Nash 4 Feb 2024] seems like on Linux, the third axis is actually the Left Trigger, resulting in the player uncontrollably looking upwards.
const EJoyAxis SDLInputJoystick::DefaultJoystickAxes[5] = {JOYAXIS_Side, JOYAXIS_Forward, JOYAXIS_None, JOYAXIS_Yaw, JOYAXIS_Pitch};
// Defaults if we have access to the Gamepad API for this device
const EJoyAxis SDLInputJoystick::DefaultControllerAxes[6] = {JOYAXIS_Side, JOYAXIS_Forward, JOYAXIS_Yaw, JOYAXIS_Pitch, JOYAXIS_None, JOYAXIS_None};
const SDLInputJoystick::DefaultAxisConfig SDLInputJoystick::DefaultJoystickAxes[5] = {
{JOYDEADZONE_DEFAULT, JOYAXIS_Side, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_X, JOYCURVE_DEFAULT},
{JOYDEADZONE_DEFAULT, JOYAXIS_Forward, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_Y, JOYCURVE_DEFAULT},
{JOYDEADZONE_DEFAULT, JOYAXIS_None, JOYSENSITIVITY_DEFAULT, JOYTHRESH_DEFAULT, JOYCURVE_DEFAULT},
{JOYDEADZONE_DEFAULT, JOYAXIS_Yaw, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_X, JOYCURVE_DEFAULT},
{JOYDEADZONE_DEFAULT, JOYAXIS_Pitch, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_Y, JOYCURVE_DEFAULT}
};
// Defaults if we have access to the GameController API for this device
const SDLInputJoystick::DefaultAxisConfig SDLInputJoystick::DefaultControllerAxes[6] = {
{JOYDEADZONE_DEFAULT, JOYAXIS_Side, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_X, JOYCURVE_DEFAULT},
{JOYDEADZONE_DEFAULT, JOYAXIS_Forward, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_Y, JOYCURVE_DEFAULT},
{JOYDEADZONE_DEFAULT, JOYAXIS_Yaw, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_X, JOYCURVE_DEFAULT},
{JOYDEADZONE_DEFAULT, JOYAXIS_Pitch, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_Y, JOYCURVE_DEFAULT},
{JOYDEADZONE_DEFAULT, JOYAXIS_None, JOYSENSITIVITY_DEFAULT, JOYTHRESH_TRIGGER, JOYCURVE_DEFAULT},
{JOYDEADZONE_DEFAULT, JOYAXIS_None, JOYSENSITIVITY_DEFAULT, JOYTHRESH_TRIGGER, JOYCURVE_DEFAULT},
};
class SDLInputJoystickManager
{
public:
SDLInputJoystickManager()
{
this->UpdateDeviceList();
UpdateDeviceList();
}
void UpdateDeviceList()
@ -334,6 +511,19 @@ public:
if(Joysticks[i]->Enabled) Joysticks[i]->ProcessInput();
}
bool IsJoystickEnabled(int instanceID)
{
for(unsigned int i = 0; i < Joysticks.Size(); i++)
{
if (Joysticks[i]->InstanceID != instanceID)
{
continue;
}
return Joysticks[i]->Enabled;
}
return false;
}
protected:
TDeletingArray<SDLInputJoystick *> Joysticks;
};
@ -381,103 +571,14 @@ void I_ProcessJoysticks()
JoystickManager->ProcessInput();
}
void PostKeyEvent(bool down, EKeyCodes which)
void I_JoyConsumeEvent(int instanceID, event_t * event)
{
event_t event = { 0,0,0,0,0,0,0 };
event.type = down ? EV_KeyDown : EV_KeyUp;
event.data1 = which;
D_PostEvent(&event);
}
void SDLInputJoystick::ProcessInput() {
uint8_t buttonstate;
if (Mapping)
if (event->type == EV_KeyDown)
{
// GameController API available
auto lastTriggerL = Axes[SDL_CONTROLLER_AXIS_TRIGGERLEFT].Value > JOYTHRESH_DEFAULT;
auto lastTriggerR = Axes[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].Value > JOYTHRESH_DEFAULT;
for (auto i = 0; i < SDL_CONTROLLER_AXIS_MAX && i < NumAxes; ++i)
{
buttonstate = 0;
Axes[i].Value = SDL_GameControllerGetAxis(Mapping, static_cast<SDL_GameControllerAxis>(i))/32767.0;
Axes[i].Value = Joy_RemoveDeadZone(Axes[i].Value, Axes[i].DeadZone, &buttonstate);
}
auto currTriggerL = Axes[SDL_CONTROLLER_AXIS_TRIGGERLEFT].Value > JOYTHRESH_DEFAULT;
auto currTriggerR = Axes[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].Value > JOYTHRESH_DEFAULT;
if (lastTriggerL != currTriggerL) PostKeyEvent(currTriggerL, KEY_PAD_LTRIGGER);
if (lastTriggerR != currTriggerR) PostKeyEvent(currTriggerR, KEY_PAD_RTRIGGER);
buttonstate = Joy_XYAxesToButtons(Axes[0].Value, Axes[1].Value);
Joy_GenerateButtonEvents(Axes[0].ButtonValue, buttonstate, 4, KEY_JOYAXIS1PLUS);
Axes[0].ButtonValue = buttonstate;
}
else
{
// Joystick API fallback
for (int i = 0; i < NumAxes; ++i)
{
buttonstate = 0;
Axes[i].Value = SDL_JoystickGetAxis(Device, i)/32767.0;
Axes[i].Value = Joy_RemoveDeadZone(Axes[i].Value, Axes[i].DeadZone, &buttonstate);
// Map button to axis
// X and Y are handled differently so if we have 2 or more axes then we'll use that code instead.
if (NumAxes == 1 || (i >= 2 && i < NUM_JOYAXISBUTTONS))
{
Joy_GenerateButtonEvents(Axes[i].ButtonValue, buttonstate, 2, KEY_JOYAXIS1PLUS + i*2);
Axes[i].ButtonValue = buttonstate;
}
}
if(NumAxes > 1)
{
buttonstate = Joy_XYAxesToButtons(Axes[0].Value, Axes[1].Value);
Joy_GenerateButtonEvents(Axes[0].ButtonValue, buttonstate, 4, KEY_JOYAXIS1PLUS);
Axes[0].ButtonValue = buttonstate;
}
// Map POV hats to buttons and axes. Why axes? Well apparently I have
// a gamepad where the left control stick is a POV hat (instead of the
// d-pad like you would expect, no that's pressure sensitive). Also
// KDE's joystick dialog maps them to axes as well.
for (int i = 0; i < NumHats; ++i)
{
AxisInfo &x = Axes[NumAxes + i*2];
AxisInfo &y = Axes[NumAxes + i*2 + 1];
buttonstate = SDL_JoystickGetHat(Device, i);
// If we're going to assume that we can pass SDL's value into
// Joy_GenerateButtonEvents then we might as well assume the format here.
if(buttonstate & 0x1) // Up
y.Value = -1.0;
else if(buttonstate & 0x4) // Down
y.Value = 1.0;
else
y.Value = 0.0;
if(buttonstate & 0x2) // Left
x.Value = 1.0;
else if(buttonstate & 0x8) // Right
x.Value = -1.0;
else
x.Value = 0.0;
if(i < 4)
{
Joy_GenerateButtonEvents(x.ButtonValue, buttonstate, 4, KEY_JOYPOV1_UP + i*4);
x.ButtonValue = buttonstate;
}
}
bool okay = use_joystick && JoystickManager && JoystickManager->IsJoystickEnabled(instanceID);
if (!okay) return;
}
D_PostEvent(event);
}
IJoystickConfig *I_UpdateDeviceList()