Gamepad Improvements

* Cleanup: Alignment, long lines, Replace 0 with SDLK_UNKNOWN

* Gamecontroller api analogue input

* Added some button handling

* Added mapping for other buttons

* Added trigger events

* Added force_joystick flag

* Removed force_joystick flag

Rationale:
1. It was actually broken lol
2. I cannot think of a case where enabling this would be a useful thing for
gzdoom. If the user is using a gamecontroller, it is pointless. If they are
not using a gamecontroller, it will just default to using the joystick api.
If they are not using a gamecontroller, but SDL thinks they are, it is an
SDL bug, and will be reported and fixed

* Modified default mapping

* Added analogue to digital threshold

* Added analogue response curve

* Per axis settings

* Fixed controller reconnect

* Added threshold and curve to IJoystickConfig
Enabled saving of settings

* Added stubs

* Cleanup

Constants are no longer defines.
Constants are mostly shared between backends.
Moved some logic to m_joy

* Implemented xinput stubs

* Implemented dinput stubs

* Implemented ps2 stubs (untested)

* Fixed inclusive check

* Implemented osx stubs (untested)

* Fixed curve implementation

No longer savable, I screwed the curve function up.
I though it needed 2 control points, but it needs 4.
Need to re-do controller settings :(

* Now using CubicBezier struct

* Fixed SetDefaultConfig to match xinput behavior

* Expanded gamepad CCMD

* Rename enum JoyResponseCurve to EJoyCurve

* Initial menu implementation

* Fixed SDL controller setting saving

* SDL gamepads can now actually be disabled

* Fixed initial controller connect of some versions of SDL

* Spelling error

* Enable gamepad by default

* Fixed segfault on some versions of SDL

* Only block keydown
This commit is contained in:
Marcus Minhorst 2025-07-05 16:41:40 -04:00 committed by GitHub
commit 2a5cce543b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 1613 additions and 215 deletions

View file

@ -147,7 +147,9 @@ const char *KeyNames[NUM_KEYS] =
"DPadUp","DPadDown","DPadLeft","DPadRight", // Gamepad buttons
"Pad_Start","Pad_Back","LThumb","RThumb",
"LShoulder","RShoulder","LTrigger","RTrigger",
"Pad_A", "Pad_B", "Pad_X", "Pad_Y"
"Pad_A", "Pad_B", "Pad_X", "Pad_Y",
"Paddle_1", "Paddle_2", "Paddle_3", "Paddle_4",
"Guide", "Pad_Misc", "Pad_Touchpad"
};
FKeyBindings Bindings;

View file

@ -134,8 +134,15 @@ enum EKeyCodes
KEY_PAD_B = 0x1C1,
KEY_PAD_X = 0x1C2,
KEY_PAD_Y = 0x1C3,
KEY_PAD_PADDLE1 = 0x1C4,
KEY_PAD_PADDLE2 = 0x1C5,
KEY_PAD_PADDLE3 = 0x1C6,
KEY_PAD_PADDLE4 = 0x1C7,
KEY_PAD_GUIDE = 0x1C8,
KEY_PAD_MISC1 = 0x1C9,
KEY_PAD_TOUCHPAD = 0x1CA,
NUM_KEYS = 0x1C4,
NUM_KEYS = 0x1CB,
NUM_JOYAXISBUTTONS = 8,
};

View file

@ -33,6 +33,11 @@
// HEADER FILES ------------------------------------------------------------
#include <math.h>
#include "c_dispatch.h"
#include "gain_analysis.h"
#include "keydef.h"
#include "name.h"
#include "tarray.h"
#include "vectors.h"
#include "m_joy.h"
#include "configfile.h"
@ -40,6 +45,7 @@
#include "d_eventbase.h"
#include "cmdlib.h"
#include "printf.h"
#include "zstring.h"
// MACROS ------------------------------------------------------------------
@ -57,9 +63,26 @@ EXTERN_CVAR(Bool, joy_ps2raw)
EXTERN_CVAR(Bool, joy_dinput)
EXTERN_CVAR(Bool, joy_xinput)
extern const float JOYDEADZONE_DEFAULT = 0.1; // reduced from 0.25
extern const float JOYSENSITIVITY_DEFAULT = 1.0;
extern const float JOYTHRESH_DEFAULT = 0.05;
extern const float JOYTHRESH_TRIGGER = 0.05;
extern const float JOYTHRESH_STICK_X = 0.65;
extern const float JOYTHRESH_STICK_Y = 0.35;
extern const CubicBezier JOYCURVE[NUM_JOYCURVE] = {
{{0.3, 0.0, 0.7, 0.4}}, // DEFAULT -> QUADRATIC
{{0.0, 0.0, 1.0, 1.0}}, // LINEAR
{{0.3, 0.0, 0.7, 0.4}}, // QUADRATIC
{{0.5, 0.0, 0.7, 0.2}}, // CUBIC
};
// PUBLIC DATA DEFINITIONS -------------------------------------------------
CUSTOM_CVARD(Bool, use_joystick, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL, "enables input from the joystick if it is present")
CUSTOM_CVARD(Bool, use_joystick, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL, "enables input from the joystick if it is present")
{
#ifdef _WIN32
joy_ps2raw->Callback();
@ -163,6 +186,48 @@ bool M_LoadJoystickConfig(IJoystickConfig *joy)
joy->SetAxisScale(i, (float)atof(value));
}
mysnprintf(key + axislen, countof(key) - axislen, "threshold");
value = GameConfig->GetValueForKey(key);
if (value)
{
joy->SetAxisDigitalThreshold(i, (float)atof(value));
}
mysnprintf(key + axislen, countof(key) - axislen, "curve");
value = GameConfig->GetValueForKey(key);
if (value)
{
joy->SetAxisResponseCurve(i, (EJoyCurve)clamp(atoi(value), (int)JOYCURVE_CUSTOM, (int)NUM_JOYCURVE-1));
}
mysnprintf(key + axislen, countof(key) - axislen, "curve-x1");
value = GameConfig->GetValueForKey(key);
if (value)
{
joy->SetAxisResponseCurvePoint(i, 0, (float)atof(value));
}
mysnprintf(key + axislen, countof(key) - axislen, "curve-y1");
value = GameConfig->GetValueForKey(key);
if (value)
{
joy->SetAxisResponseCurvePoint(i, 1, (float)atof(value));
}
mysnprintf(key + axislen, countof(key) - axislen, "curve-x2");
value = GameConfig->GetValueForKey(key);
if (value)
{
joy->SetAxisResponseCurvePoint(i, 2, (float)atof(value));
}
mysnprintf(key + axislen, countof(key) - axislen, "curve-y2");
value = GameConfig->GetValueForKey(key);
if (value)
{
joy->SetAxisResponseCurvePoint(i, 3, (float)atof(value));
}
mysnprintf(key + axislen, countof(key) - axislen, "map");
value = GameConfig->GetValueForKey(key);
if (value)
@ -227,6 +292,33 @@ void M_SaveJoystickConfig(IJoystickConfig *joy)
mysnprintf(value, countof(value), "%g", joy->GetAxisScale(i));
GameConfig->SetValueForKey(key, value);
}
if (!joy->IsAxisDigitalThresholdDefault(i))
{
mysnprintf(key + axislen, countof(key) - axislen, "threshold");
mysnprintf(value, countof(value), "%g", joy->GetAxisDigitalThreshold(i));
GameConfig->SetValueForKey(key, value);
}
if (!joy->IsAxisResponseCurveDefault(i))
{
mysnprintf(key + axislen, countof(key) - axislen, "curve");
mysnprintf(value, countof(value), "%d", joy->GetAxisResponseCurve(i));
GameConfig->SetValueForKey(key, value);
}
if (joy->GetAxisResponseCurve(i) == JOYCURVE_CUSTOM)
{
mysnprintf(key + axislen, countof(key) - axislen, "curve-x1");
mysnprintf(value, countof(value), "%g", joy->GetAxisResponseCurvePoint(i, 0));
GameConfig->SetValueForKey(key, value);
mysnprintf(key + axislen, countof(key) - axislen, "curve-y1");
mysnprintf(value, countof(value), "%g", joy->GetAxisResponseCurvePoint(i, 1));
GameConfig->SetValueForKey(key, value);
mysnprintf(key + axislen, countof(key) - axislen, "curve-x2");
mysnprintf(value, countof(value), "%g", joy->GetAxisResponseCurvePoint(i, 2));
GameConfig->SetValueForKey(key, value);
mysnprintf(key + axislen, countof(key) - axislen, "curve-y2");
mysnprintf(value, countof(value), "%g", joy->GetAxisResponseCurvePoint(i, 3));
GameConfig->SetValueForKey(key, value);
}
if (!joy->IsAxisMapDefault(i))
{
mysnprintf(key + axislen, countof(key) - axislen, "map");
@ -243,6 +335,174 @@ void M_SaveJoystickConfig(IJoystickConfig *joy)
}
}
CCMD (gamepad)
{
int COMMAND = 1, IDENTIFIER = 2, VALUE = 3;
int argc = argv.argc()-1;
TArray<IJoystickConfig *> sticks;
I_GetJoysticks(sticks);
auto usage = []()
{
Printf(
"usage:"
"\n\tgamepad list"
"\n\tgamepad reset pad"
"\n\tgamepad enabled pad [0|1]"
"\n\tgamepad background pad [0|1]"
"\n\tgamepad sensitivity pad [float]"
"\n\tgamepad deadzone pad.axis [float]"
"\n\tgamepad scale pad.axis [float]"
"\n\tgamepad threshold pad.axis [float]"
"\n\tgamepad curve pad.axis [-1|0|1|2|3]"
"\n\tgamepad curve-x1 pad.axis [float]"
"\n\tgamepad curve-y1 pad.axis [float]"
"\n\tgamepad curve-x2 pad.axis [float]"
"\n\tgamepad curve-y2 pad.axis [float]"
"\n\tgamepad map pad.axis [-1|0|1|2|3|4]"
"\n"
);
};
if (argc < COMMAND)
{
return usage();
};
FName command = argv[COMMAND];
if (argc < IDENTIFIER)
{
if (command == "list")
{
for (int i = 0; i < sticks.SSize(); i++)
{
Printf("%d: '%s'\n", i, sticks[i]->GetName().GetChars());
for (int j = 0; j < sticks[i]->GetNumAxes(); j++)
{
Printf(" %d.%d: '%s'\n", i, j, sticks[i]->GetAxisName(j));
}
}
return;
}
return usage();
}
const char * id = argv[IDENTIFIER];
const char * hasAxis = strchr(id, '.');
int pad, axis;
try {
pad = (int)std::stod(id);
if (pad < 0 || pad >= sticks.SSize())
{
return (void) Printf("Pad # out of range\n");
}
} catch (...) {
return (void) Printf("Failed to parse pad #\n");
}
if (hasAxis)
{
try {
axis = (int)std::stod(hasAxis+1);
if (axis < 0 || axis >= sticks[pad]->GetNumAxes())
{
return (void) Printf("Axis # out of range\n");
}
} catch (...) {
return (void) Printf("Failed to parse axis #\n");
}
}
float value = 0;
bool set = argc >= VALUE;
if (set)
{
try {
value = std::stod(argv[VALUE]);
} catch (...) {
return (void) Printf("Failed to parse args\n");
}
}
if (command == "reset")
{
if (set) return usage();
sticks[pad]->SetDefaultConfig();
sticks[pad]->SetEnabled(true);
sticks[pad]->SetEnabledInBackground(sticks[pad]->AllowsEnabledInBackground());
sticks[pad]->SetSensitivity(1);
return;
}
if (command == "enabled")
{
if (set) sticks[pad]->SetEnabled((int)value);
return (void) Printf("%d\n", sticks[pad]->GetEnabled());
}
if (command == "background")
{
if (set) sticks[pad]->SetEnabledInBackground((int)value);
return (void) Printf("%d\n", sticks[pad]->GetEnabledInBackground());
}
if (command == "sensitivity")
{
if (set) sticks[pad]->SetSensitivity(value);
return (void) Printf("%g\n", sticks[pad]->GetSensitivity());
}
if (command == "deadzone")
{
if (set) sticks[pad]->SetAxisDeadZone(axis, value);
return (void) Printf("%g\n", sticks[pad]->GetAxisDeadZone(axis));
}
if (command == "scale")
{
if (set) sticks[pad]->SetAxisScale(axis, value);
return (void) Printf("%g\n", sticks[pad]->GetAxisScale(axis));
}
if (command == "threshold")
{
if (set) sticks[pad]->SetAxisDigitalThreshold(axis, value);
return (void) Printf("%g\n", sticks[pad]->GetAxisDigitalThreshold(axis));
}
if (command == "curve")
{
if (set) sticks[pad]->SetAxisResponseCurve(axis, (EJoyCurve)value);
return (void) Printf("%d\n", sticks[pad]->GetAxisResponseCurve(axis));
}
if (command == "curve-x1")
{
if (set) sticks[pad]->SetAxisResponseCurvePoint(axis, 0, value);
return (void) Printf("%g\n", sticks[pad]->GetAxisResponseCurvePoint(axis, 0));
}
if (command == "curve-y1")
{
if (set) sticks[pad]->SetAxisResponseCurvePoint(axis, 1, value);
return (void) Printf("%g\n", sticks[pad]->GetAxisResponseCurvePoint(axis, 1));
}
if (command == "curve-x2")
{
if (set) sticks[pad]->SetAxisResponseCurvePoint(axis, 2, value);
return (void) Printf("%g\n", sticks[pad]->GetAxisResponseCurvePoint(axis, 2));
}
if (command == "curve-y2")
{
if (set) sticks[pad]->SetAxisResponseCurvePoint(axis, 3, value);
return (void) Printf("%g\n", sticks[pad]->GetAxisResponseCurvePoint(axis, 3));
}
if (command == "map")
{
if (set) sticks[pad]->SetAxisMap(axis, (EJoyAxis)value);
return (void) Printf("%d\n", sticks[pad]->GetAxisMap(axis));
}
return usage();
}
//===========================================================================
//
@ -278,6 +538,48 @@ double Joy_RemoveDeadZone(double axisval, double deadzone, uint8_t *buttons)
return axisval;
}
//===========================================================================
//
// Joy_ApplyResponseCurveBezier
//
// Applies cubic bezier easing function
// Curve is defined by control points [(0,0) (x1,y1) (x2,y2) (1,1)]
// https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function/cubic-bezier
//
//===========================================================================
double Joy_ApplyResponseCurveBezier(const CubicBezier &curve, double input)
{
// clamp + trivial cases
if (input == 0) return 0;
double sign = (input >= 0)? 1.0: -1.0;
input = abs(input);
input = (input > 1.0)? 1.0: input;
if (input == 1.0) return sign*input;
double t = input, T;
float x1 = curve.x1, y1 = curve.y1, x2 = curve.x2, y2 = curve.y2;
const int max_iter = 4;
for (auto i = 0; i < max_iter; i++)
{
T = 1-t;
double x = 3*T*T*t*x1 + 3*T*t*t*x2 + t*t*t;
double dx = 3*T*T*x1 + 6*T*t*(x2-x1) + 3*t*t*(1-x2);
// no div by 0
if (abs(dx) < 0.00001) break;
t = clamp(t - (x-input)/dx, 0.0, 1.0);
}
T = 1-t;
t = 3*T*T*t*y1 + 3*T*t*t*y2 + t*t*t;
return sign*t;
}
//===========================================================================
//
// Joy_XYAxesToButtons
@ -315,6 +617,22 @@ int Joy_XYAxesToButtons(double x, double y)
return JoyAngleButtons[int(rad) & 7];
}
//===========================================================================
//
// Joy_GenerateButtonEvent
//
// Send either a button up or button down event for supplied key code
//
//===========================================================================
void Joy_GenerateButtonEvent(bool down, EKeyCodes which)
{
event_t event = { 0,0,0,0,0,0,0 };
event.type = down ? EV_KeyDown : EV_KeyUp;
event.data1 = which;
D_PostEvent(&event);
}
//===========================================================================
//
// Joy_GenerateButtonEvents
@ -330,15 +648,12 @@ void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, in
int changed = oldbuttons ^ newbuttons;
if (changed != 0)
{
event_t ev = { 0, 0, 0, 0, 0, 0, 0 };
int mask = 1;
for (int j = 0; j < numbuttons; mask <<= 1, ++j)
{
if (changed & mask)
{
ev.data1 = base + j;
ev.type = (newbuttons & mask) ? EV_KeyDown : EV_KeyUp;
D_PostEvent(&ev);
Joy_GenerateButtonEvent(newbuttons & mask, static_cast<EKeyCodes>(base + j));
}
}
}
@ -349,15 +664,12 @@ void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, co
int changed = oldbuttons ^ newbuttons;
if (changed != 0)
{
event_t ev = { 0, 0, 0, 0, 0, 0, 0 };
int mask = 1;
for (int j = 0; j < numbuttons; mask <<= 1, ++j)
{
if (changed & mask)
{
ev.data1 = keys[j];
ev.type = (newbuttons & mask) ? EV_KeyDown : EV_KeyUp;
D_PostEvent(&ev);
Joy_GenerateButtonEvent(newbuttons & mask, static_cast<EKeyCodes>(keys[j]));
}
}
}

View file

@ -2,9 +2,30 @@
#define M_JOY_H
#include "basics.h"
#include "keydef.h"
#include "tarray.h"
#include "c_cvars.h"
union CubicBezier {
struct {
float x1;
float y1;
float x2;
float y2;
};
float pts[4];
};
enum EJoyCurve {
JOYCURVE_CUSTOM = -1,
JOYCURVE_DEFAULT,
JOYCURVE_LINEAR,
JOYCURVE_QUADRATIC,
JOYCURVE_CUBIC,
NUM_JOYCURVE
};
enum EJoyAxis
{
JOYAXIS_None = -1,
@ -13,10 +34,22 @@ enum EJoyAxis
JOYAXIS_Forward,
JOYAXIS_Side,
JOYAXIS_Up,
// JOYAXIS_Roll, // Ha ha. No roll for you.
// JOYAXIS_Roll, // Ha ha. No roll for you.
NUM_JOYAXIS,
};
extern const float JOYDEADZONE_DEFAULT;
extern const float JOYSENSITIVITY_DEFAULT;
extern const float JOYTHRESH_DEFAULT;
extern const float JOYTHRESH_TRIGGER;
extern const float JOYTHRESH_STICK_X;
extern const float JOYTHRESH_STICK_Y;
extern const CubicBezier JOYCURVE[NUM_JOYCURVE];
// Generic configuration interface for a controller.
struct IJoystickConfig
{
@ -31,10 +64,16 @@ struct IJoystickConfig
virtual EJoyAxis GetAxisMap(int axis) = 0;
virtual const char *GetAxisName(int axis) = 0;
virtual float GetAxisScale(int axis) = 0;
virtual float GetAxisDigitalThreshold(int axis) = 0;
virtual EJoyCurve GetAxisResponseCurve(int axis) = 0;
virtual float GetAxisResponseCurvePoint(int axis, int point) = 0;
virtual void SetAxisDeadZone(int axis, float zone) = 0;
virtual void SetAxisMap(int axis, EJoyAxis gameaxis) = 0;
virtual void SetAxisScale(int axis, float scale) = 0;
virtual void SetAxisDigitalThreshold(int axis, float threshold) = 0;
virtual void SetAxisResponseCurve(int axis, EJoyCurve preset) = 0;
virtual void SetAxisResponseCurvePoint(int axis, int point, float value) = 0;
virtual bool GetEnabled() = 0;
virtual void SetEnabled(bool enabled) = 0;
@ -48,6 +87,8 @@ struct IJoystickConfig
virtual bool IsAxisDeadZoneDefault(int axis) = 0;
virtual bool IsAxisMapDefault(int axis) = 0;
virtual bool IsAxisScaleDefault(int axis) = 0;
virtual bool IsAxisDigitalThresholdDefault(int axis) = 0;
virtual bool IsAxisResponseCurveDefault(int axis) = 0;
virtual void SetDefaultConfig() = 0;
virtual FString GetIdentifier() = 0;
@ -58,10 +99,12 @@ EXTERN_CVAR(Bool, use_joystick);
bool M_LoadJoystickConfig(IJoystickConfig *joy);
void M_SaveJoystickConfig(IJoystickConfig *joy);
void Joy_GenerateButtonEvent(bool down, EKeyCodes which);
void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, int base);
void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, const int *keys);
int Joy_XYAxesToButtons(double x, double y);
double Joy_RemoveDeadZone(double axisval, double deadzone, uint8_t *buttons);
double Joy_ApplyResponseCurveBezier(const CubicBezier &curve, double input);
// These ought to be provided by a system-specific i_input.cpp.
void I_GetAxes(float axes[NUM_JOYAXIS]);

View file

@ -84,6 +84,56 @@ DEFINE_ACTION_FUNCTION(IJoystickConfig, SetAxisDeadZone)
return 0;
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, GetAxisDigitalThreshold)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
PARAM_INT(axis);
ACTION_RETURN_FLOAT(self->GetAxisDigitalThreshold(axis));
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, SetAxisDigitalThreshold)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
PARAM_INT(axis);
PARAM_FLOAT(dt);
self->SetAxisDigitalThreshold(axis, (float)dt);
return 0;
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, GetAxisResponseCurve)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
PARAM_INT(axis);
ACTION_RETURN_INT(self->GetAxisResponseCurve(axis));
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, SetAxisResponseCurve)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
PARAM_INT(axis);
PARAM_INT(curve);
self->SetAxisResponseCurve(axis, (EJoyCurve)curve);
return 0;
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, GetAxisResponseCurvePoint)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
PARAM_INT(axis);
PARAM_INT(point);
ACTION_RETURN_INT(self->GetAxisResponseCurvePoint(axis, point));
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, SetAxisResponseCurvePoint)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
PARAM_INT(axis);
PARAM_INT(point);
PARAM_FLOAT(value);
self->SetAxisResponseCurvePoint(axis, point, value);
return 0;
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, GetAxisMap)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);

View file

@ -680,7 +680,7 @@ bool M_Responder (event_t *ev)
else if (menuactive != MENU_WaitKey && (ev->type == EV_KeyDown || ev->type == EV_KeyUp))
{
// eat blocked controller events without dispatching them.
if (ev->data1 >= KEY_FIRSTJOYBUTTON && m_blockcontrollers) return true;
if (ev->data1 >= KEY_FIRSTJOYBUTTON && m_blockcontrollers && ev->type == EV_KeyDown) return true;
keyup = ev->type == EV_KeyUp;

View file

@ -94,15 +94,23 @@ public:
virtual EJoyAxis GetAxisMap(int axis);
virtual const char* GetAxisName(int axis);
virtual float GetAxisScale(int axis);
float GetAxisDigitalThreshold(int axis);
EJoyCurve GetAxisResponseCurve(int axis);
float GetAxisResponseCurvePoint(int axis, int point);
virtual void SetAxisDeadZone(int axis, float deadZone);
virtual void SetAxisMap(int axis, EJoyAxis gameAxis);
virtual void SetAxisScale(int axis, float scale);
void SetAxisDigitalThreshold(int axis, float threshold);
void SetAxisResponseCurve(int axis, EJoyCurve preset);
void SetAxisResponseCurvePoint(int axis, int point, float value);
virtual bool IsSensitivityDefault();
virtual bool IsAxisDeadZoneDefault(int axis);
virtual bool IsAxisMapDefault(int axis);
virtual bool IsAxisScaleDefault(int axis);
bool IsAxisDigitalThresholdDefault(int axis);
bool IsAxisResponseCurveDefault(int axis);
virtual bool GetEnabled();
virtual void SetEnabled(bool enabled);
@ -146,6 +154,11 @@ private:
float defaultDeadZone;
float sensitivity;
float defaultSensitivity;
float digitalThreshold;
float defaultDigitalThreshold;
EJoyCurve responseCurvePreset;
EJoyCurve defaultResponseCurvePreset;
CubicBezier responseCurve;
EJoyAxis gameAxis;
EJoyAxis defaultGameAxis;
@ -177,10 +190,6 @@ private:
io_object_t m_notification;
static const float DEFAULT_DEADZONE;
static const float DEFAULT_SENSITIVITY;
void ProcessAxes();
bool ProcessAxis (const IOHIDEventStruct& event);
bool ProcessButton(const IOHIDEventStruct& event);
@ -200,10 +209,6 @@ private:
};
const float IOKitJoystick::DEFAULT_DEADZONE = 0.25f;
const float IOKitJoystick::DEFAULT_SENSITIVITY = 1.0f;
IOHIDDeviceInterface** CreateDeviceInterface(const io_object_t device)
{
IOCFPlugInInterface** plugInInterface = NULL;
@ -286,7 +291,7 @@ IOHIDQueueInterface** CreateDeviceQueue(IOHIDDeviceInterface** const interface)
IOKitJoystick::IOKitJoystick(const io_object_t device)
: m_interface(CreateDeviceInterface(device))
, m_queue(CreateDeviceQueue(m_interface))
, m_sensitivity(DEFAULT_SENSITIVITY)
, m_sensitivity(JOYSENSITIVITY_DEFAULT)
, m_enabled(true)
, m_useAxesPolling(true)
, m_notification(0)
@ -386,6 +391,21 @@ float IOKitJoystick::GetAxisScale(int axis)
return IS_AXIS_VALID ? m_axes[axis].sensitivity : 0.0f;
}
float IOKitJoystick::GetAxisDigitalThreshold(int axis)
{
return IS_AXIS_VALID ? m_axes[axis].digitalThreshold : JOYTHRESH_DEFAULT;
}
EJoyCurve IOKitJoystick::GetAxisResponseCurve(int axis)
{
return IS_AXIS_VALID ? m_axes[axis].responseCurvePreset : JOYCURVE_DEFAULT;
}
float IOKitJoystick::GetAxisResponseCurvePoint(int axis, int point)
{
return ( IS_AXIS_VALID && unsigned(point) < 4 )? m_axes[axis].responseCurve.pts[point] : 0;
}
void IOKitJoystick::SetAxisDeadZone(int axis, float deadZone)
{
if (IS_AXIS_VALID)
@ -412,10 +432,37 @@ void IOKitJoystick::SetAxisScale(int axis, float scale)
}
}
void IOKitJoystick::SetAxisDigitalThreshold(int axis, float threshold)
{
if (IS_AXIS_VALID)
{
m_axes[axis].digitalThreshold = threshold;
}
}
void IOKitJoystick::SetAxisResponseCurve(int axis, EJoyCurve preset)
{
if (IS_AXIS_VALID)
{
if (preset >= NUM_JOYCURVE || preset < JOYCURVE_CUSTOM) return;
m_axes[axis].responseCurvePreset = preset;
if (preset == JOYCURVE_CUSTOM) return;
m_axes[axis].responseCurve = JOYCURVE[preset];
}
}
void IOKitJoystick::SetAxisResponseCurvePoint(int axis, int point, float value)
{
if (IS_AXIS_VALID && unsigned(point) < 4)
{
m_axes[axis].responseCurvePreset = JOYCURVE_CUSTOM;
m_axes[axis].responseCurve.pts[point] = value;
}
}
bool IOKitJoystick::IsSensitivityDefault()
{
return DEFAULT_SENSITIVITY == m_sensitivity;
return JOYSENSITIVITY_DEFAULT == m_sensitivity;
}
bool IOKitJoystick::IsAxisDeadZoneDefault(int axis)
@ -439,7 +486,19 @@ bool IOKitJoystick::IsAxisScaleDefault(int axis)
: true;
}
bool IOKitJoystick::IsAxisDigitalThresholdDefault(int axis)
{
return IS_AXIS_VALID
? (m_axes[axis].digitalThreshold == m_axes[axis].defaultDigitalThreshold)
: true;
}
bool IOKitJoystick::IsAxisResponseCurveDefault(int axis)
{
return IS_AXIS_VALID
? m_axes[axis].responseCurvePreset == m_axes[axis].defaultResponseCurvePreset
: true;
}
bool IOKitJoystick::GetEnabled()
{
@ -454,15 +513,18 @@ void IOKitJoystick::SetEnabled(bool enabled)
void IOKitJoystick::SetDefaultConfig()
{
m_sensitivity = DEFAULT_SENSITIVITY;
m_sensitivity = JOYSENSITIVITY_DEFAULT;
const size_t axisCount = m_axes.Size();
for (size_t i = 0; i < axisCount; ++i)
{
m_axes[i].deadZone = DEFAULT_DEADZONE;
m_axes[i].sensitivity = DEFAULT_SENSITIVITY;
m_axes[i].deadZone = JOYDEADZONE_DEFAULT;
m_axes[i].sensitivity = JOYSENSITIVITY_DEFAULT;
m_axes[i].gameAxis = JOYAXIS_None;
m_axes[i].digitalThreshold = JOYTHRESH_DEFAULT;
m_axes[i].responseCurvePreset = JOYCURVE_DEFAULT;
m_axes[i].responseCurve = JOYCURVE[JOYCURVE_DEFAULT];
}
// Two axes? Horizontal is yaw and vertical is forward.
@ -470,7 +532,10 @@ void IOKitJoystick::SetDefaultConfig()
if (2 == axisCount)
{
m_axes[0].gameAxis = JOYAXIS_Yaw;
m_axes[0].digitalThreshold = JOYTHRESH_STICK_X;
m_axes[1].gameAxis = JOYAXIS_Forward;
m_axes[1].digitalThreshold = JOYTHRESH_STICK_Y;
}
// Three axes? First two are movement, third is yaw.
@ -478,8 +543,13 @@ void IOKitJoystick::SetDefaultConfig()
else if (axisCount >= 3)
{
m_axes[0].gameAxis = JOYAXIS_Side;
m_axes[0].digitalThreshold = JOYTHRESH_STICK_X;
m_axes[1].gameAxis = JOYAXIS_Forward;
m_axes[1].digitalThreshold = JOYTHRESH_STICK_Y;
m_axes[2].gameAxis = JOYAXIS_Yaw;
m_axes[2].digitalThreshold = JOYTHRESH_STICK_X;
// Four axes? First two are movement, last two are looking around.
@ -487,12 +557,14 @@ void IOKitJoystick::SetDefaultConfig()
{
m_axes[3].gameAxis = JOYAXIS_Pitch;
// ??? m_axes[3].sensitivity = 0.75f;
m_axes[3].digitalThreshold = JOYTHRESH_STICK_Y;
// Five axes? Use the fifth one for moving up and down.
if (axisCount >= 5)
{
m_axes[4].gameAxis = JOYAXIS_Up;
m_axes[4].digitalThreshold = JOYTHRESH_STICK_Y;
}
}
}
@ -504,9 +576,11 @@ void IOKitJoystick::SetDefaultConfig()
for (size_t i = 0; i < axisCount; ++i)
{
m_axes[i].defaultDeadZone = m_axes[i].deadZone;
m_axes[i].defaultSensitivity = m_axes[i].sensitivity;
m_axes[i].defaultGameAxis = m_axes[i].gameAxis;
m_axes[i].defaultDeadZone = m_axes[i].deadZone;
m_axes[i].defaultSensitivity = m_axes[i].sensitivity;
m_axes[i].defaultGameAxis = m_axes[i].gameAxis;
m_axes[i].defaultDigitalThreshold = m_axes[i].digitalThreshold;
m_axes[i].defaultResponseCurvePreset = m_axes[i].responseCurvePreset;
}
}
@ -602,8 +676,9 @@ void IOKitJoystick::ProcessAxes()
const double scaledValue = scaledMin +
(event.value - axis.minValue) * (scaledMax - scaledMin) / (axis.maxValue - axis.minValue);
const double filteredValue = Joy_RemoveDeadZone(scaledValue, axis.deadZone, NULL);
const double smoothedValue = Joy_ApplyResponseCurveBezier(axis.responseCurve, filteredValue);
axis.value = static_cast<float>(filteredValue * m_sensitivity * axis.sensitivity);
axis.value = static_cast<float>(smoothedValue * m_sensitivity * axis.sensitivity);
}
else
{
@ -635,8 +710,9 @@ bool IOKitJoystick::ProcessAxis(const IOHIDEventStruct& event)
const double scaledValue = scaledMin +
(event.value - axis.minValue) * (scaledMax - scaledMin) / (axis.maxValue - axis.minValue);
const double filteredValue = Joy_RemoveDeadZone(scaledValue, axis.deadZone, NULL);
const double smoothedValue = Joy_ApplyResponseCurveBezier(axis.responseCurve, filteredValue);
axis.value = static_cast<float>(filteredValue * m_sensitivity * axis.sensitivity);
axis.value = static_cast<float>(smoothedValue * m_sensitivity * axis.sensitivity);
return true;
}

View file

@ -31,11 +31,14 @@
**
*/
#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"
@ -47,88 +50,147 @@
#include "engineerrors.h"
#include "i_interface.h"
static void I_CheckGUICapture ();
static void I_CheckNativeMouse ();
bool GUICapture;
static bool NativeMouse = true;
CVAR (Bool, use_mouse, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
extern int WaitingForKey;
static const SDL_Keycode DIKToKeySym[256] =
{
0, SDLK_ESCAPE, SDLK_1, SDLK_2, SDLK_3, SDLK_4, SDLK_5, SDLK_6,
SDLK_7, SDLK_8, SDLK_9, SDLK_0,SDLK_MINUS, SDLK_EQUALS, SDLK_BACKSPACE, SDLK_TAB,
SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_t, SDLK_y, SDLK_u, SDLK_i,
SDLK_o, SDLK_p, SDLK_LEFTBRACKET, SDLK_RIGHTBRACKET, SDLK_RETURN, SDLK_LCTRL, SDLK_a, SDLK_s,
SDLK_d, SDLK_f, SDLK_g, SDLK_h, SDLK_j, SDLK_k, SDLK_l, SDLK_SEMICOLON,
SDLK_QUOTE, SDLK_BACKQUOTE, SDLK_LSHIFT, SDLK_BACKSLASH, SDLK_z, SDLK_x, SDLK_c, SDLK_v,
SDLK_b, SDLK_n, SDLK_m, SDLK_COMMA, SDLK_PERIOD, SDLK_SLASH, SDLK_RSHIFT, SDLK_KP_MULTIPLY,
SDLK_LALT, SDLK_SPACE, SDLK_CAPSLOCK, SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5,
SDLK_F6, SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10, SDLK_NUMLOCKCLEAR, SDLK_SCROLLLOCK, SDLK_KP_7,
SDLK_KP_8, SDLK_KP_9, SDLK_KP_MINUS, SDLK_KP_4, SDLK_KP_5, SDLK_KP_6, SDLK_KP_PLUS, SDLK_KP_1,
SDLK_KP_2, SDLK_KP_3, SDLK_KP_0, SDLK_KP_PERIOD, 0, 0, 0, SDLK_F11,
SDLK_F12, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, SDLK_F13, SDLK_F14, SDLK_F15, SDLK_F16,
SDLK_F17, SDLK_F18, SDLK_F19, SDLK_F20, SDLK_F21, SDLK_F22, SDLK_F23, SDLK_F24,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, SDLK_KP_EQUALS, 0, 0,
0, SDLK_AT, SDLK_COLON, 0, 0, 0, 0, 0,
0, 0, 0, 0, SDLK_KP_ENTER, SDLK_RCTRL, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, SDLK_KP_COMMA, 0, SDLK_KP_DIVIDE, 0, SDLK_SYSREQ,
SDLK_RALT, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, SDLK_PAUSE, 0, SDLK_HOME,
SDLK_UP, SDLK_PAGEUP, 0, SDLK_LEFT, 0, SDLK_RIGHT, 0, SDLK_END,
SDLK_DOWN, SDLK_PAGEDOWN, SDLK_INSERT, SDLK_DELETE, 0, 0, 0, 0,
0, 0, 0, SDLK_LGUI, SDLK_RGUI, SDLK_MENU, SDLK_POWER, SDLK_SLEEP,
0, 0, 0, 0, 0, SDLK_AC_SEARCH, SDLK_AC_BOOKMARKS, SDLK_AC_REFRESH,
SDLK_AC_STOP, SDLK_AC_FORWARD, SDLK_AC_BACK, SDLK_COMPUTER, SDLK_MAIL, SDLK_MEDIASELECT, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
SDLK_UNKNOWN, SDLK_ESCAPE, SDLK_1, SDLK_2,
SDLK_3, SDLK_4, SDLK_5, SDLK_6,
SDLK_7, SDLK_8, SDLK_9, SDLK_0,
SDLK_MINUS, SDLK_EQUALS, SDLK_BACKSPACE, SDLK_TAB,
SDLK_q, SDLK_w, SDLK_e, SDLK_r,
SDLK_t, SDLK_y, SDLK_u, SDLK_i,
SDLK_o, SDLK_p, SDLK_LEFTBRACKET, SDLK_RIGHTBRACKET,
SDLK_RETURN, SDLK_LCTRL, SDLK_a, SDLK_s,
SDLK_d, SDLK_f, SDLK_g, SDLK_h,
SDLK_j, SDLK_k, SDLK_l, SDLK_SEMICOLON,
SDLK_QUOTE, SDLK_BACKQUOTE, SDLK_LSHIFT, SDLK_BACKSLASH,
SDLK_z, SDLK_x, SDLK_c, SDLK_v,
SDLK_b, SDLK_n, SDLK_m, SDLK_COMMA,
SDLK_PERIOD, SDLK_SLASH, SDLK_RSHIFT, SDLK_KP_MULTIPLY,
SDLK_LALT, SDLK_SPACE, SDLK_CAPSLOCK, SDLK_F1,
SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5,
SDLK_F6, SDLK_F7, SDLK_F8, SDLK_F9,
SDLK_F10, SDLK_NUMLOCKCLEAR, SDLK_SCROLLLOCK, SDLK_KP_7,
SDLK_KP_8, SDLK_KP_9, SDLK_KP_MINUS, SDLK_KP_4,
SDLK_KP_5, SDLK_KP_6, SDLK_KP_PLUS, SDLK_KP_1,
SDLK_KP_2, SDLK_KP_3, SDLK_KP_0, SDLK_KP_PERIOD,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_F11,
SDLK_F12, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_F13, SDLK_F14, SDLK_F15, SDLK_F16,
SDLK_F17, SDLK_F18, SDLK_F19, SDLK_F20,
SDLK_F21, SDLK_F22, SDLK_F23, SDLK_F24,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_KP_EQUALS, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_AT, SDLK_COLON, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_KP_ENTER, SDLK_RCTRL, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_KP_COMMA,
SDLK_UNKNOWN, SDLK_KP_DIVIDE, SDLK_UNKNOWN, SDLK_SYSREQ,
SDLK_RALT, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_PAUSE, SDLK_UNKNOWN, SDLK_HOME,
SDLK_UP, SDLK_PAGEUP, SDLK_UNKNOWN, SDLK_LEFT,
SDLK_UNKNOWN, SDLK_RIGHT, SDLK_UNKNOWN, SDLK_END,
SDLK_DOWN, SDLK_PAGEDOWN, SDLK_INSERT, SDLK_DELETE,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_LGUI,
SDLK_RGUI, SDLK_MENU, SDLK_POWER, SDLK_SLEEP,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_AC_SEARCH, SDLK_AC_BOOKMARKS, SDLK_AC_REFRESH,
SDLK_AC_STOP, SDLK_AC_FORWARD, SDLK_AC_BACK, SDLK_COMPUTER,
SDLK_MAIL, SDLK_MEDIASELECT, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN
};
static const SDL_Scancode DIKToKeyScan[256] =
{
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_ESCAPE, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6,
SDL_SCANCODE_7, SDL_SCANCODE_8, SDL_SCANCODE_9, SDL_SCANCODE_0 ,SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS, SDL_SCANCODE_BACKSPACE, SDL_SCANCODE_TAB,
SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_E, SDL_SCANCODE_R, SDL_SCANCODE_T, SDL_SCANCODE_Y, SDL_SCANCODE_U, SDL_SCANCODE_I,
SDL_SCANCODE_O, SDL_SCANCODE_P, SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_RETURN, SDL_SCANCODE_LCTRL, SDL_SCANCODE_A, SDL_SCANCODE_S,
SDL_SCANCODE_D, SDL_SCANCODE_F, SDL_SCANCODE_G, SDL_SCANCODE_H, SDL_SCANCODE_J, SDL_SCANCODE_K, SDL_SCANCODE_L, SDL_SCANCODE_SEMICOLON,
SDL_SCANCODE_APOSTROPHE, SDL_SCANCODE_GRAVE, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_BACKSLASH, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_C, SDL_SCANCODE_V,
SDL_SCANCODE_B, SDL_SCANCODE_N, SDL_SCANCODE_M, SDL_SCANCODE_COMMA, SDL_SCANCODE_PERIOD, SDL_SCANCODE_SLASH, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_KP_MULTIPLY,
SDL_SCANCODE_LALT, SDL_SCANCODE_SPACE, SDL_SCANCODE_CAPSLOCK, SDL_SCANCODE_F1, SDL_SCANCODE_F2, SDL_SCANCODE_F3, SDL_SCANCODE_F4, SDL_SCANCODE_F5,
SDL_SCANCODE_F6, SDL_SCANCODE_F7, SDL_SCANCODE_F8, SDL_SCANCODE_F9, SDL_SCANCODE_F10, SDL_SCANCODE_NUMLOCKCLEAR, SDL_SCANCODE_SCROLLLOCK, SDL_SCANCODE_KP_7,
SDL_SCANCODE_KP_8, SDL_SCANCODE_KP_9, SDL_SCANCODE_KP_MINUS, SDL_SCANCODE_KP_4, SDL_SCANCODE_KP_5, SDL_SCANCODE_KP_6, SDL_SCANCODE_KP_PLUS, SDL_SCANCODE_KP_1,
SDL_SCANCODE_KP_2, SDL_SCANCODE_KP_3, SDL_SCANCODE_KP_0, SDL_SCANCODE_KP_PERIOD, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F11,
SDL_SCANCODE_F12, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F13, SDL_SCANCODE_F14, SDL_SCANCODE_F15, SDL_SCANCODE_F16,
SDL_SCANCODE_F17, SDL_SCANCODE_F18, SDL_SCANCODE_F19, SDL_SCANCODE_F20, SDL_SCANCODE_F21, SDL_SCANCODE_F22, SDL_SCANCODE_F23, SDL_SCANCODE_F24,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_EQUALS, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_ENTER, SDL_SCANCODE_RCTRL, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_COMMA, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_DIVIDE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_SYSREQ,
SDL_SCANCODE_RALT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_PAUSE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_HOME,
SDL_SCANCODE_UP, SDL_SCANCODE_PAGEUP, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_END,
SDL_SCANCODE_DOWN, SDL_SCANCODE_PAGEDOWN, SDL_SCANCODE_INSERT, SDL_SCANCODE_DELETE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LGUI, SDL_SCANCODE_RGUI, SDL_SCANCODE_MENU, SDL_SCANCODE_POWER, SDL_SCANCODE_SLEEP,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_AC_SEARCH, SDL_SCANCODE_AC_BOOKMARKS, SDL_SCANCODE_AC_REFRESH,
SDL_SCANCODE_AC_STOP, SDL_SCANCODE_AC_FORWARD, SDL_SCANCODE_AC_BACK, SDL_SCANCODE_COMPUTER, SDL_SCANCODE_MAIL, SDL_SCANCODE_MEDIASELECT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_ESCAPE, SDL_SCANCODE_1, SDL_SCANCODE_2,
SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6,
SDL_SCANCODE_7, SDL_SCANCODE_8, SDL_SCANCODE_9, SDL_SCANCODE_0,
SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS, SDL_SCANCODE_BACKSPACE, SDL_SCANCODE_TAB,
SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_E, SDL_SCANCODE_R,
SDL_SCANCODE_T, SDL_SCANCODE_Y, SDL_SCANCODE_U, SDL_SCANCODE_I,
SDL_SCANCODE_O, SDL_SCANCODE_P, SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET,
SDL_SCANCODE_RETURN, SDL_SCANCODE_LCTRL, SDL_SCANCODE_A, SDL_SCANCODE_S,
SDL_SCANCODE_D, SDL_SCANCODE_F, SDL_SCANCODE_G, SDL_SCANCODE_H,
SDL_SCANCODE_J, SDL_SCANCODE_K, SDL_SCANCODE_L, SDL_SCANCODE_SEMICOLON,
SDL_SCANCODE_APOSTROPHE, SDL_SCANCODE_GRAVE, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_BACKSLASH,
SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_C, SDL_SCANCODE_V,
SDL_SCANCODE_B, SDL_SCANCODE_N, SDL_SCANCODE_M, SDL_SCANCODE_COMMA,
SDL_SCANCODE_PERIOD, SDL_SCANCODE_SLASH, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_KP_MULTIPLY,
SDL_SCANCODE_LALT, SDL_SCANCODE_SPACE, SDL_SCANCODE_CAPSLOCK, SDL_SCANCODE_F1,
SDL_SCANCODE_F2, SDL_SCANCODE_F3, SDL_SCANCODE_F4, SDL_SCANCODE_F5,
SDL_SCANCODE_F6, SDL_SCANCODE_F7, SDL_SCANCODE_F8, SDL_SCANCODE_F9,
SDL_SCANCODE_F10, SDL_SCANCODE_NUMLOCKCLEAR, SDL_SCANCODE_SCROLLLOCK, SDL_SCANCODE_KP_7,
SDL_SCANCODE_KP_8, SDL_SCANCODE_KP_9, SDL_SCANCODE_KP_MINUS, SDL_SCANCODE_KP_4,
SDL_SCANCODE_KP_5, SDL_SCANCODE_KP_6, SDL_SCANCODE_KP_PLUS, SDL_SCANCODE_KP_1,
SDL_SCANCODE_KP_2, SDL_SCANCODE_KP_3, SDL_SCANCODE_KP_0, SDL_SCANCODE_KP_PERIOD,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F11,
SDL_SCANCODE_F12, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_F13, SDL_SCANCODE_F14, SDL_SCANCODE_F15, SDL_SCANCODE_F16,
SDL_SCANCODE_F17, SDL_SCANCODE_F18, SDL_SCANCODE_F19, SDL_SCANCODE_F20,
SDL_SCANCODE_F21, SDL_SCANCODE_F22, SDL_SCANCODE_F23, SDL_SCANCODE_F24,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_EQUALS, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_KP_ENTER, SDL_SCANCODE_RCTRL, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_COMMA,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_DIVIDE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_SYSREQ,
SDL_SCANCODE_RALT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_PAUSE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_HOME,
SDL_SCANCODE_UP, SDL_SCANCODE_PAGEUP, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LEFT,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_END,
SDL_SCANCODE_DOWN, SDL_SCANCODE_PAGEDOWN, SDL_SCANCODE_INSERT, SDL_SCANCODE_DELETE,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LGUI,
SDL_SCANCODE_RGUI, SDL_SCANCODE_MENU, SDL_SCANCODE_POWER, SDL_SCANCODE_SLEEP,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_AC_SEARCH, SDL_SCANCODE_AC_BOOKMARKS, SDL_SCANCODE_AC_REFRESH,
SDL_SCANCODE_AC_STOP, SDL_SCANCODE_AC_FORWARD, SDL_SCANCODE_AC_BACK, SDL_SCANCODE_COMPUTER,
SDL_SCANCODE_MAIL, SDL_SCANCODE_MEDIASELECT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN
};
static TMap<SDL_Keycode, uint8_t> InitKeySymMap ()
@ -463,14 +525,60 @@ void MessagePump (const SDL_Event &sev)
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
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:
case SDL_CONTROLLERBUTTONUP:
event.type = sev.type == SDL_CONTROLLERBUTTONDOWN ? EV_KeyDown : EV_KeyUp;
switch (sev.cbutton.button)
{
case SDL_CONTROLLER_BUTTON_A: event.data1 = KEY_PAD_A; break;
case SDL_CONTROLLER_BUTTON_B: event.data1 = KEY_PAD_B; break;
case SDL_CONTROLLER_BUTTON_X: event.data1 = KEY_PAD_X; break;
case SDL_CONTROLLER_BUTTON_Y: event.data1 = KEY_PAD_Y; break;
case SDL_CONTROLLER_BUTTON_BACK: event.data1 = KEY_PAD_BACK; break;
case SDL_CONTROLLER_BUTTON_GUIDE: event.data1 = KEY_PAD_GUIDE; break;
case SDL_CONTROLLER_BUTTON_START: event.data1 = KEY_PAD_START; break;
case SDL_CONTROLLER_BUTTON_LEFTSTICK: event.data1 = KEY_PAD_LTHUMB; break;
case SDL_CONTROLLER_BUTTON_RIGHTSTICK: event.data1 = KEY_PAD_RTHUMB; break;
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: event.data1 = KEY_PAD_LSHOULDER; break;
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: event.data1 = KEY_PAD_RSHOULDER; break;
case SDL_CONTROLLER_BUTTON_DPAD_UP: event.data1 = KEY_PAD_DPAD_UP; break;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: event.data1 = KEY_PAD_DPAD_DOWN; break;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: event.data1 = KEY_PAD_DPAD_LEFT; break;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: event.data1 = KEY_PAD_DPAD_RIGHT; break;
case SDL_CONTROLLER_BUTTON_MISC1: event.data1 = KEY_PAD_MISC1; break;
case SDL_CONTROLLER_BUTTON_PADDLE1: event.data1 = KEY_PAD_PADDLE1; break;
case SDL_CONTROLLER_BUTTON_PADDLE2: event.data1 = KEY_PAD_PADDLE2; break;
case SDL_CONTROLLER_BUTTON_PADDLE3: event.data1 = KEY_PAD_PADDLE3; break;
case SDL_CONTROLLER_BUTTON_PADDLE4: event.data1 = KEY_PAD_PADDLE4; break;
case SDL_CONTROLLER_BUTTON_TOUCHPAD: event.data1 = KEY_PAD_TOUCHPAD; break;
default: event.data1 = 0;
}
if(event.data1 != 0)
I_JoyConsumeEvent(sev.cbutton.which, &event);
break;
case SDL_JOYDEVICEADDED:
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:
case SDL_CONTROLLERDEVICEREMAPPED:
if (sev.type == SDL_JOYDEVICEREMOVED && SDL_GameControllerFromInstanceID(sev.jdevice.which))
break; // skip double event
I_UpdateDeviceList();
event.type = EV_DeviceChange;
D_PostEvent (&event);

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

@ -31,47 +31,80 @@
**
*/
#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"
#define DEFAULT_DEADZONE 0.25f;
// Very small deadzone so that floating point magic doesn't happen
#define MIN_DEADZONE 0.000001f
class SDLInputJoystick: public IJoystickConfig
{
public:
SDLInputJoystick(int DeviceIndex) : DeviceIndex(DeviceIndex), Multiplier(1.0f) , Enabled(true)
SDLInputJoystick(int DeviceIndex) :
DeviceIndex(DeviceIndex),
InstanceID(SDL_JoystickGetDeviceInstanceID(DeviceIndex)),
Multiplier(JOYSENSITIVITY_DEFAULT),
Enabled(true),
SettingsChanged(false)
{
Device = SDL_JoystickOpen(DeviceIndex);
if(Device != NULL)
if (SDL_IsGameController(DeviceIndex))
{
NumAxes = SDL_JoystickNumAxes(Device);
NumHats = SDL_JoystickNumHats(Device);
Mapping = SDL_GameControllerOpen(DeviceIndex);
Device = NULL;
SetDefaultConfig();
DefaultAxes = DefaultControllerAxes;
DefaultAxesCount = sizeof(DefaultControllerAxes) / sizeof(DefaultAxisConfig);
if(Mapping != NULL)
{
NumAxes = SDL_CONTROLLER_AXIS_MAX;
NumHats = 0;
SetDefaultConfig();
}
}
else
{
Device = SDL_JoystickOpen(DeviceIndex);
Mapping = NULL;
DefaultAxes = DefaultJoystickAxes;
DefaultAxesCount = sizeof(DefaultJoystickAxes) / sizeof(DefaultAxisConfig);
if(Device != NULL)
{
NumAxes = SDL_JoystickNumAxes(Device);
NumHats = SDL_JoystickNumHats(Device);
SetDefaultConfig();
}
}
M_LoadJoystickConfig(this);
}
~SDLInputJoystick()
{
if(Device != NULL)
if(IsValid() && SettingsChanged)
M_SaveJoystickConfig(this);
SDL_JoystickClose(Device);
if (Mapping)
SDL_GameControllerClose(Mapping);
if (Device)
SDL_JoystickClose(Device);
}
bool IsValid() const
{
return Device != NULL;
return Device != NULL || Mapping != NULL;
}
FString GetName()
{
return SDL_JoystickName(Device);
return (Mapping)
? SDL_GameControllerName(Mapping)
: SDL_JoystickName(Device);
}
float GetSensitivity()
{
@ -79,6 +112,7 @@ public:
}
void SetSensitivity(float scale)
{
SettingsChanged = true;
Multiplier = scale;
}
@ -102,58 +136,145 @@ public:
{
return Axes[axis].Multiplier;
}
float GetAxisDigitalThreshold(int axis)
{
return Axes[axis].DigitalThreshold;
}
EJoyCurve GetAxisResponseCurve(int axis)
{
return Axes[axis].ResponseCurvePreset;
}
float GetAxisResponseCurvePoint(int axis, int point)
{
return unsigned(point) < 4
? Axes[axis].ResponseCurve.pts[point]
: 0;
};
void SetAxisDeadZone(int axis, float zone)
{
Axes[axis].DeadZone = clamp(zone, MIN_DEADZONE, 1.f);
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.
bool IsSensitivityDefault()
{
return Multiplier == 1.0f;
return Multiplier == JOYSENSITIVITY_DEFAULT;
}
bool IsAxisDeadZoneDefault(int axis)
{
return Axes[axis].DeadZone <= MIN_DEADZONE;
if(axis >= DefaultAxesCount)
return Axes[axis].DeadZone == JOYDEADZONE_DEFAULT;
return Axes[axis].DeadZone == DefaultAxes[axis].DeadZone;
}
bool IsAxisMapDefault(int axis)
{
if(axis >= 5)
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 == 1.0f;
if(axis >= DefaultAxesCount)
return Axes[axis].Multiplier == JOYSENSITIVITY_DEFAULT;
return Axes[axis].Multiplier == DefaultAxes[axis].Multiplier;
}
bool IsAxisDigitalThresholdDefault(int axis)
{
if(axis >= DefaultAxesCount)
return Axes[axis].DigitalThreshold == JOYTHRESH_DEFAULT;
return Axes[axis].DigitalThreshold == DefaultAxes[axis].DigitalThreshold;
}
bool IsAxisResponseCurveDefault(int axis)
{
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(i < NumAxes)
info.Name.Format("Axis %d", i+1);
if (Mapping) {
switch(i) {
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)
Axes[i].Name.Format("Axis %d", i+1);
else
Axes[i].Name.Format("Hat %d (%c)", (i-NumAxes)/2 + 1, (i-NumAxes)%2 == 0 ? 'x' : 'y');
}
Axes[i].Value = 0.0;
Axes[i].ButtonValue = 0;
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
info.Name.Format("Hat %d (%c)", (i-NumAxes)/2 + 1, (i-NumAxes)%2 == 0 ? 'x' : 'y');
info.DeadZone = DEFAULT_DEADZONE;
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);
{
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];
}
}
}
@ -161,9 +282,10 @@ public:
{
return Enabled;
}
void SetEnabled(bool enabled)
{
SettingsChanged = true;
Enabled = enabled;
}
@ -188,63 +310,101 @@ public:
}
}
void ProcessInput()
{
void ProcessInput() {
uint8_t buttonstate;
for (int i = 0; i < NumAxes; ++i)
if (Mapping)
{
buttonstate = 0;
// GameController API available
Axes[i].Value = SDL_JoystickGetAxis(Device, i)/32767.0;
Axes[i].Value = Joy_RemoveDeadZone(Axes[i].Value, Axes[i].DeadZone, &buttonstate);
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;
// 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))
for (auto i = 0; i < SDL_CONTROLLER_AXIS_MAX && i < NumAxes; ++i)
{
Joy_GenerateButtonEvents(Axes[i].ButtonValue, buttonstate, 2, KEY_JOYAXIS1PLUS + i*2);
Axes[i].ButtonValue = buttonstate;
}
}
buttonstate = 0;
if(NumAxes > 1)
{
buttonstate = Joy_XYAxesToButtons(Axes[0].Value, Axes[1].Value);
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;
}
// 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)
else
{
AxisInfo &x = Axes[NumAxes + i*2];
AxisInfo &y = Axes[NumAxes + i*2 + 1];
// Joystick API fallback
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)
for (int i = 0; i < NumAxes; ++i)
{
Joy_GenerateButtonEvents(x.ButtonValue, buttonstate, 4, KEY_JOYPOV1_UP + i*4);
x.ButtonValue = buttonstate;
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;
}
}
}
}
@ -255,33 +415,66 @@ protected:
FString Name;
float DeadZone;
float Multiplier;
float DigitalThreshold;
EJoyCurve ResponseCurvePreset;
CubicBezier ResponseCurve;
EJoyAxis GameAxis;
double Value;
uint8_t ButtonValue;
};
static const EJoyAxis DefaultAxes[5];
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;
float Multiplier;
bool Enabled;
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::DefaultAxes[5] = {JOYAXIS_Side, JOYAXIS_Forward, JOYAXIS_None, JOYAXIS_Yaw, JOYAXIS_Pitch};
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()
@ -318,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;
};
@ -326,7 +532,7 @@ static SDLInputJoystickManager *JoystickManager;
void I_StartupJoysticks()
{
#ifndef NO_SDL_JOYSTICK
if(SDL_InitSubSystem(SDL_INIT_JOYSTICK) >= 0)
if(SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) >= 0)
JoystickManager = new SDLInputJoystickManager();
#endif
}
@ -335,7 +541,7 @@ void I_ShutdownInput()
if(JoystickManager)
{
delete JoystickManager;
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
}
}
@ -365,6 +571,16 @@ void I_ProcessJoysticks()
JoystickManager->ProcessInput();
}
void I_JoyConsumeEvent(int instanceID, event_t * event)
{
if (event->type == EV_KeyDown)
{
bool okay = use_joystick && JoystickManager && JoystickManager->IsJoystickEnabled(instanceID);
if (!okay) return;
}
D_PostEvent(event);
}
IJoystickConfig *I_UpdateDeviceList()
{
JoystickManager->UpdateDeviceList();

View file

@ -146,7 +146,6 @@ public:
// MACROS ------------------------------------------------------------------
#define DEFAULT_DEADZONE 0.25f
// TYPES -------------------------------------------------------------------
@ -170,15 +169,23 @@ public:
EJoyAxis GetAxisMap(int axis);
const char *GetAxisName(int axis);
float GetAxisScale(int axis);
float GetAxisDigitalThreshold(int axis);
EJoyCurve GetAxisResponseCurve(int axis);
float GetAxisResponseCurvePoint(int axis, int point);
void SetAxisDeadZone(int axis, float deadzone);
void SetAxisMap(int axis, EJoyAxis gameaxis);
void SetAxisScale(int axis, float scale);
void SetAxisDigitalThreshold(int axis, float threshold);
void SetAxisResponseCurve(int axis, EJoyCurve preset);
void SetAxisResponseCurvePoint(int axis, int point, float value);
bool IsSensitivityDefault();
bool IsAxisDeadZoneDefault(int axis);
bool IsAxisMapDefault(int axis);
bool IsAxisScaleDefault(int axis);
bool IsAxisDigitalThresholdDefault(int axis);
bool IsAxisResponseCurveDefault(int axis);
bool GetEnabled();
void SetEnabled(bool enabled);
@ -201,6 +208,9 @@ protected:
float Value;
float DeadZone, DefaultDeadZone;
float Multiplier, DefaultMultiplier;
float DigitalThreshold, DefaultDigitalThreshold;
EJoyCurve ResponseCurvePreset, DefaultResponseCurvePreset;
CubicBezier ResponseCurve;
EJoyAxis GameAxis, DefaultGameAxis;
uint8_t ButtonValue;
};
@ -454,16 +464,21 @@ void FDInputJoystick::ProcessInput()
axisval = (value - info->Min) * 2.0 / (info->Max - info->Min) - 1.0;
// Cancel out dead zone
axisval = Joy_RemoveDeadZone(axisval, info->DeadZone, &buttonstate);
axisval = Joy_ApplyResponseCurveBezier(info->ResponseCurve, axisval);
info->Value = float(axisval);
if (i < NUM_JOYAXISBUTTONS && (i > 2 || Axes.Size() == 1))
{
if (abs(axisval) < info->DigitalThreshold) buttonstate = 0;
Joy_GenerateButtonEvents(info->ButtonValue, buttonstate, 2, KEY_JOYAXIS1PLUS + i*2);
}
else if (i == 1)
{
// Since we sorted the axes, we know that the first two are definitely X and Y.
// They are probably a single stick, so use angular position to determine buttons.
buttonstate = Joy_XYAxesToButtons(Axes[0].Value, axisval);
buttonstate = Joy_XYAxesToButtons(
(abs(Axes[0].Value) < Axes[0].DigitalThreshold) ? 0 : Axes[0].Value,
(abs(axisval) < info->DigitalThreshold) ? 0 : axisval
);
Joy_GenerateButtonEvents(info->ButtonValue, buttonstate, 4, KEY_JOYAXIS1PLUS);
}
info->ButtonValue = buttonstate;
@ -761,38 +776,54 @@ void FDInputJoystick::SetDefaultConfig()
{
unsigned i;
Multiplier = 1;
Multiplier = JOYSENSITIVITY_DEFAULT;
for (i = 0; i < Axes.Size(); ++i)
{
Axes[i].DeadZone = DEFAULT_DEADZONE;
Axes[i].Multiplier = 1;
Axes[i].DeadZone = JOYDEADZONE_DEFAULT;
Axes[i].Multiplier = JOYSENSITIVITY_DEFAULT;
Axes[i].GameAxis = JOYAXIS_None;
Axes[i].DigitalThreshold = JOYTHRESH_DEFAULT;
Axes[i].ResponseCurvePreset = JOYCURVE_DEFAULT;
Axes[i].ResponseCurve = JOYCURVE[JOYCURVE_DEFAULT];
}
// Triggers on a 360 controller have a much smaller deadzone.
if (Axes.Size() == 5 && Axes[4].Guid == GUID_ZAxis)
{
Axes[4].DeadZone = 30 / 256.f;
Axes[4].DigitalThreshold = JOYTHRESH_TRIGGER;
}
// Two axes? Horizontal is yaw and vertical is forward.
if (Axes.Size() == 2)
{
Axes[0].GameAxis = JOYAXIS_Yaw;
Axes[0].DigitalThreshold = JOYTHRESH_STICK_X;
Axes[1].GameAxis = JOYAXIS_Forward;
Axes[1].DigitalThreshold = JOYTHRESH_STICK_Y;
}
// Three axes? First two are movement, third is yaw.
else if (Axes.Size() >= 3)
{
Axes[0].GameAxis = JOYAXIS_Side;
Axes[0].DigitalThreshold = JOYTHRESH_STICK_X;
Axes[1].GameAxis = JOYAXIS_Forward;
Axes[1].DigitalThreshold = JOYTHRESH_STICK_Y;
Axes[2].GameAxis = JOYAXIS_Yaw;
Axes[2].DigitalThreshold = JOYTHRESH_STICK_X;
// Four axes? First two are movement, last two are looking around.
if (Axes.Size() >= 4)
{
Axes[3].GameAxis = JOYAXIS_Pitch; Axes[3].Multiplier = 0.75f;
Axes[3].GameAxis = JOYAXIS_Pitch;
// Axes[3].Multiplier = 0.75f;
Axes[3].DigitalThreshold = JOYTHRESH_STICK_Y;
// Five axes? Use the fifth one for moving up and down.
if (Axes.Size() >= 5)
{
Axes[4].GameAxis = JOYAXIS_Up;
Axes[4].DigitalThreshold = JOYTHRESH_STICK_Y;
}
}
}
@ -805,6 +836,8 @@ void FDInputJoystick::SetDefaultConfig()
Axes[i].DefaultDeadZone = Axes[i].DeadZone;
Axes[i].DefaultMultiplier = Axes[i].Multiplier;
Axes[i].DefaultGameAxis = Axes[i].GameAxis;
Axes[i].DefaultDigitalThreshold = Axes[i].DigitalThreshold;
Axes[i].DefaultResponseCurvePreset = Axes[i].ResponseCurvePreset;
}
}
@ -849,7 +882,7 @@ void FDInputJoystick::SetSensitivity(float scale)
bool FDInputJoystick::IsSensitivityDefault()
{
return Multiplier == 1;
return Multiplier == JOYSENSITIVITY_DEFAULT;
}
//===========================================================================
@ -923,6 +956,51 @@ float FDInputJoystick::GetAxisScale(int axis)
return Axes[axis].Multiplier;
}
//===========================================================================
//
// FDInputJoystick :: GetAxisDigitalThreshold
//
//===========================================================================
float FDInputJoystick::GetAxisDigitalThreshold(int axis)
{
if (unsigned(axis) >= Axes.Size())
{
return JOYTHRESH_DEFAULT;
}
return Axes[axis].DigitalThreshold;
}
//===========================================================================
//
// FDInputJoystick :: GetAxisResponseCurve
//
//===========================================================================
EJoyCurve FDInputJoystick::GetAxisResponseCurve(int axis)
{
if (unsigned(axis) >= Axes.Size())
{
return JOYCURVE_DEFAULT;
}
return Axes[axis].ResponseCurvePreset;
}
//===========================================================================
//
// FDInputJoystick :: GetAxisResponseCurvePoint
//
//===========================================================================
float FDInputJoystick::GetAxisResponseCurvePoint(int axis, int point)
{
if (unsigned(axis) >= Axes.Size() || unsigned(point) >= 4)
{
return 0;
}
return Axes[axis].ResponseCurve.pts[point];
}
//===========================================================================
//
// FDInputJoystick :: SetAxisDeadZone
@ -965,6 +1043,52 @@ void FDInputJoystick::SetAxisScale(int axis, float scale)
}
}
//===========================================================================
//
// FDInputJoystick :: SetAxisDigitalThreshold
//
//===========================================================================
void FDInputJoystick::SetAxisDigitalThreshold(int axis, float threshold)
{
if (unsigned(axis) < Axes.Size())
{
Axes[axis].DigitalThreshold = threshold;
}
}
//===========================================================================
//
// FDInputJoystick :: SetAxisResponseCurve
//
//===========================================================================
void FDInputJoystick::SetAxisResponseCurve(int axis, EJoyCurve preset)
{
if (unsigned(axis) < Axes.Size())
{
if (preset >= NUM_JOYCURVE || preset < JOYCURVE_CUSTOM) return;
Axes[axis].ResponseCurvePreset = preset;
if (preset == JOYCURVE_CUSTOM) return;
Axes[axis].ResponseCurve = JOYCURVE[preset];
}
}
//===========================================================================
//
// FDInputJoystick :: SetAxisResponseCurvePoint
//
//===========================================================================
void FDInputJoystick::SetAxisResponseCurvePoint(int axis, int point, float value)
{
if (unsigned(axis) < Axes.Size() && unsigned(point) < 4)
{
Axes[axis].ResponseCurvePreset = JOYCURVE_CUSTOM;
Axes[axis].ResponseCurve.pts[point] = value;
}
}
//===========================================================================
//
// FDInputJoystick :: IsAxisDeadZoneDefault
@ -995,6 +1119,36 @@ bool FDInputJoystick::IsAxisScaleDefault(int axis)
return true;
}
//===========================================================================
//
// FDInputJoystick :: IsAxisDigitalThresholdDefault
//
//===========================================================================
bool FDInputJoystick::IsAxisDigitalThresholdDefault(int axis)
{
if (unsigned(axis) < Axes.Size())
{
return Axes[axis].DigitalThreshold == Axes[axis].DefaultDigitalThreshold;
}
return true;
}
//===========================================================================
//
// FDInputJoystick :: IsAxisResponseCurveDefault
//
//===========================================================================
bool FDInputJoystick::IsAxisResponseCurveDefault(int axis)
{
if (unsigned(axis) < Axes.Size())
{
return Axes[axis].ResponseCurvePreset == Axes[axis].DefaultResponseCurvePreset;
}
return true;
}
//===========================================================================
//
// FDInputJoystick :: GetEnabled

View file

@ -48,7 +48,6 @@
// MACROS ------------------------------------------------------------------
#define DEFAULT_DEADZONE 0.25f
#define STATUS_SWITCH_TIME 3
#define VID_PLAY_COM 0x0b43
@ -104,15 +103,23 @@ public:
EJoyAxis GetAxisMap(int axis);
const char *GetAxisName(int axis);
float GetAxisScale(int axis);
float GetAxisDigitalThreshold(int axis);
EJoyCurve GetAxisResponseCurve(int axis);
float GetAxisResponseCurvePoint(int axis, int point);
void SetAxisDeadZone(int axis, float deadzone);
void SetAxisMap(int axis, EJoyAxis gameaxis);
void SetAxisScale(int axis, float scale);
void SetAxisDigitalThreshold(int axis, float threshold);
void SetAxisResponseCurve(int axis, EJoyCurve preset);
void SetAxisResponseCurvePoint(int axis, int point, float value);
bool IsSensitivityDefault();
bool IsAxisDeadZoneDefault(int axis);
bool IsAxisMapDefault(int axis);
bool IsAxisScaleDefault(int axis);
bool IsAxisDigitalThresholdDefault(int axis);
bool IsAxisResponseCurveDefault(int axis);
bool GetEnabled();
void SetEnabled(bool enabled);
@ -130,6 +137,9 @@ protected:
float Value;
float DeadZone;
float Multiplier;
float DigitalThreshold;
EJoyCurve ResponseCurvePreset;
CubicBezier ResponseCurve;
EJoyAxis GameAxis;
uint8_t ButtonValue;
};
@ -137,6 +147,8 @@ protected:
{
EJoyAxis GameAxis;
float Multiplier;
float DigitalThreshold;
EJoyCurve ResponseCurvePreset;
};
enum
{
@ -357,11 +369,11 @@ static const char *AxisNames[] =
FRawPS2Controller::DefaultAxisConfig FRawPS2Controller::DefaultAxes[NUM_AXES] =
{
// Game axis, multiplier
{ JOYAXIS_Side, 1 }, // ThumbLX
{ JOYAXIS_Forward, 1 }, // ThumbLY
{ JOYAXIS_Yaw, 1 }, // ThumbRX
{ JOYAXIS_Pitch, 0.75 }, // ThumbRY
// Game axis, multiplier, digital threshold, response curve A, response curve B
{ JOYAXIS_Side, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_X, JOYCURVE_DEFAULT }, // ThumbLX
{ JOYAXIS_Forward, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_Y, JOYCURVE_DEFAULT }, // ThumbLY
{ JOYAXIS_Yaw, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_X, JOYCURVE_DEFAULT }, // ThumbRX
{ JOYAXIS_Pitch, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_Y, JOYCURVE_DEFAULT }, // ThumbRY
};
// CODE --------------------------------------------------------------------
@ -552,11 +564,16 @@ void FRawPS2Controller::ProcessThumbstick(int value1, AxisInfo *axis1, int value
axisval2 = value2 * (2.0 / 255) - 1.0;
axisval1 = Joy_RemoveDeadZone(axisval1, axis1->DeadZone, NULL);
axisval2 = Joy_RemoveDeadZone(axisval2, axis2->DeadZone, NULL);
axisval1 = Joy_ApplyResponseCurveBezier(axis1->ResponseCurve, axisval1);
axisval2 = Joy_ApplyResponseCurveBezier(axis2->ResponseCurve, axisval2);
axis1->Value = float(axisval1);
axis2->Value = float(axisval2);
// We store all four buttons in the first axis and ignore the second.
buttonstate = Joy_XYAxesToButtons(axisval1, axisval2);
buttonstate = Joy_XYAxesToButtons(
(abs(axisval1) < axis1->DigitalThreshold) ? 0 : axisval1,
(abs(axisval2) < axis2->DigitalThreshold) ? 0 : axisval2
);
Joy_GenerateButtonEvents(axis1->ButtonValue, buttonstate, 4, base);
axis1->ButtonValue = buttonstate;
}
@ -644,10 +661,10 @@ void FRawPS2Controller::AddAxes(float axes[NUM_JOYAXIS])
void FRawPS2Controller::SetDefaultConfig()
{
Multiplier = 1;
Multiplier = JOYSENSITIVITY_DEFAULT;
for (int i = 0; i < NUM_AXES; ++i)
{
Axes[i].DeadZone = DEFAULT_DEADZONE;
Axes[i].DeadZone = JOYDEADZONE_DEFAULT;
Axes[i].GameAxis = DefaultAxes[i].GameAxis;
Axes[i].Multiplier = DefaultAxes[i].Multiplier;
}
@ -712,7 +729,7 @@ void FRawPS2Controller::SetSensitivity(float scale)
bool FRawPS2Controller::IsSensitivityDefault()
{
return Multiplier == 1;
return Multiplier == JOYSENSITIVITY_DEFAULT;
}
//==========================================================================
@ -786,6 +803,51 @@ float FRawPS2Controller::GetAxisScale(int axis)
return 0;
}
//==========================================================================
//
// FRawPS2Controller :: GetAxisDigitalThreshold
//
//==========================================================================
float FRawPS2Controller::GetAxisDigitalThreshold(int axis)
{
if (unsigned(axis) < NUM_AXES)
{
return Axes[axis].DigitalThreshold;
}
return JOYTHRESH_DEFAULT;
}
//==========================================================================
//
// FRawPS2Controller :: GetAxisResponseCurve
//
//==========================================================================
EJoyCurve FRawPS2Controller::GetAxisResponseCurve(int axis)
{
if (unsigned(axis) < NUM_AXES)
{
return Axes[axis].ResponseCurvePreset;
}
return JOYCURVE_DEFAULT;
}
//==========================================================================
//
// FRawPS2Controller :: GetAxisResponseCurvePoint
//
//==========================================================================
float FRawPS2Controller::GetAxisResponseCurvePoint(int axis, int point)
{
if (unsigned(axis) < NUM_AXES && unsigned(point) < 4)
{
return Axes[axis].ResponseCurve.pts[point];
}
return 0;
}
//==========================================================================
//
// FRawPS2Controller :: SetAxisDeadZone
@ -828,6 +890,52 @@ void FRawPS2Controller::SetAxisScale(int axis, float scale)
}
}
//==========================================================================
//
// FRawPS2Controller :: SetAxisDigitalThreshold
//
//==========================================================================
void FRawPS2Controller::SetAxisDigitalThreshold(int axis, float threshold)
{
if (unsigned(axis) < NUM_AXES)
{
Axes[axis].DigitalThreshold = threshold;
}
}
//==========================================================================
//
// FRawPS2Controller :: SetAxisResponseCurve
//
//==========================================================================
void FRawPS2Controller::SetAxisResponseCurve(int axis, EJoyCurve preset)
{
if (unsigned(axis) < NUM_AXES)
{
if (preset >= NUM_JOYCURVE || preset < JOYCURVE_CUSTOM) return;
Axes[axis].ResponseCurvePreset = preset;
if (preset == JOYCURVE_CUSTOM) return;
Axes[axis].ResponseCurve = JOYCURVE[preset];
}
}
//==========================================================================
//
// FRawPS2Controller :: SetAxisResponseCurvePoint
//
//==========================================================================
void FRawPS2Controller::SetAxisResponseCurvePoint(int axis, int point, float value)
{
if (unsigned(axis) < NUM_AXES && unsigned(point) < 4)
{
Axes[axis].ResponseCurvePreset = JOYCURVE_CUSTOM;
Axes[axis].ResponseCurve.pts[point] = value;
}
}
//===========================================================================
//
// FRawPS2Controller :: IsAxisDeadZoneDefault
@ -838,7 +946,7 @@ bool FRawPS2Controller::IsAxisDeadZoneDefault(int axis)
{
if (unsigned(axis) < NUM_AXES)
{
return Axes[axis].DeadZone == DEFAULT_DEADZONE;
return Axes[axis].DeadZone == JOYDEADZONE_DEFAULT;
}
return true;
}
@ -858,6 +966,36 @@ bool FRawPS2Controller::IsAxisScaleDefault(int axis)
return true;
}
//===========================================================================
//
// FRawPS2Controller :: IsAxisDigitalThresholdDefault
//
//===========================================================================
bool FRawPS2Controller::IsAxisDigitalThresholdDefault(int axis)
{
if (unsigned(axis) < NUM_AXES)
{
return Axes[axis].DigitalThreshold == DefaultAxes[axis].DigitalThreshold;
}
return true;
}
//===========================================================================
//
// FRawPS2Controller :: IsAxisResponseCurveDefault
//
//===========================================================================
bool FRawPS2Controller::IsAxisResponseCurveDefault(int axis)
{
if (unsigned(axis) < NUM_AXES)
{
return Axes[axis].ResponseCurvePreset == DefaultAxes[axis].ResponseCurvePreset;
}
return true;
}
//===========================================================================
//
// FRawPS2Controller :: GetEnabled

View file

@ -94,15 +94,23 @@ public:
EJoyAxis GetAxisMap(int axis);
const char *GetAxisName(int axis);
float GetAxisScale(int axis);
float GetAxisDigitalThreshold(int axis);
EJoyCurve GetAxisResponseCurve(int axis);
float GetAxisResponseCurvePoint(int axis, int point);
void SetAxisDeadZone(int axis, float deadzone);
void SetAxisMap(int axis, EJoyAxis gameaxis);
void SetAxisScale(int axis, float scale);
void SetAxisDigitalThreshold(int axis, float threshold);
void SetAxisResponseCurve(int axis, EJoyCurve preset);
void SetAxisResponseCurvePoint(int axis, int point, float value);
bool IsSensitivityDefault();
bool IsAxisDeadZoneDefault(int axis);
bool IsAxisMapDefault(int axis);
bool IsAxisScaleDefault(int axis);
bool IsAxisDigitalThresholdDefault(int axis);
bool IsAxisResponseCurveDefault(int axis);
bool GetEnabled();
void SetEnabled(bool enabled);
@ -122,12 +130,17 @@ protected:
float Multiplier;
EJoyAxis GameAxis;
uint8_t ButtonValue;
float DigitalThreshold;
EJoyCurve ResponseCurvePreset;
CubicBezier ResponseCurve;
};
struct DefaultAxisConfig
{
float DeadZone;
EJoyAxis GameAxis;
float Multiplier;
float DigitalThreshold;
EJoyCurve ResponseCurvePreset;
};
enum
{
@ -211,13 +224,13 @@ static const char *AxisNames[] =
FXInputController::DefaultAxisConfig FXInputController::DefaultAxes[NUM_AXES] =
{
// Dead zone, game axis, multiplier
{ XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE / 32768.f, JOYAXIS_Side, 1 }, // ThumbLX
{ XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE / 32768.f, JOYAXIS_Forward, 1 }, // ThumbLY
{ XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE / 32768.f, JOYAXIS_Yaw, 1 }, // ThumbRX
{ XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE / 32768.f, JOYAXIS_Pitch, 0.75 }, // ThumbRY
{ XINPUT_GAMEPAD_TRIGGER_THRESHOLD / 256.f, JOYAXIS_None, 0 }, // LeftTrigger
{ XINPUT_GAMEPAD_TRIGGER_THRESHOLD / 256.f, JOYAXIS_None, 0 } // RightTrigger
// Dead zone, game axis, multiplier, digitalthreshold, curveA, curveB
{ XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE / 32768.f, JOYAXIS_Side, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_X, JOYCURVE_DEFAULT }, // ThumbLX
{ XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE / 32768.f, JOYAXIS_Forward, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_Y, JOYCURVE_DEFAULT }, // ThumbLY
{ XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE / 32768.f, JOYAXIS_Yaw, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_X, JOYCURVE_DEFAULT }, // ThumbRX
{ XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE / 32768.f, JOYAXIS_Pitch, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_Y, JOYCURVE_DEFAULT }, // ThumbRY
{ XINPUT_GAMEPAD_TRIGGER_THRESHOLD / 256.f, JOYAXIS_None, JOYSENSITIVITY_DEFAULT, JOYTHRESH_TRIGGER, JOYCURVE_DEFAULT }, // LeftTrigger
{ XINPUT_GAMEPAD_TRIGGER_THRESHOLD / 256.f, JOYAXIS_None, JOYSENSITIVITY_DEFAULT, JOYTHRESH_TRIGGER, JOYCURVE_DEFAULT } // RightTrigger
};
// CODE --------------------------------------------------------------------
@ -327,11 +340,16 @@ void FXInputController::ProcessThumbstick(int value1, AxisInfo *axis1,
axisval2 = (value2 - SHRT_MIN) * 2.0 / 65536 - 1.0;
axisval1 = Joy_RemoveDeadZone(axisval1, axis1->DeadZone, NULL);
axisval2 = Joy_RemoveDeadZone(axisval2, axis2->DeadZone, NULL);
axisval1 = Joy_ApplyResponseCurveBezier(axis1->ResponseCurve, axisval1);
axisval2 = Joy_ApplyResponseCurveBezier(axis2->ResponseCurve, axisval2);
axis1->Value = float(axisval1);
axis2->Value = float(axisval2);
// We store all four buttons in the first axis and ignore the second.
buttonstate = Joy_XYAxesToButtons(axisval1, axisval2);
buttonstate = Joy_XYAxesToButtons(
abs(axisval1) < axis1->DigitalThreshold ? 0: axisval1,
abs(axisval2) < axis2->DigitalThreshold ? 0: axisval2
);
Joy_GenerateButtonEvents(axis1->ButtonValue, buttonstate, 4, base);
axis1->ButtonValue = buttonstate;
}
@ -351,6 +369,11 @@ void FXInputController::ProcessTrigger(int value, AxisInfo *axis, int base)
double axisval;
axisval = Joy_RemoveDeadZone(value / 256.0, axis->DeadZone, &buttonstate);
axisval = Joy_ApplyResponseCurveBezier(axis->ResponseCurve, axisval);
// TODO: probably just put this into Joy_RemoveDeadZone
if (abs(axisval) < axis->DigitalThreshold) buttonstate = 0;
Joy_GenerateButtonEvents(axis->ButtonValue, buttonstate, 1, base);
axis->ButtonValue = buttonstate;
axis->Value = float(axisval);
@ -431,12 +454,15 @@ void FXInputController::AddAxes(float axes[NUM_JOYAXIS])
void FXInputController::SetDefaultConfig()
{
Multiplier = 1;
Multiplier = JOYSENSITIVITY_DEFAULT;
for (int i = 0; i < NUM_AXES; ++i)
{
Axes[i].DeadZone = DefaultAxes[i].DeadZone;
Axes[i].GameAxis = DefaultAxes[i].GameAxis;
Axes[i].Multiplier = DefaultAxes[i].Multiplier;
Axes[i].DigitalThreshold = DefaultAxes[i].DigitalThreshold;
Axes[i].ResponseCurve = JOYCURVE[DefaultAxes[i].ResponseCurvePreset];
Axes[i].ResponseCurvePreset = DefaultAxes[i].ResponseCurvePreset;
}
}
@ -494,7 +520,7 @@ void FXInputController::SetSensitivity(float scale)
bool FXInputController::IsSensitivityDefault()
{
return Multiplier == 1;
return Multiplier == JOYSENSITIVITY_DEFAULT;
}
//==========================================================================
@ -565,6 +591,51 @@ float FXInputController::GetAxisScale(int axis)
{
return Axes[axis].Multiplier;
}
return JOYSENSITIVITY_DEFAULT;
}
//==========================================================================
//
// FXInputController :: GetAxisDigitalThreshold
//
//==========================================================================
float FXInputController::GetAxisDigitalThreshold(int axis)
{
if (unsigned(axis) < NUM_AXES)
{
return Axes[axis].DigitalThreshold;
}
return JOYTHRESH_DEFAULT;
}
//==========================================================================
//
// FXInputController :: GetAxisResponseCurve
//
//==========================================================================
EJoyCurve FXInputController::GetAxisResponseCurve(int axis)
{
if (unsigned(axis) < NUM_AXES)
{
return Axes[axis].ResponseCurvePreset;
}
return JOYCURVE_DEFAULT;
}
//==========================================================================
//
// FXInputController :: GetAxisResponseCurvePoint
//
//==========================================================================
float FXInputController::GetAxisResponseCurvePoint(int axis, int point)
{
if (unsigned(axis) < NUM_AXES && unsigned(point) < 4)
{
return Axes[axis].ResponseCurve.pts[point];
}
return 0;
}
@ -610,6 +681,52 @@ void FXInputController::SetAxisScale(int axis, float scale)
}
}
//==========================================================================
//
// FXInputController :: SetAxisDigitalThreshold
//
//==========================================================================
void FXInputController::SetAxisDigitalThreshold(int axis, float threshold)
{
if (unsigned(axis) < NUM_AXES)
{
Axes[axis].DigitalThreshold = threshold;
}
}
//==========================================================================
//
// FXInputController :: SetAxisResponseCurve
//
//==========================================================================
void FXInputController::SetAxisResponseCurve(int axis, EJoyCurve preset)
{
if (unsigned(axis) < NUM_AXES)
{
if (preset >= NUM_JOYCURVE || preset < JOYCURVE_CUSTOM) return;
Axes[axis].ResponseCurvePreset = preset;
if (preset == JOYCURVE_CUSTOM) return;
Axes[axis].ResponseCurve = JOYCURVE[preset];
}
}
//==========================================================================
//
// FXInputController :: SetAxisResponseCurvePoint
//
//==========================================================================
void FXInputController::SetAxisResponseCurvePoint(int axis, int point, float value)
{
if (unsigned(axis) < NUM_AXES && unsigned(point) < 4)
{
Axes[axis].ResponseCurvePreset = JOYCURVE_CUSTOM;
Axes[axis].ResponseCurve.pts[point] = value;
}
}
//===========================================================================
//
// FXInputController :: IsAxisDeadZoneDefault
@ -640,6 +757,36 @@ bool FXInputController::IsAxisScaleDefault(int axis)
return true;
}
//===========================================================================
//
// FXInputController :: IsAxisDigitalThresholdDefault
//
//===========================================================================
bool FXInputController::IsAxisDigitalThresholdDefault(int axis)
{
if (unsigned(axis) < NUM_AXES)
{
return Axes[axis].DigitalThreshold == DefaultAxes[axis].DigitalThreshold;
}
return true;
}
//===========================================================================
//
// FXInputController :: IsAxisResponseCurveDefault
//
//===========================================================================
bool FXInputController::IsAxisResponseCurveDefault(int axis)
{
if (unsigned(axis) < NUM_AXES)
{
return Axes[axis].ResponseCurvePreset == DefaultAxes[axis].ResponseCurvePreset;
}
return true;
}
//===========================================================================
//
// FXInputController :: GetEnabled

View file

@ -837,6 +837,15 @@ OptionMenu "JoystickOptions" protected
Title "$JOYMNU_OPTIONS"
}
OptionValue "JoyAxisCurveNames"
{
-1, "$OPTVAL_CUSTOM"
0, "$OPTVAL_DEFAULT"
1, "$OPTVAL_LINEAR"
2, "$OPTVAL_QUADRATIC"
3, "$OPTVAL_CUBIC"
}
OptionValue "JoyAxisMapNames"
{
-1, "$OPTVAL_NONE"

View file

@ -130,7 +130,94 @@ class OptionMenuSliderJoyDeadZone : OptionMenuSliderBase
//=============================================================================
//
//
//
//
//=============================================================================
class OptionMenuSliderJoyDigitalThreshold : OptionMenuSliderBase
{
int mAxis;
int mNeg;
JoystickConfig mJoy;
OptionMenuSliderJoyDigitalThreshold Init(String label, int axis, double min, double max, double step, int showval, JoystickConfig joy)
{
Super.Init(label, min, max, step, showval);
mAxis = axis;
mNeg = 1;
mJoy = joy;
return self;
}
override double GetSliderValue()
{
double d = mJoy.GetAxisDigitalThreshold(mAxis);
mNeg = d < 0? -1:1;
return d;
}
override void SetSliderValue(double val)
{
mJoy.SetAxisDigitalThreshold(mAxis, val * mNeg);
}
}
//=============================================================================
//
//
//
//=============================================================================
class OptionMenuItemJoyCurve : OptionMenuItemOptionBase
{
int mAxis;
JoystickConfig mJoy;
OptionMenuItemJoyCurve Init(String label, int axis, Name values, int center, JoystickConfig joy)
{
Super.Init(label, 'none', values, null, center);
mAxis = axis;
mJoy = joy;
return self;
}
override int GetSelection()
{
double f = mJoy.GetAxisResponseCurve(mAxis);
let opt = OptionValues.GetCount(mValues);
if (opt > 0)
{
// Map from joystick curve to menu selection.
for(int i = 0; i < opt; i++)
{
if (f ~== OptionValues.GetValue(mValues, i))
{
return i;
}
}
}
return JoystickConfig.JOYCURVE_CUSTOM;
}
override void SetSelection(int selection)
{
let opt = OptionValues.GetCount(mValues);
// Map from menu selection to joystick curve.
if (opt == 0 || selection >= opt)
{
selection = JoystickConfig.JOYCURVE_DEFAULT;
}
else
{
selection = int(OptionValues.GetValue(mValues, selection));
}
mJoy.setAxisResponseCurve(mAxis, selection);
}
}
//=============================================================================
//
//
//
//=============================================================================
@ -340,8 +427,25 @@ class OptionMenuItemJoyConfigMenu : OptionMenuItemSubmenu
opt.mItems.Push(it);
it = new("OptionMenuItemInverter").Init("$JOYMNU_INVERT", i, false, joy);
opt.mItems.Push(it);
it = new("OptionMenuItemJoyCurve").Init("$JOYMNU_CURVE", i, "JoyAxisCurveNames", false, joy);
opt.mItems.Push(it);
// // is there a way to do something like this?
// // add extra options if the selected value is something specific?
// if (joy.GetAxisResponseCurve(i) == JoystickConfig.JOYCURVE_CUSTOM)
// {
// it = new("OptionMenuItemStaticText").Init("$JOYMNU_CURVE_X1", false);
// opt.mItems.Push(it);
// it = new("OptionMenuItemStaticText").Init("$JOYMNU_CURVE_Y1", false);
// opt.mItems.Push(it);
// it = new("OptionMenuItemStaticText").Init("$JOYMNU_CURVE_X2", false);
// opt.mItems.Push(it);
// it = new("OptionMenuItemStaticText").Init("$JOYMNU_CURVE_Y2", false);
// opt.mItems.Push(it);
// }
it = new("OptionMenuSliderJoyDeadZone").Init("$JOYMNU_DEADZONE", i, 0, 0.9, 0.05, 3, joy);
opt.mItems.Push(it);
it = new("OptionMenuSliderJoyDigitalThreshold").Init("$JOYMNU_THRESHOLD", i, 0, 0.9, 0.05, 3, joy);
opt.mItems.Push(it);
}
}
else

View file

@ -68,6 +68,16 @@ struct JoystickConfig native version("2.4")
NUM_JOYAXIS,
};
enum EJoyCurve {
JOYCURVE_CUSTOM = -1,
JOYCURVE_DEFAULT,
JOYCURVE_LINEAR,
JOYCURVE_QUADRATIC,
JOYCURVE_CUBIC,
NUM_JOYCURVE
};
native float GetSensitivity();
native void SetSensitivity(float scale);
@ -77,6 +87,15 @@ struct JoystickConfig native version("2.4")
native float GetAxisDeadZone(int axis);
native void SetAxisDeadZone(int axis, float zone);
native float GetAxisDigitalThreshold(int axis);
native void SetAxisDigitalThreshold(int axis, float thresh);
native int GetAxisResponseCurve(int axis);
native void SetAxisResponseCurve(int axis, int preset);
native float GetAxisResponseCurvePoint(int axis, int point);
native void SetAxisResponseCurvePoint(int axis, int point, float value);
native int GetAxisMap(int axis);
native void SetAxisMap(int axis, int gameaxis);