ViGEmClient SDK

repository·master·Indexed 41 days ago

https://github.com/nefarius/vigemclient

A C/C++ SDK used to communicate with the ViGEmBus driver to create and control virtual game controllers, such as Xbox 360 and DualShock 4 devices, from software applications. It provides capabilities for device discovery, virtual device management, and input emulation, including support for rumble and LED notifications.

Tokens
3.1K
Snippets
11
Records
14
Agent score
52%

What's inside ViGEmClient

  1. Overview of the ViGEm user-mode client library

    master

    The ViGEm user-mode client library is a static library that serves as a gateway to bus driver functionalities. It allows developers to interact with virtual controllers on a system through three primary capabilities:

    1. Device Discovery: Searching for and connecting to a library-compatible bus device currently on the system.
    2. Virtual Device Management: Attaching and removing a limited number of virtual devices.
    3. Input Emulation: Feeding input state changes to the emulated devices to simulate controller behavior.
  2. Shutdown and release ViGEmClient resources

    master

    When your application is finished with the ViGEm API, you must disconnect from the driver and free the client handle to prevent resource leaks. Once vigem_free is called, the client handle is invalid.

    vigem_disconnect(client);
    vigem_free(client);
  3. Initialize and connect to the ViGEmBus

    master

    To use the SDK, you must first allocate a client handle and establish a connection to the driver.

    Note: vigem_connect is an expensive operation; perform it once during your application's initialization rather than in a high-frequency loop.

    Thread Safety Warning: The library is not thread-safe. You must ensure proper synchronization if using the client in a multi-threaded environment.

    // Include headers
    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    #include <ViGEm/Client.h>
    #pragma comment(lib, "setupapi.lib")
    
    // 1. Allocate the client handle
    const auto client = vigem_alloc();
    if (client == nullptr)
    {
        // Handle allocation error
        return -1;
    }
    
    // 2. Connect to the driver
    const auto retval = vigem_connect(client);
    if (!VIGEM_SUCCESS(retval))
    {
        // Handle connection error
        return -1;
    }
  4. Integrate ViGEmClient into your project

    master

    You can integrate the ViGEmClient library using one of the following methods:

    1. vcpkg (Recommended): Use the provided vcpkg package manager integration for easy updates.
    2. Git Submodule/Subtree: Add this repository directly into your source tree.
    3. Static Linking: Highly recommended to avoid 'DLL hell' and handle driver compatibility internally.

    Important: If you are linking statically, you must also link against setupapi.lib.

  5. Emulate an Xbox 360 controller

    master

    To create a virtual Xbox 360 controller, allocate a target, add it to the client, and then periodically update its state using vigem_target_x360_update.

    To stop emulation, use vigem_target_remove to disconnect the virtual device and vigem_target_free to release the target's resources.

    // Allocate handle for an Xbox 360 pad
    const auto pad = vigem_target_x360_alloc();
    
    // Add the target to the client (triggers a plug-in event)
    const auto pir = vigem_target_add(client, pad);
    if (!VIGEM_SUCCESS(pir))
    {
        return -1;
    }
    
    // Example: Feeding input (e.g., from XInput)
    XINPUT_STATE state;
    XInputGetState(0, &state);
    
    // Update the virtual controller state
    // Note: XINPUT_GAMEPAD is cast to XUSB_REPORT
    vigem_target_x360_update(client, pad, *reinterpret_cast<XUSB_REPORT*>(&state.Gamepad));
    
    // Cleanup
    vigem_target_remove(client, pad);
    vigem_target_free(pad);
  6. Handle virtual device notifications (Rumble/LED)

    master

    Virtual devices can receive state changes from the host (e.g., rumble/vibration requests or LED changes). You can intercept these by registering a callback function using vigem_target_x360_register_notification.

    // 1. Define the callback function
    VOID CALLBACK notification(
        PVIGEM_CLIENT Client,
        PVIGEM_TARGET Target,
        UCHAR LargeMotor,
        UCHAR SmallMotor,
        UCHAR LedNumber,
        LPVOID UserData
    )
    {
        // Handle rumble or LED requests here
        std::cout << "Rumble: " << (int)LargeMotor << " / " << (int)SmallMotor << std::endl;
    }
    
    // 2. Register the callback
    const auto retval = vigem_target_x360_register_notification(client, pad, &notification, nullptr);
    if (!VIGEM_SUCCESS(retval))
    {
        return -1;
    }
  7. Manage the ViGEm Client connection

    master

    To use the ViGEm SDK, you must first allocate a client object and establish a connection to the emulation bus driver.

    1. Allocate: Use vigem_alloc() to create a PVIGEM_CLIENT.
    2. Connect: Use vigem_connect() to initialize the driver and connect to the bus. This may fail if no compatible bus driver is found.
    3. Disconnect: Use vigem_disconnect() to reset the driver state. This automatically destroys all currently connected targets.
    4. Free: Use vigem_free() to release the memory used by the client object.
    PVIGEM_CLIENT client = vigem_alloc();
    if (VIGEM_SUCCESS(vigem_connect(client))) {
        // Use client...
        vigem_disconnect(client);
    }
    vigem_free(client);
  8. Consume DualShock 4 output reports

    master

    To handle incoming data from a DualShock 4 (like LightBar or vibration changes), use the following event-based blocking functions. These should typically be called from a dedicated thread.

    • vigem_target_ds4_await_output_report(client, target, buffer): Blocks until a 64-byte output report is available.
    • vigem_target_ds4_await_output_report_timeout(client, target, milliseconds, buffer): Blocks until a report is available or the specified timeout is reached.

    Both functions write the data into a PDS4_OUTPUT_BUFFER.

    DS4_OUTPUT_BUFFER buffer;
    // In a loop/thread:
    VIGEM_ERROR err = vigem_target_ds4_await_output_report(client, target, &buffer);
    if (VIGEM_SUCCESS(err)) {
        // Process buffer
    }
  9. Update DualShock 4 controller state

    master

    To send input data to an emulated DualShock 4 controller, it is recommended to use vigem_target_ds4_update_ex(). This function accepts a DS4_REPORT_EX structure, which allows you to utilize all DS4 features including touchpads and gyroscopes.

    Note: The older vigem_target_ds4_update() function is deprecated.

    DS4_REPORT_EX report = { /* initialize report fields */ };
    VIGEM_ERROR err = vigem_target_ds4_update_ex(client, target, report);
  10. Update Xbox 360 controller state

    master

    To send input data (buttons, axes, etc.) to an emulated Xbox 360 controller, use vigem_target_x360_update(). This requires an XUSB_REPORT structure containing the desired state.

    XUSB_REPORT report = { /* initialize report fields */ };
    VIGEM_ERROR err = vigem_target_x360_update(client, target, report);
  11. Register notifications for controller state changes

    master

    You can register callbacks to be notified when certain properties of a target device change (e.g., LED or vibration state).

    • Xbox 360: Use vigem_target_x360_register_notification() and provide a function matching the PFN_VIGEM_X360_NOTIFICATION signature. Unregister using vigem_target_x360_unregister_notification().
    • DualShock 4: The function vigem_target_ds4_register_notification() is deprecated. Instead, use the await functions to consume output reports (see 'Consume DualShock 4 output reports' below).