Skip to content

The Ultimate Cross-Platform DualSense and DualShock lib API. Pure C++ • Zero Dependencies • Engine Agnostic. Customizable framework tested and validated on Unreal Engine, O3DE and Godot.

License

Notifications You must be signed in to change notification settings

rafaelvaloto/Dualsense-Multiplatform

Repository files navigation

🎮 Dualsense-Multiplatform

The Ultimate Cross-Platform DualSense & DualShock API

Pure C++ • Zero Dependencies • Engine Agnostic

Build Status CodeQL License: MIT


Report Bug · Suggest a Feature · Documentation · Security Policy

Core Tech Stack
C++20 CMake

Supported Platforms
Windows Linux macOS PlayStation

Microcontrollers & SBCs
Raspberry Pi ESP32 Pico W

Game Engine Ready
Unreal Engine Godot Unity O3DE


Works with any C++ project — Game Engines, Emulators, Desktop Apps, and more

FeaturesExamplesInstallationTestsIntegrationArchitectureContributing

🎮 Release > v0.0.10

Important

API Change: The BufferOutput attribute in the FDeviceContext struct is now private. To access the write buffer, use the new method: Context->GetRawOutputBuffer().

Important

After calling any effect event on the controller (lights, triggers, vibrations, etc.), it is always necessary to call Gamepad->UpdateOutput() to apply the changes.

🚀 What is Gamepad-Core?

Gamepad-Core is a high-performance, policy-based C++ library that unlocks the full potential of Sony's DualSense and DualShock 4 controllers through direct HID communication.

Unlike generic gamepad APIs (XInput, SDL, etc.), Gamepad-Core gives you raw, low-level access to advanced hardware features that standard drivers can't touch:

  • 🎯 Adaptive Triggers — Dynamic resistance and haptic feedback on L2/R2
  • 💡 RGB Lightbar — Full LED control with per-device customization
  • 🎧 Audio Haptics — Convert game audio into tactile feedback (USB & Wireless)
  • High-Frequency Rumble — Precise motor control for immersive vibration
  • 🔄 Hot-Swap Detection — Real-time plug-and-play support
  • 🎮 Multi-Device — Handle up to 4 controllers simultaneously
  • 🎚️ Factory Calibration — Automatic calibration on connection, no setup required
  • 🎤 Smart Mute Detection — Automatic mute LED control, no coding required

🌍 True Cross-Platform Architecture

Gamepad-Core is engine-agnostic by design. It's a pure C++ library that works anywhere C++20 is supported:

🧱 Policy-Based Architecture

The library leverages policy-based design to abstract platform-specific details. This zero-cost abstraction makes it trivial to extend support to new platforms or custom hardware without touching the core logic.


✨ Features

🎮 Advanced Hardware Control

Unlock the full potential of the DualSense controller with direct access to hardware features.

  • Adaptive Triggers: Full control over resistance profiles including Feedback, Weapon, Bow, Galloping, and raw HEX commands.
  • Audio-to-Haptic: Process real-time audio buffers and convert them into high-fidelity haptic feedback (USB & Bluetooth).
  • RGB & LEDs: Programmatic control over the Lightbar, player indicator LEDs, and microphone mute status.
  • IMU Sensors: Access raw Gyroscope & Accelerometer data for motion controls and precision aiming.
  • Power Management: Query battery level, charging status, and connection type.

⚡ Performance & Reliability

Built for high-performance engines where every millisecond counts.

  • Zero-Allocation Hot Loop: No memory allocation during the update loop to prevent GC spikes or latency.
  • Non-Blocking I/O: Asynchronous device discovery and state updates.
  • Thread-Safe: Designed from the ground up for multi-threaded game engines.
  • Plug-and-Play: Hot-swap support with automatic device detection and reconnection.

🛠️ Developer Experience

  • Modern C++20: Built with concepts, templates, and smart pointers for type safety.
  • Extensible: Implement simple platform policies to add new support.
  • Test Suite: Includes a standalone integration test target (CMake) to verify features on physical hardware.
  • MIT Licensed: Free for commercial and open-source use.

Gamepad-Core is production-ready and currently powers projects used by the gaming community, from high-end Game Engines to low-power Microcontrollers.


🍓 Raspberry Pi Pico W — Microcontroller Integration

Demonstrating the extreme portability and architectural efficiency of the library, the same core logic used in AAA game engines runs perfectly on a Pico W (264KB RAM / 2MB Flash).

The Pico W implementation includes complete support for all advanced DualSense capabilities:

Input Output (Haptics & More)
Motion: Full 6-Axis Gyro & Accel Adaptive Triggers: Weapon, Feedback & Buzz modes
Touchpad: Multi-touch & Coordinates Haptic Feedback: Dual independent motor control
System: Battery & Charging status RGB Lightbar: Full color & Player LED control
Standard: All 17 buttons + Analog sticks Architecture: Production-ready C++20

👉 Check out the Pico W implementation

The Pico W implementation uses the exact same C++ core files as the Unreal and O3DE integrations, with zero logic changes.


🏆 Unreal Engine — Primary Integration

Unreal-Dualsense (v2.0.3)

The flagship plugin bringing native DualSense support to UE5 via Blueprint and C++.

  • ✅ Live Adaptive Trigger prototyping via Data Tables.
  • ✅ Real-time Audio-to-Haptics submix processing.
  • ✅ Native Input System integration for Force Feedback & Motion.

⚙️ O3DE (Open 3D Engine) — Gem Integration

O3DE-Dualsense (O3DE Gem)

A native O3DE Gem that brings DualSense and DualShock 4 support to the Open 3D Engine. Demonstrates Gamepad-Core's flexibility with O3DE's modular Gem architecture.

Features:

  • ✅ Full adaptive trigger support
  • ✅ RGB lightbar and player LED control
  • ✅ Haptic feedback integration
  • ✅ Native O3DE EBus system integration
  • ✅ Blueprint-compatible scripting bindings

A prototype demonstrating Gamepad-Core's portability through native GDExtension bindings. Shows how the library can be wrapped for any scripting environment.

👉 Godot-Dualsense (GDExtension for Godot 4.x)

A specialized implementation tailored for Session: Skate Sim, demonstrating the library's ability to enhance gameplay physics.

👉 Start by cloning the Session Mod Repo


📦 Installation & Submodules

Depending on your project needs, you can clone Gamepad-Core in different ways. The library is designed to be modular, allowing you to include only what is necessary for your target environment.

1. 🍃 Minimal Version (Core Only)

Ideal for: Embedded systems (ESP32, Raspberry Pi Pico W, etc.), or projects where resources are extremely limited and audio is not required.

  • Features: Basic HID communication, buttons, sticks, triggers (non-audio), and lightbar.
  • Size: Very small footprint, no external dependencies.
git clone https://github.com/rafaelvaloto/Gamepad-Core.git

2. 🎧 Standard Version (With Audio Support)

Ideal for: OS-level apps, Desktop software, Raspberry Pi, or any system where you want to use Audio-to-Haptics features.

  • Features: Everything in Minimal + High-fidelity Haptics and Speaker support via miniaudio.
git clone https://github.com/rafaelvaloto/Gamepad-Core.git
cd Gamepad-Core
git submodule update --init Libs/miniaudio

3. 🛠️ Developer Version (Full + Tests)

Ideal for: Contributors, library development, or if you want to run the integration tests on your hardware.

  • Features: Everything in Standard + the complete Integration Test suite.
git clone --recursive https://github.com/rafaelvaloto/Gamepad-Core.git

If you have already cloned the repository without submodules, run:

git submodule update --init --recursive

🧪 Tests

The fastest way to verify Gamepad-Core on your hardware is by running the Integration Tests. This requires cloning the repository with all submodules.

# 1. Clone the repository with tests and audio submodules
git clone --recursive https://github.com/rafaelvaloto/Gamepad-Core.git
cd Gamepad-Core
git submodule update --init --recursive


# 2. Configure and build
cmake -S . -B cmake-build-release -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON
cmake --build cmake-build-release --target test-gamepad-outputs -j

# 3. Run (make sure your DualSense/DualShock is connected)
./cmake-build-release/Tests/Integration/test-gamepad-inputs

🎮 Test Controls

Once the console application is running, use your DualSense to test the features:

Input Testing (test-gamepad-inputs)

The test-gamepad-inputs executable allows you to monitor controller data in real-time. To avoid log misalignment due to terminal width limits, it is highly recommended to test one parameter at a time.

Usage:

./cmake-build-release/Tests/Integration/test-gamepad-inputs [flags]

Available Flags:

Flag Description
--buttons Displays digital button states (Cross, Circle, etc.)
--analogs Displays stick and trigger positions (Default if no flags)
--touch Displays detailed touchpad data (ID, Fingers, Position, Velocity)
--sensors Displays Motion Sensor data (Gyroscope and Accelerometer)

Note: When --touch or --sensors are passed, the respective hardware features are automatically enabled on the controller.


Output Testing (test-gamepad-outputs)

The test-gamepad-outputs executable allows you to test various controller feedback mechanisms, including vibrations, lightbar colors, and adaptive triggers.

Usage:

./cmake-build-release/Tests/Integration/test-gamepad-outputs

[ FACE BUTTONS ]

Button Action Effect
❌ Cross Vibration/LED Heavy Rumble + 🔴 Red Light
⭕ Circle Vibration/LED Soft Rumble + 🔵 Blue Light
🟥 Square Trigger Effect Activates GameCube-style trigger snap on R2
🔺 Triangle Reset Stops all effects (Panic Button)

[ D-PAD & SHOULDERS (Trigger Mods) ]

Button Hand Effect
L1 L2 Gallop Effect (Vibration on trigger)
R1 R2 Machine Gun Effect (Fast vibration)
⬆️ Up L2 Feedback (Rigid Resistance)
⬇️ Down R2 Bow (String Tension)
⬅️ Left R2 Weapon (Semi-Automatic)
➡️ Right R2 Automatic Gun (Buzzing)

🎧 Audio Haptics Integration Test (test-audio-haptics)

The test-audio-haptics demonstrates the high-fidelity Audio Haptics feature. It can play a WAV file or capture system audio, converting it into tactile feedback for the DualSense.

Usage:

# Play a specific WAV file (using relative path)
./cmake-build-release/Tests/Integration/test-audio-haptics "Tests/Integration/Datasets/ES_Touch_SCENE.wav"

# Capture system audio (Loopback mode)
./cmake-build-release/Tests/Integration/test-audio-haptics

Requirements:

  • DualSense controller connected via USB or Bluetooth.
  • USB: Provides 48kHz high-fidelity haptics.
  • Bluetooth: Provides 3000Hz haptics via HID reports.

Supported Modes:

  • WAV Playback: Reads a file and plays it on your default speakers while sending haptics to the controller.
  • System Audio: Captures whatever is playing on your computer and converts it to haptics in real-time.

🎛️ Multi-Channel Haptics Test (test-channels-haptics)

The test-channels-haptics allows testing multiple controllers simultaneously with independent audio sources for haptics.

Usage:

# Assign different WAV files to different gamepads (using relative paths)
./cmake-build-release/Tests/Integration/test-channels-haptics "Tests/Integration/Datasets/ES_Touch_SCENE.wav" "Tests/Integration/Datasets/ES_Replay_Lawd_Ito.wav"

# If more controllers are connected than files provided, the last file is repeated.
# If no file is provided, it defaults to System Audio Loopback for all controllers.

Features:

  • Independent Channels: Gamepad 1 gets the first WAV, Gamepad 2 gets the second, and so on.
  • Automatic Assignment: Automatically detects connected gamepads and starts a dedicated audio worker for each.
  • Hot-Swap Support: New controllers connected during the test will automatically start receiving haptic feedback.

🎵 Music Credits

Special thanks to Epidemic Sound for providing high-quality royalty-free music for testing:

  1. Track: Touch
    Artist: SCENE
    Source: Epidemic Sound

  2. Track: Replay
    Artist: Lawd Ito
    Source: Epidemic Sound


💻 Integration

Minimal Example (Standalone C++)

#include "GCore/Templates/TBasicDeviceRegistry.h"

// 1. Choose your platform policy
#ifdef _WIN32
    #include "Platform/windows/windows_hardware_policy.h"
    using platform_hardware = windows_platform::windows_hardware;
#else
    #include "Platform/linux/linux_hardware_policy.h"
    using platform_hardware = linux_platform::linux_hardware;
#endif

// 2. Define your registry policy
#include "Examples/Adapters/Tests/test_device_registry_policy.h"
using DeviceRegistry = GamepadCore::TBasicDeviceRegistry<Test_DeviceRegistryPolicy>;

int main() {
    // Initialize hardware layer
    auto Hardware = std::make_unique<HardwareInfo>();
    IPlatformHardwareInfo:: SetInstance(std::move(Hardware));

    // Create device registry
    auto Registry = std::make_unique<DeviceRegistry>();

    // Game loop
    while (true) {
        float DeltaTime = 0.016f; // 60 FPS
        
        // Scan for connected devices
        Registry->PlugAndPlay(DeltaTime);

        // Get first connected gamepad
        if (auto* Gamepad = Registry->GetLibrary(0)) {
            if (Gamepad->IsConnected()) {
                // Update input state
                Gamepad->UpdateInput(DeltaTime);
                
                // Read button state
                auto Context = Gamepad->GetMutableDeviceContext();
                auto Input = Context->GetInputState();
                
                if (Input.bCross) {
                    // Trigger haptic feedback
                    Gamepad->SetLightbar({255, 0, 0});
                    Gamepad->SetRumble(255, 128);
                }
                
                // Control adaptive triggers
                if (auto* Trigger = Gamepad->GetIGamepadTrigger()) {
                    
                    Trigger->SetGameCube(EDSGamepadHand::AnyHand);
                    
                    // Example Custom Trigger Bow(0x22)
                    std::vector<uint8_t> BufferTrigger(10);
                    BufferTrigger[0] = 0x22;
                    BufferTrigger[1] = 0x02;
                    BufferTrigger[2] = 0x01;
                    BufferTrigger[3] = 0x3f;
                    BufferTrigger[4] = 0x00;
                    BufferTrigger[5] = 0x00;
                    BufferTrigger[6] = 0x00;
                    BufferTrigger[7] = 0x00;
                    BufferTrigger[8] = 0x00;
                    BufferTrigger[9] = 0x00;

                    if (Trigger) {
                        // Trigger->SetBow22();
                        Trigger->SetCustomTrigger(EDSGamepadHand::Right, BufferTrigger);
                    }
                }
                
                // Audio Haptic Interface
                if (auto* Haptic = Gamepad->IGamepadAudioHaptics()) {
                    // Convert audio buffer into haptic feedback
                    // Haptic->AudioHapticUpdate(<AudioData>);
                }
            }
        }
        
        std::this_thread::sleep_for(std::chrono::milliseconds(16));
    }
}

Platform Policy Structure

Gamepad-Core uses policies to abstract OS-specific code:

struct MyCustomHardwarePolicy {
      	void Read(FDeviceContext* Context)
		{
			test_windows_platform::test_windows_device_info::Read(Context);
		}

		void Write(FDeviceContext* Context)
		{
		}

		void Detect(std::vector<FDeviceContext>& Devices)
		{
		}

		bool CreateHandle(FDeviceContext* Context)
		{
		}

		void InvalidateHandle(FDeviceContext* Context)
		{
		}

		void ProcessAudioHaptic(FDeviceContext* Context)
		{
		}

		void InitializeAudioDevice (FDeviceContext* Context)
		{
		}
};

This design makes it trivial to support custom platforms (e.g., PlayStation SDK, proprietary embedded systems) without touching core logic.

🏗️ Architecture

Gamepad-Core follows strict separation of concerns to ensure portability and extensibility:

┌─────────────────────────────────────────────────────────────┐
│                    Your Application                         │
│              (Game Engine, Desktop App, Tool)               │
└────────────────────────┬────────────────────────────────────┘
                         │
         ┌───────────────┴───────────────┐
         │   Adapter Layer (Policy)      │  ◄── You implement this
         │  (Engine-specific bindings)   │      (or use examples)
         └───────────────┬───────────────┘
                         │
         ┌───────────────┴───────────────┐
         │      GCore (Abstract)         │
         │  • Device Registry            │  ◄── Pure C++, stable API
         │  • ISonyGamepad Interface     │
         │  • IGamepadTrigger Interface  │
         └───────────────┬───────────────┘
                         │
         ┌───────────────┴───────────────┐
         │   GImplementations (Drivers)  │
         │  • DualSense HID Protocol     │  ◄── Hardware-specific
         │  • DualShock 4 HID Protocol   │
         └───────────────┬───────────────┘
                         │
         ┌───────────────┴───────────────┐
         │   Platform Policy (OS/HAL)    │  ◄── OS-specific I/O
         │  • Windows (SetupAPI + HID)   │
         │  • Linux (HIDAPI)             │
         │  • macOS (IOKit)              │
         │  • Custom (PS5 SDK, etc.)     │
         └───────────────────────────────┘

🎵 Audio Pipeline — How It Works

Gamepad-Core provides a complete audio-to-haptics and audio-to-speaker pipeline. Here's how the data flows from your application to the DualSense hardware:

┌─────────────────────────────────────────────────────────────┐
│                    YOUR APPLICATION                         │
│  • Captures audio (game sounds, music, etc.)                │
│  • Mixes audio channels                                     │
│  • Applies effects/filters                                  │
└──────────────────────┬──────────────────────────────────────┘
                       │ (sends audio buffer)
                       ▼
┌─────────────────────────────────────────────────────────────┐
│                   GAMEPAD-CORE LIB                          │
│  • Receives audio buffer                                    │
│  • Converts to haptic commands (for haptics)                │
│  • Encodes for speaker output (for speaker)                 │
│  • Sends via HID (USB/Bluetooth)                            │
└──────────────────────┬──────────────────────────────────────┘
                       │ (HID commands)
                       ▼
┌─────────────────────────────────────────────────────────────┐
│                   DUALSENSE HARDWARE                        │
│  • Vibration motors (haptics)                               │
│  • Built-in speaker                                         │
└─────────────────────────────────────────────────────────────┘

Key Components

1. GCore — The Stable Abstraction

Pure C++ interfaces and templates. Completely OS and engine-agnostic.

Example:

// Set LED color
gamepad->SetLightbar(255, 0, 0);

// Set trigger effect
gamepad->SetResistance(TriggerEffect:: Resistance, ... );

// Apply vibration
gamepad->SetVibration(0.5f, 0.5f);

// ⚠️ REQUIRED: Update output to apply all changes
gamepad->UpdateOutput();

2. GImplementations — The Hardware Drivers

Contains the raw HID protocol logic for each controller:

  • SonyGamepadAbstract — Shared logic between DualShock 4 and DualSense
  • DualSenseLibrary — Adaptive Triggers, Haptics, LED control
  • DualShock4Library — Classic force feedback and lightbar

3. Platform Policy — The OS Bridge

Tells the library how to discover and communicate with devices on your platform:

  • Windows: Uses SetupAPI and hid.dll
  • Linux: Uses hidapi or libusb
  • macOS: Uses IOKit
  • Custom: Implement your own for proprietary SDKs

🧪 Building from Source

Prerequisites

  • CMake 3.20 or higher
  • C++20 compatible compiler (MSVC, GCC, Clang)
  • Ninja (recommended) or Make

Build Commands

# Configure (Debug)
cmake -S . -B build/debug -DCMAKE_BUILD_TYPE=Debug

# Configure (Release)
cmake -S . -B build/release -DCMAKE_BUILD_TYPE=Release

# Compile the library
cmake --build build/release --target GamepadCore -j

# Run integration tests (hardware required)
cmake --build build/debug --target test-gamepad-outputs -j
./build/debug/Tests/Integration/test-gamepad-outputs

🤝 Contributing

Contributions are welcome! Whether you want to:

  • Add support for a new platform (e.g., FreeBSD, Android)
  • Improve documentation or examples
  • Optimize HID communication
  • Report bugs or suggest features

Feel free to open an Issue or submit a Pull Request.

Guidelines

  1. Follow the existing code style (use clang-format)
  2. Test your changes with a physical controller
  3. Update documentation if you add new features
  4. Keep commits focused and well-described

📄 License

License

This project is licensed under the MIT License. See the LICENSE file for details.

Copyright (c) 2025 Rafael Valoto


⭐ Credits and Acknowledgments

The foundation of this plugin was built upon the research and code from several amazing projects in the community:

Special thanks to the community members who helped improve this plugin:

  • yncat: For the extensive research and implementation logic regarding USB Audio Haptics, which was crucial for supporting high-fidelity haptics via USB (Issue #105).

⚖️ Legal & Trademarks

This software is an independent project and is not affiliated with Sony Interactive Entertainment Inc., Epic Games, Unity Technologies, Godot Engine, or any of their subsidiaries.

Trademarks belong to their respective owners:

  • Sony: PlayStation, DualSense, DualShock are trademarks of Sony Interactive Entertainment Inc.
  • Microsoft: Windows, Xbox are trademarks of Microsoft Corporation
  • Apple: macOS is a trademark of Apple Inc.
  • Epic Games: Unreal Engine is a trademark of Epic Games, Inc.
  • Unity: Unity is a trademark of Unity Technologies
  • Godot: Godot and the Godot logo are trademarks of the Godot Engine project

About

The Ultimate Cross-Platform DualSense and DualShock lib API. Pure C++ • Zero Dependencies • Engine Agnostic. Customizable framework tested and validated on Unreal Engine, O3DE and Godot.

Topics

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published

Contributors 3

  •  
  •  
  •