xash3d-fwgs/engine/client/in_joy.c

430 lines
11 KiB
C

/*
joyinput.c - joystick common input code
Copyright (C) 2016 a1batross
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "common.h"
#include "input.h"
#include "keydefs.h"
#include "client.h"
#include "platform/platform.h"
#ifndef SHRT_MAX
#define SHRT_MAX 0x7FFF
#endif
#define MAX_AXES JOY_AXIS_NULL
// index - axis num come from event
// value - inner axis
static engineAxis_t joyaxesmap[MAX_AXES] =
{
JOY_AXIS_SIDE, // left stick, x
JOY_AXIS_FWD, // left stick, y
JOY_AXIS_PITCH, // right stick, y
JOY_AXIS_YAW, // right stick, x
JOY_AXIS_RT, // right trigger
JOY_AXIS_LT // left trigger
};
static struct joy_axis_s
{
short val;
short prevval;
} joyaxis[MAX_AXES] = { 0 };
static byte currentbinding; // add posibility to remap keys, to place it in joykeys[]
convar_t *joy_enable;
static convar_t *joy_pitch;
static convar_t *joy_yaw;
static convar_t *joy_forward;
static convar_t *joy_side;
static convar_t *joy_found;
static convar_t *joy_index;
static convar_t *joy_lt_threshold;
static convar_t *joy_rt_threshold;
static convar_t *joy_side_deadzone;
static convar_t *joy_forward_deadzone;
static convar_t *joy_side_key_threshold;
static convar_t *joy_forward_key_threshold;
static convar_t *joy_pitch_deadzone;
static convar_t *joy_yaw_deadzone;
static convar_t *joy_axis_binding;
/*
============
Joy_IsActive
============
*/
qboolean Joy_IsActive( void )
{
return joy_found->value && joy_enable->value;
}
/*
============
Joy_HatMotionEvent
DPad events
============
*/
void Joy_HatMotionEvent( byte hat, byte value )
{
struct
{
int mask;
int key;
} keys[] =
{
{ JOY_HAT_UP, K_UPARROW },
{ JOY_HAT_DOWN, K_DOWNARROW },
{ JOY_HAT_LEFT, K_LEFTARROW },
{ JOY_HAT_RIGHT, K_RIGHTARROW },
};
int i;
if( !joy_found->value )
return;
for( i = 0; i < ARRAYSIZE( keys ); i++ )
{
if( value & keys[i].mask )
{
if( !Key_IsDown( keys[i].key ))
Key_Event( keys[i].key, true );
}
else
{
if( Key_IsDown( keys[i].key ))
Key_Event( keys[i].key, false );
}
}
}
/*
=============
Joy_ProcessTrigger
=============
*/
static void Joy_ProcessTrigger( const engineAxis_t engineAxis, short value )
{
int trigButton = 0, trigThreshold = 0;
switch( engineAxis )
{
case JOY_AXIS_RT:
trigButton = K_JOY2;
trigThreshold = joy_rt_threshold->value;
break;
case JOY_AXIS_LT:
trigButton = K_JOY1;
trigThreshold = joy_lt_threshold->value;
break;
default:
Con_Reportf( S_ERROR "Joy_ProcessTrigger: invalid axis = %i", engineAxis );
break;
}
// update axis values
joyaxis[engineAxis].prevval = joyaxis[engineAxis].val;
joyaxis[engineAxis].val = value;
if( joyaxis[engineAxis].val > trigThreshold &&
joyaxis[engineAxis].prevval <= trigThreshold ) // ignore random press
{
Key_Event( trigButton, true );
}
else if( joyaxis[engineAxis].val < trigThreshold &&
joyaxis[engineAxis].prevval >= trigThreshold ) // we're unpressing (inverted)
{
Key_Event( trigButton, false );
}
}
static int Joy_GetHatValueForAxis( const engineAxis_t engineAxis )
{
int threshold, negative, positive;
switch( engineAxis )
{
case JOY_AXIS_SIDE:
threshold = joy_side_key_threshold->value;
negative = JOY_HAT_LEFT;
positive = JOY_HAT_RIGHT;
break;
case JOY_AXIS_FWD:
threshold = joy_side_key_threshold->value;
negative = JOY_HAT_UP;
positive = JOY_HAT_DOWN;
break;
default:
ASSERT( false ); // only fwd/side axes can emit key events
return 0;
}
// similar code in Joy_ProcessTrigger
if( joyaxis[engineAxis].val > threshold &&
joyaxis[engineAxis].prevval <= threshold ) // ignore random press
{
return positive;
}
if( joyaxis[engineAxis].val < -threshold &&
joyaxis[engineAxis].prevval >= -threshold ) // we're unpressing (inverted)
{
return negative;
}
return 0;
}
/*
=============
Joy_ProcessStick
=============
*/
static void Joy_ProcessStick( const engineAxis_t engineAxis, short value )
{
int deadzone = 0;
switch( engineAxis )
{
case JOY_AXIS_FWD: deadzone = joy_forward_deadzone->value; break;
case JOY_AXIS_SIDE: deadzone = joy_side_deadzone->value; break;
case JOY_AXIS_PITCH: deadzone = joy_pitch_deadzone->value; break;
case JOY_AXIS_YAW: deadzone = joy_yaw_deadzone->value; break;
default:
Con_Reportf( S_ERROR "Joy_ProcessStick: invalid axis = %i", engineAxis );
break;
}
if( value < deadzone && value > -deadzone )
value = 0; // caught new event in deadzone, fill it with zero(no motion)
// update axis values
joyaxis[engineAxis].prevval = joyaxis[engineAxis].val;
joyaxis[engineAxis].val = value;
// fwd/side axis simulate hat movement
if( ( engineAxis == JOY_AXIS_SIDE || engineAxis == JOY_AXIS_FWD ) &&
( CL_IsInMenu() || CL_IsInConsole() ) )
{
int val = 0;
val |= Joy_GetHatValueForAxis( JOY_AXIS_SIDE );
val |= Joy_GetHatValueForAxis( JOY_AXIS_FWD );
Joy_HatMotionEvent( 0, val );
}
}
/*
=============
Joy_AxisMotionEvent
Axis events
=============
*/
void Joy_AxisMotionEvent( byte axis, short value )
{
if( !joy_found->value )
return;
if( axis >= MAX_AXES )
{
Con_Reportf( "Only 6 axes is supported\n" );
return;
}
Joy_KnownAxisMotionEvent( joyaxesmap[axis], value );
}
void Joy_KnownAxisMotionEvent( engineAxis_t engineAxis, short value )
{
if( engineAxis == JOY_AXIS_NULL )
return;
if( value == joyaxis[engineAxis].val )
return; // it is not an update
if( engineAxis >= JOY_AXIS_RT )
Joy_ProcessTrigger( engineAxis, value );
else
Joy_ProcessStick( engineAxis, value );
}
/*
=============
Joy_BallMotionEvent
Trackball events. UNDONE
=============
*/
void Joy_BallMotionEvent( byte ball, short xrel, short yrel )
{
//if( !joy_found->value )
// return;
}
/*
=============
Joy_ButtonEvent
Button events
=============
*/
void Joy_ButtonEvent( byte button, byte down )
{
if( !joy_found->value )
return;
// generic game button code.
if( button > 32 )
{
int origbutton = button;
button = ( button & 31 ) + K_AUX1;
Con_Reportf( "Only 32 joybuttons is supported, converting %i button ID to %s\n", origbutton, Key_KeynumToString( button ) );
}
else button += K_AUX1;
Key_Event( button, down );
}
/*
=============
Joy_RemoveEvent
Called when joystick is removed. For future expansion
=============
*/
void Joy_RemoveEvent( void )
{
if( joy_found->value )
Cvar_FullSet( "joy_found", "0", FCVAR_READ_ONLY );
}
/*
=============
Joy_RemoveEvent
Called when joystick is removed. For future expansion
=============
*/
void Joy_AddEvent( void )
{
if( joy_enable->value && !joy_found->value )
Cvar_FullSet( "joy_found", "1", FCVAR_READ_ONLY );
}
/*
=============
Joy_FinalizeMove
Append movement from axis. Called everyframe
=============
*/
void Joy_FinalizeMove( float *fw, float *side, float *dpitch, float *dyaw )
{
if( !Joy_IsActive() )
return;
if( FBitSet( joy_axis_binding->flags, FCVAR_CHANGED ) )
{
const char *bind = joy_axis_binding->string;
size_t i;
for( i = 0; bind[i]; i++ )
{
switch( bind[i] )
{
case 's': joyaxesmap[i] = JOY_AXIS_SIDE; break;
case 'f': joyaxesmap[i] = JOY_AXIS_FWD; break;
case 'y': joyaxesmap[i] = JOY_AXIS_YAW; break;
case 'p': joyaxesmap[i] = JOY_AXIS_PITCH; break;
case 'r': joyaxesmap[i] = JOY_AXIS_RT; break;
case 'l': joyaxesmap[i] = JOY_AXIS_LT; break;
default : joyaxesmap[i] = JOY_AXIS_NULL; break;
}
}
ClearBits( joy_axis_binding->flags, FCVAR_CHANGED );
}
*fw -= joy_forward->value * (float)joyaxis[JOY_AXIS_FWD ].val/(float)SHRT_MAX; // must be form -1.0 to 1.0
*side += joy_side->value * (float)joyaxis[JOY_AXIS_SIDE].val/(float)SHRT_MAX;
#if !defined(XASH_SDL)
*dpitch += joy_pitch->value * (float)joyaxis[JOY_AXIS_PITCH].val/(float)SHRT_MAX * host.realframetime; // abs axis rotate is frametime related
*dyaw -= joy_yaw->value * (float)joyaxis[JOY_AXIS_YAW ].val/(float)SHRT_MAX * host.realframetime;
#else
// HACKHACK: SDL have inverted look axis.
*dpitch -= joy_pitch->value * (float)joyaxis[JOY_AXIS_PITCH].val/(float)SHRT_MAX * host.realframetime;
*dyaw += joy_yaw->value * (float)joyaxis[JOY_AXIS_YAW ].val/(float)SHRT_MAX * host.realframetime;
#endif
}
/*
=============
Joy_Init
Main init procedure
=============
*/
void Joy_Init( void )
{
joy_pitch = Cvar_Get( "joy_pitch", "100.0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "joystick pitch sensitivity" );
joy_yaw = Cvar_Get( "joy_yaw", "100.0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "joystick yaw sensitivity" );
joy_side = Cvar_Get( "joy_side", "1.0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "joystick side sensitivity. Values from -1.0 to 1.0" );
joy_forward = Cvar_Get( "joy_forward", "1.0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "joystick forward sensitivity. Values from -1.0 to 1.0" );
joy_lt_threshold = Cvar_Get( "joy_lt_threshold", "16384", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "left trigger threshold. Value from 0 to 32767");
joy_rt_threshold = Cvar_Get( "joy_rt_threshold", "16384", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "right trigger threshold. Value from 0 to 32767" );
// emit a key event at 75% axis move
joy_side_key_threshold = Cvar_Get( "joy_side_key_threshold", "24576", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "side axis key event emit threshold. Value from 0 to 32767" );
joy_forward_key_threshold = Cvar_Get( "joy_forward_key_threshold", "24576", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "forward axis key event emit threshold. Value from 0 to 32767");
// by default, we rely on deadzone detection come from system, but some glitchy devices report false deadzones
joy_side_deadzone = Cvar_Get( "joy_side_deadzone", DEFAULT_JOY_DEADZONE, FCVAR_ARCHIVE | FCVAR_FILTERABLE, "side axis deadzone. Value from 0 to 32767" );
joy_forward_deadzone = Cvar_Get( "joy_forward_deadzone", DEFAULT_JOY_DEADZONE, FCVAR_ARCHIVE | FCVAR_FILTERABLE, "forward axis deadzone. Value from 0 to 32767");
joy_pitch_deadzone = Cvar_Get( "joy_pitch_deadzone", DEFAULT_JOY_DEADZONE, FCVAR_ARCHIVE | FCVAR_FILTERABLE, "pitch axis deadzone. Value from 0 to 32767");
joy_yaw_deadzone = Cvar_Get( "joy_yaw_deadzone", DEFAULT_JOY_DEADZONE, FCVAR_ARCHIVE | FCVAR_FILTERABLE, "yaw axis deadzone. Value from 0 to 32767" );
joy_axis_binding = Cvar_Get( "joy_axis_binding", "sfpyrl", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "axis hardware id to engine inner axis binding, "
"s - side, f - forward, y - yaw, p - pitch, r - left trigger, l - right trigger" );
joy_found = Cvar_Get( "joy_found", "0", FCVAR_READ_ONLY, "is joystick is connected" );
// we doesn't loaded config.cfg yet, so this cvar is not archive.
// change by +set joy_index in cmdline
joy_index = Cvar_Get( "joy_index", "0", FCVAR_READ_ONLY, "current active joystick" );
joy_enable = Cvar_Get( "joy_enable", "1", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "enable joystick" );
if( Sys_CheckParm( "-nojoy" ))
{
Cvar_FullSet( "joy_enable", "0", FCVAR_READ_ONLY );
return;
}
Cvar_FullSet( "joy_found", va( "%d", Platform_JoyInit( joy_index->value )), FCVAR_READ_ONLY );
}
/*
===========
Joy_Shutdown
Shutdown joystick code
===========
*/
void Joy_Shutdown( void )
{
Cvar_FullSet( "joy_found", 0, FCVAR_READ_ONLY );
}