deadzones adjustable & other fixes
[vg.git] / vg_input.h
1 /* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
2 #ifndef VG_INPUT_H
3 #define VG_INPUT_H
4
5 #include "common.h"
6 #include "vg/vg_loader.h"
7
8 VG_STATIC inline float vg_get_axis( const char *axis );
9 VG_STATIC inline int vg_get_button( const char *button );
10
11 /*
12 * Cannot be used in fixed update
13 */
14 VG_STATIC inline int vg_get_button_down( const char *button );
15 VG_STATIC inline int vg_get_button_up( const char *button );
16
17 VG_STATIC float g_controller_deadzone = 0.05f;
18
19 enum vg_button_state
20 {
21 k_button_state_down = 1,
22 k_button_state_up = 3,
23 k_button_state_pressed = 2,
24 k_button_state_none = 0
25 };
26
27 struct input_binding
28 {
29 const char *name;
30
31 enum input_type
32 {
33 k_input_type_button,
34 k_input_type_axis,
35 k_input_type_axis_norm,
36
37 k_input_type_unknown,
38 k_input_type_keyboard_key,
39 k_input_type_mouse_button, /* ? TODO */
40 k_input_type_gamepad_axis,
41 k_input_type_gamepad_button
42 }
43 type;
44
45 union
46 {
47 struct input_axis
48 {
49 SDL_GameControllerAxis gamepad_axis;
50 SDL_Keycode keyboard_positive,
51 keyboard_negative;
52
53 int gamepad_inverted;
54 float value;
55 }
56 axis;
57
58 struct
59 {
60 SDL_GameControllerButton gamepad_id;
61 SDL_Keycode keyboard_id;
62 int value, prev;
63 }
64 button;
65 };
66
67 int save_this;
68 };
69
70 struct
71 {
72 const u8 *sdl_keys;
73 struct input_binding named_inputs[ 32 ];
74 u32 named_input_count;
75
76 const char *controller_name;
77 SDL_GameController *controller_handle; /* null if unplugged */
78 SDL_JoystickID controller_joystick_id;
79 int controller_should_use_trackpad_look;
80
81 float controller_axises[ SDL_CONTROLLER_AXIS_MAX ];
82 int controller_buttons[ SDL_CONTROLLER_BUTTON_MAX ];
83 }
84 VG_STATIC vg_input;
85
86 VG_STATIC void vg_create_unnamed_input( struct input_binding *bind,
87 enum input_type type )
88 {
89 memset( bind, 0, sizeof(struct input_binding) );
90
91 bind->name = "API DEFINED";
92 bind->save_this = 0;
93 bind->type = type;
94
95 bind->axis.gamepad_axis = -1;
96 bind->axis.keyboard_positive = -1;
97 bind->axis.keyboard_negative = -1;
98 bind->button.gamepad_id = -1;
99 bind->button.keyboard_id = -1;
100 }
101
102 VG_STATIC struct input_binding *vg_create_named_input( const char *name,
103 enum input_type type )
104 {
105 struct input_binding *bind =
106 &vg_input.named_inputs[ vg_input.named_input_count ++ ];
107 memset( bind, 0, sizeof(struct input_binding) );
108
109 bind->name = name;
110 bind->save_this = 0;
111 bind->type = type;
112
113 bind->axis.gamepad_axis = -1;
114 bind->axis.keyboard_positive = -1;
115 bind->axis.keyboard_negative = -1;
116 bind->button.gamepad_id = -1;
117 bind->button.keyboard_id = -1;
118
119 return bind;
120 }
121
122 VG_STATIC struct input_binding *vg_get_named_input( const char *name )
123 {
124 if( name[0] == '+' || name[0] == '-' )
125 name ++;
126
127 for( u32 i=0; i<vg_input.named_input_count; i++ )
128 {
129 struct input_binding *bind = &vg_input.named_inputs[i];
130 if( !strcmp( bind->name, name ) )
131 return bind;
132 }
133
134 return NULL;
135 }
136
137 struct input_en
138 {
139 enum input_type type;
140
141 const char *alias;
142 int id;
143 }
144 vg_all_bindable_inputs[] =
145 {
146 {k_input_type_keyboard_key, "space", SDLK_SPACE},
147 {k_input_type_keyboard_key, ";", SDLK_SEMICOLON},
148 {k_input_type_keyboard_key, "-", SDLK_MINUS},
149 {k_input_type_keyboard_key, ".", SDLK_PERIOD},
150 {k_input_type_keyboard_key, ",", SDLK_COMMA},
151 {k_input_type_keyboard_key, "=", SDLK_EQUALS},
152 {k_input_type_keyboard_key, "[", SDLK_LEFTBRACKET},
153 {k_input_type_keyboard_key, "]", SDLK_RIGHTBRACKET},
154 {k_input_type_keyboard_key, "left", SDLK_LEFT},
155 {k_input_type_keyboard_key, "right", SDLK_RIGHT},
156 {k_input_type_keyboard_key, "up", SDLK_UP},
157 {k_input_type_keyboard_key, "down", SDLK_DOWN},
158 {k_input_type_keyboard_key, "shift", SDLK_LSHIFT},
159 {k_input_type_keyboard_key, "control", SDLK_LCTRL},
160 {k_input_type_keyboard_key, "\2enter", SDLK_RETURN},
161 {k_input_type_keyboard_key, "\2escape", SDLK_ESCAPE },
162
163 {k_input_type_gamepad_axis, "gp-lt", SDL_CONTROLLER_AXIS_TRIGGERLEFT},
164 {k_input_type_gamepad_axis, "gp-rt", SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
165 {k_input_type_gamepad_axis, "gp-ls-h", SDL_CONTROLLER_AXIS_LEFTX},
166 {k_input_type_gamepad_axis, "gp-ls-v", SDL_CONTROLLER_AXIS_LEFTY},
167 {k_input_type_gamepad_axis, "gp-rs-h", SDL_CONTROLLER_AXIS_RIGHTX},
168 {k_input_type_gamepad_axis, "gp-rs-v", SDL_CONTROLLER_AXIS_RIGHTY},
169
170 {k_input_type_gamepad_button, "gp-a", SDL_CONTROLLER_BUTTON_A},
171 {k_input_type_gamepad_button, "gp-b", SDL_CONTROLLER_BUTTON_B},
172 {k_input_type_gamepad_button, "gp-x", SDL_CONTROLLER_BUTTON_X},
173 {k_input_type_gamepad_button, "gp-y", SDL_CONTROLLER_BUTTON_Y},
174 {k_input_type_gamepad_button, "gp-rb", SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
175 {k_input_type_gamepad_button, "gp-lb", SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
176 {k_input_type_gamepad_button, "gp-rs", SDL_CONTROLLER_BUTTON_RIGHTSTICK},
177 {k_input_type_gamepad_button, "gp-ls", SDL_CONTROLLER_BUTTON_LEFTSTICK},
178 {k_input_type_gamepad_button, "gp-dpad-down", SDL_CONTROLLER_BUTTON_DPAD_DOWN},
179 {k_input_type_gamepad_button, "gp-dpad-left", SDL_CONTROLLER_BUTTON_DPAD_LEFT},
180 {k_input_type_gamepad_button,"gp-dpad-right",SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
181 {k_input_type_gamepad_button, "gp-dpad-up", SDL_CONTROLLER_BUTTON_DPAD_UP},
182 {k_input_type_gamepad_button, "\2gp-menu", SDL_CONTROLLER_BUTTON_BACK}
183 };
184
185 VG_STATIC const char *vg_input_to_str( u32 input, enum input_type input_type )
186 {
187 if( input == -1 )
188 return NULL;
189
190 if( input_type == k_input_type_keyboard_key )
191 {
192 if( (input >= SDLK_a) && (input <= SDLK_z) )
193 {
194 return &"a\0b\0c\0d\0e\0f\0g\0h\0i\0j\0k\0l\0m\0n\0o\0p\0"
195 "q\0r\0s\0t\0u\0v\0w\0x\0y\0z\0"[(input-SDLK_a)*2];
196 }
197
198 if( (input >= SDLK_0) && (input <= SDLK_9) )
199 {
200 return &"0\0" "1\0" "2\0" "3\0" "4\0"
201 "5\0" "6\0" "7\0" "8\0" "9\0"[(input-SDLK_0)*2];
202 }
203 }
204
205 for( int i=0; i<vg_list_size(vg_all_bindable_inputs); i++ )
206 {
207 struct input_en *desc = &vg_all_bindable_inputs[i];
208
209 if( (desc->type == input_type) && (desc->id == input) )
210 return desc->alias;
211 }
212
213 return NULL;
214 }
215
216 VG_STATIC enum input_type vg_str_to_input( const char *str, u32 *input )
217 {
218 if( !str )
219 {
220 *input = -1;
221 return k_input_type_unknown;
222 }
223
224 u32 len = strlen(str);
225
226 if( len == 0 )
227 {
228 *input = -1;
229 return k_input_type_unknown;
230 }
231
232 if( len == 1 )
233 {
234 u8 uch = str[0];
235
236 if( (uch >= (u8)'a') && (uch <= (u8)'z') )
237 {
238 *input = SDLK_a + (uch-(u8)'a');
239 return k_input_type_keyboard_key;
240 }
241
242 if( (uch >= (u8)'0') && (uch <= (u8)'9') )
243 {
244 *input = SDLK_0 + (uch-(u8)'0');
245 return k_input_type_keyboard_key;
246 }
247 }
248
249 for( int i=0; i<vg_list_size(vg_all_bindable_inputs); i++ )
250 {
251 struct input_en *desc = &vg_all_bindable_inputs[i];
252
253 if( !strcmp( desc->alias, str ) )
254 {
255 *input = desc->id;
256 return desc->type;
257 }
258 }
259
260 *input = -1;
261 return k_input_type_unknown;
262 }
263
264 VG_STATIC void vg_print_binding_info( struct input_binding *bind )
265 {
266 vg_info( " name: %s\n", bind->name );
267 vg_info( " type: %s\n", (const char *[]){"button","axis","axis[0-1]"}
268 [ bind->type ] );
269 vg_info( " save this? %d\n", bind->save_this );
270
271 if( (bind->type == k_input_type_axis) ||
272 (bind->type == k_input_type_axis_norm) )
273 {
274 vg_info( " gamepad_axis: %s\n",
275 vg_input_to_str(bind->axis.gamepad_axis, k_input_type_gamepad_axis));
276
277 vg_info( " keyboard_positive: %s\n",
278 vg_input_to_str(bind->axis.keyboard_positive,
279 k_input_type_keyboard_key ));
280
281 vg_info( " keyboard_negative: %s\n",
282 vg_input_to_str(bind->axis.keyboard_negative,
283 k_input_type_keyboard_key ));
284 }
285 else
286 {
287 vg_info( " gamepad_id: %s\n",
288 vg_input_to_str(bind->button.gamepad_id, k_input_type_gamepad_button));
289 vg_info( " keyboard_id: %s\n",
290 vg_input_to_str(bind->button.keyboard_id,
291 k_input_type_keyboard_key));
292 }
293 }
294
295 VG_STATIC void vg_apply_bind_str( struct input_binding *bind,
296 const char *mod,
297 const char *str )
298 {
299 int axis_mod = 0;
300 char modch = ' ';
301 if( (mod[0] == '-') || (mod[0] == '+') )
302 {
303 axis_mod = 1;
304 modch = mod[0];
305 mod ++;
306 }
307
308 int invert = 0;
309 if( (str[0] == '-' ) )
310 {
311 invert = 1;
312 str ++;
313 }
314
315 u32 id;
316 enum input_type type = vg_str_to_input( str, &id );
317
318 if( bind->type == k_input_type_button )
319 {
320 if( axis_mod )
321 {
322 vg_error( "Cannot use axis modifiers on button input!\n" );
323 return;
324 }
325
326 if( invert )
327 {
328 vg_error( "Cannot invert button input!\n" );
329 return;
330 }
331
332 if( type == k_input_type_keyboard_key )
333 bind->button.keyboard_id = id;
334 else if( type == k_input_type_gamepad_button )
335 bind->button.gamepad_id = id;
336 else
337 {
338 vg_error( "Unknown button or key '%s'\n", str );
339 return;
340 }
341 }
342 else if( (bind->type == k_input_type_axis ) ||
343 (bind->type == k_input_type_axis_norm))
344 {
345 if( axis_mod )
346 {
347 if( type == k_input_type_keyboard_key )
348 {
349 if( invert )
350 {
351 vg_error( "Cannot invert a keyboard key!\n" );
352 return;
353 }
354
355 if( modch == '+' )
356 bind->axis.keyboard_positive = id;
357 else
358 bind->axis.keyboard_negative = id;
359 }
360 else
361 {
362 vg_error( "You can only bind keyboard keys to +- axises\n" );
363 return;
364 }
365 }
366 else
367 {
368 if( type == k_input_type_gamepad_axis )
369 {
370 bind->axis.gamepad_inverted = invert;
371 bind->axis.gamepad_axis = id;
372 }
373 else
374 {
375 vg_error( "You can only bind gamepad axises to this\n" );
376 return;
377 }
378 }
379 }
380 }
381
382 /*
383 * bind x jump
384 * bind a -horizontal
385 * bind d +horizontal
386 * bind -gp-ls-h horizontal
387 */
388
389 VG_STATIC int vg_rebind_input_cmd( int argc, const char *argv[] )
390 {
391 if( argc == 0 )
392 {
393 vg_info( "Usage: bind jump x\n" );
394 vg_info( " bind -steerh j\n" );
395 vg_info( " bind steerh gp-ls-h\n" );
396 return 0;
397 }
398
399 const char *str_bind_name = argv[0];
400 struct input_binding *bind = vg_get_named_input( str_bind_name );
401
402 if( !bind )
403 {
404 vg_error( "There is no bind with that name '%s'\n", str_bind_name );
405 return 0;
406 }
407
408 if( argc == 1 )
409 {
410 vg_print_binding_info( bind );
411 return 0;
412 }
413
414 if( argc == 2 )
415 {
416 const char *str_input_id = argv[1];
417
418 vg_apply_bind_str( bind, str_bind_name, str_input_id );
419 return 0;
420 }
421
422 return 0;
423 }
424
425 VG_STATIC u8 vg_getkey( SDL_Keycode kc )
426 {
427 SDL_Scancode sc = SDL_GetScancodeFromKey( kc );
428 return vg_input.sdl_keys[sc];
429 }
430
431 VG_STATIC void vg_input_update( u32 num, struct input_binding *binds )
432 {
433 if( vg_console.enabled )
434 {
435 for( i32 i=0; i<num; i++ )
436 {
437 struct input_binding *bind = &binds[i];
438
439 if( bind->type == k_input_type_button )
440 {
441 bind->button.prev = bind->button.value;
442 bind->button.value = 0;
443 }
444 }
445
446 return;
447 }
448
449 for( i32 i=0; i<num; i++ )
450 {
451 struct input_binding *bind = &binds[i];
452
453 if( bind->type == k_input_type_button )
454 {
455 bind->button.prev = bind->button.value;
456 bind->button.value = 0;
457
458 if( bind->button.gamepad_id != -1 )
459 bind->button.value |=
460 vg_input.controller_buttons[ bind->button.gamepad_id ];
461
462 if( bind->button.keyboard_id != -1 )
463 bind->button.value |= vg_getkey( bind->button.keyboard_id );
464 }
465 else if( bind->type == k_input_type_axis )
466 {
467 float keyboard_value = 0.0f,
468 gamepad_value = 0.0f;
469
470 if( bind->axis.keyboard_positive != -1 )
471 if( vg_getkey( bind->axis.keyboard_positive ) )
472 keyboard_value += 1.0f;
473
474 if( bind->axis.keyboard_negative != -1 )
475 if( vg_getkey( bind->axis.keyboard_negative ) )
476 keyboard_value -= 1.0f;
477
478 if( bind->axis.gamepad_axis != -1 )
479 {
480 gamepad_value =
481 vg_input.controller_axises[ bind->axis.gamepad_axis ];
482
483 if( bind->axis.gamepad_inverted )
484 gamepad_value *= -1.0f;
485 }
486
487 float deadz = vg_clampf( g_controller_deadzone, 0.0f, 0.999f ),
488 high = vg_maxf( 0.0f, fabsf(gamepad_value) - deadz ),
489 norm = high / (1.0f-deadz);
490
491 gamepad_value = vg_signf( gamepad_value ) * norm;
492
493 if( fabsf(keyboard_value) > fabsf(gamepad_value) )
494 bind->axis.value = keyboard_value;
495 else
496 bind->axis.value = gamepad_value;
497 }
498 else if( bind->type == k_input_type_axis_norm )
499 {
500 float value = 0.0f;
501 if( bind->axis.keyboard_positive != -1 )
502 if( vg_getkey( bind->axis.keyboard_positive ))
503 value = 1.0f;
504
505 if( bind->axis.gamepad_axis != -1 )
506 value = vg_maxf( value,
507 vg_input.controller_axises[bind->axis.gamepad_axis] );
508
509 bind->axis.value = value;
510 }
511 }
512 }
513
514 VG_STATIC void vg_input_controller_event( SDL_Event *ev )
515 {
516 if( ev->type == SDL_CONTROLLERAXISMOTION )
517 {
518 if( ev->caxis.which == vg_input.controller_joystick_id )
519 {
520 vg_input.controller_axises[ ev->caxis.axis ] =
521 (float)ev->caxis.value / 32767.0f;
522 }
523 }
524 else if( ev->type == SDL_CONTROLLERBUTTONDOWN )
525 {
526 if( ev->cbutton.which == vg_input.controller_joystick_id )
527 vg_input.controller_buttons[ ev->cbutton.button ] = 1;
528 }
529 else if( ev->type == SDL_CONTROLLERBUTTONUP )
530 {
531 if( ev->cbutton.which == vg_input.controller_joystick_id )
532 vg_input.controller_buttons[ ev->cbutton.button ] = 0;
533 }
534 }
535
536 VG_STATIC void vg_try_attach_controller(void)
537 {
538 int joy_count = SDL_NumJoysticks();
539 for( int i=0; i<joy_count; i++ )
540 {
541 if( SDL_IsGameController(i) )
542 {
543 vg_input.controller_handle = SDL_GameControllerOpen(i);
544 vg_input.controller_joystick_id = i;
545 vg_success( "Attached game controller with joystick ID %d\n", i );
546 return;
547 }
548 }
549 }
550
551
552 VG_STATIC void vg_update_inputs(void)
553 {
554 vg_input.sdl_keys = SDL_GetKeyboardState(NULL);
555
556 if( vg_input.controller_handle )
557 {
558 if( !SDL_GameControllerGetAttached( vg_input.controller_handle ) )
559 {
560 SDL_GameControllerClose( vg_input.controller_handle );
561 vg_input.controller_handle = NULL;
562 }
563 }
564
565 if( !vg_input.controller_handle )
566 {
567 vg_input.controller_axises[ SDL_CONTROLLER_AXIS_TRIGGERLEFT ] = -1.0f;
568 vg_input.controller_axises[ SDL_CONTROLLER_AXIS_TRIGGERRIGHT ] = -1.0f;
569 vg_try_attach_controller();
570 }
571
572 /* update all inputs */
573 vg_input_update( vg_input.named_input_count, vg_input.named_inputs );
574 }
575
576 VG_STATIC int vg_console_enabled(void);
577 VG_STATIC int vg_input_button_down( struct input_binding *bind )
578 {
579 if( bind->button.value && !bind->button.prev )
580 return 1;
581 return 0;
582 }
583
584 VG_STATIC void vg_input_init(void)
585 {
586 vg_acquire_thread_sync();
587
588 vg_function_push( (struct vg_cmd)
589 {
590 .name = "bind",
591 .function = vg_rebind_input_cmd
592 });
593
594 vg_convar_push( (struct vg_convar){
595 .name = "controller_deadzone",
596 .data = &g_controller_deadzone,
597 .data_type = k_convar_dtype_f32,
598 .opt_f32 = { .clamp = 0 },
599 .persistent = 1
600 });
601
602 vg_info( "Checking for controller\n" );
603 SDL_GameControllerAddMappingsFromFile( "gamecontrollerdb.txt" );
604
605 int joy_count = SDL_NumJoysticks();
606 for( int i=0; i<joy_count; i++ )
607 {
608 vg_info( "joystick %d: %s [gamecontroller: %d]\n",
609 i, SDL_JoystickNameForIndex( i ),
610 SDL_IsGameController(i) );
611 }
612
613 vg_try_attach_controller();
614
615 vg_input.controller_axises[ SDL_CONTROLLER_AXIS_TRIGGERLEFT ] = -1.0f;
616 vg_input.controller_axises[ SDL_CONTROLLER_AXIS_TRIGGERRIGHT ] = -1.0f;
617
618 vg_release_thread_sync();
619 }
620
621 VG_STATIC void vg_input_free(void)
622 {
623 if( vg_input.controller_handle )
624 {
625 SDL_GameControllerClose( vg_input.controller_handle );
626 vg_input.controller_handle = NULL;
627 }
628 }
629
630 #endif