X-Git-Url: https://harrygodden.com/git/?p=vg.git;a=blobdiff_plain;f=vg_input.c;fp=vg_input.c;h=3c138d0dad85b2e866153cbc3468e30c5340df51;hp=0000000000000000000000000000000000000000;hb=3b14f3dcd5bf9dd3c85144f2123d667bfa4bb63f;hpb=fce86711735b15bff37de0f70716808410fcf269 diff --git a/vg_input.c b/vg_input.c new file mode 100644 index 0000000..3c138d0 --- /dev/null +++ b/vg_input.c @@ -0,0 +1,584 @@ +/* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved */ + +#include "vg_input.h" +#include "vg_loader.h" +#include "vg_engine.h" +#include "vg_async.h" +#include "vg_string.h" + +f32 controller_deadzone = 0.05f; + +struct vg_input vg_input = { + .active_controller_index = -2 +}; + +u8 vg_getkey( SDL_Keycode kc ) +{ + SDL_Scancode sc = SDL_GetScancodeFromKey( kc ); + return vg_input.sdl_keys[sc]; +} + +/* + * takes SDL device index, and tries to open that on any free channel + */ +static int vg_open_gamecontroller( Sint32 index ) +{ + 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 -1; + } + + for( int j=0; jhandle ){ + if( esta->instance_id == instance_id ){ + vg_warn( " . SDL_JoystickID[%d] is already in open at index #%d\n", + esta->instance_id, j ); + return -1; + } + } + else{ + if( !controller ){ + controller = &vg_input.controllers[j]; + vg_id = j; + } + } + } + + 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 ); + + for( u32 i=0; i< SDL_CONTROLLER_BUTTON_MAX; i++ ) + controller->buttons[i] = 0; + + for( u32 i=0; i< SDL_CONTROLLER_AXIS_MAX; i++ ) + controller->axises[i] = 0.0f; + + if( vg_input.active_controller_index == -2 ){ + vg_input.active_controller_index = vg_id; + vg_input.display_input_method = k_input_method_controller; + vg_input.display_input_type = + SDL_GameControllerGetType( controller->handle ); + } + + return vg_id; + } + else{ + vg_error( ". Failed to attach game controller '%s'. Reason: %s\n", + name, SDL_GetError() ); + return -1; + } + } + else{ + vg_error( ". Too many controllers open! ignoring '%s'\n", name ); + return -1; + } +} + +void vg_input_device_event( SDL_Event *ev ) +{ + 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; ihandle ){ + 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_input.display_input_method = k_input_method_kbm; + vg_info( "display_input: k_input_method_kbm\n" ); + } + break; + } + } + } + } +} + +void vg_input_controller_event( SDL_Event *ev ) +{ + if( ev->type == SDL_CONTROLLERAXISMOTION ){ + for( int i=0; icaxis.which == esta->instance_id ){ + float value = (float)ev->caxis.value / 32767.0f; + + if( ev->caxis.axis == SDL_CONTROLLER_AXIS_LEFTX || + ev->caxis.axis == SDL_CONTROLLER_AXIS_LEFTY || + ev->caxis.axis == SDL_CONTROLLER_AXIS_RIGHTX || + ev->caxis.axis == SDL_CONTROLLER_AXIS_RIGHTY ) + { + float deadz = vg_clampf( controller_deadzone, 0.0f, 0.999f ), + high = vg_maxf( 0.0f, fabsf(value) - deadz ); + + value = vg_signf(value) * (high / (1.0f-deadz)); + } + + esta->axises[ ev->caxis.axis ] = value; + break; + } + } + } + else if( ev->type == SDL_CONTROLLERBUTTONDOWN ){ + struct vg_controller *active = NULL; + + if( vg_input.active_controller_index >= 0 ) + 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; + vg_input.display_input_method = k_input_method_kbm; + + for( int i=0; icbutton.which ){ + active = &vg_input.controllers[i]; + vg_input.active_controller_index = i; + vg_input.display_input_type = + SDL_GameControllerGetType(active->handle); + 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 ){ + if( vg_input.display_input_method != k_input_method_controller ){ + vg_input.display_input_method = k_input_method_controller; + vg_info( "display_input: k_input_method_controller\n" ); + } + active->buttons[ ev->cbutton.button ] = 1; + } + } + else if( ev->type == SDL_CONTROLLERBUTTONUP ){ + for( int i=0; icbutton.which == esta->instance_id ){ + esta->buttons[ ev->cbutton.button ] = 0; + break; + } + } + } +} + +void vg_process_inputs(void) +{ + int count; + vg_input.sdl_keys = SDL_GetKeyboardState( &count ); + vg_input.sdl_mouse = SDL_GetMouseState(NULL,NULL); + + if( vg_input.display_input_method != k_input_method_kbm ){ + /* check for giving keyboard priority */ + for( int i=0; ihandle ){ + SDL_GameControllerClose( controller->handle ); + controller->handle = NULL; + } + } +} + +struct vg_controller *vg_active_controller(void) +{ + if( vg_input.active_controller_index >= 0 ) + return &vg_input.controllers[vg_input.active_controller_index]; + else + return NULL; +} + +u8 vg_controller_button( SDL_GameControllerButton button ) +{ + struct vg_controller *c = vg_active_controller(); + if( c ) return c->buttons[ button ]; + else return 0; +} + +f32 vg_controller_axis( SDL_GameControllerAxis axis ) +{ + struct vg_controller *c = vg_active_controller(); + if( c ) return c->axises[ axis ]; + else return 0; +} + +static void vg_input_apply_to_u8( vg_input_op mode, u8 data, u8 *inout_result ){ + if ( mode == vg_mode_absmax ) *inout_result |= data; + else if( mode == vg_mode_mul ) *inout_result &= data; + else vg_fatal_error( "mode not supported for destination type (%d)", mode ); +} + +static void vg_input_apply_to_f32( vg_input_op mode, f32 data, + f32 *inout_result ){ + if ( mode == vg_mode_absmax ){ + if( fabsf(data) > fabsf(*inout_result) ) + *inout_result = data; + } + else if( mode == vg_mode_max ) *inout_result = vg_maxf(*inout_result,data); + else if( mode == vg_mode_mul ) *inout_result *= (f32)data; + else if( mode == vg_mode_sub ) *inout_result -= (f32)data; + else if( mode == vg_mode_add ) *inout_result += (f32)data; + else vg_fatal_error( "mode not supported for destination type (%d)", mode ); +} + +/* + * Run an input program. out_result must point to memory with sufficient + * storage respective to the size set by type. + */ +void vg_exec_input_program( enum vg_input_type type, vg_input_op *ops, + void *out_result ){ + u8 *out_button = NULL; + f32 *out_joy = NULL; + + if( type == k_vg_input_type_button_u8 ){ + out_button = out_result; + *out_button = 0; + } + else if( type == k_vg_input_type_axis_f32 ){ + out_joy = out_result; + out_joy[0] = 0.0f; + } + else if( type == k_vg_input_type_joy_v2f ){ + out_joy = out_result; + out_joy[0] = 0.0f; + out_joy[1] = 0.0f; + } + + /* computer state */ + vg_input_op mode = vg_mode_absmax; + u32 pc = 0, index = 0; + +next_code:; + vg_input_op op = ops[ pc ++ ]; + + if( (op >= vg_mode_mul) && (op <= vg_mode_max) ) + mode = op; + else if( (op == vg_keyboard) || (op == vg_mouse) || (op == vg_joy_button) ){ + u8 state = 0; + + if( op == vg_keyboard ) + state = vg_getkey(ops[pc ++]); + else if( op == vg_mouse ) + state = (vg_input.sdl_mouse & SDL_BUTTON(ops[pc ++]))?1:0; + else + state = vg_controller_button(ops[pc ++]); + + if( type == k_vg_input_type_button_u8 ) + vg_input_apply_to_u8( mode, state, out_button ); + else + vg_input_apply_to_f32( mode, (f32)state, &out_joy[index] ); + } + else if( op == vg_joy_axis ){ + f32 state = vg_controller_axis( ops[pc ++] ); + if( type == k_vg_input_type_button_u8 ) + vg_input_apply_to_u8( mode, state>0.5f?1:0, out_button ); + else + vg_input_apply_to_f32( mode, state, &out_joy[index] ); + } + else if( (op == vg_joy_ls) || (op == vg_joy_rs) ){ + if( type == k_vg_input_type_joy_v2f ){ + vg_input_apply_to_f32( mode, + vg_controller_axis( op==vg_joy_ls? SDL_CONTROLLER_AXIS_LEFTX: + SDL_CONTROLLER_AXIS_RIGHTX), + &out_joy[0] ); + vg_input_apply_to_f32( mode, + vg_controller_axis( op==vg_joy_ls? SDL_CONTROLLER_AXIS_LEFTY: + SDL_CONTROLLER_AXIS_RIGHTY), + &out_joy[1] ); + } + } + else if( op == vg_index ) + index = ops[pc ++]; + else if( op == vg_end ) + return; + else if( op == vg_normalize ) + v2_normalize( out_joy ); + else if( op == vg_gui_visible ) + pc ++; + else + vg_fatal_error( "unknown op\n" ); + + goto next_code; +} + +/* + * Get vendor specific button glyphs based on SDL button ID + */ +const char *controller_button_str( SDL_GameControllerButton button ) +{ + static const char *controller_glyphs[ SDL_CONTROLLER_BUTTON_MAX ][2] = { + /* xbox/generic playstation */ + [ SDL_CONTROLLER_BUTTON_A ] = { "\x1e\x85","\x1e\x82" }, + [ SDL_CONTROLLER_BUTTON_B ] = { "\x1e\x86","\x1e\x81" }, + [ SDL_CONTROLLER_BUTTON_X ] = { "\x1e\x83","\x1e\x7f" }, + [ SDL_CONTROLLER_BUTTON_Y ] = { "\x1e\x84","\x1e\x80" }, + [ SDL_CONTROLLER_BUTTON_LEFTSTICK ] = { "\x87", "\x87" }, + [ SDL_CONTROLLER_BUTTON_RIGHTSTICK ] = { "\x8b", "\x8b" }, + [ SDL_CONTROLLER_BUTTON_LEFTSHOULDER ] = { "\x91", "\x91" }, + [ SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ]= { "\x92", "\x92" }, + [ SDL_CONTROLLER_BUTTON_DPAD_LEFT ] = { "\x1e\x93","\x1e\x93" }, + [ SDL_CONTROLLER_BUTTON_DPAD_UP ] = { "\x1e\x94","\x1e\x94" }, + [ SDL_CONTROLLER_BUTTON_DPAD_RIGHT ] = { "\x1e\x95","\x1e\x95" }, + [ SDL_CONTROLLER_BUTTON_DPAD_DOWN ] = { "\x1e\x96","\x1e\x96" }, + [ SDL_CONTROLLER_BUTTON_GUIDE ] = { "\x91", "\x91" }, + }; + + if( vg_input.display_input_type == SDL_CONTROLLER_TYPE_PS3 || + vg_input.display_input_type == SDL_CONTROLLER_TYPE_PS4 || + vg_input.display_input_type == SDL_CONTROLLER_TYPE_PS5 ) + { + return controller_glyphs[ button ][ 1 ]; + } + else if( vg_input.display_input_type == + SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO || + vg_input.display_input_type == + SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT || + vg_input.display_input_type == + SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR || + vg_input.display_input_type == + SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT ) + { + return NULL; + } + else + return controller_glyphs[ button ][ 0 ]; +} + +/* + * Cat keyboard key string. special_glyphs include SR glyphs + */ +void vg_keyboard_key_string( vg_str *str, u32 key, int special_glyphs ) +{ + if( (key >= SDLK_a) && (key <= SDLK_z) ){ + key = (key-SDLK_a)+(u32)'A'; + + if( special_glyphs ){ + vg_strcatch( str, '\x1f' ); + vg_strcatch( str, key ); + vg_strcatch( str, ' ' ); + } + else + vg_strcatch( str, key ); + } + else if( (key == SDLK_LSHIFT) || (key == SDLK_RSHIFT) ) + vg_strcat( str, special_glyphs? "\x9e": "shift" ); + else if( (key == SDLK_LCTRL) || (key == SDLK_RCTRL) ) + vg_strcat( str, special_glyphs? "\x9f": "ctrl" ); + else if( (key == SDLK_LALT) || (key == SDLK_RALT) ) + vg_strcat( str, special_glyphs? "\xa0": "alt" ); + else if( key == SDLK_SPACE ) + vg_strcat( str, special_glyphs? "\xa1": "space" ); + else if( (key == SDLK_RETURN) || (key == SDLK_RETURN2) ) + vg_strcat( str, special_glyphs? "\xa2": "return" ); + else if( key == SDLK_ESCAPE ) + vg_strcat( str, special_glyphs? "\xa3": "escape" ); + else if( key == SDLK_RIGHT ) + vg_strcat( str, special_glyphs? "\x1f\x95 ": "right" ); + else if( key == SDLK_LEFT ) + vg_strcat( str, special_glyphs? "\x1f\x93 ": "left" ); + else if( key == SDLK_UP ) + vg_strcat( str, special_glyphs? "\x1f\x94 ": "up" ); + else if( key == SDLK_DOWN ) + vg_strcat( str, special_glyphs? "\x1f\x96 ": "down" ); + else { + vg_strcat( str, "keyboard key #" ); + vg_strcati32( str, key ); + } +} + +/* + * Cat mouse button string. special_glyphs include SR glyphs + */ +void vg_mouse_button_string( vg_str *str, u32 button, int special_glyphs ) +{ + if ( button == SDL_BUTTON_LEFT ) + vg_strcat( str, special_glyphs? "\x99": "left mouse" ); + else if( button == SDL_BUTTON_RIGHT ) + vg_strcat( str, special_glyphs? "\x9a": "right mouse" ); + else if( button == SDL_BUTTON_MIDDLE ) + vg_strcat( str, special_glyphs? "\x9c": "middle mouse" ); + else{ + vg_strcat( str, "mouse button #" ); + vg_strcati32( str, button ); + } +} + +/* + * Cat string represeinting single axis + */ +void vg_joy_axis_string( vg_str *str, SDL_GameControllerAxis axis, + int special_glyphs ) +{ + if( axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT ) + vg_strcat( str, special_glyphs?"\x8f":"left trigger" ); + else if( axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT ) + vg_strcat( str, special_glyphs?"\x90":"right trigger" ); + else if( axis == SDL_CONTROLLER_AXIS_LEFTX ) + vg_strcat( str, special_glyphs?"\x88":"left stick horizontal" ); + else if( axis == SDL_CONTROLLER_AXIS_LEFTY ) + vg_strcat( str, special_glyphs?"\x89":"left stick vertical" ); + else if( axis == SDL_CONTROLLER_AXIS_RIGHTX ) + vg_strcat( str, special_glyphs?"\x8c":"right stick horizontal" ); + else if( axis == SDL_CONTROLLER_AXIS_RIGHTY ) + vg_strcat( str, special_glyphs?"\x8d":"right stick vertical" ); + else{ + vg_strcat( str, "axis " ); + vg_strcati32( str, axis ); + } +} + +/* + * Cat string represeinting whole joystick + */ +void vg_joy_string( vg_str *str, vg_input_op op, int special_glyphs ) +{ + if( op == vg_joy_ls ) + vg_strcat( str, special_glyphs? "\x87": "left stick" ); + else + vg_strcat( str, special_glyphs? "\x8b": "right stick" ); +} + +/* + * Convert an input program into a readable string + */ +void vg_input_string( vg_str *str, vg_input_op *ops, int glyphs ) +{ + u32 pc = 0; + int applicable = 0, visible = 1; + +next_code:; + vg_input_op op = ops[ pc ++ ]; + + if( (op == vg_keyboard) || (op == vg_mouse) ){ + if( (vg_input.display_input_method == k_input_method_kbm) && visible ){ + applicable = 1; + + if( op == vg_keyboard ) + vg_keyboard_key_string( str, ops[pc], glyphs ); + else + vg_mouse_button_string( str, ops[pc], glyphs ); + } + else applicable = 0; + pc ++; + } + else if( (op == vg_joy_button) || (op == vg_joy_axis) ){ + if( (vg_input.display_input_method == k_input_method_controller) + && visible ){ + applicable = 1; + + if( op == vg_joy_button ) + vg_strcat( str, controller_button_str(ops[pc]) ); + else + vg_joy_axis_string( str, ops[pc], glyphs ); + } + else applicable = 0; + pc ++; + } + else if( (op == vg_joy_ls) || (op == vg_joy_rs) ){ + if( (vg_input.display_input_method == k_input_method_controller) + && visible ){ + applicable = 1; + vg_joy_string( str, op, glyphs ); + } + else applicable = 0; + } + else if( op == vg_mode_mul ){ + if( applicable && visible ) + vg_strcat( str, " + " ); + } + else if( op == vg_index ) + pc ++; + else if( op == vg_gui_visible ) + visible = ops[pc++]; + else if( op == vg_end ) + return; + + goto next_code; +}