input programs? experimental..
[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 #define VG_MAX_CONTROLLERS 4
9
10 static float controller_deadzone = 0.05f;
11 typedef u32 vg_input_op;
12 typedef vg_input_op *vg_input_program;
13
14 enum vg_input_type {
15 k_vg_input_type_button_u8,
16 k_vg_input_type_axis_f32,
17 k_vg_input_type_joy_v2f
18 };
19
20 enum vg_input_op {
21 /* data source */
22 vg_keyboard,
23 vg_mouse,
24 vg_joy_button,
25 vg_joy_axis,
26
27 /* hacky constants */
28 vg_0_0f,
29 vg_0_5f,
30 vg_1_0f,
31 vg_2_0f,
32
33 /* modes */
34 vg_mode_mul,
35 vg_mode_sub,
36 vg_mode_add,
37 vg_mode_absmax,
38 vg_mode_max,
39
40 /* control */
41 vg_index,
42 vg_end,
43
44 /* math */
45 vg_normalize
46 };
47
48 struct{
49 const u8 *sdl_keys;
50 u32 sdl_mouse;
51
52 struct vg_controller{
53 SDL_GameController *handle; /* handle for controller. NULL if unused */
54 SDL_JoystickID instance_id; /* uid used in events */
55
56 float axises[ SDL_CONTROLLER_AXIS_MAX ];
57 u32 buttons[ SDL_CONTROLLER_BUTTON_MAX ];
58 }
59 controllers[4];
60
61 int active_controller_index; /* most recent controller (by button press)
62 will be -1 if no controllers active */
63
64 /* what the user is currently using. the keyboard and controller are still
65 * active simultaneously, but this reflects what the UI should show */
66 enum input_method{
67 k_input_method_kbm,
68 k_input_method_controller
69 }
70 display_input_method;
71 SDL_GameControllerType display_input_type;
72 }
73 static vg_input = { .active_controller_index = -2 };
74
75 static u8 vg_getkey( SDL_Keycode kc )
76 {
77 SDL_Scancode sc = SDL_GetScancodeFromKey( kc );
78 return vg_input.sdl_keys[sc];
79 }
80
81 /*
82 * takes SDL device index, and tries to open that on any free channel
83 */
84 static int vg_open_gamecontroller( Sint32 index )
85 {
86 struct vg_controller *controller = NULL;
87 int vg_id = 0;
88 const char *name = SDL_GameControllerNameForIndex( index );
89 SDL_JoystickID instance_id = SDL_JoystickGetDeviceInstanceID( index );
90
91 if( instance_id == -1 ){
92 vg_error( ". Invalid device index (vg_open_gamecontroller)\n" );
93 return -1;
94 }
95
96 for( int j=0; j<VG_MAX_CONTROLLERS; j++ ){
97 struct vg_controller *esta = &vg_input.controllers[j];
98
99 if( esta->handle ){
100 if( esta->instance_id == instance_id ){
101 vg_warn( " . SDL_JoystickID[%d] is already in open at index #%d\n",
102 esta->instance_id, j );
103 return -1;
104 }
105 }
106 else{
107 if( !controller ){
108 controller = &vg_input.controllers[j];
109 vg_id = j;
110 }
111 }
112 }
113
114 if( controller ){
115 controller->handle = SDL_GameControllerOpen( index );
116 controller->instance_id = instance_id;
117
118 if( controller->handle ){
119 vg_success(
120 " . opened SDL_JoystickID[%d] as controller '%s' at index #%d\n",
121 instance_id, name, vg_id );
122
123 for( u32 i=0; i< SDL_CONTROLLER_BUTTON_MAX; i++ )
124 controller->buttons[i] = 0;
125
126 for( u32 i=0; i< SDL_CONTROLLER_AXIS_MAX; i++ )
127 controller->axises[i] = 0.0f;
128
129 if( vg_input.active_controller_index == -2 ){
130 vg_input.active_controller_index = vg_id;
131 vg_input.display_input_method = k_input_method_controller;
132 vg_input.display_input_type =
133 SDL_GameControllerGetType( controller->handle );
134 }
135
136 return vg_id;
137 }
138 else{
139 vg_error( ". Failed to attach game controller '%s'. Reason: %s\n",
140 name, SDL_GetError() );
141 return -1;
142 }
143 }
144 else{
145 vg_error( ". Too many controllers open! ignoring '%s'\n", name );
146 return -1;
147 }
148 }
149
150 static void vg_input_device_event( SDL_Event *ev )
151 {
152 if( ev->type == SDL_CONTROLLERDEVICEADDED ){
153 int is_controller = SDL_IsGameController( ev->cdevice.which );
154 const char *name = SDL_JoystickNameForIndex( ev->cdevice.which );
155
156 Sint32 index = ev->cdevice.which;
157 SDL_JoystickID instance_id = SDL_JoystickGetDeviceInstanceID( index );
158 vg_info( "SDL_CONTROLLERDEVICEADDED | device index: %d, name: '%s'\n",
159 index, name );
160
161 if( is_controller ){
162 vg_open_gamecontroller( index );
163 }
164 }
165 else if( ev->type == SDL_CONTROLLERDEVICEREMOVED ){
166 vg_info( "SDL_CONTROLLERDEVICEREMOVED | instance_id: %d\n",
167 ev->cdevice.which );
168
169 for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
170 struct vg_controller *controller = &vg_input.controllers[i];
171
172 if( controller->handle ){
173 if( controller->instance_id == ev->cdevice.which ){
174 vg_info( " . closing controller at index #%d\n", i );
175 SDL_GameControllerClose( controller->handle );
176 controller->handle = NULL;
177 controller->instance_id = -1;
178
179 if( vg_input.active_controller_index == i ){
180 vg_input.active_controller_index = -1;
181 vg_input.display_input_method = k_input_method_kbm;
182 vg_info( "display_input: k_input_method_kbm\n" );
183 }
184 break;
185 }
186 }
187 }
188 }
189 }
190
191 static void vg_input_controller_event( SDL_Event *ev )
192 {
193 if( ev->type == SDL_CONTROLLERAXISMOTION ){
194 for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
195 struct vg_controller *esta = &vg_input.controllers[i];
196
197 if( ev->caxis.which == esta->instance_id ){
198 float value = (float)ev->caxis.value / 32767.0f;
199
200 if( ev->caxis.axis == SDL_CONTROLLER_AXIS_LEFTX ||
201 ev->caxis.axis == SDL_CONTROLLER_AXIS_LEFTY ||
202 ev->caxis.axis == SDL_CONTROLLER_AXIS_RIGHTX ||
203 ev->caxis.axis == SDL_CONTROLLER_AXIS_RIGHTY )
204 {
205 float deadz = vg_clampf( controller_deadzone, 0.0f, 0.999f ),
206 high = vg_maxf( 0.0f, fabsf(value) - deadz );
207
208 value = vg_signf(value) * (high / (1.0f-deadz));
209 }
210 else if( ev->caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT ||
211 ev->caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT ){
212 value = 0.5f + value*0.5f;
213 }
214
215 esta->axises[ ev->caxis.axis ] = value;
216 break;
217 }
218 }
219 }
220 else if( ev->type == SDL_CONTROLLERBUTTONDOWN ){
221 struct vg_controller *active = NULL;
222
223 if( vg_input.active_controller_index >= 0 )
224 active = &vg_input.controllers[vg_input.active_controller_index];
225
226 if( !active || (ev->cbutton.which != active->instance_id) ){
227 active = NULL;
228 vg_input.active_controller_index = -1;
229 vg_input.display_input_method = k_input_method_kbm;
230
231 for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
232 if( vg_input.controllers[i].instance_id == ev->cbutton.which ){
233 active = &vg_input.controllers[i];
234 vg_input.active_controller_index = i;
235 vg_input.display_input_type =
236 SDL_GameControllerGetType(active->handle);
237 break;
238 }
239 }
240
241 if( active ){
242 vg_info( "Switching active controller index to #%d\n",
243 vg_input.active_controller_index );
244 }
245 else{
246 vg_error( "Input out of range (SDL_JoystickID#%d)\n",
247 ev->cbutton.which );
248 }
249 }
250
251 if( active ){
252 if( vg_input.display_input_method != k_input_method_controller ){
253 vg_input.display_input_method = k_input_method_controller;
254 vg_info( "display_input: k_input_method_controller\n" );
255 }
256 active->buttons[ ev->cbutton.button ] = 1;
257 }
258 }
259 else if( ev->type == SDL_CONTROLLERBUTTONUP ){
260 for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
261 struct vg_controller *esta = &vg_input.controllers[i];
262
263 if( ev->cbutton.which == esta->instance_id ){
264 esta->buttons[ ev->cbutton.button ] = 0;
265 break;
266 }
267 }
268 }
269 }
270
271 static void vg_process_inputs(void)
272 {
273 int count;
274 vg_input.sdl_keys = SDL_GetKeyboardState( &count );
275 vg_input.sdl_mouse = SDL_GetMouseState(NULL,NULL);
276
277 if( vg_input.display_input_method != k_input_method_kbm ){
278 /* check for giving keyboard priority */
279 for( int i=0; i<count; i++ ){
280 if( vg_input.sdl_keys[i] ){
281 vg_input.display_input_method = k_input_method_kbm;
282 vg_info( "display_input: k_input_method_kbm (keyboard %d)\n", i );
283 break;
284 }
285 }
286
287 /* check for giving mouse priority */
288 if( vg_input.sdl_mouse &
289 (SDL_BUTTON(SDL_BUTTON_LEFT)|SDL_BUTTON(SDL_BUTTON_RIGHT)|
290 SDL_BUTTON(SDL_BUTTON_MIDDLE)) )
291 {
292 vg_input.display_input_method = k_input_method_kbm;
293 vg_info( "display_input: k_input_method_kbm (mouse)\n" );
294 }
295 }
296 }
297
298 static void async_vg_input_init( void *payload, u32 size )
299 {
300 VG_VAR_F32( controller_deadzone, flags=VG_VAR_PERSISTENT );
301
302 vg_info( "Checking for controllers\n" );
303 SDL_GameControllerAddMappingsFromFile( "gamecontrollerdb.txt" );
304
305 int joy_count = SDL_NumJoysticks();
306 for( int i=0; i<joy_count; i++ ) {
307 const char *name = SDL_JoystickNameForIndex( i );
308 int is_controller = SDL_IsGameController(i);
309
310 vg_info( "%d: %s [controller: %d]\n", i, name, is_controller );
311
312 if( is_controller ){
313 vg_open_gamecontroller( i );
314 }
315 }
316 }
317
318 static void vg_input_init(void)
319 {
320 vg_async_call( async_vg_input_init, NULL, 0 );
321 }
322
323 static void vg_input_free(void)
324 {
325 for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
326 struct vg_controller *controller = &vg_input.controllers[i];
327
328 if( controller->handle ){
329 SDL_GameControllerClose( controller->handle );
330 controller->handle = NULL;
331 }
332 }
333 }
334
335 struct vg_controller *vg_active_controller(void){
336 if( vg_input.active_controller_index >= 0 )
337 return &vg_input.controllers[vg_input.active_controller_index];
338 else
339 return NULL;
340 }
341
342 static u8 vg_controller_button( SDL_GameControllerButton button ){
343 struct vg_controller *c = vg_active_controller();
344 if( c ) return c->buttons[ button ];
345 else return 0;
346 }
347
348 static f32 vg_controller_axis( SDL_GameControllerAxis axis ){
349 struct vg_controller *c = vg_active_controller();
350 if( c ) return c->axises[ axis ];
351 else return 0;
352 }
353
354 static void vg_input_apply_to_u8( vg_input_op mode, u8 data, u8 *inout_result ){
355 if ( mode == vg_mode_absmax ) *inout_result |= data;
356 else if( mode == vg_mode_mul ) *inout_result &= data;
357 else vg_fatal_error( "mode not supported for destination type (%d)", mode );
358 }
359
360 static void vg_input_apply_to_f32( vg_input_op mode, f32 data,
361 f32 *inout_result ){
362 if ( mode == vg_mode_absmax ){
363 if( fabsf(data) > fabsf(*inout_result) )
364 *inout_result = data;
365 }
366 else if( mode == vg_mode_max ) *inout_result = vg_maxf(*inout_result,data);
367 else if( mode == vg_mode_mul ) *inout_result *= (f32)data;
368 else if( mode == vg_mode_sub ) *inout_result -= (f32)data;
369 else if( mode == vg_mode_add ) *inout_result += (f32)data;
370 else vg_fatal_error( "mode not supported for destination type (%d)", mode );
371 }
372
373 static void vg_exec_input_program( enum vg_input_type type, vg_input_op *ops,
374 void *out_result ){
375 u8 *out_button = NULL;
376 f32 *out_joy = NULL;
377
378 if( type == k_vg_input_type_button_u8 ){
379 out_button = out_result;
380 *out_button = 0;
381 }
382 else if( type == k_vg_input_type_axis_f32 ){
383 out_joy = out_result;
384 out_joy[0] = 0.0f;
385 }
386 else if( type == k_vg_input_type_joy_v2f ){
387 out_joy = out_result;
388 out_joy[0] = 0.0f;
389 out_joy[1] = 0.0f;
390 }
391
392 /* computer state */
393 vg_input_op mode = vg_mode_absmax;
394 u32 pc = 0, index = 0;
395
396 next_code:;
397 vg_input_op op = ops[ pc ++ ];
398
399 if( (op >= vg_mode_mul) && (op <= vg_mode_max) ){
400 mode = op;
401 }
402 else if( (op == vg_keyboard) || (op == vg_mouse) || (op == vg_joy_button) ){
403 u8 state = 0;
404
405 if( op == vg_keyboard )
406 state = vg_getkey(ops[pc ++]);
407 else if( op == vg_mouse )
408 state = (vg_input.sdl_mouse & SDL_BUTTON(ops[pc ++]))?1:0;
409 else
410 state = vg_controller_button(ops[pc ++]);
411
412 if( type == k_vg_input_type_button_u8 )
413 vg_input_apply_to_u8( mode, state, out_button );
414 else
415 vg_input_apply_to_f32( mode, (f32)state, &out_joy[index] );
416 }
417 else if( op == vg_joy_axis ){
418 f32 state = vg_controller_axis( ops[pc ++] );
419 if( type == k_vg_input_type_button_u8 )
420 vg_input_apply_to_u8( mode, state>0.5f?1:0, out_button );
421 else
422 vg_input_apply_to_f32( mode, state, &out_joy[index] );
423 }
424 else if( (op >= vg_0_0f) && (op <= vg_2_0f) ){
425 f32 value = (f32)(op - vg_0_5f) * 0.5f;
426 vg_input_apply_to_f32( mode, value, &out_joy[index] );
427 }
428 else if( op == vg_index ){
429 index = ops[pc ++];
430 }
431 else if( op == vg_end ){
432 return;
433 }
434 else if( op == vg_normalize ){
435 v2_normalize( out_joy );
436 }
437 else {
438 vg_fatal_error( "unknown op\n" );
439 }
440
441 goto next_code;
442 }
443
444 #endif