1 /* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
5 #include "vg/vg_loader.h"
7 #define VG_MAX_CONTROLLERS 4
9 static float controller_deadzone
= 0.05f
;
10 typedef u32 vg_input_op
;
11 typedef vg_input_op
*vg_input_program
;
14 k_vg_input_type_button_u8
,
15 k_vg_input_type_axis_f32
,
16 k_vg_input_type_joy_v2f
49 SDL_GameController
*handle
; /* handle for controller. NULL if unused */
50 SDL_JoystickID instance_id
; /* uid used in events */
52 float axises
[ SDL_CONTROLLER_AXIS_MAX
];
53 u32 buttons
[ SDL_CONTROLLER_BUTTON_MAX
];
57 int active_controller_index
; /* most recent controller (by button press)
58 will be -1 if no controllers active */
60 /* what the user is currently using. the keyboard and controller are still
61 * active simultaneously, but this reflects what the UI should show */
64 k_input_method_controller
67 SDL_GameControllerType display_input_type
;
69 static vg_input
= { .active_controller_index
= -2 };
71 static u8
vg_getkey( SDL_Keycode kc
)
73 SDL_Scancode sc
= SDL_GetScancodeFromKey( kc
);
74 return vg_input
.sdl_keys
[sc
];
78 * takes SDL device index, and tries to open that on any free channel
80 static int vg_open_gamecontroller( Sint32 index
)
82 struct vg_controller
*controller
= NULL
;
84 const char *name
= SDL_GameControllerNameForIndex( index
);
85 SDL_JoystickID instance_id
= SDL_JoystickGetDeviceInstanceID( index
);
87 if( instance_id
== -1 ){
88 vg_error( ". Invalid device index (vg_open_gamecontroller)\n" );
92 for( int j
=0; j
<VG_MAX_CONTROLLERS
; j
++ ){
93 struct vg_controller
*esta
= &vg_input
.controllers
[j
];
96 if( esta
->instance_id
== instance_id
){
97 vg_warn( " . SDL_JoystickID[%d] is already in open at index #%d\n",
98 esta
->instance_id
, j
);
104 controller
= &vg_input
.controllers
[j
];
111 controller
->handle
= SDL_GameControllerOpen( index
);
112 controller
->instance_id
= instance_id
;
114 if( controller
->handle
){
116 " . opened SDL_JoystickID[%d] as controller '%s' at index #%d\n",
117 instance_id
, name
, vg_id
);
119 for( u32 i
=0; i
< SDL_CONTROLLER_BUTTON_MAX
; i
++ )
120 controller
->buttons
[i
] = 0;
122 for( u32 i
=0; i
< SDL_CONTROLLER_AXIS_MAX
; i
++ )
123 controller
->axises
[i
] = 0.0f
;
125 if( vg_input
.active_controller_index
== -2 ){
126 vg_input
.active_controller_index
= vg_id
;
127 vg_input
.display_input_method
= k_input_method_controller
;
128 vg_input
.display_input_type
=
129 SDL_GameControllerGetType( controller
->handle
);
135 vg_error( ". Failed to attach game controller '%s'. Reason: %s\n",
136 name
, SDL_GetError() );
141 vg_error( ". Too many controllers open! ignoring '%s'\n", name
);
146 static void vg_input_device_event( SDL_Event
*ev
)
148 if( ev
->type
== SDL_CONTROLLERDEVICEADDED
){
149 int is_controller
= SDL_IsGameController( ev
->cdevice
.which
);
150 const char *name
= SDL_JoystickNameForIndex( ev
->cdevice
.which
);
152 Sint32 index
= ev
->cdevice
.which
;
153 SDL_JoystickID instance_id
= SDL_JoystickGetDeviceInstanceID( index
);
154 vg_info( "SDL_CONTROLLERDEVICEADDED | device index: %d, name: '%s'\n",
158 vg_open_gamecontroller( index
);
161 else if( ev
->type
== SDL_CONTROLLERDEVICEREMOVED
){
162 vg_info( "SDL_CONTROLLERDEVICEREMOVED | instance_id: %d\n",
165 for( int i
=0; i
<VG_MAX_CONTROLLERS
; i
++ ){
166 struct vg_controller
*controller
= &vg_input
.controllers
[i
];
168 if( controller
->handle
){
169 if( controller
->instance_id
== ev
->cdevice
.which
){
170 vg_info( " . closing controller at index #%d\n", i
);
171 SDL_GameControllerClose( controller
->handle
);
172 controller
->handle
= NULL
;
173 controller
->instance_id
= -1;
175 if( vg_input
.active_controller_index
== i
){
176 vg_input
.active_controller_index
= -1;
177 vg_input
.display_input_method
= k_input_method_kbm
;
178 vg_info( "display_input: k_input_method_kbm\n" );
187 static void vg_input_controller_event( SDL_Event
*ev
)
189 if( ev
->type
== SDL_CONTROLLERAXISMOTION
){
190 for( int i
=0; i
<VG_MAX_CONTROLLERS
; i
++ ){
191 struct vg_controller
*esta
= &vg_input
.controllers
[i
];
193 if( ev
->caxis
.which
== esta
->instance_id
){
194 float value
= (float)ev
->caxis
.value
/ 32767.0f
;
196 if( ev
->caxis
.axis
== SDL_CONTROLLER_AXIS_LEFTX
||
197 ev
->caxis
.axis
== SDL_CONTROLLER_AXIS_LEFTY
||
198 ev
->caxis
.axis
== SDL_CONTROLLER_AXIS_RIGHTX
||
199 ev
->caxis
.axis
== SDL_CONTROLLER_AXIS_RIGHTY
)
201 float deadz
= vg_clampf( controller_deadzone
, 0.0f
, 0.999f
),
202 high
= vg_maxf( 0.0f
, fabsf(value
) - deadz
);
204 value
= vg_signf(value
) * (high
/ (1.0f
-deadz
));
207 esta
->axises
[ ev
->caxis
.axis
] = value
;
212 else if( ev
->type
== SDL_CONTROLLERBUTTONDOWN
){
213 struct vg_controller
*active
= NULL
;
215 if( vg_input
.active_controller_index
>= 0 )
216 active
= &vg_input
.controllers
[vg_input
.active_controller_index
];
218 if( !active
|| (ev
->cbutton
.which
!= active
->instance_id
) ){
220 vg_input
.active_controller_index
= -1;
221 vg_input
.display_input_method
= k_input_method_kbm
;
223 for( int i
=0; i
<VG_MAX_CONTROLLERS
; i
++ ){
224 if( vg_input
.controllers
[i
].instance_id
== ev
->cbutton
.which
){
225 active
= &vg_input
.controllers
[i
];
226 vg_input
.active_controller_index
= i
;
227 vg_input
.display_input_type
=
228 SDL_GameControllerGetType(active
->handle
);
234 vg_info( "Switching active controller index to #%d\n",
235 vg_input
.active_controller_index
);
238 vg_error( "Input out of range (SDL_JoystickID#%d)\n",
244 if( vg_input
.display_input_method
!= k_input_method_controller
){
245 vg_input
.display_input_method
= k_input_method_controller
;
246 vg_info( "display_input: k_input_method_controller\n" );
248 active
->buttons
[ ev
->cbutton
.button
] = 1;
251 else if( ev
->type
== SDL_CONTROLLERBUTTONUP
){
252 for( int i
=0; i
<VG_MAX_CONTROLLERS
; i
++ ){
253 struct vg_controller
*esta
= &vg_input
.controllers
[i
];
255 if( ev
->cbutton
.which
== esta
->instance_id
){
256 esta
->buttons
[ ev
->cbutton
.button
] = 0;
263 static void vg_process_inputs(void)
266 vg_input
.sdl_keys
= SDL_GetKeyboardState( &count
);
267 vg_input
.sdl_mouse
= SDL_GetMouseState(NULL
,NULL
);
269 if( vg_input
.display_input_method
!= k_input_method_kbm
){
270 /* check for giving keyboard priority */
271 for( int i
=0; i
<count
; i
++ ){
272 if( vg_input
.sdl_keys
[i
] ){
273 vg_input
.display_input_method
= k_input_method_kbm
;
274 vg_info( "display_input: k_input_method_kbm (keyboard %d)\n", i
);
279 /* check for giving mouse priority */
280 if( vg_input
.sdl_mouse
&
281 (SDL_BUTTON(SDL_BUTTON_LEFT
)|SDL_BUTTON(SDL_BUTTON_RIGHT
)|
282 SDL_BUTTON(SDL_BUTTON_MIDDLE
)) )
284 vg_input
.display_input_method
= k_input_method_kbm
;
285 vg_info( "display_input: k_input_method_kbm (mouse)\n" );
290 static void async_vg_input_init( void *payload
, u32 size
)
292 VG_VAR_F32( controller_deadzone
, flags
=VG_VAR_PERSISTENT
);
294 vg_info( "Checking for controllers\n" );
295 SDL_GameControllerAddMappingsFromFile( "gamecontrollerdb.txt" );
297 int joy_count
= SDL_NumJoysticks();
298 for( int i
=0; i
<joy_count
; i
++ ) {
299 const char *name
= SDL_JoystickNameForIndex( i
);
300 int is_controller
= SDL_IsGameController(i
);
302 vg_info( "%d: %s [controller: %d]\n", i
, name
, is_controller
);
305 vg_open_gamecontroller( i
);
310 static void vg_input_init(void)
312 vg_async_call( async_vg_input_init
, NULL
, 0 );
315 static void vg_input_free(void)
317 for( int i
=0; i
<VG_MAX_CONTROLLERS
; i
++ ){
318 struct vg_controller
*controller
= &vg_input
.controllers
[i
];
320 if( controller
->handle
){
321 SDL_GameControllerClose( controller
->handle
);
322 controller
->handle
= NULL
;
327 struct vg_controller
*vg_active_controller(void){
328 if( vg_input
.active_controller_index
>= 0 )
329 return &vg_input
.controllers
[vg_input
.active_controller_index
];
334 static u8
vg_controller_button( SDL_GameControllerButton button
){
335 struct vg_controller
*c
= vg_active_controller();
336 if( c
) return c
->buttons
[ button
];
340 static f32
vg_controller_axis( SDL_GameControllerAxis axis
){
341 struct vg_controller
*c
= vg_active_controller();
342 if( c
) return c
->axises
[ axis
];
346 static void vg_input_apply_to_u8( vg_input_op mode
, u8 data
, u8
*inout_result
){
347 if ( mode
== vg_mode_absmax
) *inout_result
|= data
;
348 else if( mode
== vg_mode_mul
) *inout_result
&= data
;
349 else vg_fatal_error( "mode not supported for destination type (%d)", mode
);
352 static void vg_input_apply_to_f32( vg_input_op mode
, f32 data
,
354 if ( mode
== vg_mode_absmax
){
355 if( fabsf(data
) > fabsf(*inout_result
) )
356 *inout_result
= data
;
358 else if( mode
== vg_mode_max
) *inout_result
= vg_maxf(*inout_result
,data
);
359 else if( mode
== vg_mode_mul
) *inout_result
*= (f32
)data
;
360 else if( mode
== vg_mode_sub
) *inout_result
-= (f32
)data
;
361 else if( mode
== vg_mode_add
) *inout_result
+= (f32
)data
;
362 else vg_fatal_error( "mode not supported for destination type (%d)", mode
);
366 * Run an input program. out_result must point to memory with sufficient
367 * storage respective to the size set by type.
369 static void vg_exec_input_program( enum vg_input_type type
, vg_input_op
*ops
,
371 u8
*out_button
= NULL
;
374 if( type
== k_vg_input_type_button_u8
){
375 out_button
= out_result
;
378 else if( type
== k_vg_input_type_axis_f32
){
379 out_joy
= out_result
;
382 else if( type
== k_vg_input_type_joy_v2f
){
383 out_joy
= out_result
;
389 vg_input_op mode
= vg_mode_absmax
;
390 u32 pc
= 0, index
= 0;
393 vg_input_op op
= ops
[ pc
++ ];
395 if( (op
>= vg_mode_mul
) && (op
<= vg_mode_max
) )
397 else if( (op
== vg_keyboard
) || (op
== vg_mouse
) || (op
== vg_joy_button
) ){
400 if( op
== vg_keyboard
)
401 state
= vg_getkey(ops
[pc
++]);
402 else if( op
== vg_mouse
)
403 state
= (vg_input
.sdl_mouse
& SDL_BUTTON(ops
[pc
++]))?1:0;
405 state
= vg_controller_button(ops
[pc
++]);
407 if( type
== k_vg_input_type_button_u8
)
408 vg_input_apply_to_u8( mode
, state
, out_button
);
410 vg_input_apply_to_f32( mode
, (f32
)state
, &out_joy
[index
] );
412 else if( op
== vg_joy_axis
){
413 f32 state
= vg_controller_axis( ops
[pc
++] );
414 if( type
== k_vg_input_type_button_u8
)
415 vg_input_apply_to_u8( mode
, state
>0.5f
?1:0, out_button
);
417 vg_input_apply_to_f32( mode
, state
, &out_joy
[index
] );
419 else if( (op
== vg_joy_ls
) || (op
== vg_joy_rs
) ){
420 if( type
== k_vg_input_type_joy_v2f
){
421 vg_input_apply_to_f32( mode
,
422 vg_controller_axis( op
==vg_joy_ls
? SDL_CONTROLLER_AXIS_LEFTX
:
423 SDL_CONTROLLER_AXIS_RIGHTX
),
425 vg_input_apply_to_f32( mode
,
426 vg_controller_axis( op
==vg_joy_ls
? SDL_CONTROLLER_AXIS_LEFTY
:
427 SDL_CONTROLLER_AXIS_RIGHTY
),
431 else if( op
== vg_index
)
433 else if( op
== vg_end
)
435 else if( op
== vg_normalize
)
436 v2_normalize( out_joy
);
437 else if( op
== vg_gui_visible
)
440 vg_fatal_error( "unknown op\n" );
446 * Get vendor specific button glyphs based on SDL button ID
448 static const char *controller_button_str( SDL_GameControllerButton button
){
449 static const char *controller_glyphs
[ SDL_CONTROLLER_BUTTON_MAX
][2] = {
450 /* xbox/generic playstation */
451 [ SDL_CONTROLLER_BUTTON_A
] = { "\x1e\x85","\x1e\x82" },
452 [ SDL_CONTROLLER_BUTTON_B
] = { "\x1e\x86","\x1e\x81" },
453 [ SDL_CONTROLLER_BUTTON_X
] = { "\x1e\x83","\x1e\x7f" },
454 [ SDL_CONTROLLER_BUTTON_Y
] = { "\x1e\x84","\x1e\x80" },
455 [ SDL_CONTROLLER_BUTTON_LEFTSTICK
] = { "\x87", "\x87" },
456 [ SDL_CONTROLLER_BUTTON_RIGHTSTICK
] = { "\x8b", "\x8b" },
457 [ SDL_CONTROLLER_BUTTON_LEFTSHOULDER
] = { "\x91", "\x91" },
458 [ SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
]= { "\x92", "\x92" },
459 [ SDL_CONTROLLER_BUTTON_DPAD_LEFT
] = { "\x1e\x93","\x1e\x93" },
460 [ SDL_CONTROLLER_BUTTON_DPAD_UP
] = { "\x1e\x94","\x1e\x94" },
461 [ SDL_CONTROLLER_BUTTON_DPAD_RIGHT
] = { "\x1e\x95","\x1e\x95" },
462 [ SDL_CONTROLLER_BUTTON_DPAD_DOWN
] = { "\x1e\x96","\x1e\x96" },
463 [ SDL_CONTROLLER_BUTTON_GUIDE
] = { "\x91", "\x91" },
466 if( vg_input
.display_input_type
== SDL_CONTROLLER_TYPE_PS3
||
467 vg_input
.display_input_type
== SDL_CONTROLLER_TYPE_PS4
||
468 vg_input
.display_input_type
== SDL_CONTROLLER_TYPE_PS5
)
470 return controller_glyphs
[ button
][ 1 ];
472 else if( vg_input
.display_input_type
==
473 SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO
||
474 vg_input
.display_input_type
==
475 SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT
||
476 vg_input
.display_input_type
==
477 SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR
||
478 vg_input
.display_input_type
==
479 SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT
)
484 return controller_glyphs
[ button
][ 0 ];
488 * Cat keyboard key string. special_glyphs include SR glyphs
490 static void vg_keyboard_key_string( vg_str
*str
, u32 key
, int special_glyphs
){
491 if( (key
>= SDLK_a
) && (key
<= SDLK_z
) ){
492 key
= (key
-SDLK_a
)+(u32
)'A';
494 if( special_glyphs
){
495 vg_strcatch( str
, '\x1f' );
496 vg_strcatch( str
, key
);
497 vg_strcatch( str
, ' ' );
500 vg_strcatch( str
, key
);
502 else if( (key
== SDLK_LSHIFT
) || (key
== SDLK_RSHIFT
) )
503 vg_strcat( str
, special_glyphs
? "\x9e": "shift" );
504 else if( (key
== SDLK_LCTRL
) || (key
== SDLK_RCTRL
) )
505 vg_strcat( str
, special_glyphs
? "\x9f": "ctrl" );
506 else if( (key
== SDLK_LALT
) || (key
== SDLK_RALT
) )
507 vg_strcat( str
, special_glyphs
? "\xa0": "alt" );
508 else if( key
== SDLK_SPACE
)
509 vg_strcat( str
, special_glyphs
? "\xa1": "space" );
510 else if( (key
== SDLK_RETURN
) || (key
== SDLK_RETURN2
) )
511 vg_strcat( str
, special_glyphs
? "\xa2": "return" );
512 else if( key
== SDLK_ESCAPE
)
513 vg_strcat( str
, special_glyphs
? "\xa3": "escape" );
514 else if( key
== SDLK_RIGHT
)
515 vg_strcat( str
, special_glyphs
? "\x1f\x95 ": "right" );
516 else if( key
== SDLK_LEFT
)
517 vg_strcat( str
, special_glyphs
? "\x1f\x93 ": "left" );
518 else if( key
== SDLK_UP
)
519 vg_strcat( str
, special_glyphs
? "\x1f\x94 ": "up" );
520 else if( key
== SDLK_DOWN
)
521 vg_strcat( str
, special_glyphs
? "\x1f\x96 ": "down" );
523 vg_strcat( str
, "keyboard key #" );
524 vg_strcati32( str
, key
);
529 * Cat mouse button string. special_glyphs include SR glyphs
531 static void vg_mouse_button_string( vg_str
*str
, u32 button
,
532 int special_glyphs
){
533 if ( button
== SDL_BUTTON_LEFT
)
534 vg_strcat( str
, special_glyphs
? "\x99": "left mouse" );
535 else if( button
== SDL_BUTTON_RIGHT
)
536 vg_strcat( str
, special_glyphs
? "\x9a": "right mouse" );
537 else if( button
== SDL_BUTTON_MIDDLE
)
538 vg_strcat( str
, special_glyphs
? "\x9c": "middle mouse" );
540 vg_strcat( str
, "mouse button #" );
541 vg_strcati32( str
, button
);
546 * Cat string represeinting single axis
548 static void vg_joy_axis_string( vg_str
*str
,
549 SDL_GameControllerAxis axis
, int special_glyphs
){
550 if( axis
== SDL_CONTROLLER_AXIS_TRIGGERLEFT
)
551 vg_strcat( str
, special_glyphs
?"\x8f":"left trigger" );
552 else if( axis
== SDL_CONTROLLER_AXIS_TRIGGERRIGHT
)
553 vg_strcat( str
, special_glyphs
?"\x90":"right trigger" );
554 else if( axis
== SDL_CONTROLLER_AXIS_LEFTX
)
555 vg_strcat( str
, special_glyphs
?"\x88":"left stick horizontal" );
556 else if( axis
== SDL_CONTROLLER_AXIS_LEFTY
)
557 vg_strcat( str
, special_glyphs
?"\x89":"left stick vertical" );
558 else if( axis
== SDL_CONTROLLER_AXIS_RIGHTX
)
559 vg_strcat( str
, special_glyphs
?"\x8c":"right stick horizontal" );
560 else if( axis
== SDL_CONTROLLER_AXIS_RIGHTY
)
561 vg_strcat( str
, special_glyphs
?"\x8d":"right stick vertical" );
563 vg_strcat( str
, "axis " );
564 vg_strcati32( str
, axis
);
569 * Cat string represeinting whole joystick
571 static void vg_joy_string( vg_str
*str
, vg_input_op op
, int special_glyphs
){
572 if( op
== vg_joy_ls
)
573 vg_strcat( str
, special_glyphs
? "\x87": "left stick" );
575 vg_strcat( str
, special_glyphs
? "\x8b": "right stick" );
579 * Convert an input program into a readable string
581 static void vg_input_string( vg_str
*str
, vg_input_op
*ops
, int glyphs
){
583 int applicable
= 0, visible
= 1;
586 vg_input_op op
= ops
[ pc
++ ];
588 if( (op
== vg_keyboard
) || (op
== vg_mouse
) ){
589 if( (vg_input
.display_input_method
== k_input_method_kbm
) && visible
){
592 if( op
== vg_keyboard
)
593 vg_keyboard_key_string( str
, ops
[pc
], glyphs
);
595 vg_mouse_button_string( str
, ops
[pc
], glyphs
);
600 else if( (op
== vg_joy_button
) || (op
== vg_joy_axis
) ){
601 if( (vg_input
.display_input_method
== k_input_method_controller
)
605 if( op
== vg_joy_button
)
606 vg_strcat( str
, controller_button_str(ops
[pc
]) );
608 vg_joy_axis_string( str
, ops
[pc
], glyphs
);
613 else if( (op
== vg_joy_ls
) || (op
== vg_joy_rs
) ){
614 if( (vg_input
.display_input_method
== k_input_method_controller
)
617 vg_joy_string( str
, op
, glyphs
);
621 else if( op
== vg_mode_mul
){
622 if( applicable
&& visible
)
623 vg_strcat( str
, " + " );
625 else if( op
== vg_index
)
627 else if( op
== vg_gui_visible
)
629 else if( op
== vg_end
)