Skip to content

Physical Controller Backend Plugin

Samuel Grossman edited this page Mar 29, 2026 · 1 revision

This document describes how to implement a physical controller backend plugin for Xidi. A plugin of this type allows the mechanism by which Xidi communicates with physical controllers to be replaced. Xidi's built-in mechanism uses the official, documented XInput API.

Interfaces

A physical controller backend plugin implements the IPhysicalControllerBackend interface, described in detail in the subsections that follow.

IPhysicalControllerBackend

The IPhysicalControllerBackend interface type is defined in the "PhysicalControllerBackend.h" header file. The types referenced in the declaration below are themselves defined in the "PhysicalControllerTypes.h" header file and are included in the code excerpt below for the sake of convenience. For more details on these types, refer to the Types section, below.

namespace Xidi
{
  namespace Controller
  {
    struct SPhysicalCapabilities;
    struct SPhysicalState;
    struct SForceFeedbackState;
  }

  using SPhysicalControllerCapabilities = ::Xidi::Controller::SPhysicalCapabilities;
  using SPhysicalControllerState = ::Xidi::Controller::SPhysicalState;
  using SPhysicalControllerVibration = ::Xidi::Controller::SForceFeedbackState;
  using TPhysicalControllerIndex = uint16_t;

  class IPhysicalControllerBackend : public IPlugin
  {
  public:
    virtual TPhysicalControllerIndex MaxPhysicalControllerCount(void) = 0;
    virtual bool SupportsControllerByGuidAndPath(const wchar_t* guidAndPath) = 0;
    virtual SPhysicalControllerCapabilities GetCapabilities(void) = 0;
    virtual SPhysicalControllerState ReadInputState(TPhysicalControllerIndex physicalControllerIndex) = 0;
    virtual bool WriteForceFeedbackState(TPhysicalControllerIndex physicalControllerIndex, SPhysicalControllerVibration vibrationState) = 0;
  };

MaxPhysicalControllerCount

Retrieves and returns the maximum number of physical controllers supported by this physical controller backend plugin.

Return Value

Maximum number of physical controllers supported by this physical controller backend plugin.

Notes

The maximum number of physical controllers with which Xidi will communicate depends on both the return value of this function and Xidi's own internal limit. Refer to the constant kVirtualControllerMaxCount in the header "VirtualControllerTypes.h" to see this internal limit.

Returning 0 from this function effectively disables Xidi.

Xidi's internal XInput plugin returns the value 4, based on XInput's official documentation imposing a limit of 4 physical XInput controllers.

SupportsControllerByGuidAndPath

Determines if this physical controller backend plugin supports a specific physical controller, identified by its GUID and path string.

Parameters
guidAndPath

GUID and path string identifying the controller. Xidi obtains this string by calling the GetProperty method on the DirectInputDevice object associated with the controller and asking for the DIPROP_GUIDANDPATH property.

Return Value

true if this physical controller backend supports the identified controller, false otherwise.

Notes

If a plugin returns true for a particular controller, Xidi will route all communication with it via the plugin by mapping it to a virtual controller and preventing the application from communicating with it directly via DirectInput. Otherwise, Xidi will enumerate the controller normally as a DirectInput controller and not intercept any of the communication with it.

Strings passed as parameters to this method typically contain the controller's vendor ID (VID) and product ID (PID). Plugins can parse these fields and use them to determine if they can support communication with the physical controller. Alternatively, if it is known that a plugin supports communication with all types of physical controllers, plugins can simply return true unconditionally.

Some examples of GUID and property strings, retrieved from real devices during Xidi testing:

  • \\?\hid#vid_045e&pid_0b12&ig_00#9&2e649ca1&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
  • \\?\hid#{00001812-0000-1000-8000-00805f9b34fb}&dev&vid_045e&pid_0b13&rev_0513&5cba3788986a&ig_00#c&2eaed628&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
  • \\?\hid#vid_046d&pid_c218#6&1f24a0f&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}

Xidi's internal XInput plugin checks each controller for whether or not it supports XInput by searching through these strings for the "ig_" field, which is a documented way of identifyingi this support. If the string contains this field, the internal XInput plugin returns true, otherwise it returns false.

GetCapabilities

Identifies which components of a physical controller this physical controller backend supports.

Return Value

Filled-in SPhysicalCapabilities structure indicating which components of a physical controller this physical controller backend supports.

Notes

Xidi uses this method to determine if a physical controller backend plugin is compatible with the mapper that the user has configured for a virtual controller. If a mapper reads from part of the physical controller that this plugin does not support, then Xidi considers the mapper incompatible with this plugin.

Several pre-filled constants are available to assist in easy construction of structures of this type. Refer to SPhysicalCapabilities for more information.

This method is intended for identifying physical controller capabilities independently of the actual physical device that is plugged in. For example, Xidi's built-in XInput plugin identifies all standard XInput controller components as being supported, but not the Guide or Share buttons, which are not exposed as part of the official documented XInput API. Thus, Xidi's built-in XInput controller uses this method to identify the limitations of the XInput API itself, which is independent of the actual physical controller device.

ReadInputState

Reads the current, instantaneous state of a physical controller device identified by index.

Parameters
physicalControllerIndex

Zero-based index identifying the physical controller for which Xidi is requesting instantaneous state.

Return Value

Filled-in SPhysicalState structure indicating the current instantaneous state of the requested physical controller.

Notes

Xidi invokes this method very frequently as part of a controller state polling loop. Plugin authors should therefore ensure it returns as quickly as possible.

Xidi's built-in XInput plugin implements this method by invoking the XInput function XInputGetState.

WriteForceFeedbackState

Writes a new, instantaneous state of force feedback actuators to the physical controller device identified by index.

Parameters
physicalControllerIndex

Zero-based index identifying the physical controller to which Xidi is attempting to write a new force feedback state.

vibrationState

Filled-in SForceFeedbackState structure indicating the values to write to each force feedback actuator.

Return Value

true if setting the new force feedback state was successful, false otherwise.

Notes

Xidi's built-in XInput plugin implements this method by invoking the XInput function XInputSetState.

Types

This subsection documents the various supporting type definitions for physical controller backend plugins.

Xidi was originally built for XInput controllers and hence internally makes heavy use of concepts from that API. As a result, many of the types and enumerators use names that correspond to the layout of an Xbox-type controller. Plugin authors intending to support other controller types will need to determine how to map the components of those controllers to the Xbox-style concepts that Xidi uses. For example, a PlayStation-style controller might have its physical circle button mapped to Xidi's B button, its L2 and R2 analog triggers to Xidi's LT and RT analog triggers, and its force feedback motors mapped to Xidi's left and right motors.

Enumerations

All of these enumerations are defined in the "PhysicalControllerTypes.h" header file. More detailed documentation on the meanings of each enumerator is available in that file. The Count enumerator in all cases is used as a sentinel value indicating the number of enumerators and is not intended as a valid enumerator value.

EPhysicalDeviceStatus

Enumerates possible statuses for individual physical controllers.

namespace Xidi
{
  namespace Controller
  {
    enum class EPhysicalDeviceStatus : uint8_t
    {
      Ok,
      NotConnected,
      Error,
      // There may be other enumerators not shown in this summary excerpt.
      Count
    };
  }
}

EPhysicalStick

Enumerates the analog stick axes that may be present on a physical controller device. Here, "X" means horizontal (left and right) and "Y" means vertical (forwards and backwards).

namespace Xidi
{
  namespace Controller
  {
    enum class EPhysicalStick : uint8_t
    {
      LeftX,
      LeftY,
      RightX,
      RightY,
      Count
    };
  }
}

EPhysicalTrigger

Enumerates the triggers that may be present on a physical controller device.

namespace Xidi
{
  namespace Controller
  {
    enum class EPhysicalTrigger : uint8_t
    {
      LT,
      RT,
      Count
    };
  }
}

EPhysicalButton

Enumerates the digital buttons that may be present on a physical controller device.

namespace Xidi
{
  namespace Controller
  {
    enum class EPhysicalButton : uint8_t
    {
      DpadUp,
      DpadDown,
      DpadLeft,
      DpadRight,
      Start,
      Back,
      LS,
      RS,
      LB,
      RB,
      Guide,
      Share,
      A,
      B,
      X,
      Y,
      Count
    };
  }
}

EForceFeedbackActuator

Enumerates the force feedback actuators that may be present on a physical controller device.

namespace Xidi
{
  namespace Controller
  {
    enum class EForceFeedbackActuator : uint8_t
    {
      LeftMotor,
      RightMotor,
      LeftImpulseTrigger,
      RightImpulseTrigger,
      Count
    };
  }
}

Structures

All of these structures are defined in the "PhysicalControllerTypes.h" header file. More detailed documentation on the meanings of each field is available in that file.

SPhysicalCapabilities

Represents the capabilities of a physical controller.

Each field in this structure is a bitset, whereby a '1' bit indicates that the corresponding component is supported by this plugin and a '0' bit indicates that the corresponding component is not supported by this plugin. Mapping from bit position to controller component is based on the corresponding enumerations. For example, determining if the A button is supported can be accomplished by looking at bit position EPhysicalButton::A in the button field.

namespace Xidi
{
  namespace Controller
  {
    struct SPhysicalCapabilities
    {
      std::bitset<static_cast<int>(EPhysicalStick::Count)> stick;
      std::bitset<static_cast<int>(EPhysicalTrigger::Count)> trigger;
      std::bitset<static_cast<int>(EPhysicalButton::Count)> button;
      std::bitset<static_cast<int>(EForceFeedbackActuator::Count)> forceFeedbackActuator;
    }
  }

  using SPhysicalControllerCapabilities = ::Xidi::Controller::SPhysicalCapabilities;
}

As a convenience for filling in structures of this type, the "PhysicalControllerTypes.h" header file makes available some pre-filled contants for the individual fields of this structure, as follows.

  • kPhysicalCapabilitiesAllAnalogSticks: Can be assigned to the stick field to indicate that all analog sticks are supported.
  • kPhysicalCapabilitiesAllAnalogTriggers: Can be assigned to the trigger field to indicate that all analog triggers are supported.
  • kPhysicalCapabilitiesStandardXInputButtons: Can be assigned to the button field to indicate that the standard set of XInput buttons are supported. The "standard set" includes all buttons except for Guide and Share.
  • kPhysicalCapabilitiesAllButtons: Can be assigned to the button field to indicate that all buttons are supported, including Guide and Share.
  • kPhysicalCapabilitiesStandardXInputForceFeedbackActuators: Can be assigned to the forceFeedbackActuator field to indicate that the standard set of XInput force feedback actuators are supported. The "standard set" includes the left and right motors only.
  • kPhysicalCapabilitiesAllForceFeedbackActuators: Can be assigned to the forceFeedbackActuator field to indicate that all force feedback actuators are supported, including impulse triggers.

SPhysicalState

Represents the current instantaneous state of a physical controller.

In addition to the status of the device, one field exists for each component type. Each such field is a fixed-size array of either numbers or bits, whereby controller component is inferred by position in the array based on corresponding enumerator value.

namespace Xidi
{
  namespace Controller
  {
    struct SPhysicalState
    {
      EPhysicalDeviceStatus deviceStatus;
      std::array<int16_t, static_cast<int>(EPhysicalStick::Count)> stick;
      std::array<uint8_t, static_cast<int>(EPhysicalTrigger::Count)> trigger;
      std::bitset<static_cast<int>(EPhysicalButton::Count)> button;
    }
  }

  using SPhysicalControllerState = ::Xidi::Controller::SPhysicalState;
}

SForceFeedbackState

Represents the desired instantaneous state of the force feedback actuators of a physical controller. Each field holds the desired vibration intensity for one of the physical controller force feedback actuators, as indicated by the field's name.

namespace Xidi
{
  namespace Controller
  {
    using TForceFeedbackActuatorValue = uint16_t;

    struct SForceFeedbackState
    {
      TForceFeedbackActuatorValue leftMotor;
      TForceFeedbackActuatorValue rightMotor;
      TForceFeedbackActuatorValue leftImpulseTrigger;
      TForceFeedbackActuatorValue rightImpulseTrigger;
    };
  }

  using SPhysicalControllerVibration = ::Xidi::Controller::SForceFeedbackState;
}

Clone this wiki locally