From 582bbe51846bb39e16e308d85be08bc865974323 Mon Sep 17 00:00:00 2001 From: Mike Parks Date: Wed, 8 Jan 2025 14:28:02 -0800 Subject: [PATCH] Mostly implemented menu navigation, config change storage, and reboot events --- headers/addons/display.h | 1 + headers/display/ui/elements/GPWidget.h | 4 + headers/display/ui/screens/MainMenuScreen.h | 26 ++- headers/eventmanager.h | 6 + headers/events/GPMenuNavigateEvent.h | 21 ++ headers/events/GPStorageSaveEvent.h | 23 ++ headers/events/GPSystemRebootEvent.h | 21 ++ headers/gp2040.h | 8 + headers/storagemanager.h | 2 + proto/enums.proto | 10 + src/addons/display.cpp | 19 +- src/addons/focus_mode.cpp | 3 + src/addons/turbo.cpp | 2 + src/display/ui/elements/GPMenu.cpp | 68 +++--- src/display/ui/screens/MainMenuScreen.cpp | 246 +++++++++++++++----- src/eventmanager.cpp | 10 +- src/gamepad.cpp | 37 ++- src/gp2040.cpp | 26 +++ www/src/Locales/en/SettingsPage.jsx | 7 + www/src/Pages/SettingsPage.jsx | 7 + 20 files changed, 452 insertions(+), 95 deletions(-) create mode 100644 headers/events/GPMenuNavigateEvent.h create mode 100644 headers/events/GPStorageSaveEvent.h create mode 100644 headers/events/GPSystemRebootEvent.h diff --git a/headers/addons/display.h b/headers/addons/display.h index bbf7902fc..bdc3bf348 100644 --- a/headers/addons/display.h +++ b/headers/addons/display.h @@ -196,6 +196,7 @@ class DisplayAddon : public GPAddon virtual std::string name() { return DisplayName; } void handleSystemRestart(GPEvent* e); + void handleMenuNavigation(GPEvent* e); private: bool updateDisplayScreen(); void drawStatusBar(Gamepad*); diff --git a/headers/display/ui/elements/GPWidget.h b/headers/display/ui/elements/GPWidget.h index 1f3c399a8..133438880 100644 --- a/headers/display/ui/elements/GPWidget.h +++ b/headers/display/ui/elements/GPWidget.h @@ -28,6 +28,9 @@ class GPWidget : public GPGFX_UI { double getScaleX() { return ((double)(this->getViewport().right - this->getViewport().left) / (double)(getRenderer()->getDriver()->getMetrics()->width)); } double getScaleY() { return ((double)(this->getViewport().bottom - this->getViewport().top) / (double)(getRenderer()->getDriver()->getMetrics()->height)); } + + void setVisibility(bool visible) { this->_visibility = visible; } + bool getVisibility() { return this->_visibility; } protected: uint16_t x = 0; uint16_t y = 0; @@ -36,6 +39,7 @@ class GPWidget : public GPGFX_UI { uint16_t fillColor = 0; uint16_t _ID; uint16_t _priority = 0; + bool _visibility = true; GPViewport _viewport; }; diff --git a/headers/display/ui/screens/MainMenuScreen.h b/headers/display/ui/screens/MainMenuScreen.h index f3edd3530..78472e97a 100644 --- a/headers/display/ui/screens/MainMenuScreen.h +++ b/headers/display/ui/screens/MainMenuScreen.h @@ -5,6 +5,7 @@ #include "GPGFX_UI_types.h" #include "enums.pb.h" #include "AnimationStation.hpp" +#include "eventmanager.h" #define INPUT_MODE_XINPUT_NAME "XInput" #define INPUT_MODE_SWITCH_NAME "Nintendo Switch" @@ -65,6 +66,8 @@ class MainMenuScreen : public GPScreen { void selectTurboMode(); int32_t currentTurboMode(); + + void updateMenuNavigation(GpioAction action); protected: virtual void drawScreen(); private: @@ -77,6 +80,10 @@ class MainMenuScreen : public GPScreen { Mask_t prevValues; GPMenu* gpMenu; + bool screenIsPrompting = false; + bool promptChoice = false; + + int8_t exitToScreenBeforePrompt = -1; int8_t exitToScreen = -1; GamepadButtonMapping *mapMenuUp; @@ -88,7 +95,8 @@ class MainMenuScreen : public GPScreen { GamepadButtonMapping *mapMenuToggle; void saveOptions(); - void updateMenuNavigation(GpioAction action); + bool changeRequiresReboot = false; + bool changeRequiresSave = false; #define INPUT_MODE_ENTRIES(name, value) {name##_NAME, NULL, nullptr, std::bind(&MainMenuScreen::currentInputMode, this), std::bind(&MainMenuScreen::selectInputMode, this), value}, #define DPAD_MODE_ENTRIES(name, value) {name##_NAME, NULL, nullptr, std::bind(&MainMenuScreen::currentDpadMode, this), std::bind(&MainMenuScreen::selectDPadMode, this), value}, @@ -97,26 +105,38 @@ class MainMenuScreen : public GPScreen { std::vector inputModeMenu = { InputMode_VALUELIST(INPUT_MODE_ENTRIES) }; + InputMode prevInputMode; + InputMode updateInputMode; std::vector dpadModeMenu = { DpadMode_VALUELIST(DPAD_MODE_ENTRIES) }; + DpadMode prevDpadMode; + DpadMode updateDpadMode; std::vector socdModeMenu = { SOCDMode_VALUELIST(SOCD_MODE_ENTRIES) }; + SOCDMode prevSocdMode; + SOCDMode updateSocdMode; std::vector profilesMenu = {}; + uint8_t prevProfile; + uint8_t updateProfile; std::vector focusModeMenu = { - {"On", NULL, nullptr, std::bind(&MainMenuScreen::currentFocusMode, this), std::bind(&MainMenuScreen::selectFocusMode, this), 1}, {"Off", NULL, nullptr, std::bind(&MainMenuScreen::currentFocusMode, this), std::bind(&MainMenuScreen::selectFocusMode, this), 0}, + {"On", NULL, nullptr, std::bind(&MainMenuScreen::currentFocusMode, this), std::bind(&MainMenuScreen::selectFocusMode, this), 1}, }; + bool prevFocus; + bool updateFocus; std::vector turboModeMenu = { - {"On", NULL, nullptr, std::bind(&MainMenuScreen::currentTurboMode, this), std::bind(&MainMenuScreen::selectTurboMode, this), 1}, {"Off", NULL, nullptr, std::bind(&MainMenuScreen::currentTurboMode, this), std::bind(&MainMenuScreen::selectTurboMode, this), 0}, + {"On", NULL, nullptr, std::bind(&MainMenuScreen::currentTurboMode, this), std::bind(&MainMenuScreen::selectTurboMode, this), 1}, }; + bool prevTurbo; + bool updateTurbo; std::vector mainMenu = { {"Input Mode", NULL, &inputModeMenu, std::bind(&MainMenuScreen::modeValue, this), std::bind(&MainMenuScreen::testMenu, this)}, diff --git a/headers/eventmanager.h b/headers/eventmanager.h index 58cb10b3f..3e224acf9 100644 --- a/headers/eventmanager.h +++ b/headers/eventmanager.h @@ -14,8 +14,11 @@ #include "GPEvent.h" #include "GPGamepadEvent.h" #include "GPEncoderEvent.h" +#include "GPMenuNavigateEvent.h" #include "GPProfileEvent.h" #include "GPRestartEvent.h" +#include "GPStorageSaveEvent.h" +#include "GPSystemRebootEvent.h" #include "GPUSBHostEvent.h" #define EVENTMGR EventManager::getInstance() @@ -33,6 +36,9 @@ class EventManager { return instance; } + void init(); + void clearEventHandlers(); + void registerEventHandler(GPEventType eventType, EventFunction handler); void triggerEvent(GPEvent* event); private: diff --git a/headers/events/GPMenuNavigateEvent.h b/headers/events/GPMenuNavigateEvent.h new file mode 100644 index 000000000..5034a07da --- /dev/null +++ b/headers/events/GPMenuNavigateEvent.h @@ -0,0 +1,21 @@ +#ifndef _GPMENUNAVIGATEEVENT_H_ +#define _GPMENUNAVIGATEEVENT_H_ + +#include "system.h" + +class GPMenuNavigateEvent : public GPEvent { + public: + GPMenuNavigateEvent() {} + GPMenuNavigateEvent(GpioAction action) { + this->menuAction = action; + } + ~GPMenuNavigateEvent() {} + + GPEventType eventType() { return this->_eventType; } + + GpioAction menuAction; + private: + GPEventType _eventType = GP_EVENT_MENU_NAVIGATE; +}; + +#endif \ No newline at end of file diff --git a/headers/events/GPStorageSaveEvent.h b/headers/events/GPStorageSaveEvent.h new file mode 100644 index 000000000..f81c7f2a0 --- /dev/null +++ b/headers/events/GPStorageSaveEvent.h @@ -0,0 +1,23 @@ +#ifndef _GPSTORAGESAVEEVENT_H_ +#define _GPSTORAGESAVEEVENT_H_ + +#include "system.h" + +class GPStorageSaveEvent : public GPEvent { + public: + GPStorageSaveEvent() {} + GPStorageSaveEvent(bool force, bool restart = false) { + this->forceSave = force; + this->restartAfterSave = restart; + } + ~GPStorageSaveEvent() {} + + GPEventType eventType() { return this->_eventType; } + + bool forceSave = false; + bool restartAfterSave = false; + private: + GPEventType _eventType = GP_EVENT_STORAGE_SAVE; +}; + +#endif \ No newline at end of file diff --git a/headers/events/GPSystemRebootEvent.h b/headers/events/GPSystemRebootEvent.h new file mode 100644 index 000000000..9846549c0 --- /dev/null +++ b/headers/events/GPSystemRebootEvent.h @@ -0,0 +1,21 @@ +#ifndef _GPSYSTEMREBOOTEVENT_H_ +#define _GPSYSTEMREBOOTEVENT_H_ + +#include "system.h" + +class GPSystemRebootEvent : public GPEvent { + public: + GPSystemRebootEvent() {} + GPSystemRebootEvent(System::BootMode mode) { + this->bootMode = mode; + } + ~GPSystemRebootEvent() {} + + GPEventType eventType() { return this->_eventType; } + + System::BootMode bootMode = System::BootMode::DEFAULT; + private: + GPEventType _eventType = GP_EVENT_SYSTEM_REBOOT; +}; + +#endif \ No newline at end of file diff --git a/headers/gp2040.h b/headers/gp2040.h index 574f79057..dfc89a22e 100644 --- a/headers/gp2040.h +++ b/headers/gp2040.h @@ -11,6 +11,7 @@ // GP2040 Classes #include "gamepad.h" #include "addonmanager.h" +#include "eventmanager.h" #include "gpdriver.h" #include "pico/types.h" @@ -75,6 +76,13 @@ class GP2040 { // input mask, action std::map bootActions; + + bool saveRequested = false; + bool saveSuccessful = false; + void handleStorageSave(GPEvent* e); + + bool rebootRequested = false; + void handleSystemReboot(GPEvent* e); }; #endif diff --git a/headers/storagemanager.h b/headers/storagemanager.h index bed769283..a564be000 100644 --- a/headers/storagemanager.h +++ b/headers/storagemanager.h @@ -17,6 +17,8 @@ #include "config.pb.h" #include #include "pico/critical_section.h" +#include "eventmanager.h" +#include "GPStorageSaveEvent.h" #define SI Storage::getInstance() diff --git a/proto/enums.proto b/proto/enums.proto index c34dc80d9..9ba5085fa 100644 --- a/proto/enums.proto +++ b/proto/enums.proto @@ -329,6 +329,13 @@ enum GamepadHotkey HOTKEY_DPAD_RIGHT = 41; HOTKEY_PREVIOUS_PROFILE = 42; HOTKEY_SAVE_CONFIG = 43; + HOTKEY_MENU_NAV_UP = 44; + HOTKEY_MENU_NAV_DOWN = 45; + HOTKEY_MENU_NAV_LEFT = 46; + HOTKEY_MENU_NAV_RIGHT = 47; + HOTKEY_MENU_NAV_SELECT = 48; + HOTKEY_MENU_NAV_BACK = 49; + HOTKEY_MENU_NAV_TOGGLE = 50; } // This has to be kept in sync with LEDFormat in NeoPico.hpp @@ -497,4 +504,7 @@ enum GPEventType GP_EVENT_BUTTON_PROCESSED_DOWN = 9; GP_EVENT_ANALOG_MOVE = 10; GP_EVENT_ANALOG_PROCESSED_MOVE = 11; + GP_EVENT_STORAGE_SAVE = 12; + GP_EVENT_SYSTEM_REBOOT = 13; + GP_EVENT_MENU_NAVIGATE = 14; }; diff --git a/src/addons/display.cpp b/src/addons/display.cpp index c5669fe23..d6c746f9c 100644 --- a/src/addons/display.cpp +++ b/src/addons/display.cpp @@ -78,6 +78,7 @@ void DisplayAddon::setup() { updateDisplayScreen(); EventManager::getInstance().registerEventHandler(GP_EVENT_RESTART, GPEVENT_CALLBACK(this->handleSystemRestart(event))); + EventManager::getInstance().registerEventHandler(GP_EVENT_MENU_NAVIGATE, GPEVENT_CALLBACK(this->handleMenuNavigation(event))); } bool DisplayAddon::updateDisplayScreen() { @@ -231,4 +232,20 @@ void DisplayAddon::handleSystemRestart(GPEvent* e) { currDisplayMode = DisplayMode::RESTART; bootMode = (uint32_t)((GPRestartEvent*)e)->bootMode; updateDisplayScreen(); -} \ No newline at end of file +} + +void DisplayAddon::handleMenuNavigation(GPEvent* e) { + if (currDisplayMode != MAIN_MENU) { + if (((GPMenuNavigateEvent*)e)->menuAction == GpioAction::MENU_NAVIGATION_TOGGLE) { + currDisplayMode = MAIN_MENU; + updateDisplayScreen(); + } + } else { + if (((GPMenuNavigateEvent*)e)->menuAction != GpioAction::MENU_NAVIGATION_TOGGLE) { + ((MainMenuScreen*)gpScreen)->updateMenuNavigation(((GPMenuNavigateEvent*)e)->menuAction); + } else { + currDisplayMode = BUTTONS; + updateDisplayScreen(); + } + } +} diff --git a/src/addons/focus_mode.cpp b/src/addons/focus_mode.cpp index 094f77426..37bb58b33 100644 --- a/src/addons/focus_mode.cpp +++ b/src/addons/focus_mode.cpp @@ -28,6 +28,9 @@ void FocusModeAddon::setup() { void FocusModeAddon::process() { Gamepad * gamepad = Storage::getInstance().GetGamepad(); Mask_t values = Storage::getInstance().GetGamepad()->debouncedGpio; + + if (!Storage::getInstance().getAddonOptions().focusModeOptions.enabled) return; + if (values & mapFocusMode->pinMask) { if (buttonLockMask & GAMEPAD_MASK_DU) { gamepad->state.dpad &= ~GAMEPAD_MASK_UP; diff --git a/src/addons/turbo.cpp b/src/addons/turbo.cpp index 8a05cb969..48ecd8331 100644 --- a/src/addons/turbo.cpp +++ b/src/addons/turbo.cpp @@ -134,6 +134,8 @@ void TurboInput::process() uint16_t buttonsPressed = gamepad->state.buttons & TURBO_BUTTON_MASK; uint8_t dpadPressed = gamepad->state.dpad & GAMEPAD_MASK_DPAD; + if (!options.enabled) return; + // Check for TURBO pin enabled if (gamepad->debouncedGpio & turboPinMask) { if (buttonsPressed && (lastPressed != buttonsPressed)) { diff --git a/src/display/ui/elements/GPMenu.cpp b/src/display/ui/elements/GPMenu.cpp index 50c1978c3..f81ba51c3 100644 --- a/src/display/ui/elements/GPMenu.cpp +++ b/src/display/ui/elements/GPMenu.cpp @@ -3,41 +3,43 @@ #include void GPMenu::draw() { - uint16_t baseX = this->x; - uint16_t baseY = this->y; - - uint16_t menuWidth = this->menuSizeX * 6; - uint16_t menuHeight = this->menuSizeY * 8; - - uint16_t dataSize = this->getDataSize(); - uint16_t totalPages = (dataSize + this->menuSizeY - 1) / this->menuSizeY; - uint16_t itemPage = (this->menuIndex / this->menuSizeY); - - int16_t currPageItems = (dataSize - (itemPage * this->menuSizeY)); - if (currPageItems > this->menuSizeY) { - currPageItems = this->menuSizeY; - } else if (currPageItems <= 0) { - currPageItems = 0; - } - - getRenderer()->drawText((21-this->menuTitle.length()) / 2, 0, this->menuTitle.c_str()); - - std::string pageDisplay = ""; - pageDisplay += "Page: " + std::to_string(itemPage+1) + "/" + std::to_string(totalPages); - getRenderer()->drawText(11, 7, pageDisplay.c_str()); + if (this->getVisibility()) { + uint16_t baseX = this->x; + uint16_t baseY = this->y; + + uint16_t menuWidth = this->menuSizeX * 6; + uint16_t menuHeight = this->menuSizeY * 8; + + uint16_t dataSize = this->getDataSize(); + uint16_t totalPages = (dataSize + this->menuSizeY - 1) / this->menuSizeY; + uint16_t itemPage = (this->menuIndex / this->menuSizeY); + + int16_t currPageItems = (dataSize - (itemPage * this->menuSizeY)); + if (currPageItems > this->menuSizeY) { + currPageItems = this->menuSizeY; + } else if (currPageItems <= 0) { + currPageItems = 0; + } - if (this->menuEntryData->size() > 0) { - for (uint8_t menuLine = 0; menuLine < currPageItems; menuLine++) { - uint8_t pageLine = (this->menuSizeY * itemPage) + menuLine; - int32_t lineValue = this->menuEntryData->at(pageLine).optionValue; - bool showCurrentOption = false; - if (lineValue != -1) { - showCurrentOption = (this->menuEntryData->at(pageLine).currentValue() == this->menuEntryData->at(pageLine).optionValue); + getRenderer()->drawText((21-this->menuTitle.length()) / 2, 0, this->menuTitle.c_str()); + + std::string pageDisplay = ""; + pageDisplay += "Page: " + std::to_string(itemPage+1) + "/" + std::to_string(totalPages); + getRenderer()->drawText(11, 7, pageDisplay.c_str()); + + if (this->menuEntryData->size() > 0) { + for (uint8_t menuLine = 0; menuLine < currPageItems; menuLine++) { + uint8_t pageLine = (this->menuSizeY * itemPage) + menuLine; + int32_t lineValue = this->menuEntryData->at(pageLine).optionValue; + bool showCurrentOption = false; + if (lineValue != -1) { + showCurrentOption = (this->menuEntryData->at(pageLine).currentValue() == this->menuEntryData->at(pageLine).optionValue); + } + getRenderer()->drawText(2, 2+menuLine, this->menuEntryData->at(pageLine).label + (showCurrentOption ? " *" : "")); } - getRenderer()->drawText(2, 2+menuLine, this->menuEntryData->at(pageLine).label + (showCurrentOption ? " *" : "")); } - } - // draw cursor - getRenderer()->drawText(1, 2+(this->menuIndex % this->menuSizeY), CHAR_RIGHT); + // draw cursor + getRenderer()->drawText(1, 2+(this->menuIndex % this->menuSizeY), CHAR_RIGHT); + } } diff --git a/src/display/ui/screens/MainMenuScreen.cpp b/src/display/ui/screens/MainMenuScreen.cpp index 42b67cc70..dd94d3102 100644 --- a/src/display/ui/screens/MainMenuScreen.cpp +++ b/src/display/ui/screens/MainMenuScreen.cpp @@ -43,7 +43,7 @@ void MainMenuScreen::init() { if (menuLabel.empty()) { menuLabel = "Profile #" + std::to_string(profileCtr); } - MenuEntry menuEntry = {menuLabel, NULL, nullptr, std::bind(&MainMenuScreen::currentProfile, this), std::bind(&MainMenuScreen::selectProfile, this), profileCtr}; + MenuEntry menuEntry = {menuLabel, NULL, nullptr, std::bind(&MainMenuScreen::currentProfile, this), std::bind(&MainMenuScreen::selectProfile, this), profileCtr+1}; profilesMenu.push_back(menuEntry); } @@ -60,6 +60,26 @@ void MainMenuScreen::init() { default: break; } } + + changeRequiresReboot = false; + changeRequiresSave = false; + prevInputMode = Storage::getInstance().GetGamepad()->getOptions().inputMode; + updateInputMode = Storage::getInstance().GetGamepad()->getOptions().inputMode; + + prevDpadMode = Storage::getInstance().GetGamepad()->getOptions().dpadMode; + updateDpadMode = Storage::getInstance().GetGamepad()->getOptions().dpadMode; + + prevSocdMode = Storage::getInstance().GetGamepad()->getOptions().socdMode; + updateSocdMode = Storage::getInstance().GetGamepad()->getOptions().socdMode; + + prevProfile = Storage::getInstance().GetGamepad()->getOptions().profileNumber; + updateProfile = Storage::getInstance().GetGamepad()->getOptions().profileNumber; + + prevFocus = Storage::getInstance().getAddonOptions().focusModeOptions.enabled; + updateFocus = Storage::getInstance().getAddonOptions().focusModeOptions.enabled; + + prevTurbo = Storage::getInstance().getAddonOptions().turboOptions.enabled; + updateTurbo = Storage::getInstance().getAddonOptions().turboOptions.enabled; } void MainMenuScreen::shutdown() { @@ -68,6 +88,27 @@ void MainMenuScreen::shutdown() { } void MainMenuScreen::drawScreen() { + gpMenu->setVisibility(!screenIsPrompting); + + if (!screenIsPrompting) { + + } else { + getRenderer()->drawText(1, 1, "Config has changed."); + if (changeRequiresSave && !changeRequiresReboot) { + getRenderer()->drawText(3, 3, "Would you like"); + getRenderer()->drawText(6, 4, "to save?"); + } else if (changeRequiresSave && changeRequiresReboot) { + getRenderer()->drawText(3, 3, "Would you like"); + getRenderer()->drawText(1, 4, "to save & restart?"); + } else { + + } + + if (promptChoice) getRenderer()->drawText(5, 6, CHAR_RIGHT); + getRenderer()->drawText(6, 6, "Yes"); + if (!promptChoice) getRenderer()->drawText(11, 6, CHAR_RIGHT); + getRenderer()->drawText(12, 6, "No"); + } } void MainMenuScreen::setMenu(std::vector* menu) { @@ -90,13 +131,18 @@ int8_t MainMenuScreen::update() { } else if (values & mapMenuBack->pinMask) { updateMenuNavigation(GpioAction::MENU_NAVIGATION_BACK); } - } else { - isPressed = false; } prevButtonState = buttonState; prevValues = values; + if ((exitToScreen != -1) && ((changeRequiresSave) || (changeRequiresReboot))) { + // trying to exit menu but a change requires a save/reboot + exitToScreenBeforePrompt = exitToScreen; + exitToScreen = -1; + screenIsPrompting = true; + } + return exitToScreen; } @@ -106,59 +152,83 @@ void MainMenuScreen::updateMenuNavigation(GpioAction action) { switch (action) { case GpioAction::MENU_NAVIGATION_UP: - if (menuIndex > 0) { - menuIndex--; + if (!screenIsPrompting) { + if (menuIndex > 0) { + menuIndex--; + } else { + menuIndex = menuSize-1; + } + changeIndex = true; } else { - menuIndex = menuSize-1; + promptChoice = !promptChoice; } - changeIndex = true; isPressed = true; break; case GpioAction::MENU_NAVIGATION_DOWN: - if (menuIndex < menuSize-1) { - menuIndex++; + if (!screenIsPrompting) { + if (menuIndex < menuSize-1) { + menuIndex++; + } else { + menuIndex = 0; + } + changeIndex = true; } else { - menuIndex = 0; + promptChoice = !promptChoice; } - changeIndex = true; isPressed = true; break; case GpioAction::MENU_NAVIGATION_LEFT: + if (screenIsPrompting) { + promptChoice = !promptChoice; + } break; case GpioAction::MENU_NAVIGATION_RIGHT: + if (screenIsPrompting) { + promptChoice = !promptChoice; + } break; case GpioAction::MENU_NAVIGATION_SELECT: - if (currentMenu->at(menuIndex).submenu != nullptr) { - previousMenu = currentMenu; - currentMenu = currentMenu->at(menuIndex).submenu; - gpMenu->setMenuData(currentMenu); - gpMenu->setMenuTitle(previousMenu->at(menuIndex).label); - menuIndex = 0; - changeIndex = true; + if (!screenIsPrompting) { + if (currentMenu->at(menuIndex).submenu != nullptr) { + previousMenu = currentMenu; + currentMenu = currentMenu->at(menuIndex).submenu; + gpMenu->setMenuData(currentMenu); + gpMenu->setMenuTitle(previousMenu->at(menuIndex).label); + menuIndex = 0; + changeIndex = true; + } else { + currentMenu->at(menuIndex).action(); + } } else { - currentMenu->at(menuIndex).action(); + if (promptChoice) { + saveOptions(); + } else { + + } } isPressed = true; break; case GpioAction::MENU_NAVIGATION_BACK: - if (previousMenu != nullptr) { - currentMenu = previousMenu; - previousMenu = nullptr; - menuIndex = 0; - changeIndex = true; - gpMenu->setMenuData(currentMenu); - gpMenu->setMenuTitle(MAIN_MENU_NAME); - } else { - exitToScreen = DisplayMode::BUTTONS; - isPressed = false; + if (!screenIsPrompting) { + if (previousMenu != nullptr) { + currentMenu = previousMenu; + previousMenu = nullptr; + menuIndex = 0; + changeIndex = true; + gpMenu->setMenuData(currentMenu); + gpMenu->setMenuTitle(MAIN_MENU_NAME); + } else { + exitToScreen = DisplayMode::BUTTONS; + exitToScreenBeforePrompt = DisplayMode::BUTTONS; + isPressed = false; + } } isPressed = true; break; - case GpioAction::MENU_NAVIGATION_TOGGLE: - // when in the menu screen, this exits the menu without confirming changes - exitToScreen = DisplayMode::BUTTONS; - isPressed = false; - break; +// case GpioAction::MENU_NAVIGATION_TOGGLE: +// exitToScreen = DisplayMode::BUTTONS; +// isPressed = false; +// break; default: break; } @@ -172,7 +242,7 @@ void MainMenuScreen::testMenu() { void MainMenuScreen::saveAndExit() { saveOptions(); - exitToScreen = DisplayMode::BUTTONS; + //exitToScreen = DisplayMode::BUTTONS; } int32_t MainMenuScreen::modeValue() { @@ -181,73 +251,137 @@ int32_t MainMenuScreen::modeValue() { void MainMenuScreen::selectInputMode() { if (currentMenu->at(menuIndex).optionValue != -1) { - if (Storage::getInstance().GetGamepad()->getOptions().inputMode != (InputMode)currentMenu->at(menuIndex).optionValue) { - Storage::getInstance().GetGamepad()->setInputMode((InputMode)currentMenu->at(menuIndex).optionValue); - saveOptions(); + InputMode valueToSave = (InputMode)currentMenu->at(menuIndex).optionValue; + prevInputMode = Storage::getInstance().GetGamepad()->getOptions().inputMode; + updateInputMode = valueToSave; + + if (prevInputMode != valueToSave) { + // input mode requires a save and reboot + changeRequiresReboot = true; + changeRequiresSave = true; } } } int32_t MainMenuScreen::currentInputMode() { - return Storage::getInstance().getGamepadOptions().inputMode; + return updateInputMode; } void MainMenuScreen::selectDPadMode() { if (currentMenu->at(menuIndex).optionValue != -1) { - Storage::getInstance().GetGamepad()->setDpadMode((DpadMode)currentMenu->at(menuIndex).optionValue); - saveOptions(); + DpadMode valueToSave = (DpadMode)currentMenu->at(menuIndex).optionValue; + prevDpadMode = Storage::getInstance().GetGamepad()->getOptions().dpadMode; + updateDpadMode = valueToSave; + + if (prevDpadMode != valueToSave) changeRequiresSave = true; } } int32_t MainMenuScreen::currentDpadMode() { - return Storage::getInstance().getGamepadOptions().dpadMode; + return updateDpadMode; } void MainMenuScreen::selectSOCDMode() { if (currentMenu->at(menuIndex).optionValue != -1) { - Storage::getInstance().GetGamepad()->setSOCDMode((SOCDMode)currentMenu->at(menuIndex).optionValue); - saveOptions(); + SOCDMode valueToSave = (SOCDMode)currentMenu->at(menuIndex).optionValue; + prevSocdMode = Storage::getInstance().GetGamepad()->getOptions().socdMode; + updateSocdMode = valueToSave; + + if (prevDpadMode != valueToSave) changeRequiresSave = true; } } int32_t MainMenuScreen::currentSOCDMode() { - return Storage::getInstance().getGamepadOptions().socdMode; + return updateSocdMode; } void MainMenuScreen::saveOptions() { - //Storage::getInstance().save(true); - //System::reboot(System::BootMode::DEFAULT); + GamepadOptions& options = Storage::getInstance().getGamepadOptions(); + + if (changeRequiresSave) { + bool saveHasChanged = false; + if (prevInputMode != updateInputMode) { + options.inputMode = updateInputMode; + saveHasChanged = true; + } + if (prevDpadMode != updateDpadMode) { + options.dpadMode = updateDpadMode; + saveHasChanged = true; + } + if (prevSocdMode != updateSocdMode) { + options.socdMode = updateSocdMode; + saveHasChanged = true; + } + if (prevProfile != updateProfile) { + options.profileNumber = updateProfile; + saveHasChanged = true; + } + if (prevFocus != updateFocus) { + Storage::getInstance().getAddonOptions().focusModeOptions.enabled = updateFocus; + saveHasChanged = true; + } + if (prevTurbo != updateTurbo) { + Storage::getInstance().getAddonOptions().turboOptions.enabled = updateTurbo; + saveHasChanged = true; + } + + if (saveHasChanged) { + EventManager::getInstance().triggerEvent(new GPStorageSaveEvent(true)); + screenIsPrompting = false; + } + changeRequiresSave = false; + } + + if (changeRequiresReboot) { + EventManager::getInstance().triggerEvent(new GPRestartEvent(System::BootMode::DEFAULT)); + screenIsPrompting = false; + changeRequiresReboot = false; + } + + if (exitToScreenBeforePrompt != -1) { + exitToScreen = exitToScreenBeforePrompt; + exitToScreenBeforePrompt = -1; + } } void MainMenuScreen::selectProfile() { if (currentMenu->at(menuIndex).optionValue != -1) { - Storage::getInstance().setProfile(currentMenu->at(menuIndex).optionValue); - saveOptions(); + uint8_t valueToSave = currentMenu->at(menuIndex).optionValue; + prevProfile = Storage::getInstance().GetGamepad()->getOptions().profileNumber; + updateProfile = valueToSave; + + if (prevProfile != valueToSave) changeRequiresSave = true; } } int32_t MainMenuScreen::currentProfile() { - return Storage::getInstance().GetGamepad()->getOptions().profileNumber; + return updateProfile; } void MainMenuScreen::selectFocusMode() { if (currentMenu->at(menuIndex).optionValue != -1) { - Storage::getInstance().getAddonOptions().focusModeOptions.enabled = (bool)currentMenu->at(menuIndex).optionValue; - saveOptions(); + uint8_t valueToSave = currentMenu->at(menuIndex).optionValue; + prevFocus = Storage::getInstance().getAddonOptions().focusModeOptions.enabled; + updateFocus = valueToSave; + + if (prevFocus != valueToSave) changeRequiresSave = true; } } int32_t MainMenuScreen::currentFocusMode() { - return Storage::getInstance().getAddonOptions().focusModeOptions.enabled; + return updateFocus; } void MainMenuScreen::selectTurboMode() { if (currentMenu->at(menuIndex).optionValue != -1) { - Storage::getInstance().getAddonOptions().turboOptions.enabled = (bool)currentMenu->at(menuIndex).optionValue; - saveOptions(); + uint8_t valueToSave = currentMenu->at(menuIndex).optionValue; + prevTurbo = Storage::getInstance().getAddonOptions().turboOptions.enabled; + updateTurbo = valueToSave; + + if (updateTurbo != valueToSave) changeRequiresSave = true; } } int32_t MainMenuScreen::currentTurboMode() { - return Storage::getInstance().getAddonOptions().turboOptions.enabled; + return updateTurbo; } diff --git a/src/eventmanager.cpp b/src/eventmanager.cpp index cf80b2eab..3ba513462 100644 --- a/src/eventmanager.cpp +++ b/src/eventmanager.cpp @@ -2,6 +2,10 @@ #include "storagemanager.h" #include "enums.pb.h" +void EventManager::init() { + clearEventHandlers(); +} + void EventManager::registerEventHandler(GPEventType eventType, EventFunction handler) { typename std::vector::iterator it = std::find_if(eventList.begin(), eventList.end(), [&eventType](const EventEntry& entry) { return entry.first == eventType; }); @@ -26,4 +30,8 @@ void EventManager::triggerEvent(GPEvent* event) { } } delete event; -} \ No newline at end of file +} + +void EventManager::clearEventHandlers() { + +} diff --git a/src/gamepad.cpp b/src/gamepad.cpp index df699f5b6..a36d118cc 100644 --- a/src/gamepad.cpp +++ b/src/gamepad.cpp @@ -651,12 +651,47 @@ void Gamepad::processHotkeyAction(GamepadHotkey action) { reqSave = true; } break; + case HOTKEY_MENU_NAV_UP: + if (action != lastAction) { + EventManager::getInstance().triggerEvent(new GPMenuNavigateEvent(GpioAction::MENU_NAVIGATION_UP)); + } + break; + case HOTKEY_MENU_NAV_DOWN: + if (action != lastAction) { + EventManager::getInstance().triggerEvent(new GPMenuNavigateEvent(GpioAction::MENU_NAVIGATION_DOWN)); + } + break; + case HOTKEY_MENU_NAV_LEFT: + if (action != lastAction) { + EventManager::getInstance().triggerEvent(new GPMenuNavigateEvent(GpioAction::MENU_NAVIGATION_LEFT)); + } + break; + case HOTKEY_MENU_NAV_RIGHT: + if (action != lastAction) { + EventManager::getInstance().triggerEvent(new GPMenuNavigateEvent(GpioAction::MENU_NAVIGATION_RIGHT)); + } + break; + case HOTKEY_MENU_NAV_SELECT: + if (action != lastAction) { + EventManager::getInstance().triggerEvent(new GPMenuNavigateEvent(GpioAction::MENU_NAVIGATION_SELECT)); + } + break; + case HOTKEY_MENU_NAV_BACK: + if (action != lastAction) { + EventManager::getInstance().triggerEvent(new GPMenuNavigateEvent(GpioAction::MENU_NAVIGATION_BACK)); + } + break; + case HOTKEY_MENU_NAV_TOGGLE: + if (action != lastAction) { + EventManager::getInstance().triggerEvent(new GPMenuNavigateEvent(GpioAction::MENU_NAVIGATION_TOGGLE)); + } + break; default: // Unknown action return; } // only save if requested if (reqSave) { - Storage::getInstance().save(); + EventManager::getInstance().triggerEvent(new GPStorageSaveEvent(true)); } } diff --git a/src/gp2040.cpp b/src/gp2040.cpp index 28f8b01d0..82226ddc6 100644 --- a/src/gp2040.cpp +++ b/src/gp2040.cpp @@ -184,6 +184,9 @@ void GP2040::setup() { // before USB host will be used so we can force it to ignore the check Storage::getInstance().save(true); } + + // register system event handlers + EventManager::getInstance().registerEventHandler(GP_EVENT_STORAGE_SAVE, GPEVENT_CALLBACK(this->handleStorageSave(event))); } /** @@ -317,6 +320,20 @@ void GP2040::run() { addons.ProcessAddons(ADDON_PROCESS::CORE0_USBREPORT); tud_task(); // TinyUSB Task update + + if (rebootRequested) { + rebootRequested = false; + if (saveRequested) { + saveRequested = false; + Storage::getInstance().save(true); + } + System::reboot(System::BootMode::DEFAULT); + } else { + if (saveRequested) { + saveRequested = false; + Storage::getInstance().save(true); + } + } } } @@ -526,3 +543,12 @@ void GP2040::checkProcessedState(GamepadState prevState, GamepadState currState) EventManager::getInstance().triggerEvent(new GPAnalogProcessedMoveEvent(currState.lx, currState.ly, currState.rx, currState.ry, currState.lt, currState.rt)); } } + +void GP2040::handleStorageSave(GPEvent* e) { + saveRequested = true; + rebootRequested = ((GPStorageSaveEvent*)e)->restartAfterSave; +} + +void GP2040::handleSystemReboot(GPEvent* e) { + rebootRequested = true; +} diff --git a/www/src/Locales/en/SettingsPage.jsx b/www/src/Locales/en/SettingsPage.jsx index 2df00bc49..5c44e719e 100644 --- a/www/src/Locales/en/SettingsPage.jsx +++ b/www/src/Locales/en/SettingsPage.jsx @@ -130,6 +130,13 @@ export default { 'save-config': 'Save Config', 'next-profile': 'Next Profile', 'previous-profile': 'Previous Profile', + 'menu-nav-up': 'Menu Up', + 'menu-nav-down': 'Menu Down', + 'menu-nav-left': 'Menu Left', + 'menu-nav-right': 'Menu Right', + 'menu-nav-select': 'Menu Select', + 'menu-nav-back': 'Menu Back', + 'menu-nav-toggle': 'Menu Toggle', }, 'forced-setup-mode-label': 'Forced Setup Mode', 'forced-setup-mode-options': { diff --git a/www/src/Pages/SettingsPage.jsx b/www/src/Pages/SettingsPage.jsx index ee15bc565..47d3486f0 100644 --- a/www/src/Pages/SettingsPage.jsx +++ b/www/src/Pages/SettingsPage.jsx @@ -287,6 +287,13 @@ const HOTKEY_ACTIONS = [ { labelKey: 'hotkey-actions.dpad-down', value: 39 }, { labelKey: 'hotkey-actions.dpad-left', value: 40 }, { labelKey: 'hotkey-actions.dpad-right', value: 41 }, + { labelKey: 'hotkey-actions.menu-nav-up', value: 44 }, + { labelKey: 'hotkey-actions.menu-nav-down', value: 45 }, + { labelKey: 'hotkey-actions.menu-nav-left', value: 46 }, + { labelKey: 'hotkey-actions.menu-nav-right', value: 47 }, + { labelKey: 'hotkey-actions.menu-nav-select', value: 48 }, + { labelKey: 'hotkey-actions.menu-nav-back', value: 49 }, + { labelKey: 'hotkey-actions.menu-nav-toggle', value: 50 }, ]; const FORCED_SETUP_MODES = [