Output Data

Some Human Interface Devices both generate input and accept output. The IDirectInputDevice8::SendDeviceData method is used to send packets of data to such devices.

IDirectInputDevice8::SendDeviceData may be viewed as IDirectInputDevice8::GetDeviceData in reverse. As with that method, it uses the DIDEVICEOBJECTDATA structure as the basic unit of data. In this case, however, the dwOfs member contains the instance ID of the device object associated with the data rather than its offset in the data format for the device. (Because offset identifiers exist only for device objects that provide input in the selected data format, an object that accepts only output might not even have an offset.) The dwData member contains whatever data is appropriate for the object. The dwTimeStamp and dwSequence members are not used and must be set to 0.

To send data to the device, set up an array of DIDEVICEOBJECTDATA structures, fill the required number of elements with data, and then pass the array address and the number of elements used to IDirectInputDevice8::SendDeviceData. Data for different device objects is combined into a single packet that is then sent to the device.

The form of the data packet is specific to the device, as is the treatment of unused fields in the packet. Some devices may treat fields as optional. In other words, if no data is supplied, the state of the object remains unchanged. More commonly, all fields are significant, even when you do not specifically supply data for them. For example, if you send data to a single keyboard LED, it is assumed that the data for the other two LEDs is 0 and that they are turned off. However, you can override this behavior by using the DISDD_CONTINUE flag. In this case, the data for the other two LEDs is the value you most recently sent them.

The device object type identifiers are obtained from the dwType member of the DIDEVICEOBJECTINSTANCE structure after passing the HID usage page and usage code to IDirectInputDevice8::GetObjectInfo. For example, the type identifier of the Scroll Lock LED can be obtained by using the following code, where pdev is an IDirectInputDevice8 Interface interface pointer representing the keyboard.

DIDEVICEOBJECTINSTANCE didoi;
DWORD NumLockID; 
HRESULT hr = pdev->GetObjectInfo(&didoi, 
        DIMAKEUSAGEDWORD(0x07,0x53), DIPH_BYUSAGE);
NumLockID = didoi.dwType;

The following sample function, when called repeatedly, causes the LEDs on the keyboard to flash in a recurring pattern. It is assumed that the high bit of the data byte determines the state of the LED. The DWORDs NumLockID, CapsLockID, and ScrollLockID have all been obtained as shown above.

#define ARRAYSIZE 4
void FlashLEDs(void)
    {
    static int         rgiBits[] = { 1, 2, 4, 2 };
    static int         iLooper = 0; 
    DWORD              cdod = 3;                  // Number of items
    DIDEVICEOBJECTDATA rgdod[ARRAYSIZE-1];
    HRESULT            hres;

    // Must clear dwTimeStamp and dwSequence
    ZeroMemory(rgdod, sizeof(rgdod));

    rgdod[0].dwOfs = NumLockID; 
    rgdod[1].dwOfs = CapsLockID; 
    rgdod[2].dwOfs = ScrollLockID;

    rgdod[0].dwData = (rgiBits[iLooper] & 1) ? 0x80 : 0; 
                                           // NumLock light on/off
    rgdod[1].dwData = (rgiBits[iLooper] & 2) ? 0x80 : 0; 
                                           // CapsLock light on/off
    rgdod[2].dwData = (rgiBits[iLooper] & 4) ? 0x80 : 0; 
                                           // ScrollLock light on/off

    iLooper = (iLooper + 1) % ARRAYSIZE; // Loops from 0 to 3

    hres = pdev->SendDeviceData(sizeof(DIDEVICEOBJECTDATA), 
            rgdod, &cdod, 0);
    }