From 19bb4a6e5d6c32a4313232f13781e73361f1c6ea Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Sat, 20 Jan 2024 19:23:09 +0100 Subject: [PATCH] switch: implement rumble with native API --- CMakeLists.txt | 5 +- core/sdl/sdl.cpp | 11 ++-- core/sdl/sdl_gamepad.h | 6 ++- shell/switch/switch_gamepad.h | 94 +++++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 shell/switch/switch_gamepad.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0aceba4e7a..9e851ca77b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1037,7 +1037,10 @@ else() target_sources(${PROJECT_NAME} PRIVATE core/linux/libnx_vmem.cpp shell/switch/stubs.c - shell/switch/context_switch.S) + shell/switch/context_switch.S + shell/switch/nswitch.h + shell/switch/switch_gamepad.h + shell/switch/ucontext.h) endif() endif() diff --git a/core/sdl/sdl.cpp b/core/sdl/sdl.cpp index e65383575f..cc181a76ef 100644 --- a/core/sdl/sdl.cpp +++ b/core/sdl/sdl.cpp @@ -26,6 +26,7 @@ #endif #ifdef __SWITCH__ #include "nswitch.h" +#include "switch_gamepad.h" #endif static SDL_Window* window = NULL; @@ -51,7 +52,7 @@ static struct SDLDeInit } bool initialized = false; -} sqlDeinit; +} sdlDeInit; static void sdl_open_joystick(int index) { @@ -63,7 +64,11 @@ static void sdl_open_joystick(int index) return; } try { +#ifdef __SWITCH__ + std::shared_ptr gamepad = std::make_shared(index < MAPLE_PORTS ? index : -1, index, pJoystick); +#else std::shared_ptr gamepad = std::make_shared(index < MAPLE_PORTS ? index : -1, index, pJoystick); +#endif SDLGamepad::AddSDLGamepad(gamepad); } catch (const FlycastException& e) { } @@ -189,7 +194,7 @@ void input_sdl_init() if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) die("SDL: error initializing Joystick subsystem"); } - sqlDeinit.initialized = true; + sdlDeInit.initialized = true; SDL_SetRelativeMouseMode(SDL_FALSE); @@ -720,7 +725,7 @@ void sdl_window_create() SDL_Vulkan_LoadLibrary("libvulkan.dylib"); #endif } - sqlDeinit.initialized = true; + sdlDeInit.initialized = true; initRenderApi(); // ImGui copy & paste ImGui::GetIO().GetClipboardTextFn = getClipboardText; diff --git a/core/sdl/sdl_gamepad.h b/core/sdl/sdl_gamepad.h index f62ea604ac..33d9e83dd9 100644 --- a/core/sdl/sdl_gamepad.h +++ b/core/sdl/sdl_gamepad.h @@ -410,11 +410,13 @@ class SDLGamepad : public GamepadDevice pair.second->update_rumble(); } +protected: + double vib_stop_time = 0; + SDL_JoystickID sdl_joystick_instance; + private: SDL_Joystick* sdl_joystick; - SDL_JoystickID sdl_joystick_instance; float vib_inclination = 0; - double vib_stop_time = 0; SDL_GameController *sdl_controller = nullptr; static std::map> sdl_gamepads; }; diff --git a/shell/switch/switch_gamepad.h b/shell/switch/switch_gamepad.h new file mode 100644 index 0000000000..b21c7b96ed --- /dev/null +++ b/shell/switch/switch_gamepad.h @@ -0,0 +1,94 @@ +/* + Copyright 2024 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#include "sdl/sdl_gamepad.h" +#include "nswitch.h" + +// +// SDL 2.0.14-4 rumble doesn't seem to work +// so this class implements rumble using the native API. +// +class SwitchGamepad : public SDLGamepad +{ +public: + SwitchGamepad(int maple_port, int joystick_idx, SDL_Joystick* sdl_joystick) + : SDLGamepad(maple_port, joystick_idx, sdl_joystick) + { + // A dual joycon controller has 2 vibration devices (left and right joycons) + // Joystick 0 is either HidNpadIdType_Handheld or HidNpadIdType_No1 depending on the joycon configuration + Result rc = hidInitializeVibrationDevices(vibDeviceHandlesNoN, 2, (HidNpadIdType)(HidNpadIdType_No1 + joystick_idx), + HidNpadStyleTag_NpadJoyDual); + if (R_FAILED(rc)) + WARN_LOG(INPUT, "hidInitializeVibrationDevices(No%d) failed %x", joystick_idx + 1, rc); + if (joystick_idx == 0) + { + padInitializeDefault(&pad); + rc = hidInitializeVibrationDevices(vibDeviceHandlesHandHeld, 2, HidNpadIdType_Handheld, HidNpadStyleTag_NpadHandheld); + if (R_FAILED(rc)) + WARN_LOG(INPUT, "hidInitializeVibrationDevices(handHeld) failed %x", rc); + } + } + + void rumble(float power, float inclination, u32 duration_ms) override + { + if (!rumbleEnabled) + return; + + power = std::min(power / std::pow(1.06f, 100.f - rumblePower), 1.f); + float freq = 160.f + inclination * 100.f; + HidVibrationValue vibValues[2]{}; + vibValues[0].amp_low = power; + vibValues[0].freq_low = freq; + vibValues[0].amp_high = power; + vibValues[0].freq_high = freq; + memcpy(&vibValues[1], &vibValues[0], sizeof(HidVibrationValue)); + hidSendVibrationValues(getDeviceHandle(), vibValues, 2); + if (power != 0.f) + vib_stop_time = os_GetSeconds() + duration_ms / 1000.0; + else + vib_stop_time = 0.0; + } + + void update_rumble() override + { + if (!rumbleEnabled || vib_stop_time == 0.0) + return; + int rem_time = (vib_stop_time - os_GetSeconds()) * 1000; + if (rem_time <= 0) + { + HidVibrationValue vibValues[2]{}; + vibValues[0].freq_low = vibValues[1].freq_low = 160.f; + vibValues[0].freq_high = vibValues[1].freq_high = 320.f; + hidSendVibrationValues(getDeviceHandle(), vibValues, 2); + vib_stop_time = 0.0; + } + } + +private: + HidVibrationDeviceHandle *getDeviceHandle() + { + if (sdl_joystick_instance != 0) + return vibDeviceHandlesNoN; + padUpdate(&pad); + return padIsHandheld(&pad) ? vibDeviceHandlesHandHeld : vibDeviceHandlesNoN; + } + + PadState pad; + HidVibrationDeviceHandle vibDeviceHandlesHandHeld[2]; + HidVibrationDeviceHandle vibDeviceHandlesNoN[2]; +};