Skip to content

Commit

Permalink
switch: implement rumble with native API
Browse files Browse the repository at this point in the history
  • Loading branch information
flyinghead committed Jan 20, 2024
1 parent bc70d91 commit 19bb4a6
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 6 deletions.
5 changes: 4 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
11 changes: 8 additions & 3 deletions core/sdl/sdl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#endif
#ifdef __SWITCH__
#include "nswitch.h"
#include "switch_gamepad.h"
#endif

static SDL_Window* window = NULL;
Expand All @@ -51,7 +52,7 @@ static struct SDLDeInit
}

bool initialized = false;
} sqlDeinit;
} sdlDeInit;

static void sdl_open_joystick(int index)
{
Expand All @@ -63,7 +64,11 @@ static void sdl_open_joystick(int index)
return;
}
try {
#ifdef __SWITCH__
std::shared_ptr<SDLGamepad> gamepad = std::make_shared<SwitchGamepad>(index < MAPLE_PORTS ? index : -1, index, pJoystick);
#else
std::shared_ptr<SDLGamepad> gamepad = std::make_shared<SDLGamepad>(index < MAPLE_PORTS ? index : -1, index, pJoystick);
#endif
SDLGamepad::AddSDLGamepad(gamepad);
} catch (const FlycastException& e) {
}
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
Expand Down
6 changes: 4 additions & 2 deletions core/sdl/sdl_gamepad.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_JoystickID, std::shared_ptr<SDLGamepad>> sdl_gamepads;
};
Expand Down
94 changes: 94 additions & 0 deletions shell/switch/switch_gamepad.h
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
*/
#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];
};

0 comments on commit 19bb4a6

Please sign in to comment.