1 /* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved */
9 f32 controller_deadzone
= 0.05f
;
11 struct vg_input vg_input
= {
12 .active_controller_index
= -2
15 u8
vg_getkey( SDL_Keycode kc
)
17 SDL_Scancode sc
= SDL_GetScancodeFromKey( kc
);
18 return vg_input
.sdl_keys
[sc
];
22 * takes SDL device index, and tries to open that on any free channel
24 static int vg_open_gamecontroller( Sint32 index
)
26 struct vg_controller
*controller
= NULL
;
28 const char *name
= SDL_GameControllerNameForIndex( index
);
29 SDL_JoystickID instance_id
= SDL_JoystickGetDeviceInstanceID( index
);
31 if( instance_id
== -1 ){
32 vg_error( ". Invalid device index (vg_open_gamecontroller)\n" );
36 for( int j
=0; j
<VG_MAX_CONTROLLERS
; j
++ ){
37 struct vg_controller
*esta
= &vg_input
.controllers
[j
];
40 if( esta
->instance_id
== instance_id
){
41 vg_warn( " . SDL_JoystickID[%d] is already in open at index #%d\n",
42 esta
->instance_id
, j
);
48 controller
= &vg_input
.controllers
[j
];
55 controller
->handle
= SDL_GameControllerOpen( index
);
56 controller
->instance_id
= instance_id
;
58 if( controller
->handle
){
60 " . opened SDL_JoystickID[%d] as controller '%s' at index #%d\n",
61 instance_id
, name
, vg_id
);
63 for( u32 i
=0; i
< SDL_CONTROLLER_BUTTON_MAX
; i
++ )
64 controller
->buttons
[i
] = 0;
66 for( u32 i
=0; i
< SDL_CONTROLLER_AXIS_MAX
; i
++ )
67 controller
->axises
[i
] = 0.0f
;
69 if( vg_input
.active_controller_index
== -2 ){
70 vg_input
.active_controller_index
= vg_id
;
71 vg_input
.display_input_method
= k_input_method_controller
;
72 vg_input
.display_input_type
=
73 SDL_GameControllerGetType( controller
->handle
);
79 vg_error( ". Failed to attach game controller '%s'. Reason: %s\n",
80 name
, SDL_GetError() );
85 vg_error( ". Too many controllers open! ignoring '%s'\n", name
);
90 void vg_input_device_event( SDL_Event
*ev
)
92 if( ev
->type
== SDL_CONTROLLERDEVICEADDED
){
93 int is_controller
= SDL_IsGameController( ev
->cdevice
.which
);
94 const char *name
= SDL_JoystickNameForIndex( ev
->cdevice
.which
);
96 Sint32 index
= ev
->cdevice
.which
;
97 SDL_JoystickID instance_id
= SDL_JoystickGetDeviceInstanceID( index
);
98 vg_info( "SDL_CONTROLLERDEVICEADDED | device index: %d, name: '%s'\n",
102 vg_open_gamecontroller( index
);
105 else if( ev
->type
== SDL_CONTROLLERDEVICEREMOVED
){
106 vg_info( "SDL_CONTROLLERDEVICEREMOVED | instance_id: %d\n",
109 for( int i
=0; i
<VG_MAX_CONTROLLERS
; i
++ ){
110 struct vg_controller
*controller
= &vg_input
.controllers
[i
];
112 if( controller
->handle
){
113 if( controller
->instance_id
== ev
->cdevice
.which
){
114 vg_info( " . closing controller at index #%d\n", i
);
115 SDL_GameControllerClose( controller
->handle
);
116 controller
->handle
= NULL
;
117 controller
->instance_id
= -1;
119 if( vg_input
.active_controller_index
== i
){
120 vg_input
.active_controller_index
= -1;
121 vg_input
.display_input_method
= k_input_method_kbm
;
122 vg_info( "display_input: k_input_method_kbm\n" );
131 void vg_input_controller_event( SDL_Event
*ev
)
133 if( ev
->type
== SDL_CONTROLLERAXISMOTION
){
134 for( int i
=0; i
<VG_MAX_CONTROLLERS
; i
++ ){
135 struct vg_controller
*esta
= &vg_input
.controllers
[i
];
137 if( ev
->caxis
.which
== esta
->instance_id
){
138 float value
= (float)ev
->caxis
.value
/ 32767.0f
;
140 if( ev
->caxis
.axis
== SDL_CONTROLLER_AXIS_LEFTX
||
141 ev
->caxis
.axis
== SDL_CONTROLLER_AXIS_LEFTY
||
142 ev
->caxis
.axis
== SDL_CONTROLLER_AXIS_RIGHTX
||
143 ev
->caxis
.axis
== SDL_CONTROLLER_AXIS_RIGHTY
)
145 float deadz
= vg_clampf( controller_deadzone
, 0.0f
, 0.999f
),
146 high
= vg_maxf( 0.0f
, fabsf(value
) - deadz
);
148 value
= vg_signf(value
) * (high
/ (1.0f
-deadz
));
151 esta
->axises
[ ev
->caxis
.axis
] = value
;
156 else if( ev
->type
== SDL_CONTROLLERBUTTONDOWN
){
157 struct vg_controller
*active
= NULL
;
159 if( vg_input
.active_controller_index
>= 0 )
160 active
= &vg_input
.controllers
[vg_input
.active_controller_index
];
162 if( !active
|| (ev
->cbutton
.which
!= active
->instance_id
) ){
164 vg_input
.active_controller_index
= -1;
165 vg_input
.display_input_method
= k_input_method_kbm
;
167 for( int i
=0; i
<VG_MAX_CONTROLLERS
; i
++ ){
168 if( vg_input
.controllers
[i
].instance_id
== ev
->cbutton
.which
){
169 active
= &vg_input
.controllers
[i
];
170 vg_input
.active_controller_index
= i
;
171 vg_input
.display_input_type
=
172 SDL_GameControllerGetType(active
->handle
);
178 vg_info( "Switching active controller index to #%d\n",
179 vg_input
.active_controller_index
);
182 vg_error( "Input out of range (SDL_JoystickID#%d)\n",
188 if( vg_input
.display_input_method
!= k_input_method_controller
){
189 vg_input
.display_input_method
= k_input_method_controller
;
190 vg_info( "display_input: k_input_method_controller\n" );
192 active
->buttons
[ ev
->cbutton
.button
] = 1;
195 else if( ev
->type
== SDL_CONTROLLERBUTTONUP
){
196 for( int i
=0; i
<VG_MAX_CONTROLLERS
; i
++ ){
197 struct vg_controller
*esta
= &vg_input
.controllers
[i
];
199 if( ev
->cbutton
.which
== esta
->instance_id
){
200 esta
->buttons
[ ev
->cbutton
.button
] = 0;
207 void vg_process_inputs(void)
210 vg_input
.sdl_keys
= SDL_GetKeyboardState( &count
);
211 vg_input
.sdl_mouse
= SDL_GetMouseState(NULL
,NULL
);
213 if( vg_input
.display_input_method
!= k_input_method_kbm
){
214 /* check for giving keyboard priority */
215 for( int i
=0; i
<count
; i
++ ){
216 if( vg_input
.sdl_keys
[i
] ){
217 vg_input
.display_input_method
= k_input_method_kbm
;
218 vg_info( "display_input: k_input_method_kbm (keyboard %d)\n", i
);
223 /* check for giving mouse priority */
224 if( vg_input
.sdl_mouse
&
225 (SDL_BUTTON(SDL_BUTTON_LEFT
)|SDL_BUTTON(SDL_BUTTON_RIGHT
)|
226 SDL_BUTTON(SDL_BUTTON_MIDDLE
)) )
228 vg_input
.display_input_method
= k_input_method_kbm
;
229 vg_info( "display_input: k_input_method_kbm (mouse)\n" );
234 void async_vg_input_init( void *payload
, u32 size
)
236 vg_info( "Checking for controllers\n" );
237 SDL_GameControllerAddMappingsFromFile( "gamecontrollerdb.txt" );
239 int joy_count
= SDL_NumJoysticks();
240 for( int i
=0; i
<joy_count
; i
++ ) {
241 const char *name
= SDL_JoystickNameForIndex( i
);
242 int is_controller
= SDL_IsGameController(i
);
244 vg_info( "%d: %s [controller: %d]\n", i
, name
, is_controller
);
247 vg_open_gamecontroller( i
);
252 void vg_input_init(void)
254 VG_VAR_F32( controller_deadzone
, flags
=VG_VAR_PERSISTENT
);
255 vg_async_call( async_vg_input_init
, NULL
, 0 );
258 void vg_input_free(void)
260 for( int i
=0; i
<VG_MAX_CONTROLLERS
; i
++ ){
261 struct vg_controller
*controller
= &vg_input
.controllers
[i
];
263 if( controller
->handle
){
264 SDL_GameControllerClose( controller
->handle
);
265 controller
->handle
= NULL
;
270 struct vg_controller
*vg_active_controller(void)
272 if( vg_input
.active_controller_index
>= 0 )
273 return &vg_input
.controllers
[vg_input
.active_controller_index
];
278 u8
vg_controller_button( SDL_GameControllerButton button
)
280 struct vg_controller
*c
= vg_active_controller();
281 if( c
) return c
->buttons
[ button
];
285 f32
vg_controller_axis( SDL_GameControllerAxis axis
)
287 struct vg_controller
*c
= vg_active_controller();
288 if( c
) return c
->axises
[ axis
];
292 static void vg_input_apply_to_u8( vg_input_op mode
, u8 data
, u8
*inout_result
){
293 if ( mode
== vg_mode_absmax
) *inout_result
|= data
;
294 else if( mode
== vg_mode_mul
) *inout_result
&= data
;
295 else vg_fatal_error( "mode not supported for destination type (%d)", mode
);
298 static void vg_input_apply_to_f32( vg_input_op mode
, f32 data
,
300 if ( mode
== vg_mode_absmax
){
301 if( fabsf(data
) > fabsf(*inout_result
) )
302 *inout_result
= data
;
304 else if( mode
== vg_mode_max
) *inout_result
= vg_maxf(*inout_result
,data
);
305 else if( mode
== vg_mode_mul
) *inout_result
*= (f32
)data
;
306 else if( mode
== vg_mode_sub
) *inout_result
-= (f32
)data
;
307 else if( mode
== vg_mode_add
) *inout_result
+= (f32
)data
;
308 else vg_fatal_error( "mode not supported for destination type (%d)", mode
);
312 * Run an input program. out_result must point to memory with sufficient
313 * storage respective to the size set by type.
315 void vg_exec_input_program( enum vg_input_type type
, vg_input_op
*ops
,
317 u8
*out_button
= NULL
;
320 if( type
== k_vg_input_type_button_u8
){
321 out_button
= out_result
;
324 else if( type
== k_vg_input_type_axis_f32
){
325 out_joy
= out_result
;
328 else if( type
== k_vg_input_type_joy_v2f
){
329 out_joy
= out_result
;
335 vg_input_op mode
= vg_mode_absmax
;
336 u32 pc
= 0, index
= 0;
339 vg_input_op op
= ops
[ pc
++ ];
341 if( (op
>= vg_mode_mul
) && (op
<= vg_mode_max
) )
343 else if( (op
== vg_keyboard
) || (op
== vg_mouse
) || (op
== vg_joy_button
) ){
346 if( op
== vg_keyboard
)
347 state
= vg_getkey(ops
[pc
++]);
348 else if( op
== vg_mouse
)
349 state
= (vg_input
.sdl_mouse
& SDL_BUTTON(ops
[pc
++]))?1:0;
351 state
= vg_controller_button(ops
[pc
++]);
353 if( type
== k_vg_input_type_button_u8
)
354 vg_input_apply_to_u8( mode
, state
, out_button
);
356 vg_input_apply_to_f32( mode
, (f32
)state
, &out_joy
[index
] );
358 else if( op
== vg_joy_axis
){
359 f32 state
= vg_controller_axis( ops
[pc
++] );
360 if( type
== k_vg_input_type_button_u8
)
361 vg_input_apply_to_u8( mode
, state
>0.5f
?1:0, out_button
);
363 vg_input_apply_to_f32( mode
, state
, &out_joy
[index
] );
365 else if( (op
== vg_joy_ls
) || (op
== vg_joy_rs
) ){
366 if( type
== k_vg_input_type_joy_v2f
){
367 vg_input_apply_to_f32( mode
,
368 vg_controller_axis( op
==vg_joy_ls
? SDL_CONTROLLER_AXIS_LEFTX
:
369 SDL_CONTROLLER_AXIS_RIGHTX
),
371 vg_input_apply_to_f32( mode
,
372 vg_controller_axis( op
==vg_joy_ls
? SDL_CONTROLLER_AXIS_LEFTY
:
373 SDL_CONTROLLER_AXIS_RIGHTY
),
377 else if( op
== vg_index
)
379 else if( op
== vg_end
)
381 else if( op
== vg_normalize
)
382 v2_normalize( out_joy
);
383 else if( op
== vg_gui_visible
)
386 vg_fatal_error( "unknown op\n" );
392 * Get vendor specific button glyphs based on SDL button ID
394 const char *controller_button_str( SDL_GameControllerButton button
)
396 static const char *controller_glyphs
[ SDL_CONTROLLER_BUTTON_MAX
][2] = {
397 /* xbox/generic playstation */
398 [ SDL_CONTROLLER_BUTTON_A
] = { KGRN
"\x06\x02\x85",KBLU
"\x06\x02\x82" },
399 [ SDL_CONTROLLER_BUTTON_B
] = { KRED
"\x06\x02\x86",KRED
"\x06\x02\x81" },
400 [ SDL_CONTROLLER_BUTTON_X
] = { KBLU
"\x06\x02\x83",KMAG
"\x06\x02\x7f" },
401 [ SDL_CONTROLLER_BUTTON_Y
] = { KYEL
"\x06\x02\x84",KGRN
"\x06\x02\x80" },
402 [ SDL_CONTROLLER_BUTTON_LEFTSTICK
] = { "\x87","\x87" },
403 [ SDL_CONTROLLER_BUTTON_RIGHTSTICK
] = { "\x8b","\x8b" },
404 [ SDL_CONTROLLER_BUTTON_LEFTSHOULDER
] = { "\x91","\x91" },
405 [ SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
]= { "\x92","\x92" },
406 [ SDL_CONTROLLER_BUTTON_DPAD_LEFT
] = { "\x93","\x93" },
407 [ SDL_CONTROLLER_BUTTON_DPAD_UP
] = { "\x94","\x94" },
408 [ SDL_CONTROLLER_BUTTON_DPAD_RIGHT
] = { "\x95","\x95" },
409 [ SDL_CONTROLLER_BUTTON_DPAD_DOWN
] = { "\x96","\x96" },
410 [ SDL_CONTROLLER_BUTTON_GUIDE
] = { "\x91","\x91" },
413 if( vg_input
.display_input_type
== SDL_CONTROLLER_TYPE_PS3
||
414 vg_input
.display_input_type
== SDL_CONTROLLER_TYPE_PS4
||
415 vg_input
.display_input_type
== SDL_CONTROLLER_TYPE_PS5
)
417 return controller_glyphs
[ button
][ 1 ];
419 else if( vg_input
.display_input_type
==
420 SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO
||
421 vg_input
.display_input_type
==
422 SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT
||
423 vg_input
.display_input_type
==
424 SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR
||
425 vg_input
.display_input_type
==
426 SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT
)
431 return controller_glyphs
[ button
][ 0 ];
435 * Cat keyboard key string. special_glyphs include SR glyphs
437 void vg_keyboard_key_string( vg_str
*str
, u32 key
, int special_glyphs
)
439 if( (key
>= SDLK_a
) && (key
<= SDLK_z
) ){
440 key
= (key
-SDLK_a
)+(u32
)'A';
441 vg_strcatch( str
, key
);
443 else if( (key
== SDLK_LSHIFT
) || (key
== SDLK_RSHIFT
) )
444 vg_strcat( str
, special_glyphs
? "\x9e": "shift" );
445 else if( (key
== SDLK_LCTRL
) || (key
== SDLK_RCTRL
) )
446 vg_strcat( str
, special_glyphs
? "\x9f": "ctrl" );
447 else if( (key
== SDLK_LALT
) || (key
== SDLK_RALT
) )
448 vg_strcat( str
, special_glyphs
? "\xa0": "alt" );
449 else if( key
== SDLK_SPACE
)
450 vg_strcat( str
, special_glyphs
? "\xa1": "space" );
451 else if( (key
== SDLK_RETURN
) || (key
== SDLK_RETURN2
) )
452 vg_strcat( str
, special_glyphs
? "\xa2": "return" );
453 else if( key
== SDLK_ESCAPE
)
454 vg_strcat( str
, special_glyphs
? "\xa3": "escape" );
455 else if( key
== SDLK_RIGHT
)
456 vg_strcat( str
, special_glyphs
? "\x95 ": "right" );
457 else if( key
== SDLK_LEFT
)
458 vg_strcat( str
, special_glyphs
? "\x93 ": "left" );
459 else if( key
== SDLK_UP
)
460 vg_strcat( str
, special_glyphs
? "\x94 ": "up" );
461 else if( key
== SDLK_DOWN
)
462 vg_strcat( str
, special_glyphs
? "\x96 ": "down" );
464 vg_strcat( str
, "keyboard key #" );
465 vg_strcati32( str
, key
);
470 * Cat mouse button string. special_glyphs include SR glyphs
472 void vg_mouse_button_string( vg_str
*str
, u32 button
, int special_glyphs
)
474 if ( button
== SDL_BUTTON_LEFT
)
475 vg_strcat( str
, special_glyphs
? "\x99": "left mouse" );
476 else if( button
== SDL_BUTTON_RIGHT
)
477 vg_strcat( str
, special_glyphs
? "\x9a": "right mouse" );
478 else if( button
== SDL_BUTTON_MIDDLE
)
479 vg_strcat( str
, special_glyphs
? "\x9c": "middle mouse" );
481 vg_strcat( str
, "mouse button #" );
482 vg_strcati32( str
, button
);
487 * Cat string represeinting single axis
489 void vg_joy_axis_string( vg_str
*str
, SDL_GameControllerAxis axis
,
492 if( axis
== SDL_CONTROLLER_AXIS_TRIGGERLEFT
)
493 vg_strcat( str
, special_glyphs
?"\x8f":"left trigger" );
494 else if( axis
== SDL_CONTROLLER_AXIS_TRIGGERRIGHT
)
495 vg_strcat( str
, special_glyphs
?"\x90":"right trigger" );
496 else if( axis
== SDL_CONTROLLER_AXIS_LEFTX
)
497 vg_strcat( str
, special_glyphs
?"\x88":"left stick horizontal" );
498 else if( axis
== SDL_CONTROLLER_AXIS_LEFTY
)
499 vg_strcat( str
, special_glyphs
?"\x89":"left stick vertical" );
500 else if( axis
== SDL_CONTROLLER_AXIS_RIGHTX
)
501 vg_strcat( str
, special_glyphs
?"\x8c":"right stick horizontal" );
502 else if( axis
== SDL_CONTROLLER_AXIS_RIGHTY
)
503 vg_strcat( str
, special_glyphs
?"\x8d":"right stick vertical" );
505 vg_strcat( str
, "axis " );
506 vg_strcati32( str
, axis
);
511 * Cat string represeinting whole joystick
513 void vg_joy_string( vg_str
*str
, vg_input_op op
, int special_glyphs
)
515 if( op
== vg_joy_ls
)
516 vg_strcat( str
, special_glyphs
? "\x87": "left stick" );
518 vg_strcat( str
, special_glyphs
? "\x8b": "right stick" );
522 * Convert an input program into a readable string
524 void vg_input_string( vg_str
*str
, vg_input_op
*ops
, int glyphs
)
527 int applicable
= 0, visible
= 1;
530 vg_input_op op
= ops
[ pc
++ ];
532 if( (op
== vg_keyboard
) || (op
== vg_mouse
) ){
533 if( (vg_input
.display_input_method
== k_input_method_kbm
) && visible
){
536 if( op
== vg_keyboard
)
537 vg_keyboard_key_string( str
, ops
[pc
], glyphs
);
539 vg_mouse_button_string( str
, ops
[pc
], glyphs
);
544 else if( (op
== vg_joy_button
) || (op
== vg_joy_axis
) ){
545 if( (vg_input
.display_input_method
== k_input_method_controller
)
549 if( op
== vg_joy_button
)
550 vg_strcat( str
, controller_button_str(ops
[pc
]) );
552 vg_joy_axis_string( str
, ops
[pc
], glyphs
);
557 else if( (op
== vg_joy_ls
) || (op
== vg_joy_rs
) ){
558 if( (vg_input
.display_input_method
== k_input_method_controller
)
561 vg_joy_string( str
, op
, glyphs
);
565 else if( op
== vg_mode_mul
){
566 if( applicable
&& visible
)
567 vg_strcat( str
, " + " );
569 else if( op
== vg_index
)
571 else if( op
== vg_gui_visible
)
573 else if( op
== vg_end
)