gzdoom/src/sdl/i_joystick.cpp

300 lines
6.1 KiB
C++

#include <SDL_joystick.h>
#include "doomdef.h"
#include "m_joy.h"
class SDLInputJoystick: public IJoystickConfig
{
public:
SDLInputJoystick(int DeviceIndex) : DeviceIndex(DeviceIndex), Multiplier(1.0f)
{
Device = SDL_JoystickOpen(DeviceIndex);
if(Device != NULL)
{
NumAxes = SDL_JoystickNumAxes(Device);
NumHats = SDL_JoystickNumHats(Device);
SetDefaultConfig();
}
}
~SDLInputJoystick()
{
if(Device != NULL)
M_SaveJoystickConfig(this);
SDL_JoystickClose(Device);
}
bool IsValid() const
{
return Device != NULL;
}
FString GetName()
{
return SDL_JoystickName(DeviceIndex);
}
float GetSensitivity()
{
return Multiplier;
}
void SetSensitivity(float scale)
{
Multiplier = scale;
}
int GetNumAxes()
{
return NumAxes + NumHats*2;
}
float GetAxisDeadZone(int axis)
{
return Axes[axis].DeadZone;
}
EJoyAxis GetAxisMap(int axis)
{
return Axes[axis].GameAxis;
}
const char *GetAxisName(int axis)
{
return Axes[axis].Name.GetChars();
}
float GetAxisScale(int axis)
{
return Axes[axis].Multiplier;
}
void SetAxisDeadZone(int axis, float zone)
{
Axes[axis].DeadZone = zone;
}
void SetAxisMap(int axis, EJoyAxis gameaxis)
{
Axes[axis].GameAxis = gameaxis;
}
void SetAxisScale(int axis, float scale)
{
Axes[axis].Multiplier = scale;
}
// Used by the saver to not save properties that are at their defaults.
bool IsSensitivityDefault()
{
return Multiplier == 1.0f;
}
bool IsAxisDeadZoneDefault(int axis)
{
return Axes[axis].DeadZone == 0.0f;
}
bool IsAxisMapDefault(int axis)
{
if(axis >= 5)
return Axes[axis].GameAxis == JOYAXIS_None;
return Axes[axis].GameAxis == DefaultAxes[axis];
}
bool IsAxisScaleDefault(int axis)
{
return Axes[axis].Multiplier == 1.0f;
}
void SetDefaultConfig()
{
for(int i = 0;i < GetNumAxes();i++)
{
AxisInfo info;
if(i < NumAxes)
info.Name.Format("Axis %d", i+1);
else
info.Name.Format("Hat %d (%c)", (i-NumAxes)/2 + 1, (i-NumAxes)%2 == 0 ? 'x' : 'y');
info.DeadZone = 0.0f;
info.Multiplier = 1.0f;
info.Value = 0.0;
info.ButtonValue = 0;
if(i >= 5)
info.GameAxis = JOYAXIS_None;
else
info.GameAxis = DefaultAxes[i];
Axes.Push(info);
}
}
FString GetIdentifier()
{
char id[16];
mysnprintf(id, countof(id), "JS:%d", DeviceIndex);
return id;
}
void AddAxes(float axes[NUM_JOYAXIS])
{
// Add to game axes.
for (int i = 0; i < GetNumAxes(); ++i)
{
if(Axes[i].GameAxis != JOYAXIS_None)
axes[Axes[i].GameAxis] -= float(Axes[i].Value * Multiplier * Axes[i].Multiplier);
}
}
void ProcessInput()
{
BYTE buttonstate;
for (int i = 0; i < NumAxes; ++i)
{
buttonstate = 0;
Axes[i].Value = SDL_JoystickGetAxis(Device, i)/32768.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;
}
}
}
protected:
struct AxisInfo
{
FString Name;
float DeadZone;
float Multiplier;
EJoyAxis GameAxis;
double Value;
BYTE ButtonValue;
};
static const EJoyAxis DefaultAxes[5];
int DeviceIndex;
SDL_Joystick *Device;
float Multiplier;
TArray<AxisInfo> Axes;
int NumAxes;
int NumHats;
};
const EJoyAxis SDLInputJoystick::DefaultAxes[5] = {JOYAXIS_Side, JOYAXIS_Forward, JOYAXIS_Pitch, JOYAXIS_Yaw, JOYAXIS_Up};
class SDLInputJoystickManager
{
public:
SDLInputJoystickManager()
{
for(int i = 0;i < SDL_NumJoysticks();i++)
{
SDLInputJoystick *device = new SDLInputJoystick(i);
if(device->IsValid())
Joysticks.Push(device);
else
delete device;
}
}
~SDLInputJoystickManager()
{
for(unsigned int i = 0;i < Joysticks.Size();i++)
delete Joysticks[i];
}
void AddAxes(float axes[NUM_JOYAXIS])
{
for(unsigned int i = 0;i < Joysticks.Size();i++)
Joysticks[i]->AddAxes(axes);
}
void GetDevices(TArray<IJoystickConfig *> &sticks)
{
for(unsigned int i = 0;i < Joysticks.Size();i++)
{
M_LoadJoystickConfig(Joysticks[i]);
sticks.Push(Joysticks[i]);
}
}
void ProcessInput() const
{
for(unsigned int i = 0;i < Joysticks.Size();++i)
Joysticks[i]->ProcessInput();
}
protected:
TArray<SDLInputJoystick *> Joysticks;
};
static SDLInputJoystickManager *JoystickManager;
void I_StartupJoysticks()
{
JoystickManager = new SDLInputJoystickManager();
}
void I_ShutdownJoysticks()
{
delete JoystickManager;
}
void I_GetJoysticks(TArray<IJoystickConfig *> &sticks)
{
sticks.Clear();
JoystickManager->GetDevices(sticks);
}
void I_GetAxes(float axes[NUM_JOYAXIS])
{
for (int i = 0; i < NUM_JOYAXIS; ++i)
{
axes[i] = 0;
}
if (use_joystick)
{
JoystickManager->AddAxes(axes);
}
}
void I_ProcessJoysticks()
{
if (use_joystick)
JoystickManager->ProcessInput();
}
IJoystickConfig *I_UpdateDeviceList()
{
return NULL;
}