diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b192867b..89ca2af4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -193,11 +193,13 @@ src/display/ui/screens/ButtonLayoutScreen.cpp src/display/ui/screens/ConfigScreen.cpp src/display/ui/screens/MainMenuScreen.cpp src/display/ui/screens/PinViewerScreen.cpp +src/display/ui/screens/RestartScreen.cpp src/display/ui/screens/StatsScreen.cpp src/display/ui/screens/SplashScreen.cpp src/display/GPGFX.cpp src/display/GPGFX_UI.cpp src/drivermanager.cpp +src/eventmanager.cpp src/layoutmanager.cpp src/peripheralmanager.cpp src/storagemanager.cpp @@ -271,6 +273,7 @@ headers headers/addons headers/configs headers/drivers +headers/events headers/interfaces headers/interfaces/i2c headers/interfaces/i2c/ads1219 diff --git a/headers/addons/display.h b/headers/addons/display.h index 7cbb19906..175b622bb 100644 --- a/headers/addons/display.h +++ b/headers/addons/display.h @@ -186,6 +186,8 @@ class DisplayAddon : public GPAddon virtual void preprocess() {} virtual void process(); virtual std::string name() { return DisplayName; } + + void handleSystemRestart(GPEvent* e); private: bool updateDisplayScreen(); void drawStatusBar(Gamepad*); @@ -209,6 +211,7 @@ class DisplayAddon : public GPAddon DisplayMode currDisplayMode; DisplayMode prevDisplayMode; bool turnOffWhenSuspended; + uint32_t bootMode; GPGFX_DisplayTypeOptions gpOptions; }; diff --git a/headers/addons/turbo.h b/headers/addons/turbo.h index b0dc258e6..447d452db 100644 --- a/headers/addons/turbo.h +++ b/headers/addons/turbo.h @@ -3,6 +3,7 @@ #include "gpaddon.h" #include "storagemanager.h" +#include "eventmanager.h" #include "enums.pb.h" #ifndef TURBO_ENABLED @@ -94,6 +95,8 @@ class TurboInput : public GPAddon { virtual void preprocess() {} virtual void process(); // TURBO Setting of buttons (Enable/Disable) virtual std::string name() { return TurboName; } + + void handleEncoder(GPEvent* e); private: void updateInterval(uint8_t shotCount); void updateTurboShotCount(uint8_t turboShotCount); @@ -120,5 +123,6 @@ class TurboInput : public GPAddon { uint16_t shmupBtnMask[4]; // Turbo SHMUP Non-Turbo Button Masks uint16_t lastButtons; // Last buttons (for Turbo Reset on Release) bool hasLedPin; // Flag for LED pin presence + uint8_t encoderValue; // Rotary encoder value }; #endif // TURBO_H_ diff --git a/headers/display/GPGFX_UI.h b/headers/display/GPGFX_UI.h index 74d7efb08..c7111776e 100644 --- a/headers/display/GPGFX_UI.h +++ b/headers/display/GPGFX_UI.h @@ -10,6 +10,7 @@ #include "config.pb.h" #include "GamepadState.h" #include "storagemanager.h" +#include "eventmanager.h" class GPGFX_UI { public: diff --git a/headers/display/GPGFX_UI_screens.h b/headers/display/GPGFX_UI_screens.h index 644367853..cb602d3e8 100644 --- a/headers/display/GPGFX_UI_screens.h +++ b/headers/display/GPGFX_UI_screens.h @@ -7,13 +7,15 @@ enum DisplayMode { SPLASH, PIN_VIEWER, STATS, - MAIN_MENU + MAIN_MENU, + RESTART }; #include "ui/screens/ButtonLayoutScreen.h" #include "ui/screens/ConfigScreen.h" #include "ui/screens/MainMenuScreen.h" #include "ui/screens/PinViewerScreen.h" +#include "ui/screens/RestartScreen.h" #include "ui/screens/SplashScreen.h" #include "ui/screens/StatsScreen.h" diff --git a/headers/display/ui/screens/ButtonLayoutScreen.h b/headers/display/ui/screens/ButtonLayoutScreen.h index ce1a42fc6..985da94da 100644 --- a/headers/display/ui/screens/ButtonLayoutScreen.h +++ b/headers/display/ui/screens/ButtonLayoutScreen.h @@ -102,6 +102,9 @@ class ButtonLayoutScreen : public GPScreen { virtual int8_t update(); virtual void init(); virtual void shutdown(); + + void handleProfileChange(GPEvent* e); + void handleUSB(GPEvent* e); protected: virtual void drawScreen(); private: @@ -144,12 +147,14 @@ class ButtonLayoutScreen : public GPScreen { std::deque inputHistory; std::array lastInput; - bool profileModeDisplay; - uint8_t profileDelay = 2; - int profileDelayStart = 0; + bool bannerDisplay; + uint8_t bannerDelay = 2; + int bannerDelayStart = 0; + std::string bannerMessage; uint16_t prevButtonState = 0; uint8_t prevLayoutLeft = 0; uint8_t prevLayoutRight = 0; + uint8_t profileNumber = 0; uint8_t prevProfileNumber = 0; ButtonLayoutParamsLeft prevLeftOptions; ButtonLayoutParamsRight prevRightOptions; diff --git a/headers/display/ui/screens/RestartScreen.h b/headers/display/ui/screens/RestartScreen.h new file mode 100644 index 000000000..71be33e03 --- /dev/null +++ b/headers/display/ui/screens/RestartScreen.h @@ -0,0 +1,21 @@ +#ifndef _RESTARTSCREEN_H_ +#define _RESTARTSCREEN_H_ + +#include "GPGFX_UI_widgets.h" +#include "bitmaps.h" + +class RestartScreen : public GPScreen { + public: + RestartScreen() {} + RestartScreen(GPGFX* renderer) { setRenderer(renderer); } + virtual int8_t update(); + virtual void init(); + virtual void shutdown(); + + void setBootMode(uint32_t mode); + protected: + virtual void drawScreen(); + uint32_t bootMode; +}; + +#endif \ No newline at end of file diff --git a/headers/eventmanager.h b/headers/eventmanager.h new file mode 100644 index 000000000..58cb10b3f --- /dev/null +++ b/headers/eventmanager.h @@ -0,0 +1,44 @@ +#ifndef _EVENTMANAGER_H_ +#define _EVENTMANAGER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include "config.pb.h" +#include "enums.pb.h" + +#include "GPEvent.h" +#include "GPGamepadEvent.h" +#include "GPEncoderEvent.h" +#include "GPProfileEvent.h" +#include "GPRestartEvent.h" +#include "GPUSBHostEvent.h" + +#define EVENTMGR EventManager::getInstance() + +class EventManager { + public: + typedef std::function EventFunction; + typedef std::pair> EventEntry; + + EventManager(EventManager const&) = delete; + void operator=(EventManager const&) = delete; + static EventManager& getInstance() // Thread-safe storage ensures cross-thread talk + { + static EventManager instance; + return instance; + } + + void registerEventHandler(GPEventType eventType, EventFunction handler); + void triggerEvent(GPEvent* event); + private: + EventManager(){} + + std::vector>> eventList; +}; + +#endif \ No newline at end of file diff --git a/headers/events/GPEncoderEvent.h b/headers/events/GPEncoderEvent.h new file mode 100644 index 000000000..fc57eaf1c --- /dev/null +++ b/headers/events/GPEncoderEvent.h @@ -0,0 +1,21 @@ +#ifndef _GPENCODEREVENT_H_ +#define _GPENCODEREVENT_H_ + +class GPEncoderChangeEvent : public GPEvent { + public: + GPEncoderChangeEvent() {} + GPEncoderChangeEvent(uint8_t id, int8_t dir) { + this->encoder = id; + this->direction = dir; + } + ~GPEncoderChangeEvent() {} + + uint8_t encoder = 0; + int8_t direction = 0; + + GPEventType eventType() { return this->_eventType; } + private: + GPEventType _eventType = GP_EVENT_ENCODER_CHANGE; +}; + +#endif \ No newline at end of file diff --git a/headers/events/GPEvent.h b/headers/events/GPEvent.h new file mode 100644 index 000000000..f9dd256e7 --- /dev/null +++ b/headers/events/GPEvent.h @@ -0,0 +1,16 @@ +#ifndef _GPEVENT_H_ +#define _GPEVENT_H_ + +#define GPEVENT_CALLBACK(x) ([this](GPEvent* event){x;}) + +class GPEvent { + public: + GPEvent() {} + ~GPEvent() {} + + virtual GPEventType eventType() { return this->_eventType; } + private: + GPEventType _eventType = GP_EVENT_BASE; +}; + +#endif \ No newline at end of file diff --git a/headers/events/GPGamepadEvent.h b/headers/events/GPGamepadEvent.h new file mode 100644 index 000000000..f4aafec90 --- /dev/null +++ b/headers/events/GPGamepadEvent.h @@ -0,0 +1,130 @@ +#ifndef _GPGAMEPADEVENT_H_ +#define _GPGAMEPADEVENT_H_ + +#include "gamepad.h" + +class GPGamepadEvent : public GPEvent { + public: + GPGamepadEvent() {} + GPGamepadEvent(GamepadState currState) { this->state = currState; } + GPGamepadEvent(uint8_t dpad, uint16_t buttons, uint16_t aux) { + this->state.dpad = dpad; + this->state.buttons = buttons; + this->state.aux = aux; + } + GPGamepadEvent(uint16_t lx, uint16_t ly, uint16_t rx, uint16_t ry, uint8_t lt, uint8_t rt) { + this->state.lx = lx; + this->state.ly = ly; + this->state.rx = rx; + this->state.ry = ry; + this->state.lt = lt; + this->state.rt = rt; + } + GPGamepadEvent(uint8_t dpad, uint16_t buttons, uint16_t aux, uint16_t lx, uint16_t ly, uint16_t rx, uint16_t ry, uint8_t lt, uint8_t rt) { + this->state.dpad = dpad; + this->state.buttons = buttons; + this->state.aux = aux; + this->state.lx = lx; + this->state.ly = ly; + this->state.rx = rx; + this->state.ry = ry; + this->state.lt = lt; + this->state.rt = rt; + } + ~GPGamepadEvent() {} + + GamepadState state; + private: +}; + +class GPButtonEvent : public GPGamepadEvent { + public: + GPButtonEvent() {} + GPButtonEvent(GamepadState currState) : GPGamepadEvent(currState) {} + GPButtonEvent(uint8_t dpad, uint16_t buttons, uint16_t aux) : GPGamepadEvent(dpad, buttons, aux) {} + ~GPButtonEvent() {} + private: +}; + +class GPButtonUpEvent : public GPButtonEvent { + public: + GPButtonUpEvent() {} + GPButtonUpEvent(GamepadState currState) : GPButtonEvent(currState) {} + GPButtonUpEvent(uint8_t dpad, uint16_t buttons, uint16_t aux) : GPButtonEvent(dpad, buttons, aux) {} + ~GPButtonUpEvent() {} + + GPEventType eventType() { return this->_eventType; } + private: + GPEventType _eventType = GP_EVENT_BUTTON_UP; +}; + +class GPButtonDownEvent : public GPButtonEvent { + public: + GPButtonDownEvent() {} + GPButtonDownEvent(GamepadState currState) : GPButtonEvent(currState) {} + GPButtonDownEvent(uint8_t dpad, uint16_t buttons, uint16_t aux) : GPButtonEvent(dpad, buttons, aux) {} + ~GPButtonDownEvent() {} + + GPEventType eventType() { return this->_eventType; } + private: + GPEventType _eventType = GP_EVENT_BUTTON_DOWN; +}; + +class GPButtonProcessedUpEvent : public GPButtonEvent { + public: + GPButtonProcessedUpEvent() {} + GPButtonProcessedUpEvent(GamepadState currState) : GPButtonEvent(currState) {} + GPButtonProcessedUpEvent(uint8_t dpad, uint16_t buttons, uint16_t aux) : GPButtonEvent(dpad, buttons, aux) {} + ~GPButtonProcessedUpEvent() {} + + GPEventType eventType() { return this->_eventType; } + private: + GPEventType _eventType = GP_EVENT_BUTTON_PROCESSED_UP; +}; + +class GPButtonProcessedDownEvent : public GPButtonEvent { + public: + GPButtonProcessedDownEvent() {} + GPButtonProcessedDownEvent(GamepadState currState) : GPButtonEvent(currState) {} + GPButtonProcessedDownEvent(uint8_t dpad, uint16_t buttons, uint16_t aux) : GPButtonEvent(dpad, buttons, aux) {} + ~GPButtonProcessedDownEvent() {} + + GPEventType eventType() { return this->_eventType; } + private: + GPEventType _eventType = GP_EVENT_BUTTON_PROCESSED_DOWN; +}; + +class GPAnalogEvent : public GPGamepadEvent { + public: + GPAnalogEvent() {} + GPAnalogEvent(GamepadState currState) : GPGamepadEvent(currState) {} + GPAnalogEvent(uint16_t lx, uint16_t ly, uint16_t rx, uint16_t ry, uint8_t lt, uint8_t rt) : GPGamepadEvent(lx, ly, rx, ry, lt, rt) {} + ~GPAnalogEvent() {} + private: +}; + +class GPAnalogMoveEvent : public GPAnalogEvent { + public: + GPAnalogMoveEvent() {} + GPAnalogMoveEvent(GamepadState currState) : GPAnalogEvent(currState) {} + GPAnalogMoveEvent(uint16_t lx, uint16_t ly, uint16_t rx, uint16_t ry, uint8_t lt, uint8_t rt) : GPAnalogEvent(lx, ly, rx, ry, lt, rt) {} + ~GPAnalogMoveEvent() {} + + GPEventType eventType() { return this->_eventType; } + private: + GPEventType _eventType = GP_EVENT_ANALOG_MOVE; +}; + +class GPAnalogProcessedMoveEvent : public GPAnalogEvent { + public: + GPAnalogProcessedMoveEvent() {} + GPAnalogProcessedMoveEvent(GamepadState currState) : GPAnalogEvent(currState) {} + GPAnalogProcessedMoveEvent(uint16_t lx, uint16_t ly, uint16_t rx, uint16_t ry, uint8_t lt, uint8_t rt) : GPAnalogEvent(lx, ly, rx, ry, lt, rt) {} + ~GPAnalogProcessedMoveEvent() {} + + GPEventType eventType() { return this->_eventType; } + private: + GPEventType _eventType = GP_EVENT_ANALOG_PROCESSED_MOVE; +}; + +#endif \ No newline at end of file diff --git a/headers/events/GPProfileEvent.h b/headers/events/GPProfileEvent.h new file mode 100644 index 000000000..327b422ee --- /dev/null +++ b/headers/events/GPProfileEvent.h @@ -0,0 +1,21 @@ +#ifndef _GPPROFILEEVENT_H_ +#define _GPPROFILEEVENT_H_ + +class GPProfileChangeEvent : public GPEvent { + public: + GPProfileChangeEvent() {} + GPProfileChangeEvent(uint8_t prev, uint8_t curr) { + this->previousValue = prev; + this->currentValue = curr; + } + ~GPProfileChangeEvent() {} + + GPEventType eventType() { return this->_eventType; } + + uint8_t previousValue; + uint8_t currentValue; + private: + GPEventType _eventType = GP_EVENT_PROFILE_CHANGE; +}; + +#endif \ No newline at end of file diff --git a/headers/events/GPRestartEvent.h b/headers/events/GPRestartEvent.h new file mode 100644 index 000000000..8b304de76 --- /dev/null +++ b/headers/events/GPRestartEvent.h @@ -0,0 +1,21 @@ +#ifndef _GPRESTARTEVENT_H_ +#define _GPRESTARTEVENT_H_ + +#include "system.h" + +class GPRestartEvent : public GPEvent { + public: + GPRestartEvent() {} + GPRestartEvent(System::BootMode mode) { + this->bootMode = mode; + } + ~GPRestartEvent() {} + + GPEventType eventType() { return this->_eventType; } + + System::BootMode bootMode; + private: + GPEventType _eventType = GP_EVENT_RESTART; +}; + +#endif \ No newline at end of file diff --git a/headers/events/GPUSBHostEvent.h b/headers/events/GPUSBHostEvent.h new file mode 100644 index 000000000..dd8ec2609 --- /dev/null +++ b/headers/events/GPUSBHostEvent.h @@ -0,0 +1,42 @@ +#ifndef _GPUSBHOSTEVENT_H_ +#define _GPUSBHOSTEVENT_H_ + +class GPUSBHostEvent : public GPEvent { + public: + GPUSBHostEvent() {} + GPUSBHostEvent(uint8_t devAddr, uint16_t vid, uint16_t pid) { + this->deviceAddress = devAddr; + this->vendorID = vid; + this->productID = pid; + } + ~GPUSBHostEvent() {} + + uint8_t deviceAddress = 0; + uint16_t vendorID = 0; + uint16_t productID = 0; + private: +}; + +class GPUSBHostMountEvent : public GPUSBHostEvent { + public: + GPUSBHostMountEvent() {} + GPUSBHostMountEvent(uint8_t devAddr, uint16_t vid, uint16_t pid) : GPUSBHostEvent(devAddr, vid, pid) {} + ~GPUSBHostMountEvent() {} + + GPEventType eventType() { return this->_eventType; } + private: + GPEventType _eventType = GP_EVENT_USBHOST_MOUNT; +}; + +class GPUSBHostUnmountEvent : public GPUSBHostEvent { + public: + GPUSBHostUnmountEvent() {} + GPUSBHostUnmountEvent(uint8_t devAddr, uint16_t vid, uint16_t pid) : GPUSBHostEvent(devAddr, vid, pid) {} + ~GPUSBHostUnmountEvent() {} + + GPEventType eventType() { return this->_eventType; } + private: + GPEventType _eventType = GP_EVENT_USBHOST_UNMOUNT; +}; + +#endif \ No newline at end of file diff --git a/headers/gp2040.h b/headers/gp2040.h index c252ef773..574f79057 100644 --- a/headers/gp2040.h +++ b/headers/gp2040.h @@ -69,6 +69,10 @@ class GP2040 { void initializeStandardGpio(); void deinitializeStandardGpio(); + // event handling checking + void checkRawState(GamepadState prevState, GamepadState currState); + void checkProcessedState(GamepadState prevState, GamepadState currState); + // input mask, action std::map bootActions; }; diff --git a/proto/enums.proto b/proto/enums.proto index 1473951e5..a6b815551 100644 --- a/proto/enums.proto +++ b/proto/enums.proto @@ -442,3 +442,21 @@ enum PS4ControllerIDMode PS4_ID_CONSOLE = 0; PS4_ID_EMULATION = 1; }; + +enum GPEventType +{ + option (nanopb_enumopt).long_names = false; + + GP_EVENT_BASE = 0; + GP_EVENT_RESTART = 1; + GP_EVENT_USBHOST_MOUNT = 2; + GP_EVENT_USBHOST_UNMOUNT = 3; + GP_EVENT_PROFILE_CHANGE = 4; + GP_EVENT_ENCODER_CHANGE = 5; + GP_EVENT_BUTTON_UP = 6; + GP_EVENT_BUTTON_DOWN = 7; + GP_EVENT_BUTTON_PROCESSED_UP = 8; + GP_EVENT_BUTTON_PROCESSED_DOWN = 9; + GP_EVENT_ANALOG_MOVE = 10; + GP_EVENT_ANALOG_PROCESSED_MOVE = 11; +}; \ No newline at end of file diff --git a/src/addons/display.cpp b/src/addons/display.cpp index 3d7b02b10..cce4c4504 100644 --- a/src/addons/display.cpp +++ b/src/addons/display.cpp @@ -66,6 +66,8 @@ void DisplayAddon::setup() { } gpScreen = nullptr; updateDisplayScreen(); + + EventManager::getInstance().registerEventHandler(GP_EVENT_RESTART, GPEVENT_CALLBACK(this->handleSystemRestart(event))); } bool DisplayAddon::updateDisplayScreen() { @@ -90,6 +92,9 @@ bool DisplayAddon::updateDisplayScreen() { case STATS: delete (StatsScreen*)gpScreen; break; + case RESTART: + delete (RestartScreen*)gpScreen; + break; default: break; } @@ -114,6 +119,10 @@ bool DisplayAddon::updateDisplayScreen() { case STATS: gpScreen = new StatsScreen(gpDisplay); break; + case RESTART: + gpScreen = new RestartScreen(gpDisplay); + ((RestartScreen*)gpScreen)->setBootMode(bootMode); + break; default: gpScreen = nullptr; break; @@ -185,3 +194,9 @@ const DisplayOptions& DisplayAddon::getDisplayOptions() { return configMode ? Storage::getInstance().getPreviewDisplayOptions() : Storage::getInstance().getDisplayOptions(); } + +void DisplayAddon::handleSystemRestart(GPEvent* e) { + currDisplayMode = DisplayMode::RESTART; + bootMode = (uint32_t)((GPRestartEvent*)e)->bootMode; + updateDisplayScreen(); +} \ No newline at end of file diff --git a/src/addons/rotaryencoder.cpp b/src/addons/rotaryencoder.cpp index 0e3b630b7..17fdc54a6 100644 --- a/src/addons/rotaryencoder.cpp +++ b/src/addons/rotaryencoder.cpp @@ -1,6 +1,8 @@ #include "addons/rotaryencoder.h" +#include "eventmanager.h" #include "storagemanager.h" +#include "GPEncoderEvent.h" #include "types.h" #include "GamepadEnums.h" @@ -126,6 +128,12 @@ void RotaryEncoderInput::process() if ((encoderValues[i] - prevValues[i]) != 0) { encoderState[i].changeTime = now; + + if ((encoderValues[i] - prevValues[i]) > 0) { + EventManager::getInstance().triggerEvent(new GPEncoderChangeEvent(i, 1)); + } else if ((encoderValues[i] - prevValues[i]) < 0) { + EventManager::getInstance().triggerEvent(new GPEncoderChangeEvent(i, -1)); + } } if ((encoderMap[i].resetAfter > 0) && (lastChange >= encoderMap[i].resetAfter)) { diff --git a/src/addons/turbo.cpp b/src/addons/turbo.cpp index 3869bb3c1..d0a0832d8 100644 --- a/src/addons/turbo.cpp +++ b/src/addons/turbo.cpp @@ -42,6 +42,7 @@ void TurboInput::setup() const TurboOptions& options = Storage::getInstance().getAddonOptions().turboOptions; uint32_t now = getMillis(); + // Turbo Dial uint8_t shotCount = std::clamp(options.shotCount, TURBO_SHOT_MIN, TURBO_SHOT_MAX); if (isValidPin(options.shmupDialPin)) { @@ -103,6 +104,7 @@ void TurboInput::setup() bTurboFlicker = false; updateInterval(shotCount); nextTimer = getMicro(); + encoderValue = shotCount; } /** diff --git a/src/configs/webconfig.cpp b/src/configs/webconfig.cpp index 78291030d..0b35ae563 100644 --- a/src/configs/webconfig.cpp +++ b/src/configs/webconfig.cpp @@ -4,6 +4,7 @@ #include "storagemanager.h" #include "configmanager.h" +#include "eventmanager.h" #include "layoutmanager.h" #include "peripheralmanager.h" #include "AnimationStorage.hpp" @@ -646,6 +647,7 @@ std::string setGamepadOptions() DynamicJsonDocument doc = get_post_data(); GamepadOptions& gamepadOptions = Storage::getInstance().getGamepadOptions(); + readDoc(gamepadOptions.dpadMode, doc, "dpadMode"); readDoc(gamepadOptions.inputMode, doc, "inputMode"); readDoc(gamepadOptions.socdMode, doc, "socdMode"); @@ -667,6 +669,7 @@ std::string setGamepadOptions() readDoc(gamepadOptions.xinputAuthType, doc, "xinputAuthType"); readDoc(gamepadOptions.ps4ControllerIDMode, doc, "ps4ControllerIDMode"); + HotkeyOptions& hotkeyOptions = Storage::getInstance().getHotkeyOptions(); save_hotkey(&hotkeyOptions.hotkey01, doc, "hotkey01"); save_hotkey(&hotkeyOptions.hotkey02, doc, "hotkey02"); @@ -2288,6 +2291,7 @@ std::string reboot() default: rebootMode = System::BootMode::DEFAULT; } + EventManager::getInstance().triggerEvent(new GPRestartEvent(rebootMode)); return serialize_json(doc); } diff --git a/src/display/ui/screens/ButtonLayoutScreen.cpp b/src/display/ui/screens/ButtonLayoutScreen.cpp index 0a15acbf8..63774c52f 100644 --- a/src/display/ui/screens/ButtonLayoutScreen.cpp +++ b/src/display/ui/screens/ButtonLayoutScreen.cpp @@ -11,9 +11,13 @@ void ButtonLayoutScreen::init() { inputHistoryX = inputHistoryOptions.row; inputHistoryY = inputHistoryOptions.col; inputHistoryLength = inputHistoryOptions.length; - profileDelayStart = getMillis(); + bannerDelayStart = getMillis(); gamepad = Storage::getInstance().GetGamepad(); inputMode = DriverManager::getInstance().getInputMode(); + + EventManager::getInstance().registerEventHandler(GP_EVENT_PROFILE_CHANGE, GPEVENT_CALLBACK(this->handleProfileChange(event))); + EventManager::getInstance().registerEventHandler(GP_EVENT_USBHOST_MOUNT, GPEVENT_CALLBACK(this->handleUSB(event))); + EventManager::getInstance().registerEventHandler(GP_EVENT_USBHOST_UNMOUNT, GPEVENT_CALLBACK(this->handleUSB(event))); footer = ""; historyString = ""; @@ -33,8 +37,9 @@ void ButtonLayoutScreen::init() { } // start with profile mode displayed - profileModeDisplay = true; + bannerDisplay = true; prevProfileNumber = -1; + prevLayoutLeft = Storage::getInstance().getDisplayOptions().buttonLayout; prevLayoutRight = Storage::getInstance().getDisplayOptions().buttonLayoutRight; prevLeftOptions = Storage::getInstance().getDisplayOptions().buttonLayoutCustomOptions.paramsLeft; @@ -87,9 +92,9 @@ int8_t ButtonLayoutScreen::update() { // main logic loop if (prevProfileNumber != profileNumber) { - profileDelayStart = getMillis(); + bannerDelayStart = getMillis(); prevProfileNumber = profileNumber; - profileModeDisplay = true; + bannerDisplay = true; } // main logic loop @@ -118,18 +123,23 @@ void ButtonLayoutScreen::generateHeader() { Storage& storage = Storage::getInstance(); // Display Profile # banner - if ( profileModeDisplay ) { - if (((getMillis() - profileDelayStart) / 1000) < profileDelay) { - statusBar.assign(storage.currentProfileLabel(), strlen(storage.currentProfileLabel())); - if (statusBar.empty()) { - statusBar = " Profile #"; - statusBar += std::to_string(getGamepad()->getOptions().profileNumber); + if ( bannerDisplay ) { + if (((getMillis() - bannerDelayStart) / 1000) < bannerDelay) { + if (bannerMessage.empty()) { + statusBar.assign(storage.currentProfileLabel(), strlen(storage.currentProfileLabel())); + if (statusBar.empty()) { + statusBar = " Profile #"; + statusBar += std::to_string(getGamepad()->getOptions().profileNumber); + } else { + statusBar.insert(statusBar.begin(), (21-statusBar.length())/2, ' '); + } } else { - statusBar.insert(statusBar.begin(), (21-statusBar.length())/2, ' '); + statusBar = bannerMessage; } return; } else { - profileModeDisplay = false; + bannerDisplay = false; + bannerMessage.clear(); } } @@ -210,7 +220,7 @@ void ButtonLayoutScreen::generateHeader() { } void ButtonLayoutScreen::drawScreen() { - if (profileModeDisplay) { + if (bannerDisplay) { getRenderer()->drawRectangle(0, 0, 128, 7, true, true); getRenderer()->drawText(0, 0, statusBar, true); } else { @@ -484,3 +494,23 @@ bool ButtonLayoutScreen::pressedDownRight() return false; } + +void ButtonLayoutScreen::handleProfileChange(GPEvent* e) { + GPProfileChangeEvent* event = (GPProfileChangeEvent*)e; + + profileNumber = event->currentValue; + prevProfileNumber = event->previousValue; +} + +void ButtonLayoutScreen::handleUSB(GPEvent* e) { + GPUSBHostEvent* event = (GPUSBHostEvent*)e; + bannerDelayStart = getMillis(); + prevProfileNumber = profileNumber; + + if (e->eventType() == GP_EVENT_USBHOST_MOUNT) { + bannerMessage = " USB Connected"; + } else if (e->eventType() == GP_EVENT_USBHOST_UNMOUNT) { + bannerMessage = " USB Disconnnected"; + } + bannerDisplay = true; +} diff --git a/src/display/ui/screens/RestartScreen.cpp b/src/display/ui/screens/RestartScreen.cpp new file mode 100644 index 000000000..63dd07b21 --- /dev/null +++ b/src/display/ui/screens/RestartScreen.cpp @@ -0,0 +1,42 @@ +#include "RestartScreen.h" + +#include "pico/stdlib.h" +#include "system.h" + +void RestartScreen::init() { + getRenderer()->clearScreen(); + //splashStartTime = getMillis(); + //configMode = Storage::getInstance().GetConfigMode(); +} + +void RestartScreen::shutdown() { + clearElements(); +} + +void RestartScreen::drawScreen() { + getRenderer()->drawSprite((uint8_t *)bootLogoBottom, 128, 35, 10, 0, 2, 1); + + switch ((System::BootMode)this->bootMode) { + case System::BootMode::USB: + getRenderer()->drawText(1, 6, "Rebooting to BOOTSEL"); + getRenderer()->drawText(2, 7, "and Mounting Drive"); + break; + case System::BootMode::WEBCONFIG: + getRenderer()->drawText(2, 6, "Booting WebConfig"); + getRenderer()->drawText(4, 7, "Please Wait"); + break; + case System::BootMode::GAMEPAD: + case System::BootMode::DEFAULT: + getRenderer()->drawText(4, 6, "Gamepad Mode"); + getRenderer()->drawText(4, 7, "Please Wait"); + break; + } +} + +void RestartScreen::setBootMode(uint32_t mode) { + this->bootMode = mode; +} + +int8_t RestartScreen::update() { + return -1; // -1 means no change in screen state +} diff --git a/src/eventmanager.cpp b/src/eventmanager.cpp new file mode 100644 index 000000000..cf80b2eab --- /dev/null +++ b/src/eventmanager.cpp @@ -0,0 +1,29 @@ +#include "eventmanager.h" +#include "storagemanager.h" +#include "enums.pb.h" + +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; }); + + if (it != eventList.end()) { + // If the event already exists, add the handler to its vector + it->second.push_back(handler); + } else { + // If the event does not exist, create a new entry with the handler + eventList.emplace_back(eventType, std::vector{handler}); + } +} + +void EventManager::triggerEvent(GPEvent* event) { + GPEventType eventType = event->eventType(); + for (typename std::vector::const_iterator it = eventList.begin(); it != eventList.end(); ++it) { + if (it->first == eventType) { + // Call all event handlers for the specified event + const std::vector& handlers = it->second; + for (typename std::vector::const_iterator handler = handlers.begin(); handler != handlers.end(); ++handler) { + (*handler)(event); + } + } + } + delete event; +} \ No newline at end of file diff --git a/src/gp2040.cpp b/src/gp2040.cpp index 13c09a35f..28f8b01d0 100644 --- a/src/gp2040.cpp +++ b/src/gp2040.cpp @@ -263,6 +263,7 @@ void GP2040::run() { Gamepad * gamepad = Storage::getInstance().GetGamepad(); Gamepad * processedGamepad = Storage::getInstance().GetProcessedGamepad(); bool configMode = Storage::getInstance().GetConfigMode(); + GamepadState prevState; // Start the TinyUSB Device functionality tud_init(TUD_OPT_RHPORT); @@ -270,6 +271,8 @@ void GP2040::run() { while (1) { // LOOP this->getReinitGamepad(gamepad); + memcpy(&prevState, &gamepad->state, sizeof(GamepadState)); + // Do any queued saves in StorageManager Storage::getInstance().performEnqueuedSaves(); @@ -278,6 +281,8 @@ void GP2040::run() { // Read Gamepad gamepad->read(); + checkRawState(prevState, gamepad->state); + // Config Loop (Web-Config does not require gamepad) if (configMode == true) { @@ -300,6 +305,8 @@ void GP2040::run() { // (Post) Process for add-ons addons.ProcessAddons(ADDON_PROCESS::CORE0_INPUT); + checkProcessedState(processedGamepad->state, gamepad->state); + // Copy Processed Gamepad for Core1 (race condition otherwise) memcpy(&processedGamepad->state, &gamepad->state, sizeof(GamepadState)); @@ -468,3 +475,54 @@ void GP2040::RebootHotkeys::process(Gamepad* gamepad, bool configMode) { } } } + +void GP2040::checkRawState(GamepadState prevState, GamepadState currState) { + // buttons pressed + if ( + ((currState.aux & ~prevState.aux) != 0) || + ((currState.dpad & ~prevState.dpad) != 0) || + ((currState.buttons & ~prevState.buttons) != 0) + ) { + EventManager::getInstance().triggerEvent(new GPButtonDownEvent((currState.dpad & ~prevState.dpad), (currState.buttons & ~prevState.buttons), (currState.aux & ~prevState.aux))); + } + + // buttons released + if ( + ((prevState.aux & ~currState.aux) != 0) || + ((prevState.dpad & ~currState.dpad) != 0) || + ((prevState.buttons & ~currState.buttons) != 0) + ) { + EventManager::getInstance().triggerEvent(new GPButtonUpEvent((prevState.dpad & ~currState.dpad), (prevState.buttons & ~currState.buttons), (prevState.aux & ~currState.aux))); + } +} + +void GP2040::checkProcessedState(GamepadState prevState, GamepadState currState) { + // buttons pressed + if ( + ((currState.aux & ~prevState.aux) != 0) || + ((currState.dpad & ~prevState.dpad) != 0) || + ((currState.buttons & ~prevState.buttons) != 0) + ) { + EventManager::getInstance().triggerEvent(new GPButtonProcessedDownEvent((currState.dpad & ~prevState.dpad), (currState.buttons & ~prevState.buttons), (currState.aux & ~prevState.aux))); + } + + // buttons released + if ( + ((prevState.aux & ~currState.aux) != 0) || + ((prevState.dpad & ~currState.dpad) != 0) || + ((prevState.buttons & ~currState.buttons) != 0) + ) { + EventManager::getInstance().triggerEvent(new GPButtonProcessedUpEvent((prevState.dpad & ~currState.dpad), (prevState.buttons & ~currState.buttons), (prevState.aux & ~currState.aux))); + } + + if ( + (currState.lx != prevState.lx) || + (currState.ly != prevState.ly) || + (currState.rx != prevState.rx) || + (currState.ry != prevState.ry) || + (currState.lt != prevState.lt) || + (currState.rt != prevState.rt) + ) { + EventManager::getInstance().triggerEvent(new GPAnalogProcessedMoveEvent(currState.lx, currState.ly, currState.rx, currState.ry, currState.lt, currState.rt)); + } +} diff --git a/src/storagemanager.cpp b/src/storagemanager.cpp index 68d7de338..91d1f6c0c 100644 --- a/src/storagemanager.cpp +++ b/src/storagemanager.cpp @@ -8,6 +8,7 @@ #include "BoardConfig.h" #include "AnimationStorage.hpp" #include "FlashPROM.h" +#include "eventmanager.h" #include "peripheralmanager.h" #include "config.pb.h" #include "hardware/watchdog.h" @@ -130,6 +131,7 @@ bool Storage::setProfile(const uint32_t profileNum) // is this profile enabled? // profile 1 (core) is always enabled, others we must check if (profileNum == 1 || config.profileOptions.gpioMappingsSets[profileNum-2].enabled) { + EventManager::getInstance().triggerEvent(new GPProfileChangeEvent(this->config.gamepadOptions.profileNumber, profileNum)); this->config.gamepadOptions.profileNumber = profileNum; return true; } diff --git a/src/usbhostmanager.cpp b/src/usbhostmanager.cpp index 7eaf41649..dc45d0cc3 100644 --- a/src/usbhostmanager.cpp +++ b/src/usbhostmanager.cpp @@ -1,6 +1,7 @@ #include "usbhostmanager.h" #include "storagemanager.h" #include "peripheralmanager.h" +#include "eventmanager.h" #include "pio_usb.h" #include "tusb.h" @@ -112,6 +113,24 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_re } } +void tuh_mount_cb(uint8_t dev_addr) { + uint16_t vid, pid; + if (!tuh_vid_pid_get(dev_addr, &vid, &pid)) { + vid = 0xFFFF; + pid = 0xFFFF; + } + EventManager::getInstance().triggerEvent(new GPUSBHostMountEvent(dev_addr, vid, pid)); +} + +void tuh_umount_cb(uint8_t dev_addr) { + uint16_t vid, pid; + if (!tuh_vid_pid_get(dev_addr, &vid, &pid)) { + vid = 0xFFFF; + pid = 0xFFFF; + } + EventManager::getInstance().triggerEvent(new GPUSBHostUnmountEvent(dev_addr, vid, pid)); +} + /// Invoked when device is unmounted (bus reset/unplugged) void tuh_hid_umount_cb(uint8_t daddr, uint8_t instance) {