SDL: Added missing file

From aaee6e242c6309e33ac67c70ed96183beef6a75c Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Tue, 23 Aug 2022 22:56:32 -0700
Subject: [PATCH] Added missing file

---
 src/joystick/hidapi/SDL_hidapi_ps3.c | 497 +++++++++++++++++++++++++++
 1 file changed, 497 insertions(+)
 create mode 100644 src/joystick/hidapi/SDL_hidapi_ps3.c

diff --git a/src/joystick/hidapi/SDL_hidapi_ps3.c b/src/joystick/hidapi/SDL_hidapi_ps3.c
new file mode 100644
index 00000000000..e41238afef7
--- /dev/null
+++ b/src/joystick/hidapi/SDL_hidapi_ps3.c
@@ -0,0 +1,497 @@
+/*
+  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.
+*/
+/* This driver supports both simplified reports and the extended input reports enabled by Steam.
+   Code and logic contributed by Valve Corporation under the SDL zlib license.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_HIDAPI
+
+#include "SDL_hints.h"
+#include "SDL_events.h"
+#include "SDL_timer.h"
+#include "SDL_joystick.h"
+#include "SDL_gamecontroller.h"
+#include "../../SDL_hints_c.h"
+#include "../SDL_sysjoystick.h"
+#include "SDL_hidapijoystick_c.h"
+#include "SDL_hidapi_rumble.h"
+
+
+#ifdef SDL_JOYSTICK_HIDAPI_PS3
+
+/* Define this if you want to log all packets from the controller */
+/*#define DEBUG_PS3_PROTOCOL*/
+//#define DEBUG_PS3_PROTOCOL
+
+#define LOAD16(A, B)  (Sint16)((Uint16)(A) | (((Uint16)(B)) << 8))
+
+typedef enum
+{
+    k_EPS3ReportIdState = 1,
+    k_EPS3ReportIdEffects = 1,
+} EPS3ReportId;
+
+typedef struct {
+    SDL_HIDAPI_Device *device;
+    SDL_Joystick *joystick;
+    SDL_bool is_bluetooth;
+    SDL_bool is_shanwan;
+    SDL_bool report_sensors;
+    SDL_bool effects_updated;
+    Uint8 last_state[USB_PACKET_LENGTH];
+    int player_index;
+    Uint8 rumble_left;
+    Uint8 rumble_right;
+} SDL_DriverPS3_Context;
+
+
+static int HIDAPI_DriverPS3_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size);
+
+static void
+HIDAPI_DriverPS3_RegisterHints(SDL_HintCallback callback, void *userdata)
+{
+    SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS3, callback, userdata);
+}
+
+static void
+HIDAPI_DriverPS3_UnregisterHints(SDL_HintCallback callback, void *userdata)
+{
+    SDL_DelHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS3, callback, userdata);
+}
+
+static SDL_bool
+HIDAPI_DriverPS3_IsEnabled(void)
+{
+#if 1 /* This is not enabled by default, it needs much more testing */
+    return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS3, SDL_FALSE);
+#else
+    return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS3,
+               SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI,
+                   SDL_HIDAPI_DEFAULT));
+#endif
+}
+
+static SDL_bool
+HIDAPI_DriverPS3_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_SONY && product_id == USB_PRODUCT_SONY_DS3) {
+        return SDL_TRUE;
+    }
+    return SDL_FALSE;
+}
+
+static const char *
+HIDAPI_DriverPS3_GetDeviceName(const char *name, Uint16 vendor_id, Uint16 product_id)
+{
+    if (vendor_id == USB_VENDOR_SONY) {
+        if (name && SDL_strncasecmp(name, "ShanWan", 7) == 0) {
+            return "ShanWan PS3 Controller";
+        } else {
+            return "PS3 Controller";
+        }
+    }
+    return NULL;
+}
+
+static int ReadFeatureReport(SDL_hid_device *dev, Uint8 report_id, Uint8 *report, size_t length)
+{
+    SDL_memset(report, 0, length);
+    report[0] = report_id;
+    return SDL_hid_get_feature_report(dev, report, length);
+}
+
+static int SendFeatureReport(SDL_hid_device *dev, Uint8 *report, size_t length)
+{
+    return SDL_hid_send_feature_report(dev, report, length);
+}
+
+static SDL_bool
+HIDAPI_DriverPS3_InitDevice(SDL_HIDAPI_Device *device)
+{
+    return HIDAPI_JoystickConnected(device, NULL);
+}
+
+static int
+HIDAPI_DriverPS3_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
+{
+    return -1;
+}
+
+static int
+HIDAPI_DriverPS3_UpdateEffects(SDL_HIDAPI_Device *device)
+{
+    SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;
+
+    Uint8 effects[] = {
+        0x01, 0xff, 0x00, 0xff, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00,
+        0xff, 0x27, 0x10, 0x00, 0x32,
+        0xff, 0x27, 0x10, 0x00, 0x32,
+        0xff, 0x27, 0x10, 0x00, 0x32,
+        0xff, 0x27, 0x10, 0x00, 0x32,
+        0x00, 0x00, 0x00, 0x00, 0x00
+    };
+
+    effects[2] = ctx->rumble_right ? 1 : 0;
+    effects[4] = ctx->rumble_left;
+
+    effects[9] = (0x01 << (1+(ctx->player_index % 4)));
+
+    return HIDAPI_DriverPS3_SendJoystickEffect(device, ctx->joystick, &effects, sizeof(effects));
+}
+
+static void
+HIDAPI_DriverPS3_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
+{
+    SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;
+
+    if (!ctx) {
+        return;
+    }
+
+    ctx->player_index = player_index;
+
+    /* This will set the new LED state based on the new player index */
+    HIDAPI_DriverPS3_UpdateEffects(device);
+}
+
+static SDL_bool
+HIDAPI_DriverPS3_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
+{
+    SDL_DriverPS3_Context *ctx;
+
+    ctx = (SDL_DriverPS3_Context *)SDL_calloc(1, sizeof(*ctx));
+    if (!ctx) {
+        SDL_OutOfMemory();
+        return SDL_FALSE;
+    }
+    ctx->device = device;
+    ctx->joystick = joystick;
+
+    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_strncasecmp(device->name, "ShanWan", 7) == 0) {
+        ctx->is_shanwan = SDL_TRUE;
+    }
+
+    /* Set the controller into report mode over Bluetooth */
+    {
+        Uint8 data[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 };
+
+        if (SendFeatureReport(device->dev, data, sizeof(data)) == sizeof(data)) {
+            ctx->is_bluetooth = SDL_TRUE;
+        }
+    }
+
+    /* Set the controller into report mode over USB */
+    if (!ctx->is_bluetooth) {
+        Uint8 data[USB_PACKET_LENGTH];
+        int size;
+
+        if ((size = ReadFeatureReport(device->dev, 0xf2, data, 17)) < 0) {
+            SDL_SetError("Couldn't read feature report 0xf2");
+            return SDL_FALSE;
+        }
+#ifdef DEBUG_PS3_PROTOCOL
+        HIDAPI_DumpPacket("PS3 0xF2 packet: size = %d", data, size);
+#endif
+        if ((size = ReadFeatureReport(device->dev, 0xf5, data, 8)) < 0) {
+            SDL_SetError("Couldn't read feature report 0xf5");
+            return SDL_FALSE;
+        }
+#ifdef DEBUG_PS3_PROTOCOL
+        HIDAPI_DumpPacket("PS3 0xF5 packet: size = %d", data, size);
+#endif
+        if (!ctx->is_shanwan) {
+            /* An output report could cause ShanWan controllers to rumble non-stop */
+            SDL_hid_write(device->dev, data, 1);
+        }
+    }
+
+    /* Initialize player index (needed for setting LEDs) */
+    ctx->player_index = SDL_JoystickGetPlayerIndex(joystick);
+
+    /* Initialize the joystick capabilities */
+    joystick->nbuttons = 15;
+    joystick->naxes = 16;
+    joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
+
+    SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 100.0f);
+
+    return SDL_TRUE;
+}
+
+static int
+HIDAPI_DriverPS3_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
+{
+    SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;
+
+    ctx->rumble_left = (low_frequency_rumble >> 8);
+    ctx->rumble_right = (high_frequency_rumble >> 8);
+
+    return HIDAPI_DriverPS3_UpdateEffects(device);
+}
+
+static int
+HIDAPI_DriverPS3_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+    return SDL_Unsupported();
+}
+
+static Uint32
+HIDAPI_DriverPS3_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
+{
+    return SDL_JOYCAP_RUMBLE;
+}
+
+static int
+HIDAPI_DriverPS3_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
+{
+    return SDL_Unsupported();
+}
+
+static int
+HIDAPI_DriverPS3_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size)
+{
+    Uint8 data[36];
+    int report_size, offset;
+
+    SDL_zeroa(data);
+
+    data[0] = k_EPS3ReportIdEffects;
+    report_size = sizeof(data);
+    offset = 1;
+    SDL_memcpy(&data[offset], effect, SDL_min((sizeof(data) - offset), (size_t)size));
+
+    if (SDL_HIDAPI_SendRumble(device, data, report_size) != report_size) {
+        return SDL_SetError("Couldn't send rumble packet");
+    }
+    return 0;
+}
+
+static int
+HIDAPI_DriverPS3_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
+{
+    SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;
+
+    ctx->report_sensors = enabled;
+
+    return 0;
+}
+
+static float
+HIDAPI_DriverPS3_ScaleAccel(Sint16 value)
+{
+    return (float)(value - 511) / 113.0f;
+}
+
+static void
+HIDAPI_DriverPS3_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverPS3_Context *ctx, Uint8 *data, int size)
+{
+    Sint16 axis;
+
+    if (ctx->last_state[2] != data[2]) {
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[2] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[2] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[2] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[2] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[2] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
+    }
+
+    if (ctx->last_state[3] != data[3]) {
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[3] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[3] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (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_A, (data[3] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[3] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
+    }
+
+    if (ctx->last_state[4] != data[4]) {
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[4] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+    }
+
+    axis = ((int)data[18] * 257) - 32768;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
+    axis = ((int)data[19] * 257) - 32768;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
+    axis = ((int)data[6] * 257) - 32768;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
+    axis = ((int)data[7] * 257) - 32768;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
+    axis = ((int)data[8] * 257) - 32768;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
+    axis = ((int)data[9] * 257) - 32768;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
+
+    /* Buttons are mapped as axes in the order they appear in the button enumeration */
+    {
+        static int button_axis_offsets[] = {
+            24, /* SDL_CONTROLLER_BUTTON_A */
+            23, /* SDL_CONTROLLER_BUTTON_B */
+            25, /* SDL_CONTROLLER_BUTTON_X */
+            22, /* SDL_CONTROLLER_BUTTON_Y */
+            0,  /* SDL_CONTROLLER_BUTTON_BACK */
+            0,  /* SDL_CONTROLLER_BUTTON_GUIDE */
+            0,  /* SDL_CONTROLLER_BUTTON_START */
+            0,  /* SDL_CONTROLLER_BUTTON_LEFTSTICK */
+            0,  /* SDL_CONTROLLER_BUTTON_RIGHTSTICK */
+            20, /* SDL_CONTROLLER_BUTTON_LEFTSHOULDER */
+            21, /* SDL_CONTROLLER_BUTTON_RIGHTSHOULDER */
+            14, /* SDL_CONTROLLER_BUTTON_DPAD_UP */
+            16, /* SDL_CONTROLLER_BUTTON_DPAD_DOWN */
+            17, /* SDL_CONTROLLER_BUTTON_DPAD_LEFT */
+            15, /* SDL_CONTROLLER_BUTTON_DPAD_RIGHT */
+        };
+        int i, axis_index = 6;
+
+        for (i = 0; i < SDL_arraysize(button_axis_offsets); ++i) {
+            int offset = button_axis_offsets[i];
+            if (!offset) {
+                /* This button doesn't report as an axis */
+                continue;
+            }
+
+            axis = ((int)data[offset] * 257) - 32768;
+            SDL_PrivateJoystickAxis(joystick, axis_index, axis);
+            ++axis_index;
+        }
+    }
+
+    if (ctx->report_sensors) {
+        float sensor_data[3];
+
+        sensor_data[0] = HIDAPI_DriverPS3_ScaleAccel(LOAD16(data[41], data[42]));
+        sensor_data[1] = -HIDAPI_DriverPS3_ScaleAccel(LOAD16(data[45], data[46]));
+        sensor_data[2] = -HIDAPI_DriverPS3_ScaleAccel(LOAD16(data[43], data[44]));
+        SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_ACCEL, sensor_data, SDL_arraysize(sensor_data));
+    }
+
+    SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
+}
+
+static SDL_bool
+HIDAPI_DriverPS3_UpdateDevice(SDL_HIDAPI_Device *device)
+{
+    SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;
+    SDL_Joystick *joystick = NULL;
+    Uint8 data[USB_PACKET_LENGTH];
+    int size;
+
+    if (device->num_joysticks > 0) {
+        joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
+    }
+    if (!joystick) {
+        return SDL_FALSE;
+    }
+
+    while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
+#ifdef DEBUG_PS3_PROTOCOL
+        HIDAPI_DumpPacket("PS3 packet: size = %d", data, size);
+#endif
+
+        switch (data[0]) {
+        case k_EPS3ReportIdState:
+            if (data[1] == 0xFF) {
+                /* Invalid data packet, ignore */
+                break;
+            }
+            HIDAPI_DriverPS3_HandleStatePacket(joystick, ctx, data, size);
+
+            /* Wait for the first report to set the LED state after the controller stops blinking */
+            if (!ctx->effects_updated) {
+                HIDAPI_DriverPS3_UpdateEffects(device);
+                ctx->effects_updated = SDL_TRUE;
+            }
+            break;
+        default:
+#ifdef DEBUG_JOYSTICK
+            SDL_Log("Unknown PS3 packet: 0x%.2x\n", data[0]);
+#endif
+            break;
+        }
+    }
+
+    if (size < 0) {
+        /* Read error, device is disconnected */
+        HIDAPI_JoystickDisconnected(device, joystick->instance_id);
+    }
+    return (size >= 0);
+}
+
+static void
+HIDAPI_DriverPS3_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
+{
+    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);
+}
+
+static void
+HIDAPI_DriverPS3_FreeDevice(SDL_HIDAPI_Device *device)
+{
+}
+
+SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS3 =
+{
+    SDL_HINT_JOYSTICK_HIDAPI_PS3,
+    SDL_TRUE,
+    HIDAPI_DriverPS3_RegisterHints,
+    HIDAPI_DriverPS3_UnregisterHints,
+    HIDAPI_DriverPS3_IsEnabled,
+    HIDAPI_DriverPS3_IsSupportedDevice,
+    HIDAPI_DriverPS3_GetDeviceName,
+    HIDAPI_DriverPS3_InitDevice,
+    HIDAPI_DriverPS3_GetDevicePlayerIndex,
+    HIDAPI_DriverPS3_SetDevicePlayerIndex,
+    HIDAPI_DriverPS3_UpdateDevice,
+    HIDAPI_DriverPS3_OpenJoystick,
+    HIDAPI_DriverPS3_RumbleJoystick,
+    HIDAPI_DriverPS3_RumbleJoystickTriggers,
+    HIDAPI_DriverPS3_GetJoystickCapabilities,
+    HIDAPI_DriverPS3_SetJoystickLED,
+    HIDAPI_DriverPS3_SendJoystickEffect,
+    HIDAPI_DriverPS3_SetJoystickSensorsEnabled,
+    HIDAPI_DriverPS3_CloseJoystick,
+    HIDAPI_DriverPS3_FreeDevice,
+};
+
+#endif /* SDL_JOYSTICK_HIDAPI_PS3 */
+
+#endif /* SDL_JOYSTICK_HIDAPI */
+
+/* vi: set ts=4 sw=4 expandtab: */