1 /* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
6 #include "vg/vg_loader.h"
8 #define VG_MAX_CONTROLLERS 4
10 static float controller_deadzone
= 0.05f
;
11 typedef u32 vg_input_op
;
12 typedef vg_input_op
*vg_input_program
;
15 k_vg_input_type_button_u8
,
16 k_vg_input_type_axis_f32
,
17 k_vg_input_type_joy_v2f
50 SDL_GameController
*handle
; /* handle for controller. NULL if unused */
51 SDL_JoystickID instance_id
; /* uid used in events */
53 float axises
[ SDL_CONTROLLER_AXIS_MAX
];
54 u32 buttons
[ SDL_CONTROLLER_BUTTON_MAX
];
58 int active_controller_index
; /* most recent controller (by button press)
59 will be -1 if no controllers active */
61 /* what the user is currently using. the keyboard and controller are still
62 * active simultaneously, but this reflects what the UI should show */
65 k_input_method_controller
68 SDL_GameControllerType display_input_type
;
70 static vg_input
= { .active_controller_index
= -2 };
72 static u8
vg_getkey( SDL_Keycode kc
)
74 SDL_Scancode sc
= SDL_GetScancodeFromKey( kc
);
75 return vg_input
.sdl_keys
[sc
];
79 * takes SDL device index, and tries to open that on any free channel
81 static int vg_open_gamecontroller( Sint32 index
)
83 struct vg_controller
*controller
= NULL
;
85 const char *name
= SDL_GameControllerNameForIndex( index
);
86 SDL_JoystickID instance_id
= SDL_JoystickGetDeviceInstanceID( index
);
88 if( instance_id
== -1 ){
89 vg_error( ". Invalid device index (vg_open_gamecontroller)\n" );
93 for( int j
=0; j
<VG_MAX_CONTROLLERS
; j
++ ){
94 struct vg_controller
*esta
= &vg_input
.controllers
[j
];
97 if( esta
->instance_id
== instance_id
){
98 vg_warn( " . SDL_JoystickID[%d] is already in open at index #%d\n",
99 esta
->instance_id
, j
);
105 controller
= &vg_input
.controllers
[j
];
112 controller
->handle
= SDL_GameControllerOpen( index
);
113 controller
->instance_id
= instance_id
;
115 if( controller
->handle
){
117 " . opened SDL_JoystickID[%d] as controller '%s' at index #%d\n",
118 instance_id
, name
, vg_id
);
120 for( u32 i
=0; i
< SDL_CONTROLLER_BUTTON_MAX
; i
++ )
121 controller
->buttons
[i
] = 0;
123 for( u32 i
=0; i
< SDL_CONTROLLER_AXIS_MAX
; i
++ )
124 controller
->axises
[i
] = 0.0f
;
126 if( vg_input
.active_controller_index
== -2 ){
127 vg_input
.active_controller_index
= vg_id
;
128 vg_input
.display_input_method
= k_input_method_controller
;
129 vg_input
.display_input_type
=
130 SDL_GameControllerGetType( controller
->handle
);
136 vg_error( ". Failed to attach game controller '%s'. Reason: %s\n",
137 name
, SDL_GetError() );
142 vg_error( ". Too many controllers open! ignoring '%s'\n", name
);
147 static void vg_input_device_event( SDL_Event
*ev
)
149 if( ev
->type
== SDL_CONTROLLERDEVICEADDED
){
150 int is_controller
= SDL_IsGameController( ev
->cdevice
.which
);
151 const char *name
= SDL_JoystickNameForIndex( ev
->cdevice
.which
);
153 Sint32 index
= ev
->cdevice
.which
;
154 SDL_JoystickID instance_id
= SDL_JoystickGetDeviceInstanceID( index
);
155 vg_info( "SDL_CONTROLLERDEVICEADDED | device index: %d, name: '%s'\n",
159 vg_open_gamecontroller( index
);
162 else if( ev
->type
== SDL_CONTROLLERDEVICEREMOVED
){
163 vg_info( "SDL_CONTROLLERDEVICEREMOVED | instance_id: %d\n",
166 for( int i
=0; i
<VG_MAX_CONTROLLERS
; i
++ ){
167 struct vg_controller
*controller
= &vg_input
.controllers
[i
];
169 if( controller
->handle
){
170 if( controller
->instance_id
== ev
->cdevice
.which
){
171 vg_info( " . closing controller at index #%d\n", i
);
172 SDL_GameControllerClose( controller
->handle
);
173 controller
->handle
= NULL
;
174 controller
->instance_id
= -1;
176 if( vg_input
.active_controller_index
== i
){
177 vg_input
.active_controller_index
= -1;
178 vg_input
.display_input_method
= k_input_method_kbm
;
179 vg_info( "display_input: k_input_method_kbm\n" );
188 static void vg_input_controller_event( SDL_Event
*ev
)
190 if( ev
->type
== SDL_CONTROLLERAXISMOTION
){
191 for( int i
=0; i
<VG_MAX_CONTROLLERS
; i
++ ){
192 struct vg_controller
*esta
= &vg_input
.controllers
[i
];
194 if( ev
->caxis
.which
== esta
->instance_id
){
195 float value
= (float)ev
->caxis
.value
/ 32767.0f
;
197 if( ev
->caxis
.axis
== SDL_CONTROLLER_AXIS_LEFTX
||
198 ev
->caxis
.axis
== SDL_CONTROLLER_AXIS_LEFTY
||
199 ev
->caxis
.axis
== SDL_CONTROLLER_AXIS_RIGHTX
||
200 ev
->caxis
.axis
== SDL_CONTROLLER_AXIS_RIGHTY
)
202 float deadz
= vg_clampf( controller_deadzone
, 0.0f
, 0.999f
),
203 high
= vg_maxf( 0.0f
, fabsf(value
) - deadz
);
205 value
= vg_signf(value
) * (high
/ (1.0f
-deadz
));
208 esta
->axises
[ ev
->caxis
.axis
] = value
;
213 else if( ev
->type
== SDL_CONTROLLERBUTTONDOWN
){
214 struct vg_controller
*active
= NULL
;
216 if( vg_input
.active_controller_index
>= 0 )
217 active
= &vg_input
.controllers
[vg_input
.active_controller_index
];
219 if( !active
|| (ev
->cbutton
.which
!= active
->instance_id
) ){
221 vg_input
.active_controller_index
= -1;
222 vg_input
.display_input_method
= k_input_method_kbm
;
224 for( int i
=0; i
<VG_MAX_CONTROLLERS
; i
++ ){
225 if( vg_input
.controllers
[i
].instance_id
== ev
->cbutton
.which
){
226 active
= &vg_input
.controllers
[i
];
227 vg_input
.active_controller_index
= i
;
228 vg_input
.display_input_type
=
229 SDL_GameControllerGetType(active
->handle
);
235 vg_info( "Switching active controller index to #%d\n",
236 vg_input
.active_controller_index
);
239 vg_error( "Input out of range (SDL_JoystickID#%d)\n",
245 if( vg_input
.display_input_method
!= k_input_method_controller
){
246 vg_input
.display_input_method
= k_input_method_controller
;
247 vg_info( "display_input: k_input_method_controller\n" );
249 active
->buttons
[ ev
->cbutton
.button
] = 1;
252 else if( ev
->type
== SDL_CONTROLLERBUTTONUP
){
253 for( int i
=0; i
<VG_MAX_CONTROLLERS
; i
++ ){
254 struct vg_controller
*esta
= &vg_input
.controllers
[i
];
256 if( ev
->cbutton
.which
== esta
->instance_id
){
257 esta
->buttons
[ ev
->cbutton
.button
] = 0;
264 static void vg_process_inputs(void)
267 vg_input
.sdl_keys
= SDL_GetKeyboardState( &count
);
268 vg_input
.sdl_mouse
= SDL_GetMouseState(NULL
,NULL
);
270 if( vg_input
.display_input_method
!= k_input_method_kbm
){
271 /* check for giving keyboard priority */
272 for( int i
=0; i
<count
; i
++ ){
273 if( vg_input
.sdl_keys
[i
] ){
274 vg_input
.display_input_method
= k_input_method_kbm
;
275 vg_info( "display_input: k_input_method_kbm (keyboard %d)\n", i
);
280 /* check for giving mouse priority */
281 if( vg_input
.sdl_mouse
&
282 (SDL_BUTTON(SDL_BUTTON_LEFT
)|SDL_BUTTON(SDL_BUTTON_RIGHT
)|
283 SDL_BUTTON(SDL_BUTTON_MIDDLE
)) )
285 vg_input
.display_input_method
= k_input_method_kbm
;
286 vg_info( "display_input: k_input_method_kbm (mouse)\n" );
291 static void async_vg_input_init( void *payload
, u32 size
)
293 VG_VAR_F32( controller_deadzone
, flags
=VG_VAR_PERSISTENT
);
295 vg_info( "Checking for controllers\n" );
296 SDL_GameControllerAddMappingsFromFile( "gamecontrollerdb.txt" );
298 int joy_count
= SDL_NumJoysticks();
299 for( int i
=0; i
<joy_count
; i
++ ) {
300 const char *name
= SDL_JoystickNameForIndex( i
);
301 int is_controller
= SDL_IsGameController(i
);
303 vg_info( "%d: %s [controller: %d]\n", i
, name
, is_controller
);
306 vg_open_gamecontroller( i
);
311 static void vg_input_init(void)
313 vg_async_call( async_vg_input_init
, NULL
, 0 );
316 static void vg_input_free(void)
318 for( int i
=0; i
<VG_MAX_CONTROLLERS
; i
++ ){
319 struct vg_controller
*controller
= &vg_input
.controllers
[i
];
321 if( controller
->handle
){
322 SDL_GameControllerClose( controller
->handle
);
323 controller
->handle
= NULL
;
328 struct vg_controller
*vg_active_controller(void){
329 if( vg_input
.active_controller_index
>= 0 )
330 return &vg_input
.controllers
[vg_input
.active_controller_index
];
335 static u8
vg_controller_button( SDL_GameControllerButton button
){
336 struct vg_controller
*c
= vg_active_controller();
337 if( c
) return c
->buttons
[ button
];
341 static f32
vg_controller_axis( SDL_GameControllerAxis axis
){
342 struct vg_controller
*c
= vg_active_controller();
343 if( c
) return c
->axises
[ axis
];
347 static void vg_input_apply_to_u8( vg_input_op mode
, u8 data
, u8
*inout_result
){
348 if ( mode
== vg_mode_absmax
) *inout_result
|= data
;
349 else if( mode
== vg_mode_mul
) *inout_result
&= data
;
350 else vg_fatal_error( "mode not supported for destination type (%d)", mode
);
353 static void vg_input_apply_to_f32( vg_input_op mode
, f32 data
,
355 if ( mode
== vg_mode_absmax
){
356 if( fabsf(data
) > fabsf(*inout_result
) )
357 *inout_result
= data
;
359 else if( mode
== vg_mode_max
) *inout_result
= vg_maxf(*inout_result
,data
);
360 else if( mode
== vg_mode_mul
) *inout_result
*= (f32
)data
;
361 else if( mode
== vg_mode_sub
) *inout_result
-= (f32
)data
;
362 else if( mode
== vg_mode_add
) *inout_result
+= (f32
)data
;
363 else vg_fatal_error( "mode not supported for destination type (%d)", mode
);
367 * Run an input program. out_result must point to memory with sufficient
368 * storage respective to the size set by type.
370 static void vg_exec_input_program( enum vg_input_type type
, vg_input_op
*ops
,
372 u8
*out_button
= NULL
;
375 if( type
== k_vg_input_type_button_u8
){
376 out_button
= out_result
;
379 else if( type
== k_vg_input_type_axis_f32
){
380 out_joy
= out_result
;
383 else if( type
== k_vg_input_type_joy_v2f
){
384 out_joy
= out_result
;
390 vg_input_op mode
= vg_mode_absmax
;
391 u32 pc
= 0, index
= 0;
394 vg_input_op op
= ops
[ pc
++ ];
396 if( (op
>= vg_mode_mul
) && (op
<= vg_mode_max
) )
398 else if( (op
== vg_keyboard
) || (op
== vg_mouse
) || (op
== vg_joy_button
) ){
401 if( op
== vg_keyboard
)
402 state
= vg_getkey(ops
[pc
++]);
403 else if( op
== vg_mouse
)
404 state
= (vg_input
.sdl_mouse
& SDL_BUTTON(ops
[pc
++]))?1:0;
406 state
= vg_controller_button(ops
[pc
++]);
408 if( type
== k_vg_input_type_button_u8
)
409 vg_input_apply_to_u8( mode
, state
, out_button
);
411 vg_input_apply_to_f32( mode
, (f32
)state
, &out_joy
[index
] );
413 else if( op
== vg_joy_axis
){
414 f32 state
= vg_controller_axis( ops
[pc
++] );
415 if( type
== k_vg_input_type_button_u8
)
416 vg_input_apply_to_u8( mode
, state
>0.5f
?1:0, out_button
);
418 vg_input_apply_to_f32( mode
, state
, &out_joy
[index
] );
420 else if( (op
== vg_joy_ls
) || (op
== vg_joy_rs
) ){
421 if( type
== k_vg_input_type_joy_v2f
){
422 vg_input_apply_to_f32( mode
,
423 vg_controller_axis( op
==vg_joy_ls
? SDL_CONTROLLER_AXIS_LEFTX
:
424 SDL_CONTROLLER_AXIS_RIGHTX
),
426 vg_input_apply_to_f32( mode
,
427 vg_controller_axis( op
==vg_joy_ls
? SDL_CONTROLLER_AXIS_LEFTY
:
428 SDL_CONTROLLER_AXIS_RIGHTY
),
432 else if( op
== vg_index
)
434 else if( op
== vg_end
)
436 else if( op
== vg_normalize
)
437 v2_normalize( out_joy
);
438 else if( op
== vg_gui_visible
)
441 vg_fatal_error( "unknown op\n" );
447 * Get vendor specific button glyphs based on SDL button ID
449 static const char *controller_button_str( SDL_GameControllerButton button
){
450 static const char *controller_glyphs
[ SDL_CONTROLLER_BUTTON_MAX
][2] = {
451 /* xbox/generic playstation */
452 [ SDL_CONTROLLER_BUTTON_A
] = { "\x1e\x85","\x1e\x82" },
453 [ SDL_CONTROLLER_BUTTON_B
] = { "\x1e\x86","\x1e\x81" },
454 [ SDL_CONTROLLER_BUTTON_X
] = { "\x1e\x83","\x1e\x7f" },
455 [ SDL_CONTROLLER_BUTTON_Y
] = { "\x1e\x84","\x1e\x80" },
456 [ SDL_CONTROLLER_BUTTON_LEFTSTICK
] = { "\x87", "\x87" },
457 [ SDL_CONTROLLER_BUTTON_RIGHTSTICK
] = { "\x8b", "\x8b" },
458 [ SDL_CONTROLLER_BUTTON_LEFTSHOULDER
] = { "\x91", "\x91" },
459 [ SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
]= { "\x92", "\x92" },
460 [ SDL_CONTROLLER_BUTTON_DPAD_LEFT
] = { "\x1e\x93","\x1e\x93" },
461 [ SDL_CONTROLLER_BUTTON_DPAD_UP
] = { "\x1e\x94","\x1e\x94" },
462 [ SDL_CONTROLLER_BUTTON_DPAD_RIGHT
] = { "\x1e\x95","\x1e\x95" },
463 [ SDL_CONTROLLER_BUTTON_DPAD_DOWN
] = { "\x1e\x96","\x1e\x96" },
464 [ SDL_CONTROLLER_BUTTON_GUIDE
] = { "\x91", "\x91" },
467 if( vg_input
.display_input_type
== SDL_CONTROLLER_TYPE_PS3
||
468 vg_input
.display_input_type
== SDL_CONTROLLER_TYPE_PS4
||
469 vg_input
.display_input_type
== SDL_CONTROLLER_TYPE_PS5
)
471 return controller_glyphs
[ button
][ 1 ];
473 else if( vg_input
.display_input_type
==
474 SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO
||
475 vg_input
.display_input_type
==
476 SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT
||
477 vg_input
.display_input_type
==
478 SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR
||
479 vg_input
.display_input_type
==
480 SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT
)
485 return controller_glyphs
[ button
][ 0 ];
489 * Cat keyboard key string. special_glyphs include SR glyphs
491 static void vg_keyboard_key_string( vg_str
*str
, u32 key
, int special_glyphs
){
492 if( (key
>= SDLK_a
) && (key
<= SDLK_z
) ){
493 key
= (key
-SDLK_a
)+(u32
)'A';
495 if( special_glyphs
){
496 vg_strcatch( str
, '\x1f' );
497 vg_strcatch( str
, key
);
498 vg_strcatch( str
, ' ' );
501 vg_strcatch( str
, key
);
503 else if( (key
== SDLK_LSHIFT
) || (key
== SDLK_RSHIFT
) )
504 vg_strcat( str
, special_glyphs
? "\x9e": "shift" );
505 else if( (key
== SDLK_LCTRL
) || (key
== SDLK_RCTRL
) )
506 vg_strcat( str
, special_glyphs
? "\x9f": "ctrl" );
507 else if( (key
== SDLK_LALT
) || (key
== SDLK_RALT
) )
508 vg_strcat( str
, special_glyphs
? "\xa0": "alt" );
509 else if( key
== SDLK_SPACE
)
510 vg_strcat( str
, special_glyphs
? "\xa1": "space" );
511 else if( (key
== SDLK_RETURN
) || (key
== SDLK_RETURN2
) )
512 vg_strcat( str
, special_glyphs
? "\xa2": "return" );
513 else if( key
== SDLK_ESCAPE
)
514 vg_strcat( str
, special_glyphs
? "\xa3": "escape" );
515 else if( key
== SDLK_RIGHT
)
516 vg_strcat( str
, special_glyphs
? "\x1f\x95 ": "right" );
517 else if( key
== SDLK_LEFT
)
518 vg_strcat( str
, special_glyphs
? "\x1f\x93 ": "left" );
519 else if( key
== SDLK_UP
)
520 vg_strcat( str
, special_glyphs
? "\x1f\x94 ": "up" );
521 else if( key
== SDLK_DOWN
)
522 vg_strcat( str
, special_glyphs
? "\x1f\x96 ": "down" );
524 vg_strcat( str
, "keyboard key #" );
525 vg_strcati32( str
, key
);
530 * Cat mouse button string. special_glyphs include SR glyphs
532 static void vg_mouse_button_string( vg_str
*str
, u32 button
,
533 int special_glyphs
){
534 if ( button
== SDL_BUTTON_LEFT
)
535 vg_strcat( str
, special_glyphs
? "\x99": "left mouse" );
536 else if( button
== SDL_BUTTON_RIGHT
)
537 vg_strcat( str
, special_glyphs
? "\x9a": "right mouse" );
538 else if( button
== SDL_BUTTON_MIDDLE
)
539 vg_strcat( str
, special_glyphs
? "\x9c": "middle mouse" );
541 vg_strcat( str
, "mouse button #" );
542 vg_strcati32( str
, button
);
547 * Cat string represeinting single axis
549 static void vg_joy_axis_string( vg_str
*str
,
550 SDL_GameControllerAxis axis
, int special_glyphs
){
551 if( axis
== SDL_CONTROLLER_AXIS_TRIGGERLEFT
)
552 vg_strcat( str
, special_glyphs
?"\x8f":"left trigger" );
553 else if( axis
== SDL_CONTROLLER_AXIS_TRIGGERRIGHT
)
554 vg_strcat( str
, special_glyphs
?"\x90":"right trigger" );
555 else if( axis
== SDL_CONTROLLER_AXIS_LEFTX
)
556 vg_strcat( str
, special_glyphs
?"\x88":"left stick horizontal" );
557 else if( axis
== SDL_CONTROLLER_AXIS_LEFTY
)
558 vg_strcat( str
, special_glyphs
?"\x89":"left stick vertical" );
559 else if( axis
== SDL_CONTROLLER_AXIS_RIGHTX
)
560 vg_strcat( str
, special_glyphs
?"\x8c":"right stick horizontal" );
561 else if( axis
== SDL_CONTROLLER_AXIS_RIGHTY
)
562 vg_strcat( str
, special_glyphs
?"\x8d":"right stick vertical" );
564 vg_strcat( str
, "axis " );
565 vg_strcati32( str
, axis
);
570 * Cat string represeinting whole joystick
572 static void vg_joy_string( vg_str
*str
, vg_input_op op
, int special_glyphs
){
573 if( op
== vg_joy_ls
)
574 vg_strcat( str
, special_glyphs
? "\x87": "left stick" );
576 vg_strcat( str
, special_glyphs
? "\x8b": "right stick" );
580 * Convert an input program into a readable string
582 static void vg_input_string( vg_str
*str
, vg_input_op
*ops
, int glyphs
){
584 int applicable
= 0, visible
= 1;
587 vg_input_op op
= ops
[ pc
++ ];
589 if( (op
== vg_keyboard
) || (op
== vg_mouse
) ){
590 if( (vg_input
.display_input_method
== k_input_method_kbm
) && visible
){
593 if( op
== vg_keyboard
)
594 vg_keyboard_key_string( str
, ops
[pc
], glyphs
);
596 vg_mouse_button_string( str
, ops
[pc
], glyphs
);
601 else if( (op
== vg_joy_button
) || (op
== vg_joy_axis
) ){
602 if( (vg_input
.display_input_method
== k_input_method_controller
)
606 if( op
== vg_joy_button
)
607 vg_strcat( str
, controller_button_str(ops
[pc
]) );
609 vg_joy_axis_string( str
, ops
[pc
], glyphs
);
614 else if( (op
== vg_joy_ls
) || (op
== vg_joy_rs
) ){
615 if( (vg_input
.display_input_method
== k_input_method_controller
)
618 vg_joy_string( str
, op
, glyphs
);
622 else if( op
== vg_mode_mul
){
623 if( applicable
&& visible
)
624 vg_strcat( str
, " + " );
626 else if( op
== vg_index
)
628 else if( op
== vg_gui_visible
)
630 else if( op
== vg_end
)