Merge GameController support from Quadrilateral Cowboy

https://github.com/blendogames/quadrilateralcowboy

pretty much as it is there, with only minimal changes required to work
with dhewm3
This commit is contained in:
Daniel Gibson 2024-01-07 01:26:45 +01:00
parent 37b0551ba8
commit ab53e9aa13
7 changed files with 604 additions and 70 deletions

View file

@ -2919,7 +2919,7 @@ void idCommonLocal::Init( int argc, char **argv ) {
#endif
#endif
if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_JOYSTICK)) // init joystick to work around SDL 2.0.9 bug #4391
if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER)) // init joystick to work around SDL 2.0.9 bug #4391
Sys_Error("Error while initializing SDL: %s", SDL_GetError());
Sys_InitThreads();

View file

@ -124,23 +124,24 @@ static const keyname_t keynames[] =
{"JOY13", K_JOY13, "#str_07074"},
{"JOY14", K_JOY14, "#str_07075"},
{"JOY15", K_JOY15, "#str_07076"},
{"JOY16", K_JOY16, "#str_07077"},
{"JOY17", K_JOY17, "#str_07078"},
{"JOY18", K_JOY18, "#str_07079"},
{"JOY19", K_JOY19, "#str_07080"},
{"JOY20", K_JOY20, "#str_07081"},
{"JOY21", K_JOY21, "#str_07082"},
{"JOY22", K_JOY22, "#str_07083"},
{"JOY23", K_JOY23, "#str_07084"},
{"JOY24", K_JOY24, "#str_07085"},
{"JOY25", K_JOY25, "#str_07086"},
{"JOY26", K_JOY26, "#str_07087"},
{"JOY27", K_JOY27, "#str_07088"},
{"JOY28", K_JOY28, "#str_07089"},
{"JOY29", K_JOY29, "#str_07090"},
{"JOY30", K_JOY30, "#str_07091"},
{"JOY31", K_JOY31, "#str_07092"},
{"JOY32", K_JOY32, "#str_07093"},
{"JOY_STICK1_UP", K_JOY_STICK1_UP, "JOY_STICK1_UP"},
{"JOY_STICK1_DOWN", K_JOY_STICK1_DOWN, "JOY_STICK1_DOWN"},
{"JOY_STICK1_LEFT", K_JOY_STICK1_LEFT, "JOY_STICK1_LEFT"},
{"JOY_STICK1_RIGHT", K_JOY_STICK1_RIGHT, "JOY_STICK1_RIGHT"},
{"JOY_STICK2_UP", K_JOY_STICK2_UP, "JOY_STICK2_UP"},
{"JOY_STICK2_DOWN", K_JOY_STICK2_DOWN, "JOY_STICK2_DOWN"},
{"JOY_STICK2_LEFT", K_JOY_STICK2_LEFT, "JOY_STICK2_LEFT"},
{"JOY_STICK2_RIGHT", K_JOY_STICK2_RIGHT, "JOY_STICK2_RIGHT"},
{"JOY_TRIGGER1", K_JOY_TRIGGER1, "JOY_TRIGGER1"},
{"JOY_TRIGGER2", K_JOY_TRIGGER2, "JOY_TRIGGER2"},
{"JOY_DPAD_UP", K_JOY_DPAD_UP, "JOY_DPAD_UP"},
{"JOY_DPAD_DOWN", K_JOY_DPAD_DOWN, "JOY_DPAD_DOWN"},
{"JOY_DPAD_LEFT", K_JOY_DPAD_LEFT, "JOY_DPAD_LEFT"},
{"JOY_DPAD_RIGHT", K_JOY_DPAD_RIGHT, "JOY_DPAD_RIGHT"},
{"AUX1", K_AUX1, "#str_07094"},
{"AUX2", K_AUX2, "#str_07095"},

View file

@ -138,6 +138,10 @@ typedef enum {
K_MWHEELDOWN = 195,
K_MWHEELUP,
//------------------------
// K_JOY codes must be contiguous, too
//------------------------
K_JOY1 = 197,
K_JOY2,
K_JOY3,
@ -154,23 +158,26 @@ typedef enum {
K_JOY14,
K_JOY15,
K_JOY16,
K_JOY17,
K_JOY18,
K_JOY19,
K_JOY20,
K_JOY21,
K_JOY22,
K_JOY23,
K_JOY24,
K_JOY25,
K_JOY26,
K_JOY27,
K_GRAVE_A = 224, // lowercase a with grave accent
K_JOY28,
K_JOY29,
K_JOY30,
K_JOY31,
K_JOY32,
K_JOY_STICK1_UP,
K_JOY_STICK1_DOWN,
K_JOY_STICK1_LEFT,
K_JOY_STICK1_RIGHT,
K_JOY_STICK2_UP,
K_JOY_STICK2_DOWN,
K_JOY_STICK2_LEFT,
K_JOY_STICK2_RIGHT,
K_JOY_TRIGGER1,
K_JOY_TRIGGER2,
K_JOY_DPAD_UP,
K_JOY_DPAD_DOWN,
K_JOY_DPAD_LEFT,
K_JOY_DPAD_RIGHT,
K_GRAVE_A = 229, // lowercase a with grave accent FIXME: used to be 224; this probably isn't used anyway
K_AUX1 = 230,
K_CEDILLA_C = 231, // lowercase c with Cedilla

View file

@ -350,6 +350,8 @@ private:
bool Inhibited( void );
void AdjustAngles( void );
void KeyMove( void );
void CircleToSquare( float & axis_x, float & axis_y ) const;
void HandleJoystickAxis( int keyNum, float unclampedValue, float threshold, bool positive );
void JoystickMove( void );
void MouseMove( void );
void CmdButtons( void );
@ -384,7 +386,14 @@ private:
bool mouseDown;
int mouseDx, mouseDy; // added to by mouse events
int joystickAxis[MAX_JOYSTICK_AXIS]; // set by joystick events
float joystickAxis[MAX_JOYSTICK_AXIS]; // set by joystick events
int pollTime;
int lastPollTime;
float lastLookValuePitch;
float lastLookValueYaw;
bool heldJump; // TODO: ???
static idCVar in_yawSpeed;
static idCVar in_pitchSpeed;
@ -419,6 +428,24 @@ idCVar idUsercmdGenLocal::m_smooth( "m_smooth", "1", CVAR_SYSTEM | CVAR_ARCHIVE
idCVar idUsercmdGenLocal::m_strafeSmooth( "m_strafeSmooth", "4", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_INTEGER, "number of samples blended for mouse moving", 1, 8, idCmdSystem::ArgCompletion_Integer<1,8> );
idCVar idUsercmdGenLocal::m_showMouseRate( "m_showMouseRate", "0", CVAR_SYSTEM | CVAR_BOOL, "shows mouse movement" );
idCVar joy_mergedThreshold( "joy_mergedThreshold", "1", CVAR_BOOL | CVAR_ARCHIVE, "If the thresholds aren't merged, you drift more off center" );
idCVar joy_newCode( "joy_newCode", "0", CVAR_BOOL | CVAR_ARCHIVE, "Use the new codepath" );
idCVar joy_triggerThreshold( "joy_triggerThreshold", "0.05", CVAR_FLOAT | CVAR_ARCHIVE, "how far the joystick triggers have to be pressed before they register as down" );
idCVar joy_deadZone( "joy_deadZone", "0.4", CVAR_FLOAT | CVAR_ARCHIVE, "specifies how large the dead-zone is on the joystick" );
idCVar joy_range( "joy_range", "1.0", CVAR_FLOAT | CVAR_ARCHIVE, "allow full range to be mapped to a smaller offset" );
idCVar joy_gammaLook( "joy_gammaLook", "1", CVAR_INTEGER | CVAR_ARCHIVE, "use a log curve instead of a power curve for movement" );
idCVar joy_powerScale( "joy_powerScale", "2", CVAR_FLOAT | CVAR_ARCHIVE, "Raise joystick values to this power" );
idCVar joy_pitchSpeed( "joy_pitchSpeed", "130", CVAR_ARCHIVE | CVAR_FLOAT, "pitch speed when pressing up or down on the joystick", 60, 600 );
idCVar joy_yawSpeed( "joy_yawSpeed", "240", CVAR_ARCHIVE | CVAR_FLOAT, "pitch speed when pressing left or right on the joystick", 60, 600 );
// these were a bad idea!
idCVar joy_dampenLook( "joy_dampenLook", "1", CVAR_BOOL | CVAR_ARCHIVE, "Do not allow full acceleration on look" );
idCVar joy_deltaPerMSLook( "joy_deltaPerMSLook", "0.003", CVAR_FLOAT | CVAR_ARCHIVE, "Max amount to be added on look per MS" );
idCVar in_useJoystick( "in_useJoystick", "1", CVAR_ARCHIVE | CVAR_BOOL, "enables/disables the gamepad for PC use" );
idCVar in_invertLook( "in_invertLook", "0", CVAR_ARCHIVE | CVAR_BOOL, "inverts the look controls so the forward looks up (flight controls) - the proper way to play games!" );
idCVar in_mouseInvertLook( "in_mouseInvertLook", "0", CVAR_ARCHIVE | CVAR_BOOL, "inverts the look controls so the forward looks up (flight controls) - the proper way to play games!" );
static idUsercmdGenLocal localUsercmdGen;
idUsercmdGen *usercmdGen = &localUsercmdGen;
@ -573,9 +600,17 @@ void idUsercmdGenLocal::KeyMove( void ) {
forward += KEY_MOVESPEED * ButtonState( UB_FORWARD );
forward -= KEY_MOVESPEED * ButtonState( UB_BACK );
cmd.forwardmove = idMath::ClampChar( forward );
cmd.rightmove = idMath::ClampChar( side );
cmd.upmove = idMath::ClampChar( up );
// only set each movement variable if its unset at this point.
// NOTE: joystick input happens before this.
if (cmd.forwardmove == 0) {
cmd.forwardmove = idMath::ClampChar( forward );
}
if (cmd.rightmove == 0) {
cmd.rightmove = idMath::ClampChar( side );
}
if (cmd.upmove == 0) {
cmd.upmove = idMath::ClampChar( up );
}
}
/*
@ -672,29 +707,199 @@ void idUsercmdGenLocal::MouseMove( void ) {
}
}
/*
========================
idUsercmdGenLocal::CircleToSquare
========================
*/
void idUsercmdGenLocal::CircleToSquare( float & axis_x, float & axis_y ) const {
// bring everything in the first quadrant
bool flip_x = false;
if ( axis_x < 0.0f ) {
flip_x = true;
axis_x *= -1.0f;
}
bool flip_y = false;
if ( axis_y < 0.0f ) {
flip_y = true;
axis_y *= -1.0f;
}
// swap the two axes so we project against the vertical line X = 1
bool swap = false;
if ( axis_y > axis_x ) {
float tmp = axis_x;
axis_x = axis_y;
axis_y = tmp;
swap = true;
}
if ( axis_x < 0.001f ) {
// on one of the axes where no correction is needed
return;
}
// length (max 1.0f at the unit circle)
float len = idMath::Sqrt( axis_x * axis_x + axis_y * axis_y );
if ( len > 1.0f ) {
len = 1.0f;
}
// thales
float axis_y_us = axis_y / axis_x;
// use a power curve to shift the correction to happen closer to the unit circle
float correctionRatio = Square( len );
axis_x += correctionRatio * ( len - axis_x );
axis_y += correctionRatio * ( axis_y_us - axis_y );
// go back through the symmetries
if ( swap ) {
float tmp = axis_x;
axis_x = axis_y;
axis_y = tmp;
}
if ( flip_x ) {
axis_x *= -1.0f;
}
if ( flip_y ) {
axis_y *= -1.0f;
}
}
/*
========================
idUsercmdGenLocal::HandleJoystickAxis
========================
*/
void idUsercmdGenLocal::HandleJoystickAxis( int keyNum, float unclampedValue, float threshold, bool positive ) {
if ( ( unclampedValue > 0.0f ) && !positive ) {
return;
}
if ( ( unclampedValue < 0.0f ) && positive ) {
return;
}
float value = 0.0f;
bool pressed = false;
if ( unclampedValue > threshold ) {
value = idMath::Fabs( ( unclampedValue - threshold ) / ( 1.0f - threshold ) );
pressed = true;
} else if ( unclampedValue < -threshold ) {
value = idMath::Fabs( ( unclampedValue + threshold ) / ( 1.0f - threshold ) );
pressed = true;
}
int action = idKeyInput::GetUsercmdAction( keyNum );
if ( action >= UB_ATTACK ) {
Key( keyNum, pressed );
return;
}
if ( !pressed ) {
return;
}
float lookValue = 0.0f;
if ( joy_gammaLook.GetBool() ) {
lookValue = idMath::Pow( 1.04712854805f, value * 100.0f ) * 0.01f;
} else {
lookValue = idMath::Pow( value, joy_powerScale.GetFloat() );
}
#if 0 // TODO: aim assist maybe.
idGame * game = common->Game();
if ( game != NULL ) {
lookValue *= game->GetAimAssistSensitivity();
}
#endif
switch ( action ) {
case UB_FORWARD: {
float move = (float)cmd.forwardmove + ( KEY_MOVESPEED * value );
cmd.forwardmove = idMath::ClampChar( idMath::Ftoi( move ) );
break;
}
case UB_BACK: {
float move = (float)cmd.forwardmove - ( KEY_MOVESPEED * value );
cmd.forwardmove = idMath::ClampChar( idMath::Ftoi( move ) );
break;
}
case UB_MOVELEFT: {
float move = (float)cmd.rightmove - ( KEY_MOVESPEED * value );
cmd.rightmove = idMath::ClampChar( idMath::Ftoi( move ) );
break;
}
case UB_MOVERIGHT: {
float move = (float)cmd.rightmove + ( KEY_MOVESPEED * value );
cmd.rightmove = idMath::ClampChar( idMath::Ftoi( move ) );
break;
}
case UB_LOOKUP: {
if ( joy_dampenLook.GetBool() ) {
lookValue = Min( lookValue, ( pollTime - lastPollTime ) * joy_deltaPerMSLook.GetFloat() + lastLookValuePitch );
lastLookValuePitch = lookValue;
}
float invertPitch = in_invertLook.GetBool() ? -1.0f : 1.0f;
viewangles[PITCH] -= MS2SEC( pollTime - lastPollTime ) * lookValue * joy_pitchSpeed.GetFloat() * invertPitch;
break;
}
case UB_LOOKDOWN: {
if ( joy_dampenLook.GetBool() ) {
lookValue = Min( lookValue, ( pollTime - lastPollTime ) * joy_deltaPerMSLook.GetFloat() + lastLookValuePitch );
lastLookValuePitch = lookValue;
}
float invertPitch = in_invertLook.GetBool() ? -1.0f : 1.0f;
viewangles[PITCH] += MS2SEC( pollTime - lastPollTime ) * lookValue * joy_pitchSpeed.GetFloat() * invertPitch;
break;
}
case UB_LEFT: {
if ( joy_dampenLook.GetBool() ) {
lookValue = Min( lookValue, ( pollTime - lastPollTime ) * joy_deltaPerMSLook.GetFloat() + lastLookValueYaw );
lastLookValueYaw = lookValue;
}
viewangles[YAW] += MS2SEC( pollTime - lastPollTime ) * lookValue * joy_yawSpeed.GetFloat();
break;
}
case UB_RIGHT: {
if ( joy_dampenLook.GetBool() ) {
lookValue = Min( lookValue, ( pollTime - lastPollTime ) * joy_deltaPerMSLook.GetFloat() + lastLookValueYaw );
lastLookValueYaw = lookValue;
}
viewangles[YAW] -= MS2SEC( pollTime - lastPollTime ) * lookValue * joy_yawSpeed.GetFloat();
break;
}
}
}
/*
=================
idUsercmdGenLocal::JoystickMove
=================
*/
void idUsercmdGenLocal::JoystickMove( void ) {
float anglespeed;
void idUsercmdGenLocal::JoystickMove() {
float threshold = joy_deadZone.GetFloat();
float triggerThreshold = joy_triggerThreshold.GetFloat();
if ( toggled_run.on ^ ( in_alwaysRun.GetBool() && idAsyncNetwork::IsActive() ) ) {
anglespeed = idMath::M_MS2SEC * USERCMD_MSEC * in_angleSpeedKey.GetFloat();
} else {
anglespeed = idMath::M_MS2SEC * USERCMD_MSEC;
}
float axis_y = joystickAxis[ AXIS_LEFT_Y ];
float axis_x = joystickAxis[ AXIS_LEFT_X ];
CircleToSquare( axis_x, axis_y );
if ( !ButtonState( UB_STRAFE ) ) {
viewangles[YAW] += anglespeed * in_yawSpeed.GetFloat() * joystickAxis[AXIS_SIDE];
viewangles[PITCH] += anglespeed * in_pitchSpeed.GetFloat() * joystickAxis[AXIS_FORWARD];
} else {
cmd.rightmove = idMath::ClampChar( cmd.rightmove + joystickAxis[AXIS_SIDE] );
cmd.forwardmove = idMath::ClampChar( cmd.forwardmove + joystickAxis[AXIS_FORWARD] );
}
HandleJoystickAxis( K_JOY_STICK1_UP, axis_y, threshold, false );
HandleJoystickAxis( K_JOY_STICK1_DOWN, axis_y, threshold, true );
HandleJoystickAxis( K_JOY_STICK1_LEFT, axis_x, threshold, false );
HandleJoystickAxis( K_JOY_STICK1_RIGHT, axis_x, threshold, true );
cmd.upmove = idMath::ClampChar( cmd.upmove + joystickAxis[AXIS_UP] );
axis_y = joystickAxis[ AXIS_RIGHT_Y ];
axis_x = joystickAxis[ AXIS_RIGHT_X ];
CircleToSquare( axis_x, axis_y );
HandleJoystickAxis( K_JOY_STICK2_UP, axis_y, threshold, false );
HandleJoystickAxis( K_JOY_STICK2_DOWN, axis_y, threshold, true );
HandleJoystickAxis( K_JOY_STICK2_LEFT, axis_x, threshold, false );
HandleJoystickAxis( K_JOY_STICK2_RIGHT, axis_x, threshold, true );
HandleJoystickAxis( K_JOY_TRIGGER1, joystickAxis[ AXIS_LEFT_TRIG ], triggerThreshold, true );
HandleJoystickAxis( K_JOY_TRIGGER2, joystickAxis[ AXIS_RIGHT_TRIG ], triggerThreshold, true );
}
/*
@ -778,6 +983,9 @@ void idUsercmdGenLocal::MakeCurrent( void ) {
// keyboard angle adjustment
AdjustAngles();
// get basic movement from joystick
JoystickMove();
// set button bits
CmdButtons();
@ -787,9 +995,6 @@ void idUsercmdGenLocal::MakeCurrent( void ) {
// get basic movement from mouse
MouseMove();
// get basic movement from joystick
JoystickMove();
// check to make sure the angles haven't wrapped
if ( viewangles[PITCH] - oldAngles[PITCH] > 90 ) {
viewangles[PITCH] = oldAngles[PITCH] + 90;
@ -877,6 +1082,7 @@ void idUsercmdGenLocal::Clear( void ) {
// clears all key states
memset( buttonState, 0, sizeof( buttonState ) );
memset( keyState, false, sizeof( keyState ) );
memset( joystickAxis, 0, sizeof( joystickAxis ) );
inhibitCommands = false;
@ -939,6 +1145,8 @@ void idUsercmdGenLocal::Key( int keyNum, bool down ) {
int action = idKeyInput::GetUsercmdAction( keyNum );
// TODO: if action == 0 return ?
if ( down ) {
buttonState[ action ]++;
@ -1039,7 +1247,28 @@ idUsercmdGenLocal::Joystick
===============
*/
void idUsercmdGenLocal::Joystick( void ) {
memset( joystickAxis, 0, sizeof( joystickAxis ) );
int numEvents = Sys_PollJoystickInputEvents( 0 );
// Study each of the buffer elements and process them.
for ( int i = 0; i < numEvents; i++ ) {
int action;
int value;
if ( Sys_ReturnJoystickInputEvent( i, action, value ) ) {
if ( action >= J_ACTION1 && action <= J_ACTION_MAX ) {
int joyButton = K_JOY1 + ( action - J_ACTION1 );
Key( joyButton, ( value != 0 ) );
} else if ( ( action >= J_AXIS_MIN ) && ( action <= J_AXIS_MAX ) ) {
joystickAxis[ action - J_AXIS_MIN ] = static_cast<float>( value ) / 32767.0f;
} else if ( action >= J_DPAD_UP && action <= J_DPAD_RIGHT ) {
int joyButton = K_JOY_DPAD_UP + ( action - J_DPAD_UP );
Key( joyButton, ( value != 0 ) );
} else {
//assert( !"Unknown joystick event" );
}
}
}
Sys_EndJoystickInputEvents();
}
/*
@ -1065,7 +1294,9 @@ void idUsercmdGenLocal::UsercmdInterrupt( void ) {
Keyboard();
// process the system joystick events
Joystick();
if ( in_useJoystick.GetBool() ) {
Joystick();
}
// create the usercmd for com_ticNumber+1
MakeCurrent();
@ -1095,6 +1326,11 @@ idUsercmdGenLocal::GetDirectUsercmd
*/
usercmd_t idUsercmdGenLocal::GetDirectUsercmd( void ) {
pollTime = Sys_Milliseconds();
if ( pollTime - lastPollTime > 100 ) {
lastPollTime = pollTime - 100;
}
// initialize current usercmd
InitCurrent();
@ -1105,12 +1341,15 @@ usercmd_t idUsercmdGenLocal::GetDirectUsercmd( void ) {
Keyboard();
// process the system joystick events
Joystick();
if ( in_useJoystick.GetBool() ) {
Joystick();
}
// create the usercmd
MakeCurrent();
cmd.duplicateCount = 0;
lastPollTime = pollTime;
return cmd;
}

View file

@ -108,8 +108,26 @@ struct mouse_poll_t {
}
};
struct joystick_poll_t {
int action;
int value;
joystick_poll_t() : action(0), value(0) {} // TODO: or -1?
joystick_poll_t(int a, int v) {
action = a;
value = v;
}
};
static idList<kbd_poll_t> kbd_polls;
static idList<mouse_poll_t> mouse_polls;
static idList<joystick_poll_t> joystick_polls;
static bool buttonStates[K_LAST_KEY];
static int joyAxis[MAX_JOYSTICK_AXIS];
static idList<sysEvent_t> event_overflow;
#if SDL_VERSION_ATLEAST(2, 0, 0)
// for utf8ToISO8859_1() - used for non-ascii text input and Sys_GetLocalizedScancodeName()
@ -477,6 +495,73 @@ static byte mapkey(SDL_Keycode key) {
return 0;
}
static sys_jEvents mapjoybutton(SDL_GameControllerButton button) {
switch (button)
{
case SDL_CONTROLLER_BUTTON_A:
return J_ACTION1;
case SDL_CONTROLLER_BUTTON_B:
return J_ACTION2;
case SDL_CONTROLLER_BUTTON_X:
return J_ACTION3;
case SDL_CONTROLLER_BUTTON_Y:
return J_ACTION4;
case SDL_CONTROLLER_BUTTON_BACK:
return J_ACTION10;
case SDL_CONTROLLER_BUTTON_GUIDE:
// TODO:
break;
case SDL_CONTROLLER_BUTTON_START:
return J_ACTION9;
case SDL_CONTROLLER_BUTTON_LEFTSTICK:
return J_ACTION7;
case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
return J_ACTION8;
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
return J_ACTION5;
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
return J_ACTION6;
case SDL_CONTROLLER_BUTTON_DPAD_UP:
return J_DPAD_UP;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
return J_DPAD_DOWN;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
return J_DPAD_LEFT;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
return J_DPAD_RIGHT;
default:
common->Warning("unknown game controller button %u", button);
break;
}
return MAX_JOY_EVENT;
}
static sys_jEvents mapjoyaxis(SDL_GameControllerAxis axis) {
switch (axis)
{
case SDL_CONTROLLER_AXIS_LEFTX:
return J_AXIS_LEFT_X;
case SDL_CONTROLLER_AXIS_LEFTY:
return J_AXIS_LEFT_Y;
case SDL_CONTROLLER_AXIS_RIGHTX:
return J_AXIS_RIGHT_X;
case SDL_CONTROLLER_AXIS_RIGHTY:
return J_AXIS_RIGHT_Y;
case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
return J_AXIS_LEFT_TRIG;
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
return J_AXIS_RIGHT_TRIG;
default:
common->Warning("unknown game controller axis %u", axis);
break;
}
return J_AXIS_MAX;
}
static void PushConsoleEvent(const char *s) {
char *b;
size_t len;
@ -531,6 +616,19 @@ void Sys_InitInput() {
#else // SDL1.2 doesn't support this
in_grabKeyboard.ClearModified();
#endif
joystick_polls.SetGranularity(64);
event_overflow.SetGranularity(64);
memset( buttonStates, 0, sizeof( buttonStates ) );
memset( joyAxis, 0, sizeof( joyAxis ) );
const int NumJoysticks = SDL_NumJoysticks();
printf("XXX found %d joysticks\n", NumJoysticks);
for( int i = 0; i < NumJoysticks; ++i )
{
SDL_GameController* gc = SDL_GameControllerOpen( i );
}
}
/*
@ -541,6 +639,8 @@ Sys_ShutdownInput
void Sys_ShutdownInput() {
kbd_polls.Clear();
mouse_polls.Clear();
joystick_polls.Clear();
event_overflow.Clear();
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_iconv_close( iconvDesc ); // used by utf8ToISO8859_1()
iconvDesc = ( SDL_iconv_t ) -1;
@ -666,6 +766,18 @@ void Sys_GrabMouseCursor(bool grabIt) {
GLimp_GrabInput(flags);
}
static void PushButton( int key, bool value ) {
// So we don't keep sending the same SE_KEY message over and over again
if ( buttonStates[key] != value ) {
buttonStates[key] = value;
sysEvent_t res = { SE_KEY, key, value ? 1 : 0, 0, NULL };
// this is done to generate two events per controller axis event
// one SE_JOYSTICK and one SE_KEY
event_overflow.Append(res);
}
}
/*
================
Sys_GetEvent
@ -678,6 +790,14 @@ sysEvent_t Sys_GetEvent() {
static const sysEvent_t res_none = { SE_NONE, 0, 0, 0, NULL };
// process any overflow.
if (event_overflow.Num() > 0)
{
res = event_overflow[0];
event_overflow.RemoveIndex(0);
return res;
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
static char s[SDL_TEXTINPUTEVENT_TEXT_SIZE] = {0};
static size_t s_pos = 0;
@ -958,6 +1078,76 @@ sysEvent_t Sys_GetEvent() {
return res;
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
{
sys_jEvents jEvent = mapjoybutton( (SDL_GameControllerButton)ev.cbutton.button);
joystick_polls.Append(joystick_poll_t(jEvent, ev.cbutton.state == SDL_PRESSED ? 1 : 0) );
res.evType = SE_KEY;
res.evValue2 = ev.cbutton.state == SDL_PRESSED ? 1 : 0;
if ( ( jEvent >= J_ACTION1 ) && ( jEvent <= J_ACTION_MAX ) ) {
res.evValue = K_JOY1 + ( jEvent - J_ACTION1 );
return res;
} else if ( ( jEvent >= J_DPAD_UP ) && ( jEvent <= J_DPAD_RIGHT ) ) {
res.evValue = K_JOY_DPAD_UP + ( jEvent - J_DPAD_UP );
return res;
}
continue; // try to get a decent event.
}
case SDL_CONTROLLERAXISMOTION:
{
const int range = 16384;
sys_jEvents jEvent = mapjoyaxis( (SDL_GameControllerAxis)ev.caxis.axis);
joystick_polls.Append(joystick_poll_t( jEvent, ev.caxis.value) );
if ( jEvent == J_AXIS_LEFT_X ) {
PushButton( K_JOY_STICK1_LEFT, ( ev.caxis.value < -range ) );
PushButton( K_JOY_STICK1_RIGHT, ( ev.caxis.value > range ) );
} else if ( jEvent == J_AXIS_LEFT_Y ) {
PushButton( K_JOY_STICK1_UP, ( ev.caxis.value < -range ) );
PushButton( K_JOY_STICK1_DOWN, ( ev.caxis.value > range ) );
} else if ( jEvent == J_AXIS_RIGHT_X ) {
PushButton( K_JOY_STICK2_LEFT, ( ev.caxis.value < -range ) );
PushButton( K_JOY_STICK2_RIGHT, ( ev.caxis.value > range ) );
} else if ( jEvent == J_AXIS_RIGHT_Y ) {
PushButton( K_JOY_STICK2_UP, ( ev.caxis.value < -range ) );
PushButton( K_JOY_STICK2_DOWN, ( ev.caxis.value > range ) );
} else if ( jEvent == J_AXIS_LEFT_TRIG ) {
PushButton( K_JOY_TRIGGER1, ( ev.caxis.value > range ) );
} else if ( jEvent == J_AXIS_RIGHT_TRIG ) {
PushButton( K_JOY_TRIGGER2, ( ev.caxis.value > range ) );
}
if ( jEvent >= J_AXIS_MIN && jEvent <= J_AXIS_MAX ) {
int axis = jEvent - J_AXIS_MIN;
int percent = ( ev.caxis.value * 16 ) / range;
if ( joyAxis[axis] != percent ) {
joyAxis[axis] = percent;
res.evType = SE_JOYSTICK;
res.evValue = axis;
res.evValue2 = percent;
return res;
}
}
continue; // try to get a decent event.
}
break;
case SDL_JOYDEVICEADDED:
SDL_GameControllerOpen( ev.jdevice.which );
// TODO: hot swapping maybe.
//lbOnControllerPlugIn(event.jdevice.which);
break;
case SDL_JOYDEVICEREMOVED:
// TODO: hot swapping maybe.
//lbOnControllerUnPlug(event.jdevice.which);
break;
case SDL_QUIT:
PushConsoleEvent("quit");
return res_none;
@ -996,6 +1186,12 @@ void Sys_ClearEvents() {
kbd_polls.SetNum(0, false);
mouse_polls.SetNum(0, false);
joystick_polls.SetNum(0, false);
memset( buttonStates, 0, sizeof( buttonStates ) );
memset( joyAxis, 0, sizeof( joyAxis ) );
event_overflow.SetNum(0, false);
}
static void handleMouseGrab() {
@ -1146,3 +1342,35 @@ Sys_EndMouseInputEvents
void Sys_EndMouseInputEvents() {
mouse_polls.SetNum(0, false);
}
/*
================
Joystick Input Methods
================
*/
void Sys_SetRumble( int device, int low, int hi ) {
// TODO: support multiple controllers.
assert(device == 0);
// TODO: support rumble maybe.
assert(0);
}
int Sys_PollJoystickInputEvents( int deviceNum ) {
// TODO: support multiple controllers.
assert(deviceNum == 0);
return joystick_polls.Num();
}
int Sys_ReturnJoystickInputEvent( const int n, int &action, int &value ) {
if (n >= joystick_polls.Num())
return 0;
action = joystick_polls[n].action;
value = joystick_polls[n].value;
return 1;
}
void Sys_EndJoystickInputEvents() {
joystick_polls.SetNum(0, false);
}

View file

@ -44,12 +44,12 @@ typedef enum {
} cpuidSimd_t;
typedef enum {
AXIS_SIDE,
AXIS_FORWARD,
AXIS_UP,
AXIS_ROLL,
AXIS_YAW,
AXIS_PITCH,
AXIS_LEFT_X,
AXIS_LEFT_Y,
AXIS_RIGHT_X,
AXIS_RIGHT_Y,
AXIS_LEFT_TRIG,
AXIS_RIGHT_TRIG,
MAX_JOYSTICK_AXIS
} joystickAxis_t;
@ -59,7 +59,7 @@ typedef enum {
SE_CHAR, // evValue is an ascii char
SE_MOUSE, // evValue and evValue2 are relative signed x / y moves
SE_MOUSE_ABS, // evValue and evValue2 are absolute x / y coordinates in the window
SE_JOYSTICK_AXIS, // evValue is an axis number and evValue2 is the current state (-127 to 127)
SE_JOYSTICK, // evValue is an axis number and evValue2 is the current state (-127 to 127)
SE_CONSOLE // evPtr is a char*, from typing something at a non-game console
} sysEventType_t;
@ -77,6 +77,59 @@ typedef enum {
M_DELTAZ
} sys_mEvents;
typedef enum {
J_ACTION1,
J_ACTION2,
J_ACTION3,
J_ACTION4,
J_ACTION5,
J_ACTION6,
J_ACTION7,
J_ACTION8,
J_ACTION9,
J_ACTION10,
J_ACTION11,
J_ACTION12,
J_ACTION13,
J_ACTION14,
J_ACTION15,
J_ACTION16,
J_ACTION17,
J_ACTION18,
J_ACTION19,
J_ACTION20,
J_ACTION21,
J_ACTION22,
J_ACTION23,
J_ACTION24,
J_ACTION25,
J_ACTION26,
J_ACTION27,
J_ACTION28,
J_ACTION29,
J_ACTION30,
J_ACTION31,
J_ACTION32,
J_ACTION_MAX = J_ACTION32,
J_AXIS_MIN,
J_AXIS_LEFT_X = J_AXIS_MIN + AXIS_LEFT_X,
J_AXIS_LEFT_Y = J_AXIS_MIN + AXIS_LEFT_Y,
J_AXIS_RIGHT_X = J_AXIS_MIN + AXIS_RIGHT_X,
J_AXIS_RIGHT_Y = J_AXIS_MIN + AXIS_RIGHT_Y,
J_AXIS_LEFT_TRIG = J_AXIS_MIN + AXIS_LEFT_TRIG,
J_AXIS_RIGHT_TRIG = J_AXIS_MIN + AXIS_RIGHT_TRIG,
J_AXIS_MAX = J_AXIS_MIN + MAX_JOYSTICK_AXIS - 1,
J_DPAD_UP,
J_DPAD_DOWN,
J_DPAD_LEFT,
J_DPAD_RIGHT,
MAX_JOY_EVENT
} sys_jEvents;
struct sysEvent_t {
sysEventType_t evType;
int evValue;
@ -189,6 +242,12 @@ int Sys_PollMouseInputEvents( void );
int Sys_ReturnMouseInputEvent( const int n, int &action, int &value );
void Sys_EndMouseInputEvents( void );
// joystick input polling
void Sys_SetRumble( int device, int low, int hi );
int Sys_PollJoystickInputEvents( int deviceNum );
int Sys_ReturnJoystickInputEvent( const int n, int &action, int &value );
void Sys_EndJoystickInputEvents();
// when the console is down, or the game is about to perform a lengthy
// operation like map loading, the system can release the mouse cursor
// when in windowed mode

View file

@ -736,7 +736,7 @@ const char *idWindow::HandleEvent(const sysEvent_t *event, bool *updateVisuals)
*updateVisuals = true;
}
if (event->evValue == K_MOUSE1) {
if (event->evValue == K_MOUSE1 || event->evValue == K_JOY2) {
if (!event->evValue2 && GetCaptureChild()) {
GetCaptureChild()->LoseCapture();
@ -785,7 +785,7 @@ const char *idWindow::HandleEvent(const sysEvent_t *event, bool *updateVisuals)
} else if (!actionUpRun) {
actionUpRun = RunScript( ON_ACTIONRELEASE );
}
} else if (event->evValue == K_MOUSE2) {
} else if (event->evValue == K_MOUSE2 || event->evValue == K_JOY1) {
if (!event->evValue2 && GetCaptureChild()) {
GetCaptureChild()->LoseCapture();