early-access version 1611

This commit is contained in:
pineappleEA
2021-04-18 05:35:25 +02:00
parent 16f54e367d
commit 18db69f039
1409 changed files with 545335 additions and 10 deletions

View File

@@ -0,0 +1,414 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef SDL_JOYSTICK_HIDAPI
#include "SDL_hints.h"
#include "SDL_log.h"
#include "SDL_events.h"
#include "SDL_timer.h"
#include "SDL_haptic.h"
#include "SDL_joystick.h"
#include "SDL_gamecontroller.h"
#include "../../SDL_hints_c.h"
#include "../SDL_sysjoystick.h"
#include "SDL_hidapijoystick_c.h"
#include "SDL_hidapi_rumble.h"
#ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE
#define MAX_CONTROLLERS 4
typedef struct {
SDL_JoystickID joysticks[MAX_CONTROLLERS];
Uint8 wireless[MAX_CONTROLLERS];
Uint8 min_axis[MAX_CONTROLLERS*SDL_CONTROLLER_AXIS_MAX];
Uint8 max_axis[MAX_CONTROLLERS*SDL_CONTROLLER_AXIS_MAX];
Uint8 rumbleAllowed[MAX_CONTROLLERS];
Uint8 rumble[1+MAX_CONTROLLERS];
/* Without this variable, hid_write starts to lag a TON */
SDL_bool rumbleUpdate;
SDL_bool m_bUseButtonLabels;
} SDL_DriverGameCube_Context;
static SDL_bool
HIDAPI_DriverGameCube_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
{
if (vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_GAMECUBE_ADAPTER) {
/* Nintendo Co., Ltd. Wii U GameCube Controller Adapter */
return SDL_TRUE;
}
return SDL_FALSE;
}
static const char *
HIDAPI_DriverGameCube_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
{
return "Nintendo GameCube Controller";
}
static void
ResetAxisRange(SDL_DriverGameCube_Context *ctx, int joystick_index)
{
SDL_memset(&ctx->min_axis[joystick_index*SDL_CONTROLLER_AXIS_MAX], 128-88, SDL_CONTROLLER_AXIS_MAX);
SDL_memset(&ctx->max_axis[joystick_index*SDL_CONTROLLER_AXIS_MAX], 128+88, SDL_CONTROLLER_AXIS_MAX);
/* Trigger axes may have a higher resting value */
ctx->min_axis[joystick_index*SDL_CONTROLLER_AXIS_MAX+SDL_CONTROLLER_AXIS_TRIGGERLEFT] = 40;
ctx->min_axis[joystick_index*SDL_CONTROLLER_AXIS_MAX+SDL_CONTROLLER_AXIS_TRIGGERRIGHT] = 40;
}
static float fsel(float fComparand, float fValGE, float fLT)
{
return fComparand >= 0 ? fValGE : fLT;
}
static float RemapVal(float val, float A, float B, float C, float D)
{
if (A == B) {
return fsel(val - B , D , C);
}
if (val < A) {
val = A;
}
if (val > B) {
val = B;
}
return C + (D - C) * (val - A) / (B - A);
}
static void SDLCALL SDL_GameControllerButtonReportingHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
{
SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)userdata;
ctx->m_bUseButtonLabels = SDL_GetStringBoolean(hint, SDL_TRUE);
}
static Uint8 RemapButton(SDL_DriverGameCube_Context *ctx, Uint8 button)
{
if (!ctx->m_bUseButtonLabels) {
/* Use button positions */
switch (button) {
case SDL_CONTROLLER_BUTTON_B:
return SDL_CONTROLLER_BUTTON_X;
case SDL_CONTROLLER_BUTTON_X:
return SDL_CONTROLLER_BUTTON_B;
default:
break;
}
}
return button;
}
static SDL_bool
HIDAPI_DriverGameCube_InitDevice(SDL_HIDAPI_Device *device)
{
SDL_DriverGameCube_Context *ctx;
Uint8 packet[37];
Uint8 *curSlot;
Uint8 i;
int size;
Uint8 initMagic = 0x13;
Uint8 rumbleMagic = 0x11;
ctx = (SDL_DriverGameCube_Context *)SDL_calloc(1, sizeof(*ctx));
if (!ctx) {
SDL_OutOfMemory();
return SDL_FALSE;
}
device->dev = hid_open_path(device->path, 0);
if (!device->dev) {
SDL_free(ctx);
SDL_SetError("Couldn't open %s", device->path);
return SDL_FALSE;
}
device->context = ctx;
ctx->joysticks[0] = -1;
ctx->joysticks[1] = -1;
ctx->joysticks[2] = -1;
ctx->joysticks[3] = -1;
ctx->rumble[0] = rumbleMagic;
/* This is all that's needed to initialize the device. Really! */
if (hid_write(device->dev, &initMagic, sizeof(initMagic)) != sizeof(initMagic)) {
SDL_SetError("Couldn't initialize WUP-028");
goto error;
}
/* Wait for the adapter to initialize */
SDL_Delay(10);
/* Add all the applicable joysticks */
while ((size = hid_read_timeout(device->dev, packet, sizeof(packet), 0)) > 0) {
if (size < 37 || packet[0] != 0x21) {
continue; /* Nothing to do yet...? */
}
/* Go through all 4 slots */
curSlot = packet + 1;
for (i = 0; i < MAX_CONTROLLERS; i += 1, curSlot += 9) {
ctx->wireless[i] = (curSlot[0] & 0x20) != 0;
/* Only allow rumble if the adapter's second USB cable is connected */
ctx->rumbleAllowed[i] = (curSlot[0] & 0x04) != 0 && !ctx->wireless[i];
if (curSlot[0] & 0x30) { /* 0x10 - Wired, 0x20 - Wireless */
if (ctx->joysticks[i] == -1) {
ResetAxisRange(ctx, i);
HIDAPI_JoystickConnected(device, &ctx->joysticks[i]);
}
} else {
if (ctx->joysticks[i] != -1) {
HIDAPI_JoystickDisconnected(device, ctx->joysticks[i]);
ctx->joysticks[i] = -1;
}
continue;
}
}
}
SDL_AddHintCallback(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS,
SDL_GameControllerButtonReportingHintChanged, ctx);
return SDL_TRUE;
error:
if (device->dev) {
hid_close(device->dev);
device->dev = NULL;
}
if (device->context) {
SDL_free(device->context);
device->context = NULL;
}
return SDL_FALSE;
}
static int
HIDAPI_DriverGameCube_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
{
SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context;
Uint8 i;
for (i = 0; i < 4; ++i) {
if (instance_id == ctx->joysticks[i]) {
return i;
}
}
return -1;
}
static void
HIDAPI_DriverGameCube_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
{
}
static SDL_bool
HIDAPI_DriverGameCube_UpdateDevice(SDL_HIDAPI_Device *device)
{
SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context;
SDL_Joystick *joystick;
Uint8 packet[37];
Uint8 *curSlot;
Uint8 i;
Sint16 axis_value;
int size;
/* Read input packet */
while ((size = hid_read_timeout(device->dev, packet, sizeof(packet), 0)) > 0) {
if (size < 37 || packet[0] != 0x21) {
continue; /* Nothing to do right now...? */
}
/* Go through all 4 slots */
curSlot = packet + 1;
for (i = 0; i < MAX_CONTROLLERS; i += 1, curSlot += 9) {
ctx->wireless[i] = (curSlot[0] & 0x20) != 0;
/* Only allow rumble if the adapter's second USB cable is connected */
ctx->rumbleAllowed[i] = (curSlot[0] & 0x04) != 0 && !ctx->wireless[i];
if (curSlot[0] & 0x30) { /* 0x10 - Wired, 0x20 - Wireless */
if (ctx->joysticks[i] == -1) {
ResetAxisRange(ctx, i);
HIDAPI_JoystickConnected(device, &ctx->joysticks[i]);
}
joystick = SDL_JoystickFromInstanceID(ctx->joysticks[i]);
/* Hasn't been opened yet, skip */
if (joystick == NULL) {
continue;
}
} else {
if (ctx->joysticks[i] != -1) {
HIDAPI_JoystickDisconnected(device, ctx->joysticks[i]);
ctx->joysticks[i] = -1;
}
continue;
}
#define READ_BUTTON(off, flag, button) \
SDL_PrivateJoystickButton( \
joystick, \
RemapButton(ctx, button), \
(curSlot[off] & flag) ? SDL_PRESSED : SDL_RELEASED \
);
READ_BUTTON(1, 0x01, 0) /* A */
READ_BUTTON(1, 0x04, 1) /* B */
READ_BUTTON(1, 0x02, 2) /* X */
READ_BUTTON(1, 0x08, 3) /* Y */
READ_BUTTON(1, 0x10, 4) /* DPAD_LEFT */
READ_BUTTON(1, 0x20, 5) /* DPAD_RIGHT */
READ_BUTTON(1, 0x40, 6) /* DPAD_DOWN */
READ_BUTTON(1, 0x80, 7) /* DPAD_UP */
READ_BUTTON(2, 0x01, 8) /* START */
READ_BUTTON(2, 0x02, 9) /* RIGHTSHOULDER */
/* These two buttons are for the bottoms of the analog triggers.
* More than likely, you're going to want to read the axes instead!
* -flibit
*/
READ_BUTTON(2, 0x04, 10) /* TRIGGERRIGHT */
READ_BUTTON(2, 0x08, 11) /* TRIGGERLEFT */
#undef READ_BUTTON
#define READ_AXIS(off, axis) \
if (axis < SDL_CONTROLLER_AXIS_TRIGGERLEFT) \
if (curSlot[off] < ctx->min_axis[i*SDL_CONTROLLER_AXIS_MAX+axis]) ctx->min_axis[i*SDL_CONTROLLER_AXIS_MAX+axis] = curSlot[off]; \
if (curSlot[off] > ctx->max_axis[i*SDL_CONTROLLER_AXIS_MAX+axis]) ctx->max_axis[i*SDL_CONTROLLER_AXIS_MAX+axis] = curSlot[off]; \
axis_value = (Sint16)(RemapVal(curSlot[off], ctx->min_axis[i*SDL_CONTROLLER_AXIS_MAX+axis], ctx->max_axis[i*SDL_CONTROLLER_AXIS_MAX+axis], SDL_MIN_SINT16, SDL_MAX_SINT16)); \
SDL_PrivateJoystickAxis( \
joystick, \
axis, axis_value \
);
READ_AXIS(3, SDL_CONTROLLER_AXIS_LEFTX)
READ_AXIS(4, SDL_CONTROLLER_AXIS_LEFTY)
READ_AXIS(5, SDL_CONTROLLER_AXIS_RIGHTX)
READ_AXIS(6, SDL_CONTROLLER_AXIS_RIGHTY)
READ_AXIS(7, SDL_CONTROLLER_AXIS_TRIGGERLEFT)
READ_AXIS(8, SDL_CONTROLLER_AXIS_TRIGGERRIGHT)
#undef READ_AXIS
}
}
/* Write rumble packet */
if (ctx->rumbleUpdate) {
SDL_HIDAPI_SendRumble(device, ctx->rumble, sizeof(ctx->rumble));
ctx->rumbleUpdate = SDL_FALSE;
}
/* If we got here, nothing bad happened! */
return SDL_TRUE;
}
static SDL_bool
HIDAPI_DriverGameCube_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context;
Uint8 i;
for (i = 0; i < MAX_CONTROLLERS; i += 1) {
if (joystick->instance_id == ctx->joysticks[i]) {
joystick->nbuttons = 12;
joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
joystick->epowerlevel = ctx->wireless[i] ? SDL_JOYSTICK_POWER_UNKNOWN : SDL_JOYSTICK_POWER_WIRED;
return SDL_TRUE;
}
}
return SDL_FALSE; /* Should never get here! */
}
static int
HIDAPI_DriverGameCube_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
{
SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context;
Uint8 i, val;
for (i = 0; i < MAX_CONTROLLERS; i += 1) {
if (joystick->instance_id == ctx->joysticks[i]) {
if (ctx->wireless[i]) {
return SDL_SetError("Ninteno GameCube WaveBird controllers do not support rumble");
}
if (!ctx->rumbleAllowed[i]) {
return SDL_SetError("Second USB cable for WUP-028 not connected");
}
val = (low_frequency_rumble > 0 || high_frequency_rumble > 0);
if (val != ctx->rumble[i + 1]) {
ctx->rumble[i + 1] = val;
ctx->rumbleUpdate = SDL_TRUE;
}
return 0;
}
}
/* Should never get here! */
SDL_SetError("Couldn't find joystick");
return -1;
}
static void
HIDAPI_DriverGameCube_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context;
/* Stop rumble activity */
if (ctx->rumbleUpdate) {
SDL_HIDAPI_SendRumble(device, ctx->rumble, sizeof(ctx->rumble));
ctx->rumbleUpdate = SDL_FALSE;
}
}
static void
HIDAPI_DriverGameCube_FreeDevice(SDL_HIDAPI_Device *device)
{
SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context;
hid_close(device->dev);
device->dev = NULL;
SDL_DelHintCallback(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS,
SDL_GameControllerButtonReportingHintChanged, ctx);
SDL_free(device->context);
device->context = NULL;
}
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverGameCube =
{
SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE,
SDL_TRUE,
HIDAPI_DriverGameCube_IsSupportedDevice,
HIDAPI_DriverGameCube_GetDeviceName,
HIDAPI_DriverGameCube_InitDevice,
HIDAPI_DriverGameCube_GetDevicePlayerIndex,
HIDAPI_DriverGameCube_SetDevicePlayerIndex,
HIDAPI_DriverGameCube_UpdateDevice,
HIDAPI_DriverGameCube_OpenJoystick,
HIDAPI_DriverGameCube_RumbleJoystick,
HIDAPI_DriverGameCube_CloseJoystick,
HIDAPI_DriverGameCube_FreeDevice
};
#endif /* SDL_JOYSTICK_HIDAPI_GAMECUBE */
#endif /* SDL_JOYSTICK_HIDAPI */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,539 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/* This driver supports both simplified reports and the extended input reports enabled by Steam.
Code and logic contributed by Valve Corporation under the SDL zlib license.
*/
#include "../../SDL_internal.h"
#ifdef SDL_JOYSTICK_HIDAPI
#include "SDL_hints.h"
#include "SDL_log.h"
#include "SDL_events.h"
#include "SDL_timer.h"
#include "SDL_joystick.h"
#include "SDL_gamecontroller.h"
#include "../SDL_sysjoystick.h"
#include "SDL_hidapijoystick_c.h"
#include "SDL_hidapi_rumble.h"
#ifdef SDL_JOYSTICK_HIDAPI_PS4
typedef enum
{
k_EPS4ReportIdUsbState = 1,
k_EPS4ReportIdUsbEffects = 5,
k_EPS4ReportIdBluetoothState = 17,
k_EPS4ReportIdBluetoothEffects = 17,
k_EPS4ReportIdDisconnectMessage = 226,
} EPS4ReportId;
typedef enum
{
k_ePS4FeatureReportIdGyroCalibration_USB = 0x02,
k_ePS4FeatureReportIdGyroCalibration_BT = 0x05,
k_ePS4FeatureReportIdSerialNumber = 0x12,
} EPS4FeatureReportID;
typedef struct
{
Uint8 ucLeftJoystickX;
Uint8 ucLeftJoystickY;
Uint8 ucRightJoystickX;
Uint8 ucRightJoystickY;
Uint8 rgucButtonsHatAndCounter[ 3 ];
Uint8 ucTriggerLeft;
Uint8 ucTriggerRight;
Uint8 _rgucPad0[ 3 ];
Sint16 sGyroX;
Sint16 sGyroY;
Sint16 sGyroZ;
Sint16 sAccelX;
Sint16 sAccelY;
Sint16 sAccelZ;
Uint8 _rgucPad1[ 5 ];
Uint8 ucBatteryLevel;
Uint8 _rgucPad2[ 4 ];
Uint8 ucTrackpadCounter1;
Uint8 rgucTrackpadData1[ 3 ];
Uint8 ucTrackpadCounter2;
Uint8 rgucTrackpadData2[ 3 ];
} PS4StatePacket_t;
typedef struct
{
Uint8 ucRumbleRight;
Uint8 ucRumbleLeft;
Uint8 ucLedRed;
Uint8 ucLedGreen;
Uint8 ucLedBlue;
Uint8 ucLedDelayOn;
Uint8 ucLedDelayOff;
Uint8 _rgucPad0[ 8 ];
Uint8 ucVolumeLeft;
Uint8 ucVolumeRight;
Uint8 ucVolumeMic;
Uint8 ucVolumeSpeaker;
} DS4EffectsState_t;
typedef struct {
SDL_bool is_dongle;
SDL_bool is_bluetooth;
SDL_bool audio_supported;
SDL_bool rumble_supported;
int player_index;
Uint8 volume;
Uint32 last_volume_check;
PS4StatePacket_t last_state;
} SDL_DriverPS4_Context;
/* Public domain CRC implementation adapted from:
http://home.thep.lu.se/~bjorn/crc/crc32_simple.c
*/
static Uint32 crc32_for_byte(Uint32 r)
{
int i;
for(i = 0; i < 8; ++i) {
r = (r & 1? 0: (Uint32)0xEDB88320L) ^ r >> 1;
}
return r ^ (Uint32)0xFF000000L;
}
static Uint32 crc32(Uint32 crc, const void *data, int count)
{
int i;
for(i = 0; i < count; ++i) {
crc = crc32_for_byte((Uint8)crc ^ ((const Uint8*)data)[i]) ^ crc >> 8;
}
return crc;
}
static SDL_bool
HIDAPI_DriverPS4_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
{
return (type == SDL_CONTROLLER_TYPE_PS4);
}
static const char *
HIDAPI_DriverPS4_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
{
if (vendor_id == USB_VENDOR_SONY) {
return "PS4 Controller";
}
return NULL;
}
static SDL_bool ReadFeatureReport(hid_device *dev, Uint8 report_id, Uint8 *data, size_t size)
{
Uint8 report[USB_PACKET_LENGTH + 1];
SDL_memset(report, 0, sizeof(report));
report[0] = report_id;
if (hid_get_feature_report(dev, report, sizeof(report)) < 0) {
return SDL_FALSE;
}
SDL_memcpy(data, report, SDL_min(size, sizeof(report)));
return SDL_TRUE;
}
static SDL_bool CheckUSBConnected(hid_device *dev)
{
int i;
Uint8 data[16];
/* This will fail if we're on Bluetooth */
if (ReadFeatureReport(dev, k_ePS4FeatureReportIdSerialNumber, data, sizeof(data))) {
for (i = 0; i < sizeof(data); ++i) {
if (data[i] != 0x00) {
return SDL_TRUE;
}
}
/* Maybe the dongle without a connected controller? */
}
return SDL_FALSE;
}
static SDL_bool HIDAPI_DriverPS4_CanRumble(Uint16 vendor_id, Uint16 product_id)
{
/* The Razer Panthera fight stick hangs when trying to rumble */
if (vendor_id == USB_VENDOR_RAZER &&
(product_id == USB_PRODUCT_RAZER_PANTHERA || product_id == USB_PRODUCT_RAZER_PANTHERA_EVO)) {
return SDL_FALSE;
}
return SDL_TRUE;
}
static void
SetLedsForPlayerIndex(DS4EffectsState_t *effects, int player_index)
{
/* This list is the same as what hid-sony.c uses in the Linux kernel.
The first 4 values correspond to what the PS4 assigns.
*/
static const Uint8 colors[7][3] = {
{ 0x00, 0x00, 0x40 }, /* Blue */
{ 0x40, 0x00, 0x00 }, /* Red */
{ 0x00, 0x40, 0x00 }, /* Green */
{ 0x20, 0x00, 0x20 }, /* Pink */
{ 0x02, 0x01, 0x00 }, /* Orange */
{ 0x00, 0x01, 0x01 }, /* Teal */
{ 0x01, 0x01, 0x01 } /* White */
};
if (player_index >= 0) {
player_index %= SDL_arraysize(colors);
} else {
player_index = 0;
}
effects->ucLedRed = colors[player_index][0];
effects->ucLedGreen = colors[player_index][1];
effects->ucLedBlue = colors[player_index][2];
}
static SDL_bool
HIDAPI_DriverPS4_InitDevice(SDL_HIDAPI_Device *device)
{
return HIDAPI_JoystickConnected(device, NULL);
}
static int
HIDAPI_DriverPS4_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
{
return -1;
}
static int HIDAPI_DriverPS4_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble);
static void
HIDAPI_DriverPS4_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
{
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
if (!ctx) {
return;
}
ctx->player_index = player_index;
/* This will set the new LED state based on the new player index */
HIDAPI_DriverPS4_RumbleJoystick(device, SDL_JoystickFromInstanceID(instance_id), 0, 0);
}
static SDL_bool
HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
SDL_DriverPS4_Context *ctx;
ctx = (SDL_DriverPS4_Context *)SDL_calloc(1, sizeof(*ctx));
if (!ctx) {
SDL_OutOfMemory();
return SDL_FALSE;
}
device->dev = hid_open_path(device->path, 0);
if (!device->dev) {
SDL_free(ctx);
SDL_SetError("Couldn't open %s", device->path);
return SDL_FALSE;
}
device->context = ctx;
/* Check for type of connection */
ctx->is_dongle = (device->vendor_id == USB_VENDOR_SONY && device->product_id == USB_PRODUCT_SONY_DS4_DONGLE);
if (ctx->is_dongle) {
ctx->is_bluetooth = SDL_FALSE;
} else if (device->vendor_id == USB_VENDOR_SONY) {
ctx->is_bluetooth = !CheckUSBConnected(device->dev);
} else {
/* Third party controllers appear to all be wired */
ctx->is_bluetooth = SDL_FALSE;
}
#ifdef DEBUG_PS4
SDL_Log("PS4 dongle = %s, bluetooth = %s\n", ctx->is_dongle ? "TRUE" : "FALSE", ctx->is_bluetooth ? "TRUE" : "FALSE");
#endif
/* Check to see if audio is supported */
if (device->vendor_id == USB_VENDOR_SONY &&
(device->product_id == USB_PRODUCT_SONY_DS4_SLIM || device->product_id == USB_PRODUCT_SONY_DS4_DONGLE)) {
ctx->audio_supported = SDL_TRUE;
}
if (HIDAPI_DriverPS4_CanRumble(device->vendor_id, device->product_id)) {
if (ctx->is_bluetooth) {
ctx->rumble_supported = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, SDL_FALSE);
} else {
ctx->rumble_supported = SDL_TRUE;
}
}
/* Initialize player index (needed for setting LEDs) */
ctx->player_index = SDL_JoystickGetPlayerIndex(joystick);
/* Initialize LED and effect state */
HIDAPI_DriverPS4_RumbleJoystick(device, joystick, 0, 0);
/* Initialize the joystick capabilities */
joystick->nbuttons = 16;
joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
return SDL_TRUE;
}
static int
HIDAPI_DriverPS4_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
{
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
DS4EffectsState_t *effects;
Uint8 data[78];
int report_size, offset;
if (!ctx->rumble_supported) {
return SDL_Unsupported();
}
/* In order to send rumble, we have to send a complete effect packet */
SDL_memset(data, 0, sizeof(data));
if (ctx->is_bluetooth) {
data[0] = k_EPS4ReportIdBluetoothEffects;
data[1] = 0xC0 | 0x04; /* Magic value HID + CRC, also sets interval to 4ms for samples */
data[3] = 0x03; /* 0x1 is rumble, 0x2 is lightbar, 0x4 is the blink interval */
report_size = 78;
offset = 6;
} else {
data[0] = k_EPS4ReportIdUsbEffects;
data[1] = 0x07; /* Magic value */
report_size = 32;
offset = 4;
}
effects = (DS4EffectsState_t *)&data[offset];
effects->ucRumbleLeft = (low_frequency_rumble >> 8);
effects->ucRumbleRight = (high_frequency_rumble >> 8);
/* Populate the LED state with the appropriate color from our lookup table */
SetLedsForPlayerIndex(effects, ctx->player_index);
if (ctx->is_bluetooth) {
/* Bluetooth reports need a CRC at the end of the packet (at least on Linux) */
Uint8 ubHdr = 0xA2; /* hidp header is part of the CRC calculation */
Uint32 unCRC;
unCRC = crc32(0, &ubHdr, 1);
unCRC = crc32(unCRC, data, (Uint32)(report_size - sizeof(unCRC)));
SDL_memcpy(&data[report_size - sizeof(unCRC)], &unCRC, sizeof(unCRC));
}
if (SDL_HIDAPI_SendRumble(device, data, report_size) != report_size) {
return SDL_SetError("Couldn't send rumble packet");
}
return 0;
}
static void
HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS4_Context *ctx, PS4StatePacket_t *packet)
{
Sint16 axis;
if (ctx->last_state.rgucButtonsHatAndCounter[0] != packet->rgucButtonsHatAndCounter[0]) {
{
Uint8 data = (packet->rgucButtonsHatAndCounter[0] >> 4);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
}
{
Uint8 data = (packet->rgucButtonsHatAndCounter[0] & 0x0F);
SDL_bool dpad_up = SDL_FALSE;
SDL_bool dpad_down = SDL_FALSE;
SDL_bool dpad_left = SDL_FALSE;
SDL_bool dpad_right = SDL_FALSE;
switch (data) {
case 0:
dpad_up = SDL_TRUE;
break;
case 1:
dpad_up = SDL_TRUE;
dpad_right = SDL_TRUE;
break;
case 2:
dpad_right = SDL_TRUE;
break;
case 3:
dpad_right = SDL_TRUE;
dpad_down = SDL_TRUE;
break;
case 4:
dpad_down = SDL_TRUE;
break;
case 5:
dpad_left = SDL_TRUE;
dpad_down = SDL_TRUE;
break;
case 6:
dpad_left = SDL_TRUE;
break;
case 7:
dpad_up = SDL_TRUE;
dpad_left = SDL_TRUE;
break;
default:
break;
}
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
}
}
if (ctx->last_state.rgucButtonsHatAndCounter[1] != packet->rgucButtonsHatAndCounter[1]) {
Uint8 data = packet->rgucButtonsHatAndCounter[1];
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x80) ? SDL_PRESSED : SDL_RELEASED);
}
if (ctx->last_state.rgucButtonsHatAndCounter[2] != packet->rgucButtonsHatAndCounter[2]) {
Uint8 data = (packet->rgucButtonsHatAndCounter[2] & 0x03);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, 15, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
}
axis = ((int)packet->ucTriggerLeft * 257) - 32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
axis = ((int)packet->ucTriggerRight * 257) - 32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
axis = ((int)packet->ucLeftJoystickX * 257) - 32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
axis = ((int)packet->ucLeftJoystickY * 257) - 32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
axis = ((int)packet->ucRightJoystickX * 257) - 32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
axis = ((int)packet->ucRightJoystickY * 257) - 32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
if (packet->ucBatteryLevel & 0x10) {
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
} else {
/* Battery level ranges from 0 to 10 */
int level = (packet->ucBatteryLevel & 0xF);
if (level == 0) {
joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY;
} else if (level <= 2) {
joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW;
} else if (level <= 7) {
joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM;
} else {
joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
}
}
SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state));
}
static SDL_bool
HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device)
{
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
SDL_Joystick *joystick = NULL;
Uint8 data[USB_PACKET_LENGTH];
int size;
if (device->num_joysticks > 0) {
joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
}
if (!joystick) {
return SDL_FALSE;
}
while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
switch (data[0]) {
case k_EPS4ReportIdUsbState:
HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, (PS4StatePacket_t *)&data[1]);
break;
case k_EPS4ReportIdBluetoothState:
/* Bluetooth state packets have two additional bytes at the beginning */
HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, (PS4StatePacket_t *)&data[3]);
break;
default:
#ifdef DEBUG_JOYSTICK
SDL_Log("Unknown PS4 packet: 0x%.2x\n", data[0]);
#endif
break;
}
}
if (size < 0) {
/* Read error, device is disconnected */
HIDAPI_JoystickDisconnected(device, joystick->instance_id);
}
return (size >= 0);
}
static void
HIDAPI_DriverPS4_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
hid_close(device->dev);
device->dev = NULL;
SDL_free(device->context);
device->context = NULL;
}
static void
HIDAPI_DriverPS4_FreeDevice(SDL_HIDAPI_Device *device)
{
}
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4 =
{
SDL_HINT_JOYSTICK_HIDAPI_PS4,
SDL_TRUE,
HIDAPI_DriverPS4_IsSupportedDevice,
HIDAPI_DriverPS4_GetDeviceName,
HIDAPI_DriverPS4_InitDevice,
HIDAPI_DriverPS4_GetDevicePlayerIndex,
HIDAPI_DriverPS4_SetDevicePlayerIndex,
HIDAPI_DriverPS4_UpdateDevice,
HIDAPI_DriverPS4_OpenJoystick,
HIDAPI_DriverPS4_RumbleJoystick,
HIDAPI_DriverPS4_CloseJoystick,
HIDAPI_DriverPS4_FreeDevice
};
#endif /* SDL_JOYSTICK_HIDAPI_PS4 */
#endif /* SDL_JOYSTICK_HIDAPI */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,253 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef SDL_JOYSTICK_HIDAPI
/* Handle rumble on a separate thread so it doesn't block the application */
#include "SDL_assert.h"
#include "SDL_thread.h"
#include "SDL_hidapijoystick_c.h"
#include "SDL_hidapi_rumble.h"
#include "../../thread/SDL_systhread.h"
typedef struct SDL_HIDAPI_RumbleRequest
{
SDL_HIDAPI_Device *device;
Uint8 data[2*USB_PACKET_LENGTH]; /* need enough space for the biggest report: dualshock4 is 78 bytes */
int size;
struct SDL_HIDAPI_RumbleRequest *prev;
} SDL_HIDAPI_RumbleRequest;
typedef struct SDL_HIDAPI_RumbleContext
{
SDL_atomic_t initialized;
SDL_atomic_t running;
SDL_Thread *thread;
SDL_mutex *lock;
SDL_sem *request_sem;
SDL_HIDAPI_RumbleRequest *requests_head;
SDL_HIDAPI_RumbleRequest *requests_tail;
} SDL_HIDAPI_RumbleContext;
static SDL_HIDAPI_RumbleContext rumble_context;
static int SDL_HIDAPI_RumbleThread(void *data)
{
SDL_HIDAPI_RumbleContext *ctx = (SDL_HIDAPI_RumbleContext *)data;
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
while (SDL_AtomicGet(&ctx->running)) {
SDL_HIDAPI_RumbleRequest *request = NULL;
SDL_SemWait(ctx->request_sem);
SDL_LockMutex(ctx->lock);
request = ctx->requests_tail;
if (request) {
if (request == ctx->requests_head) {
ctx->requests_head = NULL;
}
ctx->requests_tail = request->prev;
}
SDL_UnlockMutex(ctx->lock);
if (request) {
SDL_LockMutex(request->device->dev_lock);
if (request->device->dev) {
hid_write( request->device->dev, request->data, request->size );
}
SDL_UnlockMutex(request->device->dev_lock);
(void)SDL_AtomicDecRef(&request->device->rumble_pending);
SDL_free(request);
}
}
return 0;
}
static void
SDL_HIDAPI_StopRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
{
SDL_AtomicSet(&ctx->running, SDL_FALSE);
if (ctx->thread) {
int result;
SDL_SemPost(ctx->request_sem);
SDL_WaitThread(ctx->thread, &result);
ctx->thread = NULL;
}
/* This should always be called with an empty queue */
SDL_assert(!ctx->requests_head);
SDL_assert(!ctx->requests_tail);
if (ctx->request_sem) {
SDL_DestroySemaphore(ctx->request_sem);
ctx->request_sem = NULL;
}
if (ctx->lock) {
SDL_DestroyMutex(ctx->lock);
ctx->lock = NULL;
}
SDL_AtomicSet(&ctx->initialized, SDL_FALSE);
}
static int
SDL_HIDAPI_StartRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
{
ctx->lock = SDL_CreateMutex();
if (!ctx->lock) {
SDL_HIDAPI_StopRumbleThread(ctx);
return -1;
}
ctx->request_sem = SDL_CreateSemaphore(0);
if (!ctx->request_sem) {
SDL_HIDAPI_StopRumbleThread(ctx);
return -1;
}
SDL_AtomicSet(&ctx->running, SDL_TRUE);
ctx->thread = SDL_CreateThreadInternal(SDL_HIDAPI_RumbleThread, "HIDAPI Rumble", 0, ctx);
if (!ctx->thread) {
SDL_HIDAPI_StopRumbleThread(ctx);
return -1;
}
return 0;
}
int SDL_HIDAPI_LockRumble(void)
{
SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
if (SDL_AtomicCAS(&ctx->initialized, SDL_FALSE, SDL_TRUE)) {
if (SDL_HIDAPI_StartRumbleThread(ctx) < 0) {
return -1;
}
}
return SDL_LockMutex(ctx->lock);
}
SDL_bool SDL_HIDAPI_GetPendingRumbleLocked(SDL_HIDAPI_Device *device, Uint8 **data, int **size, int *maximum_size)
{
SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
SDL_HIDAPI_RumbleRequest *request;
for (request = ctx->requests_tail; request; request = request->prev) {
if (request->device == device) {
*data = request->data;
*size = &request->size;
*maximum_size = sizeof(request->data);
return SDL_TRUE;
}
}
return SDL_FALSE;
}
int SDL_HIDAPI_SendRumbleAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size)
{
SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
SDL_HIDAPI_RumbleRequest *request;
if (size > sizeof(request->data)) {
SDL_HIDAPI_UnlockRumble();
return SDL_SetError("Couldn't send rumble, size %d is greater than %d", size, (int)sizeof(request->data));
}
request = (SDL_HIDAPI_RumbleRequest *)SDL_calloc(1, sizeof(*request));
if (!request) {
SDL_HIDAPI_UnlockRumble();
return SDL_OutOfMemory();
}
request->device = device;
SDL_memcpy(request->data, data, size);
request->size = size;
SDL_AtomicIncRef(&device->rumble_pending);
if (ctx->requests_head) {
ctx->requests_head->prev = request;
} else {
ctx->requests_tail = request;
}
ctx->requests_head = request;
/* Make sure we unlock before posting the semaphore so the rumble thread can run immediately */
SDL_HIDAPI_UnlockRumble();
SDL_SemPost(ctx->request_sem);
return size;
}
void SDL_HIDAPI_UnlockRumble(void)
{
SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
SDL_UnlockMutex(ctx->lock);
}
int SDL_HIDAPI_SendRumble(SDL_HIDAPI_Device *device, const Uint8 *data, int size)
{
Uint8 *pending_data;
int *pending_size;
int maximum_size;
if (SDL_HIDAPI_LockRumble() < 0) {
return -1;
}
/* check if there is a pending request for the device and update it */
if (SDL_HIDAPI_GetPendingRumbleLocked(device, &pending_data, &pending_size, &maximum_size)) {
if (size > maximum_size) {
SDL_HIDAPI_UnlockRumble();
return SDL_SetError("Couldn't send rumble, size %d is greater than %d", size, maximum_size);
}
SDL_memcpy(pending_data, data, size);
*pending_size = size;
SDL_HIDAPI_UnlockRumble();
return size;
}
return SDL_HIDAPI_SendRumbleAndUnlock(device, data, size);
}
void SDL_HIDAPI_QuitRumble(void)
{
SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
if (SDL_AtomicGet(&ctx->running)) {
SDL_HIDAPI_StopRumbleThread(ctx);
}
}
#endif /* SDL_JOYSTICK_HIDAPI */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,39 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef SDL_JOYSTICK_HIDAPI
/* Handle rumble on a separate thread so it doesn't block the application */
/* Advanced API */
int SDL_HIDAPI_LockRumble(void);
SDL_bool SDL_HIDAPI_GetPendingRumbleLocked(SDL_HIDAPI_Device *device, Uint8 **data, int **size, int *maximum_size);
int SDL_HIDAPI_SendRumbleAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size);
void SDL_HIDAPI_UnlockRumble(void);
/* Simple API, will replace any pending rumble with the new data */
int SDL_HIDAPI_SendRumble(SDL_HIDAPI_Device *device, const Uint8 *data, int size);
void SDL_HIDAPI_QuitRumble(void);
#endif /* SDL_JOYSTICK_HIDAPI */
/* vi: set ts=4 sw=4 expandtab: */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,767 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef SDL_JOYSTICK_HIDAPI
#include "SDL_hints.h"
#include "SDL_log.h"
#include "SDL_events.h"
#include "SDL_timer.h"
#include "SDL_joystick.h"
#include "SDL_gamecontroller.h"
#include "../SDL_sysjoystick.h"
#include "SDL_hidapijoystick_c.h"
#include "SDL_hidapi_rumble.h"
#ifdef SDL_JOYSTICK_HIDAPI_XBOX360
#ifdef __WIN32__
#define SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
/* This requires the Windows 10 SDK to build */
/*#define SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT*/
#endif
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
#include "../../core/windows/SDL_xinput.h"
#endif
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
#include "../../core/windows/SDL_windows.h"
#define COBJMACROS
#include "windows.gaming.input.h"
#endif
typedef struct {
Uint8 last_state[USB_PACKET_LENGTH];
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
SDL_bool xinput_enabled;
Uint8 xinput_slot;
#endif
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
SDL_bool coinitialized;
__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics *gamepad_statics;
__x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
struct __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration;
#endif
} SDL_DriverXbox360_Context;
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
static Uint8 xinput_slots;
static void
HIDAPI_DriverXbox360_MarkXInputSlotUsed(Uint8 xinput_slot)
{
if (xinput_slot != XUSER_INDEX_ANY) {
xinput_slots |= (0x01 << xinput_slot);
}
}
static void
HIDAPI_DriverXbox360_MarkXInputSlotFree(Uint8 xinput_slot)
{
if (xinput_slot != XUSER_INDEX_ANY) {
xinput_slots &= ~(0x01 << xinput_slot);
}
}
static SDL_bool
HIDAPI_DriverXbox360_MissingXInputSlot()
{
return xinput_slots != 0x0F;
}
static Uint8
HIDAPI_DriverXbox360_GuessXInputSlot(WORD wButtons)
{
DWORD user_index;
int match_count;
Uint8 match_slot;
if (!XINPUTGETSTATE) {
return XUSER_INDEX_ANY;
}
match_count = 0;
for (user_index = 0; user_index < XUSER_MAX_COUNT; ++user_index) {
XINPUT_STATE_EX xinput_state;
if (XINPUTGETSTATE(user_index, &xinput_state) == ERROR_SUCCESS) {
if (xinput_state.Gamepad.wButtons == wButtons) {
++match_count;
match_slot = (Uint8)user_index;
}
}
}
if (match_count == 1) {
return match_slot;
}
return XUSER_INDEX_ANY;
}
#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
static void
HIDAPI_DriverXbox360_InitWindowsGamingInput(SDL_DriverXbox360_Context *ctx)
{
/* I think this takes care of RoInitialize() in a way that is compatible with the rest of SDL */
if (FAILED(WIN_CoInitialize())) {
return;
}
ctx->coinitialized = SDL_TRUE;
{
static const IID SDL_IID_IGamepadStatics = { 0x8BBCE529, 0xD49C, 0x39E9, { 0x95, 0x60, 0xE4, 0x7D, 0xDE, 0x96, 0xB7, 0xC8 } };
HRESULT hr;
HMODULE hModule = LoadLibraryA("combase.dll");
if (hModule != NULL) {
typedef HRESULT (WINAPI *WindowsCreateString_t)(PCNZWCH sourceString, UINT32 length, HSTRING* string);
typedef HRESULT (WINAPI *WindowsDeleteString_t)(HSTRING string);
typedef HRESULT (WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void** factory);
WindowsCreateString_t WindowsCreateStringFunc = (WindowsCreateString_t)GetProcAddress(hModule, "WindowsCreateString");
WindowsDeleteString_t WindowsDeleteStringFunc = (WindowsDeleteString_t)GetProcAddress(hModule, "WindowsDeleteString");
RoGetActivationFactory_t RoGetActivationFactoryFunc = (RoGetActivationFactory_t)GetProcAddress(hModule, "RoGetActivationFactory");
if (WindowsCreateStringFunc && WindowsDeleteStringFunc && RoGetActivationFactoryFunc) {
LPTSTR pNamespace = L"Windows.Gaming.Input.Gamepad";
HSTRING hNamespaceString;
hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString);
if (SUCCEEDED(hr)) {
RoGetActivationFactoryFunc(hNamespaceString, &SDL_IID_IGamepadStatics, &ctx->gamepad_statics);
WindowsDeleteStringFunc(hNamespaceString);
}
}
FreeLibrary(hModule);
}
}
}
static Uint8
HIDAPI_DriverXbox360_GetGamepadButtonsForMatch(__x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad)
{
HRESULT hr;
struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading state;
Uint8 buttons = 0;
hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_GetCurrentReading(gamepad, &state);
if (SUCCEEDED(hr)) {
if (state.Buttons & GamepadButtons_A) {
buttons |= (1 << SDL_CONTROLLER_BUTTON_A);
}
if (state.Buttons & GamepadButtons_B) {
buttons |= (1 << SDL_CONTROLLER_BUTTON_B);
}
if (state.Buttons & GamepadButtons_X) {
buttons |= (1 << SDL_CONTROLLER_BUTTON_X);
}
if (state.Buttons & GamepadButtons_Y) {
buttons |= (1 << SDL_CONTROLLER_BUTTON_Y);
}
}
return buttons;
}
static void
HIDAPI_DriverXbox360_GuessGamepad(SDL_DriverXbox360_Context *ctx, Uint8 buttons)
{
HRESULT hr;
__FIVectorView_1_Windows__CGaming__CInput__CGamepad *gamepads;
hr = __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_get_Gamepads(ctx->gamepad_statics, &gamepads);
if (SUCCEEDED(hr)) {
unsigned int i, num_gamepads;
hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_get_Size(gamepads, &num_gamepads);
if (SUCCEEDED(hr)) {
int match_count;
unsigned int match_slot;
match_count = 0;
for (i = 0; i < num_gamepads; ++i) {
__x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_GetAt(gamepads, i, &gamepad);
if (SUCCEEDED(hr)) {
Uint8 gamepad_buttons = HIDAPI_DriverXbox360_GetGamepadButtonsForMatch(gamepad);
if (buttons == gamepad_buttons) {
++match_count;
match_slot = i;
}
__x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad);
}
}
if (match_count == 1) {
hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_GetAt(gamepads, match_slot, &ctx->gamepad);
if (SUCCEEDED(hr)) {
}
}
}
__FIVectorView_1_Windows__CGaming__CInput__CGamepad_Release(gamepads);
}
}
static void
HIDAPI_DriverXbox360_QuitWindowsGamingInput(SDL_DriverXbox360_Context *ctx)
{
if (ctx->gamepad_statics) {
__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(ctx->gamepad_statics);
ctx->gamepad_statics = NULL;
}
if (ctx->gamepad) {
__x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(ctx->gamepad);
ctx->gamepad = NULL;
}
if (ctx->coinitialized) {
WIN_CoUninitialize();
ctx->coinitialized = SDL_FALSE;
}
}
#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
#if defined(__MACOSX__)
static SDL_bool
IsBluetoothXboxOneController(Uint16 vendor_id, Uint16 product_id)
{
/* Check to see if it's the Xbox One S or Xbox One Elite Series 2 in Bluetooth mode */
if (vendor_id == USB_VENDOR_MICROSOFT) {
if (product_id == USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH ||
product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLUETOOTH ||
product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH) {
return SDL_TRUE;
}
}
return SDL_FALSE;
}
#endif
static SDL_bool
HIDAPI_DriverXbox360_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
{
const int XB360W_IFACE_PROTOCOL = 129; /* Wireless */
if (vendor_id == USB_VENDOR_NVIDIA) {
/* This is the NVIDIA Shield controller which doesn't talk Xbox controller protocol */
return SDL_FALSE;
}
if ((vendor_id == USB_VENDOR_MICROSOFT && (product_id == 0x0291 || product_id == 0x0719)) ||
(type == SDL_CONTROLLER_TYPE_XBOX360 && interface_protocol == XB360W_IFACE_PROTOCOL)) {
/* This is the wireless dongle, which talks a different protocol */
return SDL_FALSE;
}
if (interface_number > 0) {
/* This is the chatpad or other input interface, not the Xbox 360 interface */
return SDL_FALSE;
}
#if defined(__MACOSX__) || defined(__WIN32__)
if (vendor_id == USB_VENDOR_MICROSOFT && product_id == 0x028e && version == 1) {
/* This is the Steam Virtual Gamepad, which isn't supported by this driver */
return SDL_FALSE;
}
#if defined(__MACOSX__)
/* Wired Xbox One controllers are handled by this driver, interfacing with
the 360Controller driver available from:
https://github.com/360Controller/360Controller/releases
Bluetooth Xbox One controllers are handled by the SDL Xbox One driver
*/
if (IsBluetoothXboxOneController(vendor_id, product_id)) {
return SDL_FALSE;
}
#endif
return (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE);
#else
return (type == SDL_CONTROLLER_TYPE_XBOX360);
#endif
}
static const char *
HIDAPI_DriverXbox360_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
{
return NULL;
}
static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot)
{
Uint8 mode = 0x02 + slot;
const Uint8 led_packet[] = { 0x01, 0x03, mode };
if (hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) {
return SDL_FALSE;
}
return SDL_TRUE;
}
static SDL_bool
HIDAPI_DriverXbox360_InitDevice(SDL_HIDAPI_Device *device)
{
return HIDAPI_JoystickConnected(device, NULL);
}
static int
HIDAPI_DriverXbox360_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
{
return -1;
}
static void
HIDAPI_DriverXbox360_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
{
if (device->dev) {
SetSlotLED(device->dev, (player_index % 4));
}
}
static SDL_bool
HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
SDL_DriverXbox360_Context *ctx;
int player_index;
ctx = (SDL_DriverXbox360_Context *)SDL_calloc(1, sizeof(*ctx));
if (!ctx) {
SDL_OutOfMemory();
return SDL_FALSE;
}
device->dev = hid_open_path(device->path, 0);
if (!device->dev) {
SDL_free(ctx);
SDL_SetError("Couldn't open %s", device->path);
return SDL_FALSE;
}
device->context = ctx;
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
ctx->xinput_enabled = SDL_GetHintBoolean(SDL_HINT_XINPUT_ENABLED, SDL_TRUE);
if (ctx->xinput_enabled && WIN_LoadXInputDLL() < 0) {
ctx->xinput_enabled = SDL_FALSE;
}
ctx->xinput_slot = XUSER_INDEX_ANY;
#endif
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
HIDAPI_DriverXbox360_InitWindowsGamingInput(ctx);
#endif
/* Set the controller LED */
player_index = SDL_JoystickGetPlayerIndex(joystick);
if (player_index >= 0) {
SetSlotLED(device->dev, (player_index % 4));
}
/* Initialize the joystick capabilities */
joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
return SDL_TRUE;
}
static int
HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
{
#if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT)
SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
#endif
#ifdef __WIN32__
SDL_bool rumbled = SDL_FALSE;
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
if (!rumbled && ctx->gamepad) {
HRESULT hr;
ctx->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16;
ctx->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16;
hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(ctx->gamepad, ctx->vibration);
if (SUCCEEDED(hr)) {
rumbled = SDL_TRUE;
}
}
#endif
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
if (!rumbled && ctx->xinput_slot != XUSER_INDEX_ANY) {
XINPUT_VIBRATION XVibration;
if (!XINPUTSETSTATE) {
return SDL_Unsupported();
}
XVibration.wLeftMotorSpeed = low_frequency_rumble;
XVibration.wRightMotorSpeed = high_frequency_rumble;
if (XINPUTSETSTATE(ctx->xinput_slot, &XVibration) == ERROR_SUCCESS) {
rumbled = SDL_TRUE;
} else {
return SDL_SetError("XInputSetState() failed");
}
}
#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
#else /* !__WIN32__ */
#ifdef __MACOSX__
if (IsBluetoothXboxOneController(device->vendor_id, device->product_id)) {
Uint8 rumble_packet[] = { 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00 };
rumble_packet[4] = (low_frequency_rumble >> 8);
rumble_packet[5] = (high_frequency_rumble >> 8);
if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
return SDL_SetError("Couldn't send rumble packet");
}
} else {
/* On Mac OS X the 360Controller driver uses this short report,
and we need to prefix it with a magic token so hidapi passes it through untouched
*/
Uint8 rumble_packet[] = { 'M', 'A', 'G', 'I', 'C', '0', 0x00, 0x04, 0x00, 0x00 };
rumble_packet[6+2] = (low_frequency_rumble >> 8);
rumble_packet[6+3] = (high_frequency_rumble >> 8);
if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
return SDL_SetError("Couldn't send rumble packet");
}
}
#else
Uint8 rumble_packet[] = { 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
rumble_packet[3] = (low_frequency_rumble >> 8);
rumble_packet[4] = (high_frequency_rumble >> 8);
if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
return SDL_SetError("Couldn't send rumble packet");
}
#endif
#endif /* __WIN32__ */
return 0;
}
#ifdef __WIN32__
/* This is the packet format for Xbox 360 and Xbox One controllers on Windows,
however with this interface there is no rumble support, no guide button,
and the left and right triggers are tied together as a single axis.
We use XInput and Windows.Gaming.Input to make up for these shortcomings.
*/
static void
HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
{
Sint16 axis;
SDL_bool has_trigger_data = SDL_FALSE;
if (ctx->last_state[10] != data[10]) {
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[10] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[10] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[10] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[10] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[10] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[10] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[10] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[10] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
}
if (ctx->last_state[11] != data[11]) {
SDL_bool dpad_up = SDL_FALSE;
SDL_bool dpad_down = SDL_FALSE;
SDL_bool dpad_left = SDL_FALSE;
SDL_bool dpad_right = SDL_FALSE;
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[11] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[11] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
switch (data[11] & 0x3C) {
case 4:
dpad_up = SDL_TRUE;
break;
case 8:
dpad_up = SDL_TRUE;
dpad_right = SDL_TRUE;
break;
case 12:
dpad_right = SDL_TRUE;
break;
case 16:
dpad_right = SDL_TRUE;
dpad_down = SDL_TRUE;
break;
case 20:
dpad_down = SDL_TRUE;
break;
case 24:
dpad_left = SDL_TRUE;
dpad_down = SDL_TRUE;
break;
case 28:
dpad_left = SDL_TRUE;
break;
case 32:
dpad_up = SDL_TRUE;
dpad_left = SDL_TRUE;
break;
default:
break;
}
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
}
axis = (int)*(Uint16*)(&data[0]) - 0x8000;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
axis = (int)*(Uint16*)(&data[2]) - 0x8000;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
axis = (int)*(Uint16*)(&data[4]) - 0x8000;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
axis = (int)*(Uint16*)(&data[6]) - 0x8000;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
if (ctx->gamepad_statics && !ctx->gamepad) {
Uint8 buttons = 0;
if (data[10] & 0x01) {
buttons |= (1 << SDL_CONTROLLER_BUTTON_A);
}
if (data[10] & 0x02) {
buttons |= (1 << SDL_CONTROLLER_BUTTON_B);
}
if (data[10] & 0x04) {
buttons |= (1 << SDL_CONTROLLER_BUTTON_X);
}
if (data[10] & 0x08) {
buttons |= (1 << SDL_CONTROLLER_BUTTON_Y);
}
if (buttons != 0) {
HIDAPI_DriverXbox360_GuessGamepad(ctx, buttons);
}
}
if (ctx->gamepad) {
HRESULT hr;
struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading state;
hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_GetCurrentReading(ctx->gamepad, &state);
if (SUCCEEDED(hr)) {
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (state.Buttons & 0x40000000) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)(state.LeftTrigger * SDL_MAX_UINT16)) - 32768);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)(state.RightTrigger * SDL_MAX_UINT16)) - 32768);
has_trigger_data = SDL_TRUE;
}
}
#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
if (ctx->xinput_enabled) {
if (ctx->xinput_slot == XUSER_INDEX_ANY && HIDAPI_DriverXbox360_MissingXInputSlot()) {
WORD wButtons = 0;
if (data[10] & 0x01) {
wButtons |= XINPUT_GAMEPAD_A;
}
if (data[10] & 0x02) {
wButtons |= XINPUT_GAMEPAD_B;
}
if (data[10] & 0x04) {
wButtons |= XINPUT_GAMEPAD_X;
}
if (data[10] & 0x08) {
wButtons |= XINPUT_GAMEPAD_Y;
}
if (wButtons != 0) {
Uint8 xinput_slot = HIDAPI_DriverXbox360_GuessXInputSlot(wButtons);
if (xinput_slot != XUSER_INDEX_ANY) {
HIDAPI_DriverXbox360_MarkXInputSlotUsed(xinput_slot);
ctx->xinput_slot = xinput_slot;
}
}
}
if (!has_trigger_data && ctx->xinput_slot != XUSER_INDEX_ANY) {
XINPUT_STATE_EX xinput_state;
if (XINPUTGETSTATE(ctx->xinput_slot, &xinput_state) == ERROR_SUCCESS) {
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)xinput_state.Gamepad.bLeftTrigger * 257) - 32768);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)xinput_state.Gamepad.bRightTrigger * 257) - 32768);
has_trigger_data = SDL_TRUE;
}
}
}
#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
if (!has_trigger_data) {
axis = (data[9] * 257) - 32768;
if (data[9] < 0x80) {
axis = -axis * 2 - 32769;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
} else if (data[9] > 0x80) {
axis = axis * 2 - 32767;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
} else {
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_MIN_SINT16);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_MIN_SINT16);
}
}
SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
}
#else
static void
HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
{
Sint16 axis;
#ifdef __MACOSX__
const SDL_bool invert_y_axes = SDL_FALSE;
#else
const SDL_bool invert_y_axes = SDL_TRUE;
#endif
if (ctx->last_state[2] != data[2]) {
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[2] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[2] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[2] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[2] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[2] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
}
if (ctx->last_state[3] != data[3]) {
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[3] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[3] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[3] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[3] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[3] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[3] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[3] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
}
axis = ((int)data[4] * 257) - 32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
axis = ((int)data[5] * 257) - 32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
axis = *(Sint16*)(&data[6]);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
axis = *(Sint16*)(&data[8]);
if (invert_y_axes) {
axis = ~axis;
}
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
axis = *(Sint16*)(&data[10]);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
axis = *(Sint16*)(&data[12]);
if (invert_y_axes) {
axis = ~axis;
}
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
}
#endif /* __WIN32__ */
static SDL_bool
HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device *device)
{
SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
SDL_Joystick *joystick = NULL;
Uint8 data[USB_PACKET_LENGTH];
int size;
if (device->num_joysticks > 0) {
joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
}
if (!joystick) {
return SDL_FALSE;
}
while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
HIDAPI_DriverXbox360_HandleStatePacket(joystick, device->dev, ctx, data, size);
}
if (size < 0) {
/* Read error, device is disconnected */
HIDAPI_JoystickDisconnected(device, joystick->instance_id);
}
return (size >= 0);
}
static void
HIDAPI_DriverXbox360_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
#if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT)
SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
#endif
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
if (ctx->xinput_enabled) {
HIDAPI_DriverXbox360_MarkXInputSlotFree(ctx->xinput_slot);
WIN_UnloadXInputDLL();
}
#endif
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
HIDAPI_DriverXbox360_QuitWindowsGamingInput(ctx);
#endif
hid_close(device->dev);
device->dev = NULL;
SDL_free(device->context);
device->context = NULL;
}
static void
HIDAPI_DriverXbox360_FreeDevice(SDL_HIDAPI_Device *device)
{
}
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 =
{
SDL_HINT_JOYSTICK_HIDAPI_XBOX,
SDL_TRUE,
HIDAPI_DriverXbox360_IsSupportedDevice,
HIDAPI_DriverXbox360_GetDeviceName,
HIDAPI_DriverXbox360_InitDevice,
HIDAPI_DriverXbox360_GetDevicePlayerIndex,
HIDAPI_DriverXbox360_SetDevicePlayerIndex,
HIDAPI_DriverXbox360_UpdateDevice,
HIDAPI_DriverXbox360_OpenJoystick,
HIDAPI_DriverXbox360_RumbleJoystick,
HIDAPI_DriverXbox360_CloseJoystick,
HIDAPI_DriverXbox360_FreeDevice
};
#endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */
#endif /* SDL_JOYSTICK_HIDAPI */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,306 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef SDL_JOYSTICK_HIDAPI
#include "SDL_hints.h"
#include "SDL_log.h"
#include "SDL_events.h"
#include "SDL_timer.h"
#include "SDL_joystick.h"
#include "SDL_gamecontroller.h"
#include "../SDL_sysjoystick.h"
#include "SDL_hidapijoystick_c.h"
#include "SDL_hidapi_rumble.h"
#ifdef SDL_JOYSTICK_HIDAPI_XBOX360
typedef struct {
SDL_bool connected;
Uint8 last_state[USB_PACKET_LENGTH];
} SDL_DriverXbox360W_Context;
static SDL_bool
HIDAPI_DriverXbox360W_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
{
const int XB360W_IFACE_PROTOCOL = 129; /* Wireless */
if ((vendor_id == USB_VENDOR_MICROSOFT && (product_id == 0x0291 || product_id == 0x02a9 || product_id == 0x0719)) ||
(type == SDL_CONTROLLER_TYPE_XBOX360 && interface_protocol == XB360W_IFACE_PROTOCOL)) {
return SDL_TRUE;
}
return SDL_FALSE;
}
static const char *
HIDAPI_DriverXbox360W_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
{
return "Xbox 360 Wireless Controller";
}
static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot)
{
Uint8 mode = 0x02 + slot;
const Uint8 led_packet[] = { 0x00, 0x00, 0x08, (0x40 + (mode % 0x0e)), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
if (hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) {
return SDL_FALSE;
}
return SDL_TRUE;
}
static void
UpdatePowerLevel(SDL_Joystick *joystick, Uint8 level)
{
float normalized_level = (float)level / 255.0f;
if (normalized_level <= 0.05f) {
joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY;
} else if (normalized_level <= 0.20f) {
joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW;
} else if (normalized_level <= 0.70f) {
joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM;
} else {
joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
}
}
static SDL_bool
HIDAPI_DriverXbox360W_InitDevice(SDL_HIDAPI_Device *device)
{
SDL_DriverXbox360W_Context *ctx;
/* Requests controller presence information from the wireless dongle */
const Uint8 init_packet[] = { 0x08, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
ctx = (SDL_DriverXbox360W_Context *)SDL_calloc(1, sizeof(*ctx));
if (!ctx) {
SDL_OutOfMemory();
return SDL_FALSE;
}
device->dev = hid_open_path(device->path, 0);
if (!device->dev) {
SDL_free(ctx);
SDL_SetError("Couldn't open %s", device->path);
return SDL_FALSE;
}
device->context = ctx;
if (hid_write(device->dev, init_packet, sizeof(init_packet)) != sizeof(init_packet)) {
SDL_SetError("Couldn't write init packet");
return SDL_FALSE;
}
return SDL_TRUE;
}
static int
HIDAPI_DriverXbox360W_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
{
return -1;
}
static void
HIDAPI_DriverXbox360W_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
{
SetSlotLED(device->dev, (player_index % 4));
}
static SDL_bool
HIDAPI_DriverXbox360W_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
SDL_DriverXbox360W_Context *ctx = (SDL_DriverXbox360W_Context *)device->context;
SDL_zeroa(ctx->last_state);
/* Initialize the joystick capabilities */
joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
joystick->epowerlevel = SDL_JOYSTICK_POWER_UNKNOWN;
return SDL_TRUE;
}
static int
HIDAPI_DriverXbox360W_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
{
Uint8 rumble_packet[] = { 0x00, 0x01, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
rumble_packet[5] = (low_frequency_rumble >> 8);
rumble_packet[6] = (high_frequency_rumble >> 8);
if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
return SDL_SetError("Couldn't send rumble packet");
}
return 0;
}
static void
HIDAPI_DriverXbox360W_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360W_Context *ctx, Uint8 *data, int size)
{
Sint16 axis;
const SDL_bool invert_y_axes = SDL_TRUE;
if (ctx->last_state[2] != data[2]) {
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[2] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[2] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[2] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[2] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[2] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
}
if (ctx->last_state[3] != data[3]) {
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[3] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[3] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[3] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[3] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[3] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[3] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[3] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
}
axis = ((int)data[4] * 257) - 32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
axis = ((int)data[5] * 257) - 32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
axis = *(Sint16*)(&data[6]);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
axis = *(Sint16*)(&data[8]);
if (invert_y_axes) {
axis = ~axis;
}
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
axis = *(Sint16*)(&data[10]);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
axis = *(Sint16*)(&data[12]);
if (invert_y_axes) {
axis = ~axis;
}
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
}
static SDL_bool
HIDAPI_DriverXbox360W_UpdateDevice(SDL_HIDAPI_Device *device)
{
SDL_DriverXbox360W_Context *ctx = (SDL_DriverXbox360W_Context *)device->context;
SDL_Joystick *joystick = NULL;
Uint8 data[USB_PACKET_LENGTH];
int size;
if (device->num_joysticks > 0) {
joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
}
while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
if (size == 2 && data[0] == 0x08) {
SDL_bool connected = (data[1] & 0x80) ? SDL_TRUE : SDL_FALSE;
#ifdef DEBUG_JOYSTICK
SDL_Log("Connected = %s\n", connected ? "TRUE" : "FALSE");
#endif
if (connected != ctx->connected) {
ctx->connected = connected;
if (connected) {
SDL_JoystickID joystickID;
HIDAPI_JoystickConnected(device, &joystickID);
} else if (device->num_joysticks > 0) {
HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
}
}
} else if (size == 29 && data[0] == 0x00 && data[1] == 0x0f && data[2] == 0x00 && data[3] == 0xf0) {
/* Serial number is data[7-13] */
#ifdef DEBUG_JOYSTICK
SDL_Log("Battery status (initial): %d\n", data[17]);
#endif
if (joystick) {
UpdatePowerLevel(joystick, data[17]);
}
} else if (size == 29 && data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x00 && data[3] == 0x13) {
#ifdef DEBUG_JOYSTICK
SDL_Log("Battery status: %d\n", data[4]);
#endif
if (joystick) {
UpdatePowerLevel(joystick, data[4]);
}
} else if (size == 29 && data[0] == 0x00 && (data[1] & 0x01) == 0x01) {
if (joystick) {
HIDAPI_DriverXbox360W_HandleStatePacket(joystick, device->dev, ctx, data+4, size-4);
}
}
}
if (joystick) {
if (size < 0) {
/* Read error, device is disconnected */
HIDAPI_JoystickDisconnected(device, joystick->instance_id);
}
}
return (size >= 0);
}
static void
HIDAPI_DriverXbox360W_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
}
static void
HIDAPI_DriverXbox360W_FreeDevice(SDL_HIDAPI_Device *device)
{
hid_close(device->dev);
device->dev = NULL;
SDL_free(device->context);
device->context = NULL;
}
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360W =
{
SDL_HINT_JOYSTICK_HIDAPI_XBOX,
SDL_TRUE,
HIDAPI_DriverXbox360W_IsSupportedDevice,
HIDAPI_DriverXbox360W_GetDeviceName,
HIDAPI_DriverXbox360W_InitDevice,
HIDAPI_DriverXbox360W_GetDevicePlayerIndex,
HIDAPI_DriverXbox360W_SetDevicePlayerIndex,
HIDAPI_DriverXbox360W_UpdateDevice,
HIDAPI_DriverXbox360W_OpenJoystick,
HIDAPI_DriverXbox360W_RumbleJoystick,
HIDAPI_DriverXbox360W_CloseJoystick,
HIDAPI_DriverXbox360W_FreeDevice
};
#endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */
#endif /* SDL_JOYSTICK_HIDAPI */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,708 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef SDL_JOYSTICK_HIDAPI
#include "SDL_hints.h"
#include "SDL_log.h"
#include "SDL_events.h"
#include "SDL_timer.h"
#include "SDL_joystick.h"
#include "SDL_gamecontroller.h"
#include "../SDL_sysjoystick.h"
#include "SDL_hidapijoystick_c.h"
#include "SDL_hidapi_rumble.h"
#ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
/* Define this if you want to log all packets from the controller */
/*#define DEBUG_XBOX_PROTOCOL*/
/* The amount of time to wait after hotplug to send controller init sequence */
#define CONTROLLER_INIT_DELAY_MS 1500 /* 475 for Xbox One S, 1275 for the PDP Battlefield 1 */
/* Connect controller */
static const Uint8 xboxone_init0[] = {
0x04, 0x20, 0x00, 0x00
};
/* Initial ack */
static const Uint8 xboxone_init1[] = {
0x01, 0x20, 0x01, 0x09, 0x00, 0x04, 0x20, 0x3a,
0x00, 0x00, 0x00, 0x80, 0x00
};
/* Start controller - extended? */
static const Uint8 xboxone_init2[] = {
0x05, 0x20, 0x00, 0x0F, 0x06, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x55, 0x53, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00
};
/* Start controller with input */
static const Uint8 xboxone_init3[] = {
0x05, 0x20, 0x03, 0x01, 0x00
};
/* Enable LED */
static const Uint8 xboxone_init4[] = {
0x0A, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14
};
/* Start input reports? */
static const Uint8 xboxone_init5[] = {
0x06, 0x20, 0x00, 0x02, 0x01, 0x00
};
/* Start rumble? */
static const Uint8 xboxone_init6[] = {
0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
0x00, 0x00, 0xFF, 0x00, 0xEB
};
/*
* This specifies the selection of init packets that a gamepad
* will be sent on init *and* the order in which they will be
* sent. The correct sequence number will be added when the
* packet is going to be sent.
*/
typedef struct {
Uint16 vendor_id;
Uint16 product_id;
Uint16 exclude_vendor_id;
Uint16 exclude_product_id;
const Uint8 *data;
int size;
const Uint8 response[2];
} SDL_DriverXboxOne_InitPacket;
static const SDL_DriverXboxOne_InitPacket xboxone_init_packets[] = {
{ 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init0, sizeof(xboxone_init0), { 0x04, 0xf0 } },
{ 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init1, sizeof(xboxone_init1), { 0x04, 0xb0 } },
{ 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init2, sizeof(xboxone_init2), { 0x00, 0x00 } },
{ 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init3, sizeof(xboxone_init3), { 0x00, 0x00 } },
{ 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init4, sizeof(xboxone_init4), { 0x00, 0x00 } },
/* These next packets are required for third party controllers (PowerA, PDP, HORI),
but aren't the correct protocol for Microsoft Xbox controllers.
*/
{ 0x0000, 0x0000, 0x045e, 0x0000, xboxone_init5, sizeof(xboxone_init5), { 0x00, 0x00 } },
{ 0x0000, 0x0000, 0x045e, 0x0000, xboxone_init6, sizeof(xboxone_init6), { 0x00, 0x00 } },
};
typedef enum {
XBOX_ONE_WIRELESS_PROTOCOL_UNKNOWN,
XBOX_ONE_WIRELESS_PROTOCOL_V1,
XBOX_ONE_WIRELESS_PROTOCOL_V2,
} SDL_XboxOneWirelessProtocol;
typedef struct {
Uint16 vendor_id;
Uint16 product_id;
SDL_bool bluetooth;
SDL_XboxOneWirelessProtocol wireless_protocol;
SDL_bool initialized;
Uint32 start_time;
Uint8 sequence;
Uint8 last_state[USB_PACKET_LENGTH];
SDL_bool has_paddles;
} SDL_DriverXboxOne_Context;
#ifdef DEBUG_XBOX_PROTOCOL
static void
DumpPacket(const char *prefix, Uint8 *data, int size)
{
int i;
char buffer[5*USB_PACKET_LENGTH];
SDL_snprintf(buffer, sizeof(buffer), prefix, size);
for (i = 0; i < size; ++i) {
if ((i % 8) == 0) {
SDL_snprintf(&buffer[SDL_strlen(buffer)], sizeof(buffer) - SDL_strlen(buffer), "\n%.2d: ", i);
}
SDL_snprintf(&buffer[SDL_strlen(buffer)], sizeof(buffer) - SDL_strlen(buffer), " 0x%.2x", data[i]);
}
SDL_strlcat(buffer, "\n", sizeof(buffer));
SDL_Log("%s", buffer);
}
#endif /* DEBUG_XBOX_PROTOCOL */
static SDL_bool
IsBluetoothXboxOneController(Uint16 vendor_id, Uint16 product_id)
{
/* Check to see if it's the Xbox One S or Xbox One Elite Series 2 in Bluetooth mode */
if (vendor_id == USB_VENDOR_MICROSOFT) {
if (product_id == USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH ||
product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLUETOOTH ||
product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH) {
return SDL_TRUE;
}
}
return SDL_FALSE;
}
static SDL_bool
ControllerHasPaddles(Uint16 vendor_id, Uint16 product_id)
{
if (vendor_id == USB_VENDOR_MICROSOFT) {
if (product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_1 ||
product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2) {
return SDL_TRUE;
}
}
return SDL_FALSE;
}
/* Return true if this controller sends the 0x02 "waiting for init" packet */
static SDL_bool
ControllerSendsWaitingForInit(Uint16 vendor_id, Uint16 product_id)
{
if (vendor_id == USB_VENDOR_HYPERKIN) {
/* The Hyperkin controllers always send 0x02 when waiting for init,
and the Hyperkin Duke plays an Xbox startup animation, so we want
to make sure we don't send the init sequence if it isn't needed.
*/
return SDL_TRUE;
}
if (vendor_id == USB_VENDOR_PDP) {
/* The PDP Rock Candy (PID 0x0246) doesn't send 0x02 on Linux for some reason */
return SDL_FALSE;
}
/* It doesn't hurt to reinit, especially if a driver has misconfigured the controller */
/*return SDL_TRUE;*/
return SDL_FALSE;
}
static SDL_bool
SendControllerInit(SDL_HIDAPI_Device *device, SDL_DriverXboxOne_Context *ctx)
{
Uint16 vendor_id = ctx->vendor_id;
Uint16 product_id = ctx->product_id;
int i;
Uint8 init_packet[USB_PACKET_LENGTH];
for (i = 0; i < SDL_arraysize(xboxone_init_packets); ++i) {
const SDL_DriverXboxOne_InitPacket *packet = &xboxone_init_packets[i];
if (packet->vendor_id && (vendor_id != packet->vendor_id)) {
continue;
}
if (packet->product_id && (product_id != packet->product_id)) {
continue;
}
if (packet->exclude_vendor_id && (vendor_id == packet->exclude_vendor_id)) {
continue;
}
if (packet->exclude_product_id && (product_id == packet->exclude_product_id)) {
continue;
}
SDL_memcpy(init_packet, packet->data, packet->size);
if (init_packet[0] != 0x01) {
init_packet[2] = ctx->sequence++;
}
if (hid_write(device->dev, init_packet, packet->size) != packet->size) {
SDL_SetError("Couldn't write Xbox One initialization packet");
return SDL_FALSE;
}
if (packet->response[0]) {
const Uint32 RESPONSE_TIMEOUT_MS = 50;
Uint32 start = SDL_GetTicks();
SDL_bool got_response = SDL_FALSE;
while (!got_response && !SDL_TICKS_PASSED(SDL_GetTicks(), start + RESPONSE_TIMEOUT_MS)) {
Uint8 data[USB_PACKET_LENGTH];
int size;
while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
#ifdef DEBUG_XBOX_PROTOCOL
DumpPacket("Xbox One INIT packet: size = %d", data, size);
#endif
if (size >= 2 && data[0] == packet->response[0] && data[1] == packet->response[1]) {
got_response = SDL_TRUE;
}
}
}
#ifdef DEBUG_XBOX_PROTOCOL
SDL_Log("Init sequence %d got response: %s\n", i, got_response ? "TRUE" : "FALSE");
#endif
}
}
return SDL_TRUE;
}
static SDL_bool
HIDAPI_DriverXboxOne_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
{
#ifdef __LINUX__
if (vendor_id == USB_VENDOR_POWERA && product_id == 0x541a) {
/* The PowerA Mini controller, model 1240245-01, blocks while writing feature reports */
return SDL_FALSE;
}
#endif
#ifdef __MACOSX__
/* Wired Xbox One controllers are handled by the 360Controller driver */
if (!IsBluetoothXboxOneController(vendor_id, product_id)) {
return SDL_FALSE;
}
#endif
return (type == SDL_CONTROLLER_TYPE_XBOXONE);
}
static const char *
HIDAPI_DriverXboxOne_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
{
return NULL;
}
static SDL_bool
HIDAPI_DriverXboxOne_InitDevice(SDL_HIDAPI_Device *device)
{
return HIDAPI_JoystickConnected(device, NULL);
}
static int
HIDAPI_DriverXboxOne_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
{
return -1;
}
static void
HIDAPI_DriverXboxOne_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
{
}
static SDL_bool
HIDAPI_DriverXboxOne_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
SDL_DriverXboxOne_Context *ctx;
ctx = (SDL_DriverXboxOne_Context *)SDL_calloc(1, sizeof(*ctx));
if (!ctx) {
SDL_OutOfMemory();
return SDL_FALSE;
}
device->dev = hid_open_path(device->path, 0);
if (!device->dev) {
SDL_free(ctx);
SDL_SetError("Couldn't open %s", device->path);
return SDL_FALSE;
}
device->context = ctx;
ctx->vendor_id = device->vendor_id;
ctx->product_id = device->product_id;
ctx->bluetooth = IsBluetoothXboxOneController(device->vendor_id, device->product_id);
ctx->initialized = ctx->bluetooth ? SDL_TRUE : SDL_FALSE;
ctx->start_time = SDL_GetTicks();
ctx->sequence = 1;
ctx->has_paddles = ControllerHasPaddles(ctx->vendor_id, ctx->product_id);
/* Initialize the joystick capabilities */
joystick->nbuttons = ctx->has_paddles ? SDL_CONTROLLER_BUTTON_MAX : (SDL_CONTROLLER_BUTTON_MAX + 4);
joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
return SDL_TRUE;
}
static int
HIDAPI_DriverXboxOne_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
{
SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context;
if (ctx->bluetooth) {
Uint8 rumble_packet[] = { 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00 };
rumble_packet[4] = (low_frequency_rumble >> 8);
rumble_packet[5] = (high_frequency_rumble >> 8);
if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
return SDL_SetError("Couldn't send rumble packet");
}
} else {
Uint8 rumble_packet[] = { 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF };
/* Magnitude is 1..100 so scale the 16-bit input here */
rumble_packet[8] = low_frequency_rumble / 655;
rumble_packet[9] = high_frequency_rumble / 655;
if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
return SDL_SetError("Couldn't send rumble packet");
}
}
return 0;
}
static void
HIDAPI_DriverXboxOne_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
{
Sint16 axis;
if (ctx->last_state[4] != data[4]) {
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[4] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[4] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[4] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[4] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[4] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[4] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
}
if (ctx->last_state[5] != data[5]) {
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[5] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[5] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[5] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[5] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[5] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[5] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[5] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[5] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
}
/* Xbox One S report is 18 bytes
Xbox One Elite Series 1 report is 33 bytes, paddles in data[32], mode in data[32] & 0x10, both modes have mapped paddles by default
Paddle bits:
UL: 0x01 (A) UR: 0x02 (B)
LL: 0x04 (X) LR: 0x08 (Y)
Xbox One Elite Series 2 report is 38 bytes, paddles in data[18], mode in data[19], mode 0 has no mapped paddles by default
Paddle bits:
UL: 0x04 (A) UR: 0x01 (B)
LL: 0x08 (X) LR: 0x02 (Y)
*/
if (ctx->has_paddles && (size == 33 || size == 38)) {
int paddle_index;
int button1_bit;
int button2_bit;
int button3_bit;
int button4_bit;
SDL_bool paddles_mapped;
if (size == 33) {
/* XBox One Elite Series 1 */
paddle_index = 32;
button1_bit = 0x01;
button2_bit = 0x02;
button3_bit = 0x04;
button4_bit = 0x08;
/* The mapped controller state is at offset 4, the raw state is at offset 18, compare them to see if the paddles are mapped */
paddles_mapped = (SDL_memcmp(&data[4], &data[18], 14) != 0);
} else /* if (size == 38) */ {
/* XBox One Elite Series 2 */
paddle_index = 18;
button1_bit = 0x04;
button2_bit = 0x01;
button3_bit = 0x08;
button4_bit = 0x02;
paddles_mapped = (data[19] != 0);
}
#ifdef DEBUG_XBOX_PROTOCOL
SDL_Log(">>> Paddles: %d,%d,%d,%d mapped = %s\n",
(data[paddle_index] & button1_bit) ? 1 : 0,
(data[paddle_index] & button2_bit) ? 1 : 0,
(data[paddle_index] & button3_bit) ? 1 : 0,
(data[paddle_index] & button4_bit) ? 1 : 0,
paddles_mapped ? "TRUE" : "FALSE"
);
#endif
if (paddles_mapped) {
/* Respect that the paddles are being used for other controls and don't pass them on to the app */
data[paddle_index] = 0;
}
if (ctx->last_state[paddle_index] != data[paddle_index]) {
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_MAX+0, (data[paddle_index] & button1_bit) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_MAX+1, (data[paddle_index] & button2_bit) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_MAX+2, (data[paddle_index] & button3_bit) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_MAX+3, (data[paddle_index] & button4_bit) ? SDL_PRESSED : SDL_RELEASED);
}
}
axis = ((int)*(Sint16*)(&data[6]) * 64) - 32768;
if (axis == 32704) {
axis = 32767;
}
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
axis = ((int)*(Sint16*)(&data[8]) * 64) - 32768;
if (axis == 32704) {
axis = 32767;
}
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
axis = *(Sint16*)(&data[10]);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
axis = *(Sint16*)(&data[12]);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, ~axis);
axis = *(Sint16*)(&data[14]);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
axis = *(Sint16*)(&data[16]);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, ~axis);
SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
}
static void
HIDAPI_DriverXboxOne_HandleModePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
{
if (data[1] == 0x30) {
/* The Xbox One S controller needs acks for mode reports */
const Uint8 seqnum = data[2];
const Uint8 ack[] = { 0x01, 0x20, seqnum, 0x09, 0x00, 0x07, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 };
hid_write(dev, ack, sizeof(ack));
}
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[4] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
}
static void
HIDAPI_DriverXboxOneBluetooth_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
{
Sint16 axis;
if (ctx->last_state[14] != data[14]) {
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[14] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[14] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[14] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[14] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[14] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[14] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
}
if (ctx->last_state[15] != data[15]) {
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[15] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[15] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[15] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
if (ctx->wireless_protocol == XBOX_ONE_WIRELESS_PROTOCOL_UNKNOWN)
{
if (data[15] & 0x10) {
ctx->wireless_protocol = XBOX_ONE_WIRELESS_PROTOCOL_V2;
}
}
if (ctx->wireless_protocol == XBOX_ONE_WIRELESS_PROTOCOL_V2)
{
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[15] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
}
}
if (ctx->last_state[16] != data[16]) {
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[16] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
}
if (ctx->last_state[13] != data[13]) {
SDL_bool dpad_up = SDL_FALSE;
SDL_bool dpad_down = SDL_FALSE;
SDL_bool dpad_left = SDL_FALSE;
SDL_bool dpad_right = SDL_FALSE;
switch (data[13]) {
case 1:
dpad_up = SDL_TRUE;
break;
case 2:
dpad_up = SDL_TRUE;
dpad_right = SDL_TRUE;
break;
case 3:
dpad_right = SDL_TRUE;
break;
case 4:
dpad_right = SDL_TRUE;
dpad_down = SDL_TRUE;
break;
case 5:
dpad_down = SDL_TRUE;
break;
case 6:
dpad_left = SDL_TRUE;
dpad_down = SDL_TRUE;
break;
case 7:
dpad_left = SDL_TRUE;
break;
case 8:
dpad_up = SDL_TRUE;
dpad_left = SDL_TRUE;
break;
default:
break;
}
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
}
axis = (int)*(Uint16*)(&data[1]) - 0x8000;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
axis = (int)*(Uint16*)(&data[3]) - 0x8000;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
axis = (int)*(Uint16*)(&data[5]) - 0x8000;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
axis = (int)*(Uint16*)(&data[7]) - 0x8000;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
axis = ((int)*(Sint16*)(&data[9]) * 64) - 32768;
if (axis == 32704) {
axis = 32767;
}
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
axis = ((int)*(Sint16*)(&data[11]) * 64) - 32768;
if (axis == 32704) {
axis = 32767;
}
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
}
static void
HIDAPI_DriverXboxOneBluetooth_HandleGuidePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
{
ctx->wireless_protocol = XBOX_ONE_WIRELESS_PROTOCOL_V1;
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[1] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
}
static SDL_bool
HIDAPI_DriverXboxOne_UpdateDevice(SDL_HIDAPI_Device *device)
{
SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context;
SDL_Joystick *joystick = NULL;
Uint8 data[USB_PACKET_LENGTH];
int size;
if (device->num_joysticks > 0) {
joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
}
if (!joystick) {
return SDL_FALSE;
}
if (!ctx->initialized &&
!ControllerSendsWaitingForInit(device->vendor_id, device->product_id)) {
if (SDL_TICKS_PASSED(SDL_GetTicks(), ctx->start_time + CONTROLLER_INIT_DELAY_MS)) {
if (!SendControllerInit(device, ctx)) {
HIDAPI_JoystickDisconnected(device, joystick->instance_id);
return SDL_FALSE;
}
ctx->initialized = SDL_TRUE;
}
}
while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
#ifdef DEBUG_XBOX_PROTOCOL
DumpPacket("Xbox One packet: size = %d", data, size);
#endif
if (ctx->bluetooth) {
switch (data[0]) {
case 0x01:
HIDAPI_DriverXboxOneBluetooth_HandleStatePacket(joystick, device->dev, ctx, data, size);
break;
case 0x02:
HIDAPI_DriverXboxOneBluetooth_HandleGuidePacket(joystick, device->dev, ctx, data, size);
break;
default:
#ifdef DEBUG_JOYSTICK
SDL_Log("Unknown Xbox One packet: 0x%.2x\n", data[0]);
#endif
break;
}
} else {
switch (data[0]) {
case 0x02:
/* Controller is connected and waiting for initialization */
if (!ctx->initialized) {
#ifdef DEBUG_XBOX_PROTOCOL
SDL_Log("Delay after init: %ums\n", SDL_GetTicks() - ctx->start_time);
#endif
if (!SendControllerInit(device, ctx)) {
HIDAPI_JoystickDisconnected(device, joystick->instance_id);
return SDL_FALSE;
}
ctx->initialized = SDL_TRUE;
}
break;
case 0x03:
/* Controller heartbeat */
break;
case 0x20:
HIDAPI_DriverXboxOne_HandleStatePacket(joystick, device->dev, ctx, data, size);
break;
case 0x07:
HIDAPI_DriverXboxOne_HandleModePacket(joystick, device->dev, ctx, data, size);
break;
default:
#ifdef DEBUG_JOYSTICK
SDL_Log("Unknown Xbox One packet: 0x%.2x\n", data[0]);
#endif
break;
}
}
}
if (size < 0) {
/* Read error, device is disconnected */
HIDAPI_JoystickDisconnected(device, joystick->instance_id);
}
return (size >= 0);
}
static void
HIDAPI_DriverXboxOne_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
hid_close(device->dev);
device->dev = NULL;
SDL_free(device->context);
device->context = NULL;
}
static void
HIDAPI_DriverXboxOne_FreeDevice(SDL_HIDAPI_Device *device)
{
}
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne =
{
SDL_HINT_JOYSTICK_HIDAPI_XBOX,
SDL_TRUE,
HIDAPI_DriverXboxOne_IsSupportedDevice,
HIDAPI_DriverXboxOne_GetDeviceName,
HIDAPI_DriverXboxOne_InitDevice,
HIDAPI_DriverXboxOne_GetDevicePlayerIndex,
HIDAPI_DriverXboxOne_SetDevicePlayerIndex,
HIDAPI_DriverXboxOne_UpdateDevice,
HIDAPI_DriverXboxOne_OpenJoystick,
HIDAPI_DriverXboxOne_RumbleJoystick,
HIDAPI_DriverXboxOne_CloseJoystick,
HIDAPI_DriverXboxOne_FreeDevice
};
#endif /* SDL_JOYSTICK_HIDAPI_XBOXONE */
#endif /* SDL_JOYSTICK_HIDAPI */
/* vi: set ts=4 sw=4 expandtab: */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,128 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifndef SDL_JOYSTICK_HIDAPI_H
#define SDL_JOYSTICK_HIDAPI_H
#include "SDL_atomic.h"
#include "SDL_mutex.h"
#include "SDL_joystick.h"
#include "SDL_gamecontroller.h"
#include "../../hidapi/hidapi/hidapi.h"
#include "../usb_ids.h"
/* This is the full set of HIDAPI drivers available */
#define SDL_JOYSTICK_HIDAPI_PS4
#define SDL_JOYSTICK_HIDAPI_SWITCH
#define SDL_JOYSTICK_HIDAPI_XBOX360
#define SDL_JOYSTICK_HIDAPI_XBOXONE
#define SDL_JOYSTICK_HIDAPI_GAMECUBE
#ifdef __WINDOWS__
/* On Windows, Xbox One controllers are handled by the Xbox 360 driver */
#undef SDL_JOYSTICK_HIDAPI_XBOXONE
/* It turns out HIDAPI for Xbox controllers doesn't allow background input */
#undef SDL_JOYSTICK_HIDAPI_XBOX360
#endif
#ifdef __MACOSX__
/* On Mac OS X, Xbox One controllers are handled by the Xbox 360 driver */
#undef SDL_JOYSTICK_HIDAPI_XBOXONE
#endif
#if defined(__IPHONEOS__) || defined(__TVOS__) || defined(__ANDROID__)
/* Very basic Steam Controller support on mobile devices */
#define SDL_JOYSTICK_HIDAPI_STEAM
#endif
/* The maximum size of a USB packet for HID devices */
#define USB_PACKET_LENGTH 64
/* Forward declaration */
struct _SDL_HIDAPI_DeviceDriver;
typedef struct _SDL_HIDAPI_Device
{
char *name;
char *path;
Uint16 vendor_id;
Uint16 product_id;
Uint16 version;
SDL_JoystickGUID guid;
int interface_number; /* Available on Windows and Linux */
int interface_class;
int interface_subclass;
int interface_protocol;
Uint16 usage_page; /* Available on Windows and Mac OS X */
Uint16 usage; /* Available on Windows and Mac OS X */
struct _SDL_HIDAPI_DeviceDriver *driver;
void *context;
SDL_mutex *dev_lock;
hid_device *dev;
SDL_atomic_t rumble_pending;
int num_joysticks;
SDL_JoystickID *joysticks;
/* Used during scanning for device changes */
SDL_bool seen;
struct _SDL_HIDAPI_Device *next;
} SDL_HIDAPI_Device;
typedef struct _SDL_HIDAPI_DeviceDriver
{
const char *hint;
SDL_bool enabled;
SDL_bool (*IsSupportedDevice)(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol);
const char *(*GetDeviceName)(Uint16 vendor_id, Uint16 product_id);
SDL_bool (*InitDevice)(SDL_HIDAPI_Device *device);
int (*GetDevicePlayerIndex)(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id);
void (*SetDevicePlayerIndex)(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index);
SDL_bool (*UpdateDevice)(SDL_HIDAPI_Device *device);
SDL_bool (*OpenJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick);
int (*RumbleJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble);
void (*CloseJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick);
void (*FreeDevice)(SDL_HIDAPI_Device *device);
} SDL_HIDAPI_DeviceDriver;
/* HIDAPI device support */
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4;
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteam;
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch;
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360;
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360W;
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne;
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverGameCube;
/* Return true if a HID device is present and supported as a joystick */
extern SDL_bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name);
extern void HIDAPI_UpdateDevices(void);
extern SDL_bool HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID);
extern void HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID);
#endif /* SDL_JOYSTICK_HIDAPI_H */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,484 @@
//===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================
//
// Purpose: Defines constants used to communicate with Valve controllers.
//
//==================================================================================================
#ifndef _CONTROLLER_CONSTANTS_
#define _CONTROLLER_CONSTANTS_
#include "controller_structs.h"
#ifdef __cplusplus
extern "C" {
#endif
#define FEATURE_REPORT_SIZE 64
#define VALVE_USB_VID 0x28DE
// Frame update rate (in ms).
#define FAST_SCAN_INTERVAL 6
#define SLOW_SCAN_INTERVAL 9
// Contains each of the USB PIDs for Valve controllers (only add to this enum and never change the order)
enum ValveControllerPID
{
BASTILLE_PID = 0x2202,
CHELL_PID = 0x1101,
D0G_PID = 0x1102,
ELI_PID = 0x1103,
FREEMAN_PID = 0x1104,
D0G_BLE_PID = 0x1105,
D0G_BLE2_PID = 0x1106,
D0GGLE_PID = 0x1142,
};
// This enum contains all of the messages exchanged between the host and the target (only add to this enum and never change the order)
enum FeatureReportMessageIDs
{
ID_SET_DIGITAL_MAPPINGS = 0x80,
ID_CLEAR_DIGITAL_MAPPINGS = 0x81,
ID_GET_DIGITAL_MAPPINGS = 0x82,
ID_GET_ATTRIBUTES_VALUES = 0x83,
ID_GET_ATTRIBUTE_LABEL = 0x84,
ID_SET_DEFAULT_DIGITAL_MAPPINGS = 0x85,
ID_FACTORY_RESET = 0x86,
ID_SET_SETTINGS_VALUES = 0x87,
ID_CLEAR_SETTINGS_VALUES = 0x88,
ID_GET_SETTINGS_VALUES = 0x89,
ID_GET_SETTING_LABEL = 0x8A,
ID_GET_SETTINGS_MAXS = 0x8B,
ID_GET_SETTINGS_DEFAULTS = 0x8C,
ID_SET_CONTROLLER_MODE = 0x8D,
ID_LOAD_DEFAULT_SETTINGS = 0x8E,
ID_TRIGGER_HAPTIC_PULSE = 0x8F,
ID_TURN_OFF_CONTROLLER = 0x9F,
ID_GET_DEVICE_INFO = 0xA1,
ID_CALIBRATE_TRACKPADS = 0xA7,
ID_RESERVED_0 = 0xA8,
ID_SET_SERIAL_NUMBER = 0xA9,
ID_GET_TRACKPAD_CALIBRATION = 0xAA,
ID_GET_TRACKPAD_FACTORY_CALIBRATION = 0xAB,
ID_GET_TRACKPAD_RAW_DATA = 0xAC,
ID_ENABLE_PAIRING = 0xAD,
ID_GET_STRING_ATTRIBUTE = 0xAE,
ID_RADIO_ERASE_RECORDS = 0xAF,
ID_RADIO_WRITE_RECORD = 0xB0,
ID_SET_DONGLE_SETTING = 0xB1,
ID_DONGLE_DISCONNECT_DEVICE = 0xB2,
ID_DONGLE_COMMIT_DEVICE = 0xB3,
ID_DONGLE_GET_WIRELESS_STATE = 0xB4,
ID_CALIBRATE_GYRO = 0xB5,
ID_PLAY_AUDIO = 0xB6,
ID_AUDIO_UPDATE_START = 0xB7,
ID_AUDIO_UPDATE_DATA = 0xB8,
ID_AUDIO_UPDATE_COMPLETE = 0xB9,
ID_GET_CHIPID = 0xBA,
ID_CALIBRATE_JOYSTICK = 0xBF,
ID_CALIBRATE_ANALOG_TRIGGERS = 0xC0,
ID_SET_AUDIO_MAPPING = 0xC1,
ID_CHECK_GYRO_FW_LOAD = 0xC2,
ID_CALIBRATE_ANALOG = 0xC3,
ID_DONGLE_GET_CONNECTED_SLOTS = 0xC4,
};
// Enumeration of all wireless dongle events
typedef enum WirelessEventTypes
{
WIRELESS_EVENT_DISCONNECT = 1,
WIRELESS_EVENT_CONNECT = 2,
WIRELESS_EVENT_PAIR = 3,
} EWirelessEventType;
// Enumeration of generic digital inputs - not all of these will be supported on all controllers (only add to this enum and never change the order)
typedef enum
{
IO_DIGITAL_BUTTON_NONE = -1,
IO_DIGITAL_BUTTON_RIGHT_TRIGGER,
IO_DIGITAL_BUTTON_LEFT_TRIGGER,
IO_DIGITAL_BUTTON_1,
IO_DIGITAL_BUTTON_Y=IO_DIGITAL_BUTTON_1,
IO_DIGITAL_BUTTON_2,
IO_DIGITAL_BUTTON_B=IO_DIGITAL_BUTTON_2,
IO_DIGITAL_BUTTON_3,
IO_DIGITAL_BUTTON_X=IO_DIGITAL_BUTTON_3,
IO_DIGITAL_BUTTON_4,
IO_DIGITAL_BUTTON_A=IO_DIGITAL_BUTTON_4,
IO_DIGITAL_BUTTON_RIGHT_BUMPER,
IO_DIGITAL_BUTTON_LEFT_BUMPER,
IO_DIGITAL_BUTTON_LEFT_JOYSTICK_CLICK,
IO_DIGITAL_BUTTON_ESCAPE,
IO_DIGITAL_BUTTON_STEAM,
IO_DIGITAL_BUTTON_MENU,
IO_DIGITAL_STICK_UP,
IO_DIGITAL_STICK_DOWN,
IO_DIGITAL_STICK_LEFT,
IO_DIGITAL_STICK_RIGHT,
IO_DIGITAL_TOUCH_1,
IO_DIGITAL_BUTTON_UP=IO_DIGITAL_TOUCH_1,
IO_DIGITAL_TOUCH_2,
IO_DIGITAL_BUTTON_RIGHT=IO_DIGITAL_TOUCH_2,
IO_DIGITAL_TOUCH_3,
IO_DIGITAL_BUTTON_LEFT=IO_DIGITAL_TOUCH_3,
IO_DIGITAL_TOUCH_4,
IO_DIGITAL_BUTTON_DOWN=IO_DIGITAL_TOUCH_4,
IO_DIGITAL_BUTTON_BACK_LEFT,
IO_DIGITAL_BUTTON_BACK_RIGHT,
IO_DIGITAL_LEFT_TRACKPAD_N,
IO_DIGITAL_LEFT_TRACKPAD_NE,
IO_DIGITAL_LEFT_TRACKPAD_E,
IO_DIGITAL_LEFT_TRACKPAD_SE,
IO_DIGITAL_LEFT_TRACKPAD_S,
IO_DIGITAL_LEFT_TRACKPAD_SW,
IO_DIGITAL_LEFT_TRACKPAD_W,
IO_DIGITAL_LEFT_TRACKPAD_NW,
IO_DIGITAL_RIGHT_TRACKPAD_N,
IO_DIGITAL_RIGHT_TRACKPAD_NE,
IO_DIGITAL_RIGHT_TRACKPAD_E,
IO_DIGITAL_RIGHT_TRACKPAD_SE,
IO_DIGITAL_RIGHT_TRACKPAD_S,
IO_DIGITAL_RIGHT_TRACKPAD_SW,
IO_DIGITAL_RIGHT_TRACKPAD_W,
IO_DIGITAL_RIGHT_TRACKPAD_NW,
IO_DIGITAL_LEFT_TRACKPAD_DOUBLE_TAP,
IO_DIGITAL_RIGHT_TRACKPAD_DOUBLE_TAP,
IO_DIGITAL_LEFT_TRACKPAD_OUTER_RADIUS,
IO_DIGITAL_RIGHT_TRACKPAD_OUTER_RADIUS,
IO_DIGITAL_LEFT_TRACKPAD_CLICK,
IO_DIGITAL_RIGHT_TRACKPAD_CLICK,
IO_DIGITAL_BATTERY_LOW,
IO_DIGITAL_LEFT_TRIGGER_THRESHOLD,
IO_DIGITAL_RIGHT_TRIGGER_THRESHOLD,
IO_DIGITAL_BUTTON_BACK_LEFT2,
IO_DIGITAL_BUTTON_BACK_RIGHT2,
IO_DIGITAL_BUTTON_ALWAYS_ON,
IO_DIGITAL_BUTTON_ANCILLARY_1,
IO_DIGITAL_BUTTON_MACRO_0,
IO_DIGITAL_BUTTON_MACRO_1,
IO_DIGITAL_BUTTON_MACRO_2,
IO_DIGITAL_BUTTON_MACRO_3,
IO_DIGITAL_BUTTON_MACRO_4,
IO_DIGITAL_BUTTON_MACRO_5,
IO_DIGITAL_BUTTON_MACRO_6,
IO_DIGITAL_BUTTON_MACRO_7,
IO_DIGITAL_BUTTON_MACRO_1FINGER,
IO_DIGITAL_BUTTON_MACRO_2FINGER,
IO_DIGITAL_COUNT
} DigitalIO ;
// Enumeration of generic analog inputs - not all of these will be supported on all controllers (only add to this enum and never change the order)
typedef enum
{
IO_ANALOG_LEFT_STICK_X,
IO_ANALOG_LEFT_STICK_Y,
IO_ANALOG_RIGHT_STICK_X,
IO_ANALOG_RIGHT_STICK_Y,
IO_ANALOG_LEFT_TRIGGER,
IO_ANALOG_RIGHT_TRIGGER,
IO_MOUSE1_X,
IO_MOUSE1_Y,
IO_MOUSE1_Z,
IO_ACCEL_X,
IO_ACCEL_Y,
IO_ACCEL_Z,
IO_GYRO_X,
IO_GYRO_Y,
IO_GYRO_Z,
IO_GYRO_QUAT_W,
IO_GYRO_QUAT_X,
IO_GYRO_QUAT_Y,
IO_GYRO_QUAT_Z,
IO_GYRO_STEERING_VEC,
IO_RAW_TRIGGER_LEFT,
IO_RAW_TRIGGER_RIGHT,
IO_RAW_JOYSTICK_X,
IO_RAW_JOYSTICK_Y,
IO_GYRO_TILT_VEC,
IO_ANALOG_COUNT
} AnalogIO;
// Contains list of all types of devices that the controller emulates (only add to this enum and never change the order)
enum DeviceTypes
{
DEVICE_KEYBOARD,
DEVICE_MOUSE,
DEVICE_GAMEPAD,
DEVICE_MODE_ADJUST,
DEVICE_COUNT
};
// Scan codes for HID keyboards
enum HIDKeyboardKeys
{
KEY_INVALID,
KEY_FIRST = 0x04,
KEY_A = KEY_FIRST, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L,
KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_1, KEY_2,
KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, KEY_RETURN, KEY_ESCAPE, KEY_BACKSPACE, KEY_TAB, KEY_SPACE, KEY_DASH, KEY_EQUALS, KEY_LEFT_BRACKET,
KEY_RIGHT_BRACKET, KEY_BACKSLASH, KEY_UNUSED1, KEY_SEMICOLON, KEY_SINGLE_QUOTE, KEY_BACK_TICK, KEY_COMMA, KEY_PERIOD, KEY_FORWARD_SLASH, KEY_CAPSLOCK, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6,
KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_PRINT_SCREEN, KEY_SCROLL_LOCK, KEY_BREAK, KEY_INSERT, KEY_HOME, KEY_PAGE_UP, KEY_DELETE, KEY_END, KEY_PAGE_DOWN, KEY_RIGHT_ARROW,
KEY_LEFT_ARROW, KEY_DOWN_ARROW, KEY_UP_ARROW, KEY_NUM_LOCK, KEY_KEYPAD_FORWARD_SLASH, KEY_KEYPAD_ASTERISK, KEY_KEYPAD_DASH, KEY_KEYPAD_PLUS, KEY_KEYPAD_ENTER, KEY_KEYPAD_1, KEY_KEYPAD_2, KEY_KEYPAD_3, KEY_KEYPAD_4, KEY_KEYPAD_5, KEY_KEYPAD_6, KEY_KEYPAD_7,
KEY_KEYPAD_8, KEY_KEYPAD_9, KEY_KEYPAD_0, KEY_KEYPAD_PERIOD,
KEY_LALT,
KEY_LSHIFT,
KEY_LWIN,
KEY_LCONTROL,
KEY_RALT,
KEY_RSHIFT,
KEY_RWIN,
KEY_RCONTROL,
KEY_VOLUP,
KEY_VOLDOWN,
KEY_MUTE,
KEY_PLAY,
KEY_STOP,
KEY_NEXT,
KEY_PREV,
KEY_LAST = KEY_PREV
};
enum ModifierMasks
{
KEY_LCONTROL_MASK = (1<<0),
KEY_LSHIFT_MASK = (1<<1),
KEY_LALT_MASK = (1<<2),
KEY_LWIN_MASK = (1<<3),
KEY_RCONTROL_MASK = (1<<4),
KEY_RSHIFT_MASK = (1<<5),
KEY_RALT_MASK = (1<<6),
KEY_RWIN_MASK = (1<<7)
};
// Standard mouse buttons as specified in the HID mouse spec
enum MouseButtons
{
MOUSE_BTN_LEFT,
MOUSE_BTN_RIGHT,
MOUSE_BTN_MIDDLE,
MOUSE_BTN_BACK,
MOUSE_BTN_FORWARD,
MOUSE_SCROLL_UP,
MOUSE_SCROLL_DOWN,
MOUSE_BTN_COUNT
};
// Gamepad buttons
enum GamepadButtons
{
GAMEPAD_BTN_TRIGGER_LEFT=1,
GAMEPAD_BTN_TRIGGER_RIGHT,
GAMEPAD_BTN_A,
GAMEPAD_BTN_B,
GAMEPAD_BTN_Y,
GAMEPAD_BTN_X,
GAMEPAD_BTN_SHOULDER_LEFT,
GAMEPAD_BTN_SHOULDER_RIGHT,
GAMEPAD_BTN_LEFT_JOYSTICK,
GAMEPAD_BTN_RIGHT_JOYSTICK,
GAMEPAD_BTN_START,
GAMEPAD_BTN_SELECT,
GAMEPAD_BTN_STEAM,
GAMEPAD_BTN_DPAD_UP,
GAMEPAD_BTN_DPAD_DOWN,
GAMEPAD_BTN_DPAD_LEFT,
GAMEPAD_BTN_DPAD_RIGHT,
GAMEPAD_BTN_LSTICK_UP,
GAMEPAD_BTN_LSTICK_DOWN,
GAMEPAD_BTN_LSTICK_LEFT,
GAMEPAD_BTN_LSTICK_RIGHT,
GAMEPAD_BTN_RSTICK_UP,
GAMEPAD_BTN_RSTICK_DOWN,
GAMEPAD_BTN_RSTICK_LEFT,
GAMEPAD_BTN_RSTICK_RIGHT,
GAMEPAD_BTN_COUNT
};
// Mode adjust
enum ModeAdjustModes
{
MODE_ADJUST_SENSITITY=1,
MODE_ADJUST_LEFT_PAD_SECONDARY_MODE,
MODE_ADJUST_RIGHT_PAD_SECONDARY_MODE,
MODE_ADJUST_COUNT
};
// Read-only attributes of controllers (only add to this enum and never change the order)
typedef enum
{
ATTRIB_UNIQUE_ID,
ATTRIB_PRODUCT_ID,
ATTRIB_PRODUCT_REVISON, // deprecated
ATTRIB_CAPABILITIES = ATTRIB_PRODUCT_REVISON, // intentional aliasing
ATTRIB_FIRMWARE_VERSION, // deprecated
ATTRIB_FIRMWARE_BUILD_TIME,
ATTRIB_RADIO_FIRMWARE_BUILD_TIME,
ATTRIB_RADIO_DEVICE_ID0,
ATTRIB_RADIO_DEVICE_ID1,
ATTRIB_DONGLE_FIRMWARE_BUILD_TIME,
ATTRIB_BOARD_REVISION,
ATTRIB_BOOTLOADER_BUILD_TIME,
ATTRIB_CONNECTION_INTERVAL_IN_US,
ATTRIB_COUNT
} ControllerAttributes;
// Read-only string attributes of controllers (only add to this enum and never change the order)
typedef enum
{
ATTRIB_STR_BOARD_SERIAL,
ATTRIB_STR_UNIT_SERIAL,
ATTRIB_STR_COUNT
} ControllerStringAttributes;
typedef enum
{
STATUS_CODE_NORMAL,
STATUS_CODE_CRITICAL_BATTERY,
STATUS_CODE_GYRO_INIT_ERROR,
} ControllerStatusEventCodes;
typedef enum
{
STATUS_STATE_LOW_BATTERY=0,
} ControllerStatusStateFlags;
typedef enum {
TRACKPAD_ABSOLUTE_MOUSE,
TRACKPAD_RELATIVE_MOUSE,
TRACKPAD_DPAD_FOUR_WAY_DISCRETE,
TRACKPAD_DPAD_FOUR_WAY_OVERLAP,
TRACKPAD_DPAD_EIGHT_WAY,
TRACKPAD_RADIAL_MODE,
TRACKPAD_ABSOLUTE_DPAD,
TRACKPAD_NONE,
TRACKPAD_GESTURE_KEYBOARD,
TRACKPAD_NUM_MODES
} TrackpadDPadMode;
// Read-write controller settings (only add to this enum and never change the order)
typedef enum
{
SETTING_MOUSE_SENSITIVITY,
SETTING_MOUSE_ACCELERATION,
SETTING_TRACKBALL_ROTATION_ANGLE,
SETTING_HAPTIC_INTENSITY,
SETTING_LEFT_GAMEPAD_STICK_ENABLED,
SETTING_RIGHT_GAMEPAD_STICK_ENABLED,
SETTING_USB_DEBUG_MODE,
SETTING_LEFT_TRACKPAD_MODE,
SETTING_RIGHT_TRACKPAD_MODE,
SETTING_MOUSE_POINTER_ENABLED,
SETTING_DPAD_DEADZONE,
SETTING_MINIMUM_MOMENTUM_VEL,
SETTING_MOMENTUM_DECAY_AMMOUNT,
SETTING_TRACKPAD_RELATIVE_MODE_TICKS_PER_PIXEL,
SETTING_HAPTIC_INCREMENT,
SETTING_DPAD_ANGLE_SIN,
SETTING_DPAD_ANGLE_COS,
SETTING_MOMENTUM_VERTICAL_DIVISOR,
SETTING_MOMENTUM_MAXIMUM_VELOCITY,
SETTING_TRACKPAD_Z_ON,
SETTING_TRACKPAD_Z_OFF,
SETTING_SENSITIVY_SCALE_AMMOUNT,
SETTING_LEFT_TRACKPAD_SECONDARY_MODE,
SETTING_RIGHT_TRACKPAD_SECONDARY_MODE,
SETTING_SMOOTH_ABSOLUTE_MOUSE,
SETTING_STEAMBUTTON_POWEROFF_TIME,
SETTING_UNUSED_1,
SETTING_TRACKPAD_OUTER_RADIUS,
SETTING_TRACKPAD_Z_ON_LEFT,
SETTING_TRACKPAD_Z_OFF_LEFT,
SETTING_TRACKPAD_OUTER_SPIN_VEL,
SETTING_TRACKPAD_OUTER_SPIN_RADIUS,
SETTING_TRACKPAD_OUTER_SPIN_HORIZONTAL_ONLY,
SETTING_TRACKPAD_RELATIVE_MODE_DEADZONE,
SETTING_TRACKPAD_RELATIVE_MODE_MAX_VEL,
SETTING_TRACKPAD_RELATIVE_MODE_INVERT_Y,
SETTING_TRACKPAD_DOUBLE_TAP_BEEP_ENABLED,
SETTING_TRACKPAD_DOUBLE_TAP_BEEP_PERIOD,
SETTING_TRACKPAD_DOUBLE_TAP_BEEP_COUNT,
SETTING_TRACKPAD_OUTER_RADIUS_RELEASE_ON_TRANSITION,
SETTING_RADIAL_MODE_ANGLE,
SETTING_HAPTIC_INTENSITY_MOUSE_MODE,
SETTING_LEFT_DPAD_REQUIRES_CLICK,
SETTING_RIGHT_DPAD_REQUIRES_CLICK,
SETTING_LED_BASELINE_BRIGHTNESS,
SETTING_LED_USER_BRIGHTNESS,
SETTING_ENABLE_RAW_JOYSTICK,
SETTING_ENABLE_FAST_SCAN,
SETTING_GYRO_MODE,
SETTING_WIRELESS_PACKET_VERSION,
SETTING_SLEEP_INACTIVITY_TIMEOUT,
SETTING_COUNT,
// This is a special setting value use for callbacks and should not be set/get explicitly.
SETTING_ALL=0xFF
} ControllerSettings;
typedef enum
{
SETTING_DEFAULT,
SETTING_MIN,
SETTING_MAX,
SETTING_DEFAULTMINMAXCOUNT
} SettingDefaultMinMax;
// Bitmask that define which IMU features to enable.
typedef enum
{
SETTING_GYRO_MODE_OFF = 0x0000,
SETTING_GYRO_MODE_STEERING = 0x0001,
SETTING_GYRO_MODE_TILT = 0x0002,
SETTING_GYRO_MODE_SEND_ORIENTATION = 0x0004,
SETTING_GYRO_MODE_SEND_RAW_ACCEL = 0x0008,
SETTING_GYRO_MODE_SEND_RAW_GYRO = 0x0010,
} SettingGyroMode;
// Bitmask for haptic pulse flags
typedef enum
{
HAPTIC_PULSE_NORMAL = 0x0000,
HAPTIC_PULSE_HIGH_PRIORITY = 0x0001,
HAPTIC_PULSE_VERY_HIGH_PRIORITY = 0x0002,
} SettingHapticPulseFlags;
typedef struct
{
// default,min,max in this array in that order
short defaultminmax[SETTING_DEFAULTMINMAXCOUNT];
} SettingValueRange_t;
// below is from controller_constants.c which should be compiled into any code that uses this
extern const SettingValueRange_t g_DefaultSettingValues[SETTING_COUNT];
// Read-write settings for dongle (only add to this enum and never change the order)
typedef enum
{
DONGLE_SETTING_MOUSE_KEYBOARD_ENABLED,
DONGLE_SETTING_COUNT,
} DongleSettings;
typedef enum
{
AUDIO_STARTUP = 0,
AUDIO_SHUTDOWN = 1,
AUDIO_PAIR = 2,
AUDIO_PAIR_SUCCESS = 3,
AUDIO_IDENTIFY = 4,
AUDIO_LIZARDMODE = 5,
AUDIO_NORMALMODE = 6,
AUDIO_MAX_SLOT = 15
} ControllerAudio;
#ifdef __cplusplus
}
#endif
#endif // _CONTROLLER_CONSTANTS_H

View File

@@ -0,0 +1,255 @@
//===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================
//
// Purpose: Defines methods and structures used to communicate with Valve controllers.
//
//==================================================================================================
#ifndef _CONTROLLER_STRUCTS_
#define _CONTROLLER_STRUCTS_
#pragma pack(1)
// Roll this version forward anytime that you are breaking compatibility of existing
// message types within ValveInReport_t or the header itself. Hopefully this should
// be super rare and instead you shoudl just add new message payloads to the union,
// or just add fields to the end of existing payload structs which is expected to be
// safe in all code consuming these as they should just consume/copy upto the prior size
// they were aware of when processing.
#define k_ValveInReportMsgVersion 0x01
typedef enum
{
ID_CONTROLLER_STATE = 1,
ID_CONTROLLER_DEBUG = 2,
ID_CONTROLLER_WIRELESS = 3,
ID_CONTROLLER_STATUS = 4,
ID_CONTROLLER_DEBUG2 = 5,
ID_CONTROLLER_SECONDARY_STATE = 6,
ID_CONTROLLER_BLE_STATE = 7,
ID_CONTROLLER_MSG_COUNT
} ValveInReportMessageIDs;
typedef struct
{
unsigned short unReportVersion;
unsigned char ucType;
unsigned char ucLength;
} ValveInReportHeader_t;
// State payload
typedef struct
{
// If packet num matches that on your prior call, then the controller state hasn't been changed since
// your last call and there is no need to process it
uint32 unPacketNum;
// Button bitmask and trigger data.
union
{
uint64 ulButtons;
struct
{
unsigned char _pad0[3];
unsigned char nLeft;
unsigned char nRight;
unsigned char _pad1[3];
} Triggers;
} ButtonTriggerData;
// Left pad coordinates
short sLeftPadX;
short sLeftPadY;
// Right pad coordinates
short sRightPadX;
short sRightPadY;
// This is redundant, packed above, but still sent over wired
unsigned short sTriggerL;
unsigned short sTriggerR;
// FIXME figure out a way to grab this stuff over wireless
short sAccelX;
short sAccelY;
short sAccelZ;
short sGyroX;
short sGyroY;
short sGyroZ;
short sGyroQuatW;
short sGyroQuatX;
short sGyroQuatY;
short sGyroQuatZ;
} ValveControllerStatePacket_t;
// BLE State payload this has to be re-formatted from the normal state because BLE controller shows up as
//a HID device and we don't want to send all the optional parts of the message. Keep in sync with struct above.
typedef struct
{
// If packet num matches that on your prior call, then the controller state hasn't been changed since
// your last call and there is no need to process it
uint32 unPacketNum;
// Button bitmask and trigger data.
union
{
uint64 ulButtons;
struct
{
unsigned char _pad0[3];
unsigned char nLeft;
unsigned char nRight;
unsigned char _pad1[3];
} Triggers;
} ButtonTriggerData;
// Left pad coordinates
short sLeftPadX;
short sLeftPadY;
// Right pad coordinates
short sRightPadX;
short sRightPadY;
//This mimcs how the dongle reconstitutes HID packets, there will be 0-4 shorts depending on gyro mode
unsigned char ucGyroDataType; //TODO could maybe find some unused bits in the button field for this info (is only 2bits)
short sGyro[4];
} ValveControllerBLEStatePacket_t;
// Define a payload for reporting debug information
typedef struct
{
// Left pad coordinates
short sLeftPadX;
short sLeftPadY;
// Right pad coordinates
short sRightPadX;
short sRightPadY;
// Left mouse deltas
short sLeftPadMouseDX;
short sLeftPadMouseDY;
// Right mouse deltas
short sRightPadMouseDX;
short sRightPadMouseDY;
// Left mouse filtered deltas
short sLeftPadMouseFilteredDX;
short sLeftPadMouseFilteredDY;
// Right mouse filtered deltas
short sRightPadMouseFilteredDX;
short sRightPadMouseFilteredDY;
// Pad Z values
unsigned char ucLeftZ;
unsigned char ucRightZ;
// FingerPresent
unsigned char ucLeftFingerPresent;
unsigned char ucRightFingerPresent;
// Timestamps
unsigned char ucLeftTimestamp;
unsigned char ucRightTimestamp;
// Double tap state
unsigned char ucLeftTapState;
unsigned char ucRightTapState;
unsigned int unDigitalIOStates0;
unsigned int unDigitalIOStates1;
} ValveControllerDebugPacket_t;
typedef struct
{
unsigned char ucPadNum;
unsigned char ucPad[3]; // need Data to be word aligned
short Data[20];
unsigned short unNoise;
} ValveControllerTrackpadImage_t;
typedef struct
{
unsigned char ucPadNum;
unsigned char ucOffset;
unsigned char ucPad[2]; // need Data to be word aligned
short rgData[28];
} ValveControllerRawTrackpadImage_t;
// Payload for wireless metadata
typedef struct
{
unsigned char ucEventType;
} SteamControllerWirelessEvent_t;
typedef struct
{
// Current packet number.
unsigned int unPacketNum;
// Event codes and state information.
unsigned short sEventCode;
unsigned short unStateFlags;
// Current battery voltage (mV).
unsigned short sBatteryVoltage;
// Current battery level (0-100).
unsigned char ucBatteryLevel;
} SteamControllerStatusEvent_t;
typedef struct
{
ValveInReportHeader_t header;
union
{
ValveControllerStatePacket_t controllerState;
ValveControllerBLEStatePacket_t controllerBLEState;
ValveControllerDebugPacket_t debugState;
ValveControllerTrackpadImage_t padImage;
ValveControllerRawTrackpadImage_t rawPadImage;
SteamControllerWirelessEvent_t wirelessEvent;
SteamControllerStatusEvent_t statusEvent;
} payload;
} ValveInReport_t;
// Enumeration for BLE packet protocol
enum EBLEPacketReportNums
{
// Skipping past 2-3 because they are escape characters in Uart protocol
k_EBLEReportState = 4,
k_EBLEReportStatus = 5,
};
// Enumeration of data chunks in BLE state packets
enum EBLEOptionDataChunksBitmask
{
// First byte uppper nibble
k_EBLEButtonChunk1 = 0x10,
k_EBLEButtonChunk2 = 0x20,
k_EBLEButtonChunk3 = 0x40,
k_EBLELeftJoystickChunk = 0x80,
// Second full byte
k_EBLELeftTrackpadChunk = 0x100,
k_EBLERightTrackpadChunk = 0x200,
k_EBLEIMUAccelChunk = 0x400,
k_EBLEIMUGyroChunk = 0x800,
k_EBLEIMUQuatChunk = 0x1000,
};
#pragma pack()
#endif // _CONTROLLER_STRUCTS