2018-04-14 01:42:41 +02:00
/*
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"
2018-10-21 23:36:38 +02:00
# include "platform/platform.h"
2018-04-14 01:42:41 +02:00
# 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[]
2023-04-30 09:12:33 +02:00
static qboolean joy_initialized ;
2023-05-19 06:16:31 +02:00
static CVAR_DEFINE_AUTO ( joy_pitch , " 100.0 " , FCVAR_ARCHIVE | FCVAR_FILTERABLE , " joystick pitch sensitivity " ) ;
static CVAR_DEFINE_AUTO ( joy_yaw , " 100.0 " , FCVAR_ARCHIVE | FCVAR_FILTERABLE , " joystick yaw sensitivity " ) ;
static CVAR_DEFINE_AUTO ( joy_side , " 1.0 " , FCVAR_ARCHIVE | FCVAR_FILTERABLE , " joystick side sensitivity. Values from -1.0 to 1.0 " ) ;
static CVAR_DEFINE_AUTO ( joy_forward , " 1.0 " , FCVAR_ARCHIVE | FCVAR_FILTERABLE , " joystick forward sensitivity. Values from -1.0 to 1.0 " ) ;
static CVAR_DEFINE_AUTO ( joy_lt_threshold , " 16384 " , FCVAR_ARCHIVE | FCVAR_FILTERABLE , " left trigger threshold. Value from 0 to 32767 " ) ;
static CVAR_DEFINE_AUTO ( joy_rt_threshold , " 16384 " , FCVAR_ARCHIVE | FCVAR_FILTERABLE , " right trigger threshold. Value from 0 to 32767 " ) ;
static CVAR_DEFINE_AUTO ( joy_side_key_threshold , " 24576 " , FCVAR_ARCHIVE | FCVAR_FILTERABLE , " side axis key event emit threshold. Value from 0 to 32767 " ) ;
static CVAR_DEFINE_AUTO ( joy_forward_key_threshold , " 24576 " , FCVAR_ARCHIVE | FCVAR_FILTERABLE , " forward axis key event emit threshold. Value from 0 to 32767 " ) ;
static CVAR_DEFINE_AUTO ( joy_side_deadzone , DEFAULT_JOY_DEADZONE , FCVAR_ARCHIVE | FCVAR_FILTERABLE , " side axis deadzone. Value from 0 to 32767 " ) ;
static CVAR_DEFINE_AUTO ( joy_forward_deadzone , DEFAULT_JOY_DEADZONE , FCVAR_ARCHIVE | FCVAR_FILTERABLE , " forward axis deadzone. Value from 0 to 32767 " ) ;
static CVAR_DEFINE_AUTO ( joy_pitch_deadzone , DEFAULT_JOY_DEADZONE , FCVAR_ARCHIVE | FCVAR_FILTERABLE , " pitch axis deadzone. Value from 0 to 32767 " ) ;
static CVAR_DEFINE_AUTO ( joy_yaw_deadzone , DEFAULT_JOY_DEADZONE , FCVAR_ARCHIVE | FCVAR_FILTERABLE , " yaw axis deadzone. Value from 0 to 32767 " ) ;
static CVAR_DEFINE_AUTO ( 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 " ) ;
static CVAR_DEFINE_AUTO ( joy_found , " 0 " , FCVAR_READ_ONLY , " is joystick is connected " ) ;
static CVAR_DEFINE_AUTO ( joy_index , " 0 " , FCVAR_READ_ONLY , " current active joystick " ) ;
CVAR_DEFINE_AUTO ( joy_enable , " 1 " , FCVAR_ARCHIVE | FCVAR_FILTERABLE , " enable joystick " ) ;
2018-04-14 01:42:41 +02:00
/*
= = = = = = = = = = = =
Joy_IsActive
= = = = = = = = = = = =
*/
qboolean Joy_IsActive ( void )
{
2023-05-19 06:16:31 +02:00
return joy_found . value & & joy_enable . value ;
2018-04-14 01:42:41 +02:00
}
/*
= = = = = = = = = = = =
Joy_HatMotionEvent
DPad events
= = = = = = = = = = = =
*/
2019-07-09 02:00:54 +02:00
void Joy_HatMotionEvent ( byte hat , byte value )
2018-04-14 01:42:41 +02:00
{
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 ;
2023-05-19 06:16:31 +02:00
if ( ! joy_found . value )
2018-04-14 01:42:41 +02:00
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
= = = = = = = = = = = = =
*/
2019-07-09 02:00:54 +02:00
static void Joy_ProcessTrigger ( const engineAxis_t engineAxis , short value )
2018-04-14 01:42:41 +02:00
{
int trigButton = 0 , trigThreshold = 0 ;
switch ( engineAxis )
{
case JOY_AXIS_RT :
trigButton = K_JOY2 ;
2023-05-19 06:16:31 +02:00
trigThreshold = joy_rt_threshold . value ;
2018-04-14 01:42:41 +02:00
break ;
case JOY_AXIS_LT :
trigButton = K_JOY1 ;
2023-05-19 06:16:31 +02:00
trigThreshold = joy_lt_threshold . value ;
2018-04-14 01:42:41 +02:00
break ;
default :
2018-10-27 23:39:29 +02:00
Con_Reportf ( S_ERROR " Joy_ProcessTrigger: invalid axis = %i " , engineAxis ) ;
2018-04-14 01:42:41 +02:00
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 ) ;
}
}
2019-07-09 02:00:54 +02:00
static int Joy_GetHatValueForAxis ( const engineAxis_t engineAxis )
2018-04-14 01:42:41 +02:00
{
int threshold , negative , positive ;
switch ( engineAxis )
{
case JOY_AXIS_SIDE :
2023-05-19 06:16:31 +02:00
threshold = joy_side_key_threshold . value ;
2018-04-14 01:42:41 +02:00
negative = JOY_HAT_LEFT ;
positive = JOY_HAT_RIGHT ;
break ;
case JOY_AXIS_FWD :
2023-05-19 06:16:31 +02:00
threshold = joy_side_key_threshold . value ;
2018-04-14 01:42:41 +02:00
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
= = = = = = = = = = = = =
*/
2019-07-09 02:00:54 +02:00
static void Joy_ProcessStick ( const engineAxis_t engineAxis , short value )
2018-04-14 01:42:41 +02:00
{
int deadzone = 0 ;
switch ( engineAxis )
{
2023-05-19 06:16:31 +02:00
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 ;
2018-04-14 01:42:41 +02:00
default :
2018-10-27 23:39:29 +02:00
Con_Reportf ( S_ERROR " Joy_ProcessStick: invalid axis = %i " , engineAxis ) ;
2018-04-14 01:42:41 +02:00
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 ) ;
2019-07-09 02:00:54 +02:00
Joy_HatMotionEvent ( 0 , val ) ;
2018-04-14 01:42:41 +02:00
}
}
/*
= = = = = = = = = = = = =
Joy_AxisMotionEvent
Axis events
= = = = = = = = = = = = =
*/
2019-07-09 02:00:54 +02:00
void Joy_AxisMotionEvent ( byte axis , short value )
2018-04-14 01:42:41 +02:00
{
2023-05-19 06:16:31 +02:00
if ( ! joy_found . value )
2018-04-14 01:42:41 +02:00
return ;
if ( axis > = MAX_AXES )
{
2018-11-16 13:25:04 +01:00
Con_Reportf ( " Only 6 axes is supported \n " ) ;
2018-04-14 01:42:41 +02:00
return ;
}
2023-01-14 08:40:42 +01:00
Joy_KnownAxisMotionEvent ( joyaxesmap [ axis ] , value ) ;
2021-01-03 03:23:19 +01:00
}
2018-04-14 01:42:41 +02:00
2021-01-03 03:23:19 +01:00
void Joy_KnownAxisMotionEvent ( engineAxis_t engineAxis , short value )
{
2018-04-14 01:42:41 +02:00
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
= = = = = = = = = = = = =
*/
2019-07-09 02:00:54 +02:00
void Joy_BallMotionEvent ( byte ball , short xrel , short yrel )
2018-04-14 01:42:41 +02:00
{
2023-05-19 06:16:31 +02:00
//if( !joy_found.value )
2018-04-14 01:42:41 +02:00
// return;
}
/*
= = = = = = = = = = = = =
Joy_ButtonEvent
Button events
= = = = = = = = = = = = =
*/
2019-07-09 02:00:54 +02:00
void Joy_ButtonEvent ( byte button , byte down )
2018-04-14 01:42:41 +02:00
{
2023-05-19 06:16:31 +02:00
if ( ! joy_found . value )
2018-04-14 01:42:41 +02:00
return ;
// generic game button code.
if ( button > 32 )
{
int origbutton = button ;
button = ( button & 31 ) + K_AUX1 ;
2018-11-16 13:25:04 +01:00
Con_Reportf ( " Only 32 joybuttons is supported, converting %i button ID to %s \n " , origbutton , Key_KeynumToString ( button ) ) ;
2018-04-14 01:42:41 +02:00
}
else button + = K_AUX1 ;
Key_Event ( button , down ) ;
}
/*
= = = = = = = = = = = = =
Joy_RemoveEvent
Called when joystick is removed . For future expansion
= = = = = = = = = = = = =
*/
2019-07-09 02:00:54 +02:00
void Joy_RemoveEvent ( void )
2018-04-14 01:42:41 +02:00
{
2023-05-19 06:16:31 +02:00
if ( joy_found . value )
2019-07-09 02:00:54 +02:00
Cvar_FullSet ( " joy_found " , " 0 " , FCVAR_READ_ONLY ) ;
2018-04-14 01:42:41 +02:00
}
/*
= = = = = = = = = = = = =
Joy_RemoveEvent
Called when joystick is removed . For future expansion
= = = = = = = = = = = = =
*/
2019-07-09 02:00:54 +02:00
void Joy_AddEvent ( void )
2018-04-14 01:42:41 +02:00
{
2023-05-19 06:16:31 +02:00
if ( joy_enable . value & & ! joy_found . value )
2019-07-09 02:00:54 +02:00
Cvar_FullSet ( " joy_found " , " 1 " , FCVAR_READ_ONLY ) ;
2018-04-14 01:42:41 +02:00
}
/*
= = = = = = = = = = = = =
Joy_FinalizeMove
Append movement from axis . Called everyframe
= = = = = = = = = = = = =
*/
void Joy_FinalizeMove ( float * fw , float * side , float * dpitch , float * dyaw )
{
2019-07-09 02:00:54 +02:00
if ( ! Joy_IsActive ( ) )
2018-04-14 01:42:41 +02:00
return ;
2023-05-19 06:16:31 +02:00
if ( FBitSet ( joy_axis_binding . flags , FCVAR_CHANGED ) )
2018-04-14 01:42:41 +02:00
{
2023-05-19 06:16:31 +02:00
const char * bind = joy_axis_binding . string ;
2018-04-14 01:42:41 +02:00
size_t i ;
2019-07-09 02:00:54 +02:00
for ( i = 0 ; bind [ i ] ; i + + )
2018-04-14 01:42:41 +02:00
{
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 ;
}
}
2019-07-09 02:00:54 +02:00
2023-05-19 06:16:31 +02:00
ClearBits ( joy_axis_binding . flags , FCVAR_CHANGED ) ;
2018-04-14 01:42:41 +02:00
}
2023-05-19 06:16:31 +02:00
* 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 ;
2018-04-14 01:42:41 +02:00
# if !defined(XASH_SDL)
2023-05-19 06:16:31 +02:00
* 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 ;
2018-04-14 01:42:41 +02:00
# else
// HACKHACK: SDL have inverted look axis.
2023-05-19 06:16:31 +02:00
* 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 ;
2018-04-14 01:42:41 +02:00
# endif
}
/*
= = = = = = = = = = = = =
Joy_Init
Main init procedure
= = = = = = = = = = = = =
*/
void Joy_Init ( void )
{
2023-05-19 06:16:31 +02:00
Cvar_RegisterVariable ( & joy_pitch ) ;
Cvar_RegisterVariable ( & joy_yaw ) ;
Cvar_RegisterVariable ( & joy_side ) ;
Cvar_RegisterVariable ( & joy_forward ) ;
2018-04-14 01:42:41 +02:00
2023-05-19 06:16:31 +02:00
Cvar_RegisterVariable ( & joy_lt_threshold ) ;
Cvar_RegisterVariable ( & joy_rt_threshold ) ;
2018-04-14 01:42:41 +02:00
// emit a key event at 75% axis move
2023-05-19 06:16:31 +02:00
Cvar_RegisterVariable ( & joy_side_key_threshold ) ;
Cvar_RegisterVariable ( & joy_forward_key_threshold ) ;
2018-04-14 01:42:41 +02:00
// by default, we rely on deadzone detection come from system, but some glitchy devices report false deadzones
2023-05-19 06:16:31 +02:00
Cvar_RegisterVariable ( & joy_side_deadzone ) ;
Cvar_RegisterVariable ( & joy_forward_deadzone ) ;
Cvar_RegisterVariable ( & joy_pitch_deadzone ) ;
Cvar_RegisterVariable ( & joy_yaw_deadzone ) ;
Cvar_RegisterVariable ( & joy_axis_binding ) ;
Cvar_RegisterVariable ( & joy_found ) ;
2018-04-14 01:42:41 +02:00
// we doesn't loaded config.cfg yet, so this cvar is not archive.
// change by +set joy_index in cmdline
2023-05-19 06:16:31 +02:00
Cvar_RegisterVariable ( & joy_index ) ;
2018-04-14 01:42:41 +02:00
2023-05-19 06:16:31 +02:00
Cvar_RegisterVariable ( & joy_enable ) ;
2018-04-14 01:42:41 +02:00
2023-04-28 03:40:19 +02:00
// renamed from -nojoy to -noenginejoy to not conflict with
// client.dll's joystick support
if ( Sys_CheckParm ( " -noenginejoy " ) )
2018-04-14 01:42:41 +02:00
{
2019-07-09 02:00:54 +02:00
Cvar_FullSet ( " joy_enable " , " 0 " , FCVAR_READ_ONLY ) ;
2018-04-14 01:42:41 +02:00
return ;
}
2023-05-19 06:16:31 +02:00
Cvar_FullSet ( " joy_found " , va ( " %d " , Platform_JoyInit ( joy_index . value ) ) , FCVAR_READ_ONLY ) ;
2023-04-30 09:12:33 +02:00
joy_initialized = true ;
2018-04-14 01:42:41 +02:00
}
/*
= = = = = = = = = = =
Joy_Shutdown
Shutdown joystick code
= = = = = = = = = = =
*/
void Joy_Shutdown ( void )
{
2023-04-30 09:12:33 +02:00
if ( joy_initialized )
{
Cvar_FullSet ( " joy_found " , 0 , FCVAR_READ_ONLY ) ;
}
2018-04-14 01:42:41 +02:00
}