mirror of
https://github.com/hedge-dev/UnleashedRecomp.git
synced 2025-10-30 07:11:05 +00:00
326 lines
8.9 KiB
C++
326 lines
8.9 KiB
C++
#include <stdafx.h>
|
|
#include <SDL.h>
|
|
#include <user/config.h>
|
|
#include <hid/hid_detail.h>
|
|
#include <ui/game_window.h>
|
|
#include <kernel/xdm.h>
|
|
|
|
#define TRANSLATE_INPUT(S, X) SDL_GameControllerGetButton(controller, S) << FirstBitLow(X)
|
|
#define VIBRATION_TIMEOUT_MS 5000
|
|
|
|
class Controller
|
|
{
|
|
public:
|
|
SDL_GameController* controller{};
|
|
SDL_Joystick* joystick{};
|
|
SDL_JoystickID id{ -1 };
|
|
XAMINPUT_GAMEPAD state{};
|
|
XAMINPUT_VIBRATION vibration{ 0, 0 };
|
|
int index{};
|
|
|
|
Controller() = default;
|
|
|
|
explicit Controller(int index) : Controller(SDL_GameControllerOpen(index))
|
|
{
|
|
this->index = index;
|
|
}
|
|
|
|
Controller(SDL_GameController* controller) : controller(controller)
|
|
{
|
|
if (!controller)
|
|
return;
|
|
|
|
joystick = SDL_GameControllerGetJoystick(controller);
|
|
id = SDL_JoystickInstanceID(joystick);
|
|
}
|
|
|
|
SDL_GameControllerType GetControllerType() const
|
|
{
|
|
return SDL_GameControllerTypeForIndex(index);
|
|
}
|
|
|
|
hid::detail::EInputDevice GetInputDevice() const
|
|
{
|
|
switch (GetControllerType())
|
|
{
|
|
case SDL_CONTROLLER_TYPE_PS3:
|
|
case SDL_CONTROLLER_TYPE_PS4:
|
|
case SDL_CONTROLLER_TYPE_PS5:
|
|
return hid::detail::EInputDevice::PlayStation;
|
|
}
|
|
|
|
return hid::detail::EInputDevice::Xbox;
|
|
}
|
|
|
|
void Close()
|
|
{
|
|
if (!controller)
|
|
return;
|
|
|
|
SDL_GameControllerClose(controller);
|
|
|
|
controller = nullptr;
|
|
joystick = nullptr;
|
|
id = -1;
|
|
}
|
|
|
|
bool CanPoll()
|
|
{
|
|
return controller && (GameWindow::s_isFocused || Config::AllowBackgroundInput);
|
|
}
|
|
|
|
void PollAxis()
|
|
{
|
|
if (!CanPoll())
|
|
return;
|
|
|
|
auto& pad = state;
|
|
|
|
pad.sThumbLX = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
|
|
pad.sThumbLY = ~SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
|
|
|
|
pad.sThumbRX = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
|
|
pad.sThumbRY = ~SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
|
|
|
|
pad.bLeftTrigger = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) >> 7;
|
|
pad.bRightTrigger = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) >> 7;
|
|
}
|
|
|
|
void Poll()
|
|
{
|
|
if (!CanPoll())
|
|
return;
|
|
|
|
auto& pad = state;
|
|
|
|
pad.wButtons = 0;
|
|
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_DPAD_UP, XAMINPUT_GAMEPAD_DPAD_UP);
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_DPAD_DOWN, XAMINPUT_GAMEPAD_DPAD_DOWN);
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_DPAD_LEFT, XAMINPUT_GAMEPAD_DPAD_LEFT);
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_DPAD_RIGHT, XAMINPUT_GAMEPAD_DPAD_RIGHT);
|
|
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_START, XAMINPUT_GAMEPAD_START);
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_BACK, XAMINPUT_GAMEPAD_BACK);
|
|
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_LEFTSTICK, XAMINPUT_GAMEPAD_LEFT_THUMB);
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_RIGHTSTICK, XAMINPUT_GAMEPAD_RIGHT_THUMB);
|
|
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_LEFTSHOULDER, XAMINPUT_GAMEPAD_LEFT_SHOULDER);
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, XAMINPUT_GAMEPAD_RIGHT_SHOULDER);
|
|
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_A, XAMINPUT_GAMEPAD_A);
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_B, XAMINPUT_GAMEPAD_B);
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_X, XAMINPUT_GAMEPAD_X);
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_Y, XAMINPUT_GAMEPAD_Y);
|
|
}
|
|
|
|
void SetVibration(const XAMINPUT_VIBRATION& vibration)
|
|
{
|
|
if (!CanPoll())
|
|
return;
|
|
|
|
this->vibration = vibration;
|
|
|
|
SDL_GameControllerRumble(controller, vibration.wLeftMotorSpeed * 256, vibration.wRightMotorSpeed * 256, VIBRATION_TIMEOUT_MS);
|
|
}
|
|
};
|
|
|
|
std::array<Controller, 4> g_controllers;
|
|
Controller* g_activeController;
|
|
|
|
inline Controller* EnsureController(uint32_t dwUserIndex)
|
|
{
|
|
if (!g_controllers[dwUserIndex].controller)
|
|
return nullptr;
|
|
|
|
return &g_controllers[dwUserIndex];
|
|
}
|
|
|
|
inline size_t FindFreeController()
|
|
{
|
|
for (size_t i = 0; i < g_controllers.size(); i++)
|
|
{
|
|
if (!g_controllers[i].controller)
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
inline Controller* FindController(int which)
|
|
{
|
|
for (auto& controller : g_controllers)
|
|
{
|
|
if (controller.id == which)
|
|
return &controller;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static void SetControllerInputDevice(Controller* controller)
|
|
{
|
|
g_activeController = controller;
|
|
hid::detail::g_inputDevice = controller->GetInputDevice();
|
|
hid::detail::g_inputDeviceController = hid::detail::g_inputDevice;
|
|
}
|
|
|
|
int HID_OnSDLEvent(void*, SDL_Event* event)
|
|
{
|
|
switch (event->type)
|
|
{
|
|
case SDL_CONTROLLERDEVICEADDED:
|
|
{
|
|
const auto freeIndex = FindFreeController();
|
|
|
|
if (freeIndex != -1)
|
|
g_controllers[freeIndex] = Controller(event->cdevice.which);
|
|
|
|
break;
|
|
}
|
|
|
|
case SDL_CONTROLLERDEVICEREMOVED:
|
|
{
|
|
auto* controller = FindController(event->cdevice.which);
|
|
|
|
if (controller)
|
|
controller->Close();
|
|
|
|
break;
|
|
}
|
|
|
|
case SDL_CONTROLLERBUTTONDOWN:
|
|
case SDL_CONTROLLERBUTTONUP:
|
|
case SDL_CONTROLLERAXISMOTION:
|
|
{
|
|
auto* controller = FindController(event->cdevice.which);
|
|
|
|
if (controller)
|
|
{
|
|
if (event->type == SDL_CONTROLLERAXISMOTION)
|
|
{
|
|
if (abs(event->caxis.value) > 8000)
|
|
SetControllerInputDevice(controller);
|
|
|
|
controller->PollAxis();
|
|
}
|
|
else
|
|
{
|
|
SetControllerInputDevice(controller);
|
|
|
|
controller->Poll();
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SDL_KEYDOWN:
|
|
case SDL_KEYUP:
|
|
hid::detail::g_inputDevice = hid::detail::EInputDevice::Keyboard;
|
|
break;
|
|
|
|
case SDL_MOUSEMOTION:
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
case SDL_MOUSEBUTTONUP:
|
|
hid::detail::g_inputDevice = hid::detail::EInputDevice::Mouse;
|
|
break;
|
|
|
|
case SDL_WINDOWEVENT:
|
|
{
|
|
if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST)
|
|
{
|
|
// Stop vibrating controllers on focus lost.
|
|
for (auto& controller : g_controllers)
|
|
controller.SetVibration({ 0, 0 });
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SDL_USER_EVILSONIC:
|
|
{
|
|
if (!g_activeController)
|
|
break;
|
|
|
|
auto isNight = event->user.code;
|
|
auto r = isNight ? 22 : 0;
|
|
auto g = isNight ? 0 : 37;
|
|
auto b = isNight ? 101 : 184;
|
|
|
|
SDL_GameControllerSetLED(g_activeController->controller, r, g, b);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void hid::detail::Init()
|
|
{
|
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS3, "1");
|
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4, "1");
|
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5, "1");
|
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, "1");
|
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_WII, "1");
|
|
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
|
|
SDL_SetHint(SDL_HINT_XINPUT_ENABLED, "1");
|
|
|
|
SDL_InitSubSystem(SDL_INIT_EVENTS);
|
|
SDL_AddEventWatch(HID_OnSDLEvent, nullptr);
|
|
|
|
SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
|
|
}
|
|
|
|
uint32_t hid::detail::GetState(uint32_t dwUserIndex, XAMINPUT_STATE* pState)
|
|
{
|
|
static uint32_t packet;
|
|
|
|
if (!pState)
|
|
return ERROR_BAD_ARGUMENTS;
|
|
|
|
memset(pState, 0, sizeof(*pState));
|
|
|
|
pState->dwPacketNumber = packet++;
|
|
|
|
if (!g_activeController)
|
|
return ERROR_DEVICE_NOT_CONNECTED;
|
|
|
|
pState->Gamepad = g_activeController->state;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
uint32_t hid::detail::SetState(uint32_t dwUserIndex, XAMINPUT_VIBRATION* pVibration)
|
|
{
|
|
if (!pVibration)
|
|
return ERROR_BAD_ARGUMENTS;
|
|
|
|
if (!g_activeController)
|
|
return ERROR_DEVICE_NOT_CONNECTED;
|
|
|
|
g_activeController->SetVibration(*pVibration);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
uint32_t hid::detail::GetCapabilities(uint32_t dwUserIndex, XAMINPUT_CAPABILITIES* pCaps)
|
|
{
|
|
if (!pCaps)
|
|
return ERROR_BAD_ARGUMENTS;
|
|
|
|
if (!g_activeController)
|
|
return ERROR_DEVICE_NOT_CONNECTED;
|
|
|
|
memset(pCaps, 0, sizeof(*pCaps));
|
|
|
|
pCaps->Type = XAMINPUT_DEVTYPE_GAMEPAD;
|
|
pCaps->SubType = XAMINPUT_DEVSUBTYPE_GAMEPAD; // TODO: other types?
|
|
pCaps->Flags = 0;
|
|
pCaps->Gamepad = g_activeController->state;
|
|
pCaps->Vibration = g_activeController->vibration;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|