Controller drivers

Architecture Overview

_images/arch_overview.png

Controller implementations are broken down into several pieces:

  • Interfaces: (lavender boxes in diagram) top-level class that can be used to instantiate Interface Drivers. These have a unique name and implement Unreal’s IInputDeviceModule and ControllerHawk’s IGameControllerModuleInterface

  • Interface Drivers: (blue boxes in diagram) collect together Devices (specific controllers) and can generally automatically check if controllers are added or removed (enumeration). These implement Unreal’s IInputDevice interface, and with that are responsible for driving the lifecycle of all of the controllers communicating over this specific interface.

  • Devices: (red boxes in diagram) implements the API-specific logic on an OS for how to communicate to the controller (i.e. sending and receiving raw data) and represent a specific controller instance. These implement ControllerHawk’s IGameControllerInterface. Each device has a Backend installed.

  • Backends (silver boxes in diagram): specific logic and/or configuration of interacting with a specific kind of controller. These are matched against the controller’s Product and Vendor ID’s and know what data to send and receive through the owning Device and how to interpet that data to trigger input events back to Unreal. These implement ControllerHawk’s IGameControllerBackend interface (although in practice they implement an API-specific backend interface such as IGameControllerBackendHID)

Interfaces and Backends are registered with the IGameControllerModuleManager class instance and are then automatically enumerated and instantiated at runtime when the ControllerHawkRuntime module starts up. Interfaces are registerd with the modular feature system (IModularFeatures::Get().RegisterModularFeature(…)) for the Input Device Module (IInputDeviceModule::GetModularFeatureName()).

Runtime Flow

_images/startup_sequence.png

Backends and Interfaces are registered statically at DLL load time when in Editor and executable-load time when in a packaged game. Once ControllerHawkRuntime is started, all of the intefaces are registered with the IInputDeviceModule system. Once that system loads it will create Interface Driver instances (i.e. the IInputDevice instances).

_images/tick_sequence.png

Once the engine begins ticking, each Interface Driver will in practice periodically asynchronously enumerate devices that are available. If those devices have known Product and Vendor ID’s, then they are instantiated as a Device matched to that Backend. Those Devices then are polled and sent commands.

Human Interface Devices

WIP

DirectInput Devices

Note

Windows only!

DirectInput refers to a Windows API for interfacing with controllers. It has been replaced by XInput, but is still relevant since some controllers (such as PlayStation or Nintendo controllers) do not support XInput and instead may show up as a DirectInput device. While ControllerHawk does provide HID drivers which override the DirectInput drivers for Sony DS5 and Nintendo Switch Pro Controllers, we will cover how they work here since they are much simpler to implement.

Warning

In my experience DirectInput devices (that are not already acquired by XInput) do not support basic functions like Rumble or setting the player light slot indicator (player 1, player 2, etc.)

WIP

Accessing Controller Implementations

The IGameControllerInterface implementation of a controller can be accessed when you are inside of a FGameControllerScope scope. These are only valid while button and analog events are processed for the target controller. See this diagram for when the appropriate scope is active.

_images/flow_hid.png