mirror of https://github.com/AxioDL/metaforce.git
Initial virtual PAD API
This commit is contained in:
parent
a96fe24260
commit
9cedce737f
|
@ -1,24 +1,5 @@
|
||||||
#include "Runtime/Input/CDolphinController.hpp"
|
#include "Runtime/Input/CDolphinController.hpp"
|
||||||
|
|
||||||
namespace PAD {
|
|
||||||
// clang-format off
|
|
||||||
enum BUTTON : u16 {
|
|
||||||
LEFT = 0x0001,
|
|
||||||
RIGHT = 0x0002,
|
|
||||||
DOWN = 0x0004,
|
|
||||||
UP = 0x0008,
|
|
||||||
TRIGGER_Z = 0x0010,
|
|
||||||
TRIGGER_R = 0x0020,
|
|
||||||
TRIGGER_L = 0x0040,
|
|
||||||
BUTTON_A = 0x0100,
|
|
||||||
BUTTON_B = 0x0200,
|
|
||||||
BUTTON_X = 0x0400,
|
|
||||||
BUTTON_Y = 0x0800,
|
|
||||||
BUTTON_START = 0x1000,
|
|
||||||
};
|
|
||||||
// clang-format on
|
|
||||||
} // namespace PAD
|
|
||||||
|
|
||||||
namespace metaforce {
|
namespace metaforce {
|
||||||
CDolphinController::CDolphinController() {
|
CDolphinController::CDolphinController() {
|
||||||
static bool sIsInitialized = false;
|
static bool sIsInitialized = false;
|
||||||
|
@ -78,7 +59,7 @@ void CDolphinController::ProcessAxis(u32 controller, EJoyAxis axis) {
|
||||||
|
|
||||||
static constexpr std::array<u16, size_t(EButton::MAX)> mButtonMapping{
|
static constexpr std::array<u16, size_t(EButton::MAX)> mButtonMapping{
|
||||||
PAD::BUTTON_A, PAD::BUTTON_B, PAD::BUTTON_X, PAD::BUTTON_Y, PAD::BUTTON_START, PAD::TRIGGER_Z,
|
PAD::BUTTON_A, PAD::BUTTON_B, PAD::BUTTON_X, PAD::BUTTON_Y, PAD::BUTTON_START, PAD::TRIGGER_Z,
|
||||||
PAD::UP, PAD::RIGHT, PAD::DOWN, PAD::LEFT, PAD::TRIGGER_L, PAD::TRIGGER_R,
|
PAD::BUTTON_UP, PAD::BUTTON_RIGHT, PAD::BUTTON_DOWN, PAD::BUTTON_LEFT, PAD::TRIGGER_L, PAD::TRIGGER_R,
|
||||||
};
|
};
|
||||||
|
|
||||||
void CDolphinController::ProcessButtons(u32 controller) {
|
void CDolphinController::ProcessButtons(u32 controller) {
|
||||||
|
|
|
@ -2,19 +2,8 @@
|
||||||
|
|
||||||
#include "Runtime/Input/IController.hpp"
|
#include "Runtime/Input/IController.hpp"
|
||||||
|
|
||||||
|
|
||||||
namespace metaforce {
|
namespace metaforce {
|
||||||
struct PADStatus {
|
|
||||||
u16 x0_buttons;
|
|
||||||
s8 x2_stickX;
|
|
||||||
s8 x3_stickY;
|
|
||||||
s8 x4_substickX;
|
|
||||||
s8 x5_substickY;
|
|
||||||
u8 x6_triggerL;
|
|
||||||
u8 x7_triggerR;
|
|
||||||
u8 x8_analogA;
|
|
||||||
u8 x9_analogB;
|
|
||||||
s8 xa_err;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CDolphinController : public IController {
|
class CDolphinController : public IController {
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <magic_enum.hpp>
|
#include <magic_enum.hpp>
|
||||||
|
|
||||||
namespace metaforce {
|
namespace metaforce {
|
||||||
|
static logvisor::Module Log("CInputGenerator");
|
||||||
|
|
||||||
void CInputGenerator::Update(float dt, CArchitectureQueue& queue) {
|
void CInputGenerator::Update(float dt, CArchitectureQueue& queue) {
|
||||||
if (m_firstFrame) {
|
if (m_firstFrame) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
set(INPUT_SOURCES
|
set(INPUT_SOURCES
|
||||||
|
PAD.hpp
|
||||||
IController.hpp DolphinIController.cpp
|
IController.hpp DolphinIController.cpp
|
||||||
CControllerAxis.hpp
|
CControllerAxis.hpp
|
||||||
CControllerButton.hpp
|
CControllerButton.hpp
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "Runtime/Input/PAD.hpp"
|
||||||
|
|
||||||
|
|
||||||
namespace metaforce {
|
namespace metaforce {
|
||||||
enum class EIOPort {
|
enum class EIOPort {
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "aurora/pad.hpp"
|
||||||
|
|
||||||
|
using PADStatus = PAD::Status;
|
||||||
|
using PADButton = PAD::BUTTON;
|
|
@ -0,0 +1,80 @@
|
||||||
|
#pragma once
|
||||||
|
#include "aurora/common.hpp"
|
||||||
|
|
||||||
|
namespace PAD {
|
||||||
|
struct Status {
|
||||||
|
u16 x0_buttons;
|
||||||
|
s8 x2_stickX;
|
||||||
|
s8 x3_stickY;
|
||||||
|
s8 x4_substickX;
|
||||||
|
s8 x5_substickY;
|
||||||
|
u8 x6_triggerL;
|
||||||
|
u8 x7_triggerR;
|
||||||
|
u8 x8_analogA;
|
||||||
|
u8 x9_analogB;
|
||||||
|
s8 xa_err;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum BUTTON : u16 {
|
||||||
|
BUTTON_LEFT = 0x0001,
|
||||||
|
BUTTON_RIGHT = 0x0002,
|
||||||
|
BUTTON_DOWN = 0x0004,
|
||||||
|
BUTTON_UP = 0x0008,
|
||||||
|
TRIGGER_Z = 0x0010,
|
||||||
|
TRIGGER_R = 0x0020,
|
||||||
|
TRIGGER_L = 0x0040,
|
||||||
|
BUTTON_A = 0x0100,
|
||||||
|
BUTTON_B = 0x0200,
|
||||||
|
BUTTON_X = 0x0400,
|
||||||
|
BUTTON_Y = 0x0800,
|
||||||
|
BUTTON_START = 0x1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ERROR : s8 {
|
||||||
|
ERR_NONE = 0,
|
||||||
|
ERR_NO_CONTROLLER = -1,
|
||||||
|
ERR_NOT_READY = -2,
|
||||||
|
ERR_TRANSFER = -3,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum MOTOR : u32 {
|
||||||
|
MOTOR_STOP = 0,
|
||||||
|
MOTOR_RUMBLE = 1,
|
||||||
|
MOTOR_STOP_HARD = 2,
|
||||||
|
};
|
||||||
|
enum CHAN : u32 {
|
||||||
|
CHAN0_BIT = 0x80000000,
|
||||||
|
CHAN1_BIT = 0x40000000,
|
||||||
|
CHAN2_BIT = 0x20000000,
|
||||||
|
CHAN3_BIT = 0x10000000,
|
||||||
|
};
|
||||||
|
} // namespace PAD
|
||||||
|
|
||||||
|
namespace SI {
|
||||||
|
constexpr u32 ERROR_NO_RESPONSE = 0x0008;
|
||||||
|
constexpr u32 TYPE_GC = 0x08000000;
|
||||||
|
constexpr u32 GC_STANDARD = 0x01000000;
|
||||||
|
constexpr u32 GC_WIRELESS = 0x80000000;
|
||||||
|
constexpr u32 WIRELESS_STATE = 0x02000000;
|
||||||
|
constexpr u32 WIRELESS_FIX_ID = 0x00100000;
|
||||||
|
constexpr u32 GC_CONTROLLER = (TYPE_GC | GC_STANDARD);
|
||||||
|
constexpr u32 GC_RECEIVER = (TYPE_GC | GC_WIRELESS);
|
||||||
|
constexpr u32 GC_WAVEBIRD = (TYPE_GC | GC_WIRELESS | GC_STANDARD | WIRELESS_STATE | WIRELESS_FIX_ID);
|
||||||
|
} // namespace SI
|
||||||
|
|
||||||
|
using PADSamplingCallback = void (*)(void);
|
||||||
|
constexpr bool PADButtonDown(u16 lastButton, u16 button) { return ((lastButton ^ button) & button) != 0; }
|
||||||
|
constexpr bool PADButtonUp(u16 lastButton, u16 button) { return ((lastButton ^ button) & lastButton) != 0; }
|
||||||
|
void PADClamp(PAD::Status* status);
|
||||||
|
void PADClampCircle(PAD::Status* status);
|
||||||
|
void PADInit();
|
||||||
|
bool PADIsBarrel(s32 chan);
|
||||||
|
u32 PADRead(PAD::Status* status);
|
||||||
|
bool PADRecalibrate(u32 mask);
|
||||||
|
bool PADReset(u32 mask);
|
||||||
|
void PADSetAnalog(u32 mode);
|
||||||
|
void PADSetSpec(s32 spec);
|
||||||
|
void PADSetSamplingCallback(PADSamplingCallback callback);
|
||||||
|
void PADControlAllMotors(const u32* commands);
|
||||||
|
|
||||||
|
u32 SIProbe(s32 chan);
|
|
@ -1,4 +1,6 @@
|
||||||
#include "input.hpp"
|
#include "input.hpp"
|
||||||
|
#include "aurora/pad.hpp"
|
||||||
|
|
||||||
#include <SDL_haptic.h>
|
#include <SDL_haptic.h>
|
||||||
|
|
||||||
#include <absl/container/btree_map.h>
|
#include <absl/container/btree_map.h>
|
||||||
|
@ -13,9 +15,30 @@ struct GameController {
|
||||||
bool m_isGameCube = false;
|
bool m_isGameCube = false;
|
||||||
Sint32 m_index = -1;
|
Sint32 m_index = -1;
|
||||||
bool m_hasRumble = false;
|
bool m_hasRumble = false;
|
||||||
|
constexpr bool operator==(const GameController&) const = default;
|
||||||
};
|
};
|
||||||
absl::flat_hash_map<Uint32, GameController> g_GameControllers;
|
absl::flat_hash_map<Uint32, GameController> g_GameControllers;
|
||||||
|
|
||||||
|
GameController get_controller_for_player(u32 player) {
|
||||||
|
for (const auto& [which, controller] : g_GameControllers) {
|
||||||
|
if (player_index(which) == player) {
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Sint32 get_instance_for_player(u32 player) {
|
||||||
|
for (const auto& [which, controller] : g_GameControllers) {
|
||||||
|
if (player_index(which) == player) {
|
||||||
|
return which;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
static std::optional<std::string> remap_controller_layout(std::string_view mapping) {
|
static std::optional<std::string> remap_controller_layout(std::string_view mapping) {
|
||||||
std::string newMapping;
|
std::string newMapping;
|
||||||
newMapping.reserve(mapping.size());
|
newMapping.reserve(mapping.size());
|
||||||
|
@ -319,3 +342,109 @@ MouseButton translate_mouse_button_state(Uint8 state) noexcept {
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace aurora::input
|
} // namespace aurora::input
|
||||||
|
|
||||||
|
void PADSetSpec(u32 spec) {}
|
||||||
|
void PADInit() {}
|
||||||
|
|
||||||
|
static const std::array<std::pair<SDL_GameControllerButton, PAD::BUTTON>, 12> mMapping{{
|
||||||
|
{SDL_CONTROLLER_BUTTON_A, PAD::BUTTON_A},
|
||||||
|
{SDL_CONTROLLER_BUTTON_B, PAD::BUTTON_B},
|
||||||
|
{SDL_CONTROLLER_BUTTON_X, PAD::BUTTON_X},
|
||||||
|
{SDL_CONTROLLER_BUTTON_Y, PAD::BUTTON_Y},
|
||||||
|
{SDL_CONTROLLER_BUTTON_START, PAD::BUTTON_START},
|
||||||
|
{SDL_CONTROLLER_BUTTON_BACK, PAD::TRIGGER_Z},
|
||||||
|
{SDL_CONTROLLER_BUTTON_LEFTSHOULDER, PAD::TRIGGER_L},
|
||||||
|
{SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, PAD::TRIGGER_R},
|
||||||
|
{SDL_CONTROLLER_BUTTON_DPAD_UP, PAD::BUTTON_UP},
|
||||||
|
{SDL_CONTROLLER_BUTTON_DPAD_DOWN, PAD::BUTTON_DOWN},
|
||||||
|
{SDL_CONTROLLER_BUTTON_DPAD_LEFT, PAD::BUTTON_LEFT},
|
||||||
|
{SDL_CONTROLLER_BUTTON_DPAD_RIGHT, PAD::BUTTON_RIGHT},
|
||||||
|
}};
|
||||||
|
|
||||||
|
u32 PADRead(PAD::Status* status) {
|
||||||
|
u32 rumbleSupport = 0;
|
||||||
|
for (u32 i = 0; i < 4; ++i) {
|
||||||
|
memset(&status[i], 0, sizeof(PAD::Status));
|
||||||
|
auto controller = aurora::input::get_controller_for_player(i);
|
||||||
|
if (controller == aurora::input::GameController{}) {
|
||||||
|
status[i].xa_err = PAD::ERR_NO_CONTROLLER;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
status[i].xa_err = PAD::ERR_NONE;
|
||||||
|
std::for_each(mMapping.begin(), mMapping.end(), [&controller, &i, &status](const auto& pair) {
|
||||||
|
if (SDL_GameControllerGetButton(controller.m_controller, pair.first)) {
|
||||||
|
status[i].x0_buttons |= pair.second;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Sint16 x = SDL_GameControllerGetAxis(controller.m_controller, SDL_CONTROLLER_AXIS_LEFTX);
|
||||||
|
Sint16 y = SDL_GameControllerGetAxis(controller.m_controller, SDL_CONTROLLER_AXIS_LEFTY);
|
||||||
|
x /= 256;
|
||||||
|
y = (-(y + 1u)) / 256u;
|
||||||
|
|
||||||
|
status[i].x2_stickX = static_cast<s8>(x);
|
||||||
|
status[i].x3_stickY = static_cast<s8>(y);
|
||||||
|
|
||||||
|
x = SDL_GameControllerGetAxis(controller.m_controller, SDL_CONTROLLER_AXIS_RIGHTX);
|
||||||
|
y = SDL_GameControllerGetAxis(controller.m_controller, SDL_CONTROLLER_AXIS_RIGHTY);
|
||||||
|
x /= 256;
|
||||||
|
y = (-(y + 1u)) / 256u;
|
||||||
|
|
||||||
|
status[i].x4_substickX = static_cast<s8>(x);
|
||||||
|
status[i].x5_substickY = static_cast<s8>(y);
|
||||||
|
|
||||||
|
x = SDL_GameControllerGetAxis(controller.m_controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT);
|
||||||
|
y = SDL_GameControllerGetAxis(controller.m_controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
|
||||||
|
x /= 128;
|
||||||
|
y = (-(y + 1u)) / 128u;
|
||||||
|
|
||||||
|
status[i].x6_triggerL = static_cast<s8>(x);
|
||||||
|
status[i].x7_triggerR = static_cast<s8>(y);
|
||||||
|
|
||||||
|
|
||||||
|
if (controller.m_hasRumble) {
|
||||||
|
// Nintendo... why are these bits backwards? >.>
|
||||||
|
rumbleSupport |= PAD::CHAN3_BIT << (3 - i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rumbleSupport;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PADControlAllMotors(const u32* commands) {
|
||||||
|
for (u32 i = 0; i < 4; ++i) {
|
||||||
|
auto controller = aurora::input::get_controller_for_player(i);
|
||||||
|
auto instance = aurora::input::get_instance_for_player(i);
|
||||||
|
if (controller == aurora::input::GameController{}) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (controller.m_isGameCube) {
|
||||||
|
if (commands[i] == PAD::MOTOR_STOP) {
|
||||||
|
aurora::input::controller_rumble(instance, 0, 1, 0);
|
||||||
|
} else if (commands[i] == PAD::MOTOR_RUMBLE) {
|
||||||
|
aurora::input::controller_rumble(instance, 1, 1, 0);
|
||||||
|
} else if (commands[i] == PAD::MOTOR_STOP_HARD) {
|
||||||
|
aurora::input::controller_rumble(instance, 0, 0, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO: Figure out sane values for generic controllers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u32 SIProbe(s32 chan) {
|
||||||
|
const auto controller = aurora::input::get_controller_for_player(chan);
|
||||||
|
if (controller == aurora::input::GameController{}){
|
||||||
|
return SI::ERROR_NO_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (controller.m_isGameCube) {
|
||||||
|
auto level = SDL_JoystickCurrentPowerLevel(SDL_GameControllerGetJoystick(controller.m_controller));
|
||||||
|
if (level == SDL_JOYSTICK_POWER_UNKNOWN) {
|
||||||
|
return SI::GC_WAVEBIRD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SI::GC_CONTROLLER;
|
||||||
|
}
|
Loading…
Reference in New Issue