diff --git a/CMakeLists.txt b/CMakeLists.txt index b0eeca8..171fb9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3655,15 +3655,18 @@ if (PLATFORM_PS4) "${SDL3_SOURCE_DIR}/src/thread/pthread/SDL_sysrwlock.c" # Can be faked, if necessary "${SDL3_SOURCE_DIR}/src/thread/pthread/SDL_systls.c" "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_syssem.c" + "${SDL3_SOURCE_DIR}/src/joystick/ps4/SDL_sysjoystick.c" ) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/unix/*.c") sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/unix/*.c") set(HAVE_SDL_THREADS TRUE) set(HAVE_SDL_TIMERS TRUE) set(HAVE_SDL_TIME TRUE) + set(HAVE_SDL_JOYSTICK TRUE) set(SDL_TIME_UNIX 1) set(SDL_TIMER_UNIX 1) set(SDL_THREAD_PTHREAD 1) + set(SDL_JOYSTICK_PS4 1) endif () # We always need to have threads and timers around diff --git a/build-scripts/rename_macros.py b/build-scripts/rename_macros.py index b6063dd..363f4d4 100755 --- a/build-scripts/rename_macros.py +++ b/build-scripts/rename_macros.py @@ -236,6 +236,7 @@ DEPRECATED_PLATFORM_MACROS = { "SDL_JOYSTICK_N3DS", "SDL_JOYSTICK_OS2", "SDL_JOYSTICK_PS2", + "SDL_JOYSTICK_PS4", "SDL_JOYSTICK_PSP", "SDL_JOYSTICK_RAWINPUT", "SDL_JOYSTICK_USBHID", diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake index 520b721..087d91b 100644 --- a/include/build_config/SDL_build_config.h.cmake +++ b/include/build_config/SDL_build_config.h.cmake @@ -313,6 +313,7 @@ #cmakedefine SDL_JOYSTICK_MFI 1 #cmakedefine SDL_JOYSTICK_N3DS 1 #cmakedefine SDL_JOYSTICK_PS2 1 +#cmakedefine SDL_JOYSTICK_PS4 1 #cmakedefine SDL_JOYSTICK_PSP 1 #cmakedefine SDL_JOYSTICK_RAWINPUT 1 #cmakedefine SDL_JOYSTICK_USBHID 1 diff --git a/src/joystick/ps4/SDL_sysjoystick.c b/src/joystick/ps4/SDL_sysjoystick.c new file mode 100644 index 0000000..bd67ef6 --- /dev/null +++ b/src/joystick/ps4/SDL_sysjoystick.c @@ -0,0 +1,339 @@ +/* +Simple DirectMedia Layer +Copyright (C) 1997-2019 Sam Lantinga + +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 __INTELLISENSE__ +#define SDL_JOYSTICK_PS4 1 +#endif + +#if SDL_JOYSTICK_PS4 + +/* This is the PSP implementation of the SDL joystick API */ +#include +#include +#include + +#include /* For the definition of NULL */ +#include + +#include "../SDL_sysjoystick.h" +#include "../SDL_joystick_c.h" + +// #include "SDL_events.h" +// #include "SDL_error.h" +// #include "SDL_mutex.h" +// #include "SDL_timer.h" +#include "../../thread/SDL_systhread.h" + +#define MAX_PADS 4 +#define DS4_BTN_COUNT 20 +#define DS4_AXE_COUNT 6 // lsX/Y, rsX/Y, L2, R2 +#define DS4_HAT_COUNT 1 + +uint32_t nPads = 0; + +OrbisUserServiceUserId userId, pad_users[MAX_PADS]; +OrbisUserServiceLoginUserIdList userIdList; + +int PS4_JoystickGetCount(void) +{ + return nPads; +} + +void PS4_JoystickDetect(void) +{ + if(ORBIS_OK != sceUserServiceGetLoginUserIdList(&userIdList)) { + printf("WARNING, sceUserServiceGetLoginUserIdList() failed for JoystickDetect()!\n"); + } + + nPads = 0; + for (int i = 0; i < ORBIS_USER_SERVICE_MAX_LOGIN_USERS; i++) { + userId = userIdList.userId[i]; + if (ORBIS_USER_SERVICE_USER_ID_INVALID != userId) { + int32_t handle = scePadOpen(userId, ORBIS_PAD_PORT_TYPE_STANDARD, 0, NULL); + if (handle > 0 || handle== ORBIS_PAD_ERROR_ALREADY_OPENED) { + if(handle>0) { + printf("@@@@ got pad[%d] handle %X\n", nPads, handle); + } + pad_users[nPads++] = userId; + if (nPads >= MAX_PADS) { + break; + } + } + } + } +} + +//#include "SDL_hints.h" + + +/* Function to scan the system for joysticks. +* Joystick 0 should be the system default joystick. +* It should return number of joysticks, or -1 on an unrecoverable fatal error. +*/ +static bool PS4_JoystickInit(void) +{ + SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); // Checks for keyboard focus ability, 'or something' + + int32_t res = sceUserServiceInitialize(NULL); + if(ORBIS_OK != res && ORBIS_USER_SERVICE_ERROR_ALREADY_INITIALIZED != res) { + return SDL_SetError("Error sceUserServiceInitialize() failed!"); + } + + if (ORBIS_OK != sceUserServiceGetInitialUser(&userId)) { + return SDL_SetError("Error sceUserServiceGetInitialUser() failed!"); + } + + if(ORBIS_OK != scePadInit()) { + return SDL_SetError("Error scePadInit() failed!"); + } + + PS4_JoystickDetect(); + return nPads; +} + +/* Function to get the device-dependent name of a joystick */ +static const char * PS4_JoystickGetDeviceName(int device_index) +{ + return "Dualshock4"; +} + +/* Function to get the player index of a joystick */ +static int PS4_JoystickGetDevicePlayerIndex(int device_index) +{ + if (device_index >= MAX_PADS) { + device_index = MAX_PADS - 1; + } + + for (int i = 0; i < ORBIS_USER_SERVICE_MAX_LOGIN_USERS; i++) { + if(pad_users[device_index] == userIdList.userId[i]) { + return i; + } + } + return -1; +} + +static SDL_GUID PS4_JoystickGetDeviceGUID(int device_index) +{ + if (device_index >= MAX_PADS) { + device_index = MAX_PADS - 1; + } + + SDL_GUID guid; + SDL_zero(guid); + ((Uint32*)guid.data)[0] = 0x00000005; // 05000000; + ((Uint32*)guid.data)[0] = 0x00004c05; // 4c050000; + ((Uint32*)guid.data)[0] = 0x000009cc; // cc090000; + ((Uint32*)guid.data)[0] = 0x00008001; // 01800000; + // "050000004c050000cc09000001800000,PS4 Controller, + return guid; +} + + +/* Function to perform the mapping from device index to the instance id for this index */ +static SDL_JoystickID PS4_JoystickGetDeviceInstanceID(int device_index) +{ + if (device_index >= MAX_PADS) { + device_index = MAX_PADS - 1; + } + return scePadGetHandle(pad_users[device_index], ORBIS_PAD_PORT_TYPE_STANDARD, 0); +} + +/* Function to open a joystick for use. +The joystick to open is specified by the device index. +This should fill the nbuttons and naxes fields of the joystick structure. +It returns 0, or -1 if there is an error. +*/ +static bool PS4_JoystickOpen(SDL_Joystick *joystick, int device_index) +{ + if (device_index >= MAX_PADS) { + device_index = MAX_PADS - 1; + } + + PS4_JoystickDetect(); // should already be done but ... + + int32_t handle = PS4_JoystickGetDeviceInstanceID(device_index); // They are already open , + if (handle > 0) { + joystick->nbuttons = DS4_BTN_COUNT; + joystick->naxes = DS4_AXE_COUNT; + joystick->nhats = DS4_HAT_COUNT; + /* Create the joystick data structure */ + joystick->instance_id = handle; + return true; + } + printf("PS4_JoystickOpen(idx: %d) failed to get handled!\n", device_index); + return false; +} + +static bool PS4_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) +{ + return SDL_Unsupported(); +} + +/* Function to update the state of a joystick - called as a device poll. +* This function shouldn't update the joystick structure directly, +* but instead should call SDL_PrivateJoystick*() to deliver events +* and update joystick device state. +*/ +static void PS4_JoystickUpdate(SDL_Joystick *joystick) +{ + OrbisPadData data; + + int ret = scePadReadState(joystick->instance_id, &data); + if (ret != ORBIS_OK || !data.connected) { + printf("Warning, Controller is not connected or failed to read data! \n"); + return; + } + + //a:b0,b:b1,x:b3,y:b2,leftshoulder:b4,rightshoulder:b5 + //back:b8,start:b9,guide:b10,leftstick:b11,rightstick:b12, + //dpdown:h0.4, dpleft:h0.8, dpright:h0.2, dpup:h0.1, + //leftx:a0,lefty:a1,lefttrigger:a2,rightx:a3,righty:a4,righttrigger:a5, + const static uint32_t ds4_map[DS4_BTN_COUNT] = { + ORBIS_PAD_BUTTON_CROSS, ORBIS_PAD_BUTTON_CIRCLE, ORBIS_PAD_BUTTON_SQUARE, ORBIS_PAD_BUTTON_TRIANGLE, ORBIS_PAD_BUTTON_L1, ORBIS_PAD_BUTTON_R1, + 0, /* b6? */ 0, /* b7? */ 0, /* no share/back btn atm */ + ORBIS_PAD_BUTTON_OPTIONS, 0 /* no guide button atm */, ORBIS_PAD_BUTTON_L3, ORBIS_PAD_BUTTON_R3, + ORBIS_PAD_BUTTON_UP, ORBIS_PAD_BUTTON_DOWN, ORBIS_PAD_BUTTON_LEFT, ORBIS_PAD_BUTTON_RIGHT, + ORBIS_PAD_BUTTON_TOUCH_PAD, ORBIS_PAD_BUTTON_L2, ORBIS_PAD_BUTTON_R2, + }; + + Uint32 s_btns = data.buttons; + Uint64 timestamp = SDL_GetTicksNS(); + + for (uint32_t bn = 0; bn < DS4_BTN_COUNT; bn++) { + SDL_SendJoystickButton(timestamp, joystick, bn, s_btns & ds4_map[bn]); + } + +#define aS16(axe) -32768 + ((axe) << 8) + +#define setA(n,axe) { Sint16 tmp = aS16(axe); if (tmp < -1000 || tmp > 1000) { SDL_SendJoystickButton(timestamp, joystick, (n), tmp); } } + setA(0, data.leftStick.x); + setA(1, data.leftStick.y); + setA(2, data.analogButtons.l2); + + setA(4, data.rightStick.x); + setA(5, data.rightStick.y); + setA(6, data.analogButtons.r2); + + Uint8 hat = SDL_HAT_CENTERED; + if (s_btns & ORBIS_PAD_BUTTON_UP) { hat |= SDL_HAT_UP; } + if (s_btns & ORBIS_PAD_BUTTON_DOWN) { hat |= SDL_HAT_DOWN; } + if (s_btns & ORBIS_PAD_BUTTON_LEFT) { hat |= SDL_HAT_LEFT; } + if (s_btns & ORBIS_PAD_BUTTON_RIGHT) { hat |= SDL_HAT_RIGHT; } + SDL_SendJoystickAxis(timestamp, joystick, 0, hat); +} + +/* Function to close a joystick after use */ +void PS4_JoystickClose(SDL_Joystick *joystick) +{ + scePadClose(joystick->instance_id); +#if 0 + if (joystick->hwdata) + SDL_free(joystick->hwdata); +#endif +} + +/* Function to perform any system-specific joystick related cleanup */ +static void PS4_JoystickQuit(void) +{ + nPads = 0; +} + +/* New shit in SDL3 */ +static bool PS4_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + // We don't override any other drivers + return false; +} + +// Function to get the device-dependent path of a joystick +static const char *PS4_JoystickGetDevicePath(int index) +{ + return NULL; +} + +// Function to get the Steam virtual gamepad slot of a joystick +static int PS4_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index) +{ + return -1; +} + +// Function to set the player index of a joystick +static void PS4_JoystickSetDevicePlayerIndex(int device_index, int player_index) +{ +} + +// Rumble functionality +static bool PS4_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left, Uint16 right) +{ + return SDL_Unsupported(); +} + +// LED functionality +static bool PS4_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) +{ + return SDL_Unsupported(); +} + +// General effects +static bool PS4_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size) +{ + return SDL_Unsupported(); +} + +// Sensor functionality +static bool PS4_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled) +{ + return SDL_Unsupported(); +} + +static bool PS4_GetGamepadMapping(int device_index, SDL_GamepadMapping *out) +{ + return false; +} + +SDL_JoystickDriver SDL_PS4_JoystickDriver = +{ + PS4_JoystickInit, + PS4_JoystickGetCount, + PS4_JoystickDetect, + PS4_JoystickIsDevicePresent, + PS4_JoystickGetDeviceName, + PS4_JoystickGetDevicePath, + PS4_JoystickGetDeviceSteamVirtualGamepadSlot, + PS4_JoystickGetDevicePlayerIndex, + PS4_JoystickSetDevicePlayerIndex, + PS4_JoystickGetDeviceGUID, + PS4_JoystickGetDeviceInstanceID, + PS4_JoystickOpen, + PS4_JoystickRumble, + PS4_JoystickRumbleTriggers, + PS4_JoystickSetLED, + PS4_JoystickSendEffect, + PS4_JoystickSetSensorsEnabled, + PS4_JoystickUpdate, + PS4_JoystickClose, + PS4_JoystickQuit, + PS4_GetGamepadMapping, +}; + + +#endif /* SDL_JOYSTICK_PS4 */ \ No newline at end of file