Browse Source

Xbox 360 controller support

lilligreen 11 years ago
parent
commit
a96a8c0a80

+ 176 - 18
engine/source/input/actionMap.cc

@@ -29,8 +29,10 @@
 #include "io/fileStream.h"
 #include "io/fileStream.h"
 #include "io/resource/resourceManager.h"
 #include "io/resource/resourceManager.h"
 
 
-// Script bindings.
-#include "actionMap_ScriptBinding.h"
+// Script bindings.
+#include "actionMap_ScriptBinding.h"
+
+#define CONST_E 2.7182818284590452353602874f
 
 
 IMPLEMENT_CONOBJECT(ActionMap);
 IMPLEMENT_CONOBJECT(ActionMap);
 
 
@@ -786,6 +788,11 @@ bool ActionMap::getDeviceTypeAndInstance(const char *pDeviceName, U32 &deviceTyp
    {
    {
        deviceType = ScreenTouchDeviceType;
        deviceType = ScreenTouchDeviceType;
        offset = dStrlen("touchdevice");
        offset = dStrlen("touchdevice");
+   }
+    else if (dStrnicmp(pDeviceName, "gamepad", dStrlen("gamepad")) == 0)
+   {
+      deviceType = GamepadDeviceType;
+      offset     = dStrlen("gamepad");
    }
    }
    else
    else
       return false;
       return false;
@@ -834,6 +841,10 @@ bool ActionMap::getDeviceName(const U32 deviceType, const U32 deviceInstance, ch
      case ScreenTouchDeviceType:
      case ScreenTouchDeviceType:
      dStrcpy(buffer, "touchdevice");
      dStrcpy(buffer, "touchdevice");
      break;
      break;
+
+	  case GamepadDeviceType:
+      dSprintf(buffer, 16, "gamepad%d", deviceInstance);
+      break;
            
            
      default:
      default:
       Con::errorf( "ActionMap::getDeviceName: unknown device type specified, %d (inst: %d)", deviceType, deviceInstance);
       Con::errorf( "ActionMap::getDeviceName: unknown device type specified, %d (inst: %d)", deviceType, deviceInstance);
@@ -1022,7 +1033,7 @@ bool ActionMap::processBindCmd(const char *device, const char *action, const cha
        ( eventDescriptor.eventCode == SI_GYROZ )    ||
        ( eventDescriptor.eventCode == SI_GYROZ )    ||
        ( eventDescriptor.eventCode == SI_YAW )      ||
        ( eventDescriptor.eventCode == SI_YAW )      ||
        ( eventDescriptor.eventCode == SI_PITCH )    ||
        ( eventDescriptor.eventCode == SI_PITCH )    ||
-       ( eventDescriptor.eventCode == SI_ROLL ) )
+       ( eventDescriptor.eventCode == SI_ROLL ) )	
    {
    {
       Con::warnf( "ActionMap::processBindCmd - Cannot use 'bindCmd' with a move event type. Use 'bind' instead." );
       Con::warnf( "ActionMap::processBindCmd - Cannot use 'bindCmd' with a move event type. Use 'bind' instead." );
       return false;
       return false;
@@ -1108,6 +1119,9 @@ bool ActionMap::processBind(const U32 argc, const char** argv, SimObject* object
             break;
             break;
            case 'i': case 'I':
            case 'i': case 'I':
             assignedFlags |= Node::Inverted;
             assignedFlags |= Node::Inverted;
+            break;
+			case 'n': case 'N':
+            assignedFlags |= Node::NonLinear;
             break;
             break;
 
 
            default:
            default:
@@ -1230,8 +1244,20 @@ bool ActionMap::processAction(const InputEvent* pEvent)
           value *= pNode->scaleFactor;
           value *= pNode->scaleFactor;
 
 
       if (pNode->flags & Node::HasDeadZone)
       if (pNode->flags & Node::HasDeadZone)
-         if (value >= pNode->deadZoneBegin && value <= pNode->deadZoneEnd)
+	  {
+		  if (value >= pNode->deadZoneBegin && value <= pNode->deadZoneEnd)
              value = 0.0f;
              value = 0.0f;
+		  else			
+         {
+            if( value > 0 )
+               value = ( value - pNode->deadZoneBegin ) * ( 1.f / ( 1.f - pNode->deadZoneBegin ) );
+            else
+               value = ( value + pNode->deadZoneBegin ) * ( 1.f / ( 1.f - pNode->deadZoneBegin ) );
+         }
+	  }
+
+	   if( pNode->flags & Node::NonLinear )
+         value = ( value < 0.f ? -1.f : 1.f ) * mPow( mFabs( value ), CONST_E );
 
 
       // Ok, we're all set up, call the function.
       // Ok, we're all set up, call the function.
       if(pNode->flags & Node::BindCmd)
       if(pNode->flags & Node::BindCmd)
@@ -1311,21 +1337,61 @@ bool ActionMap::processAction(const InputEvent* pEvent)
 
 
          return true;
          return true;
       } 
       } 
-      else
+       else if ( (pEvent->objType == XI_POS || pEvent->objType == XI_FLOAT || pEvent->objType == XI_ROT || pEvent->objType == XI_INT) )
       {
       {
-         // Joystick events...
-         const Node* pNode = findNode( pEvent->deviceType, pEvent->deviceInst,
-                                       pEvent->modifier,   pEvent->objType );
+         const Node* pNode = findNode(pEvent->deviceType, pEvent->deviceInst,
+                                      pEvent->modifier,   pEvent->objInst);
+
+         if( pNode == NULL )
+            return false;
 
 
-         if ( pNode == NULL )
+         // Ok, we're all set up, call the function.
+         argv[0] = pNode->consoleFunction;
+         S32 argc = 1;
+
+         if (pEvent->objType == XI_INT)
          {
          {
-            // Check to see if we clear the modifiers, do we find an action?
-            if (pEvent->modifier != 0)
-               pNode = findNode( pEvent->deviceType, pEvent->deviceInst, 0, pEvent->objType );
+            // Handle the integer as some sort of motion such as a
+            // single component to an absolute position
+            argv[1] = Con::getIntArg( pEvent->iValue );
+            argc += 1;
+         }
+         else if (pEvent->objType == XI_FLOAT)
+         {
+            // Handle float as some sort of motion such as a
+            // single component to an absolute position
+            argv[1] = Con::getFloatArg( pEvent->fValue );
+            argc += 1;
+         }
+         else if (pEvent->objType == XI_POS)
+         {
+            // Handle Point3F type position
+            argv[1] = Con::getFloatArg( pEvent->fValue );
+            argv[2] = Con::getFloatArg( pEvent->fValue2 );
+            argv[3] = Con::getFloatArg( pEvent->fValue3 );
+
+            argc += 3;
+         }
 
 
-               if ( pNode == NULL )
-                  return false;
+         if (pNode->object)
+         {
+            Con::execute(pNode->object, argc, argv);
          }
          }
+         else
+         {
+            Con::execute(argc, argv);
+         }
+
+         return true;
+      }
+	    else if ( pEvent->deviceType == JoystickDeviceType || pEvent->deviceType == GamepadDeviceType )
+      {
+         // Joystick events...
+         const Node* pNode = findNode( pEvent->deviceType, pEvent->deviceInst,
+                                       pEvent->modifier,   pEvent->objInst );
+
+         if( pNode == NULL )
+            return false;
 
 
          // "Do nothing" bind:
          // "Do nothing" bind:
          if ( !pNode->consoleFunction[0] )
          if ( !pNode->consoleFunction[0] )
@@ -1336,7 +1402,6 @@ bool ActionMap::processAction(const InputEvent* pEvent)
          //  move events except that they don't ignore dead zone.
          //  move events except that they don't ignore dead zone.
          //
          //
          F32 value = pEvent->fValue;
          F32 value = pEvent->fValue;
-          
          if ( pNode->flags & Node::Inverted )
          if ( pNode->flags & Node::Inverted )
             value *= -1.0f;
             value *= -1.0f;
 
 
@@ -1344,15 +1409,27 @@ bool ActionMap::processAction(const InputEvent* pEvent)
             value *= pNode->scaleFactor;
             value *= pNode->scaleFactor;
 
 
          if ( pNode->flags & Node::HasDeadZone )
          if ( pNode->flags & Node::HasDeadZone )
-            if ( value >= pNode->deadZoneBegin && value <= pNode->deadZoneEnd )
+         {
+            if ( value >= pNode->deadZoneBegin &&
+                 value <= pNode->deadZoneEnd )
                value = 0.0f;
                value = 0.0f;
+            else
+            {
+               if( value > 0 )
+                  value = ( value - pNode->deadZoneBegin ) * ( 1.f / ( 1.f - pNode->deadZoneBegin ) );
+               else
+                  value = ( value + pNode->deadZoneBegin ) * ( 1.f / ( 1.f - pNode->deadZoneBegin ) );
+            }
+         }
+
+         if( pNode->flags & Node::NonLinear )
+            value = ( value < 0.f ? -1.f : 1.f ) * mPow( mFabs( value ), CONST_E );
 
 
          // Ok, we're all set up, call the function.
          // Ok, we're all set up, call the function.
          argv[0] = pNode->consoleFunction;
          argv[0] = pNode->consoleFunction;
          argv[1] = Con::getFloatArg( value );
          argv[1] = Con::getFloatArg( value );
-          
          if (pNode->object)
          if (pNode->object)
-            Con::executef(pNode->object, 2, argv[0], argv[1]);
+            Con::executef(pNode->object, S32(argv[0]), argv[1]);
          else
          else
             Con::execute(2, argv);
             Con::execute(2, argv);
 
 
@@ -1410,7 +1487,47 @@ bool ActionMap::processAction(const InputEvent* pEvent)
    {
    {
       return checkBreakTable(pEvent);
       return checkBreakTable(pEvent);
    }
    }
+      else if (pEvent->action == SI_VALUE)
+   {
+      if ( (pEvent->objType == XI_FLOAT || pEvent->objType == XI_INT) )
+      {
+         const Node* pNode = findNode(pEvent->deviceType, pEvent->deviceInst,
+                                      pEvent->modifier,   pEvent->objInst);
+
+         if( pNode == NULL )
+            return false;
+
+         // Ok, we're all set up, call the function.
+         argv[0] = pNode->consoleFunction;
+         S32 argc = 1;
 
 
+         if (pEvent->objType == XI_INT)
+         {
+            // Handle the integer as some sort of motion such as a
+            // single component to an absolute position
+            argv[1] = Con::getIntArg( pEvent->iValue );
+            argc += 1;
+         }
+         else if (pEvent->objType == XI_FLOAT)
+         {
+            // Handle float as some sort of motion such as a
+            // single component to an absolute position
+            argv[1] = Con::getFloatArg( pEvent->fValue );
+            argc += 1;
+         }
+
+         if (pNode->object)
+         {
+            Con::execute(pNode->object, argc, argv);
+         }
+         else
+         {
+            Con::execute(argc, argv);
+         }
+
+         return true;
+      }
+   }
    return false;
    return false;
 }
 }
 
 
@@ -1730,6 +1847,47 @@ CodeMapping gVirtualMap[] =
    { "lpov2",         SI_POV,    SI_LPOV2        },
    { "lpov2",         SI_POV,    SI_LPOV2        },
    { "rpov2",         SI_POV,    SI_RPOV2        },
    { "rpov2",         SI_POV,    SI_RPOV2        },
 
 
+   #if defined( TORQUE_OS_WIN32 ) || defined( TORQUE_OS_XENON )
+   //-------------------------------------- XINPUT EVENTS
+   // Controller connect / disconnect:
+   { "connect",       XI_BUTTON, XI_CONNECT     },
+   
+   // L & R Thumbsticks:
+   { "thumblx",       XI_AXIS,   XI_THUMBLX     },
+   { "thumbly",       XI_AXIS,   XI_THUMBLY     },
+   { "thumbrx",       XI_AXIS,   XI_THUMBRX     },
+   { "thumbry",       XI_AXIS,   XI_THUMBRY     },
+
+   // L & R Triggers:
+   { "triggerl",      XI_AXIS,   XI_LEFT_TRIGGER  },
+   { "triggerr",      XI_AXIS,   XI_RIGHT_TRIGGER },
+
+   // DPAD Buttons:
+   { "dpadu",         XI_BUTTON, SI_UPOV     },
+   { "dpadd",         XI_BUTTON, SI_DPOV   },
+   { "dpadl",         XI_BUTTON, SI_LPOV   },
+   { "dpadr",         XI_BUTTON, SI_RPOV  },
+
+   // START & BACK Buttons:
+   { "btn_start",     XI_BUTTON, XI_START       },
+   { "btn_back",      XI_BUTTON, XI_BACK        },
+
+   // L & R Thumbstick Buttons:
+   { "btn_lt",        XI_BUTTON, XI_LEFT_THUMB  },
+   { "btn_rt",        XI_BUTTON, XI_RIGHT_THUMB },
+
+   // L & R Shoulder Buttons:
+   { "btn_l",         XI_BUTTON, XI_LEFT_SHOULDER  },
+   { "btn_r",         XI_BUTTON, XI_RIGHT_SHOULDER },
+
+   // Primary buttons:
+   { "btn_a",         XI_BUTTON, XI_A           },
+   { "btn_b",         XI_BUTTON, XI_B           },
+   { "btn_x",         XI_BUTTON, XI_X           },
+   { "btn_y",         XI_BUTTON, XI_Y           },
+#endif
+
+
    //-------------------------------------- MOTION EVENTS
    //-------------------------------------- MOTION EVENTS
    // Accelerometer/Gyroscope axes:
    // Accelerometer/Gyroscope axes:
    { "accelx",        SI_MOTION,    SI_ACCELX    },
    { "accelx",        SI_MOTION,    SI_ACCELX    },

+ 5 - 1
engine/source/input/actionMap.h

@@ -61,7 +61,8 @@ class ActionMap : public SimObject
          HasScale    = BIT(1),   ///< Scaled input.
          HasScale    = BIT(1),   ///< Scaled input.
          HasDeadZone = BIT(2),   ///< Dead zone is present.
          HasDeadZone = BIT(2),   ///< Dead zone is present.
          Inverted    = BIT(3),   ///< Input is inverted.
          Inverted    = BIT(3),   ///< Input is inverted.
-         BindCmd     = BIT(4)    ///< Bind a console command to this.
+		 NonLinear   = BIT(4),
+         BindCmd     = BIT(5)    ///< Bind a console command to this.
       };
       };
 
 
       U32 flags;           /// @see Node::Flags
       U32 flags;           /// @see Node::Flags
@@ -134,6 +135,9 @@ class ActionMap : public SimObject
 
 
    static const char* getModifierString(const U32 modifiers);
    static const char* getModifierString(const U32 modifiers);
 
 
+   /// Pass index to a break entry, and this function will fire it off.
+   static void fireBreakEvent(U32 idx, F32 value = 0.f);
+
   public:
   public:
    ActionMap();
    ActionMap();
    ~ActionMap();
    ~ActionMap();

+ 75 - 3
engine/source/platform/event.h

@@ -177,7 +177,11 @@ struct ScreenTouchEvent : public Event
 struct InputEvent : public Event
 struct InputEvent : public Event
 {
 {
    U32   deviceInst;  ///< Device instance: joystick0, joystick1, etc
    U32   deviceInst;  ///< Device instance: joystick0, joystick1, etc
-   float fValue;      ///< Value ranges from -1.0 to 1.0
+   F32   fValue;      ///< Value ranges from -1.0 to 1.0
+   F32   fValue2;
+   F32   fValue3;
+   F32   fValue4;
+   S32   iValue;
    U16   deviceType;  ///< One of mouse, keyboard, joystick, unknown
    U16   deviceType;  ///< One of mouse, keyboard, joystick, unknown
    U16   objType;     ///< One of SI_XAXIS, SI_BUTTON, SI_KEY ...
    U16   objType;     ///< One of SI_XAXIS, SI_BUTTON, SI_KEY ...
    U16   ascii;       ///< ASCII character code if this is a keyboard event.
    U16   ascii;       ///< ASCII character code if this is a keyboard event.
@@ -388,7 +392,34 @@ enum JoystickCodes {
    SI_UPOV2          = 0x214,
    SI_UPOV2          = 0x214,
    SI_DPOV2          = 0x215,
    SI_DPOV2          = 0x215,
    SI_LPOV2          = 0x216,
    SI_LPOV2          = 0x216,
-   SI_RPOV2          = 0x217
+   SI_RPOV2          = 0x217,
+
+//Xinput specific
+
+   XI_CONNECT        = 0x300,
+   XI_THUMBLX        = 0x301,
+   XI_THUMBLY        = 0x302,
+   XI_THUMBRX        = 0x303,
+   XI_THUMBRY        = 0x304,
+   XI_LEFT_TRIGGER   = 0x305,
+   XI_RIGHT_TRIGGER  = 0x306,
+
+   XI_DPAD_UP        = 0x206,
+   XI_DPAD_DOWN      = 0x207,
+   XI_DPAD_LEFT      = 0x208,
+   XI_DPAD_RIGHT     = 0x209,
+   
+   XI_START          = 0x311,
+   XI_BACK           = 0x312,
+   XI_LEFT_THUMB     = 0x313,
+   XI_RIGHT_THUMB    = 0x314,
+   XI_LEFT_SHOULDER  = 0x315,
+   XI_RIGHT_SHOULDER = 0x316,
+
+   XI_A              = 0x317,
+   XI_B              = 0x318,
+   XI_X              = 0x319,
+   XI_Y              = 0x320
 };
 };
 
 
 enum AccelerometerCodes
 enum AccelerometerCodes
@@ -428,9 +459,12 @@ enum InputDeviceTypes
    MouseDeviceType,
    MouseDeviceType,
    KeyboardDeviceType,
    KeyboardDeviceType,
    JoystickDeviceType,
    JoystickDeviceType,
+   GamepadDeviceType,
+   XInputDeviceType,
    ScreenTouchDeviceType,
    ScreenTouchDeviceType,
    AccelerometerDeviceType,
    AccelerometerDeviceType,
    GyroscopeDeviceType
    GyroscopeDeviceType
+
 };
 };
 
 
 /// Device Event Action Types
 /// Device Event Action Types
@@ -438,8 +472,9 @@ enum InputDeviceTypes
 #define SI_BREAK     0x02
 #define SI_BREAK     0x02
 #define SI_MOVE      0x03
 #define SI_MOVE      0x03
 #define SI_REPEAT    0x04
 #define SI_REPEAT    0x04
+#define SI_VALUE	 0x05
 
 
-///Device Event Types
+//Device Event Types
 #define SI_UNKNOWN   0x01
 #define SI_UNKNOWN   0x01
 #define SI_BUTTON    0x02
 #define SI_BUTTON    0x02
 #define SI_POV       0x03
 #define SI_POV       0x03
@@ -449,6 +484,8 @@ enum InputDeviceTypes
 #define SI_GESTURE   0x0D
 #define SI_GESTURE   0x0D
 #define SI_MOTION    0x0F
 #define SI_MOTION    0x0F
 
 
+
+
 /// Event SubTypes
 /// Event SubTypes
 #define SI_ANY       0xff
 #define SI_ANY       0xff
 
 
@@ -471,4 +508,39 @@ enum InputDeviceTypes
 
 
 /// @}
 /// @}
 
 
+//Xinput structs
+
+typedef U32 InputObjectInstances;
+
+enum XInputEventType
+{
+   XI_UNKNOWN = 0x01,
+   XI_BUTTON  = 0x02,   // Button press/release
+   XI_POV     = 0x03,   // Point of View hat
+   XI_AXIS    = 0x04,   // Axis in range -1.0..1.0
+   XI_POS     = 0x05,   // Absolute position value (Point3F)
+   XI_ROT     = 0x06,   // Absolute rotation value (QuatF)
+   XI_INT     = 0x07,   // Integer value (S32)
+   XI_FLOAT   = 0x08,   // Float value (F32)
+   XI_KEY     = 0x0A,   // Keyboard key
+};
+
+enum InputActionType
+{
+   /// Button was depressed.
+   XI_MAKE    = 0x01,
+
+   /// Button was released.
+   XI_BREAK   = 0x02,
+
+   /// An axis moved.
+   XI_MOVE    = 0x03,
+
+   /// A key repeat occurred. Happens in between a SI_MAKE and SI_BREAK.
+   XI_REPEAT  = 0x04,
+
+   /// A value of some type.  Matched with SI_FLOAT or SI_INT.
+   XI_VALUE   = 0x05,
+};
+
 #endif
 #endif

+ 123 - 0
engine/source/platform/platformInput_ScriptBinding.h

@@ -92,3 +92,126 @@ ConsoleFunctionWithDocs( echoInputState, ConsoleVoid, 1, 1, ())
 {
 {
 	Input::echoInputState();
 	Input::echoInputState();
 }
 }
+
+/*!	Enables XInput for Xbox 360 controllers.
+	@return No return value.
+*/
+ConsoleFunctionWithDocs( enableXInput, ConsoleBool, 1, 1, ())
+            
+{
+   return( DInputManager::enableXInput() );
+}
+
+/*! Disables XInput for Xbox 360 controllesr
+	@return No return value.
+*/
+
+ConsoleFunctionWithDocs( disableXInput, ConsoleVoid, 1, 1, ())
+{
+   DInputManager::disableXInput();
+}
+
+/*!	Rebuilds the XInput section of the InputManager.
+     @return No return value.
+*/
+ConsoleFunctionWithDocs( resetXInput, ConsoleVoid, 1, 1, ())
+{
+   // This function requests a full "refresh" of events for all controllers the
+   // next time we go through the input processing loop. This is useful to call
+   // at the beginning of your game code after your actionMap is set up to hook
+   // all of the appropriate events
+   
+   DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
+   if ( mgr && mgr->isEnabled() ) 
+      mgr->resetXInput();
+}
+
+/*!	Checks to see if an Xbox 360 controller is connected
+	@param controllerID Zero-based index of the controller to check.
+	@return 1 if the controller is connected, 0 if it isn't, and 205 if XInput
+	hasn't been initialized.
+*/
+
+ConsoleFunctionWithDocs( isXInputConnected, ConsoleBool, 2, 2, ( int controllerID ))
+{
+   DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
+   if ( mgr && mgr->isEnabled() ) return mgr->isXInputConnected( atoi( argv[1] ) );
+   return false;
+}
+
+/*!	Queries the current state of a connected Xbox 360 controller.
+	@param controllerID Zero-based index of the controller to return information about.
+    @param property Name of input action being queried, such as \"XI_THUMBLX\".
+    @param current True checks current device in action.
+    @return Button queried - 1 if the button is pressed, 0 if it's not.
+    @return Thumbstick queried - Int representing displacement from rest position.
+    @return %Trigger queried - Int from 0 to 255 representing how far the trigger is displaced.
+*/
+
+//------------------------------------------------------------------------------
+ConsoleFunctionWithDocs( getXInputState, ConsoleInt, 3, 4, ( int controllerID, string property, bool current ) )
+{
+   DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
+
+   if ( !mgr || !mgr->isEnabled() ) 
+      return -1;
+
+   // Use a little bit of macro magic to simplify this otherwise monolothic
+   // block of code.
+#define GET_XI_STATE(constName) \
+   if (!dStricmp(argv[2], #constName)) \
+      return mgr->getXInputState( dAtoi( argv[1] ), constName, ( dAtoi ( argv[3] ) == 1) );
+   
+   GET_XI_STATE(XI_THUMBLX);
+   GET_XI_STATE(XI_THUMBLY);
+   GET_XI_STATE(XI_THUMBRX);
+   GET_XI_STATE(XI_THUMBRY);
+   GET_XI_STATE(XI_LEFT_TRIGGER);
+   GET_XI_STATE(XI_RIGHT_TRIGGER);
+   GET_XI_STATE(SI_UPOV);
+   GET_XI_STATE(SI_DPOV);
+   GET_XI_STATE(SI_LPOV);
+   GET_XI_STATE(SI_RPOV);
+   GET_XI_STATE(XI_START);
+   GET_XI_STATE(XI_BACK);
+   GET_XI_STATE(XI_LEFT_THUMB);
+   GET_XI_STATE(XI_RIGHT_THUMB);
+   GET_XI_STATE(XI_LEFT_SHOULDER);
+   GET_XI_STATE(XI_RIGHT_SHOULDER);
+   GET_XI_STATE(XI_A);
+   GET_XI_STATE(XI_B);
+   GET_XI_STATE(XI_X);
+   GET_XI_STATE(XI_Y);
+#undef GET_XI_STATE
+
+   return -1;
+}
+
+
+/*!	Activates the vibration motors in the specified controller.
+    The controller will constantly at it's xRumble and yRumble intensities until
+    changed or told to stop.
+     
+	 Valid inputs for xRumble/yRumble are [0 - 1].
+
+	 @param device Name of the device to rumble.
+      @param xRumble Intensity to apply to the left motor.
+      @param yRumble Intensity to apply to the right motor.
+
+	 @note in an Xbox 360 controller, the left motor is low-frequency,
+     while the right motor is high-frequency.
+
+*/
+
+ConsoleFunctionWithDocs( rumble, ConsoleVoid, 4, 4, (string device, float xRumble, float yRumble) )
+{
+   DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
+   if ( mgr && mgr->isEnabled() )
+   {
+      mgr->rumble(argv[1], dAtof(argv[2]), dAtof(argv[3]));
+   }
+   else
+   {
+      Con::printf( "DirectInput/XInput is not enabled." );
+   }
+}

+ 93 - 0
engine/source/platformWin32/winDInputDevice.cc

@@ -1040,6 +1040,99 @@ bool DInputDevice::buildEvent( DWORD offset, S32 newData, S32 oldData )
    return true;
    return true;
 }
 }
 
 
+
+void DInputDevice::rumble(float x, float y)
+{
+   LONG            rglDirection[2] = { 0, 0 };
+   DICONSTANTFORCE cf              = { 0 };
+   HRESULT         result;
+
+   // Now set the new parameters and start the effect immediately.
+   if (!mForceFeedbackEffect)
+   {
+      DIEFFECT eff;
+      ZeroMemory( &eff, sizeof(eff) );
+      eff.dwSize                  = sizeof(DIEFFECT);
+      eff.dwFlags                 = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
+      eff.dwDuration              = INFINITE;
+      eff.dwSamplePeriod          = 0;
+      eff.dwGain                  = DI_FFNOMINALMAX;
+      eff.dwTriggerButton         = DIEB_NOTRIGGER;
+      eff.dwTriggerRepeatInterval = 0;
+      eff.cAxes                   = mNumForceFeedbackAxes;
+      eff.rgdwAxes                = mForceFeedbackAxes;
+      eff.rglDirection            = rglDirection;
+      eff.lpEnvelope              = 0;
+      eff.cbTypeSpecificParams    = sizeof(DICONSTANTFORCE);
+      eff.lpvTypeSpecificParams   = &cf;
+      eff.dwStartDelay            = 0;
+
+      // Create the prepared effect
+      if ( FAILED( result = mDevice->CreateEffect( GUID_ConstantForce, &eff, &mForceFeedbackEffect, NULL ) ) )
+      {
+	      Con::errorf( "DInputDevice::rumbleJoystick - %s does not support force feedback.\n", mName );
+	      return;
+      }
+      else
+      {
+	      Con::printf( "DInputDevice::rumbleJoystick - %s supports force feedback.\n", mName );
+      }
+   }
+
+   // Clamp the input floats to [0 - 1]
+   x = max(0, min(1, x));
+   y = max(0, min(1, y));
+
+   if ( 1 == mNumForceFeedbackAxes )
+   {
+      cf.lMagnitude = (DWORD)( x * DI_FFNOMINALMAX );
+   }
+   else
+   {
+      rglDirection[0] = (DWORD)( x * DI_FFNOMINALMAX );
+      rglDirection[1] = (DWORD)( y * DI_FFNOMINALMAX );
+      cf.lMagnitude = (DWORD)sqrt( (double)(x * x * DI_FFNOMINALMAX * DI_FFNOMINALMAX + y * y * DI_FFNOMINALMAX * DI_FFNOMINALMAX) );
+   }
+
+   DIEFFECT eff;
+   ZeroMemory( &eff, sizeof(eff) );
+   eff.dwSize                  = sizeof(DIEFFECT);
+   eff.dwFlags                 = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
+   eff.dwDuration              = INFINITE;
+   eff.dwSamplePeriod          = 0;
+   eff.dwGain                  = DI_FFNOMINALMAX;
+   eff.dwTriggerButton         = DIEB_NOTRIGGER;
+   eff.dwTriggerRepeatInterval = 0;
+   eff.cAxes                   = mNumForceFeedbackAxes;
+   eff.rglDirection            = rglDirection;
+   eff.lpEnvelope              = 0;
+   eff.cbTypeSpecificParams    = sizeof(DICONSTANTFORCE);
+   eff.lpvTypeSpecificParams   = &cf;
+   eff.dwStartDelay            = 0;
+
+   if ( FAILED( result = mForceFeedbackEffect->SetParameters( &eff, DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_START ) ) )
+   {
+      const char* errorString = NULL;
+      switch ( result )
+      {
+         case DIERR_INPUTLOST:
+            errorString = "DIERR_INPUTLOST";
+            break;
+
+         case DIERR_INVALIDPARAM:
+            errorString = "DIERR_INVALIDPARAM";
+            break;
+
+         case DIERR_NOTACQUIRED:
+            errorString = "DIERR_NOTACQUIRED";
+            break;
+
+         default:
+            errorString = "Unknown Error";
+      }
+      Con::errorf( "DInputDevice::rumbleJoystick - %s - Failed to start rumble effect\n", errorString );
+   }
+}
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 //
 //
 // This function translates the DirectInput scan code to the associated
 // This function translates the DirectInput scan code to the associated

+ 9 - 1
engine/source/platformWin32/winDInputDevice.h

@@ -36,9 +36,9 @@
 
 
 //Luma: Adding this for direct input header
 //Luma: Adding this for direct input header
 #define DIRECTINPUT_VERSION  0x0800
 #define DIRECTINPUT_VERSION  0x0800
-
 #include <dinput.h>
 #include <dinput.h>
 
 
+
 class DInputDevice : public InputDevice
 class DInputDevice : public InputDevice
 {
 {
    public:
    public:
@@ -74,6 +74,11 @@ class DInputDevice : public InputDevice
       bool                 mAcquired;
       bool                 mAcquired;
       bool                 mNeedSync;
       bool                 mNeedSync;
 
 
+	  LPDIRECTINPUTEFFECT  mForceFeedbackEffect;   ///< Holds our DirectInput FF Effect
+      DWORD                mNumForceFeedbackAxes;  ///< # axes (we only support 0, 1, or 2
+      DWORD                mForceFeedbackAxes[2];  ///< Force Feedback axes offsets into DIOBJECTFORMAT
+
+
       //--------------------------------------
       //--------------------------------------
       DIDEVICEOBJECTINSTANCE* mObjInstance;
       DIDEVICEOBJECTINSTANCE* mObjInstance;
       DIOBJECTDATAFORMAT*     mObjFormat;
       DIOBJECTDATAFORMAT*     mObjFormat;
@@ -124,6 +129,9 @@ class DInputDevice : public InputDevice
       const char* getName();
       const char* getName();
       const char* getProductName();
       const char* getProductName();
 
 
+	   // Constant Effect Force Feedback
+      void rumble( float x, float y );
+
       // Console interface functions:
       // Console interface functions:
       const char* getJoystickAxesString();
       const char* getJoystickAxesString();
       static bool joystickDetected();
       static bool joystickDetected();

+ 426 - 1
engine/source/platformWin32/winDirectInput.cc

@@ -27,12 +27,16 @@
 #include "platform/event.h"
 #include "platform/event.h"
 #include "console/console.h"
 #include "console/console.h"
 #include "console/consoleTypes.h"
 #include "console/consoleTypes.h"
+#include "input/actionMap.h"
+#include "game/gameInterface.h"
+#include <Xinput.h>
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 // Static class variables:
 // Static class variables:
 bool DInputManager::smKeyboardEnabled = true;
 bool DInputManager::smKeyboardEnabled = true;
 bool DInputManager::smMouseEnabled = false;
 bool DInputManager::smMouseEnabled = false;
 bool DInputManager::smJoystickEnabled = false;
 bool DInputManager::smJoystickEnabled = false;
+bool DInputManager::smXInputEnabled = false;
 
 
 // Type definitions:
 // Type definitions:
 typedef HRESULT (WINAPI* FN_DirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter);
 typedef HRESULT (WINAPI* FN_DirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter);
@@ -44,6 +48,10 @@ DInputManager::DInputManager()
    mDInputLib        = NULL;
    mDInputLib        = NULL;
    mDInputInterface  = NULL;
    mDInputInterface  = NULL;
    mKeyboardActive   = mMouseActive = mJoystickActive = false;
    mKeyboardActive   = mMouseActive = mJoystickActive = false;
+   mXInputActive = true;
+
+   for(S32 i=0; i<4; i++)
+	   mLastDisconnectTime[i] = -1;
 }
 }
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
@@ -52,6 +60,7 @@ void DInputManager::init()
    Con::addVariable( "pref::Input::KeyboardEnabled",  TypeBool, &smKeyboardEnabled );
    Con::addVariable( "pref::Input::KeyboardEnabled",  TypeBool, &smKeyboardEnabled );
    Con::addVariable( "pref::Input::MouseEnabled",     TypeBool, &smMouseEnabled );
    Con::addVariable( "pref::Input::MouseEnabled",     TypeBool, &smMouseEnabled );
    Con::addVariable( "pref::Input::JoystickEnabled",  TypeBool, &smJoystickEnabled );
    Con::addVariable( "pref::Input::JoystickEnabled",  TypeBool, &smJoystickEnabled );
+   Con::addVariable( "pref::Input::XInputEnabled", TypeBool, &smXInputEnabled );
 }
 }
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
@@ -60,6 +69,27 @@ bool DInputManager::enable()
    FN_DirectInputCreate fnDInputCreate;
    FN_DirectInputCreate fnDInputCreate;
 
 
    disable();
    disable();
+
+   mXInputLib = LoadLibrary( dT("xinput9_1_0.dll") );
+
+   if( mXInputLib )
+   {
+	   mfnXInputGetState = (FN_XInputGetState) GetProcAddress( mXInputLib, "XInputGetState" );
+      mfnXInputSetState = (FN_XInputSetState) GetProcAddress( mXInputLib, "XInputSetState" );
+      
+	  if ( mfnXInputGetState && mfnXInputSetState )
+      {
+         mXInputStateReset = true;
+         mXInputDeadZoneOn = true;
+         smXInputEnabled = true;
+      }
+   }
+   else
+   {
+	   Con::warnf("Unable to enable XInput.");
+   }
+
+
    mDInputLib = LoadLibrary( dT("DInput8.dll") );
    mDInputLib = LoadLibrary( dT("DInput8.dll") );
    if ( mDInputLib )
    if ( mDInputLib )
    {
    {
@@ -117,6 +147,20 @@ void DInputManager::disable()
       mDInputLib = NULL;
       mDInputLib = NULL;
    }
    }
 
 
+   if ( mfnXInputGetState )
+   {
+      mXInputStateReset = true;
+      mfnXInputGetState = NULL;
+      mfnXInputSetState = NULL;
+   }
+
+   if ( mXInputLib )
+   {
+      FreeLibrary( mXInputLib );
+      mXInputLib = NULL;
+   }
+
+
    mEnabled = false;
    mEnabled = false;
 }
 }
 
 
@@ -180,6 +224,9 @@ void DInputManager::unacquire( U8 deviceType, U8 deviceID )
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 void DInputManager::process()
 void DInputManager::process()
 {
 {
+	if ( isXInputActive() )
+		processXInput();
+
    DInputDevice* dptr;
    DInputDevice* dptr;
    for ( iterator ptr = begin(); ptr != end(); ptr++ )
    for ( iterator ptr = begin(); ptr != end(); ptr++ )
    {
    {
@@ -194,7 +241,6 @@ void DInputManager::enumerateDevices()
 {
 {
    if ( mDInputInterface )
    if ( mDInputInterface )
    {
    {
-
       DInputDevice::init();
       DInputDevice::init();
       DInputDevice::smDInputInterface = mDInputInterface;
       DInputDevice::smDInputInterface = mDInputInterface;
       mDInputInterface->EnumDevices( DI8DEVTYPE_KEYBOARD, EnumDevicesProc, this, DIEDFL_ATTACHEDONLY );
       mDInputInterface->EnumDevices( DI8DEVTYPE_KEYBOARD, EnumDevicesProc, this, DIEDFL_ATTACHEDONLY );
@@ -424,4 +470,383 @@ const char* DInputManager::getJoystickAxesString( U32 deviceID )
    }
    }
 
 
    return( "" );
    return( "" );
+}
+
+bool DInputManager::enableXInput()
+{
+   // Largely, this series of functions is identical to the Joystick versions,
+   // except that XInput cannot be "activated" or "deactivated". You either have
+   // the DLL or you don't. Beyond that, you have up to four controllers
+   // connected at any given time
+
+   DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
+   if ( !mgr || !mgr->isEnabled() )
+      return( false );
+
+   if ( mgr->isXInputActive() )
+      return( true );
+
+   if ( Input::isActive() )
+      mgr->activateXInput();
+
+   if ( smXInputEnabled )
+   {
+      Con::printf( "XInput enabled." );
+   }
+   else
+   {
+      Con::warnf( "XInput failed to enable!" );
+   }
+
+   return( smXInputEnabled );
+}
+
+//------------------------------------------------------------------------------
+void DInputManager::disableXInput()
+{
+   DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
+   if ( !mgr || !mgr->isEnabled())
+      return;
+
+   mgr->deactivateXInput();
+   Con::printf( "XInput disabled." );
+}
+
+//------------------------------------------------------------------------------
+bool DInputManager::isXInputEnabled()
+{
+   return( smXInputEnabled );
+}
+
+//------------------------------------------------------------------------------
+bool DInputManager::isXInputConnected(int controllerID)
+{
+	return( mXInputStateNew[controllerID].bConnected );
+}
+
+int DInputManager::getXInputState(int controllerID, int property, bool current)
+{
+   int retVal;
+
+   switch(property)
+   {
+#define CHECK_PROP_ANALOG(prop, stateTest) \
+   case prop:        (current) ? retVal = mXInputStateNew[controllerID].state.Gamepad.##stateTest : retVal = mXInputStateOld[controllerID].state.Gamepad.##stateTest; return retVal;
+
+      CHECK_PROP_ANALOG(XI_THUMBLX, sThumbLX)
+      CHECK_PROP_ANALOG(XI_THUMBLY, sThumbLY)
+      CHECK_PROP_ANALOG(XI_THUMBRX, sThumbRX)
+      CHECK_PROP_ANALOG(XI_THUMBRY, sThumbRY)
+      CHECK_PROP_ANALOG(XI_LEFT_TRIGGER, bLeftTrigger)
+      CHECK_PROP_ANALOG(XI_RIGHT_TRIGGER, bRightTrigger)
+
+#undef CHECK_PROP_ANALOG
+
+#define CHECK_PROP_DIGITAL(prop, stateTest) \
+   case prop:           (current) ? retVal = (( mXInputStateNew[controllerID].state.Gamepad.wButtons & stateTest ) != 0 ) : retVal = (( mXInputStateOld[controllerID].state.Gamepad.wButtons & stateTest ) != 0 ); return retVal;
+
+      CHECK_PROP_DIGITAL(SI_UPOV, XINPUT_GAMEPAD_DPAD_UP)
+      CHECK_PROP_DIGITAL(SI_DPOV, XINPUT_GAMEPAD_DPAD_DOWN)
+      CHECK_PROP_DIGITAL(SI_LPOV, XINPUT_GAMEPAD_DPAD_LEFT)
+      CHECK_PROP_DIGITAL(SI_RPOV, XINPUT_GAMEPAD_DPAD_RIGHT)
+      CHECK_PROP_DIGITAL(XI_START, XINPUT_GAMEPAD_START)
+      CHECK_PROP_DIGITAL(XI_BACK, XINPUT_GAMEPAD_BACK)
+      CHECK_PROP_DIGITAL(XI_LEFT_THUMB, XINPUT_GAMEPAD_LEFT_THUMB)
+      CHECK_PROP_DIGITAL(XI_RIGHT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB)
+      CHECK_PROP_DIGITAL(XI_LEFT_SHOULDER, XINPUT_GAMEPAD_LEFT_SHOULDER)
+      CHECK_PROP_DIGITAL(XI_RIGHT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER)
+      CHECK_PROP_DIGITAL(XI_A, XINPUT_GAMEPAD_A)
+      CHECK_PROP_DIGITAL(XI_B, XINPUT_GAMEPAD_B)
+      CHECK_PROP_DIGITAL(XI_X, XINPUT_GAMEPAD_X)
+      CHECK_PROP_DIGITAL(XI_Y, XINPUT_GAMEPAD_Y)
+
+#undef CHECK_PROP_DIGITAL
+   }
+
+   return -1;
+}
+
+//------------------------------------------------------------------------------
+bool DInputManager::activateXInput()
+{
+   if ( !mEnabled || !Input::isActive())
+      return( false );
+
+   mXInputActive = true; //acquire( GamepadDeviceType, SI_ANY );
+
+   return( mXInputActive );
+}
+
+//------------------------------------------------------------------------------
+void DInputManager::deactivateXInput()
+{
+   if ( mEnabled && mXInputActive )
+   {
+      unacquire( GamepadDeviceType, SI_ANY );
+      mXInputActive = false;
+   }
+}
+
+//------------------------------------------------------------------------------
+bool DInputManager::rumble( const char *pDeviceName, float x, float y )
+{
+   // Determine the device
+   U32 deviceType;
+   U32 deviceInst;
+
+   // Find the requested device
+   if ( !ActionMap::getDeviceTypeAndInstance(pDeviceName, deviceType, deviceInst) )
+   {
+      Con::printf("DInputManager::rumble: unknown device: %s", pDeviceName);
+      return false;
+   }
+
+   // clamp (x, y) to the range of [0 ... 1] each
+   x = mClampF(x, 0.f, 1.f);
+   y = mClampF(y, 0.f, 1.f);
+
+   //  Easy path for xinput devices.
+   if(deviceType == GamepadDeviceType)
+   {
+      XINPUT_VIBRATION vibration;
+      vibration.wLeftMotorSpeed = static_cast<int>(x * 65535);
+      vibration.wRightMotorSpeed = static_cast<int>(y * 65535);
+      return ( mfnXInputSetState(deviceInst, &vibration) == ERROR_SUCCESS );
+   }
+
+   switch ( deviceType )
+   {
+   case JoystickDeviceType:
+
+      // Find the device and shake it!
+      DInputDevice* dptr;
+      for ( iterator ptr = begin(); ptr != end(); ptr++ )
+      {
+         dptr = dynamic_cast<DInputDevice*>( *ptr );
+         if ( dptr )
+         {
+            if (deviceType == dptr->getDeviceType() && deviceInst == dptr->getDeviceID())
+            {
+               dptr->rumble(x, y);
+               return true;
+            }
+         }
+      }
+
+      // We should never get here... something's really messed up
+      Con::errorf( "DInputManager::rumbleJoystick - Couldn't find device to rumble! This should never happen!\n" );
+      return false;
+
+   default:
+      Con::printf("DInputManager::rumble - only supports joystick and xinput/gamepad devices");
+      return false;
+   }
+}
+
+void DInputManager::buildXInputEvent( U32 deviceInst, XInputEventType objType, InputObjectInstances objInst, InputActionType action, float fValue )
+{
+InputEvent newEvent;
+
+   newEvent.deviceType = GamepadDeviceType;
+   newEvent.deviceInst = deviceInst;
+   newEvent.objType = objType;
+   newEvent.objInst = objInst;
+   newEvent.action = action;
+   newEvent.fValue = fValue;
+   //we need to find the gameinterface object from here
+
+   Game->postEvent(newEvent);
+   
+}
+
+// The next three functions: fireXInputConnectEvent, fireXInputMoveEvent, and fireXInputButtonEvent
+// determine whether a "delta" has occurred between the last captured controller state and the
+// currently captured controller state and only if so, do we fire an event. The shortcutter
+// "mXInputStateReset" is the exception and is true whenever DirectInput gets reset (because 
+// the user ALT-TABBED away, for example). That means that after every context switch,
+// you will get a full set of updates on the "true" state of the controller.
+
+inline void DInputManager::fireXInputConnectEvent( int controllerID, bool condition, bool connected )
+{
+   if ( mXInputStateReset || condition )
+   {
+      buildXInputEvent( controllerID, XI_BUTTON, XI_CONNECT, connected ? XI_MAKE : XI_BREAK, 0);
+   }
+}
+
+inline void DInputManager::fireXInputMoveEvent( int controllerID, bool condition, InputObjectInstances objInst, float fValue )
+{
+   if ( mXInputStateReset || condition )
+   {
+	   /*
+	  //Uncomment if you want real-time Input displayed to the console
+
+	  const char *objName;
+      switch (objInst)
+      {
+      case XI_THUMBLX:       objName = "THUMBLX"; break;
+      case XI_THUMBLY:       objName = "THUMBLY"; break;
+      case XI_THUMBRX:       objName = "THUMBRX"; break;
+      case XI_THUMBRY:       objName = "THUMBRY"; break;
+      case XI_LEFT_TRIGGER:  objName = "LEFT_TRIGGER"; break;
+      case XI_RIGHT_TRIGGER: objName = "RIGHT_TRIGGER"; break;
+      default:               objName = "UNKNOWN"; break;
+      }
+	  Con::printf("%s  %.1f", objName, fValue);
+	  */
+	   buildXInputEvent( controllerID, XI_AXIS, objInst, XI_MOVE, fValue );
+   }
+}
+
+inline void DInputManager::fireXInputButtonEvent( int controllerID, bool forceFire, int button, InputObjectInstances objInst )
+{
+   if ( mXInputStateReset || forceFire || ((mXInputStateNew[controllerID].state.Gamepad.wButtons & button) != (mXInputStateOld[controllerID].state.Gamepad.wButtons & button)) )
+   {
+      InputActionType action = ((mXInputStateNew[controllerID].state.Gamepad.wButtons & button) != 0) ? XI_MAKE : XI_BREAK;
+
+	  /*
+	  //Uncomment if you want real-time Input displayed to the console
+
+	  char *objName;
+	  objName = "";
+	  switch (objInst)
+      {      
+      
+      case XI_DPAD_UP:        objName = "DPAD_UP"; break;
+      case XI_DPAD_DOWN:      objName = "DPAD_DOWN"; break;
+      case XI_DPAD_LEFT:      objName = "DPAD_LEFT"; break;
+      case XI_DPAD_RIGHT:     objName = "DPAD_RIGHT"; break;
+      
+      case XI_START:          objName = "START"; break;
+      case XI_BACK:           objName = "BACK"; break;
+      case XI_LEFT_THUMB:     objName = "LEFT_THUMB"; break;
+      case XI_RIGHT_THUMB:    objName = "RIGHT_THUMB"; break;
+      case XI_LEFT_SHOULDER:  objName = "LEFT_SHOULDER"; break;
+      case XI_RIGHT_SHOULDER: objName = "RIGHT_SHOULDER"; break;
+      case XI_A:              objName = "A"; break;
+      case XI_B:              objName = "B"; break;
+      case XI_X:              objName = "X"; break;
+      case XI_Y:              objName = "Y"; break;
+	  default:                Con::printf("%u", objInst); break;
+      }
+	  Con::printf("%s", objName);
+	  */
+
+      buildXInputEvent( controllerID, XI_BUTTON, objInst, action, ( action == XI_MAKE ? 1 : 0 ) );
+   }
+}
+
+void DInputManager::processXInput( void )
+{
+   const U32 curTime = Platform::getRealMilliseconds();
+
+   // We only want to check one disconnected device per frame.
+   bool foundDisconnected = false;
+
+   if ( mfnXInputGetState )
+   {
+      for ( int i=0; i<4; i++ )
+      {
+         // Calling XInputGetState on a disconnected controller takes a fair 
+         // amount of time (probably because it tries to locate it), so we 
+         // add a delay - only check every 250ms or so.
+         if(mLastDisconnectTime[i] != -1)
+         {
+            // If it's not -1, then it was disconnected list time we checked.
+            // So skip until it's time.
+            if((curTime-mLastDisconnectTime[i]) < csmDisconnectedSkipDelay)
+            {
+               continue;
+            }
+
+            // If we already checked a disconnected controller, don't try any
+            // further potentially disconnected devices.
+            if(foundDisconnected)
+            {
+               // If we're skipping this, defer it out by the skip delay
+               // so we don't get clumped checks.
+               mLastDisconnectTime[i] += csmDisconnectedSkipDelay;
+               continue;
+            }
+         }
+
+         mXInputStateOld[i] = mXInputStateNew[i];
+         mXInputStateNew[i].bConnected = ( mfnXInputGetState( i, &mXInputStateNew[i].state ) == ERROR_SUCCESS );
+
+         // Update the last connected time.
+         if(mXInputStateNew[i].bConnected)
+            mLastDisconnectTime[i] = -1;
+         else
+         {
+            foundDisconnected = true;
+            mLastDisconnectTime[i] = curTime;
+         }
+
+         // trim the controller's thumbsticks to zero if they are within the deadzone
+         if( mXInputDeadZoneOn )
+         {
+            // Zero value if thumbsticks are within the dead zone 
+            if( (mXInputStateNew[i].state.Gamepad.sThumbLX < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbLX > -XINPUT_DEADZONE) && 
+                (mXInputStateNew[i].state.Gamepad.sThumbLY < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbLY > -XINPUT_DEADZONE) ) 
+            {
+               mXInputStateNew[i].state.Gamepad.sThumbLX = 0;
+               mXInputStateNew[i].state.Gamepad.sThumbLY = 0;
+            }
+
+            if( (mXInputStateNew[i].state.Gamepad.sThumbRX < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbRX > -XINPUT_DEADZONE) && 
+                (mXInputStateNew[i].state.Gamepad.sThumbRY < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbRY > -XINPUT_DEADZONE) ) 
+            {
+               mXInputStateNew[i].state.Gamepad.sThumbRX = 0;
+               mXInputStateNew[i].state.Gamepad.sThumbRY = 0;
+            }
+         }
+
+         // this controller was connected or disconnected
+         bool bJustConnected = ( ( mXInputStateOld[i].bConnected != mXInputStateNew[i].bConnected ) && ( mXInputStateNew[i].bConnected ) );
+         fireXInputConnectEvent( i, (mXInputStateOld[i].bConnected != mXInputStateNew[i].bConnected), mXInputStateNew[i].bConnected );
+
+         // If this controller is disconnected, stop reporting events for it
+         if ( !mXInputStateNew[i].bConnected )
+            continue;
+
+         // == LEFT THUMBSTICK ==
+         fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbLX != mXInputStateOld[i].state.Gamepad.sThumbLX), XI_THUMBLX, (mXInputStateNew[i].state.Gamepad.sThumbLX / 32768.0f) );
+         fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbLY != mXInputStateOld[i].state.Gamepad.sThumbLY), XI_THUMBLY, (mXInputStateNew[i].state.Gamepad.sThumbLY / 32768.0f) );
+
+         // == RIGHT THUMBSTICK ==
+         fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbRX != mXInputStateOld[i].state.Gamepad.sThumbRX), XI_THUMBRX, (mXInputStateNew[i].state.Gamepad.sThumbRX / 32768.0f) );
+         fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbRY != mXInputStateOld[i].state.Gamepad.sThumbRY), XI_THUMBRY, (mXInputStateNew[i].state.Gamepad.sThumbRY / 32768.0f) );
+
+         // == LEFT & RIGHT REAR TRIGGERS ==
+         fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.bLeftTrigger != mXInputStateOld[i].state.Gamepad.bLeftTrigger), XI_LEFT_TRIGGER, (mXInputStateNew[i].state.Gamepad.bLeftTrigger / 255.0f) );
+         fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.bRightTrigger != mXInputStateOld[i].state.Gamepad.bRightTrigger), XI_RIGHT_TRIGGER, (mXInputStateNew[i].state.Gamepad.bRightTrigger / 255.0f) );
+
+         // == BUTTONS: DPAD ==
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_UP, SI_UPOV );
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_DOWN, SI_DPOV );
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_LEFT, SI_LPOV );
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_RIGHT, SI_RPOV );
+
+         // == BUTTONS: START & BACK ==
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_START, XI_START );
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_BACK, XI_BACK );
+
+         // == BUTTONS: LEFT AND RIGHT THUMBSTICK ==
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_LEFT_THUMB, XI_LEFT_THUMB );
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_RIGHT_THUMB, XI_RIGHT_THUMB );
+
+         // == BUTTONS: LEFT AND RIGHT SHOULDERS (formerly WHITE and BLACK on Xbox 1) ==
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_LEFT_SHOULDER, XI_LEFT_SHOULDER );
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_RIGHT_SHOULDER, XI_RIGHT_SHOULDER );
+
+         // == BUTTONS: A, B, X, and Y ==
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_A, XI_A );
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_B, XI_B );
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_X, XI_X );
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_Y, XI_Y );
+      }
+
+      if ( mXInputStateReset ) 
+         mXInputStateReset = false;
+   }
 }
 }

+ 50 - 0
engine/source/platformWin32/winDirectInput.h

@@ -35,6 +35,21 @@
 
 
 #define DIRECTINPUT_VERSION 0x0800
 #define DIRECTINPUT_VERSION 0x0800
 #include <dinput.h>
 #include <dinput.h>
+#include <Xinput.h>
+
+// XInput related definitions
+typedef DWORD (WINAPI* FN_XInputGetState)(DWORD dwUserIndex, XINPUT_STATE* pState);
+typedef DWORD (WINAPI* FN_XInputSetState)(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration);
+#define XINPUT_MAX_CONTROLLERS 4  // XInput handles up to 4 controllers 
+#define XINPUT_DEADZONE  ( 0.24f * FLOAT(0x7FFF) )  // Default to 24% of the +/- 32767 range.   This is a reasonable default value but can be altered if needed.
+
+struct XINPUT_CONTROLLER_STATE
+{
+    XINPUT_STATE    state;
+    bool            bConnected;
+};
+
+//---------------------------------------------------------------------------
 
 
 struct InputEvent;
 struct InputEvent;
 
 
@@ -44,16 +59,32 @@ class DInputManager : public InputManager
    private:
    private:
       typedef SimGroup Parent;
       typedef SimGroup Parent;
 
 
+	    // XInput state
+      HMODULE                 mXInputLib;
+      FN_XInputGetState       mfnXInputGetState;
+      FN_XInputSetState       mfnXInputSetState;
+      XINPUT_CONTROLLER_STATE mXInputStateOld[XINPUT_MAX_CONTROLLERS];
+      XINPUT_CONTROLLER_STATE mXInputStateNew[XINPUT_MAX_CONTROLLERS];
+      U32                     mLastDisconnectTime[XINPUT_MAX_CONTROLLERS];
+      bool                    mXInputStateReset;
+      bool                    mXInputDeadZoneOn;
+
+      /// Number of milliseconds to skip checking an xinput device if it was
+      /// disconnected on last check.
+      const static U32 csmDisconnectedSkipDelay = 250;
+
       HMODULE        mDInputLib;
       HMODULE        mDInputLib;
       LPDIRECTINPUT8 mDInputInterface;
       LPDIRECTINPUT8 mDInputInterface;
 
 
       static bool smKeyboardEnabled;
       static bool smKeyboardEnabled;
       static bool smMouseEnabled;
       static bool smMouseEnabled;
       static bool smJoystickEnabled;
       static bool smJoystickEnabled;
+	  static bool smXInputEnabled;
 
 
       bool mKeyboardActive;
       bool mKeyboardActive;
       bool mMouseActive;
       bool mMouseActive;
       bool mJoystickActive;
       bool mJoystickActive;
+	  bool mXInputActive;
 
 
       void  enumerateDevices();
       void  enumerateDevices();
 
 
@@ -62,6 +93,13 @@ class DInputManager : public InputManager
       bool acquire( U8 deviceType, U8 deviceID );
       bool acquire( U8 deviceType, U8 deviceID );
       void unacquire( U8 deviceType, U8 deviceID );
       void unacquire( U8 deviceType, U8 deviceID );
 
 
+	  // XInput worker functions
+      void buildXInputEvent( U32 deviceInst, XInputEventType objType, InputObjectInstances objInst, InputActionType action, float fValue );
+      void fireXInputConnectEvent( int controllerID, bool condition, bool connected );
+      void fireXInputMoveEvent( int controllerID, bool condition, InputObjectInstances objInst, float fValue );
+      void fireXInputButtonEvent( int controllerID, bool forceFire, int button, InputObjectInstances objInst );
+      void processXInput();
+
    public:
    public:
       DInputManager();
       DInputManager();
 
 
@@ -98,8 +136,20 @@ class DInputManager : public InputManager
       void deactivateJoystick();
       void deactivateJoystick();
       bool isJoystickActive()       { return( mJoystickActive ); }
       bool isJoystickActive()       { return( mJoystickActive ); }
 
 
+	  static bool enableXInput();
+      static void disableXInput();
+      static bool isXInputEnabled();
+      bool activateXInput();
+      void deactivateXInput();
+      bool isXInputActive()         { return( mXInputActive ); }
+      void resetXInput()            { mXInputStateReset = true; }
+      bool isXInputConnected(int controllerID);
+      int getXInputState(int controllerID, int property, bool current);
+
       // Console interface:
       // Console interface:
       const char* getJoystickAxesString( U32 deviceID );
       const char* getJoystickAxesString( U32 deviceID );
+
+	  bool rumble( const char *pDeviceName, float x, float y );
 };
 };
 
 
 #endif  // _H_WINDIRECTINPUT_
 #endif  // _H_WINDIRECTINPUT_

+ 4 - 0
engine/source/platformWin32/winInput.cc

@@ -495,6 +495,10 @@ void Input::echoInputState()
 		Con::printf( "- Joystick is %sabled and %sactive.",
 		Con::printf( "- Joystick is %sabled and %sactive.",
             mgr->isJoystickEnabled() ? "en" : "dis",
             mgr->isJoystickEnabled() ? "en" : "dis",
             mgr->isJoystickActive() ? "" : "in" );
             mgr->isJoystickActive() ? "" : "in" );
+
+		Con::printf( "- Xinput is %sabled and %sactive.",
+			mgr->isXInputEnabled() ? "en" : "dis",
+			mgr->isXInputActive() ? "" : "in" );
    }
    }
    else
    else
     {
     {