From 6bfe25ee072ebc861058bfdd5c2aecc13fb1e2c2 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Fri, 29 May 2026 23:41:48 -0500 Subject: [PATCH] add gamepad menu support --- include/renpy/interact.h | 5 +++ src/main.cpp | 24 ++++++++++++ src/renpy/interact.cpp | 82 ++++++++++++++++++++++++++++++++-------- src/renpy/vulkan.cpp | 7 +++- 4 files changed, 101 insertions(+), 17 deletions(-) diff --git a/include/renpy/interact.h b/include/renpy/interact.h index a8a758f..18f5fcb 100644 --- a/include/renpy/interact.h +++ b/include/renpy/interact.h @@ -12,11 +12,16 @@ namespace renpy { constexpr int yStride = 100; }; + extern int lastGamepadItem; + extern bool lastUseGamepad; + bool overlap(int menuWidth, int menuHeight, int x, int y, int mx, int my, int windowWidth, int windowHeight); void update(interpreter & state, int mx, int my, bool mLeft, + bool gUp, bool gDown, bool gAccept, + bool useGamepad, int windowWidth, int windowHeight); } diff --git a/src/main.cpp b/src/main.cpp index a7b29ed..a827ff6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1023,6 +1023,9 @@ int main() audio::init(); audio::load(renpy::script::audio, renpy::script::audio_length); + bool useGamepad = false; + uint32_t whichGamepad = 0; + while (quit == false) { audio::update(); @@ -1063,6 +1066,7 @@ int main() } } if (event.type == SDL_EVENT_MOUSE_MOTION) { + useGamepad = false; if (event.motion.state & SDL_BUTTON_LMASK) { //collada_state.mouse_motion(cameraIndex, cameraTargetIndex, event.motion.xrel, event.motion.yrel, 0); } @@ -1079,6 +1083,11 @@ int main() if (event.type == SDL_EVENT_GAMEPAD_REMOVED) { remove_gamepad(event.gdevice.which); } + if (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) { + whichGamepad = event.gbutton.which; + useGamepad = true; + fprintf(stderr, "use gamepad: which %d\n", whichGamepad); + } } ////////////////////////////////////////////////////////////////////// @@ -1089,7 +1098,22 @@ int main() float my; uint32_t mouseFlags = SDL_GetMouseState(&mx, &my); bool mLeft = (mouseFlags & SDL_BUTTON_LMASK) != 0; + bool gUp = false; + bool gDown = false; + bool gAccept = false; + if (useGamepad) { + for (int i = 0; i < gamepad_count; i++) { + if (SDL_GetGamepadID(gamepads[i]) == whichGamepad) { + gUp = SDL_GetGamepadButton(gamepads[i], SDL_GAMEPAD_BUTTON_DPAD_UP); + gDown = SDL_GetGamepadButton(gamepads[i], SDL_GAMEPAD_BUTTON_DPAD_DOWN); + gAccept = SDL_GetGamepadButton(gamepads[i], SDL_GAMEPAD_BUTTON_SOUTH) || SDL_GetGamepadButton(gamepads[i], SDL_GAMEPAD_BUTTON_EAST); + break; + } + } + } + renpy::update(interpreter_state, mx, my, mLeft, + gUp, gDown, gAccept, useGamepad, surfaceCapabilities.currentExtent.width, surfaceCapabilities.currentExtent.height); diff --git a/src/renpy/interact.cpp b/src/renpy/interact.cpp index decc020..b6163d6 100644 --- a/src/renpy/interact.cpp +++ b/src/renpy/interact.cpp @@ -30,34 +30,84 @@ namespace renpy { return mxf >= minX && mxf <= maxX && myf >= minY && myf <= maxY; } + static bool lastMenuPause = false; static bool lastmLeft = false; + int lastGamepadItem = 0; + bool lastUseGamepad = false; + + static bool lastgUp = false; + static bool lastgDown = false; + static bool lastgAccept = false; + + static void jumpToMenuItem(interpreter & state, int i) + { + // jump to menu item + uint32_t optionIndex = state.menu.optionIndex + i; + assert(optionIndex < (uint32_t)script::options_length); + uint32_t next_pc = script::options[optionIndex].statementIndex; + fprintf(stderr, "interact[%d]: menu jump %d\n", state.pc, next_pc); + state.pc = next_pc; + state.pause.menu = false; + } void update(interpreter & state, int mx, int my, bool mLeft, + bool _gUp, bool _gDown, bool _gAccept, + bool useGamepad, int windowWidth, int windowHeight) { + lastUseGamepad = useGamepad; + bool mDown = mLeft && (!lastmLeft); lastmLeft = mLeft; - //if (mDown) { - //state.pause.voice = false; - //} - if (!state.pause.menu || !mDown) + bool pauseTransition = state.pause.menu && (!lastMenuPause); + lastMenuPause = state.pause.menu; + + bool gUp = _gUp && (!lastgUp); + bool gDown = _gDown && (!lastgDown); + bool gAccept = _gAccept && (!lastgAccept); + lastgUp = _gUp; + lastgDown = _gDown; + lastgAccept = _gAccept; + + if (!state.pause.menu) { return; + } - for (uint32_t i = 0; i < state.menu.count; i++) { - int y = menu::yStride * i + menu::y; + if (pauseTransition) { + fprintf(stderr, "interact::update: menu pause transition\n"); + lastGamepadItem = 0; + } - bool overlap = renpy::overlap(menu::width, menu::height, menu::x, y, mx, my, windowWidth, windowHeight); - if (overlap) { - // jump to menu item - uint32_t optionIndex = state.menu.optionIndex + i; - assert(optionIndex < (uint32_t)script::options_length); - uint32_t next_pc = script::options[optionIndex].statementIndex; - fprintf(stderr, "interact[%d]: menu jump %d\n", state.pc, next_pc); - state.pc = next_pc; - state.pause.menu = false; - break; + if (useGamepad) { + //printf("useGamepad %d %d\n", gUp, _gAccept); + if (gUp) { + lastGamepadItem -= 1; + if (lastGamepadItem < 0) + lastGamepadItem = state.menu.count - 1; + } + if (gDown) { + lastGamepadItem += 1; + if (lastGamepadItem >= (int)state.menu.count) + lastGamepadItem = 0; + } + if (gAccept) { + jumpToMenuItem(state, lastGamepadItem); + } + } else { + // use mouse + for (uint32_t i = 0; i < state.menu.count; i++) { + int y = menu::yStride * i + menu::y; + + bool overlap = renpy::overlap(menu::width, menu::height, menu::x, y, mx, my, windowWidth, windowHeight); + if (overlap) { + lastGamepadItem = i; + if (mDown) { + jumpToMenuItem(state, i); + return; + } + } } } } diff --git a/src/renpy/vulkan.cpp b/src/renpy/vulkan.cpp index 08b4257..518ca7b 100644 --- a/src/renpy/vulkan.cpp +++ b/src/renpy/vulkan.cpp @@ -534,7 +534,12 @@ namespace renpy { for (uint32_t i = 0; i < state.menu.count; i++) { int y = menu::yStride * i + menu::y; - bool overlap = renpy::overlap(menu::width, menu::height, menu::x, y, mx, my, windowWidth, windowHeight); + bool overlap = false; + if (renpy::lastUseGamepad) { + overlap = (i == (uint32_t)lastGamepadItem); + } else { + overlap = renpy::overlap(menu::width, menu::height, menu::x, y, mx, my, windowWidth, windowHeight); + } instanceMappedData[maximumImageCount * frameIndex + outputIndex++] = { .size = {menu::width, menu::height}, .topLeft = {menu::x, (int16_t)(y)},