diff --git a/headers/addons/display.h b/headers/addons/display.h index cc11e8f8e..23002cecf 100644 --- a/headers/addons/display.h +++ b/headers/addons/display.h @@ -187,6 +187,7 @@ class DisplayAddon : public GPAddon virtual void process(); virtual std::string name() { return DisplayName; } private: + bool updateDisplayScreen(); void drawStatusBar(Gamepad*); void initMenu(char**); bool pressedUp(); @@ -202,14 +203,9 @@ class DisplayAddon : public GPAddon uint32_t prevMillis; std::string statusBar; Gamepad* gamepad; - Gamepad* pGamepad; bool configMode; - GPGFX* gpDisplay; GPScreen* gpScreen; - - std::map loadedScreens; - DisplayMode currDisplayMode; bool turnOffWhenSuspended; }; diff --git a/headers/addons/input_macro.h b/headers/addons/input_macro.h index ac1ceb387..95119aa1f 100644 --- a/headers/addons/input_macro.h +++ b/headers/addons/input_macro.h @@ -38,23 +38,25 @@ class InputMacro : public GPAddon { virtual void setup(); // Analog Setup virtual void process() {}; // Analog Process virtual void preprocess(); + virtual void reinit(); virtual std::string name() { return InputMacroName; } private: - int macroPosition = -1; - bool isMacroRunning = false; - bool isMacroTriggerHeld = false; - - uint64_t macroStartTime = 0; - uint64_t macroTriggerDebounceStartTime = 0; - - int macroInputPosition = 0; - bool macroInputPressed = false; - uint32_t macroInputHoldTime = INPUT_HOLD_US; - bool prevMacroInputPressed = false; - bool boardLedEnabled = false; - - MacroOptions inputMacroOptions; + void checkMacroPress(); + void checkMacroAction(); + void runCurrentMacro(); void reset(); + bool isMacroRunning; + bool isMacroTriggerHeld; + int macroPosition; + uint32_t macroButtonMask; + uint32_t macroPinMasks[6]; + uint64_t macroStartTime; + int pressedMacro; + int macroInputPosition; + uint32_t macroInputHoldTime; + bool prevMacroInputPressed; + bool boardLedEnabled; + MacroOptions * inputMacroOptions; }; #endif // _InputMacro_H_ \ No newline at end of file diff --git a/headers/display/GPGFX_UI.h b/headers/display/GPGFX_UI.h index 99ba4d1ae..f97f2517f 100644 --- a/headers/display/GPGFX_UI.h +++ b/headers/display/GPGFX_UI.h @@ -14,17 +14,11 @@ class GPGFX_UI { public: GPGFX_UI(); GPGFX_UI(GPGFX* renderer) { setRenderer(renderer); } - void setRenderer(GPGFX* renderer) { _renderer = renderer; } GPGFX* getRenderer() { return _renderer; } - Gamepad* getGamepad(); Gamepad* getProcessedGamepad(); - DisplayOptions getDisplayOptions(); - - bool getConfigMode(); - uint16_t map(uint16_t x, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max); private: GPGFX* _renderer; diff --git a/headers/display/ui/elements/GPButton.h b/headers/display/ui/elements/GPButton.h index ad5e2b317..901f7a99c 100644 --- a/headers/display/ui/elements/GPButton.h +++ b/headers/display/ui/elements/GPButton.h @@ -9,8 +9,7 @@ class GPButton : public GPWidget { public: void draw(); - GPButton* setSizeX(uint16_t sizeX) { this->_sizeX = sizeX; return this; } - GPButton* setSizeY(uint16_t sizeY) { this->_sizeY = sizeY; return this; } + GPButton* setSize(uint16_t sizeX, uint16_t sizeY) { this->_sizeX = sizeX; this->_sizeY = sizeY; return this; } GPButton* setInputMask(int16_t inputMask) { this->_inputMask = inputMask; return this; } GPButton* setInputDirection(bool inputDirection) { this->_inputDirection = inputDirection; return this; } GPButton* setInputType(GPElement inputType) { this->_inputType = inputType; return this; } diff --git a/headers/display/ui/elements/GPScreen.h b/headers/display/ui/elements/GPScreen.h index b43e56fe3..b2e122791 100644 --- a/headers/display/ui/elements/GPScreen.h +++ b/headers/display/ui/elements/GPScreen.h @@ -3,30 +3,23 @@ #include #include -#include #include "GPWidget.h" class GPScreen : public GPWidget { public: void draw(); - std::string header = ""; - std::string footer = ""; - virtual int8_t update(); + virtual int8_t update() = 0; void clear(); - - template - _GPWidget* addElement(_GPArgs&&... args) { - using GPW = std::unique_ptr<_GPWidget>; - GPW new_element = std::make_unique<_GPWidget>(std::forward<_GPArgs>(args)...); - GPWidget* raw_ptr = new_element.get(); - displayList.emplace_back(std::move(new_element)); - return static_cast<_GPWidget*>(raw_ptr); - } + virtual void init() = 0; protected: virtual void drawScreen() = 0; - + GPWidget* addElement(GPWidget* element) { + displayList.push_back(element); + element->setID(displayList.size()-1); + return element; + } private: - std::vector> displayList; + std::vector displayList; }; #endif \ No newline at end of file diff --git a/headers/display/ui/elements/GPShape.h b/headers/display/ui/elements/GPShape.h index 7967620e3..81d8ce6c6 100644 --- a/headers/display/ui/elements/GPShape.h +++ b/headers/display/ui/elements/GPShape.h @@ -7,8 +7,7 @@ class GPShape : public GPWidget { public: void draw(); - GPShape* setSizeX(uint16_t sizeX) { this->_sizeX = sizeX; return this; } - GPShape* setSizeY(uint16_t sizeY) { this->_sizeY = sizeY; return this; } + GPShape* setSize(uint16_t sizeX, uint16_t sizeY) { this->_sizeX = sizeX; this->_sizeY = sizeY; return this; } GPShape* setAngle(double angle) { this->_angle = angle; return this; } GPShape* setAngleEnd(double angleEnd) { this->_angleEnd = angleEnd; return this; } GPShape* setClosed(bool closed) { this->_closed = closed; return this; } diff --git a/headers/display/ui/elements/GPSprite.h b/headers/display/ui/elements/GPSprite.h index e8ba2a4eb..1b6930e2f 100644 --- a/headers/display/ui/elements/GPSprite.h +++ b/headers/display/ui/elements/GPSprite.h @@ -6,8 +6,7 @@ class GPSprite : public GPWidget { public: void draw(); - GPSprite* setSizeX(uint16_t sizeX) { this->_sizeX = sizeX; return this; } - GPSprite* setSizeY(uint16_t sizeY) { this->_sizeY = sizeY; return this; } + GPSprite* setSize(uint16_t sizeX, uint16_t sizeY) { this->_sizeX = sizeX; this->_sizeY = sizeY; return this; } private: uint16_t _sizeX = 0; uint16_t _sizeY = 0; diff --git a/headers/display/ui/screens/ButtonLayoutScreen.h b/headers/display/ui/screens/ButtonLayoutScreen.h index f13107bd9..45e4c7043 100644 --- a/headers/display/ui/screens/ButtonLayoutScreen.h +++ b/headers/display/ui/screens/ButtonLayoutScreen.h @@ -40,20 +40,103 @@ #define INPUT_HISTORY_MAX_INPUTS 22 #define INPUT_HISTORY_MAX_MODES 11 +// Static to ensure memory is never doubled +static const char * displayNames[INPUT_HISTORY_MAX_MODES][INPUT_HISTORY_MAX_INPUTS] = { + { // HID / DINPUT + CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, + CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, + CHAR_CROSS, CHAR_CIRCLE, CHAR_SQUARE, CHAR_TRIANGLE, + "L1", "R1", "L2", "R2", + "SL", "ST", "L3", "R3", "PS", "A2" + }, + { // Switch + CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, + CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, + "B", "A", "Y", "X", + "L", "R", "ZL", "ZR", + "-", "+", "LS", "RS", CHAR_HOME_S, CHAR_CAP_S + }, + { // XInput + CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, + CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, + "A", "B", "X", "Y", + "LB", "RB", "LT", "RT", + CHAR_VIEW_X, CHAR_MENU_X, "LS", "RS", CHAR_HOME_X, "A2" + }, + { // Keyboard / HID-KB + CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, + CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, + "B1", "B2", "B3", "B4", + "L1", "R1", "L2", "R2", + "S1", "S2", "L3", "R3", "A1", "A2" + }, + { // PS4 + CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, + CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, + CHAR_CROSS, CHAR_CIRCLE, CHAR_SQUARE, CHAR_TRIANGLE, + "L1", "R1", "L2", "R2", + CHAR_SHARE_P, "OP", "L3", "R3", CHAR_HOME_P, CHAR_TPAD_P + }, + { // GEN/MD Mini + CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, + CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, + "A", "B", "X", "Y", + "", "Z", "", "C", + "M", "S", "", "", "", "" + }, + { // Neo Geo Mini + CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, + CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, + "B", "D", "A", "C", + "", "", "", "", + "SE", "ST", "", "", "", "" + }, + { // PC Engine/TG16 Mini + CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, + CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, + "I", "II", "", "", + "", "", "", "", + "SE", "RUN", "", "", "", "" + }, + { // Egret II Mini + CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, + CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, + "A", "B", "C", "D", + "", "E", "", "F", + "CRD", "ST", "", "", "MN", "" + }, + { // Astro City Mini + CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, + CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, + "A", "B", "D", "E", + "", "C", "", "F", + "CRD", "ST", "", "", "", "" + }, + { // Original Xbox + CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, + CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, + "A", "B", "X", "Y", + "BL", "WH", "L", "R", + "BK", "ST", "LS", "RS", "", "" + } +}; + class ButtonLayoutScreen : public GPScreen { public: ButtonLayoutScreen() {} ButtonLayoutScreen(GPGFX* renderer) { setRenderer(renderer); } - int8_t update(); + virtual int8_t update(); + virtual void init(); protected: - void drawScreen(); + virtual void drawScreen(); private: // new layout methods - GPLever* drawLever(uint16_t startX, uint16_t startY, uint16_t sizeX, uint16_t sizeY, uint16_t strokeColor, uint16_t fillColor, uint16_t inputType); - GPButton* drawButton(uint16_t startX, uint16_t startY, uint16_t sizeX, uint16_t sizeY, uint16_t strokeColor, uint16_t fillColor, int16_t inputMask = -1); - GPSprite* drawSprite(uint16_t startX, uint16_t startY, uint16_t sizeX, uint16_t sizeY); - GPShape* drawShape(uint16_t startX, uint16_t startY, uint16_t sizeX, uint16_t sizeY, uint16_t strokeColor, uint16_t fillColor); - GPWidget* drawElement(GPButtonLayout element); + GPLever* addLever(uint16_t startX, uint16_t startY, uint16_t sizeX, uint16_t sizeY, uint16_t strokeColor, uint16_t fillColor, uint16_t inputType); + GPButton* addButton(uint16_t startX, uint16_t startY, uint16_t sizeX, uint16_t sizeY, uint16_t strokeColor, uint16_t fillColor, int16_t inputMask = -1); + GPSprite* addSprite(uint16_t startX, uint16_t startY, uint16_t sizeX, uint16_t sizeY); + GPShape* addShape(uint16_t startX, uint16_t startY, uint16_t sizeX, uint16_t sizeY, uint16_t strokeColor, uint16_t fillColor); + GPWidget* pushElement(GPButtonLayout element); + void generateHeader(); const std::map displayModeLookup = { {INPUT_MODE_HID, 0}, @@ -72,26 +155,28 @@ class ButtonLayoutScreen : public GPScreen { {INPUT_MODE_XBOXORIGINAL, 10}, }; + Gamepad* gamepad; + InputMode inputMode; + std::string statusBar; + std::string footer; + bool isInputHistoryEnabled = false; uint16_t inputHistoryX = 0; uint16_t inputHistoryY = 0; - int16_t inputHistoryLength = 0; + size_t inputHistoryLength = 0; std::string historyString; std::deque inputHistory; std::array lastInput; + bool profileModeDisplay; uint8_t profileDelay = 2; int profileDelayStart = 0; - bool displayProfileBanner = true; - - bool hasInitialized = false; - uint16_t prevButtonState = 0; uint8_t prevLayoutLeft = 0; uint8_t prevLayoutRight = 0; uint8_t prevProfileNumber = 0; - void showProfileBanner(); + bool macroEnabled; uint16_t map(uint16_t x, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max); void processInputHistory(); @@ -103,86 +188,6 @@ class ButtonLayoutScreen : public GPScreen { bool pressedUpRight(); bool pressedDownLeft(); bool pressedDownRight(); - - const std::string displayNames[INPUT_HISTORY_MAX_MODES][INPUT_HISTORY_MAX_INPUTS] = { - { // HID / DINPUT - CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, - CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, - CHAR_CROSS, CHAR_CIRCLE, CHAR_SQUARE, CHAR_TRIANGLE, - "L1", "R1", "L2", "R2", - "SL", "ST", "L3", "R3", "PS", "A2" - }, - { // Switch - CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, - CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, - "B", "A", "Y", "X", - "L", "R", "ZL", "ZR", - "-", "+", "LS", "RS", CHAR_HOME_S, CHAR_CAP_S - }, - { // XInput - CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, - CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, - "A", "B", "X", "Y", - "LB", "RB", "LT", "RT", - CHAR_VIEW_X, CHAR_MENU_X, "LS", "RS", CHAR_HOME_X, "A2" - }, - { // Keyboard / HID-KB - CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, - CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, - "B1", "B2", "B3", "B4", - "L1", "R1", "L2", "R2", - "S1", "S2", "L3", "R3", "A1", "A2" - }, - { // PS4 - CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, - CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, - CHAR_CROSS, CHAR_CIRCLE, CHAR_SQUARE, CHAR_TRIANGLE, - "L1", "R1", "L2", "R2", - CHAR_SHARE_P, "OP", "L3", "R3", CHAR_HOME_P, CHAR_TPAD_P - }, - { // GEN/MD Mini - CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, - CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, - "A", "B", "X", "Y", - "", "Z", "", "C", - "M", "S", "", "", "", "" - }, - { // Neo Geo Mini - CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, - CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, - "B", "D", "A", "C", - "", "", "", "", - "SE", "ST", "", "", "", "" - }, - { // PC Engine/TG16 Mini - CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, - CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, - "I", "II", "", "", - "", "", "", "", - "SE", "RUN", "", "", "", "" - }, - { // Egret II Mini - CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, - CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, - "A", "B", "C", "D", - "", "E", "", "F", - "CRD", "ST", "", "", "MN", "" - }, - { // Astro City Mini - CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, - CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, - "A", "B", "D", "E", - "", "C", "", "F", - "CRD", "ST", "", "", "", "" - }, - { // Original Xbox - CHAR_UP, CHAR_DOWN, CHAR_LEFT, CHAR_RIGHT, - CHAR_UL, CHAR_UR, CHAR_DL, CHAR_DR, - "A", "B", "X", "Y", - "BL", "WH", "L", "R", - "BK", "ST", "LS", "RS", "", "" - } - }; }; #endif \ No newline at end of file diff --git a/headers/display/ui/screens/ConfigScreen.h b/headers/display/ui/screens/ConfigScreen.h index c66667517..c43f26c17 100644 --- a/headers/display/ui/screens/ConfigScreen.h +++ b/headers/display/ui/screens/ConfigScreen.h @@ -7,15 +7,15 @@ class ConfigScreen : public GPScreen { public: ConfigScreen() {} ConfigScreen(GPGFX* renderer) { setRenderer(renderer); } - - int8_t update(); + virtual int8_t update(); + virtual void init(); uint8_t getDisplayMode() { return prevDisplayMode; } protected: - void drawScreen(); - + virtual void drawScreen(); uint16_t prevButtonState = 0; int8_t prevDisplayMode = 0; int8_t currDisplayMode = 0; + std::string version; }; #endif \ No newline at end of file diff --git a/headers/display/ui/screens/MainMenuScreen.h b/headers/display/ui/screens/MainMenuScreen.h index d4a109b15..efc757f8f 100644 --- a/headers/display/ui/screens/MainMenuScreen.h +++ b/headers/display/ui/screens/MainMenuScreen.h @@ -9,14 +9,14 @@ class MainMenuScreen : public GPScreen { MainMenuScreen() {} MainMenuScreen(GPGFX* renderer) { setRenderer(renderer); } void setMenu(std::vector* menu); - int8_t update(); + virtual int8_t update(); + virtual void init(); protected: - void drawScreen(); + virtual void drawScreen(); private: uint8_t menuIndex = 0; bool isPressed = false; uint32_t checkDebounce; - std::vector* currentMenu; uint16_t prevButtonState = 0; }; diff --git a/headers/display/ui/screens/SplashScreen.h b/headers/display/ui/screens/SplashScreen.h index abc4a9410..c74ae99b3 100644 --- a/headers/display/ui/screens/SplashScreen.h +++ b/headers/display/ui/screens/SplashScreen.h @@ -8,11 +8,13 @@ class SplashScreen : public GPScreen { public: SplashScreen() {} SplashScreen(GPGFX* renderer) { setRenderer(renderer); } - int8_t update(); + virtual int8_t update(); + virtual void init(); protected: - void drawScreen(); - + virtual void drawScreen(); uint16_t prevButtonState = 0; + uint32_t splashStartTime = 0; + bool configMode; }; #endif \ No newline at end of file diff --git a/headers/drivermanager.h b/headers/drivermanager.h index 9ecbccf87..ec3d5c806 100644 --- a/headers/drivermanager.h +++ b/headers/drivermanager.h @@ -8,17 +8,19 @@ class GPDriver; class DriverManager { public: - DriverManager(DriverManager const&) = delete; - void operator=(DriverManager const&) = delete; + DriverManager(DriverManager const&) = delete; + void operator=(DriverManager const&) = delete; static DriverManager& getInstance() {// Thread-safe storage ensures cross-thread talk - static DriverManager instance; // Guaranteed to be destroyed. // Instantiated on first use. - return instance; - } - GPDriver * getDriver() { return driver; } - void setup(InputMode); + static DriverManager instance; // Guaranteed to be destroyed. // Instantiated on first use. + return instance; + } + GPDriver * getDriver() { return driver; } + void setup(InputMode); + InputMode getInputMode(){ return inputMode; } private: DriverManager() {} GPDriver * driver; + InputMode inputMode; }; #endif \ No newline at end of file diff --git a/headers/gamepad.h b/headers/gamepad.h index 8ffd47c02..772e14a56 100644 --- a/headers/gamepad.h +++ b/headers/gamepad.h @@ -167,15 +167,10 @@ class Gamepad { uint8_t getMultimedia(uint8_t code); void processHotkeyAction(GamepadHotkey action); - GamepadOptions& options; - const HotkeyOptions& hotkeyOptions; + GamepadOptions & options; + const HotkeyOptions & hotkeyOptions; GamepadHotkey lastAction = HOTKEY_NONE; - - uint32_t keep_alive_timer; - uint8_t keep_alive_sequence; - uint8_t virtual_keycode_sequence; - bool xb1_guide_pressed; }; #endif diff --git a/headers/gp2040.h b/headers/gp2040.h index 1a6c7b69d..8945f8949 100644 --- a/headers/gp2040.h +++ b/headers/gp2040.h @@ -17,8 +17,8 @@ class GP2040 { public: - GP2040(); - ~GP2040(); + GP2040() {} + ~GP2040() {} void setup(); // setup core0 void run(); // loop core0 private: diff --git a/headers/layoutmanager.h b/headers/layoutmanager.h index 8c5be2383..b1b82b7c3 100644 --- a/headers/layoutmanager.h +++ b/headers/layoutmanager.h @@ -10,6 +10,8 @@ #include "config.pb.h" #include "enums.pb.h" +#include "buttonlayouts.h" + typedef struct { uint16_t x1; uint16_t y1; @@ -121,81 +123,8 @@ class LayoutManager { std::string getLayoutNameByID(); - std::map GPButtonLayouts_LeftLayouts = { - // levers - // parameters: x, y, radiusX/width, radiusY/height, stroke, fill, DpadMode - {BUTTON_LAYOUT_STICK, [this]() { return this->drawArcadeStick(); }}, - {BUTTON_LAYOUT_TWINSTICKA, [this]() { return this->drawTwinStickA(); }}, - {BUTTON_LAYOUT_VLXA, [this]() { return this->drawVLXA(); }}, - {BUTTON_LAYOUT_FIGHTBOARD_STICK, [this]() { return this->drawFightboardStick(); }}, - - // buttons - // parameters: x, y, radiusX/width, radiusY/height, stroke, fill, Gamepad button mask, shape - {BUTTON_LAYOUT_STICKLESS, [this]() { return this->drawStickless(); }}, - {BUTTON_LAYOUT_BUTTONS_ANGLED, [this]() { return this->drawUDLR(); }}, - {BUTTON_LAYOUT_BUTTONS_BASIC, [this]() { return this->drawMAMEA(); }}, - {BUTTON_LAYOUT_KEYBOARD_ANGLED, [this]() { return this->drawKeyboardAngled(); }}, - {BUTTON_LAYOUT_KEYBOARDA, [this]() { return this->drawWasdBox(); }}, - {BUTTON_LAYOUT_DANCEPADA, [this]() { return this->drawDancepadA(); }}, - {BUTTON_LAYOUT_BLANKA, [this]() { return this->drawBlankA(); }}, - {BUTTON_LAYOUT_FIGHTBOARD_MIRRORED, [this]() { return this->drawFightboardMirrored(); }}, - {BUTTON_LAYOUT_CUSTOMA, [this]() { return this->drawButtonLayoutLeft(); }}, - {BUTTON_LAYOUT_OPENCORE0WASDA, [this]() { return this->drawOpenCore0WASDA(); }}, - {BUTTON_LAYOUT_STICKLESS_13, [this]() { return this->drawStickless13A(); }}, - {BUTTON_LAYOUT_STICKLESS_16, [this]() { return this->drawStickless16A(); }}, - {BUTTON_LAYOUT_STICKLESS_14, [this]() { return this->drawStickless14A(); }}, - {BUTTON_LAYOUT_DANCEPADA, [this]() { return this->drawDancepadA(); }}, - - {BUTTON_LAYOUT_DANCEPAD_DDR_LEFT, [this]() { return this->drawDancepadDDRLeft(); }}, - {BUTTON_LAYOUT_DANCEPAD_DDR_SOLO, [this]() { return this->drawDancepadDDRSolo(); }}, - {BUTTON_LAYOUT_DANCEPAD_PIU_LEFT, [this]() { return this->drawDancepadPIULeft(); }}, - {BUTTON_LAYOUT_POPN_A, [this]() { return this->drawPopnA(); }}, - {BUTTON_LAYOUT_TAIKO_A, [this]() { return this->drawTaikoA(); }}, - {BUTTON_LAYOUT_BM_TURNTABLE_A, [this]() { return this->drawBMTurntableA(); }}, - {BUTTON_LAYOUT_BM_5KEY_A, [this]() { return this->drawBM5KeyA(); }}, - {BUTTON_LAYOUT_BM_7KEY_A, [this]() { return this->drawBM7KeyA(); }}, - {BUTTON_LAYOUT_GITADORA_FRET_A, [this]() { return this->drawGitadoraFretA(); }}, - {BUTTON_LAYOUT_GITADORA_STRUM_A, [this]() { return this->drawGitadoraStrumA(); }}, - - {BUTTON_LAYOUT_BOARD_DEFINED_A, [this]() { return this->drawBoardDefinedA(); }}, - }; - - std::map GPButtonLayouts_RightLayouts = { - {BUTTON_LAYOUT_ARCADE, [this]() { return this->drawArcadeButtons(); }}, - {BUTTON_LAYOUT_STICKLESSB, [this]() { return this->drawSticklessButtons(); }}, - {BUTTON_LAYOUT_BUTTONS_ANGLEDB, [this]() { return this->drawMAMEB(); }}, - {BUTTON_LAYOUT_VEWLIX, [this]() { return this->drawVewlix(); }}, - {BUTTON_LAYOUT_VEWLIX7, [this]() { return this->drawVewlix7(); }}, - {BUTTON_LAYOUT_CAPCOM, [this]() { return this->drawCapcom(); }}, - {BUTTON_LAYOUT_CAPCOM6, [this]() { return this->drawCapcom6(); }}, - {BUTTON_LAYOUT_SEGA2P, [this]() { return this->drawSega2p(); }}, - {BUTTON_LAYOUT_NOIR8, [this]() { return this->drawNoir8(); }}, - {BUTTON_LAYOUT_KEYBOARDB, [this]() { return this->drawMAMEB(); }}, - {BUTTON_LAYOUT_TWINSTICKB, [this]() { return this->drawTwinStickB(); }}, - {BUTTON_LAYOUT_BLANKB, [this]() { return this->drawBlankB(); }}, - {BUTTON_LAYOUT_VLXB, [this]() { return this->drawVLXB(); }}, - {BUTTON_LAYOUT_FIGHTBOARD, [this]() { return this->drawFightboard(); }}, - {BUTTON_LAYOUT_FIGHTBOARD_STICK_MIRRORED, [this]() { return this->drawFightboardStickMirrored(); }}, - {BUTTON_LAYOUT_CUSTOMB, [this]() { return this->drawButtonLayoutRight(); }}, - {BUTTON_LAYOUT_KEYBOARD8B, [this]() { return this->drawMAME8B(); }}, - {BUTTON_LAYOUT_OPENCORE0WASDB, [this]() { return this->drawOpenCore0WASDB(); }}, - {BUTTON_LAYOUT_STICKLESS_13B, [this]() { return this->drawSticklessButtons13B(); }}, - {BUTTON_LAYOUT_STICKLESS_16B, [this]() { return this->drawSticklessButtons16B(); }}, - {BUTTON_LAYOUT_STICKLESS_14B, [this]() { return this->drawSticklessButtons14B(); }}, - {BUTTON_LAYOUT_DANCEPADB, [this]() { return this->drawDancepadB(); }}, - - {BUTTON_LAYOUT_DANCEPAD_DDR_RIGHT, [this]() { return this->drawDancepadDDRRight(); }}, - {BUTTON_LAYOUT_DANCEPAD_PIU_RIGHT, [this]() { return this->drawDancepadPIURight(); }}, - {BUTTON_LAYOUT_POPN_B, [this]() { return this->drawPopnB(); }}, - {BUTTON_LAYOUT_TAIKO_B, [this]() { return this->drawTaikoB(); }}, - {BUTTON_LAYOUT_BM_TURNTABLE_B, [this]() { return this->drawBMTurntableB(); }}, - {BUTTON_LAYOUT_BM_5KEY_B, [this]() { return this->drawBM5KeyB(); }}, - {BUTTON_LAYOUT_BM_7KEY_B, [this]() { return this->drawBM7KeyB(); }}, - {BUTTON_LAYOUT_GITADORA_FRET_B, [this]() { return this->drawGitadoraFretB(); }}, - {BUTTON_LAYOUT_GITADORA_STRUM_B, [this]() { return this->drawGitadoraStrumB(); }}, - - {BUTTON_LAYOUT_BOARD_DEFINED_B, [this]() { return this->drawBoardDefinedB(); }}, - }; + LayoutManager::LayoutList getLeftLayout(uint16_t index); + LayoutManager::LayoutList getRightLayout(uint16_t index); }; #endif \ No newline at end of file diff --git a/headers/storagemanager.h b/headers/storagemanager.h index 641ad8bbb..57f370b30 100644 --- a/headers/storagemanager.h +++ b/headers/storagemanager.h @@ -47,6 +47,7 @@ class Storage { GpioAction* getProfilePinMappings() { return functionalPinMappings; } PeripheralOptions& getPeripheralOptions() { return config.peripheralOptions; } + void init(); bool save(); // Perform saves that were enqueued from core1 @@ -73,7 +74,7 @@ class Storage { void ResetSettings(); // EEPROM Reset Feature private: - Storage(); + Storage() {} bool CONFIG_MODE = false; // Config mode (boot) Gamepad * gamepad = nullptr; // Gamepad data Gamepad * processedGamepad = nullptr; // Gamepad with ONLY processed data diff --git a/proto/config.proto b/proto/config.proto index 09aaa4fa3..9b2dcaf56 100644 --- a/proto/config.proto +++ b/proto/config.proto @@ -685,7 +685,7 @@ message Macro repeated MacroInput macroInputs = 3 [(nanopb).max_count = 30]; optional bool enabled = 4; optional bool useMacroTriggerButton = 5; - optional int32 macroTriggerPin = 6 [default = -1]; + optional int32 deprecatedMacroTriggerPin = 6 [deprecated = true]; optional uint32 macroTriggerButton = 7; optional bool exclusive = 8 [default = true]; optional bool interruptible = 9 [default = true]; @@ -695,9 +695,9 @@ message Macro message MacroOptions { optional bool enabled = 1; - optional int32 pin = 2; - optional bool macroBoardLedEnabled = 4; + optional int32 deprecatedPin = 2 [deprecated = true]; repeated Macro macroList = 3 [(nanopb).max_count = 6]; + optional bool macroBoardLedEnabled = 4; } message InputHistoryOptions diff --git a/proto/enums.proto b/proto/enums.proto index 0da091179..27b3b6adf 100644 --- a/proto/enums.proto +++ b/proto/enums.proto @@ -208,6 +208,13 @@ enum GpioAction SUSTAIN_SOCD_MODE_FIRST_WIN = 30; SUSTAIN_SOCD_MODE_BYPASS = 31; BUTTON_PRESS_TURBO = 32; + BUTTON_PRESS_MACRO = 33; + BUTTON_PRESS_MACRO_1 = 34; + BUTTON_PRESS_MACRO_2 = 35; + BUTTON_PRESS_MACRO_3 = 36; + BUTTON_PRESS_MACRO_4 = 37; + BUTTON_PRESS_MACRO_5 = 38; + BUTTON_PRESS_MACRO_6 = 39; } enum GamepadHotkey diff --git a/src/addons/display.cpp b/src/addons/display.cpp index 3681c6794..8d3c35533 100644 --- a/src/addons/display.cpp +++ b/src/addons/display.cpp @@ -9,8 +9,7 @@ #include "storagemanager.h" #include "pico/stdlib.h" #include "bitmaps.h" -#include "drivers/ps4/PS4Driver.h" -#include "drivers/xbone/XBOneDriver.h" + #include "drivermanager.h" #include "usbdriver.h" #include "version.h" @@ -23,18 +22,12 @@ bool DisplayAddon::available() { } void DisplayAddon::setup() { + //stdio_init_all(); const DisplayOptions& options = Storage::getInstance().getDisplayOptions(); PeripheralI2C* i2c = PeripheralManager::getInstance().getI2C(options.i2cBlock); - loadedScreens.insert({CONFIG_INSTRUCTION, {new ConfigScreen()}}); - loadedScreens.insert({SPLASH, {new SplashScreen()}}); - loadedScreens.insert({MAIN_MENU, {new MainMenuScreen()}}); - loadedScreens.insert({BUTTONS, {new ButtonLayoutScreen()}}); - - gpDisplay = new GPGFX(); - + // Setup GPGFX Options GPGFX_DisplayTypeOptions gpOptions; - if (PeripheralManager::getInstance().isI2CEnabled(options.i2cBlock)) { gpOptions.displayType = GPGFX_DisplayType::TYPE_SSD1306; gpOptions.i2c = i2c; @@ -45,17 +38,15 @@ void DisplayAddon::setup() { gpOptions.font.fontData = GP_Font_Standard; gpOptions.font.width = 6; gpOptions.font.height = 8; + } else { + return; // Do not run our display } + // Setup GPGFX + gpDisplay = new GPGFX(); gpDisplay->init(gpOptions); - map::iterator screenIterator; - for (screenIterator = loadedScreens.begin(); screenIterator != loadedScreens.end(); screenIterator++) { - screenIterator->second->setRenderer(gpDisplay); - } - gamepad = Storage::getInstance().GetGamepad(); - pGamepad = Storage::getInstance().GetProcessedGamepad(); displaySaverTimer = options.displaySaverTimeout; displaySaverTimeout = displaySaverTimer; @@ -72,6 +63,39 @@ void DisplayAddon::setup() { } else { currDisplayMode = DisplayMode::CONFIG_INSTRUCTION; } + + gpScreen = nullptr; + updateDisplayScreen(); +} + +bool DisplayAddon::updateDisplayScreen() { + if ( gpScreen != nullptr ) + delete gpScreen; + + switch(currDisplayMode) { + case CONFIG_INSTRUCTION: + gpScreen = new ConfigScreen(gpDisplay); + break; + case SPLASH: + gpScreen = new SplashScreen(gpDisplay); + break; + case MAIN_MENU: + gpScreen = new MainMenuScreen(gpDisplay); + break; + case BUTTONS: + gpScreen = new ButtonLayoutScreen(gpDisplay); + break; + default: + gpScreen = nullptr; + break; + }; + + if (gpScreen != nullptr) { + gpScreen->init(); + return true; + } + + return false; } bool DisplayAddon::isDisplayPowerOff() @@ -108,29 +132,21 @@ void DisplayAddon::setDisplayPower(uint8_t status) } void DisplayAddon::process() { - if (!configMode && isDisplayPowerOff()) return; - - gpScreen = loadedScreens.find(currDisplayMode)->second; - - drawStatusBar(gamepad); + // If GPDisplay is not loaded or we're in standard mode with display power off enabled + if (gpDisplay->getDriver() == nullptr || + (!configMode && isDisplayPowerOff())) { + return; + } int8_t screenReturn = gpScreen->update(); - gpScreen->draw(); + // -1 = we do not change state if (screenReturn >= 0) { + // Screen wants to change to something else if (screenReturn != currDisplayMode) { - // screen changes currDisplayMode = (DisplayMode)screenReturn; - } else { - // no change - } - } else { - // screen exited - if (!configMode) { - currDisplayMode = DisplayMode::BUTTONS; - } else { - currDisplayMode = DisplayMode::CONFIG_INSTRUCTION; + updateDisplayScreen(); } } } @@ -140,75 +156,3 @@ const DisplayOptions& DisplayAddon::getDisplayOptions() { return configMode ? Storage::getInstance().getPreviewDisplayOptions() : Storage::getInstance().getDisplayOptions(); } -void DisplayAddon::drawStatusBar(Gamepad * gamepad) -{ - const TurboOptions& turboOptions = Storage::getInstance().getAddonOptions().turboOptions; - - // Limit to 21 chars with 6x8 font for now - statusBar.clear(); - - switch (gamepad->getOptions().inputMode) - { - case INPUT_MODE_HID: statusBar += "DINPUT"; break; - case INPUT_MODE_SWITCH: statusBar += "SWITCH"; break; - case INPUT_MODE_XINPUT: statusBar += "XINPUT"; break; - case INPUT_MODE_MDMINI: statusBar += "GEN/MD"; break; - case INPUT_MODE_NEOGEO: statusBar += "NGMINI"; break; - case INPUT_MODE_PCEMINI: statusBar += "PCE/TG"; break; - case INPUT_MODE_EGRET: statusBar += "EGRET"; break; - case INPUT_MODE_ASTRO: statusBar += "ASTRO"; break; - case INPUT_MODE_PSCLASSIC: statusBar += "PSC"; break; - case INPUT_MODE_XBOXORIGINAL: statusBar += "OGXBOX"; break; - case INPUT_MODE_PS4: - statusBar += "PS4"; - if(((PS4Driver*)DriverManager::getInstance().getDriver())->getAuthSent() == true ) - statusBar += ":AS"; - else - statusBar += " "; - break; - case INPUT_MODE_PS5: - statusBar += "PS5"; - if(((PS4Driver*)DriverManager::getInstance().getDriver())->getAuthSent() == true ) - statusBar += ":AS"; - else - statusBar += " "; - break; - case INPUT_MODE_XBONE: - statusBar += "XBON"; - if(((XBOneDriver*)DriverManager::getInstance().getDriver())->getAuthSent() == true ) - statusBar += "E"; - else - statusBar += "*"; - break; - case INPUT_MODE_KEYBOARD: statusBar += "HID-KB"; break; - case INPUT_MODE_CONFIG: statusBar += "CONFIG"; break; - } - - if ( turboOptions.enabled ) { - statusBar += " T"; - if ( turboOptions.shotCount < 10 ) // padding - statusBar += "0"; - statusBar += std::to_string(turboOptions.shotCount); - } else { - statusBar += " "; // no turbo, don't show Txx setting - } - switch (gamepad->getOptions().dpadMode) - { - case DPAD_MODE_DIGITAL: statusBar += " D"; break; - case DPAD_MODE_LEFT_ANALOG: statusBar += " L"; break; - case DPAD_MODE_RIGHT_ANALOG: statusBar += " R"; break; - } - - switch (Gamepad::resolveSOCDMode(gamepad->getOptions())) - { - case SOCD_MODE_NEUTRAL: statusBar += " SOCD-N"; break; - case SOCD_MODE_UP_PRIORITY: statusBar += " SOCD-U"; break; - case SOCD_MODE_SECOND_INPUT_PRIORITY: statusBar += " SOCD-L"; break; - case SOCD_MODE_FIRST_INPUT_PRIORITY: statusBar += " SOCD-F"; break; - case SOCD_MODE_BYPASS: statusBar += " SOCD-X"; break; - } - if (Storage::getInstance().getAddonOptions().macroOptions.enabled) - statusBar += " M"; - - gpScreen->header = statusBar; -} diff --git a/src/addons/input_macro.cpp b/src/addons/input_macro.cpp index 47df083e6..ca69a3815 100644 --- a/src/addons/input_macro.cpp +++ b/src/addons/input_macro.cpp @@ -5,179 +5,198 @@ #include "hardware/gpio.h" bool InputMacro::available() { - return Storage::getInstance().getAddonOptions().macroOptions.enabled; + // Macro Button initialized by void Gamepad::setup() + GpioAction* pinMappings = Storage::getInstance().getProfilePinMappings(); + for (Pin_t pin = 0; pin < (Pin_t)NUM_BANK0_GPIOS; pin++) + { + switch( pinMappings[pin] ) { + case GpioAction::BUTTON_PRESS_MACRO: + case GpioAction::BUTTON_PRESS_MACRO_1: + case GpioAction::BUTTON_PRESS_MACRO_2: + case GpioAction::BUTTON_PRESS_MACRO_3: + case GpioAction::BUTTON_PRESS_MACRO_4: + case GpioAction::BUTTON_PRESS_MACRO_5: + case GpioAction::BUTTON_PRESS_MACRO_6: + return true; + default: + break; + } + } + return false; } void InputMacro::setup() { - inputMacroOptions = Storage::getInstance().getAddonOptions().macroOptions; - - gpio_init(inputMacroOptions.pin); // Initialize pin - gpio_set_dir(inputMacroOptions.pin, GPIO_IN); // Set as INPUT - gpio_pull_up(inputMacroOptions.pin); // Set as PULLUP + GpioAction* pinMappings = Storage::getInstance().getProfilePinMappings(); + macroButtonMask = 0; + memset(macroPinMasks, 0, sizeof(macroPinMasks)); + for (Pin_t pin = 0; pin < (Pin_t)NUM_BANK0_GPIOS; pin++) + { + switch( pinMappings[pin] ) { + case GpioAction::BUTTON_PRESS_MACRO: + macroButtonMask = 1 << pin; + break; + case GpioAction::BUTTON_PRESS_MACRO_1: + macroPinMasks[0] = 1 << pin; + break; + case GpioAction::BUTTON_PRESS_MACRO_2: + macroPinMasks[1] = 1 << pin; + break; + case GpioAction::BUTTON_PRESS_MACRO_3: + macroPinMasks[2] = 1 << pin; + break; + case GpioAction::BUTTON_PRESS_MACRO_4: + macroPinMasks[3] = 1 << pin; + break; + case GpioAction::BUTTON_PRESS_MACRO_5: + macroPinMasks[4] = 1 << pin; + break; + case GpioAction::BUTTON_PRESS_MACRO_6: + macroPinMasks[5] = 1 << pin; + break; + default: + break; + } + } - if (inputMacroOptions.macroBoardLedEnabled && isValidPin(BOARD_LED_PIN)) { + inputMacroOptions = &Storage::getInstance().getAddonOptions().macroOptions; + if (inputMacroOptions->macroBoardLedEnabled && isValidPin(BOARD_LED_PIN)) { gpio_init(BOARD_LED_PIN); gpio_set_dir(BOARD_LED_PIN, GPIO_OUT); boardLedEnabled = true; + } else { + boardLedEnabled = false; } + boardLedEnabled = false; + prevMacroInputPressed = false; + reset(); +} - for (int i = 0; i < inputMacroOptions.macroList_count; i++) { - Macro& macro = inputMacroOptions.macroList[i]; - if (!macro.enabled) continue; - if (!macro.useMacroTriggerButton && !isValidPin(macro.macroTriggerPin)) continue; - gpio_init(macro.macroTriggerPin); // Initialize pin - gpio_set_dir(macro.macroTriggerPin, GPIO_IN); // Set as INPUT - gpio_pull_up(macro.macroTriggerPin); // Set as PULLUP +void InputMacro::reset() { + macroPosition = -1; + pressedMacro = -1; + isMacroRunning = false; + macroStartTime = 0; + macroInputPosition = 0; + isMacroTriggerHeld = false; + macroInputHoldTime = INPUT_HOLD_US; + if (boardLedEnabled) { + gpio_put(BOARD_LED_PIN, 0); } } -void InputMacro::preprocess() -{ - FocusModeOptions& focusModeOptions = Storage::getInstance().getAddonOptions().focusModeOptions; - if (focusModeOptions.enabled && focusModeOptions.macroLockEnabled) return; +void InputMacro::checkMacroPress() { + // Macro is not interruptible + if ( macroPosition != -1 && inputMacroOptions->macroList[macroPosition].interruptible == false ) + return; + Gamepad * gamepad = Storage::getInstance().GetGamepad(); Mask_t allPins = gamepad->debouncedGpio; - macroInputPressed = false; - uint64_t currentMicros = getMicro(); - - if (macroPosition == -1 || inputMacroOptions.macroList[macroPosition].interruptible) { - int newMacroPosition = -1; - for (int i = 0; i < inputMacroOptions.macroList_count; i++) { - Macro& macro = inputMacroOptions.macroList[i]; - if (!macro.enabled) continue; - - if (macro.useMacroTriggerButton) { - if (macro.macroTriggerButton == 0) continue; - if ((allPins & 1 << inputMacroOptions.pin) && - ((gamepad->state.buttons & macro.macroTriggerButton) || - (gamepad->state.dpad & (macro.macroTriggerButton >> 16)))) { - macroInputPressed = true; - newMacroPosition = i; break; - } - } else { - if (!isValidPin(macro.macroTriggerPin)) continue; - if ((allPins & 1 << macro.macroTriggerPin)) { - macroInputPressed = true; - newMacroPosition = i; break; - } - } - } - - if (macroPosition == -1 && newMacroPosition == -1) - return; - if (macroPosition != -1 && newMacroPosition != -1) { - if (newMacroPosition != macroPosition || - (newMacroPosition == macroPosition && - inputMacroOptions.macroList[newMacroPosition].macroType == ON_PRESS && - isMacroRunning && (macroTriggerDebounceStartTime != 0 || (!prevMacroInputPressed && macroInputPressed)))) { - if (macroTriggerDebounceStartTime == 0) { - macroTriggerDebounceStartTime = currentMicros; - } - - if (macroTriggerDebounceStartTime != 0) { - if (((currentMicros - macroTriggerDebounceStartTime) > 500)) { - macroTriggerDebounceStartTime = 0; - if (macroInputPressed) { - reset(); - return; - } - } - } + // Go through our macro list + pressedMacro = -1; + for(int i = 0; i < MAX_MACRO_LIMIT; i++) { + if ( inputMacroOptions->macroList[i].enabled == false ) // Skip disabled macros + continue; + Macro * macro = &inputMacroOptions->macroList[i]; + if ( macro->useMacroTriggerButton ) { + // Use Gamepad Button for Macro Trigger + if ((allPins & macroButtonMask) && + ((gamepad->state.buttons & macro->macroTriggerButton) || + (gamepad->state.dpad & (macro->macroTriggerButton >> 16))) ) { + pressedMacro = i; + break; } - } - - if (newMacroPosition != -1 && !isMacroRunning) { - macroPosition = newMacroPosition; - macroStartTime = 0; + } else if ( allPins & macroPinMasks[i] ) { + // Use Pin Manager for Macro Trigger + pressedMacro = i; + break; } } +} - Macro& macro = inputMacroOptions.macroList[macroPosition]; - - if (macro.useMacroTriggerButton) { - macroInputPressed = (allPins & 1 << inputMacroOptions.pin) && - ((gamepad->state.buttons & macro.macroTriggerButton) || - (gamepad->state.dpad & (macro.macroTriggerButton >> 16))); - } else { - macroInputPressed = (allPins & 1 << macro.macroTriggerPin); - } +void InputMacro::checkMacroAction() { + bool macroInputPressed = (pressedMacro != -1); // Was any macro input pressed? - if (!isMacroRunning && macroInputPressed && macroTriggerDebounceStartTime == 0) { - macroTriggerDebounceStartTime = currentMicros; - return; + // Is our pressed macro button different from our current macro AND no macro is running? + if ( pressedMacro != macroPosition && !isMacroRunning ) { + macroPosition = pressedMacro; // move our position to that macro } - if (macroTriggerDebounceStartTime != 0) { - if (((currentMicros - macroTriggerDebounceStartTime) > 500)) { - macroTriggerDebounceStartTime = 0; - } else { - return; - } - } + bool newPress = macroInputPressed && (prevMacroInputPressed ^ macroInputPressed); - if (!isMacroRunning) { - switch (macro.macroType) { - case ON_TOGGLE: - isMacroTriggerHeld = prevMacroInputPressed && !macroInputPressed; - break; - case ON_PRESS: - isMacroTriggerHeld = !prevMacroInputPressed && macroInputPressed; - break; - case ON_HOLD_REPEAT: - isMacroTriggerHeld = macroInputPressed; - break; - default: - break; + // Check to see if we should change the current macro (or turn off based on input) + if ( inputMacroOptions->macroList[macroPosition].macroType == ON_PRESS ) { + // START Macro: On Press + if (!isMacroRunning ) { + isMacroTriggerHeld = newPress; } - } else { - switch (macro.macroType) { - case ON_PRESS: - break; // no-op - case ON_TOGGLE: - if (prevMacroInputPressed && !macroInputPressed) - isMacroTriggerHeld = false; - break; - case ON_HOLD_REPEAT: - isMacroTriggerHeld = macroInputPressed; - break; + } else if ( inputMacroOptions->macroList[macroPosition].macroType == ON_HOLD_REPEAT ) { + // Hold macro + isMacroTriggerHeld = macroInputPressed; + } else if ( inputMacroOptions->macroList[macroPosition].macroType == ON_TOGGLE ) { + if (!isMacroRunning ) { // START Macro: Toggle on new press + isMacroTriggerHeld = newPress; + } else { + // STOP Macro: Toggle on new press + if ( newPress ) { + reset(); // Stop Macro: Toggle + prevMacroInputPressed = macroInputPressed; + return; + } } } prevMacroInputPressed = macroInputPressed; - - MacroInput& macroInput = macro.macroInputs[macroInputPosition]; - uint32_t macroInputDuration = macroInput.duration + macroInput.waitDuration; - macroInputHoldTime = macroInputDuration <= 0 ? INPUT_HOLD_US : macroInputDuration; - if (!isMacroRunning && isMacroTriggerHeld) { + // New Macro to run + macroPosition = pressedMacro; // Set current macro + Macro& macro = inputMacroOptions->macroList[macroPosition]; + MacroInput& macroInput = macro.macroInputs[macroInputPosition]; + uint32_t macroInputDuration = macroInput.duration + macroInput.waitDuration; + macroInputHoldTime = macroInputDuration <= 0 ? INPUT_HOLD_US : macroInputDuration; isMacroRunning = true; - macroStartTime = currentMicros; + macroStartTime = getMicro(); // current time } - - if (!isMacroRunning) +} + +void InputMacro::runCurrentMacro() { + // Do nothing if macro is not currently running + if (!isMacroRunning || + macroPosition == -1) return; - + + Macro& macro = inputMacroOptions->macroList[macroPosition]; + + // Stop Macro: Hold-Repeat if ((!isMacroTriggerHeld && macro.interruptible)) { reset(); return; } + MacroInput& macroInput = macro.macroInputs[macroInputPosition]; + Gamepad * gamepad = Storage::getInstance().GetGamepad(); + uint64_t currentMicros = getMicro(); + if (!macro.interruptible && macro.exclusive) { + // Prevent any other inputs from modifying our input (Exclusive) gamepad->state.dpad = 0; gamepad->state.buttons = 0; } else { if (macro.useMacroTriggerButton) { + // Remove the trigger button from the input state gamepad->state.dpad &= ~(macro.macroTriggerButton >> 16); gamepad->state.buttons &= ~macro.macroTriggerButton; } - if (macro.interruptible && (gamepad->state.buttons != 0 || gamepad->state.dpad != 0)) { + if (macro.interruptible && + (gamepad->state.buttons != 0 || gamepad->state.dpad != 0)) { + // Macro is interruptible and a user pressed something reset(); return; } } + // Check if we should still hold this macro input based on duration if ((currentMicros - macroStartTime) <= macroInput.duration) { uint32_t buttonMask = macroInput.buttonMask; if (buttonMask & GAMEPAD_MASK_DU) { @@ -194,36 +213,71 @@ void InputMacro::preprocess() } gamepad->state.buttons |= buttonMask; + // Macro LED is on if we're currently running and inputs are doing something (wait-timers turn it off) if (boardLedEnabled) { gpio_put(BOARD_LED_PIN, (gamepad->state.dpad || gamepad->state.buttons) ? 1 : 0); } - } else { - if (boardLedEnabled) { - gpio_put(BOARD_LED_PIN, 0); - } } + // Have we elapsed the input hold time? if ((currentMicros - macroStartTime) >= macroInputHoldTime) { - macroStartTime = currentMicros; macroInputPosition++; - MacroInput& newMacroInput = macro.macroInputs[macroInputPosition]; - uint32_t newMacroInputDuration = newMacroInput.duration + newMacroInput.waitDuration; - macroInputHoldTime = newMacroInputDuration <= 0 ? INPUT_HOLD_US : newMacroInputDuration; - } - - if (isMacroRunning && macroInputPosition >= (macro.macroInputs_count)) { - macroInputPosition = 0; - bool isMacroTypeLoopable = macro.macroType == ON_TOGGLE || macro.macroType == ON_HOLD_REPEAT; - isMacroTriggerHeld = isMacroTriggerHeld && isMacroTypeLoopable; - isMacroRunning = isMacroTriggerHeld; - macroPosition = (isMacroTypeLoopable && isMacroTriggerHeld) ? macroPosition : -1; macroStartTime = currentMicros; + macroInputPosition++; + if (macroInputPosition >= (macro.macroInputs_count)) { + if ( macro.macroType == ON_PRESS ) { + reset(); // On press = no more macro + } else { + macroInputPosition = 0; // On Hold-Repeat or On Toggle = start macro again + macroStartTime = currentMicros; + MacroInput& newMacroInput = macro.macroInputs[macroInputPosition]; + uint32_t newMacroInputDuration = newMacroInput.duration + newMacroInput.waitDuration; + macroInputHoldTime = newMacroInputDuration <= 0 ? INPUT_HOLD_US : newMacroInputDuration; + } + } } } -void InputMacro::reset() { - macroPosition = -1; - isMacroRunning = false; - macroStartTime = 0; - prevMacroInputPressed = false; - macroInputPosition = 0; +void InputMacro::preprocess() +{ + FocusModeOptions * focusModeOptions = &Storage::getInstance().getAddonOptions().focusModeOptions; + if (focusModeOptions->enabled && focusModeOptions->macroLockEnabled) + return; + + checkMacroPress(); + checkMacroAction(); + runCurrentMacro(); +} + +void InputMacro::reinit() { + GpioAction* pinMappings = Storage::getInstance().getProfilePinMappings(); + macroButtonMask = 0; + memset(macroPinMasks, 0, sizeof(macroPinMasks)); + for (Pin_t pin = 0; pin < (Pin_t)NUM_BANK0_GPIOS; pin++) + { + switch( pinMappings[pin] ) { + case GpioAction::BUTTON_PRESS_MACRO: + macroButtonMask = 1 << pin; + break; + case GpioAction::BUTTON_PRESS_MACRO_1: + macroPinMasks[0] = 1 << pin; + break; + case GpioAction::BUTTON_PRESS_MACRO_2: + macroPinMasks[1] = 1 << pin; + break; + case GpioAction::BUTTON_PRESS_MACRO_3: + macroPinMasks[2] = 1 << pin; + break; + case GpioAction::BUTTON_PRESS_MACRO_4: + macroPinMasks[3] = 1 << pin; + break; + case GpioAction::BUTTON_PRESS_MACRO_5: + macroPinMasks[4] = 1 << pin; + break; + case GpioAction::BUTTON_PRESS_MACRO_6: + macroPinMasks[5] = 1 << pin; + break; + default: + break; + } + } } diff --git a/src/config_utils.cpp b/src/config_utils.cpp index f4c935e22..1af6eb2f6 100644 --- a/src/config_utils.cpp +++ b/src/config_utils.cpp @@ -713,10 +713,10 @@ void ConfigUtils::initUnsetPropertiesWithDefaults(Config& config) INIT_UNSET_PROPERTY(config.addonOptions.focusModeOptions, buttonLockEnabled, !!FOCUS_MODE_BUTTON_LOCK_ENABLED); INIT_UNSET_PROPERTY(config.addonOptions.focusModeOptions, macroLockEnabled, !!FOCUS_MODE_MACRO_LOCK_ENABLED); - INIT_UNSET_PROPERTY(config.addonOptions.macroOptions, enabled, !!INPUT_MACRO_ENABLED); - INIT_UNSET_PROPERTY(config.addonOptions.macroOptions, pin, INPUT_MACRO_PIN); + // Macro options (always on) + INIT_UNSET_PROPERTY(config.addonOptions.macroOptions, enabled, true); INIT_UNSET_PROPERTY(config.addonOptions.macroOptions, macroBoardLedEnabled, INPUT_MACRO_BOARD_LED_ENABLED); - config.addonOptions.macroOptions.macroList_count = 6; + config.addonOptions.macroOptions.macroList_count = MAX_MACRO_LIMIT; } @@ -804,7 +804,8 @@ void gpioMappingsMigrationCore(Config& config) // flag additional pins as being used by an addon not managed here const auto markAddonPinIfUsed = [&](Pin_t gpPin) -> void { - if (isValidPin(gpPin)) actions[gpPin] = GpioAction::ASSIGNED_TO_ADDON; + if (isValidPin(gpPin)) + actions[gpPin] = GpioAction::ASSIGNED_TO_ADDON; }; // From Protobuf or Board Config @@ -1162,13 +1163,6 @@ void gpioMappingsMigrationCore(Config& config) markAddonPinIfUsed(config.addonOptions.snesOptions.clockPin); markAddonPinIfUsed(config.addonOptions.snesOptions.latchPin); markAddonPinIfUsed(config.addonOptions.snesOptions.dataPin); - markAddonPinIfUsed(config.addonOptions.macroOptions.pin); - markAddonPinIfUsed(config.addonOptions.macroOptions.macroList[0].macroTriggerPin); - markAddonPinIfUsed(config.addonOptions.macroOptions.macroList[1].macroTriggerPin); - markAddonPinIfUsed(config.addonOptions.macroOptions.macroList[2].macroTriggerPin); - markAddonPinIfUsed(config.addonOptions.macroOptions.macroList[3].macroTriggerPin); - markAddonPinIfUsed(config.addonOptions.macroOptions.macroList[4].macroTriggerPin); - markAddonPinIfUsed(config.addonOptions.macroOptions.macroList[5].macroTriggerPin); for (Pin_t pin = 0; pin < (Pin_t)NUM_BANK0_GPIOS; pin++) { config.gpioMappings.pins[pin].action = actions[pin]; @@ -1275,6 +1269,43 @@ void migrateAuthenticationMethods(Config& config) { } } +void migrateMacroPinsToGpio(Config& config) { + + // Convert Macro pin mapping to GPIO mapping configs + MacroOptions & macroOptions = config.addonOptions.macroOptions; + if (macroOptions.enabled && (isValidPin(macroOptions.deprecatedPin) || + isValidPin(macroOptions.macroList[0].deprecatedMacroTriggerPin) || + isValidPin(macroOptions.macroList[1].deprecatedMacroTriggerPin) || + isValidPin(macroOptions.macroList[2].deprecatedMacroTriggerPin) || + isValidPin(macroOptions.macroList[3].deprecatedMacroTriggerPin) || + isValidPin(macroOptions.macroList[4].deprecatedMacroTriggerPin) || + isValidPin(macroOptions.macroList[5].deprecatedMacroTriggerPin)) ) { + // previous config had a value we haven't migrated yet, it can/should apply in the new config + if ( isValidPin(macroOptions.deprecatedPin) ) { + Pin_t pin = macroOptions.deprecatedPin; + config.gpioMappings.pins[pin].action = GpioAction::BUTTON_PRESS_MACRO; + for (uint8_t profileNum = 0; profileNum <= 2; profileNum++) { + config.profileOptions.gpioMappingsSets[profileNum].pins[pin].action = GpioAction::BUTTON_PRESS_MACRO; + } + macroOptions.deprecatedPin = -1; // set our turbo options to -1 for subsequent calls + } + const static GpioAction actionList[6] = { GpioAction::BUTTON_PRESS_MACRO_1, GpioAction::BUTTON_PRESS_MACRO_2, + GpioAction::BUTTON_PRESS_MACRO_3, GpioAction::BUTTON_PRESS_MACRO_4, + GpioAction::BUTTON_PRESS_MACRO_5, GpioAction::BUTTON_PRESS_MACRO_6 }; + for(int i = 0; i < 6; i++ ) { + if ( isValidPin(macroOptions.macroList[i].deprecatedMacroTriggerPin) ) { + Pin_t pin = macroOptions.macroList[i].deprecatedMacroTriggerPin; + config.gpioMappings.pins[pin].action = actionList[i]; + for (uint8_t profileNum = 0; profileNum <= 2; profileNum++) { + config.profileOptions.gpioMappingsSets[profileNum].pins[pin].action = actionList[i]; + } + macroOptions.macroList[i].deprecatedMacroTriggerPin = -1; // set our turbo options to -1 for subsequent calls + } + } + } + +} + // populate existing configurations' buttonsMask and auxMask to mirror behavior // from the behavior before this code merged. totally new configs get their // board defaults via initUnsetPropertiesWithDefaults @@ -1430,6 +1461,8 @@ void ConfigUtils::load(Config& config) migrateTurboPinToGpio(config); // Migrate PS4/PS5/XBone authentication methods to new organization migrateAuthenticationMethods(config); + // Macro pins to gpio + migrateMacroPinsToGpio(config); // Update boardVersion, in case we migrated from an older version strncpy(config.boardVersion, GP2040VERSION, sizeof(config.boardVersion)); @@ -2064,6 +2097,7 @@ bool ConfigUtils::fromJSON(Config& config, const char* data, size_t dataLen) gpioMappingsMigrationProfiles(config); migrateTurboPinToGpio(config); migrateAuthenticationMethods(config); + migrateMacroPinsToGpio(config); return true; } diff --git a/src/configs/webconfig.cpp b/src/configs/webconfig.cpp index 8fa1b123d..c1cfb8f1c 100644 --- a/src/configs/webconfig.cpp +++ b/src/configs/webconfig.cpp @@ -1827,9 +1827,6 @@ std::string setMacroAddonOptions() DynamicJsonDocument doc = get_post_data(); MacroOptions& macroOptions = Storage::getInstance().getAddonOptions().macroOptions; - - docToValue(macroOptions.enabled, doc, "InputMacroAddonEnabled"); - docToPin(macroOptions.pin, doc, "macroPin"); docToValue(macroOptions.macroBoardLedEnabled, doc, "macroBoardLedEnabled"); JsonObject options = doc.as(); @@ -1842,7 +1839,6 @@ std::string setMacroAddonOptions() macroOptions.macroList[macrosIndex].macroLabel[macroLabelSize - 1] = '\0'; macroOptions.macroList[macrosIndex].macroType = macro["macroType"].as(); macroOptions.macroList[macrosIndex].useMacroTriggerButton = macro["useMacroTriggerButton"].as(); - macroOptions.macroList[macrosIndex].macroTriggerPin = macro["macroTriggerPin"].as(); macroOptions.macroList[macrosIndex].macroTriggerButton = macro["macroTriggerButton"].as(); macroOptions.macroList[macrosIndex].enabled = macro["enabled"] == true; macroOptions.macroList[macrosIndex].exclusive = macro["exclusive"] == true; @@ -1859,7 +1855,8 @@ std::string setMacroAddonOptions() } macroOptions.macroList[macrosIndex].macroInputs_count = macroInputsIndex; - if (++macrosIndex >= MAX_MACRO_LIMIT) break; + if (++macrosIndex >= MAX_MACRO_LIMIT) + break; } macroOptions.macroList_count = MAX_MACRO_LIMIT; @@ -1875,11 +1872,9 @@ std::string getMacroAddonOptions() MacroOptions& macroOptions = Storage::getInstance().getAddonOptions().macroOptions; JsonArray macroList = doc.createNestedArray("macroList"); - writeDoc(doc, "macroPin", macroOptions.pin); writeDoc(doc, "macroBoardLedEnabled", macroOptions.macroBoardLedEnabled); - writeDoc(doc, "InputMacroAddonEnabled", macroOptions.enabled); - for (int i = 0; i < macroOptions.macroList_count; i++) { + for (int i = 0; i < MAX_MACRO_LIMIT; i++) { JsonObject macro = macroList.createNestedObject(); macro["enabled"] = macroOptions.macroList[i].enabled ? 1 : 0; macro["exclusive"] = macroOptions.macroList[i].exclusive ? 1 : 0; @@ -1887,7 +1882,6 @@ std::string getMacroAddonOptions() macro["showFrames"] = macroOptions.macroList[i].showFrames ? 1 : 0; macro["macroType"] = macroOptions.macroList[i].macroType; macro["useMacroTriggerButton"] = macroOptions.macroList[i].useMacroTriggerButton ? 1 : 0; - macro["macroTriggerPin"] = macroOptions.macroList[i].macroTriggerPin; macro["macroTriggerButton"] = macroOptions.macroList[i].macroTriggerButton; macro["macroLabel"] = macroOptions.macroList[i].macroLabel; diff --git a/src/display/GPGFX_UI.cpp b/src/display/GPGFX_UI.cpp index 4b0965132..5f8a5ea2c 100644 --- a/src/display/GPGFX_UI.cpp +++ b/src/display/GPGFX_UI.cpp @@ -14,15 +14,8 @@ Gamepad* GPGFX_UI::getProcessedGamepad() { } DisplayOptions GPGFX_UI::getDisplayOptions() { - if (_configMode) { - return Storage::getInstance().getPreviewDisplayOptions(); - } else { - return Storage::getInstance().getDisplayOptions(); - } -} - -bool GPGFX_UI::getConfigMode() { - return Storage::getInstance().GetConfigMode(); + bool configMode = Storage::getInstance().GetConfigMode(); + return configMode ? Storage::getInstance().getPreviewDisplayOptions() : Storage::getInstance().getDisplayOptions(); } uint16_t GPGFX_UI::map(uint16_t x, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max) { diff --git a/src/display/ui/elements/GPScreen.cpp b/src/display/ui/elements/GPScreen.cpp index b3fcec62d..a02dfa36e 100644 --- a/src/display/ui/elements/GPScreen.cpp +++ b/src/display/ui/elements/GPScreen.cpp @@ -1,12 +1,16 @@ #include "GPScreen.h" +const bool prioritySort(GPWidget * a, GPWidget * b) { + return a->getPriority() > b->getPriority(); +} + void GPScreen::draw() { getRenderer()->clearScreen(); // draw the display list if ( displayList.size() > 0 ) { - std::sort(displayList.begin(), displayList.end(), [](std::unique_ptr& a, std::unique_ptr& b){ return a->getPriority() > b->getPriority(); }); - for(std::vector>::iterator it = displayList.begin(); it != displayList.end(); ++it) { + std::sort(displayList.begin(), displayList.end(), prioritySort); + for(std::vector::iterator it = displayList.begin(); it != displayList.end(); ++it) { (*it)->draw(); } } @@ -14,10 +18,6 @@ void GPScreen::draw() { getRenderer()->render(); } -int8_t GPScreen::update() { - return 0; -} - void GPScreen::clear() { if (displayList.size() > 0) { displayList.clear(); diff --git a/src/display/ui/screens/ButtonLayoutScreen.cpp b/src/display/ui/screens/ButtonLayoutScreen.cpp index c50d3d7ca..98b6b17f3 100644 --- a/src/display/ui/screens/ButtonLayoutScreen.cpp +++ b/src/display/ui/screens/ButtonLayoutScreen.cpp @@ -1,64 +1,217 @@ #include "ButtonLayoutScreen.h" #include "buttonlayouts.h" +#include "drivermanager.h" +#include "drivers/ps4/PS4Driver.h" +#include "drivers/xbone/XBOneDriver.h" + +void ButtonLayoutScreen::init() { + const InputHistoryOptions& inputHistoryOptions = Storage::getInstance().getAddonOptions().inputHistoryOptions; + isInputHistoryEnabled = inputHistoryOptions.enabled; + inputHistoryX = inputHistoryOptions.row; + inputHistoryY = inputHistoryOptions.col; + inputHistoryLength = inputHistoryOptions.length; + profileDelayStart = getMillis(); + gamepad = Storage::getInstance().GetGamepad(); + inputMode = DriverManager::getInstance().getInputMode(); + + // load layout (drawElement pushes element to the display list) + uint16_t elementCtr = 0; + LayoutManager::LayoutList currLayoutLeft = LayoutManager::getInstance().getLayoutA(); + LayoutManager::LayoutList currLayoutRight = LayoutManager::getInstance().getLayoutB(); + for (elementCtr = 0; elementCtr < currLayoutLeft.size(); elementCtr++) { + pushElement(currLayoutLeft[elementCtr]); + } + for (elementCtr = 0; elementCtr < currLayoutRight.size(); elementCtr++) { + pushElement(currLayoutRight[elementCtr]); + } + + // start with profile mode displayed + profileModeDisplay = true; + + // we cannot look at macro options enabled, pull the pins + + // macro display now uses our pin functions, so we need to check if pins are enabled... + macroEnabled = false; + // Macro Button initialized by void Gamepad::setup() + GpioAction* pinMappings = Storage::getInstance().getProfilePinMappings(); + for (Pin_t pin = 0; pin < (Pin_t)NUM_BANK0_GPIOS; pin++) + { + switch( pinMappings[pin] ) { + case GpioAction::BUTTON_PRESS_MACRO: + case GpioAction::BUTTON_PRESS_MACRO_1: + case GpioAction::BUTTON_PRESS_MACRO_2: + case GpioAction::BUTTON_PRESS_MACRO_3: + case GpioAction::BUTTON_PRESS_MACRO_4: + case GpioAction::BUTTON_PRESS_MACRO_5: + case GpioAction::BUTTON_PRESS_MACRO_6: + macroEnabled = true; + break; + default: + break; + } + } + + getRenderer()->clearScreen(); +} + +int8_t ButtonLayoutScreen::update() { + // main logic loop + generateHeader(); + if (isInputHistoryEnabled) + processInputHistory(); + + // check for exit/screen change + if (Storage::getInstance().GetConfigMode()) { + uint16_t buttonState = getGamepad()->state.buttons; + if (prevButtonState && !buttonState) { + if (prevButtonState == GAMEPAD_MASK_B1) { + prevButtonState = 0; + return DisplayMode::CONFIG_INSTRUCTION; + } + } + prevButtonState = buttonState; + } + + return -1; +} + +void ButtonLayoutScreen::generateHeader() { + // Limit to 21 chars with 6x8 font for now + statusBar.clear(); + + // Display Profile # banner + if ( profileModeDisplay ) { + if (((getMillis() - profileDelayStart) / 1000) < profileDelay) { + statusBar = " Profile #"; + statusBar += std::to_string(getGamepad()->getOptions().profileNumber); + return; + } else { + profileModeDisplay = false; + } + } + + // Display standard header + switch (inputMode) + { + case INPUT_MODE_HID: statusBar += "DINPUT"; break; + case INPUT_MODE_SWITCH: statusBar += "SWITCH"; break; + case INPUT_MODE_XINPUT: statusBar += "XINPUT"; break; + case INPUT_MODE_MDMINI: statusBar += "GEN/MD"; break; + case INPUT_MODE_NEOGEO: statusBar += "NGMINI"; break; + case INPUT_MODE_PCEMINI: statusBar += "PCE/TG"; break; + case INPUT_MODE_EGRET: statusBar += "EGRET"; break; + case INPUT_MODE_ASTRO: statusBar += "ASTRO"; break; + case INPUT_MODE_PSCLASSIC: statusBar += "PSC"; break; + case INPUT_MODE_XBOXORIGINAL: statusBar += "OGXBOX"; break; + case INPUT_MODE_PS4: + statusBar += "PS4"; + if(((PS4Driver*)DriverManager::getInstance().getDriver())->getAuthSent() == true ) + statusBar += ":AS"; + else + statusBar += " "; + break; + case INPUT_MODE_PS5: + statusBar += "PS5"; + if(((PS4Driver*)DriverManager::getInstance().getDriver())->getAuthSent() == true ) + statusBar += ":AS"; + else + statusBar += " "; + break; + case INPUT_MODE_XBONE: + statusBar += "XBON"; + if(((XBOneDriver*)DriverManager::getInstance().getDriver())->getAuthSent() == true ) + statusBar += "E"; + else + statusBar += "*"; + break; + case INPUT_MODE_KEYBOARD: statusBar += "HID-KB"; break; + case INPUT_MODE_CONFIG: statusBar += "CONFIG"; break; + } + + const TurboOptions& turboOptions = Storage::getInstance().getAddonOptions().turboOptions; + if ( turboOptions.enabled ) { + statusBar += " T"; + if ( turboOptions.shotCount < 10 ) // padding + statusBar += "0"; + statusBar += std::to_string(turboOptions.shotCount); + } else { + statusBar += " "; // no turbo, don't show Txx setting + } + + const GamepadOptions & options = gamepad->getOptions(); + + switch (gamepad->getOptions().dpadMode) + { + case DPAD_MODE_DIGITAL: statusBar += " D"; break; + case DPAD_MODE_LEFT_ANALOG: statusBar += " L"; break; + case DPAD_MODE_RIGHT_ANALOG: statusBar += " R"; break; + } + + switch (Gamepad::resolveSOCDMode(gamepad->getOptions())) + { + case SOCD_MODE_NEUTRAL: statusBar += " SOCD-N"; break; + case SOCD_MODE_UP_PRIORITY: statusBar += " SOCD-U"; break; + case SOCD_MODE_SECOND_INPUT_PRIORITY: statusBar += " SOCD-L"; break; + case SOCD_MODE_FIRST_INPUT_PRIORITY: statusBar += " SOCD-F"; break; + case SOCD_MODE_BYPASS: statusBar += " SOCD-X"; break; + } + if (macroEnabled) + statusBar += " M"; +} void ButtonLayoutScreen::drawScreen() { - getRenderer()->drawRectangle(0, 0, 128, 7, displayProfileBanner, displayProfileBanner); - getRenderer()->drawText(0, 0, header, displayProfileBanner); + if (profileModeDisplay) { + getRenderer()->drawRectangle(0, 0, 128, 7, true, true); + getRenderer()->drawText(0, 0, statusBar, true); + } else { + getRenderer()->drawText(0, 0, statusBar); + } getRenderer()->drawText(0, 7, footer); } -GPLever* ButtonLayoutScreen::drawLever(uint16_t startX, uint16_t startY, uint16_t sizeX, uint16_t sizeY, uint16_t strokeColor, uint16_t fillColor, uint16_t inputType) { - GPLever* lever = addElement(); +GPLever* ButtonLayoutScreen::addLever(uint16_t startX, uint16_t startY, uint16_t sizeX, uint16_t sizeY, uint16_t strokeColor, uint16_t fillColor, uint16_t inputType) { + GPLever* lever = new GPLever(); lever->setRenderer(getRenderer()); - lever->setPosition(startX, startY); lever->setStrokeColor(strokeColor); lever->setFillColor(fillColor); lever->setRadius(sizeX); lever->setInputType(inputType); - - return lever; + return (GPLever*)addElement(lever); } -GPButton* ButtonLayoutScreen::drawButton(uint16_t startX, uint16_t startY, uint16_t sizeX, uint16_t sizeY, uint16_t strokeColor, uint16_t fillColor, int16_t inputMask) { - GPButton* button = addElement(); +GPButton* ButtonLayoutScreen::addButton(uint16_t startX, uint16_t startY, uint16_t sizeX, uint16_t sizeY, uint16_t strokeColor, uint16_t fillColor, int16_t inputMask) { + GPButton* button = new GPButton(); button->setRenderer(getRenderer()); - button->setPosition(startX, startY); button->setStrokeColor(strokeColor); button->setFillColor(fillColor); - button->setSizeX(sizeX); - button->setSizeY(sizeY); + button->setSize(sizeX, sizeY); button->setInputMask(inputMask); - - return button; + return (GPButton*)addElement(button); } -GPShape* ButtonLayoutScreen::drawShape(uint16_t startX, uint16_t startY, uint16_t sizeX, uint16_t sizeY, uint16_t strokeColor, uint16_t fillColor) { - GPShape* shape = addElement(); +GPShape* ButtonLayoutScreen::addShape(uint16_t startX, uint16_t startY, uint16_t sizeX, uint16_t sizeY, uint16_t strokeColor, uint16_t fillColor) { + GPShape* shape = new GPShape(); shape->setRenderer(getRenderer()); - shape->setPosition(startX, startY); shape->setStrokeColor(strokeColor); shape->setFillColor(fillColor); - shape->setSizeX(sizeX); - shape->setSizeY(sizeY); + shape->setSize(sizeX,sizeY); + addElement(shape); return shape; } -GPSprite* ButtonLayoutScreen::drawSprite(uint16_t startX, uint16_t startY, uint16_t sizeX, uint16_t sizeY) { - GPSprite* sprite = addElement(); +GPSprite* ButtonLayoutScreen::addSprite(uint16_t startX, uint16_t startY, uint16_t sizeX, uint16_t sizeY) { + GPSprite* sprite = new GPSprite(); sprite->setRenderer(getRenderer()); - sprite->setPosition(startX, startY); - sprite->setSizeX(sizeX); - sprite->setSizeY(sizeY); - - return sprite; + sprite->setSize(sizeX,sizeY); + return (GPSprite*)addElement(sprite); } -GPWidget* ButtonLayoutScreen::drawElement(GPButtonLayout element) { +GPWidget* ButtonLayoutScreen::pushElement(GPButtonLayout element) { uint16_t yOffset = 0; if (isInputHistoryEnabled) { @@ -66,9 +219,9 @@ GPWidget* ButtonLayoutScreen::drawElement(GPButtonLayout element) { } if (element.elementType == GP_ELEMENT_LEVER) { - return drawLever(element.parameters.x1, element.parameters.y1-yOffset, element.parameters.x2, element.parameters.y2, element.parameters.stroke, element.parameters.fill, element.parameters.value); + return addLever(element.parameters.x1, element.parameters.y1-yOffset, element.parameters.x2, element.parameters.y2, element.parameters.stroke, element.parameters.fill, element.parameters.value); } else if ((element.elementType == GP_ELEMENT_BTN_BUTTON) || (element.elementType == GP_ELEMENT_DIR_BUTTON) || (element.elementType == GP_ELEMENT_PIN_BUTTON)) { - GPButton* button = drawButton(element.parameters.x1, element.parameters.y1-yOffset, element.parameters.x2, element.parameters.y2, element.parameters.stroke, element.parameters.fill, element.parameters.value); + GPButton* button = addButton(element.parameters.x1, element.parameters.y1-yOffset, element.parameters.x2, element.parameters.y2, element.parameters.stroke, element.parameters.fill, element.parameters.value); // set type of button button->setInputType(element.elementType); @@ -82,9 +235,9 @@ GPWidget* ButtonLayoutScreen::drawElement(GPButtonLayout element) { return (GPWidget*)button; } else if (element.elementType == GP_ELEMENT_SPRITE) { - return drawSprite(element.parameters.x1, element.parameters.y1-yOffset, element.parameters.x2, element.parameters.y2); + return addSprite(element.parameters.x1, element.parameters.y1-yOffset, element.parameters.x2, element.parameters.y2); } else if (element.elementType == GP_ELEMENT_SHAPE) { - GPShape* shape = drawShape(element.parameters.x1, element.parameters.y1-yOffset, element.parameters.x2, element.parameters.y2, element.parameters.stroke, element.parameters.fill); + GPShape* shape = addShape(element.parameters.x1, element.parameters.y1-yOffset, element.parameters.x2, element.parameters.y2, element.parameters.stroke, element.parameters.fill); shape->setShape((GPShape_Type)element.parameters.shape); shape->setAngle(element.parameters.angleStart); shape->setAngleEnd(element.parameters.angleEnd); @@ -94,172 +247,91 @@ GPWidget* ButtonLayoutScreen::drawElement(GPButtonLayout element) { return NULL; } -int8_t ButtonLayoutScreen::update() { - GPWidget::update(); - bool configMode = getConfigMode(); - uint8_t profileNumber = getGamepad()->getOptions().profileNumber; - - if (configMode) { - uint8_t layoutLeft = Storage::getInstance().getDisplayOptions().buttonLayout; - uint8_t layoutRight = Storage::getInstance().getDisplayOptions().buttonLayoutRight; - - if ((prevLayoutLeft != layoutLeft) || (prevLayoutRight != layoutRight)) { - hasInitialized = false; - clear(); - } - - prevLayoutLeft = layoutLeft; - prevLayoutRight = layoutRight; - } - - if (!hasInitialized) { - const InputHistoryOptions& inputHistoryOptions = Storage::getInstance().getAddonOptions().inputHistoryOptions; - isInputHistoryEnabled = inputHistoryOptions.enabled; - inputHistoryX = inputHistoryOptions.row; - inputHistoryY = inputHistoryOptions.col; - inputHistoryLength = inputHistoryOptions.length; - - // load layout - uint16_t elementCtr = 0; - LayoutManager::LayoutList currLayoutLeft = LayoutManager::getInstance().getLayoutA(); - LayoutManager::LayoutList currLayoutRight = LayoutManager::getInstance().getLayoutB(); - for (elementCtr = 0; elementCtr < currLayoutLeft.size(); elementCtr++) { - drawElement(currLayoutLeft[elementCtr]); - } - for (elementCtr = 0; elementCtr < currLayoutRight.size(); elementCtr++) { - drawElement(currLayoutRight[elementCtr]); - } - - profileDelayStart = getMillis(); - - hasInitialized = true; - } - - // main logic loop - if (prevProfileNumber != profileNumber) { - profileDelayStart = getMillis(); - - prevProfileNumber = profileNumber; - } - showProfileBanner(); - if (isInputHistoryEnabled) processInputHistory(); - - // check for exit/screen change - if (!configMode) { - return DisplayMode::BUTTONS; - } else { - uint16_t buttonState = getGamepad()->state.buttons; - - if (prevButtonState && !buttonState) { - if (prevButtonState == GAMEPAD_MASK_B1) { - prevButtonState = 0; - return -1; - } - } - - prevButtonState = buttonState; - - return DisplayMode::BUTTONS; - } -} - void ButtonLayoutScreen::processInputHistory() { - std::deque pressed; - - // Get key states - std::array currentInput = { - - pressedUp(), - pressedDown(), - pressedLeft(), - pressedRight(), - - pressedUpLeft(), - pressedUpRight(), - pressedDownLeft(), - pressedDownRight(), - - getGamepad()->pressedB1(), - getGamepad()->pressedB2(), - getGamepad()->pressedB3(), - getGamepad()->pressedB4(), - getGamepad()->pressedL1(), - getGamepad()->pressedR1(), - getGamepad()->pressedL2(), - getGamepad()->pressedR2(), - getGamepad()->pressedS1(), - getGamepad()->pressedS2(), - getGamepad()->pressedL3(), - getGamepad()->pressedR3(), - getGamepad()->pressedA1(), - getGamepad()->pressedA2(), - }; - - uint8_t mode = ((displayModeLookup.count(getGamepad()->getOptions().inputMode) > 0) ? displayModeLookup.at(getGamepad()->getOptions().inputMode) : 0); - - // Check if any new keys have been pressed - if (lastInput != currentInput) { - // Iterate through array - for (uint8_t x=0; x 0) { - std::string newInput; - for(const auto &s : pressed) { - if(!newInput.empty()) - newInput += "+"; - newInput += s; - } - - inputHistory.push_back(newInput); - } - - if (inputHistory.size() > (inputHistoryLength / 2) + 1) { - inputHistory.pop_front(); - } - - std::string ret; - - for (auto it = inputHistory.crbegin(); it != inputHistory.crend(); ++it) { - std::string newRet = ret; - if (!newRet.empty()) - newRet = " " + newRet; - - newRet = *it + newRet; - ret = newRet; - - if (ret.size() >= inputHistoryLength) { - break; - } - } - - if(ret.size() >= inputHistoryLength) { - historyString = ret.substr(ret.size() - inputHistoryLength); - } else { - historyString = ret; - } + std::deque pressed; + + // Get key states + std::array currentInput = { + + pressedUp(), + pressedDown(), + pressedLeft(), + pressedRight(), + + pressedUpLeft(), + pressedUpRight(), + pressedDownLeft(), + pressedDownRight(), + + getGamepad()->pressedB1(), + getGamepad()->pressedB2(), + getGamepad()->pressedB3(), + getGamepad()->pressedB4(), + getGamepad()->pressedL1(), + getGamepad()->pressedR1(), + getGamepad()->pressedL2(), + getGamepad()->pressedR2(), + getGamepad()->pressedS1(), + getGamepad()->pressedS2(), + getGamepad()->pressedL3(), + getGamepad()->pressedR3(), + getGamepad()->pressedA1(), + getGamepad()->pressedA2(), + }; + + uint8_t mode = ((displayModeLookup.count(getGamepad()->getOptions().inputMode) > 0) ? displayModeLookup.at(getGamepad()->getOptions().inputMode) : 0); + + // Check if any new keys have been pressed + if (lastInput != currentInput) { + // Iterate through array + for (uint8_t x=0; x 0) { + std::string newInput; + for(const auto &s : pressed) { + if(!newInput.empty()) + newInput += "+"; + newInput += s; + } + + inputHistory.push_back(newInput); + } + + if (inputHistory.size() > (inputHistoryLength / 2) + 1) { + inputHistory.pop_front(); + } + + std::string ret; + + for (auto it = inputHistory.crbegin(); it != inputHistory.crend(); ++it) { + std::string newRet = ret; + if (!newRet.empty()) + newRet = " " + newRet; + + newRet = *it + newRet; + ret = newRet; + + if (ret.size() >= inputHistoryLength) { + break; + } + } + + if(ret.size() >= inputHistoryLength) { + historyString = ret.substr(ret.size() - inputHistoryLength); + } else { + historyString = ret; + } footer = historyString; } -void ButtonLayoutScreen::showProfileBanner() { - int profileDelayCheck = getMillis(); - - if (((profileDelayCheck - profileDelayStart) / 1000) >= profileDelay) { - // stop displaying - displayProfileBanner = false; - } else { - // display - header = " Profile #" + std::to_string(getGamepad()->getOptions().profileNumber); - displayProfileBanner = true; - } -} - bool ButtonLayoutScreen::pressedUp() { switch (getGamepad()->getOptions().dpadMode) diff --git a/src/display/ui/screens/ConfigScreen.cpp b/src/display/ui/screens/ConfigScreen.cpp index c98428a3c..fa26052a0 100644 --- a/src/display/ui/screens/ConfigScreen.cpp +++ b/src/display/ui/screens/ConfigScreen.cpp @@ -1,22 +1,14 @@ #include "ConfigScreen.h" - #include "version.h" -void ConfigScreen::drawScreen() { - getRenderer()->drawText(0, 1, "[Web Config Mode]"); - getRenderer()->drawText(0, 2, std::string("GP2040-CE : ") + std::string(GP2040VERSION)); - getRenderer()->drawText(0, 3, "[http://192.168.7.1]"); - getRenderer()->drawText(0, 4, "Preview:"); - getRenderer()->drawText(5, 5, "B1 > Button"); - getRenderer()->drawText(5, 6, "B2 > Splash"); - - getRenderer()->drawText(0, 0, header); - getRenderer()->drawText(0, 50, footer); +void ConfigScreen::init() { + version = "GP2040-CE : "; + version += GP2040VERSION; + getRenderer()->clearScreen(); } int8_t ConfigScreen::update() { uint16_t buttonState = getGamepad()->state.buttons; - if (prevButtonState && !buttonState) { switch (prevButtonState) { case (GAMEPAD_MASK_B1): @@ -32,8 +24,15 @@ int8_t ConfigScreen::update() { break; } } - prevButtonState = buttonState; - - return DisplayMode::CONFIG_INSTRUCTION; + return -1; } + +void ConfigScreen::drawScreen() { + getRenderer()->drawText(0, 0, "[Web Config Mode]"); + getRenderer()->drawText(0, 1, version); + getRenderer()->drawText(0, 2, "[http://192.168.7.1]"); + getRenderer()->drawText(0, 3, "Preview:"); + getRenderer()->drawText(5, 4, " B1 > Button"); + getRenderer()->drawText(5, 5, " B2 > Splash"); +} \ No newline at end of file diff --git a/src/display/ui/screens/MainMenuScreen.cpp b/src/display/ui/screens/MainMenuScreen.cpp index 770ccdf50..c3d0da92b 100644 --- a/src/display/ui/screens/MainMenuScreen.cpp +++ b/src/display/ui/screens/MainMenuScreen.cpp @@ -2,6 +2,10 @@ extern uint32_t getMillis(); +void MainMenuScreen::init() { + getRenderer()->clearScreen(); +} + void MainMenuScreen::drawScreen() { getRenderer()->drawText(1, 1, "GPGFX_UI Test Menu"); @@ -78,7 +82,7 @@ int8_t MainMenuScreen::update() { prevButtonState = buttonState; - return DisplayMode::MAIN_MENU; + return -1; } /* diff --git a/src/display/ui/screens/SplashScreen.cpp b/src/display/ui/screens/SplashScreen.cpp index 0dda06953..49d26b908 100644 --- a/src/display/ui/screens/SplashScreen.cpp +++ b/src/display/ui/screens/SplashScreen.cpp @@ -2,9 +2,13 @@ #include "pico/stdlib.h" -void SplashScreen::drawScreen() { - int mils = getMillis(); +void SplashScreen::init() { + getRenderer()->clearScreen(); + splashStartTime = getMillis(); + configMode = Storage::getInstance().GetConfigMode(); +} +void SplashScreen::drawScreen() { if (getDisplayOptions().splashMode == static_cast(SPLASH_MODE_NONE)) { getRenderer()->drawText(0, 4, " Splash NOT enabled."); } else { @@ -15,42 +19,39 @@ void SplashScreen::drawScreen() { getRenderer()->drawSprite((uint8_t*) getDisplayOptions().splashImage.bytes, 128, 64, 16, 0, 0, 1); } else if (splashMode == SPLASH_MODE_CLOSEIN) { // Close-in. Animate the GP2040 logo - getRenderer()->drawSprite((uint8_t *)bootLogoTop, 43, 39, 6, 43, std::min((mils / splashSpeed) - 39, 0), 1); - getRenderer()->drawSprite((uint8_t *)bootLogoBottom, 128, 35, 10, 0, std::max(64 - (mils / (splashSpeed * 2)), 30), 1); + int timeMS = getMillis(); + getRenderer()->drawSprite((uint8_t *)bootLogoTop, 43, 39, 6, 43, std::min((timeMS / splashSpeed) - 39, 0), 1); + getRenderer()->drawSprite((uint8_t *)bootLogoBottom, 128, 35, 10, 0, std::max(64 - (timeMS / (splashSpeed * 2)), 30), 1); } else if (splashMode == SPLASH_MODE_CLOSEINCUSTOM) { // Close-in on custom image or delayed close-in if custom image does not exist getRenderer()->drawSprite((uint8_t*) getDisplayOptions().splashImage.bytes, 128, 64, 16, 0, 0, 1); - if (mils > 1000) { - int milss = mils - 1000; - getRenderer()->drawRectangle(0, 63, 127, 62 - (milss / (splashSpeed * 2)), 0, 1); - getRenderer()->drawSprite((uint8_t *)bootLogoBottom, 128, 35, 10, 0, std::max(64 - (mils / splashSpeed), 20), 1); + int timeMS = getMillis(); + if (timeMS > 1000) { + int offsetMS = timeMS - 1000; + getRenderer()->drawRectangle(0, 63, 127, 62 - (offsetMS / (splashSpeed * 2)), 0, 1); + getRenderer()->drawSprite((uint8_t *)bootLogoBottom, 128, 35, 10, 0, std::max(64 - (timeMS / splashSpeed), 20), 1); } } } } int8_t SplashScreen::update() { + uint32_t elapsedDuration = getMillis() - splashStartTime; uint32_t splashDuration = getDisplayOptions().splashDuration; - bool configMode = getConfigMode(); - if (!configMode) { // still running - if (splashDuration == 0 || getMillis() < splashDuration) { - return DisplayMode::SPLASH; + if (splashDuration != 0 && (elapsedDuration >= splashDuration)) { + return DisplayMode::BUTTONS; } - return -1; } else { uint16_t buttonState = getGamepad()->state.buttons; - if (prevButtonState && !buttonState) { if (prevButtonState == GAMEPAD_MASK_B2) { prevButtonState = 0; - return -1; + return DisplayMode::CONFIG_INSTRUCTION; } } - prevButtonState = buttonState; - - return DisplayMode::SPLASH; } + return -1; // -1 means no change in screen state } diff --git a/src/drivermanager.cpp b/src/drivermanager.cpp index 3b8729df5..f03bbd0e0 100644 --- a/src/drivermanager.cpp +++ b/src/drivermanager.cpp @@ -20,7 +20,7 @@ void DriverManager::setup(InputMode mode) { switch (mode) { case INPUT_MODE_CONFIG: - driver = new NetDriver(); + driver = new NetDriver(); break; case INPUT_MODE_ASTRO: driver = new AstroDriver(); @@ -67,9 +67,10 @@ void DriverManager::setup(InputMode mode) { default: return; } - + // Initialize our chosen driver driver->initialize(); + inputMode = mode; // Start the TinyUSB Device functionality tud_init(TUD_OPT_RHPORT); diff --git a/src/gp2040.cpp b/src/gp2040.cpp index d3ec0ff91..619eec3d6 100644 --- a/src/gp2040.cpp +++ b/src/gp2040.cpp @@ -45,15 +45,9 @@ static const uint32_t REBOOT_HOTKEY_ACTIVATION_TIME_MS = 50; static const uint32_t REBOOT_HOTKEY_HOLD_TIME_MS = 4000; -GP2040::GP2040() { - Storage::getInstance().SetGamepad(new Gamepad()); - Storage::getInstance().SetProcessedGamepad(new Gamepad()); -} - -GP2040::~GP2040() { -} - void GP2040::setup() { + Storage::getInstance().init(); + PeripheralManager::getInstance().initI2C(); PeripheralManager::getInstance().initSPI(); PeripheralManager::getInstance().initUSB(); @@ -63,9 +57,19 @@ void GP2040::setup() { set_sys_clock_khz(120000, true); // Set Clock to 120MHz to avoid potential USB timing issues } - // Setup Gamepad and Gamepad Storage - Gamepad * gamepad = Storage::getInstance().GetGamepad(); - gamepad->setup(); + Gamepad * gamepad = new Gamepad(); + Gamepad * processedGamepad = new Gamepad(); + Storage::getInstance().SetGamepad(gamepad); + Storage::getInstance().SetProcessedGamepad(processedGamepad); + + // Set pin mappings for all GPIO functions + Storage::getInstance().setFunctionalPinMappings(); + + // Setup Gamepad + gamepad->setup(); + + // now we can load the latest configured profile, which will map the + // new set of GPIOs to use... this->initializeStandardGpio(); const GamepadOptions& gamepadOptions = Storage::getInstance().getGamepadOptions(); diff --git a/src/layoutmanager.cpp b/src/layoutmanager.cpp index f718fab72..607073365 100644 --- a/src/layoutmanager.cpp +++ b/src/layoutmanager.cpp @@ -5,12 +5,12 @@ LayoutManager::LayoutList LayoutManager::getLayoutA() { uint16_t layoutLeft = Storage::getInstance().getDisplayOptions().buttonLayout; - return GPButtonLayouts_LeftLayouts.at(layoutLeft)(); + return getLeftLayout(layoutLeft); } LayoutManager::LayoutList LayoutManager::getLayoutB() { uint16_t layoutRight = Storage::getInstance().getDisplayOptions().buttonLayoutRight; - return GPButtonLayouts_RightLayouts.at(layoutRight)(); + return getRightLayout(layoutRight); } std::string LayoutManager::getLayoutAName() { @@ -41,12 +41,159 @@ std::string LayoutManager::getButtonLayoutRightName(ButtonLayoutRight layout) { #undef ENUM_CASE } +LayoutManager::LayoutList LayoutManager::getLeftLayout(uint16_t index) { + // buttons + // parameters: x, y, radiusX/width, radiusY/height, stroke, fill, Gamepad button mask, shape + switch(index) { + // levers + // parameters: x, y, radiusX/width, radiusY/height, stroke, fill, DpadMode + case BUTTON_LAYOUT_STICK: + return drawArcadeStick(); + case BUTTON_LAYOUT_TWINSTICKA: + return drawTwinStickA(); + case BUTTON_LAYOUT_VLXA: + return drawVLXA(); + case BUTTON_LAYOUT_FIGHTBOARD_STICK: + return drawFightboardStick(); + // buttons + // parameters: x, y, radiusX/width, radiusY/height, stroke, fill, Gamepad button mask, shape + case BUTTON_LAYOUT_STICKLESS: + return drawStickless(); + case BUTTON_LAYOUT_BUTTONS_ANGLED: + return drawUDLR(); + case BUTTON_LAYOUT_BUTTONS_BASIC: + return drawMAMEA(); + case BUTTON_LAYOUT_KEYBOARD_ANGLED: + return drawKeyboardAngled(); + case BUTTON_LAYOUT_KEYBOARDA: + return drawWasdBox(); + case BUTTON_LAYOUT_DANCEPADA: + return drawDancepadA(); + case BUTTON_LAYOUT_BLANKA: + return drawBlankA(); + case BUTTON_LAYOUT_FIGHTBOARD_MIRRORED: + return drawFightboardMirrored(); + case BUTTON_LAYOUT_CUSTOMA: + return drawButtonLayoutLeft(); + case BUTTON_LAYOUT_OPENCORE0WASDA: + return drawOpenCore0WASDA(); + case BUTTON_LAYOUT_STICKLESS_13: + return drawStickless13A(); + case BUTTON_LAYOUT_STICKLESS_16: + return drawStickless16A(); + case BUTTON_LAYOUT_STICKLESS_14: + return drawStickless14A(); + case BUTTON_LAYOUT_DANCEPAD_DDR_LEFT: + return drawDancepadDDRLeft(); + case BUTTON_LAYOUT_DANCEPAD_DDR_SOLO: + return drawDancepadDDRSolo(); + case BUTTON_LAYOUT_DANCEPAD_PIU_LEFT: + return drawDancepadPIULeft(); + case BUTTON_LAYOUT_POPN_A: + return drawPopnA(); + case BUTTON_LAYOUT_TAIKO_A: + return drawTaikoA(); + case BUTTON_LAYOUT_BM_TURNTABLE_A: + return drawBMTurntableA(); + case BUTTON_LAYOUT_BM_5KEY_A: + return drawBM5KeyA(); + case BUTTON_LAYOUT_BM_7KEY_A: + return drawBM7KeyA(); + case BUTTON_LAYOUT_GITADORA_FRET_A: + return drawGitadoraFretA(); + case BUTTON_LAYOUT_GITADORA_STRUM_A: + return drawGitadoraStrumA(); + case BUTTON_LAYOUT_BOARD_DEFINED_A: + return drawBoardDefinedA(); + default: + break; + } + + return {}; +} + + +LayoutManager::LayoutList LayoutManager::getRightLayout(uint16_t index) { + // buttons + // parameters: x, y, radiusX/width, radiusY/height, stroke, fill, Gamepad button mask, shape + switch(index) { + case BUTTON_LAYOUT_ARCADE: + return this->drawArcadeButtons(); + case BUTTON_LAYOUT_STICKLESSB: + return this->drawSticklessButtons(); + case BUTTON_LAYOUT_BUTTONS_ANGLEDB: + return this->drawMAMEB(); + case BUTTON_LAYOUT_VEWLIX: + return this->drawVewlix(); + case BUTTON_LAYOUT_VEWLIX7: + return this->drawVewlix7(); + case BUTTON_LAYOUT_CAPCOM: + return this->drawCapcom(); + case BUTTON_LAYOUT_CAPCOM6: + return this->drawCapcom6(); + case BUTTON_LAYOUT_SEGA2P: + return this->drawSega2p(); + case BUTTON_LAYOUT_NOIR8: + return this->drawNoir8(); + case BUTTON_LAYOUT_KEYBOARDB: + return this->drawMAMEB(); + case BUTTON_LAYOUT_TWINSTICKB: + return this->drawTwinStickB(); + case BUTTON_LAYOUT_BLANKB: + return this->drawBlankB(); + case BUTTON_LAYOUT_VLXB: + return this->drawVLXB(); + case BUTTON_LAYOUT_FIGHTBOARD: + return this->drawFightboard(); + case BUTTON_LAYOUT_FIGHTBOARD_STICK_MIRRORED: + return this->drawFightboardStickMirrored(); + case BUTTON_LAYOUT_CUSTOMB: + return this->drawButtonLayoutRight(); + case BUTTON_LAYOUT_KEYBOARD8B: + return this->drawMAME8B(); + case BUTTON_LAYOUT_OPENCORE0WASDB: + return this->drawOpenCore0WASDB(); + case BUTTON_LAYOUT_STICKLESS_13B: + return this->drawSticklessButtons13B(); + case BUTTON_LAYOUT_STICKLESS_16B: + return this->drawSticklessButtons16B(); + case BUTTON_LAYOUT_STICKLESS_14B: + return this->drawSticklessButtons14B(); + case BUTTON_LAYOUT_DANCEPADB: + return this->drawDancepadB(); + case BUTTON_LAYOUT_DANCEPAD_DDR_RIGHT: + return this->drawDancepadDDRRight(); + case BUTTON_LAYOUT_DANCEPAD_PIU_RIGHT: + return this->drawDancepadPIURight(); + case BUTTON_LAYOUT_POPN_B: + return this->drawPopnB(); + case BUTTON_LAYOUT_TAIKO_B: + return this->drawTaikoB(); + case BUTTON_LAYOUT_BM_TURNTABLE_B: + return this->drawBMTurntableB(); + case BUTTON_LAYOUT_BM_5KEY_B: + return this->drawBM5KeyB(); + case BUTTON_LAYOUT_BM_7KEY_B: + return this->drawBM7KeyB(); + case BUTTON_LAYOUT_GITADORA_FRET_B: + return this->drawGitadoraFretB(); + case BUTTON_LAYOUT_GITADORA_STRUM_B: + return this->drawGitadoraStrumB(); + case BUTTON_LAYOUT_BOARD_DEFINED_B: + return this->drawBoardDefinedB(); + default: + break; + } + + return {}; +} + LayoutManager::LayoutList LayoutManager::drawButtonLayoutLeft() { const DisplayOptions& options = Storage::getInstance().getDisplayOptions(); ButtonLayoutCustomOptions buttonLayoutCustomOptions = options.buttonLayoutCustomOptions; ButtonLayoutParamsLeft leftOptions = buttonLayoutCustomOptions.paramsLeft; - return adjustByCustomSettings(GPButtonLayouts_LeftLayouts.at(leftOptions.layout)(), leftOptions.common); + return adjustByCustomSettings(getLeftLayout(leftOptions.layout), leftOptions.common); } LayoutManager::LayoutList LayoutManager::drawButtonLayoutRight() @@ -54,7 +201,7 @@ LayoutManager::LayoutList LayoutManager::drawButtonLayoutRight() const DisplayOptions& options = Storage::getInstance().getDisplayOptions(); ButtonLayoutCustomOptions buttonLayoutCustomOptions = options.buttonLayoutCustomOptions; ButtonLayoutParamsRight rightOptions = buttonLayoutCustomOptions.paramsRight; - return adjustByCustomSettings(GPButtonLayouts_RightLayouts.at(rightOptions.layout)(), rightOptions.common, 64); + return adjustByCustomSettings(getRightLayout(rightOptions.layout), rightOptions.common, 64); } LayoutManager::LayoutList LayoutManager::adjustByCustomSettings(LayoutManager::LayoutList layout, ButtonLayoutParamsCommon common, uint16_t originX, uint16_t originY) { diff --git a/src/storagemanager.cpp b/src/storagemanager.cpp index 52fdab907..9150e03de 100644 --- a/src/storagemanager.cpp +++ b/src/storagemanager.cpp @@ -21,13 +21,10 @@ #include "helper.h" -Storage::Storage() -{ +void Storage::init() { EEPROM.start(); critical_section_init(&animationOptionsCs); ConfigUtils::load(config); - - setFunctionalPinMappings(); } bool Storage::save() diff --git a/www/server/app.js b/www/server/app.js index c4e523b0c..4d5126813 100644 --- a/www/server/app.js +++ b/www/server/app.js @@ -512,7 +512,6 @@ app.get('/api/getMacroAddonOptions', (req, res) => { showFrames: 1, macroType: 1, useMacroTriggerButton: 0, - macroTriggerPin: -1, macroTriggerButton: 0, macroLabel: 'Shoryuken', macroInputs: [ @@ -525,10 +524,28 @@ app.get('/api/getMacroAddonOptions', (req, res) => { }, ], }, + { enabled: 0, exclusive: 1, interruptible: 1, showFrames: 1, + macroType: 1, useMacroTriggerButton: 0, + macroTriggerButton: 0, macroLabel: '', + macroInputs: [],}, + { enabled: 0, exclusive: 1, interruptible: 1, showFrames: 1, + macroType: 1, useMacroTriggerButton: 0, + macroTriggerButton: 0, macroLabel: '', + macroInputs: [],}, + { enabled: 0, exclusive: 1, interruptible: 1, showFrames: 1, + macroType: 1, useMacroTriggerButton: 0, + macroTriggerButton: 0, macroLabel: '', + macroInputs: [],}, + { enabled: 0, exclusive: 1, interruptible: 1, showFrames: 1, + macroType: 1, useMacroTriggerButton: 0, + macroTriggerButton: 0, macroLabel: '', + macroInputs: [],}, + { enabled: 0, exclusive: 1, interruptible: 1, showFrames: 1, + macroType: 1, useMacroTriggerButton: 0, + macroTriggerButton: 0, macroLabel: '', + macroInputs: [],}, ], - macroPin: -1, macroBoardLedEnabled: 1, - InputMacroAddonEnabled: 1, }); }); diff --git a/www/src/Data/Pins.ts b/www/src/Data/Pins.ts index 0416e1bae..ab4d8bd32 100644 --- a/www/src/Data/Pins.ts +++ b/www/src/Data/Pins.ts @@ -38,6 +38,13 @@ export const BUTTON_ACTIONS = { SUSTAIN_SOCD_MODE_FIRST_WIN: 30, SUSTAIN_SOCD_MODE_BYPASS: 31, BUTTON_PRESS_TURBO: 32, + BUTTON_PRESS_MACRO: 33, + BUTTON_PRESS_MACRO_1: 34, + BUTTON_PRESS_MACRO_2: 35, + BUTTON_PRESS_MACRO_3: 36, + BUTTON_PRESS_MACRO_4: 37, + BUTTON_PRESS_MACRO_5: 38, + BUTTON_PRESS_MACRO_6: 39, } as const; type PinActionKeys = keyof typeof BUTTON_ACTIONS; diff --git a/www/src/Locales/de-DE/InputMacroAddon.jsx b/www/src/Locales/de-DE/InputMacroAddon.jsx index 491f79d59..784ad1411 100644 --- a/www/src/Locales/de-DE/InputMacroAddon.jsx +++ b/www/src/Locales/de-DE/InputMacroAddon.jsx @@ -1,18 +1,13 @@ export default { 'input-macro-board-led-enabled': 'Board LED Aktivieren', - 'input-macro-macro-activated': 'Aktiviert', 'input-macro-macro-exclusive': 'Exklusiv', 'input-macro-macro-interruptible': 'Unterbrechbar', 'input-macro-macro-show-frames': 'Frames anzeigen', - 'input-macro-macro-uses-buttons': 'Benutzt Tasten', - 'input-macro-macro-button-pin-plus': 'Makro Tasten Pin + ', 'input-macro-header-text': 'Makro Einstellungen', 'input-macro-duration-label': 'Dauer', 'input-macro-wait-duration-label': 'Warte Dauer', 'input-macro-time-label-ms': 'ms', 'input-macro-time-label-frames': 'frame(s)', - 'input-macro-sub-header': - "Jedes Makro kann einem dedizierten Pin zugeordnet werden, der wie eine Taste verwendet wird. Daher ist der Makro-Tasten-Pin optional.", 'input-macro-warning': 'Der Makro-Tasten-Pin ist nicht zugewiesen. "Benutzt Tasten" Option wird deaktiviert.', 'input-macro-pin-label': 'Makro Tasten Pin', 'input-macro-macro-label-label': 'Bezeichnung', diff --git a/www/src/Locales/de-DE/PinMapping.jsx b/www/src/Locales/de-DE/PinMapping.jsx index 31c637c87..3ce5df169 100644 --- a/www/src/Locales/de-DE/PinMapping.jsx +++ b/www/src/Locales/de-DE/PinMapping.jsx @@ -48,5 +48,12 @@ export default { SUSTAIN_SOCD_MODE_FIRST_WIN: 'Erster Gewinnt SOCD Säuberung', SUSTAIN_SOCD_MODE_BYPASS: 'SOCD Säuberung Aus', BUTTON_PRESS_TURBO: 'Turbo', + BUTTON_PRESS_MACRO: 'Makro', + BUTTON_PRESS_MACRO_1: 'Makro 1', + BUTTON_PRESS_MACRO_2: 'Makro 2', + BUTTON_PRESS_MACRO_3: 'Makro 3', + BUTTON_PRESS_MACRO_4: 'Makro 4', + BUTTON_PRESS_MACRO_5: 'Makro 5', + BUTTON_PRESS_MACRO_6: 'Makro 6', }, }; diff --git a/www/src/Locales/en/InputMacroAddon.jsx b/www/src/Locales/en/InputMacroAddon.jsx index ad727359e..8ed3adb66 100644 --- a/www/src/Locales/en/InputMacroAddon.jsx +++ b/www/src/Locales/en/InputMacroAddon.jsx @@ -1,18 +1,24 @@ export default { - 'input-macro-board-led-enabled': 'Enabled Board LED', - 'input-macro-macro-activated': 'Activated', - 'input-macro-macro-exclusive': 'Exclusive', - 'input-macro-macro-interruptible': 'Interruptible', - 'input-macro-macro-show-frames': 'Show Frames', - 'input-macro-macro-uses-buttons': 'Uses Buttons', - 'input-macro-macro-button-pin-plus': 'Macro Button Pin + ', - 'input-macro-header-text': 'Macro Settings', + 'input-macro-board-led-enabled': 'Use Board LED to Display Macro Status', + 'input-macro-macro-enabled': 'Enabled', + 'input-macro-macro-exclusive': 'Exclusive (Cannot Be Switched to Another Macro)', + 'input-macro-macro-interruptible': 'Interruptible (User Input Cancels the Macro)', + 'input-macro-macro-show-frames': 'Show Frames (1 Frame = 16.666 ms)', + 'input-macro-macro-uses-buttons': 'Use Macro Button + Gamepad Button to Trigger', + 'input-macro-macro-button-pin-plus': 'Button Assignment:', + 'input-macro-header-text': 'Macros Overview', + 'input-macro-1-header-text': 'Macro 1 Settings', + 'input-macro-2-header-text': 'Macro 2 Settings', + 'input-macro-3-header-text': 'Macro 3 Settings', + 'input-macro-4-header-text': 'Macro 4 Settings', + 'input-macro-5-header-text': 'Macro 5 Settings', + 'input-macro-6-header-text': 'Macro 6 Settings', 'input-macro-duration-label': 'Duration', 'input-macro-wait-duration-label': 'Wait Duration', 'input-macro-time-label-ms': 'ms', 'input-macro-time-label-frames': 'frame(s)', 'input-macro-sub-header': - "Any macro can be assigned a dedicated pin to be used like a button and so the Macro Button Pin is optional.", + "Note: For pin mapped macros, please see the Pin Mapping page under Configuration. Macros can also be assigned to a gamepad button with the corresponding held single Macro button.", 'input-macro-warning': 'Macro Button Pin is not mapped. "Uses Buttons" option will be disabled.', 'input-macro-pin-label': 'Macro Button Pin', 'input-macro-macro-label-label': 'Label', diff --git a/www/src/Locales/en/Navigation.jsx b/www/src/Locales/en/Navigation.jsx index 41c465193..db5011069 100644 --- a/www/src/Locales/en/Navigation.jsx +++ b/www/src/Locales/en/Navigation.jsx @@ -10,7 +10,7 @@ export default { 'home-label': 'Home', 'led-config-label': 'LED Configuration', 'links-label': 'Links', - 'macro-label': 'Macro', + 'macro-label': 'Macros Configuration', 'pin-mapping-label': 'Pin Mapping', 'peripheral-mapping-label': 'Peripheral Mapping', 'profile-settings-label': 'Profile Settings', diff --git a/www/src/Locales/en/PinMapping.jsx b/www/src/Locales/en/PinMapping.jsx index 2ff5f2a01..e9c3002f6 100644 --- a/www/src/Locales/en/PinMapping.jsx +++ b/www/src/Locales/en/PinMapping.jsx @@ -52,5 +52,12 @@ export default { SUSTAIN_SOCD_MODE_FIRST_WIN: 'First Win SOCD Cleaning', SUSTAIN_SOCD_MODE_BYPASS: 'SOCD Cleaning Off', BUTTON_PRESS_TURBO: 'Turbo', + BUTTON_PRESS_MACRO: 'Macro Button', + BUTTON_PRESS_MACRO_1: 'Macro 1', + BUTTON_PRESS_MACRO_2: 'Macro 2', + BUTTON_PRESS_MACRO_3: 'Macro 3', + BUTTON_PRESS_MACRO_4: 'Macro 4', + BUTTON_PRESS_MACRO_5: 'Macro 5', + BUTTON_PRESS_MACRO_6: 'Macro 6', }, }; diff --git a/www/src/Pages/InputMacroAddonPage.jsx b/www/src/Pages/InputMacroAddonPage.jsx deleted file mode 100644 index 7108221f7..000000000 --- a/www/src/Pages/InputMacroAddonPage.jsx +++ /dev/null @@ -1,625 +0,0 @@ -import React, { useContext, useEffect, useState } from 'react'; -import { AppContext } from '../Contexts/AppContext'; -import { Button, Form, OverlayTrigger, Tooltip } from 'react-bootstrap'; -import { Formik, useFormikContext } from 'formik'; -import * as yup from 'yup'; -import { Trans, useTranslation } from 'react-i18next'; -import omit from 'lodash/omit'; - -import Section from '../Components/Section'; -import CaptureButton from '../Components/CaptureButton'; -import FormControl from '../Components/FormControl'; -import WebApi from '../Services/WebApi'; -import { getButtonLabels, BUTTONS, BUTTON_MASKS } from '../Data/Buttons'; - -const MACRO_TYPES = [ - { label: 'InputMacroAddon:input-macro-type.press', value: 1 }, - { label: 'InputMacroAddon:input-macro-type.hold-repeat', value: 2 }, - { label: 'InputMacroAddon:input-macro-type.toggle', value: 3 }, -]; - -const schema = yup.object().shape({ - macroList: yup.array().of( - yup.object().shape({ - macroType: yup.number(), - macroLabel: yup.string(), - enabled: yup.number(), - exclusive: yup.number(), - interruptible: yup.number(), - showFrames: yup.number(), - useMacroTriggerButton: yup.number(), - macroTriggerPin: yup.number().checkUsedPins(), - macroTriggerButton: yup.number(), - macroInputs: yup.array().of( - yup.object().shape({ - buttonMask: yup.number(), - duration: yup.number(), - waitDuration: yup.number(), - }), - ), - }), - ), - macroPin: yup.number().checkUsedPins(), - macroBoardLedEnabled: yup.number(), - InputMacroAddonEnabled: yup.number(), -}); - -const MACRO_INPUTS_MAX = 30; - -const defaultMacroInput = { buttonMask: 0, duration: 16666, waitDuration: 0 }; - -const defaultValues = { - macroList: Array(BUTTON_MASKS.length).fill({ - macroType: 1, - macroLabel: '', - enabled: 1, - exclusive: 1, - interruptible: 1, - showFrames: 1, - useMacroTriggerButton: 0, - macroTriggerPin: -1, - macroTriggerButton: 0, - macroInputs: [defaultMacroInput], - }), - macroPin: -1, - macroBoardLedEnabled: 1, - InputMacroAddonEnabled: 1, -}; - -const EMPTY_INPUT = null; - -const ONE_FRAME_US = 16666; - -const filterMacroInputs = (values) => { - let updated = false; - const newValues = { - ...values, - macroList: values.macroList.map((a) => { - a.macroInputs = a.macroInputs.filter((i) => { - const keep = i !== EMPTY_INPUT; - updated = updated && !keep; - return keep; - }); - return a; - }), - }; - return updated ? newValues : values; -}; - -const FormContext = () => { - const { values, setValues } = useFormikContext(); - const { setLoading } = useContext(AppContext); - - useEffect(() => { - async function fetchData() { - const options = await WebApi.getMacroAddonOptions(setLoading); - setValues(options); - } - fetchData(); - }, [setValues]); - - useEffect(() => { - setValues(filterMacroInputs(values)); - }, [values, setValues]); - - return null; -}; - -const ButtonMasksComponent = (props) => { - const { - id: key, - value, - onChange, - error, - isInvalid, - className, - buttonLabelType, - } = props; - return ( -
- - {BUTTON_MASKS.map((o, i2) => ( - - ))} - -
- ); -}; - -const tooltip = Double click to delete.; - -const MacroInputComponent = (props) => { - const { - value: { duration, buttonMask, waitDuration }, - buttonLabelType, - showFrames, - errors, - id: key, - translation: t, - setFieldValue, - } = props; - - return ( -
-
- { - setFieldValue( - `${key}.duration`, - e.target.value * (showFrames ? ONE_FRAME_US : 1000), - ); - }} - min={0} - /> -
- - {t( - showFrames - ? 'InputMacroAddon:input-macro-time-label-frames' - : 'InputMacroAddon:input-macro-time-label-ms', - )} - -
- {BUTTON_MASKS.map((mask, i1) => - buttonMask & mask.value ? ( - { - setFieldValue( - `${key}.buttonMask`, - (buttonMask ^ mask.value) | e.target.value, - ); - }} - error={errors?.buttonMask} - isInvalid={errors?.buttonMask} - translation={t} - buttonLabelType={buttonLabelType} - /> - ) : ( - <> - ), - )} -
- { - setFieldValue(`${key}.buttonMask`, buttonMask | e.target.value); - }} - error={errors?.buttonMask} - isInvalid={errors?.buttonMask} - translation={t} - buttonLabelType={buttonLabelType} - /> -
-
|
-
- { - setFieldValue( - `${key}.waitDuration`, - e.target.value * (showFrames ? ONE_FRAME_US : 1000), - ); - }} - min={0} - /> -
- - {t( - showFrames - ? 'InputMacroAddon:input-macro-time-label-frames' - : 'InputMacroAddon:input-macro-time-label-ms', - )} - - - - -
-
- ); -}; - -const MacroComponent = (props) => { - const { - value: { - macroLabel, - macroType, - macroInputs, - enabled, - exclusive, - interruptible, - showFrames, - useMacroTriggerButton, - macroTriggerButton, - macroTriggerPin, - }, - errors, - handleChange, - id: key, - translation: t, - index, - isMacroPinMapped, - buttonLabelType, - setFieldValue, - disabled, - } = props; - - const filteredMacroInputs = macroInputs.filter((i) => i !== EMPTY_INPUT); - return ( -
-
-
- { - setFieldValue(`${key}.enabled`, e.target.checked ? 1 : 0); - }} - isInvalid={false} - /> -
-
- -
-
- { - setFieldValue(`${key}.interruptible`, e.target.checked ? 1 : 0); - }} - isInvalid={false} - /> -
-
- { - setFieldValue(`${key}.exclusive`, e.target.checked ? 1 : 0); - }} - isInvalid={false} - /> -
-
- { - setFieldValue(`${key}.showFrames`, e.target.checked ? 1 : 0); - }} - isInvalid={false} - /> -
-
-
-
- { - setFieldValue( - `${key}.useMacroTriggerButton`, - e.target.checked ? 1 : 0, - ); - }} - isInvalid={false} - /> -
- {useMacroTriggerButton ? ( - <> -
- {t('InputMacroAddon:input-macro-macro-button-pin-plus')} -
-
- { - setFieldValue( - `${key}.macroTriggerButton`, - parseInt(e.target.value), - ); - }} - buttonLabelType={buttonLabelType} - translation={t} - /> -
- - ) : ( - <> -
- { - setFieldValue( - `${key}.macroTriggerPin`, - parseInt(e.target.value), - ); - }} - min={-1} - max={29} - /> -
-
- { - setFieldValue(`${key}.macroTriggerPin`, pin); - }} - /> -
- - )} -
- { - setFieldValue(`${key}.macroType`, parseInt(e.target.value)); - }} - > - {MACRO_TYPES.map((o, i2) => ( - - ))} - -
-
-
-
- {filteredMacroInputs.map((input, a) => ( - - ))} - {macroInputs.length < MACRO_INPUTS_MAX ? ( - - ) : ( - <> - )} -
-
-
- ); -}; - -export default function SettingsPage() { - const { buttonLabels } = useContext(AppContext); - const [saveMessage, setSaveMessage] = useState(''); - - const saveSettings = async (values) => { - const success = await WebApi.setMacroAddonOptions( - filterMacroInputs(values), - ); - setSaveMessage( - success - ? t('Common:saved-success-message') - : t('Common:saved-error-message'), - ); - }; - - const onSuccess = async (values) => await saveSettings(values); - - const { buttonLabelType, swapTpShareLabels } = buttonLabels; - const CURRENT_BUTTONS = getButtonLabels(buttonLabelType, swapTpShareLabels); - const buttonNames = omit(CURRENT_BUTTONS, ['label', 'value']); - - const { t } = useTranslation(''); - - const handleCheckbox = async (name, values) => { - values[name] = values[name] === 1 ? 0 : 1; - }; - - return ( - - {({ handleSubmit, handleChange, values, errors, setFieldValue }) => - ((window.values = values) && console.log('values', values)) || - console.log('errors', errors) || ( -
-
-
-
- -
- {values.macroPin === -1 && ( -
- {t('InputMacroAddon:input-macro-warning')} -
- )} -
- - { - handleCheckbox('InputMacroAddonEnabled', values); - handleChange(e); - }} - /> - { - handleCheckbox('macroBoardLedEnabled', values); - handleChange(e); - }} - /> -
-
-
- {values.macroList.map((macro, i) => ( - <> - - {values.macroList.length == i + 1 ? ( - <> - ) : ( -
- )} - - ))} -
-
-
- - {saveMessage ? ( - {saveMessage} - ) : null} - - -
- ) - } -
- ); -} diff --git a/www/src/Pages/InputMacroAddonPage.tsx b/www/src/Pages/InputMacroAddonPage.tsx new file mode 100644 index 000000000..3b89ee58d --- /dev/null +++ b/www/src/Pages/InputMacroAddonPage.tsx @@ -0,0 +1,675 @@ +import React, { useContext, useEffect, useMemo, useState } from 'react'; +import { AppContext } from '../Contexts/AppContext'; +import { Badge, Button, Col, Form, Nav, OverlayTrigger, Row, Tab, Table, Tooltip } from 'react-bootstrap'; +import { Formik, useFormikContext } from 'formik'; +import * as yup from 'yup'; +import { Trans, useTranslation } from 'react-i18next'; +import omit from 'lodash/omit'; +import zip from 'lodash/zip'; + +import Section from '../Components/Section'; +import CaptureButton from '../Components/CaptureButton'; +import FormControl from '../Components/FormControl'; +import WebApi from '../Services/WebApi'; +import { getButtonLabels, BUTTONS, BUTTON_MASKS } from '../Data/Buttons'; + +import usePinStore from '../Store/usePinStore'; + +type PinCell = [string, PinActionValues]; +type PinRow = [PinCell, PinCell]; +type PinList = [PinRow]; + +const MACRO_TYPES = [ + { label: 'InputMacroAddon:input-macro-type.press', value: 1 }, + { label: 'InputMacroAddon:input-macro-type.hold-repeat', value: 2 }, + { label: 'InputMacroAddon:input-macro-type.toggle', value: 3 }, +]; + +const schema = yup.object().shape({ + macroList: yup.array().of( + yup.object().shape({ + macroType: yup.number(), + macroLabel: yup.string(), + enabled: yup.number(), + exclusive: yup.number(), + interruptible: yup.number(), + showFrames: yup.number(), + useMacroTriggerButton: yup.number(), + macroTriggerButton: yup.number(), + macroInputs: yup.array().of( + yup.object().shape({ + buttonMask: yup.number(), + duration: yup.number(), + waitDuration: yup.number(), + }), + ), + }), + ), + macroBoardLedEnabled: yup.number(), +}); + +const MACRO_INPUTS_MAX = 30; + +const MACRO_LIMIT = 6; + +const defaultMacroInput = { buttonMask: 0, duration: 16666, waitDuration: 0 }; + +const defaultValues = { + macroList: Array(MACRO_LIMIT).fill({ + macroType: 1, + macroLabel: '', + enabled: 1, + exclusive: 1, + interruptible: 1, + showFrames: 1, + useMacroTriggerButton: 0, + macroTriggerButton: 0, + macroInputs: [defaultMacroInput], + }), + macroBoardLedEnabled: 1, +}; + +const EMPTY_INPUT = null; + +const ONE_FRAME_US = 16666; + +const filterMacroInputs = (values) => { + let updated = false; + const newValues = { + ...values, + macroList: values.macroList.map((a) => { + a.macroInputs = a.macroInputs.filter((i) => { + const keep = i !== EMPTY_INPUT; + updated = updated && !keep; + return keep; + }); + return a; + }), + }; + return updated ? newValues : values; +}; + +const FormContext = () => { + const { values, setValues } = useFormikContext(); + const { buttonLabels, setLoading } = useContext(AppContext); + const { buttonLabelType, swapTpShareLabels } = buttonLabels; + + const CURRENT_BUTTONS = getButtonLabels(buttonLabelType, swapTpShareLabels); + + useEffect(() => { + async function fetchData() { + const options = await WebApi.getMacroAddonOptions(setLoading); + setValues(options); + } + fetchData(); + }, [setValues]); +/* + useEffect(() => { + async function setData() { + await setValues(filterMacroInputs(values)); + } + setData(); + }, [values, setValues]);*/ + + return null; +}; + +const ButtonMasksComponent = (props) => { + const { + id: key, + value, + onChange, + error, + isInvalid, + className, + buttonLabelType, + buttonMasks + } = props; + return ( +
+ + {buttonMasks.map((o, i2) => ( + + ))} + +
+ ); +}; + +const tooltip = Double click to delete.; + +const MacroInputComponent = (props) => { + const { + value: { duration, buttonMask, waitDuration }, + buttonLabelType, + showFrames, + errors, + id: key, + translation: t, + setFieldValue, + } = props; + + return ( + + + + + { + setFieldValue( + `${key}.duration`, + e.target.value * (showFrames ? ONE_FRAME_US : 1000), + ); + }} + min={0} + /> + + {t( + showFrames + ? 'InputMacroAddon:input-macro-time-label-frames' + : 'InputMacroAddon:input-macro-time-label-ms', + )} + + + + + + {BUTTON_MASKS.map((mask, i1) => + buttonMask & mask.value ? ( + + { + setFieldValue( + `${key}.buttonMask`, + (buttonMask ^ mask.value) | e.target.value, + ); + }} + error={errors?.buttonMask} + isInvalid={errors?.buttonMask} + translation={t} + buttonLabelType={buttonLabelType} + buttonMasks={BUTTON_MASKS} + /> + ) : ( + <> + ), + )} + + { + setFieldValue(`${key}.buttonMask`, buttonMask | e.target.value); + }} + error={errors?.buttonMask} + isInvalid={errors?.buttonMask} + translation={t} + buttonLabelType={buttonLabelType} + buttonMasks={BUTTON_MASKS} + /> + + + + release and wait + + + + { + setFieldValue( + `${key}.waitDuration`, + e.target.value * (showFrames ? ONE_FRAME_US : 1000), + ); + }} + min={0} + /> + + + {t( + showFrames + ? 'InputMacroAddon:input-macro-time-label-frames' + : 'InputMacroAddon:input-macro-time-label-ms', + )} + + + + + + + + + + ); +}; + +const MacroComponent = (props) => { + const { + value: { + macroLabel, + macroType, + macroInputs, + enabled, + exclusive, + interruptible, + showFrames, + useMacroTriggerButton, + macroTriggerButton, + }, + errors, + handleChange, + id: key, + translation: t, + index, + buttonLabelType, + setFieldValue, + macroList, + } = props; + + const filteredMacroInputs = macroInputs.filter((i) => i !== EMPTY_INPUT); + return ( +
+ + + { + setFieldValue(`${key}.enabled`, e.target.checked ? 1 : 0); + }} + isInvalid={false} + /> + + + + + Macro Name: + + + + + + + + Macro Activation Type: + + + { + setFieldValue(`${key}.macroType`, parseInt(e.target.value)); + }} + > + {MACRO_TYPES.map((o, i2) => ( + + ))} + + + + +
+ + + { + setFieldValue(`${key}.interruptible`, e.target.checked ? 1 : 0); + }} + isInvalid={false} + /> + + + + + { + setFieldValue(`${key}.exclusive`, e.target.checked ? 1 : 0); + }} + isInvalid={false} + /> + + + + + { + setFieldValue( + `${key}.useMacroTriggerButton`, + e.target.checked ? 1 : 0, + ); + }} + isInvalid={false} + /> + + {useMacroTriggerButton == true && ( + + + {t('InputMacroAddon:input-macro-macro-button-pin-plus')} + + + { + setFieldValue( + `${key}.macroTriggerButton`, + parseInt(e.target.value), + ); + }} + buttonLabelType={buttonLabelType} + translation={t} + buttonMasks={BUTTON_MASKS.filter((b, i) => + macroList.find((m, macroIdx) => + index != macroIdx && m.useMacroTriggerButton && (m.macroTriggerButton === b.value) + ) === undefined + )} + /> + + + )} + +
+ + + { + setFieldValue(`${key}.showFrames`, e.target.checked ? 1 : 0); + }} + isInvalid={false} + /> + + + + {filteredMacroInputs.map((input, a) => ( + + ))} + + + + {macroInputs.length < MACRO_INPUTS_MAX ? ( + + ) : ( + <> + )} + + +
+ ); +}; + +export default function MacrosPage() { + const { buttonLabels, usedPins } = useContext(AppContext); + const [saveMessage, setSaveMessage] = useState(''); + + const saveSettings = async (values) => { + const success = await WebApi.setMacroAddonOptions( + filterMacroInputs(values), + ); + setSaveMessage( + success + ? t('Common:saved-success-message') + : t('Common:saved-error-message'), + ); + }; + + const onSuccess = async (values) => await saveSettings(values); + + const { buttonLabelType, swapTpShareLabels } = buttonLabels; + const CURRENT_BUTTONS = getButtonLabels(buttonLabelType, swapTpShareLabels); + const buttonNames = omit(CURRENT_BUTTONS, ['label', 'value']); + + const { t } = useTranslation(''); + + + const handleCheckbox = async (name, values) => { + values[name] = values[name] === 1 ? 0 : 1; + }; + + return ( + + {({ handleSubmit, handleChange, values, errors, setFieldValue }) => + ( +
+
+ + + + + + + + +
+ + + + + + + + + + + + + + + + {values.macroList.map((macro, i) => ( + + + + + + {macro.useMacroTriggerButton ==0 ? : + } + + + + ))} + +
#LabelTypeAssigned ToButtonActionsStatus
{i+1}{macro.macroLabel.length==0 && None}{macro.macroLabel.length>0 && macro.macroLabel.slice(0,32)}{macro.macroLabel.length>32 && "..."}{t(MACRO_TYPES.find((m) => m.value === macro.macroType).label)}{macro.useMacroTriggerButton==1 ? "Button" : "Pin"}---{`${BUTTON_MASKS.find((b) => b.value == macro.macroTriggerButton).label}`}{macro.macroInputs.length}{macro.enabled==true ? Enabled : Disabled}
+
+
+ + + + {t('InputMacroAddon:input-macro-sub-header')} + + + + + + { + handleCheckbox('macroBoardLedEnabled', values); + handleChange(e); + }} + /> + + +
+ + + + {saveMessage ? ( + {saveMessage} + ) : null} + + +
+
+ {values.macroList.map((macro, i) => ( + +
+ +
+ + {saveMessage ? ( + {saveMessage} + ) : null} +
+
+ ))} +
+
+
+ + +
+ ) + } +
+ ); +} diff --git a/www/src/Pages/SettingsPage.jsx b/www/src/Pages/SettingsPage.jsx index c880c6e0c..bd5e824f0 100644 --- a/www/src/Pages/SettingsPage.jsx +++ b/www/src/Pages/SettingsPage.jsx @@ -1,6 +1,6 @@ import React, { useContext, useEffect, useState } from 'react'; import { AppContext } from '../Contexts/AppContext'; -import { Button, Form, Modal, Nav, Row, Col, Tab, Tabs } from 'react-bootstrap'; +import { Button, Form, Modal, Nav, Row, Col, Tab } from 'react-bootstrap'; import KeyboardMapper, { validateMappings } from '../Components/KeyboardMapper'; import { Formik, useFormikContext } from 'formik'; import { NavLink } from 'react-router-dom'; @@ -888,8 +888,7 @@ export default function SettingsPage() { console.log('errors', errors) || (
- +