Add an option to automatically map controller buttons

This uses SDL's GameController API to map buttons to the DS ones
automatically based on their controller database.
This commit is contained in:
Nadia Holmquist Pedersen 2025-05-18 18:40:40 +02:00
parent d6d820c013
commit 79dc510366
7 changed files with 80 additions and 13 deletions

View File

@ -103,6 +103,7 @@ DefaultList<bool> DefaultBools =
{"Emu.DirectBoot", true},
{"Instance*.DS.Battery.LevelOkay", true},
{"Instance*.DSi.Battery.Charging", true},
{"Instance*.Joystick.AutoMap", true},
#ifdef JIT_ENABLED
{"JIT.BranchOptimisations", true},
{"JIT.LiteralOptimisations", true},

View File

@ -146,6 +146,7 @@ public:
void setJoystick(int id);
int getJoystickID() { return joystickID; }
SDL_Joystick* getJoystick() { return joystick; }
bool autoJoystickMapping;
void touchScreen(int x, int y);
void releaseScreen();

View File

@ -40,6 +40,24 @@ const char* EmuInstance::buttonNames[12] =
"Y"
};
SDL_GameControllerButton sdlButtons[12] =
{
// SDL uses A/B for south/east, X/Y for west/north, opposite of the DS
// so we swap the buttons so they map to where they are on the DS positionally
SDL_CONTROLLER_BUTTON_B,
SDL_CONTROLLER_BUTTON_A,
SDL_CONTROLLER_BUTTON_BACK,
SDL_CONTROLLER_BUTTON_START,
SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
SDL_CONTROLLER_BUTTON_DPAD_LEFT,
SDL_CONTROLLER_BUTTON_DPAD_UP,
SDL_CONTROLLER_BUTTON_DPAD_DOWN,
SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
SDL_CONTROLLER_BUTTON_Y,
SDL_CONTROLLER_BUTTON_X
};
const char* EmuInstance::hotkeyNames[HK_MAX] =
{
"HK_Lid",
@ -107,6 +125,8 @@ void EmuInstance::inputLoadConfig()
hkJoyMapping[i] = joycfg.GetInt(hotkeyNames[i]);
}
autoJoystickMapping = joycfg.GetBool("AutoMap");
setJoystick(localCfg.GetInt("JoystickID"));
}
@ -335,7 +355,14 @@ void EmuInstance::inputProcess()
}
joyInputMask = 0xFFF;
if (joystick)
if (autoJoystickMapping && controller != nullptr)
{
for (int i = 0; i < 12; i++)
if (SDL_GameControllerGetButton(controller, sdlButtons[i]))
joyInputMask &= ~(1 << i);
}
else if (joystick)
{
for (int i = 0; i < 12; i++)
if (joystickButtonDown(joyMapping[i]))

View File

@ -94,6 +94,8 @@ InputConfigDialog::InputConfigDialog(QWidget* parent) : QDialog(parent), ui(new
ui->cbxJoystick->setEnabled(false);
}
ui->cbAutoMap->setChecked(joycfg.GetBool("AutoMap"));
setupKeypadPage();
int inst = emuInstance->getInstanceID();
@ -124,7 +126,7 @@ void InputConfigDialog::setupKeypadPage()
delete pushButtonKey;
delete pushButtonJoy;
if (ui->cbxJoystick->isEnabled())
if (ui->cbxJoystick->isEnabled() && !ui->cbAutoMap->isChecked())
{
ui->stackMapping->setCurrentIndex(1);
}
@ -214,6 +216,7 @@ void InputConfigDialog::on_InputConfigDialog_accepted()
}
instcfg.SetInt("JoystickID", joystickID);
joycfg.SetBool("AutoMap", ui->cbAutoMap->isChecked());
Config::Save();
emuInstance->inputLoadConfig();
@ -246,6 +249,25 @@ void InputConfigDialog::on_cbxJoystick_currentIndexChanged(int id)
joystickID = id;
emuInstance->setJoystick(id);
setJoyMappingEnabled(ui->cbAutoMap->isChecked());
}
void InputConfigDialog::on_cbAutoMap_checkStateChanged(Qt::CheckState state)
{
setJoyMappingEnabled(state);
}
void InputConfigDialog::setJoyMappingEnabled(bool enable)
{
if (enable && SDL_IsGameController(emuInstance->getJoystickID()))
{
ui->stackMapping->setCurrentIndex(0);
ui->btnJoyMapSwitch->setEnabled(false);
}
else
{
ui->btnJoyMapSwitch->setEnabled(true);
}
}
SDL_Joystick* InputConfigDialog::getJoystick()

View File

@ -123,6 +123,7 @@ private slots:
void on_btnKeyMapSwitch_clicked();
void on_btnJoyMapSwitch_clicked();
void on_cbxJoystick_currentIndexChanged(int id);
void on_cbAutoMap_checkStateChanged(Qt::CheckState state);
private:
void populatePage(QWidget* page,
@ -130,6 +131,8 @@ private:
int* keymap, int* joymap);
void setupKeypadPage();
void setJoyMappingEnabled(bool enabled);
Ui::InputConfigDialog* ui;
EmuInstance* emuInstance;

View File

@ -2280,7 +2280,7 @@
</widget>
</item>
<item row="7" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QGridLayout" name="gridLayout_5">
<property name="leftMargin">
<number>0</number>
</property>
@ -2293,7 +2293,20 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<item row="0" column="2">
<widget class="QComboBox" name="cbxJoystick">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Selects which joystick will be used for joystick input, if any is present.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@ -2306,16 +2319,13 @@
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbxJoystick">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<item row="1" column="2">
<widget class="QCheckBox" name="cbAutoMap">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Automatically map joystick buttons to their closest equivalents on the DS for controllers that are known to SDL's game controller database.&lt;/p&gt;&lt;p&gt;The joystick mappings page will be disabled when automatic mapping is in effect.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Selects which joystick will be used for joystick input, if any is present.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<property name="text">
<string>Automatically map joystick buttons</string>
</property>
</widget>
</item>

View File

@ -298,6 +298,9 @@ int main(int argc, char** argv)
// http://stackoverflow.com/questions/14543333/joystick-wont-work-using-sdl
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
// Use position-based button mapping instead of label-based
// Needed to make auto mapping map the face buttons correctly regardless of layout
SDL_SetHint(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0");
SDL_SetHint(SDL_HINT_APP_NAME, "melonDS");