diff --git a/CMakeLists.txt b/CMakeLists.txt index d90bbb10a..d01595efa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,6 +157,7 @@ src/addons/slider_socd.cpp src/addons/wiiext.cpp src/addons/input_macro.cpp src/addons/snes_input.cpp +src/addons/inputhistory.cpp src/gamepad/GamepadDebouncer.cpp src/gamepad/GamepadDescriptors.cpp src/addons/tilt.cpp diff --git a/headers/addons/i2cdisplay.h b/headers/addons/i2cdisplay.h index df18953a8..f9ff1ebe4 100644 --- a/headers/addons/i2cdisplay.h +++ b/headers/addons/i2cdisplay.h @@ -16,6 +16,7 @@ #include "peripheralmanager.h" #include "peripheral_i2c.h" #include "peripheral_spi.h" +#include "addons/inputhistory.h" #ifndef HAS_I2C_DISPLAY #define HAS_I2C_DISPLAY -1 @@ -149,6 +150,7 @@ class I2CDisplayAddon : public GPAddon virtual void preprocess() {} virtual void process(); virtual std::string name() { return I2CDisplayName; } + virtual void attachInputHistoryAddon(InputHistoryAddon*); private: int initDisplay(int typeOverride); bool isSH1106(int detectedDisplay); @@ -222,6 +224,8 @@ class I2CDisplayAddon : public GPAddon bool isFocusModeEnabled; bool focusModePrevState; bool turnOffWhenSuspended; + bool isInputHistoryEnabled; + InputHistoryAddon* inputHistoryAddon; }; #endif diff --git a/headers/addons/inputhistory.h b/headers/addons/inputhistory.h new file mode 100644 index 000000000..13f4d2a33 --- /dev/null +++ b/headers/addons/inputhistory.h @@ -0,0 +1,54 @@ +#ifndef INPUT_HISTORY_H_ +#define INPUT_HISTORY_H_ + +#include "OneBitDisplay.h" + +#include +#include +#include "gpaddon.h" +#include +#include + +#ifndef INPUT_HISTORY_ENABLED +#define INPUT_HISTORY_ENABLED 0 +#endif + +#ifndef INPUT_HISTORY_LENGTH +#define INPUT_HISTORY_LENGTH 21 +#endif + +#ifndef INPUT_HISTORY_COL +#define INPUT_HISTORY_COL 0 +#endif + +#ifndef INPUT_HISTORY_ROW +#define INPUT_HISTORY_ROW 7 +#endif + +#define InputHistoryName "InputHistory" + +class InputHistoryAddon : public GPAddon +{ +public: + virtual bool available(); + virtual void setup(); + virtual void preprocess() {} + virtual void process(); + virtual std::string name() { return InputHistoryName; } + virtual void drawHistory(OBDISP *pOBD); +private: + uint32_t historyLength; + uint32_t col; + uint32_t row; + std::string historyString; + std::deque inputHistory; + std::array lastInput; + Gamepad* gamepad; + Gamepad* pGamepad; + bool pressedUp(); + bool pressedDown(); + bool pressedLeft(); + bool pressedRight(); +}; + +#endif diff --git a/proto/config.proto b/proto/config.proto index 12d233a0c..9c9da2f6c 100644 --- a/proto/config.proto +++ b/proto/config.proto @@ -660,6 +660,14 @@ message MacroOptions repeated Macro macroList = 3 [(nanopb).max_count = 6]; } +message InputHistoryOptions +{ + optional bool enabled = 1; + optional uint32 length = 2; + optional uint32 col = 3; + optional uint32 row = 4; +} + message AddonOptions { optional BootselButtonOptions bootselButtonOptions = 1; @@ -682,6 +690,7 @@ message AddonOptions optional TiltOptions tiltOptions = 18; optional PSPassthroughOptions psPassthroughOptions = 19; optional MacroOptions macroOptions = 20; + optional InputHistoryOptions inputHistoryOptions = 21; } message MigrationHistory diff --git a/site/docs/add-ons/input-history.mdx b/site/docs/add-ons/input-history.mdx new file mode 100644 index 000000000..b593fa186 --- /dev/null +++ b/site/docs/add-ons/input-history.mdx @@ -0,0 +1,55 @@ +--- +title: Input History +# tags: +# - +pagination_next: null +pagination_prev: null +description: "Add-on to show input history" +--- + +# Input History + +Purpose: This add-on is intended to allow you to display input history + +![GP2040-CE Configurator - Add-Ons Input History](../assets/images/gpc-add-ons-input-history.png) + +## Web Configurator Options + +- `History Length` - Set the max character length of the history. +- `Column` - Specify the column at which the history begins on the display. ( 0 - left, 20 - right ) +- `Row` - Specify the row on which the history appears on the display. ( 0 - top, 7 - bottom ) + +## Hardware + +### Requirements + +An already configured [monochrome display](../web-configurator/menu-pages/07-display-configuration.mdx). + +### Installation + +None. + +## Miscellaneous Notes + +Input - Symbol mapping: + +| GP2040-CE | PS3 | Switch | XINPUT | KEYBOARD | PS4 | CONFIG | +|-----------|:---:|:------:|:------:|:--------:|:---:|:------:| +| Up | U | U | U | U | U | U | +| Down | D | D | D | D | D | D | +| Left | L | L | L | L | L | L | +| Right | R | R | R | R | R | R | +| B1 | X | B | A | B1 | X | B1 | +| B2 | O | A | B | B2 | O | B2 | +| B3 | # | Y | X | B3 | # | B3 | +| B4 | ^ | X | Y | B4 | ^ | B4 | +| L1 | L1 | L | LB | L1 | L1 | L1 | +| R1 | R1 | R | RB | R1 | R1 | R1 | +| L2 | L2 | ZL | LT | L2 | L2 | L2 | +| R2 | R2 | ZR | RT | R2 | R2 | R2 | +| S1 | SL | - | BK | S1 | SH | S1 | +| S2 | ST | + | ST | S2 | OP | S2 | +| L3 | L3 | LS | LS | L3 | L3 | L3 | +| R3 | R3 | RS | RS | R3 | R3 | R3 | +| A1 | PS | H | G | A1 | PS | A1 | +| A2 | A2 | C | A2 | A2 | T | A2 | diff --git a/site/docs/assets/images/gpc-add-ons-input-history.png b/site/docs/assets/images/gpc-add-ons-input-history.png new file mode 100644 index 000000000..c49d1e3a6 Binary files /dev/null and b/site/docs/assets/images/gpc-add-ons-input-history.png differ diff --git a/site/docs/web-configurator/menu-pages/08-add-ons-configuration.mdx b/site/docs/web-configurator/menu-pages/08-add-ons-configuration.mdx index 5a6a77c1e..23157104e 100644 --- a/site/docs/web-configurator/menu-pages/08-add-ons-configuration.mdx +++ b/site/docs/web-configurator/menu-pages/08-add-ons-configuration.mdx @@ -26,6 +26,7 @@ Some of these add-ons are experimental and not all add-ons are interoperable wit - [Extra Button Configuration](../../add-ons/extra-button.mdx) - [Focus Mode Configuration](../../add-ons/focus-mode.mdx) - [I2C Analog ADS1219](../../add-ons/i2c-analog-ads1219.mdx) +- [Input History](../../add-ons/input-history.mdx) - [Input Reverse](../../add-ons/input-reverse.mdx) - [Joystick Selection Slider](../../add-ons/joystick-selection-slider.mdx) - [Keyboard Host Configuration](../../add-ons/keyboard-host.mdx) diff --git a/src/addons/i2cdisplay.cpp b/src/addons/i2cdisplay.cpp index 6fe08a5df..bef4e2996 100644 --- a/src/addons/i2cdisplay.cpp +++ b/src/addons/i2cdisplay.cpp @@ -54,6 +54,9 @@ void I2CDisplayAddon::setup() { displaySaverTimeout = displaySaverTimer; configMode = Storage::getInstance().GetConfigMode(); turnOffWhenSuspended = options.turnOffWhenSuspended; + + const InputHistoryOptions& inputHistoryOptions = Storage::getInstance().getAddonOptions().inputHistoryOptions; + isInputHistoryEnabled = inputHistoryOptions.enabled; } bool I2CDisplayAddon::isDisplayPowerOff() @@ -75,7 +78,7 @@ bool I2CDisplayAddon::isDisplayPowerOff() } else if (!!displaySaverTimeout && displaySaverTimer <= 0) { setDisplayPower(0); } - + if (isFocusModeEnabled) { const FocusModeOptions& focusModeOptions = Storage::getInstance().getAddonOptions().focusModeOptions; bool isFocusModeActive = !gpio_get(focusModeOptions.pin); @@ -131,37 +134,37 @@ void I2CDisplayAddon::process() { switch (options.buttonLayout) { case BUTTON_LAYOUT_STICK: - drawArcadeStick(8, 28, 8, 2); + drawArcadeStick(8, (isInputHistoryEnabled ? 22 : 28), 8, 2); break; case BUTTON_LAYOUT_STICKLESS: drawStickless(8, 20, 8, 2); break; case BUTTON_LAYOUT_BUTTONS_ANGLED: - drawWasdBox(8, 28, 7, 3); + drawWasdBox(8, (isInputHistoryEnabled ? 22 : 28), 7, 3); break; case BUTTON_LAYOUT_BUTTONS_BASIC: - drawUDLR(8, 28, 8, 2); + drawUDLR(8, (isInputHistoryEnabled ? 22 : 28), 8, 2); break; case BUTTON_LAYOUT_KEYBOARD_ANGLED: - drawKeyboardAngled(18, 28, 5, 2); + drawKeyboardAngled(18, (isInputHistoryEnabled ? 24 : 28), 5, 2); break; case BUTTON_LAYOUT_KEYBOARDA: - drawMAMEA(8, 28, 10, 1); + drawMAMEA(8, (isInputHistoryEnabled ? 22 : 28), 10, 1); break; case BUTTON_LAYOUT_OPENCORE0WASDA: - drawOpenCore0WASDA(16, 28, 10, 1); + drawOpenCore0WASDA(16, (isInputHistoryEnabled ? 22 : 28), 10, 1); break; case BUTTON_LAYOUT_DANCEPADA: - drawDancepadA(39, 12, 15, 2); + drawDancepadA(39, (isInputHistoryEnabled ? 10 : 12), (isInputHistoryEnabled ? 13 : 15), 2); break; case BUTTON_LAYOUT_TWINSTICKA: - drawTwinStickA(8, 28, 8, 2); + drawTwinStickA(8, (isInputHistoryEnabled ? 22 : 28), 8, 2); break; case BUTTON_LAYOUT_BLANKA: drawBlankA(0, 0, 0, 0); break; case BUTTON_LAYOUT_VLXA: - drawVLXA(7, 28, 7, 2); + drawVLXA(7, (isInputHistoryEnabled ? 22 : 28), 7, 2); break; case BUTTON_LAYOUT_CUSTOMA: drawButtonLayoutLeft(buttonLayoutCustomOptions.paramsLeft); @@ -176,52 +179,52 @@ void I2CDisplayAddon::process() { switch (options.buttonLayoutRight) { case BUTTON_LAYOUT_ARCADE: - drawArcadeButtons(8, 28, 8, 2); + drawArcadeButtons(8, (isInputHistoryEnabled ? 22 : 28), 8, 2); break; case BUTTON_LAYOUT_STICKLESSB: drawSticklessButtons(8, 20, 8, 2); break; case BUTTON_LAYOUT_BUTTONS_ANGLEDB: - drawWasdButtons(8, 28, 7, 3); + drawWasdButtons(8, (isInputHistoryEnabled ? 22 : 28), 7, 3); break; case BUTTON_LAYOUT_VEWLIX: - drawVewlix(8, 28, 8, 2); + drawVewlix(8, (isInputHistoryEnabled ? 22 : 28), 8, 2); break; case BUTTON_LAYOUT_VEWLIX7: - drawVewlix7(8, 28, 8, 2); + drawVewlix7(8, (isInputHistoryEnabled ? 22 : 28), 8, 2); break; case BUTTON_LAYOUT_CAPCOM: - drawCapcom(6, 28, 8, 2); + drawCapcom(6, (isInputHistoryEnabled ? 22 : 28), 8, 2); break; case BUTTON_LAYOUT_CAPCOM6: - drawCapcom6(16, 28, 8, 2); + drawCapcom6(16, (isInputHistoryEnabled ? 22 : 28), 8, 2); break; case BUTTON_LAYOUT_SEGA2P: - drawSega2p(8, 28, 8, 2); + drawSega2p(8, (isInputHistoryEnabled ? 22 : 28), 8, 2); break; case BUTTON_LAYOUT_NOIR8: - drawNoir8(8, 28, 8, 2); + drawNoir8(8, (isInputHistoryEnabled ? 22 : 28), 8, 2); break; case BUTTON_LAYOUT_KEYBOARDB: - drawMAMEB(68, 28, 10, 1); + drawMAMEB(68, (isInputHistoryEnabled ? 22 : 28), 10, 1); break; case BUTTON_LAYOUT_KEYBOARD8B: - drawMAME8B(68, 28, 10, 1); + drawMAME8B(68, (isInputHistoryEnabled ? 22 : 28), 10, 1); break; case BUTTON_LAYOUT_OPENCORE0WASDB: - drawOpenCore0WASDB(68, 28, 10, 1); - break; + drawOpenCore0WASDB(68, (isInputHistoryEnabled ? 22 : 28), 10, 1); + break; case BUTTON_LAYOUT_DANCEPADB: - drawDancepadB(39, 12, 15, 2); + drawDancepadB(39, (isInputHistoryEnabled ? 10 : 12), (isInputHistoryEnabled ? 13 : 15), 2); break; case BUTTON_LAYOUT_TWINSTICKB: - drawTwinStickB(100, 28, 8, 2); + drawTwinStickB(100, (isInputHistoryEnabled ? 22 : 28), 8, 2); break; case BUTTON_LAYOUT_BLANKB: drawSticklessButtons(0, 0, 0, 0); break; case BUTTON_LAYOUT_VLXB: - drawVLXB(6, 28, 7, 2); + drawVLXB(6, (isInputHistoryEnabled ? 22 : 28), 7, 2); break; case BUTTON_LAYOUT_CUSTOMB: drawButtonLayoutRight(buttonLayoutCustomOptions.paramsRight); @@ -233,6 +236,11 @@ void I2CDisplayAddon::process() { drawArcadeStick(90, 22, 8, 2); break; } + + if(isInputHistoryEnabled && inputHistoryAddon != nullptr) { + inputHistoryAddon->drawHistory(&obd); + } + break; } @@ -376,7 +384,7 @@ void I2CDisplayAddon::drawButtonLayoutLeft(ButtonLayoutParamsLeft& options) break; case BUTTON_LAYOUT_OPENCORE0WASDA: drawOpenCore0WASDA(startX, startY, buttonRadius, buttonPadding); - break; + break; case BUTTON_LAYOUT_DANCEPADA: drawDancepadA(startX, startY, buttonRadius, buttonPadding); break; @@ -488,7 +496,7 @@ void I2CDisplayAddon::drawStickless(int startX, int startY, int buttonRadius, in obdPreciseEllipse(&obd, startX, startY, buttonRadius, buttonRadius, 1, pressedLeft()); obdPreciseEllipse(&obd, startX + buttonMargin, startY, buttonRadius, buttonRadius, 1, pressedDown()); obdPreciseEllipse(&obd, startX + (buttonMargin * 1.875), startY + (buttonMargin / 2), buttonRadius, buttonRadius, 1, pressedRight()); - obdPreciseEllipse(&obd, startX + (buttonMargin * 2.25), startY + buttonMargin * 1.875, buttonRadius, buttonRadius, 1, pressedUp()); + obdPreciseEllipse(&obd, startX + (buttonMargin * (isInputHistoryEnabled ? 1.875 : 2.25)), startY + buttonMargin * (isInputHistoryEnabled ? 1.5 : 1.875), buttonRadius, buttonRadius, 1, pressedUp()); } void I2CDisplayAddon::drawWasdBox(int startX, int startY, int buttonRadius, int buttonPadding) @@ -1103,3 +1111,7 @@ bool I2CDisplayAddon::pressedRight() return false; } + +void I2CDisplayAddon::attachInputHistoryAddon(InputHistoryAddon* pInputHistoryAddon) { + inputHistoryAddon = pInputHistoryAddon; +} diff --git a/src/addons/inputhistory.cpp b/src/addons/inputhistory.cpp new file mode 100644 index 000000000..daef24bb4 --- /dev/null +++ b/src/addons/inputhistory.cpp @@ -0,0 +1,198 @@ +#include "addons/inputhistory.h" +#include "storagemanager.h" +#include "math.h" +#include "usb_driver.h" +#include "helper.h" +#include "config.pb.h" + +static const std::string displayNames[][18] = { + { // HID / DINPUT + "U", "D", "L", "R", + "X", "O", "#", "^", + "L1", "R1", "L2", "R2", + "SL", "ST", "L3", "R3", "PS", "A2" + }, + { // Switch + "U", "D", "L", "R", + "B", "A", "Y", "X", + "L", "R", "ZL", "ZR", + "-", "+", "LS", "RS", "H", "C" + }, + { // XInput + "U", "D", "L", "R", + "A", "B", "X", "Y", + "LB", "RB", "LT", "RT", + "BK", "ST", "LS", "RS", "G", "A2" + }, + { // Keyboard / HID-KB + "U", "D", "L", "R", + "B1", "B2", "B3", "B4", + "L1", "R1", "L2", "R2", + "S1", "S2", "L3", "R3", "A1", "A2" + }, + { // PS4 + "U", "D", "L", "R", + "X", "O", "#", "^", + "L1", "R1", "L2", "R2", + "SH", "OP", "L3", "R3", "PS", "T" + }, + { // Config + "U", "D", "L", "R", + "B1", "B2", "B3", "B4", + "L1", "R1", "L2", "R2", + "S1", "S2", "L3", "R3", "A1", "A2" + } +}; + +bool InputHistoryAddon::available() { + const DisplayOptions& displayOptions = Storage::getInstance().getDisplayOptions(); + const InputHistoryOptions& options = Storage::getInstance().getAddonOptions().inputHistoryOptions; + return displayOptions.enabled && options.enabled; +} + +void InputHistoryAddon::setup() { + const InputHistoryOptions& options = Storage::getInstance().getAddonOptions().inputHistoryOptions; + historyLength = options.length; + col = options.col; + row = options.row; + + gamepad = Storage::getInstance().GetGamepad(); + pGamepad = Storage::getInstance().GetProcessedGamepad(); +} + +void InputHistoryAddon::process() { + std::deque pressed; + + // Get key states + std::array currentInput = { + pressedUp(), + pressedDown(), + pressedLeft(), + pressedRight(), + gamepad->pressedB1(), + gamepad->pressedB2(), + gamepad->pressedB3(), + gamepad->pressedB4(), + gamepad->pressedL1(), + gamepad->pressedR1(), + gamepad->pressedL2(), + gamepad->pressedR2(), + gamepad->pressedS1(), + gamepad->pressedS2(), + gamepad->pressedL3(), + gamepad->pressedR3(), + gamepad->pressedA1(), + gamepad->pressedA2(), + }; + + uint8_t mode; + switch (gamepad->getOptions().inputMode) + { + case INPUT_MODE_HID: mode=0; break; + case INPUT_MODE_SWITCH: mode=1; break; + case INPUT_MODE_XINPUT: mode=2; break; + case INPUT_MODE_KEYBOARD: mode=3; break; + case INPUT_MODE_PS4: mode=4; break; + case INPUT_MODE_CONFIG: mode=5; break; + default: mode=0; break; + } + + // Check if any new keys have been pressed + if (lastInput != currentInput) { + // Iterate through array + for (uint8_t x=0; x<18; x++) { + // Add any pressed keys to deque + if (currentInput[x]) pressed.push_back(displayNames[mode][x]); + } + // Update the last keypress array + lastInput = currentInput; + } + + if (pressed.size() > 0) { + std::string newInput; + for(const auto &s : pressed) { + if(!newInput.empty()) + newInput += "+"; + newInput += s; + } + + inputHistory.push_back(newInput); + } + + if (inputHistory.size() > (historyLength / 2) + 1) { + inputHistory.pop_front(); + } + + std::string ret; + + for (auto it = inputHistory.crbegin(); it != inputHistory.crend(); ++it) { + std::string newRet = ret; + if (!newRet.empty()) + newRet = " " + newRet; + + newRet = *it + newRet; + ret = newRet; + + if (ret.size() >= historyLength) { + break; + } + } + + if(ret.size() >= historyLength) { + historyString = ret.substr(ret.size() - historyLength); + } else { + historyString = ret; + } +} + +void InputHistoryAddon::drawHistory(OBDISP *pOBD) { + obdWriteString(pOBD, 0, col * 6, row, (char *)historyString.c_str(), FONT_6x8, 0, 0); +} + +bool InputHistoryAddon::pressedUp() +{ + switch (gamepad->getOptions().dpadMode) + { + case DPAD_MODE_DIGITAL: return pGamepad->pressedUp(); + case DPAD_MODE_LEFT_ANALOG: return pGamepad->state.ly == GAMEPAD_JOYSTICK_MIN; + case DPAD_MODE_RIGHT_ANALOG: return pGamepad->state.ry == GAMEPAD_JOYSTICK_MIN; + } + + return false; +} + +bool InputHistoryAddon::pressedDown() +{ + switch (gamepad->getOptions().dpadMode) + { + case DPAD_MODE_DIGITAL: return pGamepad->pressedDown(); + case DPAD_MODE_LEFT_ANALOG: return pGamepad->state.ly == GAMEPAD_JOYSTICK_MAX; + case DPAD_MODE_RIGHT_ANALOG: return pGamepad->state.ry == GAMEPAD_JOYSTICK_MAX; + } + + return false; +} + +bool InputHistoryAddon::pressedLeft() +{ + switch (gamepad->getOptions().dpadMode) + { + case DPAD_MODE_DIGITAL: return pGamepad->pressedLeft(); + case DPAD_MODE_LEFT_ANALOG: return pGamepad->state.lx == GAMEPAD_JOYSTICK_MIN; + case DPAD_MODE_RIGHT_ANALOG: return pGamepad->state.rx == GAMEPAD_JOYSTICK_MIN; + } + + return false; +} + +bool InputHistoryAddon::pressedRight() +{ + switch (gamepad->getOptions().dpadMode) + { + case DPAD_MODE_DIGITAL: return pGamepad->pressedRight(); + case DPAD_MODE_LEFT_ANALOG: return pGamepad->state.lx == GAMEPAD_JOYSTICK_MAX; + case DPAD_MODE_RIGHT_ANALOG: return pGamepad->state.rx == GAMEPAD_JOYSTICK_MAX; + } + + return false; +} diff --git a/src/config_utils.cpp b/src/config_utils.cpp index a2ae2201c..d20fdc811 100644 --- a/src/config_utils.cpp +++ b/src/config_utils.cpp @@ -472,6 +472,12 @@ void ConfigUtils::initUnsetPropertiesWithDefaults(Config& config) INIT_UNSET_PROPERTY(config.addonOptions.buzzerOptions, pin, BUZZER_PIN); INIT_UNSET_PROPERTY(config.addonOptions.buzzerOptions, volume, BUZZER_VOLUME); + // 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 7e6ba112e..982157275 100644 --- a/src/configs/webconfig.cpp +++ b/src/configs/webconfig.cpp @@ -1224,6 +1224,12 @@ 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"); Pin_t oldKbPinDplus = keyboardHostOptions.pinDplus; @@ -1631,6 +1637,12 @@ 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, "keyboardHostPinDplus", keyboardHostOptions.pinDplus); diff --git a/src/gp2040aux.cpp b/src/gp2040aux.cpp index 3ddcc58ac..c425e2089 100644 --- a/src/gp2040aux.cpp +++ b/src/gp2040aux.cpp @@ -13,6 +13,7 @@ #include "addons/ps4mode.h" #include "addons/pspassthrough.h" #include "addons/neopicoleds.h" +#include "addons/inputhistory.h" #include @@ -23,9 +24,16 @@ GP2040Aux::~GP2040Aux() { } void GP2040Aux::setup() { + InputHistoryAddon* inputHistoryAddon = new InputHistoryAddon(); + I2CDisplayAddon* i2CDisplayAddon = new I2CDisplayAddon(); + + if(inputHistoryAddon->available() && i2CDisplayAddon->available()) + i2CDisplayAddon->attachInputHistoryAddon(inputHistoryAddon); + // Setup Add-ons addons.LoadUSBAddon(new PSPassthroughAddon(), CORE1_LOOP); - addons.LoadAddon(new I2CDisplayAddon(), CORE1_LOOP); + addons.LoadAddon(inputHistoryAddon, CORE1_LOOP); + addons.LoadAddon(i2CDisplayAddon, CORE1_LOOP); addons.LoadAddon(new NeoPicoLEDAddon(), CORE1_LOOP); addons.LoadAddon(new PlayerLEDAddon(), CORE1_LOOP); addons.LoadAddon(new BoardLedAddon(), CORE1_LOOP); diff --git a/www/server/app.js b/www/server/app.js index 83d887df6..8a1c65c2b 100644 --- a/www/server/app.js +++ b/www/server/app.js @@ -500,6 +500,10 @@ app.get('/api/getAddonsOptions', (req, res) => { WiiExtensionAddonEnabled: 1, SNESpadAddonEnabled: 1, PSPassthroughAddonEnabled: 1, + InputHistoryAddonEnabled: 1, + inputHistoryLength: 21, + inputHistoryCol: 0, + inputHistoryRow: 7, usedPins: Object.values(picoController), }); }); diff --git a/www/src/Addons/InputHistory.tsx b/www/src/Addons/InputHistory.tsx new file mode 100644 index 000000000..512259109 --- /dev/null +++ b/www/src/Addons/InputHistory.tsx @@ -0,0 +1,102 @@ +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/Locales/en/AddonsConfig.jsx b/www/src/Locales/en/AddonsConfig.jsx index 1e22f0e28..5c748efda 100644 --- a/www/src/Locales/en/AddonsConfig.jsx +++ b/www/src/Locales/en/AddonsConfig.jsx @@ -144,4 +144,8 @@ export default { 'pspassthrough-d-minus-label': 'D-', 'pspassthrough-five-v-label': '5V Power (optional)', 'pin-config-moved-to-core-text': 'Note: the pins for this add-on are now configured on the Pin Mapping page.', + 'input-history-header-text': 'Input History', + 'input-history-length-label': 'History length (characters)', + 'input-history-col-label': 'Column', + 'input-history-row-label': 'Row' }; diff --git a/www/src/Locales/pt-BR/AddonsConfig.jsx b/www/src/Locales/pt-BR/AddonsConfig.jsx index 931020d6c..df9d9b40c 100644 --- a/www/src/Locales/pt-BR/AddonsConfig.jsx +++ b/www/src/Locales/pt-BR/AddonsConfig.jsx @@ -154,4 +154,8 @@ export default { 'pspassthrough-d-plus-label': 'D+', 'pspassthrough-d-minus-label': 'D-', 'pspassthrough-five-v-label': 'Alimentação 5V (opcional)', + 'input-history-header-text': 'Histórico de Entrada', + 'input-history-length-label': 'Comprimento do histórico (caracteres)', + 'input-history-col-label': 'Coluna', + 'input-history-row-label': 'Linha', }; diff --git a/www/src/Locales/zh-CN/AddonsConfig.jsx b/www/src/Locales/zh-CN/AddonsConfig.jsx index fc86a9769..65b1ed946 100644 --- a/www/src/Locales/zh-CN/AddonsConfig.jsx +++ b/www/src/Locales/zh-CN/AddonsConfig.jsx @@ -138,4 +138,8 @@ export default { 'pspassthrough-d-plus-label': 'D+', 'pspassthrough-d-minus-label': 'D-', 'pspassthrough-five-v-label': '5V 电源(可选)', + 'input-history-header-text': '输入历史', + 'input-history-length-label': '历史长度(字符)', + 'input-history-col-label': '列', + 'input-history-row-label': '行' }; diff --git a/www/src/Pages/AddonsConfigPage.jsx b/www/src/Pages/AddonsConfigPage.jsx index 78fc3b015..625ce430f 100644 --- a/www/src/Pages/AddonsConfigPage.jsx +++ b/www/src/Pages/AddonsConfigPage.jsx @@ -43,6 +43,7 @@ import FocusMode, { focusModeState, } from '../Addons/FocusMode'; import Keyboard, { keyboardScheme, keyboardState } from '../Addons/Keyboard'; +import InputHistory, { inputHistoryScheme, inputHistoryState } from '../Addons/InputHistory'; const schema = yup.object().shape({ ...analogScheme, @@ -62,6 +63,7 @@ const schema = yup.object().shape({ ...wiiScheme, ...focusModeScheme, ...keyboardScheme, + ...inputHistoryScheme, }); const defaultValues = { @@ -83,6 +85,7 @@ const defaultValues = { ...snesState, ...focusModeState, ...keyboardState, + ...inputHistoryState, }; const ADDONS = [ @@ -104,6 +107,7 @@ const ADDONS = [ SNES, FocusMode, Keyboard, + InputHistory ]; const FormContext = ({ setStoredData }) => {