Add more controller support

This commit is contained in:
izzy2lost
2026-01-10 19:19:49 -05:00
parent c78ad71eaf
commit 6615baf794
8 changed files with 4582 additions and 6 deletions
+3
View File
@@ -58,6 +58,9 @@ AddressOut = "127.0.0.1"
; absolute positioning when enabled, ideal for using Lightguns.
ABSMiceOnly = false
; Optional SDL GameController mapping database (used by `-input-system=sdlgamepad` and by the Android port).
; SDLGameControllerDB = "Config/gamecontrollerdb.txt"
; Common
InputUIExit = "JOY1_BUTTON10+JOY1_BUTTON9"
InputUIPause = "JOY1_BUTTON9+JOY1_BUTTON4"
File diff suppressed because it is too large Load Diff
+45
View File
@@ -0,0 +1,45 @@
param(
[string]$SourceUrl = "https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt"
)
$ErrorActionPreference = "Stop"
function Write-DbFile([string]$Path, [string]$Content) {
$dir = Split-Path -Parent $Path
if ($dir -and !(Test-Path $dir)) { New-Item -ItemType Directory -Force -Path $dir | Out-Null }
# SDL's parser is happiest with UTF-8 without BOM.
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
[System.IO.File]::WriteAllText($Path, $Content, $utf8NoBom)
$bytes = $utf8NoBom.GetByteCount($Content)
Write-Host ("Wrote {0} ({1} bytes)" -f $Path, $bytes)
}
Write-Host ("Downloading SDL_GameControllerDB from {0}..." -f $SourceUrl)
$headers = @{ "User-Agent" = "Super3-update-gamecontrollerdb" }
$irm = Get-Command Invoke-RestMethod -ErrorAction Stop
if ($irm.Parameters.ContainsKey("UseBasicParsing")) {
$db = Invoke-RestMethod -UseBasicParsing -Uri $SourceUrl -Headers $headers
} else {
$db = Invoke-RestMethod -Uri $SourceUrl -Headers $headers
}
$db = [string]$db
if (-not $db -or $db.Length -lt 1000) {
throw "Downloaded database looks too small; aborting."
}
$stamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
$header = @(
"# Super3 snapshot of SDL_GameControllerDB"
("# Source: {0}" -f $SourceUrl)
("# Updated: {0}" -f $stamp)
""
) -join "`n"
$content = $header + $db.TrimEnd() + "`n"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
Write-DbFile (Join-Path $repoRoot "Config/gamecontrollerdb.txt") $content
Write-DbFile (Join-Path $repoRoot "android/app/src/main/assets/Config/gamecontrollerdb.txt") $content
Write-Host "Done."
+42 -6
View File
@@ -36,12 +36,30 @@
#include "Inputs/Manymouse.h"
#include <iostream>
#include <string>
#include <vector>
using namespace std;
static int available_mice = 1;
static ManyMouseEvent mm_event;
static void TryLoadGameControllerMappings(const Util::Config::Node& config)
{
std::string mappingsPath = config["SDLGameControllerDB"].ValueAsDefault<std::string>("");
if (mappingsPath.empty())
mappingsPath = "Config/gamecontrollerdb.txt";
SDL_RWops* rw = SDL_RWFromFile(mappingsPath.c_str(), "rb");
if (rw == nullptr)
return;
const int added = SDL_GameControllerAddMappingsFromRW(rw, 1);
if (added > 0)
InfoLog("Loaded %d SDL game controller mappings from %s\n", added, mappingsPath.c_str());
else if (added < 0)
ErrorLog("Failed to load SDL game controller mappings from %s (%s)\n", mappingsPath.c_str(), SDL_GetError());
}
SDLKeyMapStruct CSDLInputSystem::s_keyMap[] =
{
// General keys
@@ -209,8 +227,22 @@ void CSDLInputSystem::OpenJoysticks()
if (m_useGameController)
{
gamepad = SDL_GameControllerOpen(joyNum);
joystick = SDL_GameControllerGetJoystick(gamepad);
if (!SDL_IsGameController(joyNum))
{
DebugLog("SDL joystick %d ('%s') is not recognized as a game controller (add mapping to Config/gamecontrollerdb.txt or use -input-system=sdl).\n",
joyNum + 1,
SDL_JoystickNameForIndex(joyNum) ? SDL_JoystickNameForIndex(joyNum) : "Unknown");
continue;
}
gamepad = SDL_GameControllerOpen(joyNum);
if (gamepad == nullptr)
{
ErrorLog("Unable to open SDL game controller device %d (%s) - skipping controller.\n", joyNum + 1, SDL_GetError());
continue;
}
joystick = SDL_GameControllerGetJoystick(gamepad);
} else
{
joystick = SDL_JoystickOpen(joyNum);
@@ -218,6 +250,8 @@ void CSDLInputSystem::OpenJoysticks()
if (joystick == nullptr)
{
if (m_useGameController && gamepad != nullptr)
SDL_GameControllerClose(gamepad);
ErrorLog("Unable to open joystick device %d with SDL - skipping joystick.\n", joyNum + 1);
continue;
}
@@ -233,7 +267,7 @@ void CSDLInputSystem::OpenJoysticks()
joyDetails.name[MAX_NAME_LENGTH] = '\0';
joyDetails.numAxes = 6;
joyDetails.numPOVs = 4;
joyDetails.numButtons = 16;
joyDetails.numButtons = 17;
} else
{
const char *pName = SDL_JoystickName(joystick);
@@ -405,7 +439,8 @@ void CSDLInputSystem::OpenJoysticks()
void CSDLInputSystem::CloseJoysticks()
{
// Close all previously opened joysticks
for (int i = 0; i < GetNumJoysticks(); i++)
const int numJoys = GetNumJoysticks();
for (int i = 0; i < numJoys; i++)
{
JoyDetails joyDetails = m_joyDetails[i];
if (joyDetails.hasFFeedback)
@@ -426,14 +461,14 @@ void CSDLInputSystem::CloseJoysticks()
{
SDL_GameController *gamepad = m_gamepads[i];
SDL_GameControllerClose(gamepad);
m_gamepads.clear();
} else {
SDL_Joystick *joystick = m_joysticks[i];
SDL_JoystickClose(joystick);
m_joysticks.clear();
}
}
m_gamepads.clear();
m_joysticks.clear();
m_joyDetails.clear();
m_SDLHapticDatas.clear();
@@ -459,6 +494,7 @@ bool CSDLInputSystem::InitializeSystem()
ErrorLog("Unable to initialize SDL joystick subsystem (%s).\n", SDL_GetError());
return false;
}
TryLoadGameControllerMappings(m_config);
SDL_GameControllerEventState(SDL_ENABLE);
} else {
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC) != 0)
@@ -36,6 +36,9 @@ New3DAccurate = 0
; Input system (Android uses SDL input backend)
InputSystem = sdl
; Optional SDL GameController mapping database.
; SDLGameControllerDB = "Config/gamecontrollerdb.txt"
; Touch defaults:
; - bottom-left: Coin1
; - bottom-right: Start1
File diff suppressed because it is too large Load Diff
@@ -143,6 +143,15 @@ void AndroidInputSystem::ApplyConfig(const Util::Config::Node& config)
return config[key].ValueAsDefault<std::string>(fallback);
};
{
const std::string newPath = config["SDLGameControllerDB"].ValueAsDefault<std::string>("");
if (newPath != m_gameControllerMappingsPath)
{
m_gameControllerMappingsPath = newPath;
m_gameControllerMappingsLoaded = false;
}
}
m_gunAutoTrigger = config["InputAutoTrigger"].ValueAsDefault<unsigned>(0) != 0;
auto keySc = [&](const std::string& mapping, SDL_Scancode fallback) -> SDL_Scancode {
@@ -217,6 +226,28 @@ void AndroidInputSystem::ApplyConfig(const Util::Config::Node& config)
m_touchSkiSelect2.a = keySc(get("InputSkiSelect2", "KEY_W"), SDL_SCANCODE_W);
}
void AndroidInputSystem::EnsureGameControllerMappingsLoaded()
{
if (m_gameControllerMappingsLoaded)
return;
std::string mappingsPath = m_gameControllerMappingsPath;
if (mappingsPath.empty())
mappingsPath = "Config/gamecontrollerdb.txt";
SDL_RWops* rw = SDL_RWFromFile(mappingsPath.c_str(), "rb");
if (rw != nullptr)
{
const int added = SDL_GameControllerAddMappingsFromRW(rw, 1);
if (added > 0)
SDL_Log("Loaded %d SDL game controller mappings from %s", added, mappingsPath.c_str());
else if (added < 0)
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load SDL game controller mappings from %s (%s)", mappingsPath.c_str(), SDL_GetError());
}
m_gameControllerMappingsLoaded = true;
}
bool AndroidInputSystem::InitializeSystem()
{
std::fill(m_keys.begin(), m_keys.end(), 0);
@@ -238,6 +269,7 @@ bool AndroidInputSystem::InitializeSystem()
m_virtualJoyY = 0;
m_lastVirtualGear = -1;
EnsureGameControllerMappingsLoaded();
SDL_GameControllerEventState(SDL_ENABLE);
RefreshControllers();
return true;
@@ -891,15 +923,41 @@ void AndroidInputSystem::RefreshControllers()
for (int i = 0; i < n; i++)
{
if (!SDL_IsGameController(i))
{
const char* name = SDL_JoystickNameForIndex(i);
SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(i);
char guidStr[64] = {};
SDL_JoystickGetGUIDString(guid, guidStr, sizeof(guidStr));
SDL_Log("Unmapped joystick %d: %s (guid=%s). Add a mapping to %s to enable it.",
i + 1,
name ? name : "Unknown",
guidStr,
m_gameControllerMappingsPath.empty() ? "Config/gamecontrollerdb.txt" : m_gameControllerMappingsPath.c_str());
continue;
}
SDL_GameController* controller = SDL_GameControllerOpen(i);
if (!controller)
{
const char* name = SDL_JoystickNameForIndex(i);
SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(i);
char guidStr[64] = {};
SDL_JoystickGetGUIDString(guid, guidStr, sizeof(guidStr));
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open game controller %d: %s (guid=%s) (%s)",
i + 1,
name ? name : "Unknown",
guidStr,
SDL_GetError());
continue;
}
SDL_Joystick* js = SDL_GameControllerGetJoystick(controller);
if (!js)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get joystick handle for controller %d: %s (%s)",
i + 1,
SDL_GameControllerName(controller) ? SDL_GameControllerName(controller) : "GameController",
SDL_GetError());
SDL_GameControllerClose(controller);
continue;
}
@@ -103,6 +103,8 @@ private:
bool ButtonPressedFor(const ControllerState& c, int butNum) const;
bool PovPressedFor(const ControllerState& c, int povDir) const;
void EnsureGameControllerMappingsLoaded();
void SetMouseButton(int butNum, bool down);
void PulseMouseButton(int butNum, uint32_t durationMs);
void SetMousePosFromNormalized(float x, float y);
@@ -205,4 +207,7 @@ private:
std::unordered_map<SDL_FingerID, HeldDirKeys> m_fingerHeldDir;
std::unordered_map<SDL_FingerID, DualScancode> m_fingerHeldKey;
std::unordered_map<SDL_Scancode, uint32_t> m_pulseUntilMs;
std::string m_gameControllerMappingsPath;
bool m_gameControllerMappingsLoaded = false;
};