Search code examples
cinputvibrationgamepad

gamepad force feedback (vibration) on windows using raw input


I'm currently writing a cross platform library in C which includes gamepad support. Gamepad communication on windows is handled by both raw input and xinput, depending on the specific gamepad.

While xinput facilitates force feedback on xbox360 controllers, I have not found a way to do this using raw input. I have some gamepads that can give force feedback, but I cannot find a way to do this through raw input. Is there a way to do this?

I prefer not to use the directinput api, since it's deprecated and discouraged by Microsoft.

Edit:

Since I've implemented gamepads for a large part, maybe I can narrow down the question a bit. I suspect the amount of rumble motors in a gamepad can be found by reading the NumberOutputValueCaps of a HIDP_CAPS structure. This gives the correct result for all my test gamepads.

I'm using the funtcion HidP_GetUsageValue to read axis values, which works fine. Now I suspect calling HidP_SetUsageValue should allow me to change this output value, turning on the rumble motor. Calling this function fails, however. Should this function be able to access rumble motors?


Solution

  • HidP_SetUsageValue() only modifies a report buffer -- you need to first prepare an appropriately-sized buffer (which may be why the function was failing; input reports and output reports won't necessarily be the same size) then send it to the device before it will have any effect. MSDN suggests you can use HidD_SetOutputReport() for that purpose, but I had better luck with WriteFile(), following the sample code at: https://code.msdn.microsoft.com/windowshardware/HClient-HID-Sample-4ec99697/sourcecode?fileId=51262&pathId=340791466

    This snippet (based on the Linux driver) lets me control the motors and LED on a DualShock 4:

    const char *path = /* from GetRawInputDeviceInfo(RIDI_DEVICENAME) */;
    HANDLE hid_device = CreateFile(path, GENERIC_READ | GENERIC_WRITE,
                                   FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                                   OPEN_EXISTING, 0, NULL);
    assert(hid_device != INVALID_HANDLE_VALUE);
    uint8_t buf[32];
    memset(buf, 0, sizeof(buf));
    buf[0] = 0x05;
    buf[1] = 0xFF;
    buf[4] = right_motor_strength;  // 0-255
    buf[5] = left_motor_strength;   // 0-255
    buf[6] = led_red_level;         // 0-255
    buf[7] = led_green_level;       // 0-255
    buf[8] = led_blue_level;        // 0-255
    DWORD bytes_written;
    assert(WriteFile(hid_device, buf, sizeof(buf), &bytes_written, NULL));
    assert(bytes_written == 32);
    

    (EDIT: fixed buffer offsets)