diff --git a/CMakeLists.txt b/CMakeLists.txt index 89ca2af4e..4411c258c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,6 +186,7 @@ src/display/ui/elements/GPWidget.cpp src/display/ui/elements/GPButton.cpp src/display/ui/elements/GPLever.cpp src/display/ui/elements/GPLabel.cpp +src/display/ui/elements/GPMenu.cpp src/display/ui/elements/GPScreen.cpp src/display/ui/elements/GPShape.cpp src/display/ui/elements/GPSprite.cpp @@ -196,6 +197,7 @@ src/display/ui/screens/PinViewerScreen.cpp src/display/ui/screens/RestartScreen.cpp src/display/ui/screens/StatsScreen.cpp src/display/ui/screens/SplashScreen.cpp +src/display/ui/screens/DisplaySaverScreen.cpp src/display/GPGFX.cpp src/display/GPGFX_UI.cpp src/drivermanager.cpp diff --git a/headers/addons/display.h b/headers/addons/display.h index 175b622bb..bdc3bf348 100644 --- a/headers/addons/display.h +++ b/headers/addons/display.h @@ -106,6 +106,14 @@ #define INPUT_HISTORY_ROW 7 #endif +#ifndef DISPLAY_SAVER_MODE +#define DISPLAY_SAVER_MODE DISPLAY_SAVER_DISPLAY_OFF +#endif + +#ifndef DISPLAY_LAYOUT_ORIENTATION +#define DISPLAY_LAYOUT_ORIENTATION BUTTON_ORIENTATION_DEFAULT +#endif + #ifndef DEFAULT_SPLASH #define DEFAULT_SPLASH \ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, \ @@ -188,8 +196,9 @@ class DisplayAddon : public GPAddon virtual std::string name() { return DisplayName; } void handleSystemRestart(GPEvent* e); + void handleMenuNavigation(GPEvent* e); private: - bool updateDisplayScreen(); + bool updateDisplayScreen(); void drawStatusBar(Gamepad*); void initMenu(char**); bool pressedUp(); @@ -209,11 +218,15 @@ class DisplayAddon : public GPAddon GPGFX* gpDisplay; GPScreen* gpScreen; DisplayMode currDisplayMode; - DisplayMode prevDisplayMode; + DisplayMode prevDisplayMode; bool turnOffWhenSuspended; uint32_t bootMode; - GPGFX_DisplayTypeOptions gpOptions; + DisplaySaverMode displaySaverMode; + + GPGFX_DisplayTypeOptions gpOptions; + + GamepadButtonMapping *mapMenuToggle; }; #endif diff --git a/headers/buttonlayouts.h b/headers/buttonlayouts.h index 807e2de74..0480b1658 100644 --- a/headers/buttonlayouts.h +++ b/headers/buttonlayouts.h @@ -129,20 +129,30 @@ {GP_ELEMENT_BTN_BUTTON, {119,33, 5, 5, 1, 1, GAMEPAD_MASK_S2, GP_SHAPE_ELLIPSE}}\ } +#define BUTTON_GROUP_VLXB_6B {\ + {GP_ELEMENT_BTN_BUTTON, {50, 31, 7, 7, 1, 1, GAMEPAD_MASK_B3, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {66, 24, 7, 7, 1, 1, GAMEPAD_MASK_B4, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {82, 24, 7, 7, 1, 1, GAMEPAD_MASK_R1, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {45, 47, 7, 7, 1, 1, GAMEPAD_MASK_B1, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {61, 40, 7, 7, 1, 1, GAMEPAD_MASK_B2, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {77, 40, 7, 7, 1, 1, GAMEPAD_MASK_R2, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {119,33, 5, 5, 1, 1, GAMEPAD_MASK_S2, GP_SHAPE_ELLIPSE}}\ +} + #define BUTTON_GROUP_FIGHTBOARD {\ - {GP_ELEMENT_BTN_BUTTON, {69, 27, 7, 7, 1, 1, GAMEPAD_MASK_B3, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {86, 18, 7, 7, 1, 1, GAMEPAD_MASK_B4, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {103,18, 7, 7, 1, 1, GAMEPAD_MASK_R1, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {120,18, 7, 7, 1, 1, GAMEPAD_MASK_L1, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {69, 43, 7, 7, 1, 1, GAMEPAD_MASK_B1, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {86, 35, 7, 7, 1, 1, GAMEPAD_MASK_B2, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {103,35, 7, 7, 1, 1, GAMEPAD_MASK_R2, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {120,35, 7, 7, 1, 1, GAMEPAD_MASK_L2, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {84, 47, 3, 3, 1, 1, GAMEPAD_MASK_L3, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {94, 47, 3, 3, 1, 1, GAMEPAD_MASK_S1, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {103,47, 3, 3, 1, 1, GAMEPAD_MASK_A1, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {112,47, 3, 3, 1, 1, GAMEPAD_MASK_S2, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {122,47, 3, 3, 1, 1, GAMEPAD_MASK_R3, GP_SHAPE_ELLIPSE}}\ + {GP_ELEMENT_BTN_BUTTON, {67, 27, 7, 7, 1, 1, GAMEPAD_MASK_B3, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {84, 18, 7, 7, 1, 1, GAMEPAD_MASK_B4, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {101,18, 7, 7, 1, 1, GAMEPAD_MASK_R1, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {118,18, 7, 7, 1, 1, GAMEPAD_MASK_L1, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {67, 43, 7, 7, 1, 1, GAMEPAD_MASK_B1, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {84, 35, 7, 7, 1, 1, GAMEPAD_MASK_B2, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {101,35, 7, 7, 1, 1, GAMEPAD_MASK_R2, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {118,35, 7, 7, 1, 1, GAMEPAD_MASK_L2, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {82, 47, 3, 3, 1, 1, GAMEPAD_MASK_L3, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {92, 47, 3, 3, 1, 1, GAMEPAD_MASK_S1, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {101,47, 3, 3, 1, 1, GAMEPAD_MASK_A1, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {110,47, 3, 3, 1, 1, GAMEPAD_MASK_S2, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {120,47, 3, 3, 1, 1, GAMEPAD_MASK_R3, GP_SHAPE_ELLIPSE}}\ } #define BUTTON_GROUP_VEWLIX7 {\ @@ -166,6 +176,15 @@ {GP_ELEMENT_BTN_BUTTON, {111,46, 8, 8, 1, 1, GAMEPAD_MASK_L2, GP_SHAPE_ELLIPSE}}\ } +#define BUTTON_GROUP_SEGA_2P_6B {\ + {GP_ELEMENT_BTN_BUTTON, {57, 34, 8, 8, 1, 1, GAMEPAD_MASK_B3, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {75, 24, 8, 8, 1, 1, GAMEPAD_MASK_B4, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {93, 24, 8, 8, 1, 1, GAMEPAD_MASK_R1, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {57, 52, 8, 8, 1, 1, GAMEPAD_MASK_B1, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {75, 42, 8, 8, 1, 1, GAMEPAD_MASK_B2, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {93, 42, 8, 8, 1, 1, GAMEPAD_MASK_R2, GP_SHAPE_ELLIPSE}}\ +} + #define BUTTON_GROUP_NOIR8 {\ {GP_ELEMENT_BTN_BUTTON, {57, 33, 8, 8, 1, 1, GAMEPAD_MASK_B3, GP_SHAPE_ELLIPSE}},\ {GP_ELEMENT_BTN_BUTTON, {75, 24, 8, 8, 1, 1, GAMEPAD_MASK_B4, GP_SHAPE_ELLIPSE}},\ @@ -178,14 +197,14 @@ } #define BUTTON_GROUP_CAPCOM {\ - {GP_ELEMENT_BTN_BUTTON, {64, 28, 8, 8, 1, 1, GAMEPAD_MASK_B3, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {82, 28, 8, 8, 1, 1, GAMEPAD_MASK_B4, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {100,28, 8, 8, 1, 1, GAMEPAD_MASK_R1, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {118,28, 8, 8, 1, 1, GAMEPAD_MASK_L1, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {64, 46, 8, 8, 1, 1, GAMEPAD_MASK_B1, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {82, 46, 8, 8, 1, 1, GAMEPAD_MASK_B2, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {100,46, 8, 8, 1, 1, GAMEPAD_MASK_R2, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {118,46, 8, 8, 1, 1, GAMEPAD_MASK_L2, GP_SHAPE_ELLIPSE}}\ + {GP_ELEMENT_BTN_BUTTON, {62, 28, 8, 8, 1, 1, GAMEPAD_MASK_B3, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {80, 28, 8, 8, 1, 1, GAMEPAD_MASK_B4, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {98, 28, 8, 8, 1, 1, GAMEPAD_MASK_R1, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {116,28, 8, 8, 1, 1, GAMEPAD_MASK_L1, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {62, 46, 8, 8, 1, 1, GAMEPAD_MASK_B1, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {80, 46, 8, 8, 1, 1, GAMEPAD_MASK_B2, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {98, 46, 8, 8, 1, 1, GAMEPAD_MASK_R2, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {116,46, 8, 8, 1, 1, GAMEPAD_MASK_L2, GP_SHAPE_ELLIPSE}}\ } #define BUTTON_GROUP_CAPCOM6 {\ @@ -209,25 +228,25 @@ } #define BUTTON_GROUP_WASD_BUTTONS {\ - {GP_ELEMENT_BTN_BUTTON, {69, 28, 7, 7, 1, 1, GAMEPAD_MASK_B3, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {86, 24, 7, 7, 1, 1, GAMEPAD_MASK_B4, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {103,24, 7, 7, 1, 1, GAMEPAD_MASK_R1, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {120,28, 7, 7, 1, 1, GAMEPAD_MASK_L1, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {63, 45, 7, 7, 1, 1, GAMEPAD_MASK_B1, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {80, 41, 7, 7, 1, 1, GAMEPAD_MASK_B2, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {97, 41, 7, 7, 1, 1, GAMEPAD_MASK_R2, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {114,45, 7, 7, 1, 1, GAMEPAD_MASK_L2, GP_SHAPE_ELLIPSE}}\ + {GP_ELEMENT_BTN_BUTTON, {67, 28, 7, 7, 1, 1, GAMEPAD_MASK_B3, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {84, 24, 7, 7, 1, 1, GAMEPAD_MASK_B4, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {101,24, 7, 7, 1, 1, GAMEPAD_MASK_R1, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {118,28, 7, 7, 1, 1, GAMEPAD_MASK_L1, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {61, 45, 7, 7, 1, 1, GAMEPAD_MASK_B1, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {78, 41, 7, 7, 1, 1, GAMEPAD_MASK_B2, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {95, 41, 7, 7, 1, 1, GAMEPAD_MASK_R2, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {112,45, 7, 7, 1, 1, GAMEPAD_MASK_L2, GP_SHAPE_ELLIPSE}}\ } #define BUTTON_GROUP_ARCADE_BUTTONS {\ - {GP_ELEMENT_BTN_BUTTON, {64, 28, 8, 8, 1, 1, GAMEPAD_MASK_B3, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {82, 24, 8, 8, 1, 1, GAMEPAD_MASK_B4, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {100,24, 8, 8, 1, 1, GAMEPAD_MASK_R1, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {118,28, 8, 8, 1, 1, GAMEPAD_MASK_L1, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {59, 46, 8, 8, 1, 1, GAMEPAD_MASK_B1, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {77, 42, 8, 8, 1, 1, GAMEPAD_MASK_B2, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {95, 42, 8, 8, 1, 1, GAMEPAD_MASK_R2, GP_SHAPE_ELLIPSE}},\ - {GP_ELEMENT_BTN_BUTTON, {113,46, 8, 8, 1, 1, GAMEPAD_MASK_L2, GP_SHAPE_ELLIPSE}}\ + {GP_ELEMENT_BTN_BUTTON, {62, 28, 8, 8, 1, 1, GAMEPAD_MASK_B3, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {80, 24, 8, 8, 1, 1, GAMEPAD_MASK_B4, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {98, 24, 8, 8, 1, 1, GAMEPAD_MASK_R1, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {116,28, 8, 8, 1, 1, GAMEPAD_MASK_L1, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {57, 46, 8, 8, 1, 1, GAMEPAD_MASK_B1, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {75, 42, 8, 8, 1, 1, GAMEPAD_MASK_B2, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {93, 42, 8, 8, 1, 1, GAMEPAD_MASK_R2, GP_SHAPE_ELLIPSE}},\ + {GP_ELEMENT_BTN_BUTTON, {111,46, 8, 8, 1, 1, GAMEPAD_MASK_L2, GP_SHAPE_ELLIPSE}}\ } #define BUTTON_GROUP_STICKLESS13A {\ diff --git a/headers/display/GPGFX.h b/headers/display/GPGFX.h index 99b964803..f1aea68a2 100644 --- a/headers/display/GPGFX.h +++ b/headers/display/GPGFX.h @@ -12,7 +12,7 @@ class GPGFX { void init(GPGFX_DisplayTypeOptions options); - GPGFX_DisplayTypeOptions getAvailableDisplay(); + GPGFX_DisplayTypeOptions getAvailableDisplay(GPGFX_DisplayType displayType); GPGFX_DisplayBase* getDriver() { return displayDriver; } @@ -20,6 +20,7 @@ class GPGFX { void clearScreen(); void render(); + uint32_t getPixel(uint16_t x, uint16_t y); void drawPixel(uint16_t x, uint16_t y, uint32_t color); void drawText(uint16_t x, uint16_t y, std::string text, uint8_t invert = 0); void drawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint32_t color, uint8_t filled); @@ -27,9 +28,11 @@ class GPGFX { void drawEllipse(uint16_t x, uint16_t y, uint32_t radiusX, uint32_t radiusY, uint32_t color, uint8_t filled); void drawRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t color, uint8_t filled, double rotationAngle = 0); void drawPolygon(uint16_t x, uint16_t y, uint16_t radius, uint16_t sides, uint32_t color, uint8_t filled, double rotation = 0); - void drawSprite(uint8_t* spriteData, uint16_t width, uint16_t height, uint16_t pitch, uint16_t x, uint16_t y, uint8_t priority); + void drawSprite(uint8_t* spriteData, uint16_t width, uint16_t height, uint16_t pitch, uint16_t x, uint16_t y, uint8_t priority, double scale = 1.0); private: GPGFX_DisplayBase* displayDriver = nullptr; + + bool detectDisplay(GPGFX_DisplayTypeOptions* display, GPGFX_DisplayType displayType); }; #endif diff --git a/headers/display/GPGFX_UI_screens.h b/headers/display/GPGFX_UI_screens.h index cb602d3e8..99d35e437 100644 --- a/headers/display/GPGFX_UI_screens.h +++ b/headers/display/GPGFX_UI_screens.h @@ -6,6 +6,7 @@ enum DisplayMode { BUTTONS, SPLASH, PIN_VIEWER, + DISPLAY_SAVER, STATS, MAIN_MENU, RESTART @@ -13,6 +14,7 @@ enum DisplayMode { #include "ui/screens/ButtonLayoutScreen.h" #include "ui/screens/ConfigScreen.h" +#include "ui/screens/DisplaySaverScreen.h" #include "ui/screens/MainMenuScreen.h" #include "ui/screens/PinViewerScreen.h" #include "ui/screens/RestartScreen.h" diff --git a/headers/display/GPGFX_UI_types.h b/headers/display/GPGFX_UI_types.h index d31415608..82a643039 100644 --- a/headers/display/GPGFX_UI_types.h +++ b/headers/display/GPGFX_UI_types.h @@ -5,10 +5,13 @@ #include #include -typedef struct { +typedef struct MenuEntry { std::string label; uint8_t* icon; + std::vector* submenu; + std::function currentValue; std::function action; + int32_t optionValue = -1; } MenuEntry; typedef struct { diff --git a/headers/display/GPGFX_UI_widgets.h b/headers/display/GPGFX_UI_widgets.h index 22b85b016..25a61a764 100644 --- a/headers/display/GPGFX_UI_widgets.h +++ b/headers/display/GPGFX_UI_widgets.h @@ -8,6 +8,7 @@ #include "ui/elements/GPScreen.h" #include "ui/elements/GPShape.h" #include "ui/elements/GPSprite.h" +#include "ui/elements/GPMenu.h" #include "GPGFX_UI_screens.h" diff --git a/headers/display/ui/elements/GPButton.h b/headers/display/ui/elements/GPButton.h index 901f7a99c..d70b7963e 100644 --- a/headers/display/ui/elements/GPButton.h +++ b/headers/display/ui/elements/GPButton.h @@ -23,7 +23,7 @@ class GPButton : public GPWidget { double _angle = 0; double _angleEnd = 0; bool _closed = false; - int16_t _inputMask = -1; + int32_t _inputMask = -1; bool _inputDirection = false; GPElement _inputType = GP_ELEMENT_BTN_BUTTON; GPShape_Type _shape = GP_SHAPE_ELLIPSE; diff --git a/headers/display/ui/elements/GPMenu.h b/headers/display/ui/elements/GPMenu.h new file mode 100644 index 000000000..4c56fdff6 --- /dev/null +++ b/headers/display/ui/elements/GPMenu.h @@ -0,0 +1,31 @@ +#ifndef _GPMENU_H_ +#define _GPMENU_H_ + +#include "GPWidget.h" +#include "GPShape.h" +#include "GPGFX_UI_types.h" + +class GPMenu : public GPShape { + public: + void draw(); + GPMenu* setMenuSize(uint16_t sizeX, uint16_t sizeY) { this->menuSizeX = sizeX; this->menuSizeY = sizeY; return this; } + + uint16_t getDataSize() { return this->menuEntryData->size(); }; + + void setIndex(uint16_t pos) { this->menuIndex = pos; }; + uint16_t getIndex() { return this->menuIndex; }; + + void setMenuData(std::vector* menu) { this->menuEntryData = menu; }; + void setMenuTitle(std::string title) { this->menuTitle = title; }; + private: + uint16_t menuSizeX = 0; + uint16_t menuSizeY = 0; + + uint16_t menuLines = 15; + uint16_t menuIndex = 0; + + std::vector* menuEntryData; + std::string menuTitle; +}; + +#endif \ No newline at end of file diff --git a/headers/display/ui/elements/GPWidget.h b/headers/display/ui/elements/GPWidget.h index 1f3c399a8..133438880 100644 --- a/headers/display/ui/elements/GPWidget.h +++ b/headers/display/ui/elements/GPWidget.h @@ -28,6 +28,9 @@ class GPWidget : public GPGFX_UI { double getScaleX() { return ((double)(this->getViewport().right - this->getViewport().left) / (double)(getRenderer()->getDriver()->getMetrics()->width)); } double getScaleY() { return ((double)(this->getViewport().bottom - this->getViewport().top) / (double)(getRenderer()->getDriver()->getMetrics()->height)); } + + void setVisibility(bool visible) { this->_visibility = visible; } + bool getVisibility() { return this->_visibility; } protected: uint16_t x = 0; uint16_t y = 0; @@ -36,6 +39,7 @@ class GPWidget : public GPGFX_UI { uint16_t fillColor = 0; uint16_t _ID; uint16_t _priority = 0; + bool _visibility = true; GPViewport _viewport; }; diff --git a/headers/display/ui/screens/ButtonLayoutScreen.h b/headers/display/ui/screens/ButtonLayoutScreen.h index 985da94da..1fafc6d62 100644 --- a/headers/display/ui/screens/ButtonLayoutScreen.h +++ b/headers/display/ui/screens/ButtonLayoutScreen.h @@ -7,6 +7,9 @@ #include #include #include +#include +#include +#include #include "layoutmanager.h" #include "GPGFX_UI_widgets.h" #include "GPGFX_UI_layouts.h" @@ -158,9 +161,18 @@ class ButtonLayoutScreen : public GPScreen { uint8_t prevProfileNumber = 0; ButtonLayoutParamsLeft prevLeftOptions; ButtonLayoutParamsRight prevRightOptions; - + ButtonLayoutOrientation prevOrientation; + bool macroEnabled; + bool showInputMode = true; + bool showTurboMode = true; + bool showDpadMode = true; + bool showSocdMode = true; + bool showMacroMode = true; + bool showProfileMode = false; + void trim(std::string &s); + uint16_t map(uint16_t x, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max); void processInputHistory(); bool compareCustomLayouts(); diff --git a/headers/display/ui/screens/DisplaySaverScreen.h b/headers/display/ui/screens/DisplaySaverScreen.h new file mode 100644 index 000000000..cf388905c --- /dev/null +++ b/headers/display/ui/screens/DisplaySaverScreen.h @@ -0,0 +1,62 @@ +#ifndef _DISPLAYSAVERSCREEN_H_ +#define _DISPLAYSAVERSCREEN_H_ + +#include +#include "GPGFX_UI_widgets.h" +#include "bitmaps.h" + +const uint8_t SCREEN_WIDTH = 128; +const uint8_t SCREEN_HEIGHT = 64; + +class DisplaySaverScreen : public GPScreen { + public: + DisplaySaverScreen() {} + DisplaySaverScreen(GPGFX* renderer) { setRenderer(renderer); } + virtual int8_t update(); + virtual void init(); + virtual void shutdown(); + protected: + virtual void drawScreen(); + uint16_t prevButtonState = 0; + DisplaySaverMode displaySaverMode; + + // snow screen + uint8_t snowflakeSpeeds[SCREEN_WIDTH][SCREEN_HEIGHT] = {}; + int8_t snowflakeDrift[SCREEN_WIDTH][SCREEN_HEIGHT] = {}; + void initSnowScene(); + void drawSnowScene(); + + // bounce + uint16_t bounceSpriteX = 0; + uint16_t bounceSpriteY = 0; + uint16_t bounceSpriteWidth = 128; + uint16_t bounceSpriteHeight = 35; + double bounceSpriteVelocityX = 1; + double bounceSpriteVelocityY = 1; + double bounceScale = 0.5; + void drawBounceScene(); + + // pipes + void drawPipeScene(); + + // toaster + struct ToastParams { + uint8_t* image; + uint16_t width; + uint16_t height; + double scale; + int16_t x; + int16_t y; + int16_t dx; + int16_t dy; + }; + + std::vector toasters; + uint16_t numberOfToasters = 10; + uint16_t toasterSpriteWidth = 43; + uint16_t toasterSpriteHeight = 39; + void initToasters(); + void drawToasterScene(); +}; + +#endif \ No newline at end of file diff --git a/headers/display/ui/screens/MainMenuScreen.h b/headers/display/ui/screens/MainMenuScreen.h index f0458674a..2e9195d53 100644 --- a/headers/display/ui/screens/MainMenuScreen.h +++ b/headers/display/ui/screens/MainMenuScreen.h @@ -3,6 +3,38 @@ #include "GPGFX_UI_widgets.h" #include "GPGFX_UI_types.h" +#include "enums.pb.h" +#include "AnimationStation.hpp" +#include "eventmanager.h" + +#define INPUT_MODE_XINPUT_NAME "XInput" +#define INPUT_MODE_SWITCH_NAME "Nintendo Switch" +#define INPUT_MODE_PS3_NAME "PS3" +#define INPUT_MODE_KEYBOARD_NAME "Keyboard" +#define INPUT_MODE_PS4_NAME "PS4" +#define INPUT_MODE_XBONE_NAME "Xbox One" +#define INPUT_MODE_MDMINI_NAME "Sega Genesis Mini" +#define INPUT_MODE_NEOGEO_NAME "NEOGEO mini" +#define INPUT_MODE_PCEMINI_NAME "PC Engine Mini" +#define INPUT_MODE_EGRET_NAME "EGRET II mini" +#define INPUT_MODE_ASTRO_NAME "ASTROCITY Mini" +#define INPUT_MODE_PSCLASSIC_NAME "Playstation Classic" +#define INPUT_MODE_XBOXORIGINAL_NAME "Original Xbox" +#define INPUT_MODE_PS5_NAME "PS5" +#define INPUT_MODE_GENERIC_NAME "Generic HID" +#define INPUT_MODE_CONFIG_NAME "Web Config" + +#define SOCD_MODE_UP_PRIORITY_NAME "Up Priority" +#define SOCD_MODE_NEUTRAL_NAME "Neutral" +#define SOCD_MODE_SECOND_INPUT_PRIORITY_NAME "Last Win" +#define SOCD_MODE_FIRST_INPUT_PRIORITY_NAME "First Win" +#define SOCD_MODE_BYPASS_NAME "Off" + +#define DPAD_MODE_DIGITAL_NAME "D-Pad" +#define DPAD_MODE_LEFT_ANALOG_NAME "Left Analog" +#define DPAD_MODE_RIGHT_ANALOG_NAME "Right Analog" + +#define MAIN_MENU_NAME "GP2040-CE Mini Menu" class MainMenuScreen : public GPScreen { public: @@ -12,6 +44,30 @@ class MainMenuScreen : public GPScreen { virtual int8_t update(); virtual void init(); virtual void shutdown(); + + void testMenu() {} + void saveAndExit(); + int32_t modeValue(); + + void selectInputMode(); + int32_t currentInputMode(); + + void selectDPadMode(); + int32_t currentDpadMode(); + + void selectSOCDMode(); + int32_t currentSOCDMode(); + + void selectProfile(); + int32_t currentProfile(); + + void selectFocusMode(); + int32_t currentFocusMode(); + + void selectTurboMode(); + int32_t currentTurboMode(); + + void updateMenuNavigation(GpioAction action); protected: virtual void drawScreen(); private: @@ -19,7 +75,84 @@ class MainMenuScreen : public GPScreen { bool isPressed = false; uint32_t checkDebounce; std::vector* currentMenu; + std::vector* previousMenu; uint16_t prevButtonState = 0; + Mask_t prevValues; + GPMenu* gpMenu; + + bool screenIsPrompting = false; + bool promptChoice = false; + + int8_t exitToScreenBeforePrompt = -1; + int8_t exitToScreen = -1; + + GamepadButtonMapping *mapMenuUp; + GamepadButtonMapping *mapMenuDown; + GamepadButtonMapping *mapMenuLeft; + GamepadButtonMapping *mapMenuRight; + GamepadButtonMapping *mapMenuSelect; + GamepadButtonMapping *mapMenuBack; + GamepadButtonMapping *mapMenuToggle; + + void saveOptions(); + void resetOptions(); + bool changeRequiresReboot = false; + bool changeRequiresSave = false; + + #define INPUT_MODE_ENTRIES(name, value) {name##_NAME, NULL, nullptr, std::bind(&MainMenuScreen::currentInputMode, this), std::bind(&MainMenuScreen::selectInputMode, this), value}, + #define DPAD_MODE_ENTRIES(name, value) {name##_NAME, NULL, nullptr, std::bind(&MainMenuScreen::currentDpadMode, this), std::bind(&MainMenuScreen::selectDPadMode, this), value}, + #define SOCD_MODE_ENTRIES(name, value) {name##_NAME, NULL, nullptr, std::bind(&MainMenuScreen::currentSOCDMode, this), std::bind(&MainMenuScreen::selectSOCDMode, this), value}, + + std::vector inputModeMenu = { + InputMode_VALUELIST(INPUT_MODE_ENTRIES) + }; + InputMode prevInputMode; + InputMode updateInputMode; + + std::vector dpadModeMenu = { + DpadMode_VALUELIST(DPAD_MODE_ENTRIES) + }; + DpadMode prevDpadMode; + DpadMode updateDpadMode; + + std::vector socdModeMenu = { + SOCDMode_VALUELIST(SOCD_MODE_ENTRIES) + }; + SOCDMode prevSocdMode; + SOCDMode updateSocdMode; + + std::vector profilesMenu = {}; + uint8_t prevProfile; + uint8_t updateProfile; + + std::vector focusModeMenu = { + {"Off", NULL, nullptr, std::bind(&MainMenuScreen::currentFocusMode, this), std::bind(&MainMenuScreen::selectFocusMode, this), 0}, + {"On", NULL, nullptr, std::bind(&MainMenuScreen::currentFocusMode, this), std::bind(&MainMenuScreen::selectFocusMode, this), 1}, + }; + bool prevFocus; + bool updateFocus; + + std::vector turboModeMenu = { + {"Off", NULL, nullptr, std::bind(&MainMenuScreen::currentTurboMode, this), std::bind(&MainMenuScreen::selectTurboMode, this), 0}, + {"On", NULL, nullptr, std::bind(&MainMenuScreen::currentTurboMode, this), std::bind(&MainMenuScreen::selectTurboMode, this), 1}, + }; + bool prevTurbo; + bool updateTurbo; + + std::vector mainMenu = { + {"Input Mode", NULL, &inputModeMenu, std::bind(&MainMenuScreen::modeValue, this), std::bind(&MainMenuScreen::testMenu, this)}, + {"D-Pad Mode", NULL, &dpadModeMenu, std::bind(&MainMenuScreen::modeValue, this), std::bind(&MainMenuScreen::testMenu, this)}, + {"SOCD Mode", NULL, &socdModeMenu, std::bind(&MainMenuScreen::modeValue, this), std::bind(&MainMenuScreen::testMenu, this)}, + {"Profile", NULL, &profilesMenu, std::bind(&MainMenuScreen::modeValue, this), std::bind(&MainMenuScreen::testMenu, this)}, + {"Focus Mode", NULL, &focusModeMenu, std::bind(&MainMenuScreen::modeValue, this), std::bind(&MainMenuScreen::testMenu, this)}, + {"Turbo", NULL, &turboModeMenu, std::bind(&MainMenuScreen::modeValue, this), std::bind(&MainMenuScreen::testMenu, this)}, + {"Save & Exit",NULL, &saveMenu, std::bind(&MainMenuScreen::modeValue, this), std::bind(&MainMenuScreen::testMenu, this)}, + }; + + std::vector saveMenu = { + {"Yes", NULL, nullptr, std::bind(&MainMenuScreen::modeValue, this), std::bind(&MainMenuScreen::saveAndExit, this), 1}, + {"No", NULL, nullptr, std::bind(&MainMenuScreen::modeValue, this), std::bind(&MainMenuScreen::testMenu, this), 0}, + }; }; #endif \ No newline at end of file diff --git a/headers/eventmanager.h b/headers/eventmanager.h index 58cb10b3f..3e224acf9 100644 --- a/headers/eventmanager.h +++ b/headers/eventmanager.h @@ -14,8 +14,11 @@ #include "GPEvent.h" #include "GPGamepadEvent.h" #include "GPEncoderEvent.h" +#include "GPMenuNavigateEvent.h" #include "GPProfileEvent.h" #include "GPRestartEvent.h" +#include "GPStorageSaveEvent.h" +#include "GPSystemRebootEvent.h" #include "GPUSBHostEvent.h" #define EVENTMGR EventManager::getInstance() @@ -33,6 +36,9 @@ class EventManager { return instance; } + void init(); + void clearEventHandlers(); + void registerEventHandler(GPEventType eventType, EventFunction handler); void triggerEvent(GPEvent* event); private: diff --git a/headers/events/GPMenuNavigateEvent.h b/headers/events/GPMenuNavigateEvent.h new file mode 100644 index 000000000..5034a07da --- /dev/null +++ b/headers/events/GPMenuNavigateEvent.h @@ -0,0 +1,21 @@ +#ifndef _GPMENUNAVIGATEEVENT_H_ +#define _GPMENUNAVIGATEEVENT_H_ + +#include "system.h" + +class GPMenuNavigateEvent : public GPEvent { + public: + GPMenuNavigateEvent() {} + GPMenuNavigateEvent(GpioAction action) { + this->menuAction = action; + } + ~GPMenuNavigateEvent() {} + + GPEventType eventType() { return this->_eventType; } + + GpioAction menuAction; + private: + GPEventType _eventType = GP_EVENT_MENU_NAVIGATE; +}; + +#endif \ No newline at end of file diff --git a/headers/events/GPStorageSaveEvent.h b/headers/events/GPStorageSaveEvent.h new file mode 100644 index 000000000..f81c7f2a0 --- /dev/null +++ b/headers/events/GPStorageSaveEvent.h @@ -0,0 +1,23 @@ +#ifndef _GPSTORAGESAVEEVENT_H_ +#define _GPSTORAGESAVEEVENT_H_ + +#include "system.h" + +class GPStorageSaveEvent : public GPEvent { + public: + GPStorageSaveEvent() {} + GPStorageSaveEvent(bool force, bool restart = false) { + this->forceSave = force; + this->restartAfterSave = restart; + } + ~GPStorageSaveEvent() {} + + GPEventType eventType() { return this->_eventType; } + + bool forceSave = false; + bool restartAfterSave = false; + private: + GPEventType _eventType = GP_EVENT_STORAGE_SAVE; +}; + +#endif \ No newline at end of file diff --git a/headers/events/GPSystemRebootEvent.h b/headers/events/GPSystemRebootEvent.h new file mode 100644 index 000000000..9846549c0 --- /dev/null +++ b/headers/events/GPSystemRebootEvent.h @@ -0,0 +1,21 @@ +#ifndef _GPSYSTEMREBOOTEVENT_H_ +#define _GPSYSTEMREBOOTEVENT_H_ + +#include "system.h" + +class GPSystemRebootEvent : public GPEvent { + public: + GPSystemRebootEvent() {} + GPSystemRebootEvent(System::BootMode mode) { + this->bootMode = mode; + } + ~GPSystemRebootEvent() {} + + GPEventType eventType() { return this->_eventType; } + + System::BootMode bootMode = System::BootMode::DEFAULT; + private: + GPEventType _eventType = GP_EVENT_SYSTEM_REBOOT; +}; + +#endif \ No newline at end of file diff --git a/headers/gp2040.h b/headers/gp2040.h index 574f79057..dfc89a22e 100644 --- a/headers/gp2040.h +++ b/headers/gp2040.h @@ -11,6 +11,7 @@ // GP2040 Classes #include "gamepad.h" #include "addonmanager.h" +#include "eventmanager.h" #include "gpdriver.h" #include "pico/types.h" @@ -75,6 +76,13 @@ class GP2040 { // input mask, action std::map bootActions; + + bool saveRequested = false; + bool saveSuccessful = false; + void handleStorageSave(GPEvent* e); + + bool rebootRequested = false; + void handleSystemReboot(GPEvent* e); }; #endif diff --git a/headers/interfaces/i2c/displaybase.h b/headers/interfaces/i2c/displaybase.h index 2b620e9dd..f5ac873fc 100644 --- a/headers/interfaces/i2c/displaybase.h +++ b/headers/interfaces/i2c/displaybase.h @@ -18,6 +18,8 @@ class GPGFX_DisplayBase : public I2CDeviceBase { virtual void clear() {} + virtual uint32_t getPixel(uint8_t x, uint8_t y) {} + virtual void drawPixel(uint8_t x, uint8_t y, uint32_t color) {} virtual void drawText(uint8_t x, uint8_t y, std::string text, uint8_t invert = 0) {} @@ -32,7 +34,7 @@ class GPGFX_DisplayBase : public I2CDeviceBase { virtual void drawPolygon(uint16_t x, uint16_t y, uint16_t radius, uint16_t sides, uint32_t color, uint8_t filled, double rotation = 0) {} - virtual void drawSprite(uint8_t* spriteData, uint16_t width, uint16_t height, uint16_t pitch, uint16_t x, uint16_t y, uint8_t priority) {} + virtual void drawSprite(uint8_t* spriteData, uint16_t width, uint16_t height, uint16_t pitch, uint16_t x, uint16_t y, uint8_t priority, double scale) {} virtual void drawBuffer(uint8_t *pBuffer) {} diff --git a/headers/interfaces/i2c/ssd1306/obd_ssd1306.h b/headers/interfaces/i2c/ssd1306/obd_ssd1306.h index 38a62a7c3..9386fbcc0 100644 --- a/headers/interfaces/i2c/ssd1306/obd_ssd1306.h +++ b/headers/interfaces/i2c/ssd1306/obd_ssd1306.h @@ -17,6 +17,8 @@ class GPGFX_OBD_SSD1306 : public GPGFX_DisplayBase { void clear(); + uint32_t getPixel(uint8_t x, uint8_t y); + void drawPixel(uint8_t x, uint8_t y, uint32_t color); void drawText(uint8_t x, uint8_t y, std::string text, uint8_t invert = 0); @@ -27,7 +29,7 @@ class GPGFX_OBD_SSD1306 : public GPGFX_DisplayBase { void drawRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t color, uint8_t filled, double rotationAngle = 0); - void drawSprite(uint8_t* spriteData, uint16_t width, uint16_t height, uint16_t pitch, uint16_t x, uint16_t y, uint8_t priority); + void drawSprite(uint8_t* spriteData, uint16_t width, uint16_t height, uint16_t pitch, uint16_t x, uint16_t y, uint8_t priority, double scale = 1.0); void drawBuffer(uint8_t *pBuffer); diff --git a/headers/interfaces/i2c/ssd1306/tiny_ssd1306.h b/headers/interfaces/i2c/ssd1306/tiny_ssd1306.h index 6e6633e14..0e4f05a98 100644 --- a/headers/interfaces/i2c/ssd1306/tiny_ssd1306.h +++ b/headers/interfaces/i2c/ssd1306/tiny_ssd1306.h @@ -16,6 +16,8 @@ class GPGFX_TinySSD1306 : public GPGFX_DisplayBase { void clear(); + uint32_t getPixel(uint8_t x, uint8_t y); + void drawPixel(uint8_t x, uint8_t y, uint32_t color); void drawText(uint8_t x, uint8_t y, std::string text, uint8_t invert = 0); @@ -30,7 +32,7 @@ class GPGFX_TinySSD1306 : public GPGFX_DisplayBase { void drawPolygon(uint16_t x, uint16_t y, uint16_t radius, uint16_t sides, uint32_t color, uint8_t filled, double rotation = 0); - void drawSprite(uint8_t* spriteData, uint16_t width, uint16_t height, uint16_t pitch, uint16_t x, uint16_t y, uint8_t priority); + void drawSprite(uint8_t* spriteData, uint16_t width, uint16_t height, uint16_t pitch, uint16_t x, uint16_t y, uint8_t priority, double scale = 1.0); void drawBuffer(uint8_t *pBuffer); diff --git a/headers/layoutmanager.h b/headers/layoutmanager.h index 6f6aaad67..88a9f8737 100644 --- a/headers/layoutmanager.h +++ b/headers/layoutmanager.h @@ -56,6 +56,8 @@ class LayoutManager { std::string getButtonLayoutRightName(ButtonLayoutRight layout); LayoutList adjustByCustomSettings(LayoutList layout, ButtonLayoutParamsCommon common, uint16_t originX = 0, uint16_t originY = 0); + LayoutList adjustByOffset(LayoutList layout, int16_t originX = 0, int16_t originY = 0); + LayoutList flipHorizontally(LayoutList layout, int16_t startX = 0, int16_t startY = 0, int16_t endX = 0, int16_t endY = 0); // old layout methods LayoutList drawStickless(); @@ -71,6 +73,7 @@ class LayoutManager { LayoutList drawVewlix(); LayoutList drawVewlix7(); LayoutList drawSega2p(); + LayoutList drawSega2p6b(); LayoutList drawNoir8(); LayoutList drawCapcom(); LayoutList drawCapcom6(); @@ -85,6 +88,7 @@ class LayoutManager { LayoutList drawBlankB(); LayoutList drawVLXA(); LayoutList drawVLXB(); + LayoutList drawVLXB6B(); LayoutList drawFightboard(); LayoutList drawFightboardMirrored(); LayoutList drawFightboardStick(); diff --git a/headers/storagemanager.h b/headers/storagemanager.h index bed769283..a564be000 100644 --- a/headers/storagemanager.h +++ b/headers/storagemanager.h @@ -17,6 +17,8 @@ #include "config.pb.h" #include #include "pico/critical_section.h" +#include "eventmanager.h" +#include "GPStorageSaveEvent.h" #define SI Storage::getInstance() diff --git a/lib/FlashPROM/src/FlashPROM.h b/lib/FlashPROM/src/FlashPROM.h index efc0c3110..b9be4262e 100644 --- a/lib/FlashPROM/src/FlashPROM.h +++ b/lib/FlashPROM/src/FlashPROM.h @@ -13,8 +13,8 @@ #include #include -#define EEPROM_SIZE_BYTES 0x4000 // Reserve 16k of flash memory (ensure this value is divisible by 256) -#define EEPROM_ADDRESS_START _u(0x101FC000) // The arduino-pico EEPROM lib starts here, so we'll do the same +#define EEPROM_SIZE_BYTES 0x8000 // Reserve 32k of flash memory (ensure this value is divisible by 256) +#define EEPROM_ADDRESS_START _u(0x101F8000) // The arduino-pico EEPROM lib starts here, so we'll do the same // Warning: If the write wait is too long it can stall other processes #define EEPROM_WRITE_WAIT 50 // Amount of time in ms to wait before blocking core1 and committing to flash diff --git a/proto/config.proto b/proto/config.proto index e3cd85b02..10849569b 100644 --- a/proto/config.proto +++ b/proto/config.proto @@ -252,6 +252,21 @@ message DisplayOptions optional int32 displaySaverTimeout = 17; optional bool turnOffWhenSuspended = 18; + optional DisplaySaverMode displaySaverMode = 19; + + optional ButtonLayoutOrientation buttonLayoutOrientation = 20; + + optional bool inputMode = 21; + optional bool turboMode = 22; + optional bool dpadMode = 23; + optional bool socdMode = 24; + optional bool macroMode = 25; + optional bool profileMode = 26; + + optional bool inputHistoryEnabled = 27; + optional uint32 inputHistoryLength = 28; + optional uint32 inputHistoryCol = 29; + optional uint32 inputHistoryRow = 30; } message LEDOptions @@ -751,10 +766,10 @@ message MacroOptions message InputHistoryOptions { - optional bool enabled = 1; - optional uint32 length = 2; - optional uint32 col = 3; - optional uint32 row = 4; + optional bool deprecatedEnabled = 1 [deprecated = true]; + optional uint32 deprecatedLength = 2 [deprecated = true]; + optional uint32 deprecatedCol = 3 [deprecated = true]; + optional uint32 deprecatedRow = 4 [deprecated = true]; } message RotaryPinOptions @@ -831,7 +846,7 @@ message AddonOptions optional TiltOptions tiltOptions = 18; optional PSPassthroughOptions psPassthroughOptions = 19 [deprecated = true]; optional MacroOptions macroOptions = 20; - optional InputHistoryOptions inputHistoryOptions = 21; + optional InputHistoryOptions inputHistoryOptions = 21 [deprecated = true]; optional XBOnePassthroughOptions xbonePassthroughOptions = 22 [deprecated = true]; optional AnalogADS1256Options analogADS1256Options = 23; optional RotaryOptions rotaryOptions = 24; diff --git a/proto/enums.proto b/proto/enums.proto index aa5e79b9b..86ff6c76a 100644 --- a/proto/enums.proto +++ b/proto/enums.proto @@ -84,6 +84,8 @@ enum ButtonLayoutRight BUTTON_LAYOUT_6GAWD_ALLBUTTON_B = 35; BUTTON_LAYOUT_6GAWD_ALLBUTTONPLUS_B = 36; BUTTON_LAYOUT_STICKLESS_R16B = 37; + BUTTON_LAYOUT_VLXB_6B = 38; + BUTTON_LAYOUT_SEGA2P_6B = 39; } enum SplashMode @@ -262,7 +264,15 @@ enum GpioAction BUTTON_PRESS_INPUT_REVERSE = 69; SUSTAIN_FOCUS_MODE = 70; - SUSTAIN_4_8_WAY_MODE = 71; + SUSTAIN_4_8_WAY_MODE = 71; + + MENU_NAVIGATION_UP = 72; + MENU_NAVIGATION_DOWN = 73; + MENU_NAVIGATION_LEFT = 74; + MENU_NAVIGATION_RIGHT = 75; + MENU_NAVIGATION_SELECT = 76; + MENU_NAVIGATION_BACK = 77; + MENU_NAVIGATION_TOGGLE = 78; } enum GpioDirection @@ -321,6 +331,13 @@ enum GamepadHotkey HOTKEY_DPAD_RIGHT = 41; HOTKEY_PREVIOUS_PROFILE = 42; HOTKEY_SAVE_CONFIG = 43; + HOTKEY_MENU_NAV_UP = 44; + HOTKEY_MENU_NAV_DOWN = 45; + HOTKEY_MENU_NAV_LEFT = 46; + HOTKEY_MENU_NAV_RIGHT = 47; + HOTKEY_MENU_NAV_SELECT = 48; + HOTKEY_MENU_NAV_BACK = 49; + HOTKEY_MENU_NAV_TOGGLE = 50; } // This has to be kept in sync with LEDFormat in NeoPico.hpp @@ -453,6 +470,26 @@ enum PS4ControllerIDMode PS4_ID_EMULATION = 1; }; +enum DisplaySaverMode +{ + option (nanopb_enumopt).long_names = false; + + DISPLAY_SAVER_DISPLAY_OFF = 0; + DISPLAY_SAVER_SNOW = 1; + DISPLAY_SAVER_BOUNCE = 2; + DISPLAY_SAVER_PIPES = 3; + DISPLAY_SAVER_TOAST = 4; +}; + +enum ButtonLayoutOrientation +{ + option (nanopb_enumopt).long_names = false; + + BUTTON_ORIENTATION_DEFAULT = 0; + BUTTON_ORIENTATION_SOUTHPAW = 1; + BUTTON_ORIENTATION_SWITCHED = 2; +}; + enum GPEventType { option (nanopb_enumopt).long_names = false; @@ -469,4 +506,7 @@ enum GPEventType GP_EVENT_BUTTON_PROCESSED_DOWN = 9; GP_EVENT_ANALOG_MOVE = 10; GP_EVENT_ANALOG_PROCESSED_MOVE = 11; -}; \ No newline at end of file + GP_EVENT_STORAGE_SAVE = 12; + GP_EVENT_SYSTEM_REBOOT = 13; + GP_EVENT_MENU_NAVIGATE = 14; +}; diff --git a/src/addons/display.cpp b/src/addons/display.cpp index cce4c4504..d6c746f9c 100644 --- a/src/addons/display.cpp +++ b/src/addons/display.cpp @@ -22,7 +22,7 @@ bool DisplayAddon::available() { if (options.enabled) { // create the gfx interface gpDisplay = new GPGFX(); - gpOptions = gpDisplay->getAvailableDisplay(); + gpOptions = gpDisplay->getAvailableDisplay(GPGFX_DisplayType::DISPLAY_TYPE_NONE); result = (gpOptions.displayType != GPGFX_DisplayType::DISPLAY_TYPE_NONE); if (!result) delete gpDisplay; } @@ -53,6 +53,16 @@ void DisplayAddon::setup() { displaySaverTimeout = displaySaverTimer; configMode = Storage::getInstance().GetConfigMode(); turnOffWhenSuspended = options.turnOffWhenSuspended; + displaySaverMode = options.displaySaverMode; + + mapMenuToggle = new GamepadButtonMapping(0); + GpioMappingInfo* pinMappings = Storage::getInstance().getProfilePinMappings(); + for (Pin_t pin = 0; pin < (Pin_t)NUM_BANK0_GPIOS; pin++) { + switch (pinMappings[pin].action) { + case GpioAction::MENU_NAVIGATION_TOGGLE: mapMenuToggle->pinMask |= 1 << pin; break; + default: break; + } + } // set current display mode if (!configMode) { @@ -68,6 +78,7 @@ void DisplayAddon::setup() { updateDisplayScreen(); EventManager::getInstance().registerEventHandler(GP_EVENT_RESTART, GPEVENT_CALLBACK(this->handleSystemRestart(event))); + EventManager::getInstance().registerEventHandler(GP_EVENT_MENU_NAVIGATE, GPEVENT_CALLBACK(this->handleMenuNavigation(event))); } bool DisplayAddon::updateDisplayScreen() { @@ -81,7 +92,7 @@ bool DisplayAddon::updateDisplayScreen() { delete (SplashScreen*)gpScreen; break; case MAIN_MENU: - delete (SplashScreen*)gpScreen; + delete (MainMenuScreen*)gpScreen; break; case BUTTONS: delete (ButtonLayoutScreen*)gpScreen; @@ -89,6 +100,9 @@ bool DisplayAddon::updateDisplayScreen() { case PIN_VIEWER: delete (PinViewerScreen*)gpScreen; break; + case DISPLAY_SAVER: + delete (DisplaySaverScreen*)gpScreen; + break; case STATS: delete (StatsScreen*)gpScreen; break; @@ -116,6 +130,9 @@ bool DisplayAddon::updateDisplayScreen() { case PIN_VIEWER: gpScreen = new PinViewerScreen(gpDisplay); break; + case DISPLAY_SAVER: + gpScreen = new DisplaySaverScreen(gpDisplay); + break; case STATS: gpScreen = new StatsScreen(gpDisplay); break; @@ -153,12 +170,19 @@ bool DisplayAddon::isDisplayPowerOff() displaySaverTimer = displaySaverTimeout; setDisplayPower(1); } else if (!!displaySaverTimeout && displaySaverTimer <= 0) { - setDisplayPower(0); + if (displaySaverMode == DisplaySaverMode::DISPLAY_SAVER_DISPLAY_OFF) { + setDisplayPower(0); + } else { + if (currDisplayMode != DISPLAY_SAVER) { + currDisplayMode = DISPLAY_SAVER; + updateDisplayScreen(); + } + } } prevMillis = getMillis(); - return (!!displaySaverTimeout && displaySaverTimer <= 0); + return ((!!displaySaverTimeout && displaySaverTimer <= 0) && (displaySaverMode == DisplaySaverMode::DISPLAY_SAVER_DISPLAY_OFF)); } void DisplayAddon::setDisplayPower(uint8_t status) @@ -179,6 +203,15 @@ void DisplayAddon::process() { int8_t screenReturn = gpScreen->update(); gpScreen->draw(); + if (!configMode && screenReturn < 0) { + Mask_t values = Storage::getInstance().GetGamepad()->debouncedGpio; + if (values & mapMenuToggle->pinMask) { + if (currDisplayMode != DisplayMode::MAIN_MENU) { + screenReturn = DisplayMode::MAIN_MENU; + } + } + } + // -1 = we do not change state if (screenReturn >= 0) { // Screen wants to change to something else @@ -199,4 +232,20 @@ void DisplayAddon::handleSystemRestart(GPEvent* e) { currDisplayMode = DisplayMode::RESTART; bootMode = (uint32_t)((GPRestartEvent*)e)->bootMode; updateDisplayScreen(); -} \ No newline at end of file +} + +void DisplayAddon::handleMenuNavigation(GPEvent* e) { + if (currDisplayMode != MAIN_MENU) { + if (((GPMenuNavigateEvent*)e)->menuAction == GpioAction::MENU_NAVIGATION_TOGGLE) { + currDisplayMode = MAIN_MENU; + updateDisplayScreen(); + } + } else { + if (((GPMenuNavigateEvent*)e)->menuAction != GpioAction::MENU_NAVIGATION_TOGGLE) { + ((MainMenuScreen*)gpScreen)->updateMenuNavigation(((GPMenuNavigateEvent*)e)->menuAction); + } else { + currDisplayMode = BUTTONS; + updateDisplayScreen(); + } + } +} diff --git a/src/addons/focus_mode.cpp b/src/addons/focus_mode.cpp index 094f77426..37bb58b33 100644 --- a/src/addons/focus_mode.cpp +++ b/src/addons/focus_mode.cpp @@ -28,6 +28,9 @@ void FocusModeAddon::setup() { void FocusModeAddon::process() { Gamepad * gamepad = Storage::getInstance().GetGamepad(); Mask_t values = Storage::getInstance().GetGamepad()->debouncedGpio; + + if (!Storage::getInstance().getAddonOptions().focusModeOptions.enabled) return; + if (values & mapFocusMode->pinMask) { if (buttonLockMask & GAMEPAD_MASK_DU) { gamepad->state.dpad &= ~GAMEPAD_MASK_UP; diff --git a/src/addons/turbo.cpp b/src/addons/turbo.cpp index 8a05cb969..48ecd8331 100644 --- a/src/addons/turbo.cpp +++ b/src/addons/turbo.cpp @@ -134,6 +134,8 @@ void TurboInput::process() uint16_t buttonsPressed = gamepad->state.buttons & TURBO_BUTTON_MASK; uint8_t dpadPressed = gamepad->state.dpad & GAMEPAD_MASK_DPAD; + if (!options.enabled) return; + // Check for TURBO pin enabled if (gamepad->debouncedGpio & turboPinMask) { if (buttonsPressed && (lastPressed != buttonsPressed)) { diff --git a/src/config_utils.cpp b/src/config_utils.cpp index 34cc4f637..607eefa0d 100644 --- a/src/config_utils.cpp +++ b/src/config_utils.cpp @@ -413,6 +413,16 @@ void ConfigUtils::initUnsetPropertiesWithDefaults(Config& config) INIT_UNSET_PROPERTY(config.displayOptions, buttonLayout, BUTTON_LAYOUT); INIT_UNSET_PROPERTY(config.displayOptions, buttonLayoutRight, BUTTON_LAYOUT_RIGHT); INIT_UNSET_PROPERTY(config.displayOptions, turnOffWhenSuspended, DISPLAY_TURN_OFF_WHEN_SUSPENDED); + INIT_UNSET_PROPERTY(config.displayOptions, inputMode, 1); + INIT_UNSET_PROPERTY(config.displayOptions, turboMode, 1); + INIT_UNSET_PROPERTY(config.displayOptions, dpadMode, 1); + INIT_UNSET_PROPERTY(config.displayOptions, socdMode, 1); + INIT_UNSET_PROPERTY(config.displayOptions, macroMode, 1); + INIT_UNSET_PROPERTY(config.displayOptions, profileMode, 0); + INIT_UNSET_PROPERTY(config.displayOptions, inputHistoryEnabled, !!INPUT_HISTORY_ENABLED); + INIT_UNSET_PROPERTY(config.displayOptions, inputHistoryLength, INPUT_HISTORY_LENGTH); + INIT_UNSET_PROPERTY(config.displayOptions, inputHistoryCol, INPUT_HISTORY_COL); + INIT_UNSET_PROPERTY(config.displayOptions, inputHistoryRow, INPUT_HISTORY_ROW); ButtonLayoutParamsLeft& paramsLeft = config.displayOptions.buttonLayoutCustomOptions.paramsLeft; INIT_UNSET_PROPERTY(paramsLeft, layout, BUTTON_LAYOUT); @@ -437,6 +447,8 @@ void ConfigUtils::initUnsetPropertiesWithDefaults(Config& config) INIT_UNSET_PROPERTY(config.displayOptions, flip, DISPLAY_FLIP); INIT_UNSET_PROPERTY(config.displayOptions, invert, !!DISPLAY_INVERT); INIT_UNSET_PROPERTY(config.displayOptions, displaySaverTimeout, DISPLAY_SAVER_TIMEOUT); + INIT_UNSET_PROPERTY(config.displayOptions, displaySaverMode, DISPLAY_SAVER_MODE); + INIT_UNSET_PROPERTY(config.displayOptions, buttonLayoutOrientation, DISPLAY_LAYOUT_ORIENTATION); // peripheralOptions PeripheralOptions& peripheralOptions = config.peripheralOptions; @@ -679,12 +691,6 @@ void ConfigUtils::initUnsetPropertiesWithDefaults(Config& config) INIT_UNSET_PROPERTY(config.addonOptions.buzzerOptions, volume, BUZZER_VOLUME); INIT_UNSET_PROPERTY(config.addonOptions.buzzerOptions, enablePin, BUZZER_ENABLE_PIN); - // addonOptions.inputHistoryOptions - INIT_UNSET_PROPERTY(config.addonOptions.inputHistoryOptions, enabled, !!INPUT_HISTORY_ENABLED); - INIT_UNSET_PROPERTY(config.addonOptions.inputHistoryOptions, length, INPUT_HISTORY_LENGTH); - INIT_UNSET_PROPERTY(config.addonOptions.inputHistoryOptions, col, INPUT_HISTORY_COL); - INIT_UNSET_PROPERTY(config.addonOptions.inputHistoryOptions, row, INPUT_HISTORY_ROW); - // addonOptions.playerNumberOptions INIT_UNSET_PROPERTY(config.addonOptions.playerNumberOptions, enabled, !!PLAYERNUM_ADDON_ENABLED); INIT_UNSET_PROPERTY(config.addonOptions.playerNumberOptions, number, PLAYER_NUMBER); diff --git a/src/configs/webconfig.cpp b/src/configs/webconfig.cpp index 2491241c9..f6973a9d2 100644 --- a/src/configs/webconfig.cpp +++ b/src/configs/webconfig.cpp @@ -450,7 +450,19 @@ std::string setDisplayOptions(DisplayOptions& displayOptions) readDoc(displayOptions.splashChoice, doc, "splashChoice"); readDoc(displayOptions.splashDuration, doc, "splashDuration"); readDoc(displayOptions.displaySaverTimeout, doc, "displaySaverTimeout"); + readDoc(displayOptions.displaySaverMode, doc, "displaySaverMode"); + readDoc(displayOptions.buttonLayoutOrientation, doc, "buttonLayoutOrientation"); readDoc(displayOptions.turnOffWhenSuspended, doc, "turnOffWhenSuspended"); + readDoc(displayOptions.inputMode, doc, "inputMode"); + readDoc(displayOptions.turboMode, doc, "turboMode"); + readDoc(displayOptions.dpadMode, doc, "dpadMode"); + readDoc(displayOptions.socdMode, doc, "socdMode"); + readDoc(displayOptions.macroMode, doc, "macroMode"); + readDoc(displayOptions.profileMode, doc, "profileMode"); + readDoc(displayOptions.inputHistoryEnabled, doc, "inputHistoryEnabled"); + readDoc(displayOptions.inputHistoryLength, doc, "inputHistoryLength"); + readDoc(displayOptions.inputHistoryCol, doc, "inputHistoryCol"); + readDoc(displayOptions.inputHistoryRow, doc, "inputHistoryRow"); readDoc(displayOptions.buttonLayoutCustomOptions.paramsLeft.layout, doc, "buttonLayoutCustomOptions", "params", "layout"); readDoc(displayOptions.buttonLayoutCustomOptions.paramsLeft.common.startX, doc, "buttonLayoutCustomOptions", "params", "startX"); @@ -492,7 +504,19 @@ std::string getDisplayOptions() // Manually set Document Attributes for the disp writeDoc(doc, "splashChoice", displayOptions.splashChoice); writeDoc(doc, "splashDuration", displayOptions.splashDuration); writeDoc(doc, "displaySaverTimeout", displayOptions.displaySaverTimeout); + writeDoc(doc, "displaySaverMode", displayOptions.displaySaverMode); + writeDoc(doc, "buttonLayoutOrientation", displayOptions.buttonLayoutOrientation); writeDoc(doc, "turnOffWhenSuspended", displayOptions.turnOffWhenSuspended); + writeDoc(doc, "inputMode", displayOptions.inputMode); + writeDoc(doc, "turboMode", displayOptions.turboMode); + writeDoc(doc, "dpadMode", displayOptions.dpadMode); + writeDoc(doc, "socdMode", displayOptions.socdMode); + writeDoc(doc, "macroMode", displayOptions.macroMode); + writeDoc(doc, "profileMode", displayOptions.profileMode); + writeDoc(doc, "inputHistoryEnabled", displayOptions.inputHistoryEnabled); + writeDoc(doc, "inputHistoryLength", displayOptions.inputHistoryLength); + writeDoc(doc, "inputHistoryCol", displayOptions.inputHistoryCol); + writeDoc(doc, "inputHistoryRow", displayOptions.inputHistoryRow); writeDoc(doc, "buttonLayoutCustomOptions", "params", "layout", displayOptions.buttonLayoutCustomOptions.paramsLeft.layout); writeDoc(doc, "buttonLayoutCustomOptions", "params", "startX", displayOptions.buttonLayoutCustomOptions.paramsLeft.common.startX); @@ -1589,12 +1613,6 @@ std::string setAddonOptions() docToPin(snesOptions.latchPin, doc, "snesPadLatchPin"); docToPin(snesOptions.dataPin, doc, "snesPadDataPin"); - InputHistoryOptions& inputHistoryOptions = Storage::getInstance().getAddonOptions().inputHistoryOptions; - docToValue(inputHistoryOptions.length, doc, "inputHistoryLength"); - docToValue(inputHistoryOptions.enabled, doc, "InputHistoryAddonEnabled"); - docToValue(inputHistoryOptions.col, doc, "inputHistoryCol"); - docToValue(inputHistoryOptions.row, doc, "inputHistoryRow"); - KeyboardHostOptions& keyboardHostOptions = Storage::getInstance().getAddonOptions().keyboardHostOptions; docToValue(keyboardHostOptions.enabled, doc, "KeyboardHostAddonEnabled"); docToValue(keyboardHostOptions.mapping.keyDpadUp, doc, "keyboardHostMap", "Up"); @@ -2005,12 +2023,6 @@ std::string getAddonOptions() writeDoc(doc, "snesPadDataPin", cleanPin(snesOptions.dataPin)); writeDoc(doc, "SNESpadAddonEnabled", snesOptions.enabled); - const InputHistoryOptions& inputHistoryOptions = Storage::getInstance().getAddonOptions().inputHistoryOptions; - writeDoc(doc, "inputHistoryLength", inputHistoryOptions.length); - writeDoc(doc, "InputHistoryAddonEnabled", inputHistoryOptions.enabled); - writeDoc(doc, "inputHistoryCol", inputHistoryOptions.col); - writeDoc(doc, "inputHistoryRow", inputHistoryOptions.row); - const KeyboardHostOptions& keyboardHostOptions = Storage::getInstance().getAddonOptions().keyboardHostOptions; writeDoc(doc, "KeyboardHostAddonEnabled", keyboardHostOptions.enabled); writeDoc(doc, "keyboardHostMap", "Up", keyboardHostOptions.mapping.keyDpadUp); diff --git a/src/display/GPGFX.cpp b/src/display/GPGFX.cpp index 8b0c8aee4..95b3bfa5c 100644 --- a/src/display/GPGFX.cpp +++ b/src/display/GPGFX.cpp @@ -24,7 +24,6 @@ GPGFX::GPGFX() { void GPGFX::init(GPGFX_DisplayTypeOptions options) { switch (options.displayType) { case GPGFX_DisplayType::DISPLAY_TYPE_SSD1306: - //this->displayDriver = new GPGFX_OBD_SSD1306(); this->displayDriver = new GPGFX_TinySSD1306(); break; default: @@ -37,38 +36,53 @@ void GPGFX::init(GPGFX_DisplayTypeOptions options) { } } -GPGFX_DisplayTypeOptions GPGFX::getAvailableDisplay() { - GPGFX_DisplayBase* driver = nullptr; +GPGFX_DisplayTypeOptions GPGFX::getAvailableDisplay(GPGFX_DisplayType displayType) { GPGFX_DisplayTypeOptions display; - display.displayType = GPGFX_DisplayType::DISPLAY_TYPE_NONE; + display.displayType = displayType; - for (uint16_t i = GPGFX_DisplayType::DISPLAY_TYPE_NONE; i < GPGFX_DisplayType::DISPLAY_TYPE_COUNT; i++) { - if (i == GPGFX_DisplayType::DISPLAY_TYPE_SSD1306) { - driver = new GPGFX_TinySSD1306(); - } else { - driver = nullptr; + if (display.displayType == GPGFX_DisplayType::DISPLAY_TYPE_NONE) { + // autoscan for device + for (uint16_t i = GPGFX_DisplayType::DISPLAY_TYPE_NONE; i < GPGFX_DisplayType::DISPLAY_TYPE_COUNT; i++) { + if (detectDisplay(&display, (GPGFX_DisplayType)i)) break; } - if ((driver != nullptr) && (display.displayType == GPGFX_DisplayType::DISPLAY_TYPE_NONE)) { - if (driver->isI2C()) { - PeripheralI2CScanResult result = PeripheralManager::getInstance().scanForI2CDevice(driver->getDeviceAddresses()); - if (result.address > -1) { - display.displayType = (GPGFX_DisplayType)i; - display.address = result.address; - display.i2c = PeripheralManager::getInstance().getI2C(result.block); - display.i2c->setExclusiveUse(result.address); - return display; - } - } - if (driver->isSPI()) { - // NYI: check if SPI display exists - } - delete driver; + } else { + if (!detectDisplay(&display, display.displayType)) { + display.displayType = GPGFX_DisplayType::DISPLAY_TYPE_NONE; } } return display; } +bool GPGFX::detectDisplay(GPGFX_DisplayTypeOptions* display, GPGFX_DisplayType displayType) { + GPGFX_DisplayBase* driver = nullptr; + + if (displayType == GPGFX_DisplayType::DISPLAY_TYPE_SSD1306) { + driver = new GPGFX_TinySSD1306(); + } else { + driver = nullptr; + } + + if (driver != nullptr) { + if (driver->isI2C()) { + PeripheralI2CScanResult result = PeripheralManager::getInstance().scanForI2CDevice(driver->getDeviceAddresses()); + if (result.address > -1) { + display->displayType = displayType; + display->address = result.address; + display->i2c = PeripheralManager::getInstance().getI2C(result.block); + display->i2c->setExclusiveUse(result.address); + return true; + } + } + if (driver->isSPI()) { + // NYI: check if SPI display exists + } + delete driver; + } + + return false; +} + void GPGFX::clearScreen() { this->displayDriver->clear(); } @@ -77,6 +91,10 @@ void GPGFX::render() { this->displayDriver->drawBuffer(NULL); } +uint32_t GPGFX::getPixel(uint16_t x, uint16_t y) { + return this->displayDriver->getPixel(x, y); +} + void GPGFX::drawPixel(uint16_t x, uint16_t y, uint32_t color) { this->displayDriver->drawPixel(x, y, color); } @@ -105,6 +123,6 @@ void GPGFX::drawPolygon(uint16_t x, uint16_t y, uint16_t radius, uint16_t sides, this->displayDriver->drawPolygon(x, y, radius, sides, color, filled, rotation); } -void GPGFX::drawSprite(uint8_t* spriteData, uint16_t width, uint16_t height, uint16_t pitch, uint16_t x, uint16_t y, uint8_t priority) { - this->displayDriver->drawSprite(spriteData, width, height, pitch, x, y, priority); +void GPGFX::drawSprite(uint8_t* spriteData, uint16_t width, uint16_t height, uint16_t pitch, uint16_t x, uint16_t y, uint8_t priority, double scale) { + this->displayDriver->drawSprite(spriteData, width, height, pitch, x, y, priority, scale); } diff --git a/src/display/ui/elements/GPButton.cpp b/src/display/ui/elements/GPButton.cpp index e0ae6c5e6..4241d19d8 100644 --- a/src/display/ui/elements/GPButton.cpp +++ b/src/display/ui/elements/GPButton.cpp @@ -93,7 +93,6 @@ void GPButton::draw() { if (useMask && mapMask != NULL) { maskedPins = (pinValues & mapMask->pinMask); - for (Pin_t pin = 0; pin < (Pin_t)NUM_BANK0_GPIOS; pin++) { if ((maskedPins & (1 << pin)) == (1 << pin)) { setPin = pin; diff --git a/src/display/ui/elements/GPLever.cpp b/src/display/ui/elements/GPLever.cpp index f9cefd717..d7b9736be 100644 --- a/src/display/ui/elements/GPLever.cpp +++ b/src/display/ui/elements/GPLever.cpp @@ -43,13 +43,13 @@ void GPLever::draw() { bool leftState = (this->_leftMask > -1 ? getProcessedGamepad()->pressedButton((uint16_t)this->_leftMask) : getProcessedGamepad()->pressedLeft()); bool downState = (this->_downMask > -1 ? getProcessedGamepad()->pressedButton((uint16_t)this->_downMask) : getProcessedGamepad()->pressedDown()); bool rightState = (this->_rightMask > -1 ? getProcessedGamepad()->pressedButton((uint16_t)this->_rightMask) : getProcessedGamepad()->pressedRight()); - if (upState != downState) { - leverY -= upState ? leverRadius : -leverRadius; + if (upState != downState) { + leverY -= upState ? leverRadius : -leverRadius; } - if (leftState != rightState) { - leverX -= leftState ? leverRadius : -leverRadius; - } - } else { + if (leftState != rightState) { + leverX -= leftState ? leverRadius : -leverRadius; + } + } else { // analog uint16_t analogX = map((this->_inputType == DPAD_MODE_LEFT_ANALOG ? getProcessedGamepad()->state.lx : getProcessedGamepad()->state.rx), 0, 0xFFFF, 0, 100); uint16_t analogY = map((this->_inputType == DPAD_MODE_LEFT_ANALOG ? getProcessedGamepad()->state.ly : getProcessedGamepad()->state.ry), 0, 0xFFFF, 0, 100); diff --git a/src/display/ui/elements/GPMenu.cpp b/src/display/ui/elements/GPMenu.cpp new file mode 100644 index 000000000..f81ba51c3 --- /dev/null +++ b/src/display/ui/elements/GPMenu.cpp @@ -0,0 +1,45 @@ +#include "GPMenu.h" + +#include + +void GPMenu::draw() { + if (this->getVisibility()) { + uint16_t baseX = this->x; + uint16_t baseY = this->y; + + uint16_t menuWidth = this->menuSizeX * 6; + uint16_t menuHeight = this->menuSizeY * 8; + + uint16_t dataSize = this->getDataSize(); + uint16_t totalPages = (dataSize + this->menuSizeY - 1) / this->menuSizeY; + uint16_t itemPage = (this->menuIndex / this->menuSizeY); + + int16_t currPageItems = (dataSize - (itemPage * this->menuSizeY)); + if (currPageItems > this->menuSizeY) { + currPageItems = this->menuSizeY; + } else if (currPageItems <= 0) { + currPageItems = 0; + } + + getRenderer()->drawText((21-this->menuTitle.length()) / 2, 0, this->menuTitle.c_str()); + + std::string pageDisplay = ""; + pageDisplay += "Page: " + std::to_string(itemPage+1) + "/" + std::to_string(totalPages); + getRenderer()->drawText(11, 7, pageDisplay.c_str()); + + if (this->menuEntryData->size() > 0) { + for (uint8_t menuLine = 0; menuLine < currPageItems; menuLine++) { + uint8_t pageLine = (this->menuSizeY * itemPage) + menuLine; + int32_t lineValue = this->menuEntryData->at(pageLine).optionValue; + bool showCurrentOption = false; + if (lineValue != -1) { + showCurrentOption = (this->menuEntryData->at(pageLine).currentValue() == this->menuEntryData->at(pageLine).optionValue); + } + getRenderer()->drawText(2, 2+menuLine, this->menuEntryData->at(pageLine).label + (showCurrentOption ? " *" : "")); + } + } + + // draw cursor + getRenderer()->drawText(1, 2+(this->menuIndex % this->menuSizeY), CHAR_RIGHT); + } +} diff --git a/src/display/ui/screens/ButtonLayoutScreen.cpp b/src/display/ui/screens/ButtonLayoutScreen.cpp index 63774c52f..d8e6a538f 100644 --- a/src/display/ui/screens/ButtonLayoutScreen.cpp +++ b/src/display/ui/screens/ButtonLayoutScreen.cpp @@ -6,11 +6,10 @@ #include "drivers/xinput/XInputDriver.h" void ButtonLayoutScreen::init() { - const InputHistoryOptions& inputHistoryOptions = Storage::getInstance().getAddonOptions().inputHistoryOptions; - isInputHistoryEnabled = inputHistoryOptions.enabled; - inputHistoryX = inputHistoryOptions.row; - inputHistoryY = inputHistoryOptions.col; - inputHistoryLength = inputHistoryOptions.length; + isInputHistoryEnabled = Storage::getInstance().getDisplayOptions().inputHistoryEnabled; + inputHistoryX = Storage::getInstance().getDisplayOptions().inputHistoryRow; + inputHistoryY = Storage::getInstance().getDisplayOptions().inputHistoryCol; + inputHistoryLength = Storage::getInstance().getDisplayOptions().inputHistoryLength; bannerDelayStart = getMillis(); gamepad = Storage::getInstance().GetGamepad(); inputMode = DriverManager::getInstance().getInputMode(); @@ -44,6 +43,7 @@ void ButtonLayoutScreen::init() { prevLayoutRight = Storage::getInstance().getDisplayOptions().buttonLayoutRight; prevLeftOptions = Storage::getInstance().getDisplayOptions().buttonLayoutCustomOptions.paramsLeft; prevRightOptions = Storage::getInstance().getDisplayOptions().buttonLayoutCustomOptions.paramsRight; + prevOrientation = Storage::getInstance().getDisplayOptions().buttonLayoutOrientation; // we cannot look at macro options enabled, pull the pins @@ -68,6 +68,14 @@ void ButtonLayoutScreen::init() { } } + // determine which fields will be displayed on the status bar + showInputMode = Storage::getInstance().getDisplayOptions().inputMode; + showTurboMode = Storage::getInstance().getDisplayOptions().turboMode; + showDpadMode = Storage::getInstance().getDisplayOptions().dpadMode; + showSocdMode = Storage::getInstance().getDisplayOptions().socdMode; + showMacroMode = Storage::getInstance().getDisplayOptions().macroMode; + showProfileMode = Storage::getInstance().getDisplayOptions().profileMode; + getRenderer()->clearScreen(); } @@ -83,8 +91,9 @@ int8_t ButtonLayoutScreen::update() { if (configMode) { uint8_t layoutLeft = Storage::getInstance().getDisplayOptions().buttonLayout; uint8_t layoutRight = Storage::getInstance().getDisplayOptions().buttonLayoutRight; - bool inputHistoryEnabled = Storage::getInstance().getAddonOptions().inputHistoryOptions.enabled; - if ((prevLayoutLeft != layoutLeft) || (prevLayoutRight != layoutRight) || (isInputHistoryEnabled != inputHistoryEnabled) || compareCustomLayouts()) { + uint8_t buttonLayoutOrientation = Storage::getInstance().getDisplayOptions().buttonLayoutOrientation; + bool inputHistoryEnabled = Storage::getInstance().getDisplayOptions().inputHistoryEnabled; + if ((prevLayoutLeft != layoutLeft) || (prevLayoutRight != layoutRight) || (isInputHistoryEnabled != inputHistoryEnabled) || compareCustomLayouts() || (prevOrientation != buttonLayoutOrientation)) { shutdown(); init(); } @@ -143,80 +152,102 @@ void ButtonLayoutScreen::generateHeader() { } } - // Display standard header - switch (inputMode) - { - case INPUT_MODE_PS3: statusBar += "PS3"; break; - case INPUT_MODE_GENERIC: statusBar += "USBHID"; break; - case INPUT_MODE_SWITCH: statusBar += "SWITCH"; 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_XINPUT: - statusBar += "X"; - if(((XInputDriver*)DriverManager::getInstance().getDriver())->getAuthEnabled() == true ) - statusBar += "B360"; - else - statusBar += "INPUT"; - break; - case INPUT_MODE_KEYBOARD: statusBar += "HID-KB"; break; - case INPUT_MODE_CONFIG: statusBar += "CONFIG"; break; - } + if (showInputMode) { + // Display standard header + switch (inputMode) + { + case INPUT_MODE_PS3: statusBar += "PS3"; break; + case INPUT_MODE_GENERIC: statusBar += "USBHID"; break; + case INPUT_MODE_SWITCH: statusBar += "SWITCH"; 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_XINPUT: + statusBar += "X"; + if(((XInputDriver*)DriverManager::getInstance().getDriver())->getAuthEnabled() == true ) + statusBar += "B360"; + else + statusBar += "INPUT"; + break; + case INPUT_MODE_KEYBOARD: statusBar += "HID-KB"; break; + case INPUT_MODE_CONFIG: statusBar += "CONFIG"; break; + } + } - const TurboOptions& turboOptions = storage.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 - } + if (showTurboMode) { + const TurboOptions& turboOptions = storage.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->getActiveDpadMode()) - { - case DPAD_MODE_DIGITAL: statusBar += " D"; break; - case DPAD_MODE_LEFT_ANALOG: statusBar += " L"; break; - case DPAD_MODE_RIGHT_ANALOG: statusBar += " R"; break; - } + if (showDpadMode) { + switch (gamepad->getActiveDpadMode()) + { + 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"; + if (showSocdMode) { + 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 (showMacroMode && macroEnabled) statusBar += " M"; + + if (showProfileMode) { + statusBar += " Pr:"; + + std::string profile; + profile.assign(storage.currentProfileLabel(), strlen(storage.currentProfileLabel())); + if (profile.empty()) { + statusBar += std::to_string(getGamepad()->getOptions().profileNumber); + } else { + statusBar += profile; + } + } + + trim(statusBar); } void ButtonLayoutScreen::drawScreen() { @@ -514,3 +545,8 @@ void ButtonLayoutScreen::handleUSB(GPEvent* e) { } bannerDisplay = true; } + +void ButtonLayoutScreen::trim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), + std::not1(std::ptr_fun(std::isspace)))); +} \ No newline at end of file diff --git a/src/display/ui/screens/DisplaySaverScreen.cpp b/src/display/ui/screens/DisplaySaverScreen.cpp new file mode 100644 index 000000000..23dc97a12 --- /dev/null +++ b/src/display/ui/screens/DisplaySaverScreen.cpp @@ -0,0 +1,197 @@ +#include "DisplaySaverScreen.h" + +#include "pico/stdlib.h" +#include "version.h" + +void DisplaySaverScreen::init() { + const DisplayOptions& options = Storage::getInstance().getDisplayOptions(); + displaySaverMode = options.displaySaverMode; + + getRenderer()->clearScreen(); + + switch (displaySaverMode) { + case DisplaySaverMode::DISPLAY_SAVER_SNOW: + initSnowScene(); + break; + case DisplaySaverMode::DISPLAY_SAVER_BOUNCE: + break; + case DisplaySaverMode::DISPLAY_SAVER_PIPES: + break; + case DisplaySaverMode::DISPLAY_SAVER_TOAST: + initToasters(); + break; + } +} + +void DisplaySaverScreen::shutdown() { + clearElements(); +} + +void DisplaySaverScreen::drawScreen() { + switch (displaySaverMode) { + case DisplaySaverMode::DISPLAY_SAVER_SNOW: + drawSnowScene(); + break; + case DisplaySaverMode::DISPLAY_SAVER_BOUNCE: + drawBounceScene(); + break; + case DisplaySaverMode::DISPLAY_SAVER_PIPES: + drawPipeScene(); + break; + case DisplaySaverMode::DISPLAY_SAVER_TOAST: + drawToasterScene(); + break; + } +} + +int8_t DisplaySaverScreen::update() { + if (!Storage::getInstance().GetConfigMode()) { + uint16_t buttonState = getGamepad()->state.buttons; + if (prevButtonState && !buttonState) { + if (prevButtonState != 0) { + prevButtonState = 0; + return DisplayMode::BUTTONS; + } + } + prevButtonState = buttonState; + } + + return -1; // -1 means no change in screen state +} + +void DisplaySaverScreen::initSnowScene() { + for (uint8_t x = 0; x < SCREEN_WIDTH; ++x) { + for (uint8_t y = 0; y < SCREEN_HEIGHT; ++y) { + snowflakeSpeeds[x][y] = 0; + snowflakeDrift[x][y] = 0; + getRenderer()->drawPixel(x, y, 0); + } + } +} + +void DisplaySaverScreen::drawSnowScene() { + for (int8_t y = SCREEN_HEIGHT - 1; y >= 0; --y) { + for (uint8_t x = 0; x < SCREEN_WIDTH; ++x) { + if (snowflakeSpeeds[x][y] > 0) { + uint8_t speed = snowflakeSpeeds[x][y]; + uint8_t newY = y + speed; + int8_t drift = snowflakeDrift[x][y]; + int8_t newX = x + drift; + + if (newX < 0) newX = 0; + if (newX >= SCREEN_WIDTH) newX = SCREEN_WIDTH - 1; + + if (newY >= SCREEN_HEIGHT) { + getRenderer()->drawPixel(x, y, 0); + snowflakeSpeeds[x][y] = 0; + snowflakeDrift[x][y] = 0; + } else { + getRenderer()->drawPixel(x, y, 0); + getRenderer()->drawPixel(newX, newY, 1); + snowflakeSpeeds[newX][newY] = speed; + snowflakeDrift[newX][newY] = drift; + snowflakeSpeeds[x][y] = 0; + snowflakeDrift[x][y] = 0; + } + } + } + } + + for (uint8_t x = 0; x < SCREEN_WIDTH; ++x) { + if (rand() % 10 == 0) { + getRenderer()->drawPixel(x, 0, 1); + snowflakeSpeeds[x][0] = (rand() % 3) + 1; + snowflakeDrift[x][0] = (rand() % 3) - 1; + } + } +} + +void DisplaySaverScreen::drawBounceScene() { + uint16_t scaledWidth = static_cast(bounceSpriteWidth * bounceScale); + uint16_t scaledHeight = static_cast(bounceSpriteHeight * bounceScale); + + bounceSpriteX += bounceSpriteVelocityX; + bounceSpriteY += bounceSpriteVelocityY; + + if (bounceSpriteX <= 0 || bounceSpriteX + scaledWidth >= SCREEN_WIDTH) bounceSpriteVelocityX = -bounceSpriteVelocityX; + + if (bounceSpriteY <= 0 || bounceSpriteY + scaledHeight >= SCREEN_HEIGHT) bounceSpriteVelocityY = -bounceSpriteVelocityY; + + getRenderer()->drawSprite((uint8_t *)bootLogoBottom, bounceSpriteWidth, bounceSpriteHeight, 0, bounceSpriteX, bounceSpriteY, 0, bounceScale); +} + +void DisplaySaverScreen::drawPipeScene() { + const uint8_t PIPE_WIDTH = 4; + const uint8_t PIPE_COLOR = 1; + + uint8_t currentX = 0; + uint8_t currentY = 0; + + while (currentY < SCREEN_HEIGHT) { + bool connectRight = rand() % 2; + bool connectDown = rand() % 2; + + if (connectRight && currentX + PIPE_WIDTH < SCREEN_WIDTH) { + for (uint8_t i = 0; i < PIPE_WIDTH; ++i) { + getRenderer()->drawPixel(currentX + i, currentY, PIPE_COLOR); + } + } + + if (connectDown && currentY + PIPE_WIDTH < SCREEN_HEIGHT) { + for (uint8_t i = 0; i < PIPE_WIDTH; ++i) { + getRenderer()->drawPixel(currentX, currentY + i, PIPE_COLOR); + } + } + + getRenderer()->drawPixel(currentX, currentY, PIPE_COLOR); + + currentX += PIPE_WIDTH; + if (currentX >= SCREEN_WIDTH) { + currentX = 0; + currentY += PIPE_WIDTH; + } + + for (volatile uint32_t delay = 0; delay < 10000; ++delay) { + // Do nothing, just burn some CPU cycles + } + } +} + +void DisplaySaverScreen::initToasters() { + for (uint16_t i = 0; i < numberOfToasters; ++i) { + double scale = (static_cast(rand()) / RAND_MAX); + int16_t dx = (-1 - rand() % 3); + int16_t dy = (1 + rand() % 3); + + toasters.push_back({ + (uint8_t *)bootLogoTop, + toasterSpriteWidth, + toasterSpriteHeight, + scale, + static_cast(SCREEN_WIDTH - toasterSpriteWidth * scale), + static_cast(rand() % (SCREEN_HEIGHT - static_cast(toasterSpriteHeight * scale))), + static_cast(dx), + static_cast(dy) + }); + } +} + +void DisplaySaverScreen::drawToasterScene() { + for (uint16_t i = 0; i < toasters.size(); ++i) { + ToastParams& sprite = toasters[i]; + + getRenderer()->drawSprite(sprite.image, sprite.width, sprite.height, 0, sprite.x, sprite.y, 0, sprite.scale); + + sprite.x += sprite.dx; + sprite.y += sprite.dy; + + if (sprite.x + sprite.width * sprite.scale < 0) { + sprite.x = SCREEN_WIDTH; + sprite.y = rand() % (SCREEN_HEIGHT - static_cast(sprite.height * sprite.scale)); + } + + if (sprite.y > SCREEN_HEIGHT) { + sprite.y = 0; + } + } +} \ No newline at end of file diff --git a/src/display/ui/screens/MainMenuScreen.cpp b/src/display/ui/screens/MainMenuScreen.cpp index 5e4abda38..abb55f5e3 100644 --- a/src/display/ui/screens/MainMenuScreen.cpp +++ b/src/display/ui/screens/MainMenuScreen.cpp @@ -1,55 +1,114 @@ #include "MainMenuScreen.h" +#include "hardware/watchdog.h" +#include "system.h" extern uint32_t getMillis(); void MainMenuScreen::init() { getRenderer()->clearScreen(); + currentMenu = &mainMenu; + previousMenu = nullptr; + + exitToScreen = -1; + + gpMenu = new GPMenu(); + gpMenu->setRenderer(getRenderer()); + gpMenu->setPosition(8, 16); + gpMenu->setStrokeColor(1); + gpMenu->setFillColor(1); + gpMenu->setMenuSize(18, 4); + gpMenu->setViewport(this->getViewport()); + gpMenu->setShape(GPShape_Type::GP_SHAPE_SQUARE); + gpMenu->setMenuData(currentMenu); + gpMenu->setMenuTitle(MAIN_MENU_NAME); + addElement(gpMenu); + + mapMenuUp = new GamepadButtonMapping(0); + mapMenuDown = new GamepadButtonMapping(0); + mapMenuLeft = new GamepadButtonMapping(0); + mapMenuRight = new GamepadButtonMapping(0); + mapMenuSelect = new GamepadButtonMapping(0); + mapMenuBack = new GamepadButtonMapping(0); + mapMenuToggle = new GamepadButtonMapping(0); + + // populate the profiles menu + uint8_t profileCount = (sizeof(Storage::getInstance().getProfileOptions().gpioMappingsSets)/sizeof(GpioMappings))+1; + for (uint8_t profileCtr = 0; profileCtr < profileCount; profileCtr++) { + std::string menuLabel = ""; + if (profileCtr == 0) { + menuLabel = Storage::getInstance().getGpioMappings().profileLabel; + } else { + menuLabel = Storage::getInstance().getProfileOptions().gpioMappingsSets[profileCtr-1].profileLabel; + } + if (menuLabel.empty()) { + menuLabel = "Profile #" + std::to_string(profileCtr); + } + MenuEntry menuEntry = {menuLabel, NULL, nullptr, std::bind(&MainMenuScreen::currentProfile, this), std::bind(&MainMenuScreen::selectProfile, this), profileCtr+1}; + profilesMenu.push_back(menuEntry); + } + + GpioMappingInfo* pinMappings = Storage::getInstance().getProfilePinMappings(); + for (Pin_t pin = 0; pin < (Pin_t)NUM_BANK0_GPIOS; pin++) { + switch (pinMappings[pin].action) { + case GpioAction::MENU_NAVIGATION_UP: mapMenuUp->pinMask |= 1 << pin; break; + case GpioAction::MENU_NAVIGATION_DOWN: mapMenuDown->pinMask |= 1 << pin; break; + case GpioAction::MENU_NAVIGATION_LEFT: mapMenuLeft->pinMask |= 1 << pin; break; + case GpioAction::MENU_NAVIGATION_RIGHT: mapMenuRight->pinMask |= 1 << pin; break; + case GpioAction::MENU_NAVIGATION_SELECT: mapMenuSelect->pinMask |= 1 << pin; break; + case GpioAction::MENU_NAVIGATION_BACK: mapMenuBack->pinMask |= 1 << pin; break; + case GpioAction::MENU_NAVIGATION_TOGGLE: mapMenuToggle->pinMask |= 1 << pin; break; + default: break; + } + } + + changeRequiresReboot = false; + changeRequiresSave = false; + prevInputMode = Storage::getInstance().GetGamepad()->getOptions().inputMode; + updateInputMode = Storage::getInstance().GetGamepad()->getOptions().inputMode; + + prevDpadMode = Storage::getInstance().GetGamepad()->getOptions().dpadMode; + updateDpadMode = Storage::getInstance().GetGamepad()->getOptions().dpadMode; + + prevSocdMode = Storage::getInstance().GetGamepad()->getOptions().socdMode; + updateSocdMode = Storage::getInstance().GetGamepad()->getOptions().socdMode; + + prevProfile = Storage::getInstance().GetGamepad()->getOptions().profileNumber; + updateProfile = Storage::getInstance().GetGamepad()->getOptions().profileNumber; + + prevFocus = Storage::getInstance().getAddonOptions().focusModeOptions.enabled; + updateFocus = Storage::getInstance().getAddonOptions().focusModeOptions.enabled; + + prevTurbo = Storage::getInstance().getAddonOptions().turboOptions.enabled; + updateTurbo = Storage::getInstance().getAddonOptions().turboOptions.enabled; } void MainMenuScreen::shutdown() { clearElements(); + exitToScreen = -1; } void MainMenuScreen::drawScreen() { - getRenderer()->drawText(1, 1, "GPGFX_UI Test Menu"); + gpMenu->setVisibility(!screenIsPrompting); - for (size_t i = 0; i < currentMenu->size(); ++i) { - MenuEntry entry = currentMenu->at(i); + if (!screenIsPrompting) { - getRenderer()->drawText(3, 3+i, entry.label); - } - -/* - if (!isPressed) { - if (pressedUp()) { - if (menuIndex > 0) { - menuIndex--; - } else { - menuIndex = currentMenu->size()-1; - } - checkDebounce = getMillis(); - isPressed = true; - } else if (pressedDown()) { - if (menuIndex < currentMenu->size()-1) { - menuIndex++; - } else { - menuIndex = 0; - } - checkDebounce = getMillis(); - isPressed = true; - } else if (pressedB1()) { - currentMenu->at(menuIndex).action(); - checkDebounce = getMillis(); - isPressed = true; - } } else { - if (isPressed && ((getMillis() - checkDebounce) > 400)) { - isPressed = false; + getRenderer()->drawText(1, 1, "Config has changed."); + if (changeRequiresSave && !changeRequiresReboot) { + getRenderer()->drawText(3, 3, "Would you like"); + getRenderer()->drawText(6, 4, "to save?"); + } else if (changeRequiresSave && changeRequiresReboot) { + getRenderer()->drawText(3, 3, "Would you like"); + getRenderer()->drawText(1, 4, "to save & restart?"); + } else { + } + + if (promptChoice) getRenderer()->drawText(5, 6, CHAR_RIGHT); + getRenderer()->drawText(6, 6, "Yes"); + if (!promptChoice) getRenderer()->drawText(11, 6, CHAR_RIGHT); + getRenderer()->drawText(12, 6, "No"); } -*/ - - getRenderer()->drawText(1, 3+menuIndex, ">"); } void MainMenuScreen::setMenu(std::vector* menu) { @@ -57,51 +116,284 @@ void MainMenuScreen::setMenu(std::vector* menu) { } int8_t MainMenuScreen::update() { + Gamepad * gamepad = Storage::getInstance().GetGamepad(); + Mask_t values = Storage::getInstance().GetGamepad()->debouncedGpio; + uint16_t buttonState = getGamepad()->state.buttons; - if (prevButtonState && !buttonState) { - switch (prevButtonState) { - case (GAMEPAD_MASK_B1): + if (!isPressed && prevValues != values) { + if (values & mapMenuUp->pinMask) { + updateMenuNavigation(GpioAction::MENU_NAVIGATION_UP); + } else if (values & mapMenuDown->pinMask) { + updateMenuNavigation(GpioAction::MENU_NAVIGATION_DOWN); + } else if (values & mapMenuSelect->pinMask) { + updateMenuNavigation(GpioAction::MENU_NAVIGATION_SELECT); + } else if (values & mapMenuBack->pinMask) { + updateMenuNavigation(GpioAction::MENU_NAVIGATION_BACK); + } + } else { + isPressed = false; + } + + prevButtonState = buttonState; + prevValues = values; + + if ((exitToScreen != -1) && ((changeRequiresSave) || (changeRequiresReboot))) { + // trying to exit menu but a change requires a save/reboot + exitToScreenBeforePrompt = exitToScreen; + exitToScreen = -1; + screenIsPrompting = true; + } + + return exitToScreen; +} + +void MainMenuScreen::updateMenuNavigation(GpioAction action) { + bool changeIndex = false; + uint16_t menuSize = gpMenu->getDataSize(); + + switch (action) { + case GpioAction::MENU_NAVIGATION_UP: + if (!screenIsPrompting) { if (menuIndex > 0) { menuIndex--; } else { - menuIndex = currentMenu->size()-1; + menuIndex = menuSize-1; } - break; - case (GAMEPAD_MASK_B2): - if (menuIndex < currentMenu->size()-1) { + changeIndex = true; + } else { + promptChoice = !promptChoice; + } + isPressed = true; + break; + case GpioAction::MENU_NAVIGATION_DOWN: + if (!screenIsPrompting) { + if (menuIndex < menuSize-1) { menuIndex++; } else { menuIndex = 0; } - break; - case (GAMEPAD_MASK_S1): - currentMenu->at(menuIndex).action(); - break; - default: - //prevDisplayMode = DisplayMode::CONFIG_INSTRUCTION; - break; - } + changeIndex = true; + } else { + promptChoice = !promptChoice; + } + isPressed = true; + break; + case GpioAction::MENU_NAVIGATION_LEFT: + if (screenIsPrompting) { + promptChoice = !promptChoice; + } + isPressed = true; + break; + case GpioAction::MENU_NAVIGATION_RIGHT: + if (screenIsPrompting) { + promptChoice = !promptChoice; + } + isPressed = true; + break; + case GpioAction::MENU_NAVIGATION_SELECT: + if (!screenIsPrompting) { + if (currentMenu->at(menuIndex).submenu != nullptr) { + previousMenu = currentMenu; + currentMenu = currentMenu->at(menuIndex).submenu; + gpMenu->setMenuData(currentMenu); + gpMenu->setMenuTitle(previousMenu->at(menuIndex).label); + menuIndex = 0; + changeIndex = true; + } else { + currentMenu->at(menuIndex).action(); + } + } else { + if (promptChoice) { + saveOptions(); + } else { + resetOptions(); + exitToScreen = DisplayMode::BUTTONS; + exitToScreenBeforePrompt = DisplayMode::BUTTONS; + isPressed = false; + } + } + isPressed = true; + break; + case GpioAction::MENU_NAVIGATION_BACK: + if (!screenIsPrompting) { + if (previousMenu != nullptr) { + currentMenu = previousMenu; + previousMenu = nullptr; + menuIndex = 0; + changeIndex = true; + gpMenu->setMenuData(currentMenu); + gpMenu->setMenuTitle(MAIN_MENU_NAME); + } else { + exitToScreen = DisplayMode::BUTTONS; + exitToScreenBeforePrompt = DisplayMode::BUTTONS; + isPressed = false; + } + } else { + // back again goes back to the menu + screenIsPrompting = false; + isPressed = false; + } + isPressed = true; + break; + default: + break; } - prevButtonState = buttonState; + if (changeIndex) gpMenu->setIndex(menuIndex); +} +void MainMenuScreen::saveAndExit() { + saveOptions(); +} + +int32_t MainMenuScreen::modeValue() { return -1; } -/* - void testMenu(); +void MainMenuScreen::selectInputMode() { + if (currentMenu->at(menuIndex).optionValue != -1) { + InputMode valueToSave = (InputMode)currentMenu->at(menuIndex).optionValue; + prevInputMode = Storage::getInstance().GetGamepad()->getOptions().inputMode; + updateInputMode = valueToSave; + + if (prevInputMode != valueToSave) { + // input mode requires a save and reboot + changeRequiresReboot = true; + changeRequiresSave = true; + } + } +} + +int32_t MainMenuScreen::currentInputMode() { + return updateInputMode; +} + +void MainMenuScreen::selectDPadMode() { + if (currentMenu->at(menuIndex).optionValue != -1) { + DpadMode valueToSave = (DpadMode)currentMenu->at(menuIndex).optionValue; + prevDpadMode = Storage::getInstance().GetGamepad()->getOptions().dpadMode; + updateDpadMode = valueToSave; + + if (prevDpadMode != valueToSave) changeRequiresSave = true; + } +} + +int32_t MainMenuScreen::currentDpadMode() { + return updateDpadMode; +} + +void MainMenuScreen::selectSOCDMode() { + if (currentMenu->at(menuIndex).optionValue != -1) { + SOCDMode valueToSave = (SOCDMode)currentMenu->at(menuIndex).optionValue; + prevSocdMode = Storage::getInstance().GetGamepad()->getOptions().socdMode; + updateSocdMode = valueToSave; + + if (prevDpadMode != valueToSave) changeRequiresSave = true; + } +} + +int32_t MainMenuScreen::currentSOCDMode() { + return updateSocdMode; +} + +void MainMenuScreen::resetOptions() { + if (changeRequiresSave) { + if (prevInputMode != updateInputMode) updateInputMode = prevInputMode; + if (prevDpadMode != updateDpadMode) updateDpadMode = prevDpadMode; + if (prevSocdMode != updateSocdMode) updateSocdMode = prevSocdMode; + if (prevProfile != updateProfile) updateProfile = prevProfile; + if (prevFocus != updateFocus) updateFocus = prevFocus; + if (prevTurbo != updateTurbo) updateTurbo = prevTurbo; + } + + changeRequiresSave = false; + changeRequiresReboot = false; + screenIsPrompting = false; +} + +void MainMenuScreen::saveOptions() { + GamepadOptions& options = Storage::getInstance().getGamepadOptions(); - std::vector mainMenu = { - {"Menu 1", NULL, std::bind(&DisplayAddon::testMenu, this)}, - {"Menu 2", NULL, std::bind(&DisplayAddon::testMenu, this)}, - {"Menu 3", NULL, std::bind(&DisplayAddon::testMenu, this)}, - {"Menu 4", NULL, std::bind(&DisplayAddon::testMenu, this)}, - {"Menu 5", NULL, std::bind(&DisplayAddon::testMenu, this)}, - {"Menu 6", NULL, std::bind(&DisplayAddon::testMenu, this)}, - {"Menu 7", NULL, std::bind(&DisplayAddon::testMenu, this)}, - {"Menu 8", NULL, std::bind(&DisplayAddon::testMenu, this)}, - }; + if (changeRequiresSave) { + bool saveHasChanged = false; + if (prevInputMode != updateInputMode) { + options.inputMode = updateInputMode; + saveHasChanged = true; + } + if (prevDpadMode != updateDpadMode) { + options.dpadMode = updateDpadMode; + saveHasChanged = true; + } + if (prevSocdMode != updateSocdMode) { + options.socdMode = updateSocdMode; + saveHasChanged = true; + } + if (prevProfile != updateProfile) { + options.profileNumber = updateProfile; + saveHasChanged = true; + } + if (prevFocus != updateFocus) { + Storage::getInstance().getAddonOptions().focusModeOptions.enabled = updateFocus; + saveHasChanged = true; + } + if (prevTurbo != updateTurbo) { + Storage::getInstance().getAddonOptions().turboOptions.enabled = updateTurbo; + saveHasChanged = true; + } - std::vector* currentMenu = &mainMenu; -*/ \ No newline at end of file + if (saveHasChanged) { + EventManager::getInstance().triggerEvent(new GPStorageSaveEvent(true, changeRequiresReboot)); + screenIsPrompting = false; + } + changeRequiresSave = false; + changeRequiresReboot = false; + } + + if (exitToScreenBeforePrompt != -1) { + exitToScreen = exitToScreenBeforePrompt; + exitToScreenBeforePrompt = -1; + } +} + +void MainMenuScreen::selectProfile() { + if (currentMenu->at(menuIndex).optionValue != -1) { + uint8_t valueToSave = currentMenu->at(menuIndex).optionValue; + prevProfile = Storage::getInstance().GetGamepad()->getOptions().profileNumber; + updateProfile = valueToSave; + + if (prevProfile != valueToSave) changeRequiresSave = true; + } +} + +int32_t MainMenuScreen::currentProfile() { + return updateProfile; +} + +void MainMenuScreen::selectFocusMode() { + if (currentMenu->at(menuIndex).optionValue != -1) { + uint8_t valueToSave = currentMenu->at(menuIndex).optionValue; + prevFocus = Storage::getInstance().getAddonOptions().focusModeOptions.enabled; + updateFocus = valueToSave; + + if (prevFocus != valueToSave) changeRequiresSave = true; + } +} + +int32_t MainMenuScreen::currentFocusMode() { + return updateFocus; +} + +void MainMenuScreen::selectTurboMode() { + if (currentMenu->at(menuIndex).optionValue != -1) { + uint8_t valueToSave = currentMenu->at(menuIndex).optionValue; + prevTurbo = Storage::getInstance().getAddonOptions().turboOptions.enabled; + updateTurbo = valueToSave; + + if (updateTurbo != valueToSave) changeRequiresSave = true; + } +} + +int32_t MainMenuScreen::currentTurboMode() { + return updateTurbo; +} diff --git a/src/eventmanager.cpp b/src/eventmanager.cpp index cf80b2eab..3ba513462 100644 --- a/src/eventmanager.cpp +++ b/src/eventmanager.cpp @@ -2,6 +2,10 @@ #include "storagemanager.h" #include "enums.pb.h" +void EventManager::init() { + clearEventHandlers(); +} + void EventManager::registerEventHandler(GPEventType eventType, EventFunction handler) { typename std::vector::iterator it = std::find_if(eventList.begin(), eventList.end(), [&eventType](const EventEntry& entry) { return entry.first == eventType; }); @@ -26,4 +30,8 @@ void EventManager::triggerEvent(GPEvent* event) { } } delete event; -} \ No newline at end of file +} + +void EventManager::clearEventHandlers() { + +} diff --git a/src/gamepad.cpp b/src/gamepad.cpp index b94902184..a202db22d 100644 --- a/src/gamepad.cpp +++ b/src/gamepad.cpp @@ -656,12 +656,47 @@ void Gamepad::processHotkeyAction(GamepadHotkey action) { reqSave = true; } break; + case HOTKEY_MENU_NAV_UP: + if (action != lastAction) { + EventManager::getInstance().triggerEvent(new GPMenuNavigateEvent(GpioAction::MENU_NAVIGATION_UP)); + } + break; + case HOTKEY_MENU_NAV_DOWN: + if (action != lastAction) { + EventManager::getInstance().triggerEvent(new GPMenuNavigateEvent(GpioAction::MENU_NAVIGATION_DOWN)); + } + break; + case HOTKEY_MENU_NAV_LEFT: + if (action != lastAction) { + EventManager::getInstance().triggerEvent(new GPMenuNavigateEvent(GpioAction::MENU_NAVIGATION_LEFT)); + } + break; + case HOTKEY_MENU_NAV_RIGHT: + if (action != lastAction) { + EventManager::getInstance().triggerEvent(new GPMenuNavigateEvent(GpioAction::MENU_NAVIGATION_RIGHT)); + } + break; + case HOTKEY_MENU_NAV_SELECT: + if (action != lastAction) { + EventManager::getInstance().triggerEvent(new GPMenuNavigateEvent(GpioAction::MENU_NAVIGATION_SELECT)); + } + break; + case HOTKEY_MENU_NAV_BACK: + if (action != lastAction) { + EventManager::getInstance().triggerEvent(new GPMenuNavigateEvent(GpioAction::MENU_NAVIGATION_BACK)); + } + break; + case HOTKEY_MENU_NAV_TOGGLE: + if (action != lastAction) { + EventManager::getInstance().triggerEvent(new GPMenuNavigateEvent(GpioAction::MENU_NAVIGATION_TOGGLE)); + } + break; default: // Unknown action return; } // only save if requested if (reqSave) { - Storage::getInstance().save(); + EventManager::getInstance().triggerEvent(new GPStorageSaveEvent(true)); } } diff --git a/src/gp2040.cpp b/src/gp2040.cpp index 28f8b01d0..49c1cfe5c 100644 --- a/src/gp2040.cpp +++ b/src/gp2040.cpp @@ -47,6 +47,9 @@ static const uint32_t REBOOT_HOTKEY_ACTIVATION_TIME_MS = 50; static const uint32_t REBOOT_HOTKEY_HOLD_TIME_MS = 4000; +const static uint32_t rebootDelayMs = 500; +static absolute_time_t rebootDelayTimeout = nil_time; + void GP2040::setup() { Storage::getInstance().init(); @@ -184,6 +187,9 @@ void GP2040::setup() { // before USB host will be used so we can force it to ignore the check Storage::getInstance().save(true); } + + // register system event handlers + EventManager::getInstance().registerEventHandler(GP_EVENT_STORAGE_SAVE, GPEVENT_CALLBACK(this->handleStorageSave(event))); } /** @@ -317,6 +323,24 @@ void GP2040::run() { addons.ProcessAddons(ADDON_PROCESS::CORE0_USBREPORT); tud_task(); // TinyUSB Task update + + if (rebootRequested) { + rebootRequested = false; + if (saveRequested) { + saveRequested = false; + Storage::getInstance().save(true); + } + rebootDelayTimeout = make_timeout_time_ms(rebootDelayMs); + } else { + if (saveRequested) { + saveRequested = false; + Storage::getInstance().save(true); + } + } + + if (!is_nil_time(rebootDelayTimeout) && time_reached(rebootDelayTimeout)) { + System::reboot(System::BootMode::DEFAULT); + } } } @@ -526,3 +550,12 @@ void GP2040::checkProcessedState(GamepadState prevState, GamepadState currState) EventManager::getInstance().triggerEvent(new GPAnalogProcessedMoveEvent(currState.lx, currState.ly, currState.rx, currState.ry, currState.lt, currState.rt)); } } + +void GP2040::handleStorageSave(GPEvent* e) { + saveRequested = true; + rebootRequested = ((GPStorageSaveEvent*)e)->restartAfterSave; +} + +void GP2040::handleSystemReboot(GPEvent* e) { + rebootRequested = true; +} diff --git a/src/interfaces/i2c/ssd1306/obd_ssd1306.cpp b/src/interfaces/i2c/ssd1306/obd_ssd1306.cpp index 759353c6e..456a142c3 100644 --- a/src/interfaces/i2c/ssd1306/obd_ssd1306.cpp +++ b/src/interfaces/i2c/ssd1306/obd_ssd1306.cpp @@ -46,6 +46,10 @@ void GPGFX_OBD_SSD1306::drawPixel(uint8_t x, uint8_t y, uint32_t color) { obdSetPixel(&obd, x, y, color, 1); } +uint32_t GPGFX_OBD_SSD1306::getPixel(uint8_t x, uint8_t y) { + return 0; +} + void GPGFX_OBD_SSD1306::drawText(uint8_t x, uint8_t y, std::string text, uint8_t invert) { obdWriteString(&obd, 0, x, y, (char*)text.c_str(), FONT_6x8, 0, 1); } @@ -62,7 +66,7 @@ void GPGFX_OBD_SSD1306::drawRectangle(uint16_t x, uint16_t y, uint16_t width, ui obdRectangle(&obd, x, y, width, height, color, filled); } -void GPGFX_OBD_SSD1306::drawSprite(uint8_t* spriteData, uint16_t width, uint16_t height, uint16_t pitch, uint16_t x, uint16_t y, uint8_t priority) { +void GPGFX_OBD_SSD1306::drawSprite(uint8_t* spriteData, uint16_t width, uint16_t height, uint16_t pitch, uint16_t x, uint16_t y, uint8_t priority, double scale) { obdDrawSprite(&obd, spriteData, width, height, pitch, x, y, priority); } diff --git a/src/interfaces/i2c/ssd1306/tiny_ssd1306.cpp b/src/interfaces/i2c/ssd1306/tiny_ssd1306.cpp index effa25bbb..de7872aac 100644 --- a/src/interfaces/i2c/ssd1306/tiny_ssd1306.cpp +++ b/src/interfaces/i2c/ssd1306/tiny_ssd1306.cpp @@ -114,6 +114,25 @@ void GPGFX_TinySSD1306::clear() { memset(frameBuffer, 0, MAX_SCREEN_SIZE); } +uint32_t GPGFX_TinySSD1306::getPixel(uint8_t x, uint8_t y) { + uint16_t row, bitIndex; + uint32_t result = 0; + + if ((xscreenType == ScreenAlternatives::SCREEN_132x64) { + x+=2; + } + + row=((y/8)*MAX_SCREEN_WIDTH)+x; + bitIndex=y % 8; + + result = (frameBuffer[row] >> bitIndex) && 0x01; + } + + return result; +} + void GPGFX_TinySSD1306::drawPixel(uint8_t x, uint8_t y, uint32_t color) { uint16_t row, bitIndex; @@ -395,20 +414,22 @@ void GPGFX_TinySSD1306::drawPolygon(uint16_t x, uint16_t y, uint16_t radius, uin } } -void GPGFX_TinySSD1306::drawSprite(uint8_t* image, uint16_t width, uint16_t height, uint16_t pitch, uint16_t x, uint16_t y, uint8_t priority) { +void GPGFX_TinySSD1306::drawSprite(uint8_t* image, uint16_t width, uint16_t height, uint16_t pitch, uint16_t x, uint16_t y, uint8_t priority, double scale) { uint8_t spriteByte; uint8_t spriteBit; uint8_t spriteX, spriteY; uint8_t color; - for (spriteY = 0; spriteY < height; spriteY++) { - for (spriteX = 0; spriteX < width; spriteX++) { + for (uint16_t scaledY = 0; scaledY < height * scale; ++scaledY) { + for (uint16_t scaledX = 0; scaledX < width * scale; ++scaledX) { + spriteX = scaledX / scale; + spriteY = scaledY / scale; + spriteBit = spriteX % 8; - //spriteByte = image[(spriteY * (width / 8)) + (spriteX / 8)]; spriteByte = image[(spriteY * ((width + 7) / 8)) + (spriteX / 8)]; color = ((spriteByte >> (7 - spriteBit)) & 0x01); - - drawPixel(x+spriteX, y+spriteY, color); + + drawPixel(x + scaledX, y + scaledY, color); } } } diff --git a/src/layoutmanager.cpp b/src/layoutmanager.cpp index 32e9c59fe..8a0a804c8 100644 --- a/src/layoutmanager.cpp +++ b/src/layoutmanager.cpp @@ -4,13 +4,35 @@ #include "enums.pb.h" LayoutManager::LayoutList LayoutManager::getLayoutA() { - uint16_t layoutLeft = Storage::getInstance().getDisplayOptions().buttonLayout; - return getLeftLayout(layoutLeft); + const DisplayOptions& options = Storage::getInstance().getDisplayOptions(); + uint16_t layoutLeft = options.buttonLayout; + if (options.buttonLayoutOrientation != BUTTON_ORIENTATION_DEFAULT) { + uint16_t layoutRight = options.buttonLayoutRight; + LayoutManager::LayoutList rightLayout = getRightLayout(layoutRight); + if (options.buttonLayoutOrientation == BUTTON_ORIENTATION_SWITCHED) { + return adjustByOffset(rightLayout, -64); + } else { + return adjustByOffset(flipHorizontally(rightLayout, 64, 0, 128, 0), -64); + } + } else { + return getLeftLayout(layoutLeft); + } } LayoutManager::LayoutList LayoutManager::getLayoutB() { - uint16_t layoutRight = Storage::getInstance().getDisplayOptions().buttonLayoutRight; - return getRightLayout(layoutRight); + const DisplayOptions& options = Storage::getInstance().getDisplayOptions(); + uint16_t layoutRight = options.buttonLayoutRight; + if (options.buttonLayoutOrientation != BUTTON_ORIENTATION_DEFAULT) { + uint16_t layoutLeft = options.buttonLayout; + LayoutManager::LayoutList leftLayout = getLeftLayout(layoutLeft); + if (options.buttonLayoutOrientation == BUTTON_ORIENTATION_SWITCHED) { + return adjustByOffset(leftLayout, 64); + } else { + return adjustByOffset(flipHorizontally(leftLayout, 0, 0, 64, 0), 64); + } + } else { + return getRightLayout(layoutRight); + } } std::string LayoutManager::getLayoutAName() { @@ -145,6 +167,8 @@ LayoutManager::LayoutList LayoutManager::getRightLayout(uint16_t index) { return this->drawCapcom6(); case BUTTON_LAYOUT_SEGA2P: return this->drawSega2p(); + case BUTTON_LAYOUT_SEGA2P_6B: + return this->drawSega2p6b(); case BUTTON_LAYOUT_NOIR8: return this->drawNoir8(); case BUTTON_LAYOUT_KEYBOARDB: @@ -155,6 +179,8 @@ LayoutManager::LayoutList LayoutManager::getRightLayout(uint16_t index) { return this->drawBlankB(); case BUTTON_LAYOUT_VLXB: return this->drawVLXB(); + case BUTTON_LAYOUT_VLXB_6B: + return this->drawVLXB6B(); case BUTTON_LAYOUT_FIGHTBOARD: return this->drawFightboard(); case BUTTON_LAYOUT_FIGHTBOARD_STICK_MIRRORED: @@ -255,6 +281,52 @@ LayoutManager::LayoutList LayoutManager::adjustByCustomSettings(LayoutManager::L return layout; } +LayoutManager::LayoutList LayoutManager::adjustByOffset(LayoutManager::LayoutList layout, int16_t originX, int16_t originY) { + if (layout.size() > 0) { + int16_t minX = INT16_MAX; + int16_t maxX = INT16_MIN; + + for (uint16_t elementCtr = 0; elementCtr < layout.size(); elementCtr++) { + int16_t newX = layout[elementCtr].parameters.x1 + originX; + if (((GPShape_Type)layout[elementCtr].parameters.shape == GP_SHAPE_ELLIPSE) || ((GPShape_Type)layout[elementCtr].parameters.shape == GP_SHAPE_POLYGON)) { + newX = (layout[elementCtr].parameters.x1-(layout[elementCtr].parameters.x2)) + originX; + } else if ((GPShape_Type)layout[elementCtr].parameters.shape == GP_SHAPE_SQUARE) { + if (originX > 0) newX = layout[elementCtr].parameters.x2 + originX; + } + minX = std::min(minX, newX); + maxX = std::max(maxX, newX); + } + + int16_t offsetX = 0; + if (minX < 0) { + offsetX = -minX; + } else if (maxX > 127) { + offsetX = 127 - maxX; + } + + // Apply the calculated adjustment to all objects + for (uint16_t elementCtr = 0; elementCtr < layout.size(); elementCtr++) { + layout[elementCtr].parameters.x1 += originX + offsetX; + if ((GPShape_Type)layout[elementCtr].parameters.shape == GP_SHAPE_SQUARE) { + layout[elementCtr].parameters.x2 += originX + offsetX; + } + layout[elementCtr].parameters.y1 += originY; // Apply y offset directly + } + } + return layout; +} + +LayoutManager::LayoutList LayoutManager::flipHorizontally(LayoutList layout, int16_t startX, int16_t startY, int16_t endX, int16_t endY) { + if (layout.size() > 0) { + for (uint16_t elementCtr = 0; elementCtr < layout.size(); elementCtr++) { + int16_t originalX = layout[elementCtr].parameters.x1; + + layout[elementCtr].parameters.x1 = (endX-1) - (originalX - startX); + } + } + return layout; +} + LayoutManager::LayoutList LayoutManager::drawStickless() { return BUTTON_GROUP_STICKLESS; @@ -345,6 +417,11 @@ LayoutManager::LayoutList LayoutManager::drawVLXB() return BUTTON_GROUP_VLXB; } +LayoutManager::LayoutList LayoutManager::drawVLXB6B() +{ + return BUTTON_GROUP_VLXB_6B; +} + LayoutManager::LayoutList LayoutManager::drawFightboard() { return BUTTON_GROUP_FIGHTBOARD; @@ -360,6 +437,11 @@ LayoutManager::LayoutList LayoutManager::drawSega2p() return BUTTON_GROUP_SEGA_2P; } +LayoutManager::LayoutList LayoutManager::drawSega2p6b() +{ + return BUTTON_GROUP_SEGA_2P_6B; +} + LayoutManager::LayoutList LayoutManager::drawNoir8() { return BUTTON_GROUP_NOIR8; diff --git a/www/server/app.js b/www/server/app.js index a7a3e1567..7cc605f2a 100644 --- a/www/server/app.js +++ b/www/server/app.js @@ -56,6 +56,7 @@ app.get('/api/getDisplayOptions', (req, res) => { invertDisplay: 1, buttonLayout: 0, buttonLayoutRight: 3, + buttonLayoutOrientation: 0, splashMode: 3, splashChoice: 0, splashDuration: 0, @@ -77,7 +78,18 @@ app.get('/api/getDisplayOptions', (req, res) => { }, displaySaverTimeout: 0, + displaySaverMode: 0, turnOffWhenSuspended: 0, + inputMode: 1, + turboMode: 1, + dpadMode: 1, + socdMode: 1, + macroMode: 1, + profileMode: 0, + inputHistoryEnabled: 0, + inputHistoryLength: 21, + inputHistoryCol: 0, + inputHistoryRow: 7, }; console.log('data', data); return res.send(data); @@ -510,10 +522,6 @@ app.get('/api/getAddonsOptions', (req, res) => { TurboInputEnabled: 1, WiiExtensionAddonEnabled: 1, SNESpadAddonEnabled: 1, - InputHistoryAddonEnabled: 1, - inputHistoryLength: 21, - inputHistoryCol: 0, - inputHistoryRow: 7, Analog1256Enabled: 1, analog1256Block: 0, analog1256CsPin: -1, diff --git a/www/src/Addons/InputHistory.tsx b/www/src/Addons/InputHistory.tsx deleted file mode 100644 index 1c59f6d24..000000000 --- a/www/src/Addons/InputHistory.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { FormCheck, Row } from 'react-bootstrap'; -import * as yup from 'yup'; - -import Section from '../Components/Section'; - -import FormControl from '../Components/FormControl'; - -export const inputHistoryScheme = { - InputHistoryAddonEnabled: yup - .number() - .required() - .label('Input History Enabled'), - inputHistoryLength: yup - .number() - .label('Input History Length') - .validateRangeWhenValue('InputHistoryAddonEnabled', 1, 21), - inputHistoryCol: yup - .number() - .label('Col') - .validateRangeWhenValue('InputHistoryAddonEnabled', 0, 20), - inputHistoryRow: yup - .number() - .label('Row') - .validateRangeWhenValue('InputHistoryAddonEnabled', 0, 7), -}; - -export const inputHistoryState = { - InputHistoryAddonEnabled: 0, - inputHistoryLength: 21, - inputHistoryCol: 0, - inputHistoryRow: 7, -}; - -const InputHistory = ({ values, errors, handleChange, handleCheckbox }) => { - const { t } = useTranslation(); - return ( -
- - { - handleCheckbox('InputHistoryAddonEnabled', values); - handleChange(e); - }} - /> -
- ); -}; - -export default InputHistory; diff --git a/www/src/Data/Pins.ts b/www/src/Data/Pins.ts index fc15ac324..f5ee927a6 100644 --- a/www/src/Data/Pins.ts +++ b/www/src/Data/Pins.ts @@ -74,6 +74,13 @@ export const BUTTON_ACTIONS = { BUTTON_PRESS_INPUT_REVERSE: 69, SUSTAIN_FOCUS_MODE: 70, SUSTAIN_4_8_WAY_MODE: 71, + MENU_NAVIGATION_UP: 72, + MENU_NAVIGATION_DOWN: 73, + MENU_NAVIGATION_LEFT: 74, + MENU_NAVIGATION_RIGHT: 75, + MENU_NAVIGATION_SELECT: 76, + MENU_NAVIGATION_BACK: 77, + MENU_NAVIGATION_TOGGLE: 78, } as const; export const PIN_DIRECTIONS = { diff --git a/www/src/Locales/en/DisplayConfig.jsx b/www/src/Locales/en/DisplayConfig.jsx index 05a69b7a6..66605678a 100644 --- a/www/src/Locales/en/DisplayConfig.jsx +++ b/www/src/Locales/en/DisplayConfig.jsx @@ -8,6 +8,10 @@ export default { 'hardware-header': 'Hardware Options', 'screen-header': 'Screen Options', 'layout-header': 'Layout Options', + 'mode-header': 'Mode Options', + 'button-layout-header': 'Button Layout', + 'status-layout-header': 'Status Bar Layout', + 'history-layout-header': 'Input History Layout', }, table: { header: @@ -25,6 +29,7 @@ export default { 'invert-display-label': 'Invert Display', 'button-layout-label': 'Button Layout (Left)', 'button-layout-right-label': 'Button Layout (Right)', + 'button-layout-orientation': 'Button Layout Orientation', 'button-layout-custom-header': 'Custom Button Layout Params', 'button-layout-custom-left-label': 'Layout Left', 'button-layout-custom-right-label': 'Layout Right', @@ -35,12 +40,46 @@ export default { 'splash-mode-label': 'Splash Mode', 'splash-duration-label': 'Splash Duration (seconds, 0 for Always On)', 'display-saver-timeout-label': 'Display Saver Timeout (minutes)', + 'screen-saver-mode-label': 'Display Saver Mode', 'inverted-label': 'Inverted', 'power-management-header': 'Power Management', 'turn-off-when-suspended': 'Turn Off When Suspended', - 'flip-display-none': 'None', - 'flip-display-flip': 'Flip', - 'flip-display-mirror': 'Mirror', - 'flip-display-flip-mirror': 'Flip and Mirror', + 'input-history-label': 'Input History', + 'display-state': { + 'disabled': 'Disabled', + 'enabled': 'Enabled' + }, + 'flip-display': { + 'none': 'None', + 'flip': 'Flip', + 'mirror': 'Mirror', + 'flip-mirror': 'Flip and Mirror', + }, + 'splash-modes': { + 'enabled': 'Enabled (Custom Splash Screen)', + 'close-in': 'Logo Close In', + 'close-in-custom': 'Logo Close In Custom', + 'disabled': 'Disabled', + }, + 'saver-modes': { + 'display-off': 'Display Off', + 'snow': 'Snow', + 'bounce': 'Logo Bounce', + 'pipes': 'Pipes', + 'toast': 'Toast', + }, + 'layout-modes': { + 'standard': 'Default', + 'southpaw': 'Southpaw', + 'switched': 'Switched', + }, + 'status-header': { + 'input-mode': 'Input Mode', + 'turbo-mode': 'Turbo', + 'dpad-mode': 'D-Pad Mode', + 'socd-mode': 'SOCD Mode', + 'macro-mode': 'Macro', + 'profile-mode': 'Profile', + }, }, }; diff --git a/www/src/Locales/en/LayoutConfig.jsx b/www/src/Locales/en/LayoutConfig.jsx index 168f176c8..1341b789b 100644 --- a/www/src/Locales/en/LayoutConfig.jsx +++ b/www/src/Locales/en/LayoutConfig.jsx @@ -49,12 +49,14 @@ export default { BUTTON_LAYOUT_CAPCOM: 'Capcom', BUTTON_LAYOUT_CAPCOM6: 'Capcom 6', BUTTON_LAYOUT_SEGA2P: 'Sega 2P', + BUTTON_LAYOUT_SEGA2P_6B: 'Sega 2P 6', BUTTON_LAYOUT_NOIR8: 'Noir 8', BUTTON_LAYOUT_KEYBOARDB: 'Keyboard', BUTTON_LAYOUT_DANCEPADB: 'Dancepad', BUTTON_LAYOUT_TWINSTICKB: 'Twinstick', BUTTON_LAYOUT_BLANKB: 'Blank', BUTTON_LAYOUT_VLXB: 'VLX', + BUTTON_LAYOUT_VLXB_6B: 'VLX 6', BUTTON_LAYOUT_FIGHTBOARD: 'Fightboard', BUTTON_LAYOUT_FIGHTBOARD_STICK_MIRRORED: 'Fightboard Mirrored', BUTTON_LAYOUT_CUSTOM: 'Custom', diff --git a/www/src/Locales/en/PinMapping.jsx b/www/src/Locales/en/PinMapping.jsx index 2c2fe2029..c4e6f036f 100644 --- a/www/src/Locales/en/PinMapping.jsx +++ b/www/src/Locales/en/PinMapping.jsx @@ -99,5 +99,12 @@ export default { BUTTON_PRESS_INPUT_REVERSE: 'Reverse Input', SUSTAIN_FOCUS_MODE: 'Focus Mode Enable', SUSTAIN_4_8_WAY_MODE: 'Toggle 4-Way Mode', + MENU_NAVIGATION_UP: 'Menu Up', + MENU_NAVIGATION_DOWN: 'Menu Down', + MENU_NAVIGATION_LEFT: 'Menu Left', + MENU_NAVIGATION_RIGHT: 'Menu Right', + MENU_NAVIGATION_SELECT: 'Menu Select', + MENU_NAVIGATION_BACK: 'Menu Back', + MENU_NAVIGATION_TOGGLE: 'Menu Toggle', }, }; diff --git a/www/src/Locales/en/SettingsPage.jsx b/www/src/Locales/en/SettingsPage.jsx index 2df00bc49..5c44e719e 100644 --- a/www/src/Locales/en/SettingsPage.jsx +++ b/www/src/Locales/en/SettingsPage.jsx @@ -130,6 +130,13 @@ export default { 'save-config': 'Save Config', 'next-profile': 'Next Profile', 'previous-profile': 'Previous Profile', + 'menu-nav-up': 'Menu Up', + 'menu-nav-down': 'Menu Down', + 'menu-nav-left': 'Menu Left', + 'menu-nav-right': 'Menu Right', + 'menu-nav-select': 'Menu Select', + 'menu-nav-back': 'Menu Back', + 'menu-nav-toggle': 'Menu Toggle', }, 'forced-setup-mode-label': 'Forced Setup Mode', 'forced-setup-mode-options': { diff --git a/www/src/Pages/AddonsConfigPage.jsx b/www/src/Pages/AddonsConfigPage.jsx index e3060842a..64b0369b6 100644 --- a/www/src/Pages/AddonsConfigPage.jsx +++ b/www/src/Pages/AddonsConfigPage.jsx @@ -47,10 +47,6 @@ import FocusMode, { } from '../Addons/FocusMode'; import Keyboard, { keyboardScheme, keyboardState } from '../Addons/Keyboard'; import GamepadUSBHost, { gamepadUSBHostScheme, gamepadUSBHostState} from '../Addons/GamepadUSBHost'; -import InputHistory, { - inputHistoryScheme, - inputHistoryState, -} from '../Addons/InputHistory'; import Rotary, { rotaryScheme, rotaryState } from '../Addons/Rotary'; import PCF8575, { pcf8575Scheme, pcf8575State } from '../Addons/PCF8575'; import DRV8833Rumble, { @@ -79,7 +75,6 @@ const schema = yup.object().shape({ ...wiiScheme, ...focusModeScheme, ...keyboardScheme, - ...inputHistoryScheme, ...rotaryScheme, ...pcf8575Scheme, ...drv8833RumbleScheme, @@ -104,7 +99,6 @@ const defaultValues = { ...snesState, ...focusModeState, ...keyboardState, - ...inputHistoryState, ...rotaryState, ...pcf8575State, ...drv8833RumbleState, @@ -130,7 +124,6 @@ const ADDONS = [ FocusMode, Keyboard, GamepadUSBHost, - InputHistory, Rotary, PCF8575, DRV8833Rumble, diff --git a/www/src/Pages/DisplayConfig.jsx b/www/src/Pages/DisplayConfig.jsx index 83c28e5a2..7c1e853ce 100644 --- a/www/src/Pages/DisplayConfig.jsx +++ b/www/src/Pages/DisplayConfig.jsx @@ -1,5 +1,5 @@ import React, { useContext, useEffect, useState, useRef } from 'react'; -import { Button, Form, Row, Col, FormLabel } from 'react-bootstrap'; +import { Button, Form, Row, Col, FormLabel, Tab, Tabs } from 'react-bootstrap'; import { Formik, useFormikContext, Field } from 'formik'; import chunk from 'lodash/chunk'; import * as yup from 'yup'; @@ -13,22 +13,36 @@ import Section from '../Components/Section'; import WebApi from '../Services/WebApi'; const ON_OFF_OPTIONS = [ - { label: 'Disabled', value: 0 }, - { label: 'Enabled', value: 1 }, + { label: 'form.display-state.disabled', value: 0 }, + { label: 'form.display-state.enabled', value: 1 }, ]; const SPLASH_MODES = [ - { label: 'Enabled (Custom Splash Screen)', value: 0 }, // STATICSPLASH - { label: 'Logo Close In', value: 1 }, // CLOSEIN - { label: 'Logo Close In Custom', value: 2 }, // CLOSEINCUSTOM - { label: 'Disabled', value: 3 }, // NOSPLASH + { label: 'form.splash-modes.enabled', value: 0 }, // STATICSPLASH + { label: 'form.splash-modes.close-in', value: 1 }, // CLOSEIN + { label: 'form.splash-modes.close-in-custom', value: 2 }, // CLOSEINCUSTOM + { label: 'form.splash-modes.disabled', value: 3 }, // NOSPLASH ]; const DISPLAY_FLIP_MODES = [ - { label: 'None', value: 0 }, - { label: 'Flip', value: 1 }, - { label: 'Mirror', value: 2 }, - { label: 'Flip and Mirror', value: 3 }, + { label: 'form.flip-display.none', value: 0 }, + { label: 'form.flip-display.flip', value: 1 }, + { label: 'form.flip-display.mirror', value: 2 }, + { label: 'form.flip-display.flip-mirror', value: 3 }, +]; + +const DISPLAY_SAVER_MODES = [ + { label: 'form.saver-modes.display-off', value: 0 }, + { label: 'form.saver-modes.snow', value: 1 }, + { label: 'form.saver-modes.bounce', value: 2 }, + { label: 'form.saver-modes.pipes', value: 3 }, + { label: 'form.saver-modes.toast', value: 4 }, +]; + +const LAYOUT_ORIENTATION = [ + { label: 'form.layout-modes.standard', value: 0 }, + { label: 'form.layout-modes.southpaw', value: 1 }, + { label: 'form.layout-modes.switched', value: 2 }, ]; const defaultValues = { @@ -37,6 +51,7 @@ const defaultValues = { invertDisplay: false, buttonLayout: 0, buttonLayoutRight: 3, + buttonLayoutOrientation: 0, splashDuration: 0, splashMode: 3, splashImage: Array(16 * 64).fill(0), // 128 columns represented by bytes so 16 and 64 rows @@ -58,6 +73,17 @@ const defaultValues = { }, }, displaySaverTimeout: 0, + displaySaverMode: 0, + inputMode: true, + turboMode: true, + dpadMode: true, + socdMode: true, + macroMode: true, + profileMode: false, + inputHistoryEnabled: false, + inputHistoryLength: 21, + inputHistoryCol: 0, + inputHistoryRow: 7, }; let buttonLayoutDefinitions = { buttonLayout: {}, buttonLayoutRight: {} }; @@ -79,6 +105,7 @@ const schema = yup.object().shape({ turnOffWhenSuspended: yup.number().label('Turn Off When Suspended'), buttonLayout: buttonLayoutSchema, buttonLayoutRight: buttonLayoutRightSchema, + buttonLayoutOrientation: yup.number().label('Layout Reversed'), splashMode: yup .number() .required() @@ -121,7 +148,18 @@ const schema = yup.object().shape({ }), }), splashDuration: yup.number().required().min(0).label('Splash Duration'), - displaySaverTimeout: yup.number().required().min(0).label('Display Saver'), + displaySaverTimeout: yup.number().required().min(0).label('Display Saver Timeout'), + displaySaverMode: yup.number().required().min(0).label('Screen Saver'), + inputMode: yup.number().label('Display Input Mode'), + turboMode: yup.number().label('Display Turbo Mode'), + dpadMode: yup.number().label('Display D-Pad Mode'), + socdMode: yup.number().label('Display SOCD Mode'), + macroMode: yup.number().label('Display Macro Mode'), + profileMode: yup.number().label('Display Profile Mode'), + inputHistoryEnabled: yup.number().label('Input History Enabled?'), + inputHistoryLength: yup.number().label('Input History Length'), + inputHistoryCol: yup.number().label('Input History Column Position'), + inputHistoryRow: yup.number().label('Input History Row Position'), }); const FormContext = () => { @@ -162,6 +200,18 @@ const FormContext = () => { values.splashDuration = parseInt(values.splashDuration); if (!!values.turnOffWhenSuspended) values.turnOffWhenSuspended = parseInt(values.turnOffWhenSuspended); + if (!!values.displaySaverMode) + values.displaySaverMode = parseInt(values.displaySaverMode); + if (!!values.inputMode) values.inputMode = parseInt(values.inputMode); + if (!!values.turboMode) values.turboMode = parseInt(values.turboMode); + if (!!values.dpadMode) values.dpadMode = parseInt(values.dpadMode); + if (!!values.socdMode) values.socdMode = parseInt(values.socdMode); + if (!!values.macroMode) values.macroMode = parseInt(values.macroMode); + if (!!values.profileMode) values.profileMode = parseInt(values.profileMode); + if (!!values.inputHistoryEnabled) values.inputHistoryEnabled = parseInt(values.inputHistoryEnabled); + if (!!values.inputHistoryLength) values.inputHistoryLength = parseInt(values.inputHistoryLength); + if (!!values.inputHistoryCol) values.inputHistoryCol = parseInt(values.inputHistoryCol); + if (!!values.inputHistoryRow) values.inputHistoryRow = parseInt(values.inputHistoryRow); await WebApi.setDisplayOptions(values, true); } @@ -185,6 +235,18 @@ const FormContext = () => { if (!!values.splashMode) values.splashMode = parseInt(values.splashMode); if (!!values.splashChoice) values.splashChoice = parseInt(values.splashChoice); + if (!!values.displaySaverMode) + values.displaySaverMode = parseInt(values.displaySaverMode); + if (!!values.inputMode) values.inputMode = parseInt(values.inputMode); + if (!!values.turboMode) values.turboMode = parseInt(values.turboMode); + if (!!values.dpadMode) values.dpadMode = parseInt(values.dpadMode); + if (!!values.socdMode) values.socdMode = parseInt(values.socdMode); + if (!!values.macroMode) values.macroMode = parseInt(values.macroMode); + if (!!values.profileMode) values.profileMode = parseInt(values.profileMode); + if (!!values.inputHistoryEnabled) values.inputHistoryEnabled = parseInt(values.inputHistoryEnabled); + if (!!values.inputHistoryLength) values.inputHistoryLength = parseInt(values.inputHistoryLength); + if (!!values.inputHistoryCol) values.inputHistoryCol = parseInt(values.inputHistoryCol); + if (!!values.inputHistoryRow) values.inputHistoryRow = parseInt(values.inputHistoryRow); await WebApi.setDisplayOptions(values, true); } @@ -209,13 +271,6 @@ export default function DisplayConfigPage() { const { t } = useTranslation(''); - DISPLAY_FLIP_MODES[0].label = t('DisplayConfig:form.flip-display-none'); - DISPLAY_FLIP_MODES[1].label = t('DisplayConfig:form.flip-display-flip'); - DISPLAY_FLIP_MODES[2].label = t('DisplayConfig:form.flip-display-mirror'); - DISPLAY_FLIP_MODES[3].label = t( - 'DisplayConfig:form.flip-display-flip-mirror', - ); - useEffect(() => { updatePeripherals(); }, []); @@ -268,396 +323,637 @@ export default function DisplayConfigPage() {
-

{t('DisplayConfig:section.hardware-header')}

- - + - {ON_OFF_OPTIONS.map((o, i) => ( - - ))} - - -

{t('DisplayConfig:section.screen-header')}

- - - {DISPLAY_FLIP_MODES.map((o, i) => ( - - ))} - - ( + + ))} + + + + - {ON_OFF_OPTIONS.map((o, i) => ( - - ))} - -
- - { - setFieldValue( - 'turnOffWhenSuspended', - e.target.checked ? 1 : 0, - ); - }} - /> -
- -

{t('DisplayConfig:section.layout-header')}

- - - {Object.keys(buttonLayoutDefinitions.buttonLayout).map( - (o, i) => ( - - ), - )} - - - {Object.keys( - buttonLayoutDefinitions.buttonLayoutRight, - ).map((o, i) => ( - + ))} + + - {t(`LayoutConfig:layouts.right.${o}`)} - - ))} - - ( + + ))} + +
+ + { + setFieldValue( + 'turnOffWhenSuspended', + e.target.checked ? 1 : 0, + ); + }} + /> +
+
+
+ - {SPLASH_MODES.map((o, i) => ( - - ))} - - - {isButtonLayoutCustom(values) && ( - - - {t('DisplayConfig:form.button-layout-custom-header')} - - - - - {t( - 'DisplayConfig:form.button-layout-custom-left-label', - )} - - - {Object.keys( - buttonLayoutDefinitions.buttonLayout, - ).map((o, i) => ( + {Object.keys(buttonLayoutDefinitions.buttonLayout).map( + (o, i) => ( - ))} - - - - - {t( - 'DisplayConfig:form.button-layout-custom-start-x-label', - )} - - - + ), + )} + + + {Object.keys( + buttonLayoutDefinitions.buttonLayoutRight, + ).map((o, i) => ( + + ))} + + + {LAYOUT_ORIENTATION.map((o, i) => ( + + ))} + + + {isButtonLayoutCustom(values) && ( + + + {t('DisplayConfig:form.button-layout-custom-header')} + + + + + {t( + 'DisplayConfig:form.button-layout-custom-left-label', + )} + + + {Object.keys( + buttonLayoutDefinitions.buttonLayout, + ).map((o, i) => ( + + ))} + + + + + {t( + 'DisplayConfig:form.button-layout-custom-start-x-label', + )} + + + + + + + + {t( + 'DisplayConfig:form.button-layout-custom-start-y-label', + )} + + + + + + + + {t( + 'DisplayConfig:form.button-layout-custom-button-radius-label', + )} + + + + + + + + {t( + 'DisplayConfig:form.button-layout-custom-button-padding-label', + )} + + + + + - - - - {t( - 'DisplayConfig:form.button-layout-custom-start-y-label', - )} - - - + + + + {t( + 'DisplayConfig:form.button-layout-custom-right-label', + )} + + + {Object.keys( + buttonLayoutDefinitions.buttonLayoutRight, + ).map((o, i) => ( + + ))} + + + + + {t( + 'DisplayConfig:form.button-layout-custom-start-x-label', + )} + + + + + + + + {t( + 'DisplayConfig:form.button-layout-custom-start-y-label', + )} + + + + + + + + {t( + 'DisplayConfig:form.button-layout-custom-button-radius-label', + )} + + + + + + + + {t( + 'DisplayConfig:form.button-layout-custom-button-padding-label', + )} + + + + + - - - - {t( - 'DisplayConfig:form.button-layout-custom-button-radius-label', + + )} +

{t('DisplayConfig:section.status-layout-header')}

+ +
+ - - - - - - - {t( - 'DisplayConfig:form.button-layout-custom-button-padding-label', + type="switch" + name="inputMode" + className="align-middle" + isInvalid={false} + checked={Boolean(values.inputMode)} + onChange={(e) => { + setFieldValue( + 'inputMode', + e.target.checked ? 1 : 0, + ); + }} + /> +
+
+ - - - - - - - - - {t( - 'DisplayConfig:form.button-layout-custom-right-label', + type="switch" + name="turboMode" + className="align-middle" + isInvalid={false} + checked={Boolean(values.turboMode)} + onChange={(e) => { + setFieldValue( + 'turboMode', + e.target.checked ? 1 : 0, + ); + }} + /> +
+
+ - - {Object.keys( - buttonLayoutDefinitions.buttonLayoutRight, - ).map((o, i) => ( - - ))} - - - - - {t( - 'DisplayConfig:form.button-layout-custom-start-x-label', + type="switch" + name="dpadMode" + className="align-middle" + isInvalid={false} + checked={Boolean(values.dpadMode)} + onChange={(e) => { + setFieldValue( + 'dpadMode', + e.target.checked ? 1 : 0, + ); + }} + /> +
+
+ - - - - - - - {t( - 'DisplayConfig:form.button-layout-custom-start-y-label', + type="switch" + name="displaySocdMode" + className="align-middle" + isInvalid={false} + checked={Boolean(values.socdMode)} + onChange={(e) => { + setFieldValue( + 'socdMode', + e.target.checked ? 1 : 0, + ); + }} + /> +
+
+ - - - - - - - {t( - 'DisplayConfig:form.button-layout-custom-button-radius-label', + type="switch" + name="macroMode" + className="align-middle" + isInvalid={false} + checked={Boolean(values.macroMode)} + onChange={(e) => { + setFieldValue( + 'macroMode', + e.target.checked ? 1 : 0, + ); + }} + /> +
+
+ - - - - - - - {t( - 'DisplayConfig:form.button-layout-custom-button-padding-label', + type="switch" + name="profileMode" + className="align-middle" + isInvalid={false} + checked={Boolean(values.profileMode)} + onChange={(e) => { + setFieldValue( + 'profileMode', + e.target.checked ? 1 : 0, + ); + }} + /> +
+
+

{t('DisplayConfig:section.history-layout-header')}

+ +
+ + - - - - - - - )} - - - - - - - {({ - field, // { name, value, onChange, onBlur } - form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc. - }) => ( -
- - onChangeCanvas(base64, form, field) - } - value={field.value} + type="switch" + name="inputHistoryEnabled" + className="align-middle mt-1" + isInvalid={false} + checked={Boolean(values.inputHistoryEnabled)} + onChange={(e) => { + setFieldValue( + 'inputHistoryEnabled', + e.target.checked ? 1 : 0, + ); + }} />
- )} -
-
+ + + + + + + + + {SPLASH_MODES.map((o, i) => ( + + ))} + + + + + + {DISPLAY_SAVER_MODES.map((o, i) => ( + + ))} + + + + + + {({ + field, // { name, value, onChange, onBlur } + form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc. + }) => ( +
+ + onChangeCanvas(base64, form, field) + } + value={field.value} + /> +
+ )} +
+
+
+