mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-05 03:24:56 +08:00
601 lines
17 KiB
C
601 lines
17 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
|
|
|
|
// 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;
|
|
short rawval; // value before deadzone zeroing, for debug display
|
|
} joyaxis[MAX_AXES] = { 0 };
|
|
|
|
static qboolean joy_initialized;
|
|
|
|
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" );
|
|
CVAR_DEFINE_AUTO( joy_enable, "1", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "enable joystick" );
|
|
static CVAR_DEFINE_AUTO( joy_have_gyro, "0", FCVAR_READ_ONLY, "tells whether current active gamepad has gyroscope or not" );
|
|
static CVAR_DEFINE_AUTO( joy_calibrated, "0", FCVAR_READ_ONLY, "tells whether current active gamepad gyroscope has been calibrated or not" );
|
|
static CVAR_DEFINE_AUTO( joy_gyro_pitch, "1.0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "gyroscope sensitivity for looking up and down" );
|
|
static CVAR_DEFINE_AUTO( joy_gyro_yaw, "1.0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "gyroscope sensitivity for turning left and right" );
|
|
static CVAR_DEFINE_AUTO( joy_gyro_roll, "0.0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "gyroscope sensitivity when tilting the device sideways" );
|
|
static CVAR_DEFINE_AUTO( joy_debug, "0", 0, "visualize gamepad axes and buttons" );
|
|
|
|
// stores the latest instantaneous gyroscope rotation rates (in rad/s) from platform input
|
|
static vec3_t joy_gyro_speed;
|
|
// last non-zero gyro speed, retained for debug display after joy_gyro_speed is cleared
|
|
static vec3_t joy_gyro_speed_display;
|
|
|
|
/*
|
|
============
|
|
Joy_IsActive
|
|
============
|
|
*/
|
|
qboolean Joy_IsActive( void )
|
|
{
|
|
return joy_enable.value;
|
|
}
|
|
|
|
/*
|
|
===========
|
|
Joy_SetCapabilities
|
|
===========
|
|
*/
|
|
void Joy_SetCapabilities( qboolean have_gyro )
|
|
{
|
|
Cvar_FullSet( joy_have_gyro.name, have_gyro ? "1" : "0", joy_have_gyro.flags );
|
|
}
|
|
|
|
/*
|
|
===========
|
|
Joy_SetCalibrationState
|
|
===========
|
|
*/
|
|
void Joy_SetCalibrationState( joy_calibration_state_t state )
|
|
{
|
|
if( (int)joy_calibrated.value == state )
|
|
return;
|
|
|
|
Cvar_FullSet( joy_calibrated.name, va( "%d", state ), joy_calibrated.flags );
|
|
}
|
|
|
|
/*
|
|
============
|
|
Joy_HatMotionEvent
|
|
|
|
DPad events
|
|
============
|
|
*/
|
|
static void Joy_HatMotionEvent( int 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;
|
|
|
|
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 "%s: invalid axis = %i\n", __func__, 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 "%s: invalid axis = %i\n", __func__, engineAxis );
|
|
break;
|
|
}
|
|
|
|
joyaxis[engineAxis].rawval = value;
|
|
|
|
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 ) &&
|
|
( cls.key_dest == key_menu || cls.key_dest == key_console ))
|
|
{
|
|
int val = 0;
|
|
|
|
val |= Joy_GetHatValueForAxis( JOY_AXIS_SIDE );
|
|
val |= Joy_GetHatValueForAxis( JOY_AXIS_FWD );
|
|
|
|
Joy_HatMotionEvent( val );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
Joy_AxisMotionEvent
|
|
|
|
Axis events
|
|
=============
|
|
*/
|
|
void Joy_AxisMotionEvent( engineAxis_t engineAxis, short value )
|
|
{
|
|
if( engineAxis >= MAX_AXES )
|
|
return;
|
|
|
|
engineAxis = joyaxesmap[engineAxis];
|
|
|
|
if( engineAxis >= MAX_AXES )
|
|
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_GyroEvent
|
|
|
|
Gyroscope events
|
|
=============
|
|
*/
|
|
void Joy_GyroEvent( vec3_t data )
|
|
{
|
|
VectorCopy( data, joy_gyro_speed );
|
|
VectorCopy( data, joy_gyro_speed_display );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
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] = MAX_AXES; 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;
|
|
*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;
|
|
|
|
if( joy_have_gyro.value && (int)joy_calibrated.value == JOY_CALIBRATED )
|
|
{
|
|
float pitch_speed = joy_gyro_speed[0] * ( 180.0f / M_PI );
|
|
float yaw_speed = joy_gyro_speed[1] * ( 180.0f / M_PI );
|
|
float roll_speed = joy_gyro_speed[2] * ( 180.0f / M_PI );
|
|
|
|
*dpitch -= joy_gyro_pitch.value * pitch_speed * host.realframetime;
|
|
*dyaw += joy_gyro_yaw.value * yaw_speed * host.realframetime;
|
|
*dyaw += joy_gyro_roll.value * roll_speed * host.realframetime;
|
|
}
|
|
|
|
VectorClear( joy_gyro_speed );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
Joy_DrawDebug
|
|
|
|
Draw gamepad axes and buttons for debugging (joy_debug 1)
|
|
=============
|
|
*/
|
|
void Joy_DrawDebug( void )
|
|
{
|
|
static const int buttons[] =
|
|
{
|
|
K_A_BUTTON, K_B_BUTTON, K_X_BUTTON, K_Y_BUTTON, 0,
|
|
K_L1_BUTTON, K_R1_BUTTON, 0,
|
|
K_L2_BUTTON, K_R2_BUTTON, 0,
|
|
K_BACK_BUTTON, K_MODE_BUTTON, K_START_BUTTON, 0,
|
|
K_LSTICK, K_RSTICK, 0,
|
|
|
|
// simulated buttons
|
|
K_DPAD_UP, K_DPAD_DOWN, K_DPAD_LEFT, K_DPAD_RIGHT, 0,
|
|
K_LTRIGGER, K_RTRIGGER, 0,
|
|
|
|
// additional buttons
|
|
K_C_BUTTON, K_Z_BUTTON, K_MISC_BUTTON, 0,
|
|
K_PADDLE1_BUTTON, K_PADDLE2_BUTTON, K_PADDLE3_BUTTON, K_PADDLE4_BUTTON, 0
|
|
};
|
|
static const char *const axisnames[MAX_AXES] = { "SIDE", "FWD", "PITCH", "YAW", "RT", "LT" };
|
|
|
|
// deadzone for sticks, threshold for triggers
|
|
static const convar_t *const axis_threshold[MAX_AXES] =
|
|
{
|
|
&joy_side_deadzone,
|
|
&joy_forward_deadzone,
|
|
&joy_pitch_deadzone,
|
|
&joy_yaw_deadzone,
|
|
&joy_rt_threshold,
|
|
&joy_lt_threshold,
|
|
};
|
|
|
|
cl_font_t *font = Con_GetCurFont();
|
|
|
|
if( !joy_debug.value || !Joy_IsActive( ))
|
|
return;
|
|
|
|
float x = 8;
|
|
float y = 100;
|
|
const float bar_w = 100;
|
|
const float halfbar_w = bar_w * 0.5f;
|
|
const float bar_h = font->charHeight - 2;
|
|
const float center = x + halfbar_w;
|
|
|
|
const rgba_t bar_backcolor = { 40, 40, 40, 180 };
|
|
const rgba_t bar_fillcolor = { 100, 200, 100, 200 };
|
|
|
|
// draw axes as labeled bars
|
|
for( int i = 0; i < MAX_AXES; i++ )
|
|
{
|
|
|
|
qboolean is_trigger = ( i >= JOY_AXIS_RT );
|
|
float fval = (float)joyaxis[i].val / SHRT_MAX;
|
|
float fprev = (float)joyaxis[i].prevval / SHRT_MAX;
|
|
float fthreshold = axis_threshold[i]->value / SHRT_MAX;
|
|
|
|
CL_DrawString( x, y, axisnames[i], g_color_table[7], font, 0 );
|
|
y += font->charHeight;
|
|
|
|
// background
|
|
ref.dllFuncs.FillRGBA( kRenderTransTexture, x, y, bar_w, bar_h, bar_backcolor[0], bar_backcolor[1], bar_backcolor[2], bar_backcolor[3] );
|
|
|
|
if( is_trigger )
|
|
{
|
|
// fill bar from the left for triggers
|
|
float filled = fval * bar_w;
|
|
|
|
if( filled > 0 )
|
|
ref.dllFuncs.FillRGBA( kRenderTransTexture, x, y, filled, bar_h, bar_fillcolor[0], bar_fillcolor[1], bar_fillcolor[2], bar_fillcolor[3] );
|
|
|
|
// fill threshold with red, and yellow edge marker
|
|
float thresh_x = x + fthreshold * bar_w;
|
|
ref.dllFuncs.FillRGBA( kRenderTransTexture, x, y, thresh_x - x, bar_h, 180, 40, 40, 120 );
|
|
ref.dllFuncs.FillRGBA( kRenderTransTexture, thresh_x, y, 1, bar_h, 255, 200, 0, 220 );
|
|
|
|
// prevval marker
|
|
float prev_x = x + fprev * bar_w;
|
|
ref.dllFuncs.FillRGBA( kRenderTransTexture, prev_x, y, 1, bar_h, 200, 200, 200, 180 );
|
|
}
|
|
else
|
|
{
|
|
// for sticks bar is filled from the center
|
|
float filled = fval * halfbar_w;
|
|
ref.dllFuncs.FillRGBA( kRenderTransTexture, center + Q_min( 0, filled ), y, fabs( filled ), bar_h, bar_fillcolor[0], bar_fillcolor[1], bar_fillcolor[2], bar_fillcolor[3] );
|
|
|
|
// fill deadzone with red, and yellow edge marker
|
|
float thresh_x = fthreshold * halfbar_w;
|
|
ref.dllFuncs.FillRGBA( kRenderTransTexture, center - thresh_x, y, thresh_x * 2, bar_h, 180, 40, 40, 120 );
|
|
ref.dllFuncs.FillRGBA( kRenderTransTexture, center + thresh_x, y, 1, bar_h, 255, 200, 0, 220 );
|
|
ref.dllFuncs.FillRGBA( kRenderTransTexture, center - thresh_x, y, 1, bar_h, 255, 200, 0, 220 );
|
|
|
|
// mark the center
|
|
ref.dllFuncs.FillRGBA( kRenderTransTexture, center, y, 1, bar_h, 180, 180, 180, 220 );
|
|
|
|
// now draw the actual value
|
|
float raw_x = center + ((float)joyaxis[i].rawval / SHRT_MAX * halfbar_w );
|
|
ref.dllFuncs.FillRGBA( kRenderTransTexture, raw_x, y, 1, bar_h, 255, 255, 100, 200 );
|
|
|
|
// prevval marker
|
|
float prev_x = center + fprev * halfbar_w;
|
|
ref.dllFuncs.FillRGBA( kRenderTransTexture, prev_x, y, 1, bar_h, 200, 200, 200, 180 );
|
|
}
|
|
|
|
y += bar_h + 2;
|
|
}
|
|
|
|
// draw gyro if available
|
|
if( joy_have_gyro.value )
|
|
{
|
|
static const char *gyronames[3] = { "GYRO P", "GYRO Y", "GYRO R" };
|
|
|
|
for( int i = 0; i < 3; i++ )
|
|
{
|
|
// clamp to +-5 rad/s for display
|
|
float fval = joy_gyro_speed_display[i] / 5.0f;
|
|
|
|
fval = bound( -1.0f, fval, 1.0f );
|
|
|
|
CL_DrawString( x, y, gyronames[i], g_color_table[7], font, 0 );
|
|
y += font->charHeight;
|
|
|
|
// background
|
|
ref.dllFuncs.FillRGBA( kRenderTransTexture, x, y, bar_w, bar_h, bar_backcolor[0], bar_backcolor[1], bar_backcolor[2], bar_backcolor[3] );
|
|
|
|
// for gyro bar is filled from the center
|
|
float filled = fval * halfbar_w;
|
|
ref.dllFuncs.FillRGBA( kRenderTransTexture, center + Q_min( 0, filled ), y, fabs( filled ), bar_h, bar_fillcolor[0], bar_fillcolor[1], bar_fillcolor[2], bar_fillcolor[3] );
|
|
|
|
// mark the center
|
|
ref.dllFuncs.FillRGBA( kRenderTransTexture, center, y, 1, bar_h, 180, 180, 180, 220 );
|
|
|
|
// gyro doesn't have deadzones or prevval like axes
|
|
y += bar_h + 2;
|
|
}
|
|
}
|
|
|
|
// draw buttons in a row
|
|
y += 4;
|
|
x = 8;
|
|
for( int i = 0; i < ARRAYSIZE( buttons ); i++ )
|
|
{
|
|
if( !buttons[i] )
|
|
{
|
|
x = 8;
|
|
y += font->charHeight;
|
|
continue;
|
|
}
|
|
|
|
qboolean pressed = Key_IsDown( buttons[i] );
|
|
int btn_w = 0;
|
|
rgba_t color;
|
|
string name;
|
|
|
|
Q_strncpy( name, Key_KeynumToString( buttons[i] ), sizeof( name ));
|
|
|
|
char *p = Q_strstr( name, "_BUTTON" );
|
|
if( p )
|
|
*p = 0;
|
|
|
|
MakeRGBA( color, pressed ? 0 : 255, pressed ? 255 : 0, 0, 255 );
|
|
CL_DrawString( x, y, name, color, font, 0 );
|
|
CL_DrawStringLen( font, name, &btn_w, NULL, 0 );
|
|
x += btn_w + 6;
|
|
}
|
|
}
|
|
|
|
static void Joy_CalibrateGyro_f( void )
|
|
{
|
|
if( !joy_have_gyro.value )
|
|
{
|
|
Con_Printf( "Current active gamepad doesn't have gyroscope\n" );
|
|
return;
|
|
}
|
|
|
|
Platform_CalibrateGamepadGyro();
|
|
}
|
|
|
|
/*
|
|
=============
|
|
Joy_Init
|
|
|
|
Main init procedure
|
|
=============
|
|
*/
|
|
void Joy_Init( void )
|
|
{
|
|
Cmd_AddRestrictedCommand( "joy_calibrate_gyro", Joy_CalibrateGyro_f, "calibrate gamepad gyroscope. You must to put gamepad on stationary surface" );
|
|
|
|
Cvar_RegisterVariable( &joy_pitch );
|
|
Cvar_RegisterVariable( &joy_yaw );
|
|
Cvar_RegisterVariable( &joy_side );
|
|
Cvar_RegisterVariable( &joy_forward );
|
|
|
|
Cvar_RegisterVariable( &joy_lt_threshold );
|
|
Cvar_RegisterVariable( &joy_rt_threshold );
|
|
|
|
// emit a key event at 75% axis move
|
|
Cvar_RegisterVariable( &joy_side_key_threshold );
|
|
Cvar_RegisterVariable( &joy_forward_key_threshold );
|
|
|
|
// by default, we rely on deadzone detection come from system, but some glitchy devices report false deadzones
|
|
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_have_gyro );
|
|
Cvar_RegisterVariable( &joy_calibrated );
|
|
Cvar_RegisterVariable( &joy_enable );
|
|
|
|
Cvar_RegisterVariable( &joy_gyro_pitch );
|
|
Cvar_RegisterVariable( &joy_gyro_yaw );
|
|
Cvar_RegisterVariable( &joy_gyro_roll );
|
|
Cvar_RegisterVariable( &joy_debug );
|
|
|
|
// renamed from -nojoy to -noenginejoy to not conflict with
|
|
// client.dll's joystick support
|
|
if( Sys_CheckParm( "-noenginejoy" ))
|
|
{
|
|
Cvar_FullSet( "joy_enable", "0", FCVAR_READ_ONLY );
|
|
return;
|
|
}
|
|
|
|
Platform_JoyInit();
|
|
|
|
joy_initialized = true;
|
|
}
|
|
|
|
/*
|
|
===========
|
|
Joy_Shutdown
|
|
|
|
Shutdown joystick code
|
|
===========
|
|
*/
|
|
void Joy_Shutdown( void )
|
|
{
|
|
Platform_JoyShutdown();
|
|
}
|