Basic gamepad support through SDL (#407)

* Add basic gamepad support through SDL

* lightbar, vibration, code style changes

* okay fine

* one day clang format will finally pass
This commit is contained in:
counter185 2024-08-13 11:54:08 +02:00 committed by GitHub
parent d1a033b6af
commit bb159eafb9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 128 additions and 4 deletions

View File

@ -419,8 +419,14 @@ int PS4_SYSV_ABI scePadSetForceIntercepted() {
}
int PS4_SYSV_ABI scePadSetLightBar(s32 handle, const OrbisPadLightBarParam* pParam) {
LOG_ERROR(Lib_Pad, "(STUBBED) called");
if (pParam != nullptr) {
LOG_INFO(Lib_Pad, "scePadSetLightBar called handle = {} rgb = {} {} {}", handle, pParam->r,
pParam->g, pParam->b);
auto* controller = Common::Singleton<Input::GameController>::Instance();
controller->SetLightBarRGB(pParam->r, pParam->g, pParam->b);
return ORBIS_OK;
}
return ORBIS_PAD_ERROR_INVALID_ARG;
}
int PS4_SYSV_ABI scePadSetLightBarBaseBrightness() {
@ -479,8 +485,14 @@ int PS4_SYSV_ABI scePadSetUserColor() {
}
int PS4_SYSV_ABI scePadSetVibration(s32 handle, const OrbisPadVibrationParam* pParam) {
LOG_DEBUG(Lib_Pad, "(STUBBED) called");
if (pParam != nullptr) {
LOG_INFO(Lib_Pad, "scePadSetVibration called handle = {} data = {} , {}", handle,
pParam->smallMotor, pParam->largeMotor);
auto* controller = Common::Singleton<Input::GameController>::Instance();
controller->SetVibration(pParam->smallMotor, pParam->largeMotor);
return ORBIS_OK;
}
return ORBIS_PAD_ERROR_INVALID_ARG;
}
int PS4_SYSV_ABI scePadSetVibrationForce() {

View File

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <SDL3/SDL.h>
#include "core/libraries/kernel/time_management.h"
#include "core/libraries/pad/pad.h"
#include "input/controller.h"
@ -117,4 +118,29 @@ void GameController::Axis(int id, Input::Axis axis, int value) {
AddState(state);
}
void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) {
if (m_sdl_gamepad != nullptr) {
SDL_SetGamepadLED(m_sdl_gamepad, r, g, b);
}
}
bool GameController::SetVibration(u8 smallMotor, u8 largeMotor) {
if (m_sdl_gamepad != nullptr) {
return SDL_RumbleGamepad(m_sdl_gamepad, (smallMotor / 255.0f) * 0xFFFF,
(largeMotor / 255.0f) * 0xFFFF, -1) == 0;
}
return true;
}
void GameController::TryOpenSDLController() {
if (m_sdl_gamepad == nullptr || !SDL_GamepadConnected(m_sdl_gamepad)) {
int gamepad_count;
SDL_JoystickID* gamepads = SDL_GetGamepads(&gamepad_count);
m_sdl_gamepad = gamepad_count > 0 ? SDL_OpenGamepad(gamepads[0]) : nullptr;
SDL_free(gamepads);
}
SetLightBarRGB(0, 0, 255);
}
} // namespace Input

View File

@ -6,6 +6,8 @@
#include <mutex>
#include "common/types.h"
struct SDL_Gamepad;
namespace Input {
enum class Axis {
@ -43,6 +45,9 @@ public:
void CheckButton(int id, u32 button, bool isPressed);
void AddState(const State& state);
void Axis(int id, Input::Axis axis, int value);
void SetLightBarRGB(u8 r, u8 g, u8 b);
bool SetVibration(u8 smallMotor, u8 largeMotor);
void TryOpenSDLController();
private:
struct StateInternal {
@ -57,6 +62,8 @@ private:
u32 m_first_state = 0;
std::array<State, MAX_STATES> m_states;
std::array<StateInternal, MAX_STATES> m_private;
SDL_Gamepad* m_sdl_gamepad = nullptr;
};
} // namespace Input

View File

@ -43,6 +43,9 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_
SDL_SetWindowFullscreen(window, Config::isFullscreenMode());
SDL_InitSubSystem(SDL_INIT_GAMEPAD);
controller->TryOpenSDLController();
#if defined(SDL_PLATFORM_WIN32)
window_info.type = WindowSystemType::Windows;
window_info.render_surface = SDL_GetPointerProperty(SDL_GetWindowProperties(window),
@ -92,6 +95,11 @@ void WindowSDL::waitEvent() {
case SDL_EVENT_KEY_UP:
onKeyPress(&event);
break;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
onGamepadEvent(&event);
break;
case SDL_EVENT_QUIT:
is_open = false;
break;
@ -276,4 +284,71 @@ void WindowSDL::onKeyPress(const SDL_Event* event) {
}
}
void WindowSDL::onGamepadEvent(const SDL_Event* event) {
using Libraries::Pad::OrbisPadButtonDataOffset;
u32 button = 0;
Input::Axis axis = Input::Axis::AxisMax;
switch (event->type) {
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
button = sdlGamepadToOrbisButton(event->gbutton.button);
if (button != 0) {
controller->CheckButton(0, button, event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN);
}
break;
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
axis = event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFTX ? Input::Axis::LeftX
: event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFTY ? Input::Axis::LeftY
: event->gaxis.axis == SDL_GAMEPAD_AXIS_RIGHTX ? Input::Axis::RightX
: event->gaxis.axis == SDL_GAMEPAD_AXIS_RIGHTY ? Input::Axis::RightY
: event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER ? Input::Axis::TriggerLeft
: event->gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER ? Input::Axis::TriggerRight
: Input::Axis::AxisMax;
if (axis != Input::Axis::AxisMax) {
controller->Axis(0, axis, Input::GetAxis(-0x8000, 0x8000, event->gaxis.value));
}
break;
}
}
int WindowSDL::sdlGamepadToOrbisButton(u8 button) {
using Libraries::Pad::OrbisPadButtonDataOffset;
switch (button) {
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_DOWN;
case SDL_GAMEPAD_BUTTON_DPAD_UP:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP;
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_LEFT;
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_RIGHT;
case SDL_GAMEPAD_BUTTON_SOUTH:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CROSS;
case SDL_GAMEPAD_BUTTON_NORTH:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TRIANGLE;
case SDL_GAMEPAD_BUTTON_WEST:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_SQUARE;
case SDL_GAMEPAD_BUTTON_EAST:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CIRCLE;
case SDL_GAMEPAD_BUTTON_START:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_OPTIONS;
case SDL_GAMEPAD_BUTTON_TOUCHPAD:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD;
case SDL_GAMEPAD_BUTTON_BACK:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD;
case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L1;
case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R1;
case SDL_GAMEPAD_BUTTON_LEFT_STICK:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L3;
case SDL_GAMEPAD_BUTTON_RIGHT_STICK:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R3;
default:
return 0;
}
}
} // namespace Frontend

View File

@ -7,6 +7,7 @@
#include "common/types.h"
struct SDL_Window;
struct SDL_Gamepad;
union SDL_Event;
namespace Input {
@ -66,6 +67,9 @@ public:
private:
void onResize();
void onKeyPress(const SDL_Event* event);
void onGamepadEvent(const SDL_Event* event);
int sdlGamepadToOrbisButton(u8 button);
private:
s32 width;