DirectInput Controller Polling Across DirectX Versions

DirectInput was Microsoft's dedicated API designed to provide low-latency access to input devices, evolving from a basic wrapper around legacy Windows APIs into a robust COM-based subsystem. Throughout its development across various DirectX releases, DirectInput fundamentally altered how games acquired state data from joysticks and controllers, transitioning between synchronous polling loops, interrupt-driven buffered events, and explicit hardware interrogation.

DirectX 1.0 to 3.0: The Win32 Foundations

In DirectX 1.0 and 2.0, DirectInput was largely an abstraction layer over existing Win32 multimedia APIs, such as joyGetPos and joyGetPosEx. Joystick polling relied on the operating system's standard message pump and driver timers, which often introduced measurable latency and jitter unsuitable for fast-paced gaming.

With DirectX 3.0, Microsoft completely redesigned DirectInput into a Component Object Model (COM) interface (IDirectInput and IDirectInputDevice). This version bypassed the Windows message queue to communicate directly with hardware ring-0 drivers. For joysticks, polling was achieved primarily through immediate mode: games directly queried the device for instantaneous state using IDirectInputDevice::GetDeviceState, passing standard data structures to receive current axis positions and button presses.

DirectX 5.0: Explicit Polling and Force Feedback

DirectX 5.0 represented one of the most critical updates to DirectInput with the introduction of the IDirectInputDevice2 interface and support for force-feedback peripherals. This update formally codified the distinction between devices that automatically push updates via hardware interrupts and devices that require active interrogation.

DirectX 5.0 introduced the explicit IDirectInputDevice2::Poll method. Because complex controllers and force-feedback joysticks often operated over legacy game ports, USB, or proprietary serial protocols, hardware could not always generate interrupts on state changes. Developers were required to call Poll() before querying state to instruct the device driver to update its internal buffers. DirectX 5.0 also defined two primary data acquisition models:

  • Immediate Mode (State Polling): Developers called Poll(), followed by GetDeviceState(), returning an instantaneous snapshot of the joystick axes, hats, and buttons into a DIJOYSTATE structure.
  • Buffered Mode (Event Tracking): Games set up event notifications using standard Win32 synchronization objects via SetEventNotification. When the controller state changed, an event signaled the game thread to retrieve queued historical data via GetDeviceData(), preventing missed inputs between rendering frames.

DirectX 5.0 also implemented cooperative levels through SetCooperativeLevel, allowing games to choose between DISCL_EXCLUSIVE and DISCL_NONEXCLUSIVE, as well as DISCL_FOREGROUND and DISCL_BACKGROUND. These flags determined whether a backgrounded application would continue receiving polled joystick states or yield access to the operating system.

DirectX 7.0 and 8.0: DirectInput8 and Action Mapping

DirectX 7.0 expanded the data structures to DIJOYSTATE2, accommodating modern controllers with up to eight axes, multiple directional hats, and up to 128 buttons.

DirectX 8.0 consolidated the API into DirectInput8 (IDirectInput8 and IDirectInputDevice8), which became the final major architectural revision. In DirectInput8, polling remained central:

  1. Mandatory Polling Verification: Drivers declared whether they required active polling via capabilities flags (DIDCT_POLLEDDATAFORCE or DIDCT_POLLEDDEVICE). If set, calling IDirectInputDevice8::Poll() was strictly mandatory before calling GetDeviceState() or reading from GetDeviceData(). If a device did not require explicit polling, calling Poll() simply returned DI_NOEFFECT.
  2. Action Mapping: DirectInput8 introduced Action Mapping (IDirectInput8::ConfigureDevices), which allowed developers to map in-game actions (e.g., "Fire", "Accelerate") directly to input types rather than managing physical axis indices. While Action Mapping abstracted input binding, the underlying data retrieval still operated via the established polled or buffered pipelines.

The Shift Toward XInput

Following DirectX 8.1, Microsoft froze development on DirectInput, steering developers toward XInput alongside the launch of the Xbox 360 controller for Windows. XInput discarded the complex COM configuration, cooperative levels, and buffered event systems of DirectInput in favor of a streamlined, synchronous polling function: XInputGetState.

While DirectInput handled arbitrary joystick hardware, multi-axis throttles, and flight pedals via configurable polling intervals, XInput limited developers to a rigid four-controller model. Despite this shift, DirectInput's hybrid model—allowing developers to choose between frame-bound GetDeviceState polling and high-frequency GetDeviceData buffering—remains the baseline standard for legacy flight simulation and racing peripheral support in Windows.