way better controller handling
[vg.git] / vg_input.h
index 02c271a1ca85b0b88f829cbc8a33264ad57855c6..17f3f167aa23869262f5379819db5cc9afbffefe 100644 (file)
@@ -5,6 +5,8 @@
 #include "common.h"
 #include "vg/vg_loader.h"
 
+#define VG_MAX_CONTROLLERS 4
+
 VG_STATIC inline float vg_get_axis( const char *axis );
 VG_STATIC inline int vg_get_button( const char *button );
 
@@ -67,15 +69,35 @@ struct
    struct input_binding named_inputs[ 32 ];
    u32                  named_input_count;
 
-   const char          *controller_name;
-   SDL_GameController  *controller_handle; /* null if unplugged */
-   SDL_JoystickID       controller_joystick_id;
-   int                  controller_should_use_trackpad_look;
+   struct vg_controller{
+      SDL_GameController *handle; /* handle for controller. NULL if unused */
+      SDL_JoystickID instance_id; /* uid used in events */
+
+      enum evg_controller_type{
+         k_vg_controller_type_standard,
+         k_vg_controller_type_trackpads
+      }
+      type;
 
-   float                controller_axises[ SDL_CONTROLLER_AXIS_MAX ];
-   int                  controller_buttons[ SDL_CONTROLLER_BUTTON_MAX ];
+      float axises[ SDL_CONTROLLER_AXIS_MAX ];
+      int   buttons[ SDL_CONTROLLER_BUTTON_MAX ];
+   }
+   controllers[4];
+
+   int active_controller_index; /* most recent controller (by button press)
+                                   will be -1 if no controllers active */
+
+   /* what the user is currently using. the keyboard and controller are still
+    * active simultaneously, but this reflects what the UI should show */
+   enum userinput_method{
+      k_userinput_method_xbox,
+      k_userinput_method_playstation,
+      k_userinput_method_steamgeneric,
+      k_userinput_method_kbm
+   }
+   input_method;
 }
-VG_STATIC vg_input;
+static vg_input;
 
 VG_STATIC void vg_create_unnamed_input( struct input_binding *bind,
                                         enum input_type type )
@@ -438,6 +460,11 @@ VG_STATIC void vg_input_update( u32 num, struct input_binding *binds )
       return;
    }
 
+   struct vg_controller *acontroller = NULL;
+
+   if( vg_input.active_controller_index != -1 )
+      acontroller = &vg_input.controllers[vg_input.active_controller_index];
+
    for( i32 i=0; i<num; i++ ){
       struct input_binding *bind = &binds[i];
 
@@ -445,17 +472,14 @@ VG_STATIC void vg_input_update( u32 num, struct input_binding *binds )
          bind->button.prev = bind->button.value;
          bind->button.value = 0;
 
-         if( bind->button.gamepad_id != -1 )
-            bind->button.value |= 
-               vg_input.controller_buttons[ bind->button.gamepad_id ];
+         if( acontroller && (bind->button.gamepad_id != -1) )
+            bind->button.value |= acontroller->buttons[bind->button.gamepad_id];
 
-         if( bind->button.keyboard_id != -1 ){
+         if( bind->button.keyboard_id != -1 )
             bind->button.value |= vg_getkey( bind->button.keyboard_id );
-         }
 
          if( bind->button.mouse_id != -1 ){
-            if( SDL_GetMouseState(NULL, NULL) & 
-                                    SDL_BUTTON( bind->button.mouse_id ) )
+            if(SDL_GetMouseState(NULL,NULL) & SDL_BUTTON(bind->button.mouse_id))
                bind->button.value |= 1;
          }
       }
@@ -471,9 +495,8 @@ VG_STATIC void vg_input_update( u32 num, struct input_binding *binds )
             if( vg_getkey( bind->axis.keyboard_negative ) )
                keyboard_value -= 1.0f;
 
-         if( bind->axis.gamepad_axis != -1 ){
-            gamepad_value = 
-               vg_input.controller_axises[ bind->axis.gamepad_axis ];
+         if( acontroller && (bind->axis.gamepad_axis != -1) ){
+            gamepad_value = acontroller->axises[ bind->axis.gamepad_axis ];
 
             if( bind->axis.gamepad_inverted )
                gamepad_value *= -1.0f;
@@ -496,61 +519,188 @@ VG_STATIC void vg_input_update( u32 num, struct input_binding *binds )
             if( vg_getkey( bind->axis.keyboard_positive ))
                value = 1.0f;
          
-         if( bind->axis.gamepad_axis != -1 )
-            value = vg_maxf( value, 
-                  vg_input.controller_axises[bind->axis.gamepad_axis] );
+         if( acontroller && (bind->axis.gamepad_axis != -1) ){
+            float value1 = acontroller->axises[bind->axis.gamepad_axis];
+            value = vg_maxf( value, value1 );
+         }
 
          bind->axis.value = value;
       }
    }
 }
 
-VG_STATIC void vg_input_controller_event( SDL_Event *ev )
+/*
+ * takes SDL device index, and tries to open that on any free channel
+ */
+VG_STATIC void vg_open_gamecontroller( Sint32 index )
 {
-   if( ev->type == SDL_CONTROLLERAXISMOTION ){
-      if( ev->caxis.which == vg_input.controller_joystick_id ){
-         vg_input.controller_axises[ ev->caxis.axis ] = 
-            (float)ev->caxis.value / 32767.0f;
+   struct vg_controller *controller = NULL;
+   int vg_id = 0;
+   const char *name = SDL_GameControllerNameForIndex( index );
+   SDL_JoystickID instance_id = SDL_JoystickGetDeviceInstanceID( index );
+
+   if( instance_id == -1 ){
+      vg_error( ". Invalid device index (vg_open_gamecontroller)\n" );
+      return;
+   }
+
+   for( int j=0; j<VG_MAX_CONTROLLERS; j++ ){
+      struct vg_controller *esta = &vg_input.controllers[j];
+
+      if( esta->handle ){
+         if( esta->instance_id == instance_id ){
+            vg_warn( " . SDL_JoystickID[%d] is already in open at index #%d\n", 
+                     esta->instance_id, j );
+            return;
+         }
+      }
+      else{
+         if( !controller ){
+            controller = &vg_input.controllers[j];
+            vg_id = j;
+         }
       }
    }
-   else if( ev->type == SDL_CONTROLLERBUTTONDOWN ){
-      if( ev->cbutton.which == vg_input.controller_joystick_id )
-         vg_input.controller_buttons[ ev->cbutton.button ] = 1;
+
+   if( controller ){
+      controller->handle = SDL_GameControllerOpen( index );
+      controller->instance_id = instance_id;
+
+      if( controller->handle ){
+         vg_success( 
+               " . opened SDL_JoystickID[%d] as controller '%s' at index #%d\n",
+               instance_id, name, vg_id );
+
+         controller->axises[ SDL_CONTROLLER_AXIS_TRIGGERLEFT ] = -1.0f;
+         controller->axises[ SDL_CONTROLLER_AXIS_TRIGGERRIGHT ] = -1.0f;
+      }
+      else{
+         vg_error( ". Failed to attach game controller '%s'. Reason: %s\n",
+               name, SDL_GetError() );
+      }
    }
-   else if( ev->type == SDL_CONTROLLERBUTTONUP ){
-      if( ev->cbutton.which == vg_input.controller_joystick_id )
-         vg_input.controller_buttons[ ev->cbutton.button ] = 0;
+   else{
+      vg_error( ". Too many controllers open! ignoring '%s'\n", name );
+                
    }
 }
 
-VG_STATIC void vg_try_attach_controller(void)
+VG_STATIC void vg_input_device_event( SDL_Event *ev )
 {
-   int joy_count = SDL_NumJoysticks();
-   for( int i=0; i<joy_count; i++ ) {
-      if( SDL_IsGameController(i) ) {
-         vg_input.controller_handle = SDL_GameControllerOpen(i);
-         vg_input.controller_joystick_id = i;
-         vg_success( "Attached game controller with joystick ID %d\n", i );
-         return;
+   if( ev->type == SDL_CONTROLLERDEVICEADDED ){
+      int is_controller = SDL_IsGameController( ev->cdevice.which );
+      const char *name = SDL_JoystickNameForIndex( ev->cdevice.which );
+
+      Sint32 index = ev->cdevice.which;
+      SDL_JoystickID instance_id = SDL_JoystickGetDeviceInstanceID( index );
+      vg_info( "SDL_CONTROLLERDEVICEADDED | device index: %d, name: '%s'\n", 
+                  index, name );
+
+      if( is_controller ){
+         vg_open_gamecontroller( index );
+      }
+   }
+   else if( ev->type == SDL_CONTROLLERDEVICEREMOVED ){
+      vg_info( "SDL_CONTROLLERDEVICEREMOVED | instance_id: %d\n", 
+                  ev->cdevice.which );
+
+      for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
+         struct vg_controller *controller = &vg_input.controllers[i];
+
+         if( controller->handle ){
+            if( controller->instance_id == ev->cdevice.which ){
+               vg_info( " . closing controller at index #%d\n", i );
+               SDL_GameControllerClose( controller->handle );
+               controller->handle = NULL;
+               controller->instance_id = -1;
+
+               if( vg_input.active_controller_index == i ){
+                  vg_input.active_controller_index = -1;
+                  vg_info( " . active controller is now keyboard and mouse\n" );
+               }
+               break;
+            }
+         }
       }
    }
 }
 
-VG_STATIC void vg_update_inputs(void)
+VG_STATIC void vg_input_controller_event( SDL_Event *ev )
 {
-   vg_input.sdl_keys = SDL_GetKeyboardState(NULL);
+   if( ev->type == SDL_CONTROLLERAXISMOTION ){
+      for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
+         struct vg_controller *esta = &vg_input.controllers[i];
+
+         if( ev->caxis.which == esta->instance_id ){
+            float value = (float)ev->caxis.value / 32767.0f;
+            esta->axises[ ev->caxis.axis ] = value;
+            break;
+         }
+      }
+   }
+   else if( ev->type == SDL_CONTROLLERBUTTONDOWN ){
+      struct vg_controller *active = NULL;
+
+      if( vg_input.active_controller_index != -1 )
+         active = &vg_input.controllers[vg_input.active_controller_index];
+
+      if( !active || (ev->cbutton.which != active->instance_id) ){
+         active = NULL;
+         vg_input.active_controller_index = -1;
+
+         for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
+            if( vg_input.controllers[i].instance_id == ev->cbutton.which ){
+               active = &vg_input.controllers[i];
+               vg_input.active_controller_index = i;
+               break;
+            }
+         }
+         
+         if( active ){
+            vg_info( "Switching active controller index to #%d\n", 
+                       vg_input.active_controller_index );
+         }
+         else{
+            vg_error( "Input out of range (SDL_JoystickID#%d)\n", 
+                       ev->cbutton.which );
+         }
+      }
 
-   if( vg_input.controller_handle ){
-      if( !SDL_GameControllerGetAttached( vg_input.controller_handle ) ){
-         SDL_GameControllerClose( vg_input.controller_handle );
-         vg_input.controller_handle = NULL;
+      if( active )
+         active->buttons[ ev->cbutton.button ] = 1;
+   }
+   else if( ev->type == SDL_CONTROLLERBUTTONUP ){
+      for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
+         struct vg_controller *esta = &vg_input.controllers[i];
+
+         if( ev->cbutton.which == esta->instance_id ){
+            esta->buttons[ ev->cbutton.button ] = 0;
+            break;
+         }
       }
    }
+}
+
+VG_STATIC void vg_process_inputs(void)
+{
+   int count;
+   vg_input.sdl_keys = SDL_GetKeyboardState( &count );
+
+   if( vg_input.input_method != k_userinput_method_kbm ){
+      /* check for giving keyboard priority */
+      for( int i=0; i<count; i++ ){
+         if( vg_input.sdl_keys[i] ){
+            vg_input.input_method = k_userinput_method_kbm;
+            break;
+         }
+      }
 
-   if( !vg_input.controller_handle ){
-      vg_input.controller_axises[ SDL_CONTROLLER_AXIS_TRIGGERLEFT ] = -1.0f;
-      vg_input.controller_axises[ SDL_CONTROLLER_AXIS_TRIGGERRIGHT ] = -1.0f;
-      vg_try_attach_controller();
+      /* check for giving mouse priority */
+      if( SDL_GetMouseState(NULL,NULL) & 
+            (SDL_BUTTON(SDL_BUTTON_LEFT)|SDL_BUTTON(SDL_BUTTON_RIGHT)) )
+      {
+         vg_input.input_method = k_userinput_method_kbm;
+      }
    }
 
    /* update all inputs */
@@ -570,21 +720,21 @@ VG_STATIC void async_vg_input_init( void *payload, u32 size )
    vg_console_reg_cmd( "bind", vg_rebind_input_cmd, vg_rebind_input_cmd_poll );
 
    VG_VAR_F32( controller_deadzone, flags=VG_VAR_PERSISTENT );
-   vg_info( "Checking for controller\n" );
+
+   vg_info( "Checking for controllers\n" );
    SDL_GameControllerAddMappingsFromFile( "gamecontrollerdb.txt" );
    
    int joy_count = SDL_NumJoysticks();
-   for( int i=0; i<joy_count; i++ ) 
-   {
-      vg_info( "joystick %d: %s [gamecontroller: %d]\n", 
-            i, SDL_JoystickNameForIndex( i ),
-            SDL_IsGameController(i) );
-   }
+   for( int i=0; i<joy_count; i++ ) {
+      const char *name = SDL_JoystickNameForIndex( i );
+      int is_controller = SDL_IsGameController(i);
 
-   vg_try_attach_controller();
+      vg_info( "%d: %s [controller: %d]\n", i, name, is_controller );
 
-   vg_input.controller_axises[ SDL_CONTROLLER_AXIS_TRIGGERLEFT ] = -1.0f;
-   vg_input.controller_axises[ SDL_CONTROLLER_AXIS_TRIGGERRIGHT ] = -1.0f;
+      if( is_controller ){
+         vg_open_gamecontroller( i );
+      }
+   }
 }
 
 VG_STATIC void vg_input_init(void)
@@ -594,9 +744,13 @@ VG_STATIC void vg_input_init(void)
 
 VG_STATIC void vg_input_free(void)
 {
-   if( vg_input.controller_handle ){
-      SDL_GameControllerClose( vg_input.controller_handle );
-      vg_input.controller_handle = NULL;
+   for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
+      struct vg_controller *controller = &vg_input.controllers[i];
+
+      if( controller->handle ){
+         SDL_GameControllerClose( controller->handle );
+         controller->handle = NULL;
+      }
    }
 }