From be589d1b5121bc2a67936a8b7be34c2cf2c42e7a Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 19 Mar 2026 23:54:08 -0700
Subject: [PATCH] Added gamepad button hotkey bindings
This allows you to press the A button to start playing
---
Data/UI/main.xml | 4 +--
game/gameover.cpp | 17 +++++++++
screenlib/UIDialogButton.cpp | 7 +++-
screenlib/UIElementButton.cpp | 66 ++++++++++++++++++++++++++++++++---
screenlib/UIElementButton.h | 7 ++++
5 files changed, 94 insertions(+), 7 deletions(-)
diff --git a/Data/UI/main.xml b/Data/UI/main.xml
index 8ff48544..b10f4499 100644
--- a/Data/UI/main.xml
+++ b/Data/UI/main.xml
@@ -184,7 +184,7 @@
-->
<!-- Instructions -->
- <Button name="PlayButton" template="MenuButton" hotkey="P" text="P" action="play" clickSound="114">
+ <Button name="PlayButton" template="MenuButton" hotkey="P" gamepad="primary" text="P" action="play" clickSound="114">
<Anchor anchorFrom="TOPLEFT" anchorTo="TOPRIGHT" anchor="vertical_divider" x="9" y="10"/>
</Button>
<Label template="SmallYellow" text=" Start playing Maelstrom">
@@ -220,7 +220,7 @@
<Label template="SmallYellow" text=" About Maelstrom...">
<Anchor anchorFrom="BOTTOMLEFT" anchorTo="TOPRIGHT" anchor="AboutButton" x="3" y="21"/>
</Label>
- <Button condition="QUIT_AVAILABLE" name="QuitButton" template="MenuButton" hotkey="Q" text="Q" action="quit" clickSound="106">
+ <Button condition="QUIT_AVAILABLE" name="QuitButton" template="MenuButton" hotkey="Q" gamepad="secondary" text="Q" action="quit" clickSound="106">
<Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="PlayButton" y="170"/>
</Button>
<Label condition="QUIT_AVAILABLE" template="SmallYellow" text=" Quit Maelstrom">
diff --git a/game/gameover.cpp b/game/gameover.cpp
index 6229a57c..9d3f8829 100644
--- a/game/gameover.cpp
+++ b/game/gameover.cpp
@@ -177,12 +177,29 @@ void GameOverPanelDelegate::OnTick()
bool GameOverPanelDelegate::HandleEvent(const SDL_Event &event)
{
char key;
+ SDL_Gamepad *gamepad;
if (!m_handleLabel) {
return false;
}
switch (event.type) {
+ case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
+ return true;
+ case SDL_EVENT_GAMEPAD_BUTTON_UP:
+ gamepad = SDL_OpenGamepad(event.gbutton.which);
+ if (gamepad) {
+ switch (SDL_GetGamepadButtonLabel(gamepad, (SDL_GamepadButton)event.gbutton.button)) {
+ case SDL_GAMEPAD_BUTTON_LABEL_A:
+ case SDL_GAMEPAD_BUTTON_LABEL_CROSS:
+ FinishEnterName();
+ break;
+ default:
+ break;
+ }
+ SDL_CloseGamepad(gamepad);
+ }
+ return true;
case SDL_EVENT_KEY_DOWN:
return true;
case SDL_EVENT_KEY_UP:
diff --git a/screenlib/UIDialogButton.cpp b/screenlib/UIDialogButton.cpp
index 0f548430..94382a33 100644
--- a/screenlib/UIDialogButton.cpp
+++ b/screenlib/UIDialogButton.cpp
@@ -46,7 +46,12 @@ UIDialogButton::Load(rapidxml::xml_node<> *node, const UITemplates *templates)
LoadBool(node, "default", m_default);
if (m_default) {
- m_hotkey = SDLK_RETURN;
+ if (m_hotkey == SDLK_UNKNOWN) {
+ m_hotkey = SDLK_RETURN;
+ }
+ if (m_gamepadButton == GAMEPAD_BUTTON_NONE) {
+ m_gamepadButton = GAMEPAD_BUTTON_PRIMARY;
+ }
}
LoadBool(node, "closeDialog", m_closeDialog);
diff --git a/screenlib/UIElementButton.cpp b/screenlib/UIElementButton.cpp
index a1b39efe..2ece0de3 100644
--- a/screenlib/UIElementButton.cpp
+++ b/screenlib/UIElementButton.cpp
@@ -34,6 +34,7 @@ UIElementButton::UIElementButton(UIBaseElement *parent, const char *name, UIDraw
m_hotkey = SDLK_UNKNOWN;
m_hotkeyMod = SDL_KMOD_NONE;
+ m_gamepadButton = GAMEPAD_BUTTON_NONE;
m_pressSound = NULL;
m_releaseSound = NULL;
m_clickSound = NULL;
@@ -109,6 +110,19 @@ UIElementButton::Load(rapidxml::xml_node<> *node, const UITemplates *templates)
}
}
+ attr = node->first_attribute("gamepad", 0, false);
+ if (attr) {
+ const char *value = attr->value();
+ if (SDL_strcasecmp(value, "primary") == 0) {
+ m_gamepadButton = GAMEPAD_BUTTON_PRIMARY;
+ } else if (SDL_strcasecmp(value, "secondary") == 0) {
+ m_gamepadButton = GAMEPAD_BUTTON_SECONDARY;
+ } else {
+ SetError("Couldn't interpret gamepad value '%s'", value);
+ return false;
+ }
+ }
+
// Load the button state images, if any
static const char *stateImageNames[] = {
"normal_image",
@@ -167,11 +181,53 @@ UIElementButton::ShouldHandleKey(SDL_Keycode key)
return false;
}
+bool
+UIElementButton::ShouldHandleGamepadButton(SDL_JoystickID gamepadID, SDL_GamepadButton button)
+{
+ bool handle = false;
+ SDL_Gamepad *gamepad = nullptr;
+ switch (m_gamepadButton) {
+ case GAMEPAD_BUTTON_PRIMARY:
+ gamepad = SDL_OpenGamepad(gamepadID);
+ if (gamepad) {
+ switch (SDL_GetGamepadButtonLabel(gamepad, button)) {
+ case SDL_GAMEPAD_BUTTON_LABEL_A:
+ case SDL_GAMEPAD_BUTTON_LABEL_CROSS:
+ handle = true;
+ break;
+ default:
+ break;
+ }
+ SDL_CloseGamepad(gamepad);
+ }
+ break;
+ case GAMEPAD_BUTTON_SECONDARY:
+ gamepad = SDL_OpenGamepad(gamepadID);
+ if (gamepad) {
+ switch (SDL_GetGamepadButtonLabel(gamepad, button)) {
+ case SDL_GAMEPAD_BUTTON_LABEL_B:
+ case SDL_GAMEPAD_BUTTON_LABEL_CIRCLE:
+ handle = true;
+ break;
+ default:
+ break;
+ }
+ SDL_CloseGamepad(gamepad);
+ }
+ break;
+ default:
+ break;
+ }
+ return handle;
+}
+
bool
UIElementButton::HandleEvent(const SDL_Event &event)
{
- if (event.type == SDL_EVENT_KEY_DOWN &&
- ShouldHandleKey(event.key.key)) {
+ if ((event.type == SDL_EVENT_KEY_DOWN &&
+ ShouldHandleKey(event.key.key)) ||
+ (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN &&
+ ShouldHandleGamepadButton(event.gbutton.which, (SDL_GamepadButton)event.gbutton.button))) {
if (!m_mousePressed) {
m_mousePressed = true;
OnMouseDown();
@@ -179,8 +235,10 @@ UIElementButton::HandleEvent(const SDL_Event &event)
return true;
}
- if (event.type == SDL_EVENT_KEY_UP &&
- ShouldHandleKey(event.key.key)) {
+ if ((event.type == SDL_EVENT_KEY_UP &&
+ ShouldHandleKey(event.key.key)) ||
+ (event.type == SDL_EVENT_GAMEPAD_BUTTON_UP &&
+ ShouldHandleGamepadButton(event.gbutton.which, (SDL_GamepadButton)event.gbutton.button))) {
if (!m_hotkeyMod || (event.key.mod & m_hotkeyMod)) {
if (m_mousePressed) {
m_mousePressed = false;
diff --git a/screenlib/UIElementButton.h b/screenlib/UIElementButton.h
index 5bfeff0f..d88f74b4 100644
--- a/screenlib/UIElementButton.h
+++ b/screenlib/UIElementButton.h
@@ -35,6 +35,11 @@ DECLARE_TYPESAFE_CLASS(UIElement)
BUTTON_STATE_DISABLED,
NUM_BUTTON_STATES
};
+ enum GAMEPAD_BUTTON {
+ GAMEPAD_BUTTON_NONE,
+ GAMEPAD_BUTTON_PRIMARY,
+ GAMEPAD_BUTTON_SECONDARY
+ };
public:
UIElementButton(UIBaseElement *parent, const char *name, UIDrawEngine *drawEngine);
@@ -57,10 +62,12 @@ DECLARE_TYPESAFE_CLASS(UIElement)
virtual void OnClick() override;
bool ShouldHandleKey(SDL_Keycode key);
+ bool ShouldHandleGamepadButton(SDL_JoystickID gamepadID, SDL_GamepadButton button);
protected:
SDL_Keycode m_hotkey;
int m_hotkeyMod;
+ GAMEPAD_BUTTON m_gamepadButton;
char *m_pressSound;
char *m_releaseSound;
char *m_clickSound;