2020-09-30 17:17:25 +02:00

655 lines
28 KiB
C

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
xinput.h
Abstract:
Input API for XBOX
Environment:
XBOX API Layer
Notes:
Revision History:
03-01-00 created by Mitchell Dernis (mitchd)
--*/
#ifndef __XINPUT_API__
#define __XINPUT_API__
#include <xusbenum.h>
#ifdef __cplusplus
extern "C" {
#endif
//----------------------------------------------------------------------------
// Device Types and Subtypes.
//
// TYPES designate major types of input devices. Titles designed
// to work with one device type will not in general be able to utilize
// another unless the title explicitly supports the other device type.
//
// SUBTYPES designate a variation on a device type. Titles designed
// for one subtype will work with any subtype though perhaps not as well.
// In general, titles should follow guidelines for their genre and they
// will work well with at least the standard gamepad, and a genre targeted
// device.
//
//----------------------------------------------------------------------------
// Subtypes for Game Controllers
//
#define XINPUT_DEVSUBTYPE_GC_GAMEPAD 0
#define XINPUT_DEVSUBTYPE_GC_JOYSTICK 1
#define XINPUT_DEVSUBTYPE_GC_WHEEL 2
#include <PSHPACK1.H>
//-------------------------------------------------------------------------------------------------------
// Reports: Each report represents the packet that is transmitted to or from a device.
// There are input reports, and output reports.
//
// All XID reports begin with a one byte ReportId followed by a one byte size.
// Additionally, reports may be input or output. This is a second byte which is
// not in the report itself.
//
// XInput defines three stuctures which are the union of multiple reports:
// XINPUT_REPORT, XOUTPUT_REPORT, and XINPUT_CAPABILITIES_REPORT.
//
// XINPUT_REPORT is used with XInputGetDeviceState to retrieve the state of a device, it is
// the union of the input reports for all the device types.
//
// XOUTPUT_REPORT is used with XInputSendDeviceReport. It has a header and then then
// a union of all the output reports for all the device types.
//
// XINPUT_CAPABILITIES_REPORT is used with XInputQueryCapabilities. It is a subtype followed
// followed by a union of all the input and output reports.
//
//-------------------------------------------------------------------------------------------------------
//**
//** Definition of all the device specific reports
//**
// Standard Report ID - for use with XInputSendDeviceReport and XInputQueryCapabilities.
//
#define XINPUT_GAME_REPORT_ID 0x0100
#define XOUTPUT_RUMBLE_REPORT_ID 0x0200
#define XINPUT_CHAT_REPORT_ID 0x0100
#define XOUTPUT_CHAT_REPORT_ID 0x0200
//
// Standard Input for Game Controllers
//
typedef struct _XINPUT_GAME_REPORT
{
WORD fDpadUp:1;
WORD fDpadDown:1;
WORD fDpadLeft:1;
WORD fDpadRight:1;
WORD fStartButton:1;
WORD fBackBackButton:1;
WORD fLeftThumbButton:1;
WORD fRightThumbButton:1;
WORD bmButtonsUnused:8;
BYTE rgbAnalogButtons[8];
WORD wThumbLX;
WORD wThumbLY;
WORD wThumbRX;
WORD wThumbRY;
} XINPUT_GAME_REPORT, *PXINPUT_GAME_REPORT;
//
// Standard Rumble Motor Output for Game Controllers
//
typedef struct _XOUTPUT_RUMBLE_REPORT
{
WORD wLeftMotorSpeed;
WORD wRightMotorSpeed;
} XOUTPUT_RUMBLE_REPORT, *PXOUTPUT_RUMBLE_REPORT;
//
// Standard Chat Controller Input Report (TBD)
//
typedef struct _XINPUT_CHAT_REPORT
{
WORD TBD;
} XINPUT_CHAT_REPORT, *PXINPUT_CHAT_REPORT;
//
// Standard Chat Controller Output Report (TBD)
//
typedef struct _XOUTPUT_CHAT_REPORT
{
WORD TBD;
} XOUTPUT_CHAT_REPORT, *PXOUTPUT_CHAT_REPORT;
//**
//** Definition of the unions of reports used with
//** XInputGetDeviceState, XInputSendDeviceReport,
//** and XInputQueryCapabilities
//**
//
// Structure used with XInputGetDeviceState
//
typedef struct _XINPUT_REPORT
{
DWORD dwPacketNumber;
union
{
XINPUT_GAME_REPORT GameReport;
XINPUT_CHAT_REPORT ChatReport;
};
} XINPUT_REPORT, *PXINPUT_REPORT;
//
// Structures used with XInputSendDeviceReport
//
#define XOUTPUT_SIZE_OF_INTERNAL_HEADER 90
typedef struct _XOUTPUT_HEADER
{
DWORD dwStatus;
HANDLE OPTIONAL hEvent;
BYTE Reserved[XOUTPUT_SIZE_OF_INTERNAL_HEADER];
} XOUTPUT_HEADER, *PXOUTPUT_HEADER;
typedef struct _XOUTPUT_REPORT
{
XOUTPUT_HEADER Header;
union
{
XOUTPUT_RUMBLE_REPORT RumbleReport;
XOUTPUT_CHAT_REPORT ChatReport;
};
} XOUTPUT_REPORT, *PXOUTPUT_REPORT;
//
// Structures used with XInputQueryCapabilities
//
typedef struct _XINPUT_CAPABILTIES_REPORT
{
BYTE SubType;
WORD Reserved;
union
{
XINPUT_GAME_REPORT GameInputReport;
XINPUT_CHAT_REPORT ChatInputReport;
XOUTPUT_RUMBLE_REPORT RumbleOutputReport;
XOUTPUT_CHAT_REPORT ChatOutputReport;
};
} XINPUT_CAPABILTIES_REPORT, *PXINPUT_CAPABILTIES_REPORT;
//------------------------------------------------------------------------
// Polling Parameters
//------------------------------------------------------------------------
typedef struct _XINPUT_POLLING_PARAMETERS
{
BYTE fAutoPoll:1;
BYTE fInterruptOut:1;
BYTE ReservedMBZ1:6;
BYTE bInputInterval;
BYTE bOutputInterval;
BYTE ReservedMBZ2;
} XINPUT_POLLING_PARAMETERS, *PXINPUT_POLLING_PARAMETERS;
/*++
INTERNAL REVIEW NOTE: AFTER DOING A CAREFUL ANALYSIS, THIS MAY PROVE TO BE WAY TOO
MUCH CONTROL IF WE HAVE ONLY FOUR PORTS. IT IS THE YEAR N CASE WITH AN EXTERNAL
HUB WHERE GAME DEVELOPERS MAY REALLY NEED THIS. NOTE THAT IS OPTIONAL, SO I AM
RELUCTANT TO PULL IT. I THINK IT PROVES WE ARE WILLING TO GO THE EXTRA MILE. IT
IS NOT COMPLICATION FOR THE SAKE OF COMPLEXITY, IT IS REAL FUNCTIONALITY.
Callers of XInputOpen??? family of functions can optionally pass a pointer
to the structure to exercise fine control over the polling parameters. In
cases with many audio chat devices, where bandwidth is tight, this may be
useful.
Members:
fAutoPoll - If TRUE the system automatically keeps an input request
pending to the device. The game can call XInputGetDeviceState
at any time and get the last known state of the device.
If FALSE, the game must initiate every input request by
calling XInputPollDevice. To be assurd that XInputGetDeviceState
has the latest data, there should be a time delay of
bInputInterval plus 2 ms between calling XInputPollDevice and
XInputGetDeviceState.
DEFAULT: TRUE, for all device types.
fInterruptOut - If TRUE, opens the interrupt-OUT endpoint of the device.
IF FALSE, output is sent over the control endpoint.
Opening the out-endpoint takes memory and reserved USB bandwidth.
Output is guaranteed delivery (absent very rare bus errors) to the device
within bOutputInterval plus 1 millisecond. The output request will complete
within in bOutputInterval plus 2 milliseconds. The extra millisecond is
process the confirmation that there were no bus errors.
DEFAULT: TRUE for Game Controllers
FALSE for Chat Controllers
bInputInterval - Specifies in ms the input polling interval. Valid values are are
1,2,4,8,16,32. Any other value will be rounded down to the nearest value.
(Zero is rounded to one though).
DEFAULT: 8 for Game Controllers
16 for Chat Controllers
bOutputInterval - Specifies in ms the output polling interval. Valid values are are
1,2,4,8,16,32. Any other value will be rounded down to the nearest value.
(Zero is rounded to one though).
DEFAULT: 2 for Game Controllers
Ignored for Chat Controllers, because fInterruptOut is FALSE.
ReservedMBZ1 - These parameters are unused at this time. Set them to zero for minimum
ReservedMBZ2 porting effort when compiling against a newer version of the OS.
Comments:
This level of fine control is overkill for many games that use just 1 to 4 game pads and memory units.
It is provided for games that want to use multiple game pads with audio chat devices. In these cases,
it may be helpful to fine tune the USB bandwidth usage. A bandwidth calculator should be provided with
the XDK to aid in choosing these values.
When adjusting these parameters, you are making tradeoffs between responsive of the device, latency on
output, and the number of devices that can share the device.
Turning fAutoPoll off may be useful to reduce the amount of data transfers while maintaining better
responsiveness by synchronizing data polling with the video frame. This way a game can initiate a
poll once a video frame, but by carefully timing the call to XInputPollDevice, can be assurd of fresh data.
Say that bInputInterval is set to 4 ms. XInputPollDevice should be called 6 ms before XInputGetDeviceState.
XInputGetDeviceState should return a state no more than 12 ms old, allowing that the device takes around 6 ms
to debounce buttons presses, do A/D conversion for axes, and ready the data to be sent. Devices may vary.
Compare this with autopoll on, and an interval of 8 ms. It potentially generates twice as must data, but
reserves half the bandwidth. In the worst case, the data is about 16 ms old, compared with 12 ms from the
above.
Setting fInterruptOut FALSE saves bandwidth, and memory at the expense of not guaranteeing latency.
On average, performance will be excellent with fInterruptOut, perhaps even better than with fInterrupt
set to TRUE. In the worst case, say while writing so two or three memory units simulateneously* (using
asynchronous file I/O), with audio chat going, and quickly updating the rumble motors on a couple of
game pads. In this case, the repsonse of the motors may be a bit unpredicatable (sometimes sluggish,
good) if fInterruptOut is FALSE. Unfortunately, this case may require you to set it to FALSE, to get
all the devices on the bus at once.
*In this case, the motors will get three times as much bandwidth as the memory units.
--*/
#include <POPPACK.H>
//------------------------------------------------------------------------
// Open\Close Handle
//------------------------------------------------------------------------
// Different open methods for each input device type.
// XInputOpenGameController
// XInputOpenChatController
DWORD XInputOpenDevice(DWORD,DWORD,DWORD,PXINPUT_POLLING_PARAMETERS,PDWORD);
// One Close Method for everyone
// XInputCloseDevice
__inline
DWORD
XInputOpenGameController(
IN DWORD dwPlayerNumber,
IN PXINPUT_POLLING_PARAMETERS pPollingParameters OPTIONAL,
OUT PDWORD pdwDeviceHandle
)
/*++
Description: Opens a game controller for input. This routine results in the allocation of memory.
Parameters:
dwPlayerNumber - The one based player number to open (this corresponds to
the port number on the box).
pPollingParameters - Optionally specifies fine control over polling behavior.
Pass NULL for default behavior).
pdwDeviceHandle - pointer to DWORD to receive handle.
Return Values:
ERROR_SUCCESS - on success
ERROR_NOT_ENOUGH_MEMORY - Could not allocate enough memory to open device.
ERROR_DEVICE_NOT_CONNECTED - If the device is not connected.
ERROR_SHARING_VIOLATION - If the device is already open.
ERROR_NO_SYSTEM_RESOURCES - If there is insufficient bandwidth to open the device.
ERROR_OUTOFMEMORY - The USB stack failed a memory allocation.
This list is pretty good, but additional error codes may be possible now, or added in the future.
--*/
{
return XInputOpenDevice(
XUSB_DEVICE_TYPE_GAME_CONTROLLER,
XUSB_PORT_FRONT,
dwPlayerNumber,
pPollingParameters,
pdwDeviceHandle
);
}
__inline
DWORD
XInputOpenChatController(
IN DWORD dwPlayerNumber,
IN PXINPUT_POLLING_PARAMETERS pPollingParameters OPTIONAL,
OUT PDWORD pdwDeviceHandle
)
/*++
Description: Opens a chat controller for input. This routine results in the allocation of memory.
Parameters:
dwPlayerNumber - The player number to open (this corresponds to
the port number on the box).
pPollingParameters - Optionally specifies fine control over polling behavior.
Pass NULL for default behavior).
pdwDeviceHandle - pointer to DWORD to receive handle.
Return Values:
ERROR_SUCCESS - on success
ERROR_NOT_ENOUGH_MEMORY - Could not allocate enough memory to open device.
ERROR_DEVICE_NOT_CONNECTED - If the device is not connected.
ERROR_SHARING_VIOLATION - If the device is already open.
ERROR_NO_SYSTEM_RESOURCES - If there is insufficient bandwidth to open the device.
ERROR_OUTOFMEMORY - The USB stack failed a memory allocation.
This list is pretty good, but additional error codes may be possible now, or added in the future.
Comments:
The slot is always hard-coded to XUSB_PORT_TOP_SLOT, because the currently available
chat device only fit in the top slot.
--*/
{
return XInputOpenDevice(
XUSB_DEVICE_TYPE_CHAT_CONTROLLER,
XUSB_PORT_TOP_SLOT,
dwPlayerNumber,
pPollingParameters,
pdwDeviceHandle
);
}
DWORD
XInputOpenDevice(
IN DWORD dwDeviceType,
IN DWORD dwPort,
IN DWORD dwPlayerNumber,
IN PXINPUT_POLLING_PARAMETERS pPollingParameters OPTIONAL,
OUT PDWORD pdwDeviceHandle
);
/*++
Description: Opens an input device for use. This routine results in the allocation of memory.
Parameters:
dwDeviceType - One of the following (defined xusbenum.h):
XUSB_DEVICE_TYPE_GAME_CONTROLLER
XUSB_DEVICE_TYPE_CHAT_CONTROLLER
dwPlayerNumber - The player number to open (this corresponds to
the port number on the box.
dwPort - Valid values:
XUSB_PORT_FRONT
XUSB_PORT_TOP_SLOT
XUSB_PORT_BOTTOM_SLOT
XUSB_PORT_REAR
pPollingParameters - (Optional) Specifies fine control over polling behavior. Pass NULL
to use defaults(see comment on XINPUT_POLLING_PARAMETERS).
pdwDeviceHandle - pointer to DWORD to receive handle.
Return Values:
ERROR_SUCCESS - on success
ERROR_NOT_ENOUGH_MEMORY - Could not allocate enough memory to open device.
ERROR_DEVICE_NOT_CONNECTED - If the device is not connected.
ERROR_SHARING_VIOLATION - If the device is already open.
ERROR_NO_SYSTEM_RESOURCES - If there is insufficient bandwidth to open the device.
ERROR_OUTOFMEMORY - The USB stack failed a memory allocation.
This list is pretty good, but additional error codes may be possible now, or added in the future.
--*/
DWORD
XInputCloseDevice(
IN DWORD dwDeviceHandle
);
/*++
Description: Close the handle for a device.
Parameters:
dwDeviceHandle - Handle returned by XInputOpenDevice.
Comments:
XInputCloseDevice will cancel any outstanding input requests.
Outstanding output is not cancelled, but the actual close operation
is delayed until the output completes, this should never be more
than a couple of milliseconds. Even if there is no outstanding
closing the device is inherently asynchronous and will usually
take around 5-10 ms.
To speed shutdown time, XInputCloseDevice does not block either for
outstanding output or for the shutdown time itself. It returns right
away. The memory required by the handle will not be freed until
the close proceedure is complete.
Since you may only open each device once during this close time, the device
cannot be reopen until the close is really complete. XInputOpenDevice will
block and retry for many milliseconds before returning an ERROR_SHARING_VIOLATION.
Return Values:
ERROR_SUCCESS - Success
ERROR_INVALID_HANDLE - Handle was invalid
--*/
/*++
Description: Given a DeviceInstance fills the DeviceInformation structure.
Parameters:
DeviceInstance - This is LPARAM from WM_USB_DEVICE_INSERTED or WM_USB_DEVICE_REMOVED
DeviceInformation - Caller provided buffer to receive information, dwSize must
be initialized.
Return Values:
ERROR_SUCCESS - on success
ERROR_INVALID_PARAMETER - if DeviceInstance could not be a device.
Comments:
This routine works whether the device is attached or not. It is impossible
to know whether such a device has ever been attached. All we can do is
range check and see if such a device could have been attached. It is possible
(likely even) to "randomly" generate a plausible device instance.
However, the fConnected member will also accurately determine if the described
device is attached.
We don't check subtype, because the valid range is subject to change between
versions.
--*/
//------------------------------------------------------------------------
// Device Capabilities
//------------------------------------------------------------------------
DWORD
XInputQueryCapabilities(
IN DWORD dwDeviceHandle,
IN WORD wReportId,
OUT PXINPUT_CAPABILTIES_REPORT pCapabilities
);
/*++
Description: Retrieves the capabilities of the device for a given report. Under XID
the capabilities and the report format have the same structure.
Parameters:
dwDeviceHandle - Handle of device returned by XInputOpenDevice
wReportTypeAndID- ReportType and ID to retreive capabilities information for. (See XID Specification)
pCapabilities - Caller provided buffer to receive capabilities.
Return Values:
ERROR_SUCCESS - Success
ERROR_INVALID_HANDLE - Handle was invalid
ERROR_DEVICE_NOT_CONNECTED - The device is no longer connected.
ERROR_INVALID_PARAMETER - wReportId was not valid for the report type.
ERROR_IO_DEVICE - If the wReportTypeAndId are not supported by the device,
or if an error occurs in data transmission.
Comments:
This routine performs a data transfer on the USB bus. It blocks the calling thread
until the transfer completes. This is typically a couple of milliseconds.
pInputFormat->bSize should be filled out entry. This value represents the maximum number
of bytes to copy. No range checking is done. If the caller specifies 0, 0 bytes will be copied.
If the pInputFormat->bSize exceeds the actual report size, only the report size will be copied.
--*/
//------------------------------------------------------------------------
// Device Polling
//------------------------------------------------------------------------
DWORD
XInputGetDeviceState(
IN DWORD dwDeviceHandle,
IN OUT PXINPUT_REPORT pInputReport,
OUT PBOOL pfNewData OPTIONAL
);
/*++
Description: Retrieves latest known state of device. This does not
invoke a request for data to the hardware.
Parameters:
dwDeviceHandle - Handle of device returned by XInputOpenDevice
pInputFormat - Caller provided buffer to receive state.
pfNewData - (OPTIONAL) On output indicates whether the
data returned is new.
Return Values:
ERROR_SUCCESS - Success
ERROR_INVALID_HANDLE - Handle was invalid
ERROR_DEVICE_NOT_CONNECTED - The device is no longer connected.
Comment:
The driver always caches the last known state of the device. Either the
driver is automatically posting requests, or the caller is calling
XInput_PollDevice to explcitely post requests for data to the device
(See XINPUT_POLLING_PARAMETERS for details). In either case, the
last known state of the device is always cached. This routine retrieves
the state.
If pfNewData is non-NULL, then *pfNewData is set to TRUE if device
has reported a new state data since the last call to XInputGetDeviceState
for the device. In all cases, the data is copied.
--*/
DWORD
XInputSendDeviceReport(
IN DWORD dwDeviceHandle,
IN WORD wReportId,
IN PXOUTPUT_REPORT pOutputReport
);
/*++
Description: Sends an output report to the device. This is used for operations
such as turning on and off the motors of a rumble pack, or LEDs on a
device.
Parameters:
dwDeviceHandle - Handle of device returned by XInputOpenDevice.
wReportId - ReportId in the OutputReport.
pOutputReport - Pointer to buffer containing the output report to send.
Return Values:
ERROR_IO_PENDING - Output report has been successfully queued to be sent.
ERROR_INVALID_HANDLE - Invalid Device Handle.
ERROR_DEVICE_NOT_CONNECTED - The device is no longer connected.
ERROR_NOT_SUPPORTED - Device does not support output.
Comments:
ASYNCHRONICITY
This request is asynchronous in nature. The following notes are important:
Upon successfully queueing the request, the call always returns ERROR_IO_PENDING. dwStatus (in the XOUTPUT_HEADER structure)
is also set to ERROR_IO_PENDING. When the transfer completes the status is changed to reflect the completion status of the
operation (ERROR_SUCCESS or some other error the transfer failed). The caller MUST NOT WRITE to the buffer (or header)
while dwStatus is set to ERROR_PENDING. However, the caller may READ the buffer while the transfer is pending. Fire and
forget is *not* supported. The output report may not be freed until the game has verified that the output has completed.
At this time, there is no way to cancel pending output. Even XInputCloseDevice does not internally complete until the
output is complete.
hEvent (in the XOUTPUT_HEADER structure) is an optional event that is signalled when the I/O completes (i.e. right after
dwStatus is changed to reflect the completion status). It must be set to either NULL or a valid event handle before calling
XInputSendDeviceReport. If it does not represent a valid event handle and is not NULL, it will be *silently* NULL'd out
and the transfer will be performed without setting an event.
As a general rule, output requests should take no longer than about 4 ms to complete. To be safe ALWAYS check dwStatus. One
extermely unlikely failure mode is that the request hangs. At this time, there is no protection against this. If testing
determines that this is a problem, we can add a watchdog timer.
Output requests to the SAME device are guaranteed to be serialized. There are NO guarantees of
serialization between different devices.
Output requests are NOT cancelled by XInputCloseDevice, and may continue after XInputCloseDevice returns. See XInputCloseDevice
for further details.
--*/
DWORD
XInputPollDevice(
IN DWORD dwDeviceHandle
);
/*++
Description:
Initiates a poll request on the input pipe.
Parameters:
dwDeviceHandle - Handle of device returned by XInputOpenDevice.
Return Values:
ERROR_SUCCESS - on success
ERROR_INVALID_HANDLE - Invalid Device Handle.
ERROR_DEVICE_NOT_CONNECTED - The device is no longer connected.
Comments:
Initiates a poll request on the input pipe. The state is later retrieved
with XInputGetDeviceState. The device must have been opened with the fAutoPoll parameter
of XInputPollDevice set to FALSE. About 4 ms after calling XInputPollDevice, XInputGetDeviceState
is guaranteed to return the latest state of the device.
Turning fAutoPoll off and calling XInputPollDevice can increase performance, but will reserve more USB bandwidth.
This can be a problem if the title also supports USB audio chat devices. When a device is opened with fAutoPoll
set to TRUE, the interrupt-in endpoint is opened to be hardware polled at 8 ms intervals. Whenever new
data is transfered, the software immediately posts a new request. As long as new data is available, this
will result in an interrupt every 8 ms to process new input packets. Assuming the game calls XInputGetDeviceState
only once per video frame, half of these packets are thrown out.
With fAutoPoll set to FALSE at open, the hardware polling rate is set to 1 ms. However, the software does not post a
request until XInputPollDevice is called. If the software calls InputPollDevice once per video frame, and there is always
new data available, there will only be interrupts at the video frame rate (~16ms per frame). No data is thrown out.
The only caveat is that game needs to call XInputPollDevice about 4 ms before XInputGetDeviceState in order to be guaranteed
fresh data.
XInputPollDevice is a NOP and returns ERROR_SUCCESS if XInputOpenDevice was called with fAutoPoll set to true.
--*/
#ifdef __cplusplus
} //close extern C
#endif
#endif // __XINPUT_API__