mirror of
https://github.com/InoriRus/Kyty.git
synced 2026-08-28 13:16:40 +00:00
release 0.1.0
This commit is contained in:
@@ -0,0 +1,587 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2022 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_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"
|
||||
#include "../../hidapi/SDL_hidapi_c.h"
|
||||
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE
|
||||
|
||||
/* Define this if you want to log all packets from the controller */
|
||||
/*#define DEBUG_GAMECUBE_PROTOCOL*/
|
||||
|
||||
#define MAX_CONTROLLERS 4
|
||||
|
||||
typedef struct {
|
||||
SDL_bool pc_mode;
|
||||
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_bool useRumbleBrake;
|
||||
} 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;
|
||||
}
|
||||
if (vendor_id == USB_VENDOR_SHENZHEN && product_id == USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER) {
|
||||
/* EVORETRO 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 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 void SDLCALL SDL_JoystickGameCubeRumbleBrakeHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
|
||||
{
|
||||
if (hint) {
|
||||
SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)userdata;
|
||||
ctx->useRumbleBrake = SDL_GetStringBoolean(hint, SDL_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
#ifdef HAVE_ENABLE_GAMECUBE_ADAPTORS
|
||||
SDL_EnableGameCubeAdaptors();
|
||||
#endif
|
||||
|
||||
ctx = (SDL_DriverGameCube_Context *)SDL_calloc(1, sizeof(*ctx));
|
||||
if (!ctx) {
|
||||
SDL_OutOfMemory();
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
device->dev = SDL_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;
|
||||
ctx->useRumbleBrake = SDL_FALSE;
|
||||
|
||||
if (device->vendor_id != USB_VENDOR_NINTENDO) {
|
||||
ctx->pc_mode = SDL_TRUE;
|
||||
}
|
||||
|
||||
if (ctx->pc_mode) {
|
||||
for (i = 0; i < MAX_CONTROLLERS; ++i) {
|
||||
ResetAxisRange(ctx, i);
|
||||
HIDAPI_JoystickConnected(device, &ctx->joysticks[i]);
|
||||
}
|
||||
} else {
|
||||
/* This is all that's needed to initialize the device. Really! */
|
||||
if (SDL_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 = SDL_hid_read_timeout(device->dev, packet, sizeof(packet), 0)) > 0) {
|
||||
#ifdef DEBUG_GAMECUBE_PROTOCOL
|
||||
HIDAPI_DumpPacket("Nintendo GameCube packet: size = %d", packet, size);
|
||||
#endif
|
||||
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_JOYSTICK_GAMECUBE_RUMBLE_BRAKE,
|
||||
SDL_JoystickGameCubeRumbleBrakeHintChanged, ctx);
|
||||
SDL_AddHintCallback(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS,
|
||||
SDL_GameControllerButtonReportingHintChanged, ctx);
|
||||
|
||||
return SDL_TRUE;
|
||||
|
||||
error:
|
||||
SDL_LockMutex(device->dev_lock);
|
||||
{
|
||||
if (device->dev) {
|
||||
SDL_hid_close(device->dev);
|
||||
device->dev = NULL;
|
||||
}
|
||||
if (device->context) {
|
||||
SDL_free(device->context);
|
||||
device->context = NULL;
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(device->dev_lock);
|
||||
|
||||
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 void
|
||||
HIDAPI_DriverGameCube_HandleJoystickPacket(SDL_HIDAPI_Device *device, SDL_DriverGameCube_Context *ctx, Uint8 *packet, int size)
|
||||
{
|
||||
SDL_Joystick *joystick;
|
||||
Uint8 i, v;
|
||||
Sint16 axis_value;
|
||||
|
||||
if (size != 10) {
|
||||
return; /* How do we handle this packet? */
|
||||
}
|
||||
|
||||
i = packet[0] - 1;
|
||||
if (i >= MAX_CONTROLLERS) {
|
||||
return; /* How do we handle this packet? */
|
||||
}
|
||||
|
||||
joystick = SDL_JoystickFromInstanceID(ctx->joysticks[i]);
|
||||
if (!joystick) {
|
||||
/* Hasn't been opened yet, skip */
|
||||
return;
|
||||
}
|
||||
|
||||
#define READ_BUTTON(off, flag, button) \
|
||||
SDL_PrivateJoystickButton( \
|
||||
joystick, \
|
||||
RemapButton(ctx, button), \
|
||||
(packet[off] & flag) ? SDL_PRESSED : SDL_RELEASED \
|
||||
);
|
||||
READ_BUTTON(1, 0x02, 0) /* A */
|
||||
READ_BUTTON(1, 0x04, 1) /* B */
|
||||
READ_BUTTON(1, 0x01, 2) /* X */
|
||||
READ_BUTTON(1, 0x08, 3) /* Y */
|
||||
READ_BUTTON(2, 0x80, 4) /* DPAD_LEFT */
|
||||
READ_BUTTON(2, 0x20, 5) /* DPAD_RIGHT */
|
||||
READ_BUTTON(2, 0x40, 6) /* DPAD_DOWN */
|
||||
READ_BUTTON(2, 0x10, 7) /* DPAD_UP */
|
||||
READ_BUTTON(2, 0x02, 8) /* START */
|
||||
READ_BUTTON(1, 0x80, 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(1, 0x20, 10) /* TRIGGERRIGHT */
|
||||
READ_BUTTON(1, 0x10, 11) /* TRIGGERLEFT */
|
||||
#undef READ_BUTTON
|
||||
|
||||
#define READ_AXIS(off, axis, invert) \
|
||||
v = invert ? (0xff - packet[off]) : packet[off]; \
|
||||
if (v < ctx->min_axis[i*SDL_CONTROLLER_AXIS_MAX+axis]) ctx->min_axis[i*SDL_CONTROLLER_AXIS_MAX+axis] = v; \
|
||||
if (v > ctx->max_axis[i*SDL_CONTROLLER_AXIS_MAX+axis]) ctx->max_axis[i*SDL_CONTROLLER_AXIS_MAX+axis] = v; \
|
||||
axis_value = (Sint16)HIDAPI_RemapVal(v, 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, 0)
|
||||
READ_AXIS(4, SDL_CONTROLLER_AXIS_LEFTY, 0)
|
||||
READ_AXIS(6, SDL_CONTROLLER_AXIS_RIGHTX, 1)
|
||||
READ_AXIS(5, SDL_CONTROLLER_AXIS_RIGHTY, 1)
|
||||
READ_AXIS(7, SDL_CONTROLLER_AXIS_TRIGGERLEFT, 0)
|
||||
READ_AXIS(8, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, 0)
|
||||
#undef READ_AXIS
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverGameCube_HandleNintendoPacket(SDL_HIDAPI_Device *device, SDL_DriverGameCube_Context *ctx, Uint8 *packet, int size)
|
||||
{
|
||||
SDL_Joystick *joystick;
|
||||
Uint8 *curSlot;
|
||||
Uint8 i;
|
||||
Sint16 axis_value;
|
||||
|
||||
if (size < 37 || packet[0] != 0x21) {
|
||||
return; /* 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 (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)HIDAPI_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
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverGameCube_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context;
|
||||
Uint8 packet[USB_PACKET_LENGTH];
|
||||
int size;
|
||||
|
||||
/* Read input packet */
|
||||
while ((size = SDL_hid_read_timeout(device->dev, packet, sizeof(packet), 0)) > 0) {
|
||||
#ifdef DEBUG_GAMECUBE_PROTOCOL
|
||||
//HIDAPI_DumpPacket("Nintendo GameCube packet: size = %d", packet, size);
|
||||
#endif
|
||||
if (ctx->pc_mode) {
|
||||
HIDAPI_DriverGameCube_HandleJoystickPacket(device, ctx, packet, size);
|
||||
} else {
|
||||
HIDAPI_DriverGameCube_HandleNintendoPacket(device, ctx, packet, size);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
if (ctx->pc_mode) {
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_CONTROLLERS; i += 1) {
|
||||
if (joystick->instance_id == ctx->joysticks[i]) {
|
||||
if (ctx->wireless[i]) {
|
||||
return SDL_SetError("Nintendo GameCube WaveBird controllers do not support rumble");
|
||||
}
|
||||
if (!ctx->rumbleAllowed[i]) {
|
||||
return SDL_SetError("Second USB cable for WUP-028 not connected");
|
||||
}
|
||||
if (ctx->useRumbleBrake) {
|
||||
if (low_frequency_rumble == 0 && high_frequency_rumble > 0) {
|
||||
val = 0; /* if only low is 0 we want to do a regular stop*/
|
||||
} else if (low_frequency_rumble == 0 && high_frequency_rumble == 0) {
|
||||
val = 2; /* if both frequencies are 0 we want to do a hard stop */
|
||||
} else {
|
||||
val = 1; /* normal rumble */
|
||||
}
|
||||
} else {
|
||||
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! */
|
||||
return SDL_SetError("Couldn't find joystick");
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverGameCube_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static Uint32
|
||||
HIDAPI_DriverGameCube_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context;
|
||||
Uint32 result = 0;
|
||||
|
||||
if (!ctx->pc_mode) {
|
||||
Uint8 i;
|
||||
|
||||
for (i = 0; i < MAX_CONTROLLERS; i += 1) {
|
||||
if (joystick->instance_id == ctx->joysticks[i]) {
|
||||
if (!ctx->wireless[i] && ctx->rumbleAllowed[i]) {
|
||||
result |= SDL_JOYCAP_RUMBLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverGameCube_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverGameCube_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverGameCube_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
SDL_DelHintCallback(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS,
|
||||
SDL_GameControllerButtonReportingHintChanged, ctx);
|
||||
SDL_DelHintCallback(SDL_HINT_JOYSTICK_GAMECUBE_RUMBLE_BRAKE,
|
||||
SDL_JoystickGameCubeRumbleBrakeHintChanged, ctx);
|
||||
|
||||
SDL_LockMutex(device->dev_lock);
|
||||
{
|
||||
SDL_hid_close(device->dev);
|
||||
device->dev = NULL;
|
||||
|
||||
SDL_free(device->context);
|
||||
device->context = NULL;
|
||||
}
|
||||
SDL_UnlockMutex(device->dev_lock);
|
||||
}
|
||||
|
||||
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverGameCube =
|
||||
{
|
||||
SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE,
|
||||
SDL_TRUE,
|
||||
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_RumbleJoystickTriggers,
|
||||
HIDAPI_DriverGameCube_GetJoystickCapabilities,
|
||||
HIDAPI_DriverGameCube_SetJoystickLED,
|
||||
HIDAPI_DriverGameCube_SendJoystickEffect,
|
||||
HIDAPI_DriverGameCube_SetJoystickSensorsEnabled,
|
||||
HIDAPI_DriverGameCube_CloseJoystick,
|
||||
HIDAPI_DriverGameCube_FreeDevice,
|
||||
};
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI_GAMECUBE */
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
@@ -0,0 +1,463 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2022 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_events.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_LUNA
|
||||
|
||||
/* Define this if you want to log all packets from the controller */
|
||||
/*#define DEBUG_LUNA_PROTOCOL*/
|
||||
|
||||
enum
|
||||
{
|
||||
SDL_CONTROLLER_BUTTON_LUNA_MIC = 15,
|
||||
SDL_CONTROLLER_NUM_LUNA_BUTTONS,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
Uint8 last_state[USB_PACKET_LENGTH];
|
||||
} SDL_DriverLuna_Context;
|
||||
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverLuna_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_AMAZON_LUNA) ? SDL_TRUE : SDL_FALSE;
|
||||
}
|
||||
|
||||
static const char *
|
||||
HIDAPI_DriverLuna_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
|
||||
{
|
||||
return "Amazon Luna Controller";
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverLuna_InitDevice(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
return HIDAPI_JoystickConnected(device, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverLuna_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverLuna_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
|
||||
{
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverLuna_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
SDL_DriverLuna_Context *ctx;
|
||||
|
||||
ctx = (SDL_DriverLuna_Context *)SDL_calloc(1, sizeof(*ctx));
|
||||
if (!ctx) {
|
||||
SDL_OutOfMemory();
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
device->dev = SDL_hid_open_path(device->path, 0);
|
||||
if (!device->dev) {
|
||||
SDL_SetError("Couldn't open %s", device->path);
|
||||
SDL_free(ctx);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
device->context = ctx;
|
||||
|
||||
/* Initialize the joystick capabilities */
|
||||
joystick->nbuttons = SDL_CONTROLLER_NUM_LUNA_BUTTONS;
|
||||
joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
|
||||
joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
|
||||
joystick->serial = NULL;
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverLuna_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
|
||||
{
|
||||
if (device->product_id == BLUETOOTH_PRODUCT_LUNA_CONTROLLER) {
|
||||
/* Same packet as on Xbox One controllers connected via Bluetooth */
|
||||
Uint8 rumble_packet[] = { 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xEB };
|
||||
|
||||
/* Magnitude is 1..100 so scale the 16-bit input here */
|
||||
rumble_packet[4] = low_frequency_rumble / 655;
|
||||
rumble_packet[5] = 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;
|
||||
} else {
|
||||
/* FIXME: Is there a rumble packet over USB? */
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverLuna_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static Uint32
|
||||
HIDAPI_DriverLuna_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
Uint32 result = 0;
|
||||
|
||||
if (device->product_id == BLUETOOTH_PRODUCT_LUNA_CONTROLLER) {
|
||||
result |= SDL_JOYCAP_RUMBLE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverLuna_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverLuna_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverLuna_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverLuna_HandleUSBStatePacket(SDL_Joystick *joystick, SDL_DriverLuna_Context *ctx, Uint8 *data, int size)
|
||||
{
|
||||
if (ctx->last_state[1] != data[1]) {
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[1] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[1] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[1] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[1] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[1] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[1] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[1] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[1] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
|
||||
}
|
||||
if (ctx->last_state[2] != data[2]) {
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LUNA_MIC, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[2] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
|
||||
}
|
||||
|
||||
if (ctx->last_state[3] != data[3]) {
|
||||
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[3] & 0xf) {
|
||||
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);
|
||||
}
|
||||
|
||||
#define READ_STICK_AXIS(offset) \
|
||||
(data[offset] == 0x7f ? 0 : \
|
||||
(Sint16)HIDAPI_RemapVal((float)data[offset], 0x00, 0xff, SDL_MIN_SINT16, SDL_MAX_SINT16))
|
||||
{
|
||||
Sint16 axis = READ_STICK_AXIS(4);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
|
||||
axis = READ_STICK_AXIS(5);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
|
||||
axis = READ_STICK_AXIS(6);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
|
||||
axis = READ_STICK_AXIS(7);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
|
||||
}
|
||||
#undef READ_STICK_AXIS
|
||||
|
||||
#define READ_TRIGGER_AXIS(offset) \
|
||||
(Sint16)HIDAPI_RemapVal((float)data[offset], 0x00, 0xff, SDL_MIN_SINT16, SDL_MAX_SINT16)
|
||||
{
|
||||
Sint16 axis = READ_TRIGGER_AXIS(8);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
|
||||
axis = READ_TRIGGER_AXIS(9);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
|
||||
}
|
||||
#undef READ_TRIGGER_AXIS
|
||||
|
||||
SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverLuna_HandleBluetoothStatePacket(SDL_Joystick *joystick, SDL_DriverLuna_Context *ctx, Uint8 *data, int size)
|
||||
{
|
||||
if (size >= 2 && data[0] == 0x02) {
|
||||
/* Home button has dedicated report */
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[1] & 0x1) ? SDL_PRESSED : SDL_RELEASED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (size >= 2 && data[0] == 0x04) {
|
||||
/* Battery level report */
|
||||
int level = data[1] * 100 / 0xFF;
|
||||
if (level == 0) {
|
||||
joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY;
|
||||
}
|
||||
else if (level <= 20) {
|
||||
joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW;
|
||||
}
|
||||
else if (level <= 70) {
|
||||
joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM;
|
||||
}
|
||||
else {
|
||||
joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (size < 17 || data[0] != 0x01) {
|
||||
/* We don't know how to handle this report */
|
||||
return;
|
||||
}
|
||||
|
||||
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] & 0xf) {
|
||||
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);
|
||||
}
|
||||
|
||||
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->last_state[16] != data[16]) {
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[16] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LUNA_MIC, (data[16] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
||||
}
|
||||
|
||||
#define READ_STICK_AXIS(offset) \
|
||||
(data[offset] == 0x7f ? 0 : \
|
||||
(Sint16)HIDAPI_RemapVal((float)data[offset], 0x00, 0xff, SDL_MIN_SINT16, SDL_MAX_SINT16))
|
||||
{
|
||||
Sint16 axis = READ_STICK_AXIS(2);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
|
||||
axis = READ_STICK_AXIS(4);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
|
||||
axis = READ_STICK_AXIS(6);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
|
||||
axis = READ_STICK_AXIS(8);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
|
||||
}
|
||||
#undef READ_STICK_AXIS
|
||||
|
||||
#define READ_TRIGGER_AXIS(offset) \
|
||||
(Sint16)HIDAPI_RemapVal((float)((int)(((data[offset] | (data[offset + 1] << 8)) & 0x3ff) - 0x200)), 0x00 - 0x200, 0x3ff - 0x200, SDL_MIN_SINT16, SDL_MAX_SINT16)
|
||||
{
|
||||
Sint16 axis = READ_TRIGGER_AXIS(9);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
|
||||
axis = READ_TRIGGER_AXIS(11);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
|
||||
}
|
||||
#undef READ_TRIGGER_AXIS
|
||||
|
||||
SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverLuna_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
SDL_DriverLuna_Context *ctx = (SDL_DriverLuna_Context *)device->context;
|
||||
SDL_Joystick *joystick = NULL;
|
||||
Uint8 data[USB_PACKET_LENGTH];
|
||||
int size = 0;
|
||||
|
||||
if (device->num_joysticks > 0) {
|
||||
joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
|
||||
}
|
||||
if (!joystick) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
|
||||
#ifdef DEBUG_LUNA_PROTOCOL
|
||||
HIDAPI_DumpPacket("Amazon Luna packet: size = %d", data, size);
|
||||
#endif
|
||||
switch (size) {
|
||||
case 10:
|
||||
HIDAPI_DriverLuna_HandleUSBStatePacket(joystick, ctx, data, size);
|
||||
break;
|
||||
default:
|
||||
HIDAPI_DriverLuna_HandleBluetoothStatePacket(joystick, ctx, data, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (size < 0) {
|
||||
/* Read error, device is disconnected */
|
||||
HIDAPI_JoystickDisconnected(device, joystick->instance_id);
|
||||
}
|
||||
return (size >= 0);
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverLuna_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
SDL_LockMutex(device->dev_lock);
|
||||
{
|
||||
if (device->dev) {
|
||||
SDL_hid_close(device->dev);
|
||||
device->dev = NULL;
|
||||
}
|
||||
|
||||
SDL_free(device->context);
|
||||
device->context = NULL;
|
||||
}
|
||||
SDL_UnlockMutex(device->dev_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverLuna_FreeDevice(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
}
|
||||
|
||||
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverLuna =
|
||||
{
|
||||
SDL_HINT_JOYSTICK_HIDAPI_LUNA,
|
||||
SDL_TRUE,
|
||||
SDL_TRUE,
|
||||
HIDAPI_DriverLuna_IsSupportedDevice,
|
||||
HIDAPI_DriverLuna_GetDeviceName,
|
||||
HIDAPI_DriverLuna_InitDevice,
|
||||
HIDAPI_DriverLuna_GetDevicePlayerIndex,
|
||||
HIDAPI_DriverLuna_SetDevicePlayerIndex,
|
||||
HIDAPI_DriverLuna_UpdateDevice,
|
||||
HIDAPI_DriverLuna_OpenJoystick,
|
||||
HIDAPI_DriverLuna_RumbleJoystick,
|
||||
HIDAPI_DriverLuna_RumbleJoystickTriggers,
|
||||
HIDAPI_DriverLuna_GetJoystickCapabilities,
|
||||
HIDAPI_DriverLuna_SetJoystickLED,
|
||||
HIDAPI_DriverLuna_SendJoystickEffect,
|
||||
HIDAPI_DriverLuna_SetJoystickSensorsEnabled,
|
||||
HIDAPI_DriverLuna_CloseJoystick,
|
||||
HIDAPI_DriverLuna_FreeDevice,
|
||||
};
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI_LUNA */
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
+625
-227
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2022 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_thread.h"
|
||||
#include "SDL_timer.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) {
|
||||
#ifdef DEBUG_RUMBLE
|
||||
HIDAPI_DumpPacket("Rumble packet: size = %d", request->data, request->size);
|
||||
#endif
|
||||
SDL_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);
|
||||
|
||||
/* Make sure we're not starving report reads when there's lots of rumble */
|
||||
SDL_Delay(10);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
SDL_HIDAPI_StopRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
|
||||
{
|
||||
SDL_HIDAPI_RumbleRequest *request;
|
||||
|
||||
SDL_AtomicSet(&ctx->running, SDL_FALSE);
|
||||
|
||||
if (ctx->thread) {
|
||||
int result;
|
||||
|
||||
SDL_SemPost(ctx->request_sem);
|
||||
SDL_WaitThread(ctx->thread, &result);
|
||||
ctx->thread = NULL;
|
||||
}
|
||||
|
||||
SDL_LockMutex(ctx->lock);
|
||||
while (ctx->requests_tail) {
|
||||
request = ctx->requests_tail;
|
||||
if (request == ctx->requests_head) {
|
||||
ctx->requests_head = NULL;
|
||||
}
|
||||
ctx->requests_tail = request->prev;
|
||||
|
||||
(void)SDL_AtomicDecRef(&request->device->rumble_pending);
|
||||
SDL_free(request);
|
||||
}
|
||||
SDL_UnlockMutex(ctx->lock);
|
||||
|
||||
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, *found;
|
||||
|
||||
found = NULL;
|
||||
for (request = ctx->requests_tail; request; request = request->prev) {
|
||||
if (request->device == device) {
|
||||
found = request;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
*data = found->data;
|
||||
*size = &found->size;
|
||||
*maximum_size = sizeof(found->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: */
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2022 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: */
|
||||
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2022 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_events.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_STADIA
|
||||
|
||||
/* Define this if you want to log all packets from the controller */
|
||||
/*#define DEBUG_STADIA_PROTOCOL*/
|
||||
|
||||
enum
|
||||
{
|
||||
SDL_CONTROLLER_BUTTON_STADIA_SHARE = 15,
|
||||
SDL_CONTROLLER_BUTTON_STADIA_GOOGLE_ASSISTANT,
|
||||
SDL_CONTROLLER_NUM_STADIA_BUTTONS,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
Uint8 last_state[USB_PACKET_LENGTH];
|
||||
} SDL_DriverStadia_Context;
|
||||
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverStadia_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_GOOGLE_STADIA) ? SDL_TRUE : SDL_FALSE;
|
||||
}
|
||||
|
||||
static const char *
|
||||
HIDAPI_DriverStadia_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
|
||||
{
|
||||
return "Google Stadia Controller";
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverStadia_InitDevice(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
return HIDAPI_JoystickConnected(device, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverStadia_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverStadia_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
|
||||
{
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverStadia_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
SDL_DriverStadia_Context *ctx;
|
||||
|
||||
ctx = (SDL_DriverStadia_Context *)SDL_calloc(1, sizeof(*ctx));
|
||||
if (!ctx) {
|
||||
SDL_OutOfMemory();
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
device->dev = SDL_hid_open_path(device->path, 0);
|
||||
if (!device->dev) {
|
||||
SDL_SetError("Couldn't open %s", device->path);
|
||||
SDL_free(ctx);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
device->context = ctx;
|
||||
|
||||
/* Initialize the joystick capabilities */
|
||||
joystick->nbuttons = SDL_CONTROLLER_NUM_STADIA_BUTTONS;
|
||||
joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
|
||||
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverStadia_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
|
||||
{
|
||||
Uint8 rumble_packet[] = { 0x05, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
rumble_packet[1] = (low_frequency_rumble & 0xFF);
|
||||
rumble_packet[2] = (low_frequency_rumble >> 8);
|
||||
rumble_packet[3] = (high_frequency_rumble & 0xFF);
|
||||
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");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverStadia_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static Uint32
|
||||
HIDAPI_DriverStadia_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
return SDL_JOYCAP_RUMBLE;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverStadia_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverStadia_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverStadia_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverStadia_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverStadia_Context *ctx, Uint8 *data, int size)
|
||||
{
|
||||
Sint16 axis;
|
||||
|
||||
// The format is the same but the original FW will send 10 bytes and January '21 FW update will send 11
|
||||
if (size < 10 || data[0] != 0x03) {
|
||||
/* We don't know how to handle this report */
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->last_state[1] != data[1]) {
|
||||
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[1]) {
|
||||
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[2] != data[2]) {
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[2] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[2] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[2] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_STADIA_SHARE, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_STADIA_GOOGLE_ASSISTANT, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
||||
}
|
||||
|
||||
if (ctx->last_state[3] != data[3]) {
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[3] & 0x40) ? 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] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[3] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[3] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[3] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[3] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||
}
|
||||
|
||||
#define READ_STICK_AXIS(offset) \
|
||||
(data[offset] == 0x80 ? 0 : \
|
||||
(Sint16)HIDAPI_RemapVal((float)((int)data[offset] - 0x80), 0x01 - 0x80, 0xff - 0x80, SDL_MIN_SINT16, SDL_MAX_SINT16))
|
||||
{
|
||||
axis = READ_STICK_AXIS(4);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
|
||||
axis = READ_STICK_AXIS(5);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
|
||||
axis = READ_STICK_AXIS(6);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
|
||||
axis = READ_STICK_AXIS(7);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
|
||||
}
|
||||
#undef READ_STICK_AXIS
|
||||
|
||||
#define READ_TRIGGER_AXIS(offset) \
|
||||
(Sint16)(((int)data[offset] * 257) - 32768)
|
||||
{
|
||||
axis = READ_TRIGGER_AXIS(8);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
|
||||
axis = READ_TRIGGER_AXIS(9);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
|
||||
}
|
||||
#undef READ_TRIGGER_AXIS
|
||||
|
||||
SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverStadia_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
SDL_DriverStadia_Context *ctx = (SDL_DriverStadia_Context *)device->context;
|
||||
SDL_Joystick *joystick = NULL;
|
||||
Uint8 data[USB_PACKET_LENGTH];
|
||||
int size = 0;
|
||||
|
||||
if (device->num_joysticks > 0) {
|
||||
joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
|
||||
}
|
||||
if (!joystick) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
|
||||
#ifdef DEBUG_STADIA_PROTOCOL
|
||||
HIDAPI_DumpPacket("Google Stadia packet: size = %d", data, size);
|
||||
#endif
|
||||
HIDAPI_DriverStadia_HandleStatePacket(joystick, ctx, data, size);
|
||||
}
|
||||
|
||||
if (size < 0) {
|
||||
/* Read error, device is disconnected */
|
||||
HIDAPI_JoystickDisconnected(device, joystick->instance_id);
|
||||
}
|
||||
return (size >= 0);
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverStadia_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
SDL_LockMutex(device->dev_lock);
|
||||
{
|
||||
if (device->dev) {
|
||||
SDL_hid_close(device->dev);
|
||||
device->dev = NULL;
|
||||
}
|
||||
|
||||
SDL_free(device->context);
|
||||
device->context = NULL;
|
||||
}
|
||||
SDL_UnlockMutex(device->dev_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverStadia_FreeDevice(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
}
|
||||
|
||||
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverStadia =
|
||||
{
|
||||
SDL_HINT_JOYSTICK_HIDAPI_STADIA,
|
||||
SDL_TRUE,
|
||||
SDL_TRUE,
|
||||
HIDAPI_DriverStadia_IsSupportedDevice,
|
||||
HIDAPI_DriverStadia_GetDeviceName,
|
||||
HIDAPI_DriverStadia_InitDevice,
|
||||
HIDAPI_DriverStadia_GetDevicePlayerIndex,
|
||||
HIDAPI_DriverStadia_SetDevicePlayerIndex,
|
||||
HIDAPI_DriverStadia_UpdateDevice,
|
||||
HIDAPI_DriverStadia_OpenJoystick,
|
||||
HIDAPI_DriverStadia_RumbleJoystick,
|
||||
HIDAPI_DriverStadia_RumbleJoystickTriggers,
|
||||
HIDAPI_DriverStadia_GetJoystickCapabilities,
|
||||
HIDAPI_DriverStadia_SetJoystickLED,
|
||||
HIDAPI_DriverStadia_SendJoystickEffect,
|
||||
HIDAPI_DriverStadia_SetJoystickSensorsEnabled,
|
||||
HIDAPI_DriverStadia_CloseJoystick,
|
||||
HIDAPI_DriverStadia_FreeDevice,
|
||||
};
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI_STADIA */
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
File diff suppressed because it is too large
Load Diff
+813
-139
File diff suppressed because it is too large
Load Diff
+175
-600
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
|
||||
Copyright (C) 1997-2022 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
|
||||
@@ -23,286 +23,135 @@
|
||||
#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
|
||||
|
||||
#define USB_PACKET_LENGTH 64
|
||||
/* Define this if you want to log all packets from the controller */
|
||||
/*#define DEBUG_XBOX_PROTOCOL*/
|
||||
|
||||
|
||||
typedef struct {
|
||||
Uint8 last_state[USB_PACKET_LENGTH];
|
||||
Uint32 rumble_expiration;
|
||||
#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()
|
||||
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)
|
||||
{
|
||||
return xinput_slots != 0x0F;
|
||||
}
|
||||
const int XB360W_IFACE_PROTOCOL = 129; /* Wireless */
|
||||
|
||||
static Uint8
|
||||
HIDAPI_DriverXbox360_GuessXInputSlot(WORD wButtons)
|
||||
{
|
||||
DWORD user_index;
|
||||
int match_count;
|
||||
Uint8 match_slot;
|
||||
|
||||
if (!XINPUTGETSTATE) {
|
||||
return XUSER_INDEX_ANY;
|
||||
if (vendor_id == USB_VENDOR_NVIDIA) {
|
||||
/* This is the NVIDIA Shield controller which doesn't talk Xbox controller protocol */
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
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 ((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 (match_count == 1) {
|
||||
return match_slot;
|
||||
if (interface_number > 0) {
|
||||
/* This is the chatpad or other input interface, not the Xbox 360 interface */
|
||||
return SDL_FALSE;
|
||||
}
|
||||
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 */
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverXbox360_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number)
|
||||
{
|
||||
#if defined(__MACOSX__) || defined(__WIN32__)
|
||||
if (vendor_id == 0x045e && product_id == 0x028e && version == 1) {
|
||||
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;
|
||||
}
|
||||
return SDL_IsJoystickXbox360(vendor_id, product_id) || SDL_IsJoystickXboxOne(vendor_id, product_id);
|
||||
#endif
|
||||
#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 (SDL_IsJoystickBluetoothXboxOne(vendor_id, product_id)) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
return (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE) ? SDL_TRUE : SDL_FALSE;
|
||||
#else
|
||||
return SDL_IsJoystickXbox360(vendor_id, product_id);
|
||||
return (type == SDL_CONTROLLER_TYPE_XBOX360) ? SDL_TRUE : SDL_FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
static const char *
|
||||
HIDAPI_DriverXbox360_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
|
||||
{
|
||||
return HIDAPI_XboxControllerName(vendor_id, product_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot)
|
||||
static SDL_bool SetSlotLED(SDL_hid_device *dev, Uint8 slot)
|
||||
{
|
||||
const Uint8 led_packet[] = { 0x01, 0x03, (2 + slot) };
|
||||
const SDL_bool blink = SDL_FALSE;
|
||||
Uint8 mode = (blink ? 0x02 : 0x06) + slot;
|
||||
Uint8 led_packet[] = { 0x01, 0x03, 0x00 };
|
||||
|
||||
if (hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) {
|
||||
led_packet[2] = mode;
|
||||
if (SDL_hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverXbox360_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
if (player_index >= 0) {
|
||||
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;
|
||||
}
|
||||
#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;
|
||||
|
||||
device->dev = SDL_hid_open_path(device->path, 0);
|
||||
if (!device->dev) {
|
||||
SDL_SetError("Couldn't open %s", device->path);
|
||||
SDL_free(ctx);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
ctx->xinput_slot = XUSER_INDEX_ANY;
|
||||
#endif
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
|
||||
HIDAPI_DriverXbox360_InitWindowsGamingInput(ctx);
|
||||
#endif
|
||||
*context = ctx;
|
||||
device->context = ctx;
|
||||
|
||||
/* Set the controller LED */
|
||||
SetSlotLED(dev, (joystick->instance_id % 4));
|
||||
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->nbuttons = 15;
|
||||
joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
|
||||
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
|
||||
|
||||
@@ -310,248 +159,77 @@ HIDAPI_DriverXbox360_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverXbox360_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
|
||||
HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
|
||||
{
|
||||
SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context;
|
||||
|
||||
#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__
|
||||
/* 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 };
|
||||
if (SDL_IsJoystickBluetoothXboxOne(device->vendor_id, device->product_id)) {
|
||||
Uint8 rumble_packet[] = { 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00 };
|
||||
|
||||
rumble_packet[6+2] = (low_frequency_rumble >> 8);
|
||||
rumble_packet[6+3] = (high_frequency_rumble >> 8);
|
||||
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);
|
||||
#endif
|
||||
|
||||
if (hid_write(dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
|
||||
if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
|
||||
return SDL_SetError("Couldn't send rumble packet");
|
||||
}
|
||||
#endif /* __WIN32__ */
|
||||
|
||||
if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
|
||||
ctx->rumble_expiration = SDL_GetTicks() + duration_ms;
|
||||
} else {
|
||||
ctx->rumble_expiration = 0;
|
||||
}
|
||||
#endif
|
||||
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)
|
||||
static int
|
||||
HIDAPI_DriverXbox360_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
|
||||
{
|
||||
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)));
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static Uint32
|
||||
HIDAPI_DriverXbox360_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
/* Doesn't have an RGB LED, so don't return SDL_JOYCAP_LED here */
|
||||
return SDL_JOYCAP_RUMBLE;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverXbox360_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverXbox360_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverXbox360_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
#else
|
||||
|
||||
static void
|
||||
HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
|
||||
HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
|
||||
{
|
||||
Sint16 axis;
|
||||
#ifdef __MACOSX__
|
||||
@@ -602,182 +280,79 @@ HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev,
|
||||
|
||||
SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
|
||||
}
|
||||
#endif /* __WIN32__ */
|
||||
|
||||
#ifdef __MACOSX__
|
||||
static void
|
||||
HIDAPI_DriverXboxOneS_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_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->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_DriverXboxOneS_HandleGuidePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
|
||||
{
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[1] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||
}
|
||||
#endif /* __MACOSX__ */
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverXbox360_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
|
||||
HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context;
|
||||
SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
|
||||
SDL_Joystick *joystick = NULL;
|
||||
Uint8 data[USB_PACKET_LENGTH];
|
||||
int size;
|
||||
int size = 0;
|
||||
|
||||
while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) {
|
||||
#ifdef __WIN32__
|
||||
HIDAPI_DriverXbox360_HandleStatePacket(joystick, dev, ctx, data, size);
|
||||
#else
|
||||
switch (data[0]) {
|
||||
case 0x00:
|
||||
HIDAPI_DriverXbox360_HandleStatePacket(joystick, dev, ctx, data, size);
|
||||
break;
|
||||
#ifdef __MACOSX__
|
||||
case 0x01:
|
||||
HIDAPI_DriverXboxOneS_HandleStatePacket(joystick, dev, ctx, data, size);
|
||||
break;
|
||||
case 0x02:
|
||||
HIDAPI_DriverXboxOneS_HandleGuidePacket(joystick, dev, ctx, data, size);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
#ifdef DEBUG_JOYSTICK
|
||||
SDL_Log("Unknown Xbox 360 packet, size = %d\n", size);
|
||||
SDL_Log("%.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n",
|
||||
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
|
||||
data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15], data[16]);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#endif /* __WIN32__ */
|
||||
if (device->num_joysticks > 0) {
|
||||
joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
|
||||
}
|
||||
if (!joystick) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
if (ctx->rumble_expiration) {
|
||||
Uint32 now = SDL_GetTicks();
|
||||
if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) {
|
||||
HIDAPI_DriverXbox360_Rumble(joystick, dev, context, 0, 0, 0);
|
||||
while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
|
||||
#ifdef DEBUG_XBOX_PROTOCOL
|
||||
HIDAPI_DumpPacket("Xbox 360 packet: size = %d", data, size);
|
||||
#endif
|
||||
if (data[0] == 0x00) {
|
||||
HIDAPI_DriverXbox360_HandleStatePacket(joystick, ctx, data, size);
|
||||
}
|
||||
}
|
||||
|
||||
if (size < 0) {
|
||||
/* Read error, device is disconnected */
|
||||
HIDAPI_JoystickDisconnected(device, joystick->instance_id);
|
||||
}
|
||||
return (size >= 0);
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverXbox360_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
|
||||
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 *)context;
|
||||
#endif
|
||||
SDL_LockMutex(device->dev_lock);
|
||||
{
|
||||
if (device->dev) {
|
||||
SDL_hid_close(device->dev);
|
||||
device->dev = NULL;
|
||||
}
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
|
||||
if (ctx->xinput_enabled) {
|
||||
HIDAPI_DriverXbox360_MarkXInputSlotFree(ctx->xinput_slot);
|
||||
WIN_UnloadXInputDLL();
|
||||
SDL_free(device->context);
|
||||
device->context = NULL;
|
||||
}
|
||||
#endif
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
|
||||
HIDAPI_DriverXbox360_InitWindowsGamingInput(ctx);
|
||||
#endif
|
||||
SDL_free(context);
|
||||
SDL_UnlockMutex(device->dev_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverXbox360_FreeDevice(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
}
|
||||
|
||||
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 =
|
||||
{
|
||||
SDL_HINT_JOYSTICK_HIDAPI_XBOX,
|
||||
SDL_TRUE,
|
||||
SDL_TRUE,
|
||||
HIDAPI_DriverXbox360_IsSupportedDevice,
|
||||
HIDAPI_DriverXbox360_GetDeviceName,
|
||||
HIDAPI_DriverXbox360_Init,
|
||||
HIDAPI_DriverXbox360_Rumble,
|
||||
HIDAPI_DriverXbox360_Update,
|
||||
HIDAPI_DriverXbox360_Quit
|
||||
HIDAPI_DriverXbox360_InitDevice,
|
||||
HIDAPI_DriverXbox360_GetDevicePlayerIndex,
|
||||
HIDAPI_DriverXbox360_SetDevicePlayerIndex,
|
||||
HIDAPI_DriverXbox360_UpdateDevice,
|
||||
HIDAPI_DriverXbox360_OpenJoystick,
|
||||
HIDAPI_DriverXbox360_RumbleJoystick,
|
||||
HIDAPI_DriverXbox360_RumbleJoystickTriggers,
|
||||
HIDAPI_DriverXbox360_GetJoystickCapabilities,
|
||||
HIDAPI_DriverXbox360_SetJoystickLED,
|
||||
HIDAPI_DriverXbox360_SendJoystickEffect,
|
||||
HIDAPI_DriverXbox360_SetJoystickSensorsEnabled,
|
||||
HIDAPI_DriverXbox360_CloseJoystick,
|
||||
HIDAPI_DriverXbox360_FreeDevice,
|
||||
};
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2022 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_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
|
||||
|
||||
/* Define this if you want to log all packets from the controller */
|
||||
/*#define DEBUG_XBOX_PROTOCOL*/
|
||||
|
||||
|
||||
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) && interface_protocol == 0) ||
|
||||
(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(SDL_hid_device *dev, Uint8 slot)
|
||||
{
|
||||
const SDL_bool blink = SDL_FALSE;
|
||||
Uint8 mode = (blink ? 0x02 : 0x06) + slot;
|
||||
Uint8 led_packet[] = { 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
led_packet[3] = 0x40 + (mode % 0x0e);
|
||||
if (SDL_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 = SDL_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 (SDL_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)
|
||||
{
|
||||
if (!device->dev) {
|
||||
return;
|
||||
}
|
||||
if (player_index >= 0) {
|
||||
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 = 15;
|
||||
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 int
|
||||
HIDAPI_DriverXbox360W_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static Uint32
|
||||
HIDAPI_DriverXbox360W_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
/* Doesn't have an RGB LED, so don't return SDL_JOYCAP_LED here */
|
||||
return SDL_JOYCAP_RUMBLE;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverXbox360W_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverXbox360W_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverXbox360W_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverXbox360W_HandleStatePacket(SDL_Joystick *joystick, SDL_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 = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
|
||||
#ifdef DEBUG_XBOX_PROTOCOL
|
||||
HIDAPI_DumpPacket("Xbox 360 wireless packet: size = %d", data, size);
|
||||
#endif
|
||||
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)
|
||||
{
|
||||
SDL_LockMutex(device->dev_lock);
|
||||
{
|
||||
SDL_hid_close(device->dev);
|
||||
device->dev = NULL;
|
||||
|
||||
SDL_free(device->context);
|
||||
device->context = NULL;
|
||||
}
|
||||
SDL_UnlockMutex(device->dev_lock);
|
||||
}
|
||||
|
||||
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360W =
|
||||
{
|
||||
SDL_HINT_JOYSTICK_HIDAPI_XBOX,
|
||||
SDL_TRUE,
|
||||
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_RumbleJoystickTriggers,
|
||||
HIDAPI_DriverXbox360W_GetJoystickCapabilities,
|
||||
HIDAPI_DriverXbox360W_SetJoystickLED,
|
||||
HIDAPI_DriverXbox360W_SendJoystickEffect,
|
||||
HIDAPI_DriverXbox360W_SetJoystickSensorsEnabled,
|
||||
HIDAPI_DriverXbox360W_CloseJoystick,
|
||||
HIDAPI_DriverXbox360W_FreeDevice,
|
||||
};
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
+990
-158
File diff suppressed because it is too large
Load Diff
+655
-674
File diff suppressed because it is too large
Load Diff
+85
-20
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
|
||||
Copyright (C) 1997-2022 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
|
||||
@@ -23,51 +23,116 @@
|
||||
#ifndef SDL_JOYSTICK_HIDAPI_H
|
||||
#define SDL_JOYSTICK_HIDAPI_H
|
||||
|
||||
#include "../../hidapi/hidapi/hidapi.h"
|
||||
#include "SDL_atomic.h"
|
||||
#include "SDL_mutex.h"
|
||||
#include "SDL_joystick.h"
|
||||
#include "SDL_gamecontroller.h"
|
||||
#include "SDL_hidapi.h"
|
||||
#include "../usb_ids.h"
|
||||
|
||||
/* This is the full set of HIDAPI drivers available */
|
||||
#define SDL_JOYSTICK_HIDAPI_GAMECUBE
|
||||
#define SDL_JOYSTICK_HIDAPI_LUNA
|
||||
#define SDL_JOYSTICK_HIDAPI_PS4
|
||||
#define SDL_JOYSTICK_HIDAPI_PS5
|
||||
#define SDL_JOYSTICK_HIDAPI_STADIA
|
||||
#define SDL_JOYSTICK_HIDAPI_SWITCH
|
||||
#define SDL_JOYSTICK_HIDAPI_XBOX360
|
||||
#define SDL_JOYSTICK_HIDAPI_XBOXONE
|
||||
|
||||
#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
|
||||
#if defined(__IPHONEOS__) || defined(__TVOS__) || defined(__ANDROID__)
|
||||
/* Very basic Steam Controller support on mobile devices */
|
||||
#define SDL_JOYSTICK_HIDAPI_STEAM
|
||||
#endif
|
||||
|
||||
#ifdef __MACOSX__
|
||||
/* On Mac OS X, Xbox One controllers are handled by the Xbox 360 driver */
|
||||
#undef SDL_JOYSTICK_HIDAPI_XBOXONE
|
||||
#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;
|
||||
char *serial;
|
||||
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;
|
||||
SDL_hid_device *dev;
|
||||
SDL_atomic_t rumble_pending;
|
||||
int num_joysticks;
|
||||
SDL_JoystickID *joysticks;
|
||||
|
||||
/* Used during scanning for device changes */
|
||||
SDL_bool seen;
|
||||
|
||||
/* Used to flag that the device is being updated */
|
||||
SDL_bool updating;
|
||||
|
||||
struct _SDL_HIDAPI_Device *next;
|
||||
} SDL_HIDAPI_Device;
|
||||
|
||||
typedef struct _SDL_HIDAPI_DeviceDriver
|
||||
{
|
||||
const char *hint;
|
||||
SDL_bool enabled;
|
||||
SDL_bool (*IsSupportedDevice)(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number);
|
||||
SDL_bool enabled_default;
|
||||
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 (*Init)(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context);
|
||||
int (*Rumble)(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
|
||||
SDL_bool (*Update)(SDL_Joystick *joystick, hid_device *dev, void *context);
|
||||
void (*Quit)(SDL_Joystick *joystick, hid_device *dev, void *context);
|
||||
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);
|
||||
int (*RumbleJoystickTriggers)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble);
|
||||
Uint32 (*GetJoystickCapabilities)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick);
|
||||
int (*SetJoystickLED)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue);
|
||||
int (*SendJoystickEffect)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size);
|
||||
int (*SetJoystickSensorsEnabled)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled);
|
||||
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_DriverGameCube;
|
||||
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverLuna;
|
||||
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4;
|
||||
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS5;
|
||||
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverStadia;
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
/* Return true if a HID device is present and supported as a joystick of the given type */
|
||||
extern SDL_bool HIDAPI_IsDeviceTypePresent(SDL_GameControllerType type);
|
||||
|
||||
/* Return the name of an Xbox 360 or Xbox One controller */
|
||||
extern const char *HIDAPI_XboxControllerName(Uint16 vendor_id, Uint16 product_id);
|
||||
/* 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);
|
||||
|
||||
extern void HIDAPI_DumpPacket(const char *prefix, Uint8 *data, int size);
|
||||
|
||||
extern float HIDAPI_RemapVal(float val, float val_min, float val_max, float output_min, float output_max);
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI_H */
|
||||
|
||||
|
||||
@@ -0,0 +1,499 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 2021 Valve Corporation
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 2020 Valve Corporation
|
||||
|
||||
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.
|
||||
*/
|
||||
#ifndef _CONTROLLER_STRUCTS_
|
||||
#define _CONTROLLER_STRUCTS_
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
#define HID_FEATURE_REPORT_BYTES 64
|
||||
|
||||
// Header for all host <==> target messages
|
||||
typedef struct
|
||||
{
|
||||
unsigned char type;
|
||||
unsigned char length;
|
||||
} FeatureReportHeader;
|
||||
|
||||
// Generic controller attribute structure
|
||||
typedef struct
|
||||
{
|
||||
unsigned char attributeTag;
|
||||
uint32_t attributeValue;
|
||||
} ControllerAttribute;
|
||||
|
||||
// Generic controller settings structure
|
||||
typedef struct
|
||||
{
|
||||
ControllerAttribute attributes[ ( HID_FEATURE_REPORT_BYTES - sizeof( FeatureReportHeader ) ) / sizeof( ControllerAttribute ) ];
|
||||
} MsgGetAttributes;
|
||||
|
||||
|
||||
// This is the only message struct that application code should use to interact with feature request messages. Any new
|
||||
// messages should be added to the union. The structures defined here should correspond to the ones defined in
|
||||
// ValveDeviceCore.cpp.
|
||||
//
|
||||
typedef struct
|
||||
{
|
||||
FeatureReportHeader header;
|
||||
union
|
||||
{
|
||||
MsgGetAttributes getAttributes;
|
||||
} payload;
|
||||
|
||||
} FeatureReportMsg;
|
||||
|
||||
// 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
|
||||
Reference in New Issue
Block a user