#include "common.h"
#include "vg/vg_loader.h"
+#define VG_MAX_CONTROLLERS 4
+
VG_STATIC inline float vg_get_axis( const char *axis );
VG_STATIC inline int vg_get_button( const char *button );
*/
VG_STATIC inline int vg_get_button_down( const char *button );
VG_STATIC inline int vg_get_button_up( const char *button );
+VG_STATIC float controller_deadzone = 0.05f;
-VG_STATIC float g_controller_deadzone = 0.05f;
-
-enum vg_button_state
-{
+enum vg_button_state{
k_button_state_down = 1,
k_button_state_up = 3,
k_button_state_pressed = 2,
k_button_state_none = 0
};
-struct input_binding
-{
+struct input_binding{
const char *name;
- enum input_type
- {
+ enum input_type{
k_input_type_button,
k_input_type_axis,
k_input_type_axis_norm,
}
type;
- union
- {
- struct input_axis
- {
+ union{
+ struct input_axis{
SDL_GameControllerAxis gamepad_axis;
SDL_Keycode keyboard_positive,
keyboard_negative;
}
axis;
- struct
- {
+ struct{
SDL_GameControllerButton gamepad_id;
SDL_Keycode keyboard_id;
int mouse_id;
struct input_binding named_inputs[ 32 ];
u32 named_input_count;
- const char *controller_name;
- SDL_GameController *controller_handle; /* null if unplugged */
- SDL_JoystickID controller_joystick_id;
- int controller_should_use_trackpad_look;
+ struct vg_controller{
+ SDL_GameController *handle; /* handle for controller. NULL if unused */
+ SDL_JoystickID instance_id; /* uid used in events */
- float controller_axises[ SDL_CONTROLLER_AXIS_MAX ];
- int controller_buttons[ SDL_CONTROLLER_BUTTON_MAX ];
+ enum evg_controller_type{
+ k_vg_controller_type_standard,
+ k_vg_controller_type_trackpads
+ }
+ type;
+
+ float axises[ SDL_CONTROLLER_AXIS_MAX ];
+ int buttons[ SDL_CONTROLLER_BUTTON_MAX ];
+ }
+ controllers[4];
+
+ int active_controller_index; /* most recent controller (by button press)
+ will be -1 if no controllers active */
+
+ /* what the user is currently using. the keyboard and controller are still
+ * active simultaneously, but this reflects what the UI should show */
+ enum userinput_method{
+ k_userinput_method_xbox,
+ k_userinput_method_playstation,
+ k_userinput_method_steamgeneric,
+ k_userinput_method_kbm
+ }
+ input_method;
}
-VG_STATIC vg_input;
+static vg_input;
VG_STATIC void vg_create_unnamed_input( struct input_binding *bind,
enum input_type type )
if( name[0] == '+' || name[0] == '-' )
name ++;
- for( u32 i=0; i<vg_input.named_input_count; i++ )
- {
+ for( u32 i=0; i<vg_input.named_input_count; i++ ){
struct input_binding *bind = &vg_input.named_inputs[i];
if( !strcmp( bind->name, name ) )
return bind;
if( input == -1 )
return NULL;
- if( input_type == k_input_type_keyboard_key )
- {
- if( (input >= SDLK_a) && (input <= SDLK_z) )
- {
+ if( input_type == k_input_type_keyboard_key ){
+ if( (input >= SDLK_a) && (input <= SDLK_z) ){
return &"a\0b\0c\0d\0e\0f\0g\0h\0i\0j\0k\0l\0m\0n\0o\0p\0"
"q\0r\0s\0t\0u\0v\0w\0x\0y\0z\0"[(input-SDLK_a)*2];
}
- if( (input >= SDLK_0) && (input <= SDLK_9) )
- {
+ if( (input >= SDLK_0) && (input <= SDLK_9) ){
return &"0\0" "1\0" "2\0" "3\0" "4\0"
"5\0" "6\0" "7\0" "8\0" "9\0"[(input-SDLK_0)*2];
}
}
- for( int i=0; i<vg_list_size(vg_all_bindable_inputs); i++ )
- {
+ for( int i=0; i<vg_list_size(vg_all_bindable_inputs); i++ ){
struct input_en *desc = &vg_all_bindable_inputs[i];
if( (desc->type == input_type) && (desc->id == input) )
VG_STATIC enum input_type vg_str_to_input( const char *str, u32 *input )
{
- if( !str )
- {
+ if( !str ){
*input = -1;
return k_input_type_unknown;
}
u32 len = strlen(str);
- if( len == 0 )
- {
+ if( len == 0 ){
*input = -1;
return k_input_type_unknown;
}
- if( len == 1 )
- {
+ if( len == 1 ){
u8 uch = str[0];
- if( (uch >= (u8)'a') && (uch <= (u8)'z') )
- {
+ if( (uch >= (u8)'a') && (uch <= (u8)'z') ){
*input = SDLK_a + (uch-(u8)'a');
return k_input_type_keyboard_key;
}
- if( (uch >= (u8)'0') && (uch <= (u8)'9') )
- {
+ if( (uch >= (u8)'0') && (uch <= (u8)'9') ){
*input = SDLK_0 + (uch-(u8)'0');
return k_input_type_keyboard_key;
}
}
- for( int i=0; i<vg_list_size(vg_all_bindable_inputs); i++ )
- {
+ for( int i=0; i<vg_list_size(vg_all_bindable_inputs); i++ ){
struct input_en *desc = &vg_all_bindable_inputs[i];
- if( !strcmp( desc->alias, str ) )
- {
+ if( !strcmp( desc->alias, str ) ){
*input = desc->id;
return desc->type;
}
vg_input_to_str(bind->axis.keyboard_negative,
k_input_type_keyboard_key ));
}
- else
- {
+ else{
vg_info( " gamepad_id: %s\n",
vg_input_to_str(bind->button.gamepad_id, k_input_type_gamepad_button));
vg_info( " keyboard_id: %s\n",
{
int axis_mod = 0;
char modch = ' ';
- if( (mod[0] == '-') || (mod[0] == '+') )
- {
+ if( (mod[0] == '-') || (mod[0] == '+') ){
axis_mod = 1;
modch = mod[0];
mod ++;
}
int invert = 0;
- if( (str[0] == '-' ) )
- {
+ if( (str[0] == '-' ) ){
invert = 1;
str ++;
}
u32 id;
enum input_type type = vg_str_to_input( str, &id );
- if( bind->type == k_input_type_button )
- {
- if( axis_mod )
- {
+ if( bind->type == k_input_type_button ){
+ if( axis_mod ){
vg_error( "Cannot use axis modifiers on button input!\n" );
return;
}
- if( invert )
- {
+ if( invert ){
vg_error( "Cannot invert button input!\n" );
return;
}
bind->button.mouse_id = id;
else if( type == k_input_type_gamepad_button )
bind->button.gamepad_id = id;
- else
- {
+ else{
vg_error( "Unknown button or key '%s'\n", str );
return;
}
else if( (bind->type == k_input_type_axis ) ||
(bind->type == k_input_type_axis_norm))
{
- if( axis_mod )
- {
- if( type == k_input_type_keyboard_key )
- {
- if( invert )
- {
+ if( axis_mod ){
+ if( type == k_input_type_keyboard_key ){
+ if( invert ){
vg_error( "Cannot invert a keyboard key!\n" );
return;
}
else
bind->axis.keyboard_negative = id;
}
- else
- {
+ else{
vg_error( "You can only bind keyboard keys to +- axises\n" );
return;
}
}
- else
- {
- if( type == k_input_type_gamepad_axis )
- {
+ else{
+ if( type == k_input_type_gamepad_axis ){
bind->axis.gamepad_inverted = invert;
bind->axis.gamepad_axis = id;
}
- else
- {
+ else{
vg_error( "You can only bind gamepad axises to this\n" );
return;
}
VG_STATIC int vg_rebind_input_cmd( int argc, const char *argv[] )
{
- if( argc == 0 )
- {
+ if( argc == 0 ){
vg_info( "Usage: bind jump x\n" );
vg_info( " bind -steerh j\n" );
vg_info( " bind steerh gp-ls-h\n" );
const char *str_bind_name = argv[0];
struct input_binding *bind = vg_get_named_input( str_bind_name );
- if( !bind )
- {
+ if( !bind ){
vg_error( "There is no bind with that name '%s'\n", str_bind_name );
return 0;
}
- if( argc == 1 )
- {
+ if( argc == 1 ){
vg_print_binding_info( bind );
return 0;
}
- if( argc == 2 )
- {
+ if( argc == 2 ){
const char *str_input_id = argv[1];
vg_apply_bind_str( bind, str_bind_name, str_input_id );
const char *str_bind_name = argv[0];
- if( argc == 1 )
- {
- for( u32 i=0; i<vg_input.named_input_count; i++ )
- {
+ if( argc == 1 ){
+ for( u32 i=0; i<vg_input.named_input_count; i++ ){
struct input_binding *bind = &vg_input.named_inputs[i];
console_suggest_score_text( bind->name, argv[argc-1], 0 );
}
}
-
- else if( argc == 2 )
- {
- for( int i=0; i<vg_list_size(vg_all_bindable_inputs); i++ )
- {
+ else if( argc == 2 ){
+ for( int i=0; i<vg_list_size(vg_all_bindable_inputs); i++ ){
struct input_en *desc = &vg_all_bindable_inputs[i];
console_suggest_score_text( desc->alias, argv[argc-1], 0 );
}
VG_STATIC void vg_input_update( u32 num, struct input_binding *binds )
{
- if( vg_console.enabled )
- {
- for( i32 i=0; i<num; i++ )
- {
+ if( vg_console.enabled ){
+ for( i32 i=0; i<num; i++ ){
struct input_binding *bind = &binds[i];
- if( bind->type == k_input_type_button )
- {
+ if( bind->type == k_input_type_button ){
bind->button.prev = bind->button.value;
bind->button.value = 0;
}
return;
}
- for( i32 i=0; i<num; i++ )
- {
+ struct vg_controller *acontroller = NULL;
+
+ if( vg_input.active_controller_index != -1 )
+ acontroller = &vg_input.controllers[vg_input.active_controller_index];
+
+ for( i32 i=0; i<num; i++ ){
struct input_binding *bind = &binds[i];
- if( bind->type == k_input_type_button )
- {
+ if( bind->type == k_input_type_button ){
bind->button.prev = bind->button.value;
bind->button.value = 0;
- if( bind->button.gamepad_id != -1 )
- bind->button.value |=
- vg_input.controller_buttons[ bind->button.gamepad_id ];
+ if( acontroller && (bind->button.gamepad_id != -1) )
+ bind->button.value |= acontroller->buttons[bind->button.gamepad_id];
if( bind->button.keyboard_id != -1 )
- {
bind->button.value |= vg_getkey( bind->button.keyboard_id );
- }
- if( bind->button.mouse_id != -1 )
- {
- if( SDL_GetMouseState(NULL, NULL) &
- SDL_BUTTON( bind->button.mouse_id ) )
+ if( bind->button.mouse_id != -1 ){
+ if(SDL_GetMouseState(NULL,NULL) & SDL_BUTTON(bind->button.mouse_id))
bind->button.value |= 1;
}
}
- else if( bind->type == k_input_type_axis )
- {
+ else if( bind->type == k_input_type_axis ){
float keyboard_value = 0.0f,
gamepad_value = 0.0f;
if( vg_getkey( bind->axis.keyboard_negative ) )
keyboard_value -= 1.0f;
- if( bind->axis.gamepad_axis != -1 )
- {
- gamepad_value =
- vg_input.controller_axises[ bind->axis.gamepad_axis ];
+ if( acontroller && (bind->axis.gamepad_axis != -1) ){
+ gamepad_value = acontroller->axises[ bind->axis.gamepad_axis ];
if( bind->axis.gamepad_inverted )
gamepad_value *= -1.0f;
}
- float deadz = vg_clampf( g_controller_deadzone, 0.0f, 0.999f ),
+ float deadz = vg_clampf( controller_deadzone, 0.0f, 0.999f ),
high = vg_maxf( 0.0f, fabsf(gamepad_value) - deadz ),
norm = high / (1.0f-deadz);
else
bind->axis.value = gamepad_value;
}
- else if( bind->type == k_input_type_axis_norm )
- {
+ else if( bind->type == k_input_type_axis_norm ){
float value = 0.0f;
if( bind->axis.keyboard_positive != -1 )
if( vg_getkey( bind->axis.keyboard_positive ))
value = 1.0f;
- if( bind->axis.gamepad_axis != -1 )
- value = vg_maxf( value,
- vg_input.controller_axises[bind->axis.gamepad_axis] );
+ if( acontroller && (bind->axis.gamepad_axis != -1) ){
+ float value1 = acontroller->axises[bind->axis.gamepad_axis];
+ value = vg_maxf( value, value1 );
+ }
bind->axis.value = value;
}
}
}
-VG_STATIC void vg_input_controller_event( SDL_Event *ev )
+/*
+ * takes SDL device index, and tries to open that on any free channel
+ */
+VG_STATIC void vg_open_gamecontroller( Sint32 index )
{
- if( ev->type == SDL_CONTROLLERAXISMOTION )
- {
- if( ev->caxis.which == vg_input.controller_joystick_id )
- {
- vg_input.controller_axises[ ev->caxis.axis ] =
- (float)ev->caxis.value / 32767.0f;
+ struct vg_controller *controller = NULL;
+ int vg_id = 0;
+ const char *name = SDL_GameControllerNameForIndex( index );
+ SDL_JoystickID instance_id = SDL_JoystickGetDeviceInstanceID( index );
+
+ if( instance_id == -1 ){
+ vg_error( ". Invalid device index (vg_open_gamecontroller)\n" );
+ return;
+ }
+
+ for( int j=0; j<VG_MAX_CONTROLLERS; j++ ){
+ struct vg_controller *esta = &vg_input.controllers[j];
+
+ if( esta->handle ){
+ if( esta->instance_id == instance_id ){
+ vg_warn( " . SDL_JoystickID[%d] is already in open at index #%d\n",
+ esta->instance_id, j );
+ return;
+ }
+ }
+ else{
+ if( !controller ){
+ controller = &vg_input.controllers[j];
+ vg_id = j;
+ }
}
}
- else if( ev->type == SDL_CONTROLLERBUTTONDOWN )
- {
- if( ev->cbutton.which == vg_input.controller_joystick_id )
- vg_input.controller_buttons[ ev->cbutton.button ] = 1;
+
+ if( controller ){
+ controller->handle = SDL_GameControllerOpen( index );
+ controller->instance_id = instance_id;
+
+ if( controller->handle ){
+ vg_success(
+ " . opened SDL_JoystickID[%d] as controller '%s' at index #%d\n",
+ instance_id, name, vg_id );
+
+ controller->axises[ SDL_CONTROLLER_AXIS_TRIGGERLEFT ] = -1.0f;
+ controller->axises[ SDL_CONTROLLER_AXIS_TRIGGERRIGHT ] = -1.0f;
+ }
+ else{
+ vg_error( ". Failed to attach game controller '%s'. Reason: %s\n",
+ name, SDL_GetError() );
+ }
}
- else if( ev->type == SDL_CONTROLLERBUTTONUP )
- {
- if( ev->cbutton.which == vg_input.controller_joystick_id )
- vg_input.controller_buttons[ ev->cbutton.button ] = 0;
+ else{
+ vg_error( ". Too many controllers open! ignoring '%s'\n", name );
+
}
}
-VG_STATIC void vg_try_attach_controller(void)
+VG_STATIC void vg_input_device_event( SDL_Event *ev )
{
- int joy_count = SDL_NumJoysticks();
- for( int i=0; i<joy_count; i++ )
- {
- if( SDL_IsGameController(i) )
- {
- vg_input.controller_handle = SDL_GameControllerOpen(i);
- vg_input.controller_joystick_id = i;
- vg_success( "Attached game controller with joystick ID %d\n", i );
- return;
+ if( ev->type == SDL_CONTROLLERDEVICEADDED ){
+ int is_controller = SDL_IsGameController( ev->cdevice.which );
+ const char *name = SDL_JoystickNameForIndex( ev->cdevice.which );
+
+ Sint32 index = ev->cdevice.which;
+ SDL_JoystickID instance_id = SDL_JoystickGetDeviceInstanceID( index );
+ vg_info( "SDL_CONTROLLERDEVICEADDED | device index: %d, name: '%s'\n",
+ index, name );
+
+ if( is_controller ){
+ vg_open_gamecontroller( index );
+ }
+ }
+ else if( ev->type == SDL_CONTROLLERDEVICEREMOVED ){
+ vg_info( "SDL_CONTROLLERDEVICEREMOVED | instance_id: %d\n",
+ ev->cdevice.which );
+
+ for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
+ struct vg_controller *controller = &vg_input.controllers[i];
+
+ if( controller->handle ){
+ if( controller->instance_id == ev->cdevice.which ){
+ vg_info( " . closing controller at index #%d\n", i );
+ SDL_GameControllerClose( controller->handle );
+ controller->handle = NULL;
+ controller->instance_id = -1;
+
+ if( vg_input.active_controller_index == i ){
+ vg_input.active_controller_index = -1;
+ vg_info( " . active controller is now keyboard and mouse\n" );
+ }
+ break;
+ }
+ }
}
}
}
-
-VG_STATIC void vg_update_inputs(void)
+VG_STATIC void vg_input_controller_event( SDL_Event *ev )
{
- vg_input.sdl_keys = SDL_GetKeyboardState(NULL);
+ if( ev->type == SDL_CONTROLLERAXISMOTION ){
+ for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
+ struct vg_controller *esta = &vg_input.controllers[i];
+
+ if( ev->caxis.which == esta->instance_id ){
+ float value = (float)ev->caxis.value / 32767.0f;
+ esta->axises[ ev->caxis.axis ] = value;
+ break;
+ }
+ }
+ }
+ else if( ev->type == SDL_CONTROLLERBUTTONDOWN ){
+ struct vg_controller *active = NULL;
- if( vg_input.controller_handle )
- {
- if( !SDL_GameControllerGetAttached( vg_input.controller_handle ) )
- {
- SDL_GameControllerClose( vg_input.controller_handle );
- vg_input.controller_handle = NULL;
+ if( vg_input.active_controller_index != -1 )
+ active = &vg_input.controllers[vg_input.active_controller_index];
+
+ if( !active || (ev->cbutton.which != active->instance_id) ){
+ active = NULL;
+ vg_input.active_controller_index = -1;
+
+ for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
+ if( vg_input.controllers[i].instance_id == ev->cbutton.which ){
+ active = &vg_input.controllers[i];
+ vg_input.active_controller_index = i;
+ break;
+ }
+ }
+
+ if( active ){
+ vg_info( "Switching active controller index to #%d\n",
+ vg_input.active_controller_index );
+ }
+ else{
+ vg_error( "Input out of range (SDL_JoystickID#%d)\n",
+ ev->cbutton.which );
+ }
}
+
+ if( active )
+ active->buttons[ ev->cbutton.button ] = 1;
}
+ else if( ev->type == SDL_CONTROLLERBUTTONUP ){
+ for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
+ struct vg_controller *esta = &vg_input.controllers[i];
- if( !vg_input.controller_handle )
- {
- vg_input.controller_axises[ SDL_CONTROLLER_AXIS_TRIGGERLEFT ] = -1.0f;
- vg_input.controller_axises[ SDL_CONTROLLER_AXIS_TRIGGERRIGHT ] = -1.0f;
- vg_try_attach_controller();
+ if( ev->cbutton.which == esta->instance_id ){
+ esta->buttons[ ev->cbutton.button ] = 0;
+ break;
+ }
+ }
+ }
+}
+
+VG_STATIC void vg_process_inputs(void)
+{
+ int count;
+ vg_input.sdl_keys = SDL_GetKeyboardState( &count );
+
+ if( vg_input.input_method != k_userinput_method_kbm ){
+ /* check for giving keyboard priority */
+ for( int i=0; i<count; i++ ){
+ if( vg_input.sdl_keys[i] ){
+ vg_input.input_method = k_userinput_method_kbm;
+ break;
+ }
+ }
+
+ /* check for giving mouse priority */
+ if( SDL_GetMouseState(NULL,NULL) &
+ (SDL_BUTTON(SDL_BUTTON_LEFT)|SDL_BUTTON(SDL_BUTTON_RIGHT)) )
+ {
+ vg_input.input_method = k_userinput_method_kbm;
+ }
}
/* update all inputs */
return 0;
}
-VG_STATIC void vg_input_init(void)
+VG_STATIC void async_vg_input_init( void *payload, u32 size )
{
- vg_acquire_thread_sync();
+ vg_console_reg_cmd( "bind", vg_rebind_input_cmd, vg_rebind_input_cmd_poll );
- vg_function_push( (struct vg_cmd)
- {
- .name = "bind",
- .function = vg_rebind_input_cmd,
- .poll_suggest = vg_rebind_input_cmd_poll
- });
-
- vg_var_push( (struct vg_var){
- .name = "controller_deadzone",
- .data = &g_controller_deadzone,
- .data_type = k_var_dtype_f32,
- .opt_f32 = { .clamp = 0 },
- .persistent = 1
- });
-
- vg_info( "Checking for controller\n" );
+ VG_VAR_F32( controller_deadzone, flags=VG_VAR_PERSISTENT );
+
+ vg_info( "Checking for controllers\n" );
SDL_GameControllerAddMappingsFromFile( "gamecontrollerdb.txt" );
int joy_count = SDL_NumJoysticks();
- for( int i=0; i<joy_count; i++ )
- {
- vg_info( "joystick %d: %s [gamecontroller: %d]\n",
- i, SDL_JoystickNameForIndex( i ),
- SDL_IsGameController(i) );
- }
+ for( int i=0; i<joy_count; i++ ) {
+ const char *name = SDL_JoystickNameForIndex( i );
+ int is_controller = SDL_IsGameController(i);
- vg_try_attach_controller();
+ vg_info( "%d: %s [controller: %d]\n", i, name, is_controller );
- vg_input.controller_axises[ SDL_CONTROLLER_AXIS_TRIGGERLEFT ] = -1.0f;
- vg_input.controller_axises[ SDL_CONTROLLER_AXIS_TRIGGERRIGHT ] = -1.0f;
+ if( is_controller ){
+ vg_open_gamecontroller( i );
+ }
+ }
+}
- vg_release_thread_sync();
+VG_STATIC void vg_input_init(void)
+{
+ vg_async_call( async_vg_input_init, NULL, 0 );
}
VG_STATIC void vg_input_free(void)
{
- if( vg_input.controller_handle )
- {
- SDL_GameControllerClose( vg_input.controller_handle );
- vg_input.controller_handle = NULL;
+ for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
+ struct vg_controller *controller = &vg_input.controllers[i];
+
+ if( controller->handle ){
+ SDL_GameControllerClose( controller->handle );
+ controller->handle = NULL;
+ }
}
}