mirror of
https://github.com/izzy2lost/Super3.git
synced 2026-07-05 15:18:38 -07:00
Add controller support and touch shooting
This commit is contained in:
@@ -42,24 +42,55 @@ InputSystem = sdl
|
||||
; - top-right: TestA
|
||||
; - left-middle: D-pad / steer
|
||||
; - right-middle: throttle (top) / brake (bottom)
|
||||
InputCoin1 = KEY_5
|
||||
InputStart1 = KEY_1
|
||||
InputServiceA = KEY_F1
|
||||
InputCoin1 = KEY_5,JOY1_BUTTON7
|
||||
InputStart1 = KEY_1,JOY1_BUTTON8
|
||||
InputServiceA = KEY_F1,JOY1_BUTTON13
|
||||
InputTestA = KEY_F2
|
||||
InputJoyUp = KEY_UP
|
||||
InputJoyDown = KEY_DOWN
|
||||
InputJoyLeft = KEY_LEFT
|
||||
InputJoyRight = KEY_RIGHT
|
||||
InputJoyUp = KEY_UP,JOY1_POV1_UP,JOY1_YAXIS_NEG
|
||||
InputJoyDown = KEY_DOWN,JOY1_POV1_DOWN,JOY1_YAXIS_POS
|
||||
InputJoyLeft = KEY_LEFT,JOY1_POV1_LEFT,JOY1_XAXIS_NEG
|
||||
InputJoyRight = KEY_RIGHT,JOY1_POV1_RIGHT,JOY1_XAXIS_POS
|
||||
InputSteeringLeft = KEY_LEFT
|
||||
InputSteeringRight = KEY_RIGHT
|
||||
InputAccelerator = KEY_W
|
||||
InputBrake = KEY_S
|
||||
InputSteering = JOY1_XAXIS
|
||||
InputAccelerator = KEY_W,JOY1_RZAXIS_POS
|
||||
InputBrake = KEY_S,JOY1_ZAXIS_POS
|
||||
|
||||
; Manual transmission (LB/RB + 4-speed on face buttons)
|
||||
InputGearShiftUp = KEY_Y,JOY1_BUTTON6
|
||||
InputGearShiftDown = KEY_H,JOY1_BUTTON5
|
||||
InputGearShift1 = KEY_Q,JOY1_BUTTON3
|
||||
InputGearShift2 = KEY_W,JOY1_BUTTON1
|
||||
InputGearShift3 = KEY_E,JOY1_BUTTON4
|
||||
InputGearShift4 = KEY_R,JOY1_BUTTON2
|
||||
InputGearShiftN = KEY_T
|
||||
|
||||
; Fighting game buttons (X/A/B/Y)
|
||||
InputPunch = KEY_A,JOY1_BUTTON3
|
||||
InputKick = KEY_S,JOY1_BUTTON1
|
||||
InputGuard = KEY_D,JOY1_BUTTON2
|
||||
InputEscape = KEY_F,JOY1_BUTTON4
|
||||
|
||||
; Spikeout buttons (A/X/Y + B/RB)
|
||||
InputShift = KEY_A,JOY1_BUTTON2,JOY1_BUTTON6
|
||||
InputBeat = KEY_S,JOY1_BUTTON1
|
||||
InputCharge = KEY_D,JOY1_BUTTON3
|
||||
InputJump = KEY_F,JOY1_BUTTON4
|
||||
|
||||
; Virtua Striker buttons (A/B/X)
|
||||
InputShortPass = KEY_A,JOY1_BUTTON1
|
||||
InputLongPass = KEY_S,JOY1_BUTTON2
|
||||
InputShoot = KEY_D,JOY1_BUTTON3
|
||||
|
||||
; View change (single-button games)
|
||||
InputViewChange = KEY_A,JOY1_BUTTON4
|
||||
|
||||
; Handbrake (Sega Rally 2)
|
||||
InputHandBrake = KEY_S,JOY1_BUTTON2
|
||||
InputVR1 = KEY_A,JOY1_BUTTON1
|
||||
InputVR2 = KEY_S,JOY1_BUTTON2
|
||||
InputVR3 = KEY_D,JOY1_BUTTON3
|
||||
InputVR4 = KEY_F,JOY1_BUTTON4
|
||||
InputViewChange = KEY_A,JOY1_BUTTON1
|
||||
InputHandBrake = KEY_S,JOY1_BUTTON2
|
||||
InputRearBrake = KEY_S,JOY1_BUTTON2
|
||||
InputMusicSelect = KEY_D,JOY1_BUTTON3
|
||||
InputTwinJoyTurnLeft = KEY_Q,JOY1_RXAXIS_NEG
|
||||
|
||||
@@ -1,12 +1,36 @@
|
||||
#include "android_input_system.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
AndroidInputSystem::AndroidInputSystem()
|
||||
: CInputSystem("android-sdl"),
|
||||
m_keys(SDL_NUM_SCANCODES, 0)
|
||||
{
|
||||
std::memset(&m_mouseDetails, 0, sizeof(m_mouseDetails));
|
||||
std::strncpy(m_mouseDetails.name, "Touchscreen", MAX_NAME_LENGTH);
|
||||
m_mouseDetails.name[MAX_NAME_LENGTH] = '\0';
|
||||
m_mouseDetails.isAbsolute = true;
|
||||
}
|
||||
|
||||
AndroidInputSystem::~AndroidInputSystem()
|
||||
{
|
||||
CloseControllers();
|
||||
}
|
||||
|
||||
void AndroidInputSystem::SetGunTouchEnabled(bool enabled)
|
||||
{
|
||||
if (m_gunTouchEnabled == enabled)
|
||||
return;
|
||||
m_gunTouchEnabled = enabled;
|
||||
m_gunFingerActive = false;
|
||||
m_gunFinger = 0;
|
||||
m_mouseX = 0;
|
||||
m_mouseY = 0;
|
||||
m_mouseWheelDir = 0;
|
||||
std::memset(m_mouseButtons, 0, sizeof(m_mouseButtons));
|
||||
std::memset(m_mouseButtonPulseUntilMs, 0, sizeof(m_mouseButtonPulseUntilMs));
|
||||
}
|
||||
|
||||
void AndroidInputSystem::ApplyConfig(const Util::Config::Node& config)
|
||||
@@ -50,6 +74,16 @@ bool AndroidInputSystem::InitializeSystem()
|
||||
m_fingerHeldDir.clear();
|
||||
m_fingerHeldKey.clear();
|
||||
m_pulseUntilMs.clear();
|
||||
m_mouseX = 0;
|
||||
m_mouseY = 0;
|
||||
m_mouseWheelDir = 0;
|
||||
std::memset(m_mouseButtons, 0, sizeof(m_mouseButtons));
|
||||
std::memset(m_mouseButtonPulseUntilMs, 0, sizeof(m_mouseButtonPulseUntilMs));
|
||||
m_gunFingerActive = false;
|
||||
m_gunFinger = 0;
|
||||
|
||||
SDL_GameControllerEventState(SDL_ENABLE);
|
||||
RefreshControllers();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -82,6 +116,8 @@ void AndroidInputSystem::PulseKeys(const DualScancode& sc, uint32_t durationMs)
|
||||
|
||||
bool AndroidInputSystem::Poll()
|
||||
{
|
||||
SDL_GameControllerUpdate();
|
||||
|
||||
const uint32_t now = SDL_GetTicks();
|
||||
for (auto it = m_pulseUntilMs.begin(); it != m_pulseUntilMs.end();)
|
||||
{
|
||||
@@ -95,6 +131,15 @@ bool AndroidInputSystem::Poll()
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < kMouseButtons; i++)
|
||||
{
|
||||
if (m_mouseButtonPulseUntilMs[i] != 0 && now >= m_mouseButtonPulseUntilMs[i])
|
||||
{
|
||||
m_mouseButtonPulseUntilMs[i] = 0;
|
||||
m_mouseButtons[i] = 0;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -102,6 +147,11 @@ void AndroidInputSystem::HandleEvent(const SDL_Event& ev)
|
||||
{
|
||||
switch (ev.type)
|
||||
{
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
case SDL_CONTROLLERDEVICEREMAPPED:
|
||||
RefreshControllers();
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
HandleKeyEvent(ev.key, true);
|
||||
break;
|
||||
@@ -135,26 +185,18 @@ void AndroidInputSystem::HandleKeyEvent(const SDL_KeyboardEvent& key, bool down)
|
||||
|
||||
void AndroidInputSystem::HandleControllerButtonEvent(const SDL_ControllerButtonEvent& btn, bool down)
|
||||
{
|
||||
// Basic defaults:
|
||||
// - Start: Start1
|
||||
// - Right shoulder: Coin1
|
||||
// - Back: TestA
|
||||
// - D-pad: menu navigation
|
||||
// - A/B/X/Y: map to common keys for future bindings
|
||||
// Controller buttons can be mapped in Supermodel.ini via JOY mappings, but we also
|
||||
// synthesize a few "touch-style" keys for convenience in the UI / test menu.
|
||||
DualScancode sc;
|
||||
switch (btn.button)
|
||||
{
|
||||
case SDL_CONTROLLER_BUTTON_START: sc = m_touchStart; break;
|
||||
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: sc = m_touchCoin; break;
|
||||
case SDL_CONTROLLER_BUTTON_BACK: sc = m_touchTest; break;
|
||||
case SDL_CONTROLLER_BUTTON_BACK: sc = m_touchCoin; break;
|
||||
case SDL_CONTROLLER_BUTTON_GUIDE: sc = m_touchService; break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_UP: sc = m_touchJoyUp; break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: sc = m_touchJoyDown; break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: sc = m_touchJoyLeft; break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: sc = m_touchJoyRight; break;
|
||||
case SDL_CONTROLLER_BUTTON_A: sc = {SDL_SCANCODE_Z, SDL_SCANCODE_UNKNOWN}; break;
|
||||
case SDL_CONTROLLER_BUTTON_B: sc = {SDL_SCANCODE_X, SDL_SCANCODE_UNKNOWN}; break;
|
||||
case SDL_CONTROLLER_BUTTON_X: sc = {SDL_SCANCODE_C, SDL_SCANCODE_UNKNOWN}; break;
|
||||
case SDL_CONTROLLER_BUTTON_Y: sc = {SDL_SCANCODE_V, SDL_SCANCODE_UNKNOWN}; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
@@ -186,6 +228,38 @@ void AndroidInputSystem::HandleTouch(const SDL_TouchFingerEvent& tf, bool down)
|
||||
if (x > 0.75f && y < 0.25f) { PulseKeys(m_touchTest, 120); return; }
|
||||
}
|
||||
|
||||
// Lightgun/analog-gun games: use the touchscreen as an absolute "mouse" so
|
||||
// existing MOUSE_XAXIS/MOUSE_YAXIS + MOUSE_LEFT_BUTTON mappings work without
|
||||
// a physical mouse.
|
||||
if (m_gunTouchEnabled)
|
||||
{
|
||||
if (down)
|
||||
{
|
||||
if (!m_gunFingerActive)
|
||||
{
|
||||
m_gunFingerActive = true;
|
||||
m_gunFinger = tf.fingerId;
|
||||
SetMousePosFromNormalized(x, y);
|
||||
SetMouseButton(0, true); // left = trigger (held)
|
||||
}
|
||||
else
|
||||
{
|
||||
// Second touch while aiming: treat as offscreen/reload (right button pulse).
|
||||
PulseMouseButton(2, 120);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_gunFingerActive && tf.fingerId == m_gunFinger)
|
||||
{
|
||||
SetMouseButton(0, false);
|
||||
m_gunFingerActive = false;
|
||||
m_gunFinger = 0;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Held throttle/brake zone (right-middle): hold to accelerate/brake.
|
||||
// - Upper half: throttle (KEY_W)
|
||||
// - Lower half: brake (KEY_S)
|
||||
@@ -246,6 +320,15 @@ void AndroidInputSystem::HandleTouch(const SDL_TouchFingerEvent& tf, bool down)
|
||||
|
||||
void AndroidInputSystem::HandleTouchMotion(const SDL_TouchFingerEvent& tf)
|
||||
{
|
||||
if (m_gunTouchEnabled)
|
||||
{
|
||||
if (m_gunFingerActive && tf.fingerId == m_gunFinger)
|
||||
{
|
||||
SetMousePosFromNormalized(tf.x, tf.y);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Update held d-pad direction.
|
||||
auto it = m_fingerHeldDir.find(tf.fingerId);
|
||||
if (it == m_fingerHeldDir.end())
|
||||
@@ -268,6 +351,291 @@ void AndroidInputSystem::HandleTouchMotion(const SDL_TouchFingerEvent& tf)
|
||||
HandleTouch(tf, true);
|
||||
}
|
||||
|
||||
int AndroidInputSystem::GetNumMice()
|
||||
{
|
||||
return m_gunTouchEnabled ? 1 : 0;
|
||||
}
|
||||
|
||||
const MouseDetails* AndroidInputSystem::GetMouseDetails(int mseNum)
|
||||
{
|
||||
if (!m_gunTouchEnabled)
|
||||
return nullptr;
|
||||
if (mseNum == ANY_MOUSE || mseNum == 0)
|
||||
return &m_mouseDetails;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int AndroidInputSystem::GetMouseAxisValue(int mseNum, int axisNum)
|
||||
{
|
||||
if (!m_gunTouchEnabled)
|
||||
return 0;
|
||||
if (mseNum != ANY_MOUSE && mseNum != 0)
|
||||
return 0;
|
||||
|
||||
switch (axisNum)
|
||||
{
|
||||
case AXIS_X: return m_mouseX;
|
||||
case AXIS_Y: return m_mouseY;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int AndroidInputSystem::GetMouseWheelDir(int mseNum)
|
||||
{
|
||||
if (!m_gunTouchEnabled)
|
||||
return 0;
|
||||
if (mseNum != ANY_MOUSE && mseNum != 0)
|
||||
return 0;
|
||||
return m_mouseWheelDir;
|
||||
}
|
||||
|
||||
bool AndroidInputSystem::IsMouseButPressed(int mseNum, int butNum)
|
||||
{
|
||||
if (!m_gunTouchEnabled)
|
||||
return false;
|
||||
if (mseNum != ANY_MOUSE && mseNum != 0)
|
||||
return false;
|
||||
if (butNum < 0 || butNum >= kMouseButtons)
|
||||
return false;
|
||||
return m_mouseButtons[butNum] != 0;
|
||||
}
|
||||
|
||||
void AndroidInputSystem::SetMouseButton(int butNum, bool down)
|
||||
{
|
||||
if (butNum < 0 || butNum >= kMouseButtons)
|
||||
return;
|
||||
m_mouseButtons[butNum] = down ? 1 : 0;
|
||||
if (!down)
|
||||
m_mouseButtonPulseUntilMs[butNum] = 0;
|
||||
}
|
||||
|
||||
void AndroidInputSystem::PulseMouseButton(int butNum, uint32_t durationMs)
|
||||
{
|
||||
if (butNum < 0 || butNum >= kMouseButtons)
|
||||
return;
|
||||
m_mouseButtons[butNum] = 1;
|
||||
m_mouseButtonPulseUntilMs[butNum] = SDL_GetTicks() + durationMs;
|
||||
}
|
||||
|
||||
void AndroidInputSystem::SetMousePosFromNormalized(float x, float y)
|
||||
{
|
||||
// The emulator polls inputs with a fixed display geometry (currently 496x384).
|
||||
// Use the same coordinate system so the core's mouse/lightgun normalization works.
|
||||
constexpr int w = 496;
|
||||
constexpr int h = 384;
|
||||
const int px = (int)std::lround(std::clamp(x, 0.0f, 1.0f) * (float)(w - 1));
|
||||
const int py = (int)std::lround(std::clamp(y, 0.0f, 1.0f) * (float)(h - 1));
|
||||
m_mouseX = std::clamp(px, 0, w - 1);
|
||||
m_mouseY = std::clamp(py, 0, h - 1);
|
||||
}
|
||||
|
||||
void AndroidInputSystem::CloseControllers()
|
||||
{
|
||||
for (auto& c : m_controllers)
|
||||
{
|
||||
if (c.controller)
|
||||
{
|
||||
SDL_GameControllerClose(c.controller);
|
||||
c.controller = nullptr;
|
||||
}
|
||||
}
|
||||
m_controllers.clear();
|
||||
}
|
||||
|
||||
void AndroidInputSystem::RefreshControllers()
|
||||
{
|
||||
CloseControllers();
|
||||
|
||||
const int n = SDL_NumJoysticks();
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
if (!SDL_IsGameController(i))
|
||||
continue;
|
||||
|
||||
SDL_GameController* controller = SDL_GameControllerOpen(i);
|
||||
if (!controller)
|
||||
continue;
|
||||
|
||||
SDL_Joystick* js = SDL_GameControllerGetJoystick(controller);
|
||||
if (!js)
|
||||
{
|
||||
SDL_GameControllerClose(controller);
|
||||
continue;
|
||||
}
|
||||
|
||||
ControllerState state;
|
||||
state.controller = controller;
|
||||
state.instanceId = SDL_JoystickInstanceID(js);
|
||||
|
||||
std::memset(&state.details, 0, sizeof(state.details));
|
||||
const char* name = SDL_GameControllerName(controller);
|
||||
if (!name) name = "GameController";
|
||||
std::strncpy(state.details.name, name, MAX_NAME_LENGTH);
|
||||
state.details.name[MAX_NAME_LENGTH] = '\0';
|
||||
|
||||
// Align with the desktop SDL "gamecontroller" path.
|
||||
state.details.numAxes = 6;
|
||||
state.details.numPOVs = 4;
|
||||
state.details.numButtons = 17;
|
||||
state.details.hasFFeedback = false;
|
||||
|
||||
for (int a = 0; a < NUM_JOY_AXES; a++)
|
||||
{
|
||||
state.details.hasAxis[a] = false;
|
||||
state.details.axisHasFF[a] = false;
|
||||
std::strncpy(state.details.axisName[a], GetDefaultAxisName(a), MAX_NAME_LENGTH);
|
||||
state.details.axisName[a][MAX_NAME_LENGTH] = '\0';
|
||||
}
|
||||
|
||||
state.details.hasAxis[AXIS_X] = true;
|
||||
state.details.hasAxis[AXIS_Y] = true;
|
||||
state.details.hasAxis[AXIS_Z] = true;
|
||||
state.details.hasAxis[AXIS_RX] = true;
|
||||
state.details.hasAxis[AXIS_RY] = true;
|
||||
state.details.hasAxis[AXIS_RZ] = true;
|
||||
|
||||
m_controllers.push_back(state);
|
||||
}
|
||||
}
|
||||
|
||||
int AndroidInputSystem::GetNumJoysticks()
|
||||
{
|
||||
return static_cast<int>(m_controllers.size());
|
||||
}
|
||||
|
||||
const JoyDetails* AndroidInputSystem::GetJoyDetails(int joyNum)
|
||||
{
|
||||
if (joyNum < 0 || joyNum >= static_cast<int>(m_controllers.size()))
|
||||
return nullptr;
|
||||
return &m_controllers[static_cast<size_t>(joyNum)].details;
|
||||
}
|
||||
|
||||
int AndroidInputSystem::AxisValueFor(const ControllerState& c, int axisNum) const
|
||||
{
|
||||
if (!c.controller)
|
||||
return 0;
|
||||
|
||||
switch (axisNum)
|
||||
{
|
||||
case AXIS_X: return (int)SDL_GameControllerGetAxis(c.controller, SDL_CONTROLLER_AXIS_LEFTX);
|
||||
case AXIS_Y: return (int)SDL_GameControllerGetAxis(c.controller, SDL_CONTROLLER_AXIS_LEFTY);
|
||||
case AXIS_Z: return (int)SDL_GameControllerGetAxis(c.controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT);
|
||||
case AXIS_RX: return (int)SDL_GameControllerGetAxis(c.controller, SDL_CONTROLLER_AXIS_RIGHTX);
|
||||
case AXIS_RY: return (int)SDL_GameControllerGetAxis(c.controller, SDL_CONTROLLER_AXIS_RIGHTY);
|
||||
case AXIS_RZ: return (int)SDL_GameControllerGetAxis(c.controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool AndroidInputSystem::PovPressedFor(const ControllerState& c, int povDir) const
|
||||
{
|
||||
if (!c.controller)
|
||||
return false;
|
||||
|
||||
switch (povDir)
|
||||
{
|
||||
case POV_UP: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_DPAD_UP) != 0;
|
||||
case POV_DOWN: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_DPAD_DOWN) != 0;
|
||||
case POV_LEFT: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_DPAD_LEFT) != 0;
|
||||
case POV_RIGHT: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT) != 0;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AndroidInputSystem::ButtonPressedFor(const ControllerState& c, int butNum) const
|
||||
{
|
||||
if (!c.controller)
|
||||
return false;
|
||||
|
||||
// Match the desktop SDLInputSystem "useGameController" mapping.
|
||||
switch (butNum)
|
||||
{
|
||||
case 0: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_A) != 0;
|
||||
case 1: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_B) != 0;
|
||||
case 2: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_X) != 0;
|
||||
case 3: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_Y) != 0;
|
||||
case 4: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_LEFTSHOULDER) != 0;
|
||||
case 5: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) != 0;
|
||||
case 6: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_BACK) != 0;
|
||||
case 7: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_START) != 0;
|
||||
case 8: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_LEFTSTICK) != 0;
|
||||
case 9: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_RIGHTSTICK) != 0;
|
||||
case 10: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_PADDLE1) != 0;
|
||||
case 11: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_PADDLE2) != 0;
|
||||
case 12: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_GUIDE) != 0;
|
||||
case 13: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_PADDLE3) != 0;
|
||||
case 14: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_PADDLE4) != 0;
|
||||
case 15: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_MISC1) != 0;
|
||||
case 16: return SDL_GameControllerGetButton(c.controller, SDL_CONTROLLER_BUTTON_TOUCHPAD) != 0;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
int AndroidInputSystem::GetJoyAxisValue(int joyNum, int axisNum)
|
||||
{
|
||||
if (m_controllers.empty())
|
||||
return 0;
|
||||
|
||||
if (joyNum == ANY_JOYSTICK)
|
||||
{
|
||||
int best = 0;
|
||||
for (const auto& c : m_controllers)
|
||||
{
|
||||
const int v = AxisValueFor(c, axisNum);
|
||||
if (std::abs(v) > std::abs(best))
|
||||
best = v;
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
if (joyNum < 0 || joyNum >= static_cast<int>(m_controllers.size()))
|
||||
return 0;
|
||||
|
||||
return AxisValueFor(m_controllers[static_cast<size_t>(joyNum)], axisNum);
|
||||
}
|
||||
|
||||
bool AndroidInputSystem::IsJoyPOVInDir(int joyNum, int /*povNum*/, int povDir)
|
||||
{
|
||||
if (m_controllers.empty())
|
||||
return false;
|
||||
|
||||
if (joyNum == ANY_JOYSTICK)
|
||||
{
|
||||
for (const auto& c : m_controllers)
|
||||
{
|
||||
if (PovPressedFor(c, povDir))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (joyNum < 0 || joyNum >= static_cast<int>(m_controllers.size()))
|
||||
return false;
|
||||
|
||||
return PovPressedFor(m_controllers[static_cast<size_t>(joyNum)], povDir);
|
||||
}
|
||||
|
||||
bool AndroidInputSystem::IsJoyButPressed(int joyNum, int butNum)
|
||||
{
|
||||
if (m_controllers.empty())
|
||||
return false;
|
||||
|
||||
if (joyNum == ANY_JOYSTICK)
|
||||
{
|
||||
for (const auto& c : m_controllers)
|
||||
{
|
||||
if (ButtonPressedFor(c, butNum))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (joyNum < 0 || joyNum >= static_cast<int>(m_controllers.size()))
|
||||
return false;
|
||||
|
||||
return ButtonPressedFor(m_controllers[static_cast<size_t>(joyNum)], butNum);
|
||||
}
|
||||
|
||||
SDL_Scancode AndroidInputSystem::ScancodeFromSupermodelKeyName(const char* keyName) const
|
||||
{
|
||||
if (!keyName || !keyName[0])
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Inputs/InputSystem.h"
|
||||
@@ -17,7 +18,9 @@ class AndroidInputSystem final : public CInputSystem
|
||||
{
|
||||
public:
|
||||
AndroidInputSystem();
|
||||
~AndroidInputSystem() override = default;
|
||||
~AndroidInputSystem() override;
|
||||
|
||||
void SetGunTouchEnabled(bool enabled);
|
||||
|
||||
// Called from the SDL event loop (main thread).
|
||||
void HandleEvent(const SDL_Event& ev);
|
||||
@@ -30,22 +33,22 @@ public:
|
||||
const char* GetKeyName(int keyIndex) override;
|
||||
bool IsKeyPressed(int /*kbdNum*/, int keyIndex) override;
|
||||
|
||||
int GetMouseAxisValue(int /*mseNum*/, int /*axisNum*/) override { return 0; }
|
||||
int GetMouseWheelDir(int /*mseNum*/) override { return 0; }
|
||||
bool IsMouseButPressed(int /*mseNum*/, int /*butNum*/) override { return false; }
|
||||
int GetMouseAxisValue(int mseNum, int axisNum) override;
|
||||
int GetMouseWheelDir(int mseNum) override;
|
||||
bool IsMouseButPressed(int mseNum, int butNum) override;
|
||||
|
||||
int GetJoyAxisValue(int /*joyNum*/, int /*axisNum*/) override { return 0; }
|
||||
bool IsJoyPOVInDir(int /*joyNum*/, int /*povNum*/, int /*povDir*/) override { return false; }
|
||||
bool IsJoyButPressed(int /*joyNum*/, int /*butNum*/) override { return false; }
|
||||
int GetJoyAxisValue(int joyNum, int axisNum) override;
|
||||
bool IsJoyPOVInDir(int joyNum, int povNum, int povDir) override;
|
||||
bool IsJoyButPressed(int joyNum, int butNum) override;
|
||||
bool ProcessForceFeedbackCmd(int /*joyNum*/, int /*axisNum*/, ForceFeedbackCmd /*ffCmd*/) override { return false; }
|
||||
|
||||
int GetNumKeyboards() override { return 1; }
|
||||
int GetNumMice() override { return 0; }
|
||||
int GetNumJoysticks() override { return 0; }
|
||||
int GetNumMice() override;
|
||||
int GetNumJoysticks() override;
|
||||
|
||||
const KeyDetails* GetKeyDetails(int /*kbdNum*/) override { return nullptr; }
|
||||
const MouseDetails* GetMouseDetails(int /*mseNum*/) override { return nullptr; }
|
||||
const JoyDetails* GetJoyDetails(int /*joyNum*/) override { return nullptr; }
|
||||
const MouseDetails* GetMouseDetails(int mseNum) override;
|
||||
const JoyDetails* GetJoyDetails(int joyNum) override;
|
||||
|
||||
bool Poll() override;
|
||||
|
||||
@@ -54,11 +57,22 @@ public:
|
||||
void UngrabMouse() override {}
|
||||
|
||||
private:
|
||||
static constexpr int kMouseButtons = 5;
|
||||
|
||||
struct ControllerState {
|
||||
SDL_GameController* controller = nullptr;
|
||||
SDL_JoystickID instanceId = 0;
|
||||
JoyDetails details{};
|
||||
};
|
||||
|
||||
struct DualScancode {
|
||||
SDL_Scancode a = SDL_SCANCODE_UNKNOWN;
|
||||
SDL_Scancode b = SDL_SCANCODE_UNKNOWN;
|
||||
};
|
||||
|
||||
void RefreshControllers();
|
||||
void CloseControllers();
|
||||
|
||||
void SetKey(SDL_Scancode sc, bool down);
|
||||
void PulseKey(SDL_Scancode sc, uint32_t durationMs);
|
||||
void SetKeys(const DualScancode& sc, bool down);
|
||||
@@ -69,6 +83,14 @@ private:
|
||||
void HandleTouch(const SDL_TouchFingerEvent& tf, bool down);
|
||||
void HandleTouchMotion(const SDL_TouchFingerEvent& tf);
|
||||
|
||||
int AxisValueFor(const ControllerState& c, int axisNum) const;
|
||||
bool ButtonPressedFor(const ControllerState& c, int butNum) const;
|
||||
bool PovPressedFor(const ControllerState& c, int povDir) const;
|
||||
|
||||
void SetMouseButton(int butNum, bool down);
|
||||
void PulseMouseButton(int butNum, uint32_t durationMs);
|
||||
void SetMousePosFromNormalized(float x, float y);
|
||||
|
||||
SDL_Scancode ScancodeFromSupermodelKeyName(const char* keyName) const;
|
||||
static SDL_Scancode ParseFirstKeyboardScancode(const std::string& mapping, const AndroidInputSystem& self);
|
||||
|
||||
@@ -88,6 +110,18 @@ private:
|
||||
DualScancode m_touchThrottle{SDL_SCANCODE_W, SDL_SCANCODE_UNKNOWN};
|
||||
DualScancode m_touchBrake{SDL_SCANCODE_S, SDL_SCANCODE_UNKNOWN};
|
||||
|
||||
bool m_gunTouchEnabled = false;
|
||||
SDL_FingerID m_gunFinger = 0;
|
||||
bool m_gunFingerActive = false;
|
||||
|
||||
MouseDetails m_mouseDetails{};
|
||||
int m_mouseX = 0;
|
||||
int m_mouseY = 0;
|
||||
int m_mouseWheelDir = 0;
|
||||
uint8_t m_mouseButtons[kMouseButtons]{};
|
||||
uint32_t m_mouseButtonPulseUntilMs[kMouseButtons]{};
|
||||
|
||||
std::vector<ControllerState> m_controllers;
|
||||
std::vector<uint8_t> m_keys; // indexed by SDL_Scancode
|
||||
std::unordered_map<SDL_FingerID, DualScancode> m_fingerHeldDir;
|
||||
std::unordered_map<SDL_FingerID, DualScancode> m_fingerHeldKey;
|
||||
|
||||
@@ -312,6 +312,10 @@ struct Super3Host {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool gunGame =
|
||||
(game.inputs & (Game::INPUT_GUN1 | Game::INPUT_GUN2 | Game::INPUT_ANALOG_GUN1 | Game::INPUT_ANALOG_GUN2)) != 0;
|
||||
inputSystem.SetGunTouchEnabled(gunGame);
|
||||
|
||||
// Apply Supermodel.ini overrides (Global + [ game ]) after the loader has determined game->name.
|
||||
ApplyIniOverrides(game.name);
|
||||
ApplyAndroidHardOverrides();
|
||||
|
||||
@@ -37,4 +37,3 @@
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user