convert core GPIO pin mappings from button-to-pin to pin-to-action (#504)

* convert core GPIO pin mappings from button2pin to pin2action

this changes the core board config and gamepad behavior to be that the
settings/user configures what each pin does, rather than the old
behavior which configures what pin each button is assigned to. this
allows for the new configuration to have one canonical location for what
each of the pins does, and removes the limitation that a button can only
normally be activated by one pin --- now it is trivial to assign many
pins to take the same action

"actions" are all button presses right now, but they may be other things
in the future as this branch expands. this is just the core
functionality and migration code for now, further changes (removing
extra input addon, refactoring DDI, maybe refactoring the sliders,
working the profiles into this scheme, etc.) to come

* remove the extra button addon, it is unnecessary now

* unset deprecated pin mappings after migration

* unset the extra button addon pin after migration

* fix the processing of the aux state/Fn button

* renumber the non-managed pin enum entries

motivation for this is the migration sets everything to NONE anyway, so
the 0 value isn't super important to us, and protobuf by convention
seems to use the lowest value as a default (e.g. in the python GUI
editor), so setting that to NONE is idiomatic in both cases

* fix the unsetting of the extra input addon after migrating

* convert DDI pins to gpioMappings

the rest of the DDI settings remain untouched, and the whole addon is
basically where it always was, this just allows for centralization in
the new GPIO mappings

* add TODO for fixing the pin UI

* fix the checking of the DDI masks

* convert get/setPinMappings API to match refactor

* "hardcode" the writing of 30 pins to JSON

ArduinoJson retains the reference to the string until write time, so it
was only actually publishing the value for pin29

* convert the JS slider pins to the centralized config

there's not much left of the addon other than the config for what to do
when nothing is held, so, this is a great candidate for getting folded
into core

* rename default JS mode option for consistency w/SOCD slider

* preliminary SOCD slider port to gpioMappings

* correct JS slider migration to refer to proper config/defines

* correct SOCD slider migration by referring to proper settings

* Global pin state action example using zustand

* Export baseUrl

* Pin to action ui playground, using global state with zustand

* Hide som actions from dropdown. make the select disabled if action is set from board

* Add a form for semantics

* Add translation for labels, dynamic columns.

* Remove pin -> action PoC from playground

* Make savePins return promise

* Replace pin mapping with pin -> action

* Remove unused handler in usePinStore

* remove DDI pins from addon page, point at Pin Mapping page

* add more add-on pin usages to the UI

* flag miscellaneous addon pins as ASSIGNED in migration

* when saving addons/display pins in web config, mark them ASSIGNED

* don't allow profiles to touch RESERVED/ASSIGNED_TO_ADDON

this is partially a problem now (we shouldn't let a profile assign a pin
that is normally reserved or on a addon), and partially a problem in the
future (we shouldn't let a profile SET a pin to reserved/for an addon),
when the profiles are refactored to use gpio mappings

* Add better typing for usePinStore

* - Create a new Capture button component that is reusable for any input.
- Make new pin mapping handle labels for all input modes including swapTpShareLabels

* don't include ASSIGNED_TO_ADDON in getUsedPins

temporary measure as the addons' validation needs reworking to support
a response that includes their own pins

* don't allow setPinMappings to set/change addon/reserved pins

* addons API shouldn't refer to DDI pins anymore now that they're "core"

* remove unused API stuff from the dev server JSON

* DRY on the simple protobuf-or-boardconfig pin migrations

this also removes the protobuf isValidPin(foo) check and replaces it
with a has_ + a nested check that it's between 0 and 29, so that if a
user set a button to -1, we don't fallback to the BoardConfig.h, which
would clobber their intentional unmapping

* set ASSIGNED_TO_ADDON -> NONE if the new addon pin is invalid

* un-ASSIGNED_TO_ADDON the Dminus pins when unsetting the Dplus pins

* Remove double call on context

* Remove useState for selectedBoard as it's only set once

* Remove unused imports

* Remove setLoading on getPinMappings as this creates race conditions, remove log

* Map new pin actions to button mappings for profile settings

* Remove unused lodash import

* Remove export

* remove unused EXTRA_BUTTON_ENABLED define

* track when migrations have occurred

I believe that it would be possible, without this history, for someone
to unmap certain gpioMappings (or hotkeys) for intentional reasons and
then accidentally trigger a re-conversion of the old BoardConfig.h
values, meaning they could never unmap certain hotkeys or gpioMappings.
this bool avoids that by only running the migration code once ever

maybe it makes startup faster too, idk

* remove unnecessary save()s in slider addons

* define types for a pin and a gamepad mask

---------

Co-authored-by: ian <ian@vidales.se>
This commit is contained in:
Brian S. Stephan
2023-10-27 14:26:19 -05:00
committed by GitHub
parent 3a79f726b9
commit 691537077f
40 changed files with 1719 additions and 1295 deletions

View File

@@ -141,7 +141,6 @@ src/addons/bootsel_button.cpp
src/addons/focus_mode.cpp
src/addons/buzzerspeaker.cpp
src/addons/dualdirectional.cpp
src/addons/extra_button.cpp
src/addons/keyboard_host.cpp
src/addons/i2canalog1219.cpp
src/addons/jslider.cpp
@@ -222,4 +221,4 @@ install(FILES
if (NOT (DEFINED ENV(CI)) AND (EXISTS ${CMAKE_SOURCE_DIR}/modules/Custom.cmake))
message(STATUS "Found custom script.")
include(${CMAKE_SOURCE_DIR}/modules/Custom.cmake)
endif()
endif()

View File

@@ -249,7 +249,6 @@
// Extra Button Add-on setting
#define EXTRA_BUTTON_ENABLED 1
#define EXTRA_BUTTON_MASK GAMEPAD_MASK_DU // 0 means none, get other mask from GamepadState.h
// For directions, use GAMEPAD_MASK_DU, GAMEPAD_MASK_DD, GAMEPAD_MASK_DL and GAMEPAD_MASK_DR
#define EXTRA_BUTTON_PIN 1

View File

@@ -235,7 +235,6 @@
#define BOOTSEL_BUTTON_MASK 0 // 0 means none, get other mask from GamepadState.h
// Extra Button Add-on setting
#define EXTRA_BUTTON_ENABLED 1
#define EXTRA_BUTTON_MASK GAMEPAD_MASK_DU // 0 means none, get other mask from GamepadState.h
// For directions, use GAMEPAD_MASK_DU, GAMEPAD_MASK_DD, GAMEPAD_MASK_DL and GAMEPAD_MASK_DR
#define EXTRA_BUTTON_PIN 13

View File

@@ -67,12 +67,12 @@ private:
DpadDirection lastDualUD; // Dual Last Up-Down
DpadDirection lastDualLR; // Gamepad Last Left-Right
uint32_t dpadTime[4];
uint8_t pinDualDirDown;
uint8_t pinDualDirUp;
uint8_t pinDualDirLeft;
uint8_t pinDualDirRight;
uint8_t combineMode;
DpadMode dpadMode;
GamepadButtonMapping *mapDpadUp;
GamepadButtonMapping *mapDpadDown;
GamepadButtonMapping *mapDpadLeft;
GamepadButtonMapping *mapDpadRight;
};
#endif // _DualDirectional_H

View File

@@ -1,26 +0,0 @@
#ifndef _ExtraButton_H
#define _ExtraButton_H
#include "gpaddon.h"
#include "gamepad.h"
#ifndef EXTRA_BUTTON_ENABLED
#define EXTRA_BUTTON_ENABLED 0
#endif
// ExtraButton Module Name
#define ExtraButtonName "ExtraButton"
class ExtraButtonAddon : public GPAddon {
public:
virtual bool available();
virtual void setup(); // ExtraButton Setup
virtual void process() {} // ExtraButton Process
virtual void preprocess();
virtual std::string name() { return ExtraButtonName; }
private:
uint32_t extraButtonMap;
uint8_t extraButtonPin;
};
#endif // _ExtraButton_H_

View File

@@ -4,6 +4,7 @@
#include "gpaddon.h"
#include "GamepadEnums.h"
#include "types.h"
#ifndef JSLIDER_ENABLED
#define JSLIDER_ENABLED 0
@@ -45,6 +46,10 @@ private:
DpadMode dpadState; // Saved locally for debounce
DpadMode dDebState; // Debounce JSlider State
uint32_t uDebTime; // Debounce JSlider Time
Mask_t dpModeMask = 0;
Mask_t lsModeMask = 0;
Mask_t rsModeMask = 0;
};
#endif // _JSlider_H_
#endif // _JSlider_H_

View File

@@ -2,6 +2,7 @@
#define _SliderSOCD_H
#include "gpaddon.h"
#include "types.h"
#include "GamepadEnums.h"
@@ -45,11 +46,12 @@ private:
SOCDMode socdState; // Saved locally for debounce
SOCDMode dDebState; // Debounce SliderSOCD State
uint32_t uDebTime; // Debounce SliderSOCD Time
SOCDMode sliderSOCDModeOne;
SOCDMode sliderSOCDModeTwo;
SOCDMode sliderSOCDModeDefault;
uint8_t pinSliderSOCDOne;
uint8_t pinSliderSOCDTwo;
Mask_t upPrioModeMask = 0;
Mask_t neutralModeMask = 0;
Mask_t secondInputModeMask = 0;
Mask_t firstInputModeMask = 0;
Mask_t bypassModeMask = 0;
};
#endif // _SliderSOCD_H_
#endif // _SliderSOCD_H_

View File

@@ -2,6 +2,7 @@
#define _GAMEPAD_H_
#include "BoardConfig.h"
#include "types.h"
#include <string.h>
#include "enums.pb.h"
@@ -25,31 +26,13 @@ extern uint64_t getMicro();
struct GamepadButtonMapping
{
GamepadButtonMapping(uint8_t p, uint16_t bm) :
pin(p < NUM_BANK0_GPIOS ? p : 0xff),
pinMask(p < NUM_BANK0_GPIOS? (1 << p) : 0),
GamepadButtonMapping(Mask_t bm) :
pinMask(0),
buttonMask(bm)
{}
uint8_t pin;
uint32_t pinMask;
const uint16_t buttonMask;
inline void setPin(uint8_t p)
{
if (p < NUM_BANK0_GPIOS)
{
pin = p;
pinMask = 1 << p;
}
else
{
pin = 0xff;
pinMask = 0;
}
}
bool isAssigned() const { return pin != 0xff; }
};
#define GAMEPAD_DIGITAL_INPUT_COUNT 18 // Total number of buttons, including D-pad
@@ -173,7 +156,7 @@ public:
GamepadButtonMapping *mapButtonR3;
GamepadButtonMapping *mapButtonA1;
GamepadButtonMapping *mapButtonA2;
GamepadButtonMapping **gamepadMappings;
GamepadButtonMapping *mapButtonFn;
inline static const SOCDMode resolveSOCDMode(const GamepadOptions& options) {
return (options.socdMode == SOCD_MODE_BYPASS &&

View File

@@ -35,7 +35,9 @@ public:
GamepadOptions& getGamepadOptions() { return config.gamepadOptions; }
HotkeyOptions& getHotkeyOptions() { return config.hotkeyOptions; }
ForcedSetupOptions& getForcedSetupOptions() { return config.forcedSetupOptions; }
PinMappings& getPinMappings() { return config.pinMappings; }
PinMappings& getDeprecatedPinMappings() { return config.deprecatedPinMappings; }
GpioMappings& getGpioMappings() { return config.gpioMappings; }
GpioAction** getGpioMappingsArray() { return gpioMappingsArray; }
KeyboardMapping& getKeyboardMapping() { return config.keyboardMapping; }
DisplayOptions& getDisplayOptions() { return config.displayOptions; }
DisplayOptions& getPreviewDisplayOptions() { return previewDisplayOptions; }
@@ -43,11 +45,10 @@ public:
AddonOptions& getAddonOptions() { return config.addonOptions; }
AnimationOptions_Proto& getAnimationOptions() { return config.animationOptions; }
ProfileOptions& getProfileOptions() { return config.profileOptions; }
GpioAction* getProfilePinMappings() { return functionalPinMappings; }
bool save();
PinMappings& getProfilePinMappings();
// Perform saves that were enqueued from core1
void performEnqueuedSaves();
@@ -83,7 +84,8 @@ private:
critical_section_t animationOptionsCs;
uint32_t animationOptionsCrc = 0;
AnimationOptions animationOptionsToSave = {};
PinMappings* functionalPinMappings = nullptr;
GpioAction functionalPinMappings[NUM_BANK0_GPIOS];
GpioAction* gpioMappingsArray[NUM_BANK0_GPIOS];
};
#endif

13
headers/types.h Normal file
View File

@@ -0,0 +1,13 @@
/*
* SPDX-License-Identifier: MIT
* SPDX-FileCopyrightText: Copyright (c) 2023 Brian S. Stephan <bss@incorporeal.org>
*/
#ifndef TYPES_H_
#define TYPES_H_
// common types
#define Pin_t int32_t // signed to accommodate for -1
#define Mask_t uint32_t
#endif

View File

@@ -117,6 +117,40 @@ message PinMappings
optional int32 pinButtonFn = 19;
}
message GpioMappings
{
optional GpioAction pin00 = 1;
optional GpioAction pin01 = 2;
optional GpioAction pin02 = 3;
optional GpioAction pin03 = 4;
optional GpioAction pin04 = 5;
optional GpioAction pin05 = 6;
optional GpioAction pin06 = 7;
optional GpioAction pin07 = 8;
optional GpioAction pin08 = 9;
optional GpioAction pin09 = 10;
optional GpioAction pin10 = 11;
optional GpioAction pin11 = 12;
optional GpioAction pin12 = 13;
optional GpioAction pin13 = 14;
optional GpioAction pin14 = 15;
optional GpioAction pin15 = 16;
optional GpioAction pin16 = 17;
optional GpioAction pin17 = 18;
optional GpioAction pin18 = 19;
optional GpioAction pin19 = 20;
optional GpioAction pin20 = 21;
optional GpioAction pin21 = 22;
optional GpioAction pin22 = 23;
optional GpioAction pin23 = 24;
optional GpioAction pin24 = 25;
optional GpioAction pin25 = 26;
optional GpioAction pin26 = 27;
optional GpioAction pin27 = 28;
optional GpioAction pin28 = 29;
optional GpioAction pin29 = 30;
}
message AlternativePinMappings
{
@@ -313,23 +347,23 @@ message SliderOptions
{
optional bool enabled = 1;
optional int32 pinSliderOne = 2;
optional int32 pinSliderTwo = 3;
optional DpadMode modeOne = 4;
optional DpadMode modeTwo = 5;
optional DpadMode modeZero = 6;
optional int32 deprecatedPinSliderOne = 2;
optional int32 deprecatedPinSliderTwo = 3;
optional DpadMode deprecatedModeOne = 4;
optional DpadMode deprecatedModeTwo = 5;
optional DpadMode modeDefault = 6;
}
message SOCDSliderOptions
{
optional bool enabled = 1;
optional int32 pinOne = 2;
optional int32 pinTwo = 3;
optional int32 deprecatedPinOne = 2;
optional int32 deprecatedPinTwo = 3;
optional SOCDMode modeDefault = 4;
optional SOCDMode modeOne = 5;
optional SOCDMode modeTwo = 6;
optional SOCDMode deprecatedModeOne = 5;
optional SOCDMode deprecatedModeTwo = 6;
}
message ReverseOptions
@@ -360,10 +394,10 @@ message DualDirectionalOptions
{
optional bool enabled = 1;
optional int32 upPin = 2;
optional int32 downPin = 3;
optional int32 leftPin = 4;
optional int32 rightPin = 5;
optional int32 deprecatedUpPin = 2;
optional int32 deprecatedDownPin = 3;
optional int32 deprecatedLeftPin = 4;
optional int32 deprecatedRightPin = 5;
optional DpadMode dpadMode = 6;
optional uint32 combineMode = 7;
@@ -629,7 +663,7 @@ message AddonOptions
optional AnalogADS1219Options analogADS1219Options = 7;
optional DualDirectionalOptions dualDirectionalOptions = 8;
optional BuzzerOptions buzzerOptions = 9;
optional ExtraButtonOptions extraButtonOptions = 10;
optional ExtraButtonOptions deprecatedExtraButtonOptions = 10;
optional PlayerNumberOptions playerNumberOptions = 11;
optional PS4Options ps4Options = 12 [(nanopb).disallow_export = true];
optional WiiOptions wiiOptions = 13;
@@ -642,13 +676,19 @@ message AddonOptions
optional MacroOptions macroOptions = 20;
}
message MigrationHistory
{
optional bool hotkeysMigrated = 1 [default = false];
optional bool gpioMappingsMigrated = 2 [default = false];
}
message Config
{
optional string boardVersion = 1 [(nanopb).max_length = 31];
optional GamepadOptions gamepadOptions = 2;
optional HotkeyOptions hotkeyOptions = 3;
optional PinMappings pinMappings = 4;
optional PinMappings deprecatedPinMappings = 4;
optional KeyboardMapping keyboardMapping = 5;
optional DisplayOptions displayOptions = 6;
optional LEDOptions ledOptions = 7;
@@ -658,4 +698,6 @@ message Config
optional ProfileOptions profileOptions = 11;
optional string boardConfig = 12 [(nanopb).max_length = 63];
optional GpioMappings gpioMappings = 13;
optional MigrationHistory migrations = 14;
}

View File

@@ -121,6 +121,48 @@ enum SOCDMode
SOCD_MODE_BYPASS = 4; // U+D=UD, L+R=LR (No cleaning applied)
}
enum GpioAction
{
option (nanopb_enumopt).long_names = false;
// the lowest value is the default, which should be NONE;
// reserving some numbers in case we need more not-mapped type values
NONE = -10;
RESERVED = -5;
ASSIGNED_TO_ADDON = 0;
BUTTON_PRESS_UP = 1;
BUTTON_PRESS_DOWN = 2;
BUTTON_PRESS_LEFT = 3;
BUTTON_PRESS_RIGHT = 4;
BUTTON_PRESS_B1 = 5;
BUTTON_PRESS_B2 = 6;
BUTTON_PRESS_B3 = 7;
BUTTON_PRESS_B4 = 8;
BUTTON_PRESS_L1 = 9;
BUTTON_PRESS_R1 = 10;
BUTTON_PRESS_L2 = 11;
BUTTON_PRESS_R2 = 12;
BUTTON_PRESS_S1 = 13;
BUTTON_PRESS_S2 = 14;
BUTTON_PRESS_A1 = 15;
BUTTON_PRESS_A2 = 16;
BUTTON_PRESS_L3 = 17;
BUTTON_PRESS_R3 = 18;
BUTTON_PRESS_FN = 19;
BUTTON_PRESS_DDI_UP = 20;
BUTTON_PRESS_DDI_DOWN = 21;
BUTTON_PRESS_DDI_LEFT = 22;
BUTTON_PRESS_DDI_RIGHT = 23;
SUSTAIN_DP_MODE_DP = 24;
SUSTAIN_DP_MODE_LS = 25;
SUSTAIN_DP_MODE_RS = 26;
SUSTAIN_SOCD_MODE_UP_PRIO = 27;
SUSTAIN_SOCD_MODE_NEUTRAL = 28;
SUSTAIN_SOCD_MODE_SECOND_WIN = 29;
SUSTAIN_SOCD_MODE_FIRST_WIN = 30;
SUSTAIN_SOCD_MODE_BYPASS = 31;
}
enum GamepadHotkey
{
option (nanopb_enumopt).long_names = false;

View File

@@ -3,6 +3,7 @@
#include "storagemanager.h"
#include "helper.h"
#include "config.pb.h"
#include "types.h"
bool DualDirectionalInput::available() {
return Storage::getInstance().getAddonOptions().dualDirectionalOptions.enabled;
@@ -11,24 +12,22 @@ bool DualDirectionalInput::available() {
void DualDirectionalInput::setup() {
const DualDirectionalOptions& options = Storage::getInstance().getAddonOptions().dualDirectionalOptions;
combineMode = options.combineMode;
pinDualDirDown = options.downPin;
pinDualDirUp = options.upPin;
pinDualDirLeft = options.leftPin;
pinDualDirRight = options.rightPin;
dpadMode = options.dpadMode;
// Setup TURBO Key
uint8_t pinDualDir[4] = {pinDualDirDown,
pinDualDirUp,
pinDualDirLeft,
pinDualDirRight};
mapDpadUp = new GamepadButtonMapping(GAMEPAD_MASK_UP);
mapDpadDown = new GamepadButtonMapping(GAMEPAD_MASK_DOWN);
mapDpadLeft = new GamepadButtonMapping(GAMEPAD_MASK_LEFT);
mapDpadRight = new GamepadButtonMapping(GAMEPAD_MASK_RIGHT);
for (int i = 0; i < 4; i++) {
if ( isValidPin(pinDualDir[i]) ) {
gpio_init(pinDualDir[i]); // Initialize pin
gpio_set_dir(pinDualDir[i], GPIO_IN); // Set as INPUT
gpio_pull_up(pinDualDir[i]); // Set as PULLUP
GpioAction* pinMappings = Storage::getInstance().getProfilePinMappings();
for (Pin_t pin = 0; pin < (Pin_t)NUM_BANK0_GPIOS; pin++)
{
switch (pinMappings[pin]) {
case GpioAction::BUTTON_PRESS_DDI_UP: mapDpadUp->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_DDI_DOWN: mapDpadDown->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_DDI_LEFT: mapDpadLeft->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_DDI_RIGHT: mapDpadRight->pinMask |= 1 << pin; break;
default: break;
}
}
@@ -67,21 +66,13 @@ void DualDirectionalInput::preprocess()
{
const DualDirectionalOptions& options = Storage::getInstance().getAddonOptions().dualDirectionalOptions;
Gamepad * gamepad = Storage::getInstance().GetGamepad();
Mask_t values = ~gpio_get_all();
// Need to invert since we're using pullups
dualState = 0;
if ( pinDualDirUp != (uint8_t)-1 ) {
dualState |= (!gpio_get(pinDualDirUp) ? gamepad->mapDpadUp->buttonMask : 0);
}
if ( pinDualDirDown != (uint8_t)-1 ) {
dualState |= (!gpio_get(pinDualDirDown) ? gamepad->mapDpadDown->buttonMask : 0);
}
if ( pinDualDirLeft != (uint8_t)-1 ) {
dualState |= (!gpio_get(pinDualDirLeft) ? gamepad->mapDpadLeft->buttonMask : 0);
}
if ( pinDualDirRight != (uint8_t)-1 ) {
dualState |= (!gpio_get(pinDualDirRight) ? gamepad->mapDpadRight->buttonMask : 0);
}
dualState = 0
| ((values & mapDpadUp->pinMask) ? mapDpadUp->buttonMask : 0)
| ((values & mapDpadDown->pinMask) ? mapDpadDown->buttonMask : 0)
| ((values & mapDpadLeft->pinMask) ? mapDpadLeft->buttonMask : 0)
| ((values & mapDpadRight->pinMask) ? mapDpadRight->buttonMask : 0);
// Debounce our directional pins
debounce();

View File

@@ -1,41 +0,0 @@
#include "addons/extra_button.h"
#include "storagemanager.h"
#include "hardware/gpio.h"
#include "helper.h"
#include "config.pb.h"
bool ExtraButtonAddon::available() {
const ExtraButtonOptions& options = Storage::getInstance().getAddonOptions().extraButtonOptions;
return options.enabled && options.buttonMap != 0 && isValidPin(options.pin);
}
void ExtraButtonAddon::setup() {
const ExtraButtonOptions& options = Storage::getInstance().getAddonOptions().extraButtonOptions;
extraButtonMap = options.buttonMap;
extraButtonPin = options.pin;
gpio_init(extraButtonPin); // Initialize pin
gpio_set_dir(extraButtonPin, GPIO_IN); // Set as INPUT
gpio_pull_up(extraButtonPin); // Set as PULLUP
}
void ExtraButtonAddon::preprocess() {
Gamepad * gamepad = Storage::getInstance().GetGamepad();
if (!gpio_get(extraButtonPin)) {
switch (extraButtonMap) {
case (GAMEPAD_MASK_DU):
gamepad->state.dpad |= GAMEPAD_MASK_UP;
break;
case (GAMEPAD_MASK_DD):
gamepad->state.dpad |= GAMEPAD_MASK_DOWN;
break;
case (GAMEPAD_MASK_DL):
gamepad->state.dpad |= GAMEPAD_MASK_LEFT;
break;
case (GAMEPAD_MASK_DR):
gamepad->state.dpad |= GAMEPAD_MASK_RIGHT;
break;
default: gamepad->state.buttons |= extraButtonMap;
}
}
}

View File

@@ -1,6 +1,7 @@
#include "addons/jslider.h"
#include "storagemanager.h"
#include "types.h"
#include "GamepadEnums.h"
#include "helper.h"
@@ -12,33 +13,30 @@
bool JSliderInput::available() {
const SliderOptions& options = Storage::getInstance().getAddonOptions().sliderOptions;
return ( options.enabled && (isValidPin(options.pinSliderOne) || isValidPin(options.pinSliderTwo)) );
return options.enabled;
}
void JSliderInput::setup()
{
const SliderOptions& options = Storage::getInstance().getAddonOptions().sliderOptions;
if ( isValidPin(options.pinSliderOne)) {
gpio_init(options.pinSliderOne); // Initialize pin
gpio_set_dir(options.pinSliderOne, GPIO_IN); // Set as INPUT
gpio_pull_up(options.pinSliderOne); // Set as PULLUP
}
if ( isValidPin(options.pinSliderTwo)) {
gpio_init(options.pinSliderTwo);
gpio_set_dir(options.pinSliderTwo, GPIO_IN); // Set as INPUT
gpio_pull_up(options.pinSliderTwo); // Set as PULLUP
GpioAction* pinMappings = Storage::getInstance().getProfilePinMappings();
for (Pin_t pin = 0; pin < (Pin_t)NUM_BANK0_GPIOS; pin++)
{
switch (pinMappings[pin]) {
case SUSTAIN_DP_MODE_DP: dpModeMask |= 1 << pin; break;
case SUSTAIN_DP_MODE_LS: lsModeMask |= 1 << pin; break;
case SUSTAIN_DP_MODE_RS: rsModeMask |= 1 << pin; break;
default: break;
}
}
}
DpadMode JSliderInput::read() {
const SliderOptions& options = Storage::getInstance().getAddonOptions().sliderOptions;
if ( isValidPin(options.pinSliderOne) && !gpio_get(options.pinSliderOne)) {
return options.modeOne;
}
if ( isValidPin(options.pinSliderTwo) && !gpio_get(options.pinSliderTwo)) {
return options.modeTwo;
}
return options.modeZero;
Mask_t values = ~gpio_get_all();
if (values & dpModeMask) return DpadMode::DPAD_MODE_DIGITAL;
else if (values & lsModeMask) return DpadMode::DPAD_MODE_LEFT_ANALOG;
else if (values & rsModeMask) return DpadMode::DPAD_MODE_RIGHT_ANALOG;
return options.modeDefault;
}
void JSliderInput::debounce()
@@ -65,6 +63,5 @@ void JSliderInput::process()
Gamepad * gamepad = Storage::getInstance().GetGamepad();
if ( gamepad->getOptions().dpadMode != dpadState) {
gamepad->setDpadMode(dpadState);
gamepad->save();
}
}

View File

@@ -1,6 +1,8 @@
#include "addons/slider_socd.h"
#include "enums.pb.h"
#include "storagemanager.h"
#include "types.h"
#include "GamepadEnums.h"
@@ -10,47 +12,50 @@
bool SliderSOCDInput::available() {
const SOCDSliderOptions& options = Storage::getInstance().getAddonOptions().socdSliderOptions;
return ( options.enabled && isValidPin(options.pinOne) && isValidPin(options.pinTwo) );
return options.enabled;
}
void SliderSOCDInput::setup()
{
const SOCDSliderOptions& options = Storage::getInstance().getAddonOptions().socdSliderOptions;
sliderSOCDModeOne = static_cast<SOCDMode>(options.modeOne);
sliderSOCDModeTwo = static_cast<SOCDMode>(options.modeTwo);
sliderSOCDModeDefault = static_cast<SOCDMode>(options.modeDefault);
pinSliderSOCDOne = options.pinOne;
pinSliderSOCDTwo = options.pinTwo;
gpio_init(pinSliderSOCDOne); // Initialize pin
gpio_set_dir(pinSliderSOCDOne, GPIO_IN); // Set as INPUT
gpio_pull_up(pinSliderSOCDOne); // Set as PULLUP
gpio_init(pinSliderSOCDTwo);
gpio_set_dir(pinSliderSOCDTwo, GPIO_IN); // Set as INPUT
gpio_pull_up(pinSliderSOCDTwo); // Set as PULLUP
GpioAction* pinMappings = Storage::getInstance().getProfilePinMappings();
for (Pin_t pin = 0; pin < (Pin_t)NUM_BANK0_GPIOS; pin++)
{
switch (pinMappings[pin]) {
case SUSTAIN_SOCD_MODE_UP_PRIO: upPrioModeMask |= 1 << pin; break;
case SUSTAIN_SOCD_MODE_NEUTRAL: neutralModeMask |= 1 << pin; break;
case SUSTAIN_SOCD_MODE_SECOND_WIN: secondInputModeMask |= 1 << pin; break;
case SUSTAIN_SOCD_MODE_FIRST_WIN: firstInputModeMask |= 1 << pin; break;
case SUSTAIN_SOCD_MODE_BYPASS: bypassModeMask |= 1 << pin; break;
default: break;
}
}
}
SOCDMode SliderSOCDInput::read() {
if ( pinSliderSOCDOne != (uint8_t)-1 && pinSliderSOCDTwo != (uint8_t)-1) {
if ( !gpio_get(pinSliderSOCDOne)) {
return sliderSOCDModeOne;
} else if ( !gpio_get(pinSliderSOCDTwo)) {
return sliderSOCDModeTwo;
}
}
return sliderSOCDModeDefault;
const SOCDSliderOptions& options = Storage::getInstance().getAddonOptions().socdSliderOptions;
Mask_t values = ~gpio_get_all();
if (values & upPrioModeMask) return SOCDMode::SOCD_MODE_UP_PRIORITY;
else if (values & neutralModeMask) return SOCDMode::SOCD_MODE_NEUTRAL;
else if (values & secondInputModeMask) return SOCDMode::SOCD_MODE_SECOND_INPUT_PRIORITY;
else if (values & firstInputModeMask) return SOCDMode::SOCD_MODE_FIRST_INPUT_PRIORITY;
else if (values & bypassModeMask) return SOCDMode::SOCD_MODE_BYPASS;
return options.modeDefault;
}
void SliderSOCDInput::debounce()
{
uint32_t uNowTime = getMillis();
if ((dDebState != socdState) && ((uNowTime - uDebTime) > SLIDERSOCD_DEBOUNCE_MILLIS)) {
if ( (socdState ^ dDebState) == sliderSOCDModeTwo )
dDebState = (SOCDMode)(dDebState ^ sliderSOCDModeTwo); // Bounce Second Priority
else if ( (socdState ^ dDebState) & sliderSOCDModeOne )
dDebState = (SOCDMode)(dDebState ^ sliderSOCDModeOne); // Bounce Up Priority
else
dDebState = (SOCDMode)(dDebState ^ sliderSOCDModeDefault); // Bounce Neutral Priority
if ( (socdState ^ dDebState) == SOCDMode::SOCD_MODE_SECOND_INPUT_PRIORITY )
dDebState = (SOCDMode)(dDebState ^ SOCDMode::SOCD_MODE_SECOND_INPUT_PRIORITY); // Bounce Second Priority
else if ( (socdState ^ dDebState) & SOCDMode::SOCD_MODE_UP_PRIORITY )
dDebState = (SOCDMode)(dDebState ^ SOCDMode::SOCD_MODE_UP_PRIORITY); // Bounce Up Priority
else if ( (socdState ^ dDebState) & SOCDMode::SOCD_MODE_NEUTRAL )
dDebState = (SOCDMode)(dDebState ^ SOCDMode::SOCD_MODE_NEUTRAL);
else if ( (socdState ^ dDebState) & SOCDMode::SOCD_MODE_FIRST_INPUT_PRIORITY )
dDebState = (SOCDMode)(dDebState ^ SOCDMode::SOCD_MODE_FIRST_INPUT_PRIORITY);
else if ( (socdState ^ dDebState) & SOCDMode::SOCD_MODE_BYPASS )
dDebState = (SOCDMode)(dDebState ^ SOCDMode::SOCD_MODE_BYPASS);
uDebTime = uNowTime;
}
socdState = dDebState;
@@ -67,6 +72,5 @@ void SliderSOCDInput::process()
Gamepad * gamepad = Storage::getInstance().GetGamepad();
if ( gamepad->getOptions().socdMode != socdState) {
gamepad->setSOCDMode(socdState);
gamepad->save();
}
}

View File

@@ -762,8 +762,8 @@ bool ConfigUtils::fromLegacyStorage(Config& config)
{
legacyConfigFound = true;
PinMappings& pinMappings = config.pinMappings;
config.has_pinMappings = true;
PinMappings& pinMappings = config.deprecatedPinMappings;
config.has_deprecatedPinMappings = true;
SET_PROPERTY(pinMappings, pinDpadUp, bytePinToIntPin(legacyBoardOptions.pinDpadUp));
SET_PROPERTY(pinMappings, pinDpadDown, bytePinToIntPin(legacyBoardOptions.pinDpadDown));
SET_PROPERTY(pinMappings, pinDpadLeft, bytePinToIntPin(legacyBoardOptions.pinDpadLeft));
@@ -965,18 +965,18 @@ bool ConfigUtils::fromLegacyStorage(Config& config)
DualDirectionalOptions& dualDirectionalOptions = config.addonOptions.dualDirectionalOptions;
config.addonOptions.has_dualDirectionalOptions = true;
SET_PROPERTY(dualDirectionalOptions, enabled, legacyAddonOptions.DualDirectionalInputEnabled);
SET_PROPERTY(dualDirectionalOptions, upPin, bytePinToIntPin(legacyAddonOptions.pinDualDirUp));
SET_PROPERTY(dualDirectionalOptions, downPin, bytePinToIntPin(legacyAddonOptions.pinDualDirDown));
SET_PROPERTY(dualDirectionalOptions, leftPin, bytePinToIntPin(legacyAddonOptions.pinDualDirLeft));
SET_PROPERTY(dualDirectionalOptions, rightPin, bytePinToIntPin(legacyAddonOptions.pinDualDirRight));
SET_PROPERTY(dualDirectionalOptions, deprecatedUpPin, bytePinToIntPin(legacyAddonOptions.pinDualDirUp));
SET_PROPERTY(dualDirectionalOptions, deprecatedDownPin, bytePinToIntPin(legacyAddonOptions.pinDualDirDown));
SET_PROPERTY(dualDirectionalOptions, deprecatedLeftPin, bytePinToIntPin(legacyAddonOptions.pinDualDirLeft));
SET_PROPERTY(dualDirectionalOptions, deprecatedRightPin, bytePinToIntPin(legacyAddonOptions.pinDualDirRight));
if (isValidDpadMode(legacyAddonOptions.dualDirDpadMode))
{
SET_PROPERTY(dualDirectionalOptions, dpadMode, static_cast<DpadMode>(legacyAddonOptions.dualDirDpadMode));
}
SET_PROPERTY(dualDirectionalOptions, combineMode, legacyAddonOptions.dualDirCombineMode);
ExtraButtonOptions& extraButtonOptions = config.addonOptions.extraButtonOptions;
config.addonOptions.has_extraButtonOptions = true;
ExtraButtonOptions& extraButtonOptions = config.addonOptions.deprecatedExtraButtonOptions;
config.addonOptions.has_deprecatedExtraButtonOptions = true;
SET_PROPERTY(extraButtonOptions, enabled, legacyAddonOptions.ExtraButtonAddonEnabled);
SET_PROPERTY(extraButtonOptions, pin, bytePinToIntPin(legacyAddonOptions.extraButtonPin));
SET_PROPERTY(extraButtonOptions, buttonMap, legacyAddonOptions.extraButtonMap);
@@ -993,8 +993,8 @@ bool ConfigUtils::fromLegacyStorage(Config& config)
SliderOptions& sliderOptions = config.addonOptions.sliderOptions;
config.addonOptions.has_sliderOptions = true;
SET_PROPERTY(sliderOptions, enabled, legacyAddonOptions.JSliderInputEnabled);
SET_PROPERTY(sliderOptions, pinSliderOne, bytePinToIntPin(legacyAddonOptions.pinSliderLS));
SET_PROPERTY(sliderOptions, pinSliderTwo, bytePinToIntPin(legacyAddonOptions.pinSliderRS));
SET_PROPERTY(sliderOptions, deprecatedPinSliderOne, bytePinToIntPin(legacyAddonOptions.pinSliderLS));
SET_PROPERTY(sliderOptions, deprecatedPinSliderTwo, bytePinToIntPin(legacyAddonOptions.pinSliderRS));
PlayerNumberOptions& playerNumberOptions = config.addonOptions.playerNumberOptions;
config.addonOptions.has_playerNumberOptions = true;
@@ -1014,19 +1014,19 @@ bool ConfigUtils::fromLegacyStorage(Config& config)
SOCDSliderOptions& socdSliderOptions = config.addonOptions.socdSliderOptions;
config.addonOptions.has_socdSliderOptions = true;
SET_PROPERTY(socdSliderOptions, enabled, legacyAddonOptions.SliderSOCDInputEnabled);
SET_PROPERTY(socdSliderOptions, pinOne, bytePinToIntPin(legacyAddonOptions.pinSliderSOCDOne));
SET_PROPERTY(socdSliderOptions, pinTwo, bytePinToIntPin(legacyAddonOptions.pinSliderSOCDTwo));
SET_PROPERTY(socdSliderOptions, deprecatedPinOne, bytePinToIntPin(legacyAddonOptions.pinSliderSOCDOne));
SET_PROPERTY(socdSliderOptions, deprecatedPinTwo, bytePinToIntPin(legacyAddonOptions.pinSliderSOCDTwo));
if (isValidSOCDMode(legacyAddonOptions.sliderSOCDModeDefault))
{
SET_PROPERTY(socdSliderOptions, modeDefault, static_cast<SOCDMode>(legacyAddonOptions.sliderSOCDModeDefault));
}
if (isValidSOCDMode(legacyAddonOptions.sliderSOCDModeOne))
{
SET_PROPERTY(socdSliderOptions, modeOne, static_cast<SOCDMode>(legacyAddonOptions.sliderSOCDModeOne));
SET_PROPERTY(socdSliderOptions, deprecatedModeOne, static_cast<SOCDMode>(legacyAddonOptions.sliderSOCDModeOne));
}
if (isValidSOCDMode(legacyAddonOptions.sliderSOCDModeTwo))
{
SET_PROPERTY(socdSliderOptions, modeTwo, static_cast<SOCDMode>(legacyAddonOptions.sliderSOCDModeTwo));
SET_PROPERTY(socdSliderOptions, deprecatedModeTwo, static_cast<SOCDMode>(legacyAddonOptions.sliderSOCDModeTwo));
}
OnBoardLedOptions& onBoardLedOptions = config.addonOptions.onBoardLedOptions;

View File

@@ -5,6 +5,7 @@
#include "pb_encode.h"
#include "pb_decode.h"
#include "pb_common.h"
#include "types.h"
#include "BoardConfig.h"
#include "GamepadConfig.h"
@@ -15,7 +16,6 @@
#include "addons/buzzerspeaker.h"
#include "addons/dualdirectional.h"
#include "addons/tilt.h"
#include "addons/extra_button.h"
#include "addons/focus_mode.h"
#include "addons/i2canalog1219.h"
#include "addons/i2cdisplay.h"
@@ -85,6 +85,17 @@
#define DEFAULT_SOCD_MODE SOCD_MODE_NEUTRAL
#endif
// -----------------------------------------------------
// Migration leftovers
// -----------------------------------------------------
#ifndef EXTRA_BUTTON_PIN
#define EXTRA_BUTTON_PIN -1
#endif
#ifndef EXTRA_BUTTON_MASK
#define EXTRA_BUTTON_MASK 0
#endif
void ConfigUtils::initUnsetPropertiesWithDefaults(Config& config)
{
const uint8_t emptyByteArray[0] = {};
@@ -158,25 +169,25 @@ void ConfigUtils::initUnsetPropertiesWithDefaults(Config& config)
INIT_UNSET_PROPERTY(config.forcedSetupOptions, mode, DEFAULT_FORCED_SETUP_MODE);
// pinMappings
INIT_UNSET_PROPERTY(config.pinMappings, pinDpadUp, PIN_DPAD_UP);
INIT_UNSET_PROPERTY(config.pinMappings, pinDpadDown, PIN_DPAD_DOWN);
INIT_UNSET_PROPERTY(config.pinMappings, pinDpadLeft, PIN_DPAD_LEFT);
INIT_UNSET_PROPERTY(config.pinMappings, pinDpadRight, PIN_DPAD_RIGHT);
INIT_UNSET_PROPERTY(config.pinMappings, pinButtonB1, PIN_BUTTON_B1);
INIT_UNSET_PROPERTY(config.pinMappings, pinButtonB2, PIN_BUTTON_B2);
INIT_UNSET_PROPERTY(config.pinMappings, pinButtonB3, PIN_BUTTON_B3);
INIT_UNSET_PROPERTY(config.pinMappings, pinButtonB4, PIN_BUTTON_B4);
INIT_UNSET_PROPERTY(config.pinMappings, pinButtonL1, PIN_BUTTON_L1);
INIT_UNSET_PROPERTY(config.pinMappings, pinButtonR1, PIN_BUTTON_R1);
INIT_UNSET_PROPERTY(config.pinMappings, pinButtonL2, PIN_BUTTON_L2);
INIT_UNSET_PROPERTY(config.pinMappings, pinButtonR2, PIN_BUTTON_R2);
INIT_UNSET_PROPERTY(config.pinMappings, pinButtonS1, PIN_BUTTON_S1);
INIT_UNSET_PROPERTY(config.pinMappings, pinButtonS2, PIN_BUTTON_S2);
INIT_UNSET_PROPERTY(config.pinMappings, pinButtonL3, PIN_BUTTON_L3);
INIT_UNSET_PROPERTY(config.pinMappings, pinButtonR3, PIN_BUTTON_R3);
INIT_UNSET_PROPERTY(config.pinMappings, pinButtonA1, PIN_BUTTON_A1);
INIT_UNSET_PROPERTY(config.pinMappings, pinButtonA2, PIN_BUTTON_A2);
INIT_UNSET_PROPERTY(config.pinMappings, pinButtonFn, PIN_BUTTON_FN);
INIT_UNSET_PROPERTY(config.deprecatedPinMappings, pinDpadUp, PIN_DPAD_UP);
INIT_UNSET_PROPERTY(config.deprecatedPinMappings, pinDpadDown, PIN_DPAD_DOWN);
INIT_UNSET_PROPERTY(config.deprecatedPinMappings, pinDpadLeft, PIN_DPAD_LEFT);
INIT_UNSET_PROPERTY(config.deprecatedPinMappings, pinDpadRight, PIN_DPAD_RIGHT);
INIT_UNSET_PROPERTY(config.deprecatedPinMappings, pinButtonB1, PIN_BUTTON_B1);
INIT_UNSET_PROPERTY(config.deprecatedPinMappings, pinButtonB2, PIN_BUTTON_B2);
INIT_UNSET_PROPERTY(config.deprecatedPinMappings, pinButtonB3, PIN_BUTTON_B3);
INIT_UNSET_PROPERTY(config.deprecatedPinMappings, pinButtonB4, PIN_BUTTON_B4);
INIT_UNSET_PROPERTY(config.deprecatedPinMappings, pinButtonL1, PIN_BUTTON_L1);
INIT_UNSET_PROPERTY(config.deprecatedPinMappings, pinButtonR1, PIN_BUTTON_R1);
INIT_UNSET_PROPERTY(config.deprecatedPinMappings, pinButtonL2, PIN_BUTTON_L2);
INIT_UNSET_PROPERTY(config.deprecatedPinMappings, pinButtonR2, PIN_BUTTON_R2);
INIT_UNSET_PROPERTY(config.deprecatedPinMappings, pinButtonS1, PIN_BUTTON_S1);
INIT_UNSET_PROPERTY(config.deprecatedPinMappings, pinButtonS2, PIN_BUTTON_S2);
INIT_UNSET_PROPERTY(config.deprecatedPinMappings, pinButtonL3, PIN_BUTTON_L3);
INIT_UNSET_PROPERTY(config.deprecatedPinMappings, pinButtonR3, PIN_BUTTON_R3);
INIT_UNSET_PROPERTY(config.deprecatedPinMappings, pinButtonA1, PIN_BUTTON_A1);
INIT_UNSET_PROPERTY(config.deprecatedPinMappings, pinButtonA2, PIN_BUTTON_A2);
INIT_UNSET_PROPERTY(config.deprecatedPinMappings, pinButtonFn, PIN_BUTTON_FN);
// keyboardMapping
INIT_UNSET_PROPERTY(config.keyboardMapping, keyDpadUp, KEY_DPAD_UP);
@@ -398,11 +409,7 @@ void ConfigUtils::initUnsetPropertiesWithDefaults(Config& config)
// addonOptions.sliderOptions
INIT_UNSET_PROPERTY(config.addonOptions.sliderOptions, enabled, !!JSLIDER_ENABLED);
INIT_UNSET_PROPERTY(config.addonOptions.sliderOptions, pinSliderOne, PIN_SLIDER_ONE);
INIT_UNSET_PROPERTY(config.addonOptions.sliderOptions, pinSliderTwo, PIN_SLIDER_TWO);
INIT_UNSET_PROPERTY(config.addonOptions.sliderOptions, modeZero, SLIDER_MODE_ZERO);
INIT_UNSET_PROPERTY(config.addonOptions.sliderOptions, modeOne, SLIDER_MODE_ONE);
INIT_UNSET_PROPERTY(config.addonOptions.sliderOptions, modeTwo, SLIDER_MODE_TWO);
INIT_UNSET_PROPERTY(config.addonOptions.sliderOptions, modeDefault, SLIDER_MODE_ZERO);
// addonOptions.reverseOptions
INIT_UNSET_PROPERTY(config.addonOptions.reverseOptions, enabled, !!REVERSE_ENABLED);
@@ -415,11 +422,7 @@ void ConfigUtils::initUnsetPropertiesWithDefaults(Config& config)
// addonOptions.socdSliderOptions
INIT_UNSET_PROPERTY(config.addonOptions.socdSliderOptions, enabled, !!SLIDER_SOCD_ENABLED);
INIT_UNSET_PROPERTY(config.addonOptions.socdSliderOptions, pinOne, PIN_SLIDER_SOCD_ONE);
INIT_UNSET_PROPERTY(config.addonOptions.socdSliderOptions, pinTwo, PIN_SLIDER_SOCD_TWO);
INIT_UNSET_PROPERTY(config.addonOptions.socdSliderOptions, modeDefault, SLIDER_SOCD_SLOT_DEFAULT);
INIT_UNSET_PROPERTY(config.addonOptions.socdSliderOptions, modeOne, SLIDER_SOCD_SLOT_ONE);
INIT_UNSET_PROPERTY(config.addonOptions.socdSliderOptions, modeTwo, SLIDER_SOCD_SLOT_TWO);
// addonOptions.analogADS1219Options
INIT_UNSET_PROPERTY(config.addonOptions.analogADS1219Options, enabled, !!I2C_ANALOG1219_ENABLED);
@@ -431,10 +434,6 @@ void ConfigUtils::initUnsetPropertiesWithDefaults(Config& config)
// addonOptions.dualDirectionalOptions
INIT_UNSET_PROPERTY(config.addonOptions.dualDirectionalOptions, enabled, !!DUAL_DIRECTIONAL_ENABLED);
INIT_UNSET_PROPERTY(config.addonOptions.dualDirectionalOptions, upPin, PIN_DUAL_DIRECTIONAL_UP);
INIT_UNSET_PROPERTY(config.addonOptions.dualDirectionalOptions, downPin, PIN_DUAL_DIRECTIONAL_DOWN)
INIT_UNSET_PROPERTY(config.addonOptions.dualDirectionalOptions, leftPin, PIN_DUAL_DIRECTIONAL_LEFT);
INIT_UNSET_PROPERTY(config.addonOptions.dualDirectionalOptions, rightPin, PIN_DUAL_DIRECTIONAL_RIGHT);
INIT_UNSET_PROPERTY(config.addonOptions.dualDirectionalOptions, dpadMode, static_cast<DpadMode>(DUAL_DIRECTIONAL_STICK_MODE));
INIT_UNSET_PROPERTY(config.addonOptions.dualDirectionalOptions, combineMode, DUAL_DIRECTIONAL_COMBINE_MODE);
INIT_UNSET_PROPERTY(config.addonOptions.dualDirectionalOptions, fourWayMode, false);
@@ -466,11 +465,6 @@ void ConfigUtils::initUnsetPropertiesWithDefaults(Config& config)
INIT_UNSET_PROPERTY(config.addonOptions.buzzerOptions, pin, BUZZER_PIN);
INIT_UNSET_PROPERTY(config.addonOptions.buzzerOptions, volume, BUZZER_VOLUME);
// addonOptions.extraOptions
INIT_UNSET_PROPERTY(config.addonOptions.extraButtonOptions, enabled, !!EXTRA_BUTTON_ENABLED);
INIT_UNSET_PROPERTY(config.addonOptions.extraButtonOptions, pin, EXTRA_BUTTON_PIN);
INIT_UNSET_PROPERTY(config.addonOptions.extraButtonOptions, buttonMap, EXTRA_BUTTON_MASK);
// addonOptions.playerNumberOptions
INIT_UNSET_PROPERTY(config.addonOptions.playerNumberOptions, enabled, !!PLAYERNUM_ADDON_ENABLED);
INIT_UNSET_PROPERTY(config.addonOptions.playerNumberOptions, number, PLAYER_NUMBER);
@@ -552,6 +546,366 @@ void ConfigUtils::initUnsetPropertiesWithDefaults(Config& config)
// something *other than* the board defaults
// -----------------------------------------------------
// convert core and addon pin mappings to GPIO mapping config
// NOTE: this also handles initializations for a blank config! if/when the deprecated
// pin mappings go away, the remainder of this code should go in there (there was no point
// in duplicating it right now)
void gpioMappingsMigrationCore(Config& config)
{
PinMappings& deprecatedPinMappings = config.deprecatedPinMappings;
ExtraButtonOptions& extraButtonOptions = config.addonOptions.deprecatedExtraButtonOptions;
DualDirectionalOptions& ddiOptions = config.addonOptions.dualDirectionalOptions;
SliderOptions& jsSliderOptions = config.addonOptions.sliderOptions;
SOCDSliderOptions& socdSliderOptions = config.addonOptions.socdSliderOptions;
const auto gamepadMaskToGpioAction = [&](Mask_t gpMask) -> GpioAction
{
switch (gpMask)
{
case GAMEPAD_MASK_DU:
return GpioAction::BUTTON_PRESS_UP;
case GAMEPAD_MASK_DD:
return GpioAction::BUTTON_PRESS_DOWN;
case GAMEPAD_MASK_DL:
return GpioAction::BUTTON_PRESS_LEFT;
case GAMEPAD_MASK_DR:
return GpioAction::BUTTON_PRESS_RIGHT;
case GAMEPAD_MASK_B1:
return GpioAction::BUTTON_PRESS_B1;
case GAMEPAD_MASK_B2:
return GpioAction::BUTTON_PRESS_B2;
case GAMEPAD_MASK_B3:
return GpioAction::BUTTON_PRESS_B3;
case GAMEPAD_MASK_B4:
return GpioAction::BUTTON_PRESS_B4;
case GAMEPAD_MASK_L1:
return GpioAction::BUTTON_PRESS_L1;
case GAMEPAD_MASK_R1:
return GpioAction::BUTTON_PRESS_R1;
case GAMEPAD_MASK_L2:
return GpioAction::BUTTON_PRESS_L2;
case GAMEPAD_MASK_R2:
return GpioAction::BUTTON_PRESS_R2;
case GAMEPAD_MASK_S1:
return GpioAction::BUTTON_PRESS_S1;
case GAMEPAD_MASK_S2:
return GpioAction::BUTTON_PRESS_S2;
case GAMEPAD_MASK_L3:
return GpioAction::BUTTON_PRESS_L3;
case GAMEPAD_MASK_R3:
return GpioAction::BUTTON_PRESS_R3;
case GAMEPAD_MASK_A1:
return GpioAction::BUTTON_PRESS_A1;
case GAMEPAD_MASK_A2:
return GpioAction::BUTTON_PRESS_A2;
case AUX_MASK_FUNCTION:
return GpioAction::BUTTON_PRESS_FN;
default:
return GpioAction::NONE;
}
};
// create an array of the old
GpioAction actions[NUM_BANK0_GPIOS] = {GpioAction::NONE, GpioAction::NONE, GpioAction::NONE,
GpioAction::NONE, GpioAction::NONE, GpioAction::NONE,
GpioAction::NONE, GpioAction::NONE, GpioAction::NONE,
GpioAction::NONE, GpioAction::NONE, GpioAction::NONE,
GpioAction::NONE, GpioAction::NONE, GpioAction::NONE,
GpioAction::NONE, GpioAction::NONE, GpioAction::NONE,
GpioAction::NONE, GpioAction::NONE, GpioAction::NONE,
GpioAction::NONE, GpioAction::NONE, GpioAction::NONE,
GpioAction::NONE, GpioAction::NONE, GpioAction::NONE,
GpioAction::NONE, GpioAction::NONE, GpioAction::NONE};
const auto fromPBorBC = [&](bool isInProtobuf, Pin_t *protobufEntry, Pin_t boardconfigValue,
GpioAction action) -> void {
// get the core config value for a pin either from protobuf or, failing that, BoardConfig.h
if (isInProtobuf) {
if (*protobufEntry >= 0 && *protobufEntry < 30) {
actions[*protobufEntry] = action;
*protobufEntry = -1;
}
} else if (isValidPin(boardconfigValue)) {
actions[boardconfigValue] = action;
}
};
fromPBorBC(deprecatedPinMappings.has_pinDpadUp, &deprecatedPinMappings.pinDpadUp, PIN_DPAD_UP,
GpioAction::BUTTON_PRESS_UP);
fromPBorBC(deprecatedPinMappings.has_pinDpadDown, &deprecatedPinMappings.pinDpadDown, PIN_DPAD_DOWN,
GpioAction::BUTTON_PRESS_DOWN);
fromPBorBC(deprecatedPinMappings.has_pinDpadLeft, &deprecatedPinMappings.pinDpadLeft, PIN_DPAD_LEFT,
GpioAction::BUTTON_PRESS_LEFT);
fromPBorBC(deprecatedPinMappings.has_pinDpadRight, &deprecatedPinMappings.pinDpadRight, PIN_DPAD_RIGHT,
GpioAction::BUTTON_PRESS_RIGHT);
fromPBorBC(deprecatedPinMappings.has_pinButtonB1, &deprecatedPinMappings.pinButtonB1, PIN_BUTTON_B1,
GpioAction::BUTTON_PRESS_B1);
fromPBorBC(deprecatedPinMappings.has_pinButtonB2, &deprecatedPinMappings.pinButtonB2, PIN_BUTTON_B2,
GpioAction::BUTTON_PRESS_B2);
fromPBorBC(deprecatedPinMappings.has_pinButtonB3, &deprecatedPinMappings.pinButtonB3, PIN_BUTTON_B3,
GpioAction::BUTTON_PRESS_B3);
fromPBorBC(deprecatedPinMappings.has_pinButtonB4, &deprecatedPinMappings.pinButtonB4, PIN_BUTTON_B4,
GpioAction::BUTTON_PRESS_B4);
fromPBorBC(deprecatedPinMappings.has_pinButtonL1, &deprecatedPinMappings.pinButtonL1, PIN_BUTTON_L1,
GpioAction::BUTTON_PRESS_L1);
fromPBorBC(deprecatedPinMappings.has_pinButtonR1, &deprecatedPinMappings.pinButtonR1, PIN_BUTTON_R1,
GpioAction::BUTTON_PRESS_R1);
fromPBorBC(deprecatedPinMappings.has_pinButtonL2, &deprecatedPinMappings.pinButtonL2, PIN_BUTTON_L2,
GpioAction::BUTTON_PRESS_L2);
fromPBorBC(deprecatedPinMappings.has_pinButtonR2, &deprecatedPinMappings.pinButtonR2, PIN_BUTTON_R2,
GpioAction::BUTTON_PRESS_R2);
fromPBorBC(deprecatedPinMappings.has_pinButtonS1, &deprecatedPinMappings.pinButtonS1, PIN_BUTTON_S1,
GpioAction::BUTTON_PRESS_S1);
fromPBorBC(deprecatedPinMappings.has_pinButtonS2, &deprecatedPinMappings.pinButtonS2, PIN_BUTTON_S2,
GpioAction::BUTTON_PRESS_S2);
fromPBorBC(deprecatedPinMappings.has_pinButtonL3, &deprecatedPinMappings.pinButtonL3, PIN_BUTTON_L3,
GpioAction::BUTTON_PRESS_L3);
fromPBorBC(deprecatedPinMappings.has_pinButtonR3, &deprecatedPinMappings.pinButtonR3, PIN_BUTTON_R3,
GpioAction::BUTTON_PRESS_R3);
fromPBorBC(deprecatedPinMappings.has_pinButtonA1, &deprecatedPinMappings.pinButtonA1, PIN_BUTTON_A1,
GpioAction::BUTTON_PRESS_A1);
fromPBorBC(deprecatedPinMappings.has_pinButtonA2, &deprecatedPinMappings.pinButtonA2, PIN_BUTTON_A2,
GpioAction::BUTTON_PRESS_A2);
fromPBorBC(deprecatedPinMappings.has_pinButtonFn, &deprecatedPinMappings.pinButtonFn, PIN_BUTTON_FN,
GpioAction::BUTTON_PRESS_FN);
// convert extra pin mapping to GPIO mapping config
if (extraButtonOptions.enabled && isValidPin(extraButtonOptions.pin)) {
// previous config had a value we haven't migrated yet, it can/should apply in the new config
actions[extraButtonOptions.pin] = gamepadMaskToGpioAction(extraButtonOptions.buttonMap);
extraButtonOptions.pin = -1;
extraButtonOptions.enabled = false;
}
else if (isValidPin(EXTRA_BUTTON_PIN))
actions[EXTRA_BUTTON_PIN] = gamepadMaskToGpioAction(EXTRA_BUTTON_MASK);
// convert DDI direction pin mapping to GPIO mapping config
if (ddiOptions.enabled) {
fromPBorBC(ddiOptions.has_deprecatedUpPin, &ddiOptions.deprecatedUpPin, PIN_DUAL_DIRECTIONAL_UP,
GpioAction::BUTTON_PRESS_DDI_UP);
fromPBorBC(ddiOptions.has_deprecatedDownPin, &ddiOptions.deprecatedDownPin, PIN_DUAL_DIRECTIONAL_DOWN,
GpioAction::BUTTON_PRESS_DDI_DOWN);
fromPBorBC(ddiOptions.has_deprecatedLeftPin, &ddiOptions.deprecatedLeftPin, PIN_DUAL_DIRECTIONAL_LEFT,
GpioAction::BUTTON_PRESS_DDI_LEFT);
fromPBorBC(ddiOptions.has_deprecatedRightPin, &ddiOptions.deprecatedRightPin, PIN_DUAL_DIRECTIONAL_RIGHT,
GpioAction::BUTTON_PRESS_DDI_RIGHT);
}
// convert JS slider pin mappings to GPIO mapping config
if (jsSliderOptions.enabled && isValidPin(jsSliderOptions.deprecatedPinSliderOne)) {
switch (jsSliderOptions.deprecatedModeOne) {
case DpadMode::DPAD_MODE_DIGITAL: {
actions[jsSliderOptions.deprecatedPinSliderOne] = GpioAction::SUSTAIN_DP_MODE_DP; break;
}
case DpadMode::DPAD_MODE_LEFT_ANALOG: {
actions[jsSliderOptions.deprecatedPinSliderOne] = GpioAction::SUSTAIN_DP_MODE_LS; break;
}
case DpadMode::DPAD_MODE_RIGHT_ANALOG: {
actions[jsSliderOptions.deprecatedPinSliderOne] = GpioAction::SUSTAIN_DP_MODE_RS; break;
}
default: break;
}
jsSliderOptions.deprecatedPinSliderOne = -1;
}
else if (isValidPin(PIN_SLIDER_ONE)) {
actions[PIN_SLIDER_ONE] = GpioAction::SUSTAIN_DP_MODE_LS;
}
if (jsSliderOptions.enabled && isValidPin(jsSliderOptions.deprecatedPinSliderTwo)) {
switch (jsSliderOptions.deprecatedModeTwo) {
case DpadMode::DPAD_MODE_DIGITAL: {
actions[jsSliderOptions.deprecatedPinSliderTwo] = GpioAction::SUSTAIN_DP_MODE_DP; break;
}
case DpadMode::DPAD_MODE_LEFT_ANALOG: {
actions[jsSliderOptions.deprecatedPinSliderTwo] = GpioAction::SUSTAIN_DP_MODE_LS; break;
}
case DpadMode::DPAD_MODE_RIGHT_ANALOG: {
actions[jsSliderOptions.deprecatedPinSliderTwo] = GpioAction::SUSTAIN_DP_MODE_RS; break;
}
default: break;
}
jsSliderOptions.deprecatedPinSliderTwo = -1;
}
else if (isValidPin(PIN_SLIDER_TWO)) {
actions[PIN_SLIDER_TWO] = GpioAction::SUSTAIN_DP_MODE_RS;
}
// convert SOCD slider pin mappings to GPIO mapping config
if (socdSliderOptions.enabled && isValidPin(socdSliderOptions.deprecatedPinOne)) {
switch (socdSliderOptions.deprecatedModeOne) {
case SOCDMode::SOCD_MODE_UP_PRIORITY: {
actions[socdSliderOptions.deprecatedPinOne] = GpioAction::SUSTAIN_SOCD_MODE_UP_PRIO; break;
}
case SOCDMode::SOCD_MODE_NEUTRAL: {
actions[socdSliderOptions.deprecatedPinOne] = GpioAction::SUSTAIN_SOCD_MODE_NEUTRAL; break;
}
case SOCDMode::SOCD_MODE_SECOND_INPUT_PRIORITY: {
actions[socdSliderOptions.deprecatedPinOne] = GpioAction::SUSTAIN_SOCD_MODE_SECOND_WIN; break;
}
case SOCDMode::SOCD_MODE_FIRST_INPUT_PRIORITY: {
actions[socdSliderOptions.deprecatedPinOne] = GpioAction::SUSTAIN_SOCD_MODE_FIRST_WIN; break;
}
case SOCDMode::SOCD_MODE_BYPASS: {
actions[socdSliderOptions.deprecatedPinOne] = GpioAction::SUSTAIN_SOCD_MODE_BYPASS; break;
}
default: break;
}
socdSliderOptions.deprecatedPinOne = -1;
}
else if (isValidPin(PIN_SLIDER_SOCD_ONE)) {
switch (SLIDER_SOCD_SLOT_ONE) {
case SOCDMode::SOCD_MODE_UP_PRIORITY: {
actions[PIN_SLIDER_SOCD_ONE] = GpioAction::SUSTAIN_SOCD_MODE_UP_PRIO; break;
}
case SOCDMode::SOCD_MODE_NEUTRAL: {
actions[PIN_SLIDER_SOCD_ONE] = GpioAction::SUSTAIN_SOCD_MODE_NEUTRAL; break;
}
case SOCDMode::SOCD_MODE_SECOND_INPUT_PRIORITY: {
actions[PIN_SLIDER_SOCD_ONE] = GpioAction::SUSTAIN_SOCD_MODE_SECOND_WIN; break;
}
case SOCDMode::SOCD_MODE_FIRST_INPUT_PRIORITY: {
actions[PIN_SLIDER_SOCD_ONE] = GpioAction::SUSTAIN_SOCD_MODE_FIRST_WIN; break;
}
case SOCDMode::SOCD_MODE_BYPASS: {
actions[PIN_SLIDER_SOCD_ONE] = GpioAction::SUSTAIN_SOCD_MODE_BYPASS; break;
}
default: break;
}
}
if (socdSliderOptions.enabled && isValidPin(socdSliderOptions.deprecatedPinTwo)) {
switch (socdSliderOptions.deprecatedModeTwo) {
case SOCDMode::SOCD_MODE_UP_PRIORITY: {
actions[socdSliderOptions.deprecatedPinTwo] = GpioAction::SUSTAIN_SOCD_MODE_UP_PRIO; break;
}
case SOCDMode::SOCD_MODE_NEUTRAL: {
actions[socdSliderOptions.deprecatedPinTwo] = GpioAction::SUSTAIN_SOCD_MODE_NEUTRAL; break;
}
case SOCDMode::SOCD_MODE_SECOND_INPUT_PRIORITY: {
actions[socdSliderOptions.deprecatedPinTwo] = GpioAction::SUSTAIN_SOCD_MODE_SECOND_WIN; break;
}
case SOCDMode::SOCD_MODE_FIRST_INPUT_PRIORITY: {
actions[socdSliderOptions.deprecatedPinTwo] = GpioAction::SUSTAIN_SOCD_MODE_FIRST_WIN; break;
}
case SOCDMode::SOCD_MODE_BYPASS: {
actions[socdSliderOptions.deprecatedPinTwo] = GpioAction::SUSTAIN_SOCD_MODE_BYPASS; break;
}
default: break;
}
socdSliderOptions.deprecatedPinTwo = -1;
}
else if (isValidPin(PIN_SLIDER_SOCD_TWO)) {
switch (SLIDER_SOCD_SLOT_TWO) {
case SOCDMode::SOCD_MODE_UP_PRIORITY: {
actions[PIN_SLIDER_SOCD_TWO] = GpioAction::SUSTAIN_SOCD_MODE_UP_PRIO; break;
}
case SOCDMode::SOCD_MODE_NEUTRAL: {
actions[PIN_SLIDER_SOCD_TWO] = GpioAction::SUSTAIN_SOCD_MODE_NEUTRAL; break;
}
case SOCDMode::SOCD_MODE_SECOND_INPUT_PRIORITY: {
actions[PIN_SLIDER_SOCD_TWO] = GpioAction::SUSTAIN_SOCD_MODE_SECOND_WIN; break;
}
case SOCDMode::SOCD_MODE_FIRST_INPUT_PRIORITY: {
actions[PIN_SLIDER_SOCD_TWO] = GpioAction::SUSTAIN_SOCD_MODE_FIRST_WIN; break;
}
case SOCDMode::SOCD_MODE_BYPASS: {
actions[PIN_SLIDER_SOCD_TWO] = GpioAction::SUSTAIN_SOCD_MODE_BYPASS; break;
}
default: break;
}
}
// flag additional pins as being used by an addon not managed here
const auto markAddonPinIfUsed = [&](Pin_t gpPin) -> void {
if (isValidPin(gpPin)) actions[gpPin] = GpioAction::ASSIGNED_TO_ADDON;
};
markAddonPinIfUsed(config.displayOptions.i2cSCLPin);
markAddonPinIfUsed(config.displayOptions.i2cSDAPin);
markAddonPinIfUsed(config.ledOptions.dataPin);
markAddonPinIfUsed(config.ledOptions.pledPin1);
markAddonPinIfUsed(config.ledOptions.pledPin2);
markAddonPinIfUsed(config.ledOptions.pledPin3);
markAddonPinIfUsed(config.ledOptions.pledPin4);
markAddonPinIfUsed(config.addonOptions.analogOptions.analogAdc1PinX);
markAddonPinIfUsed(config.addonOptions.analogOptions.analogAdc1PinY);
markAddonPinIfUsed(config.addonOptions.analogOptions.analogAdc2PinX);
markAddonPinIfUsed(config.addonOptions.analogOptions.analogAdc2PinY);
markAddonPinIfUsed(config.addonOptions.buzzerOptions.pin);
markAddonPinIfUsed(config.addonOptions.focusModeOptions.pin);
markAddonPinIfUsed(config.addonOptions.turboOptions.ledPin);
markAddonPinIfUsed(config.addonOptions.turboOptions.buttonPin);
markAddonPinIfUsed(config.addonOptions.turboOptions.shmupDialPin);
markAddonPinIfUsed(config.addonOptions.turboOptions.shmupBtn1Pin);
markAddonPinIfUsed(config.addonOptions.turboOptions.shmupBtn2Pin);
markAddonPinIfUsed(config.addonOptions.turboOptions.shmupBtn3Pin);
markAddonPinIfUsed(config.addonOptions.turboOptions.shmupBtn4Pin);
markAddonPinIfUsed(config.addonOptions.reverseOptions.buttonPin);
markAddonPinIfUsed(config.addonOptions.reverseOptions.ledPin);
markAddonPinIfUsed(config.addonOptions.analogADS1219Options.i2cSCLPin);
markAddonPinIfUsed(config.addonOptions.analogADS1219Options.i2cSDAPin);
markAddonPinIfUsed(config.addonOptions.tiltOptions.tilt1Pin);
markAddonPinIfUsed(config.addonOptions.tiltOptions.tilt2Pin);
markAddonPinIfUsed(config.addonOptions.tiltOptions.tiltFunctionPin);
markAddonPinIfUsed(config.addonOptions.tiltOptions.tiltLeftAnalogUpPin);
markAddonPinIfUsed(config.addonOptions.tiltOptions.tiltLeftAnalogDownPin);
markAddonPinIfUsed(config.addonOptions.tiltOptions.tiltLeftAnalogLeftPin);
markAddonPinIfUsed(config.addonOptions.tiltOptions.tiltLeftAnalogRightPin);
markAddonPinIfUsed(config.addonOptions.tiltOptions.tiltRightAnalogUpPin);
markAddonPinIfUsed(config.addonOptions.tiltOptions.tiltRightAnalogDownPin);
markAddonPinIfUsed(config.addonOptions.tiltOptions.tiltRightAnalogLeftPin);
markAddonPinIfUsed(config.addonOptions.tiltOptions.tiltRightAnalogRightPin);
markAddonPinIfUsed(config.addonOptions.wiiOptions.i2cSCLPin);
markAddonPinIfUsed(config.addonOptions.wiiOptions.i2cSDAPin);
markAddonPinIfUsed(config.addonOptions.snesOptions.clockPin);
markAddonPinIfUsed(config.addonOptions.snesOptions.latchPin);
markAddonPinIfUsed(config.addonOptions.snesOptions.dataPin);
markAddonPinIfUsed(config.addonOptions.keyboardHostOptions.pin5V);
markAddonPinIfUsed(config.addonOptions.keyboardHostOptions.pinDplus);
if (isValidPin(config.addonOptions.keyboardHostOptions.pinDplus))
actions[config.addonOptions.keyboardHostOptions.pinDplus+1] = GpioAction::ASSIGNED_TO_ADDON;
markAddonPinIfUsed(config.addonOptions.psPassthroughOptions.pin5V);
markAddonPinIfUsed(config.addonOptions.psPassthroughOptions.pinDplus);
if (isValidPin(config.addonOptions.psPassthroughOptions.pinDplus))
actions[config.addonOptions.psPassthroughOptions.pinDplus+1] = GpioAction::ASSIGNED_TO_ADDON;
markAddonPinIfUsed(config.addonOptions.macroOptions.pin);
markAddonPinIfUsed(config.addonOptions.macroOptions.macroList[0].macroTriggerPin);
markAddonPinIfUsed(config.addonOptions.macroOptions.macroList[1].macroTriggerPin);
markAddonPinIfUsed(config.addonOptions.macroOptions.macroList[2].macroTriggerPin);
markAddonPinIfUsed(config.addonOptions.macroOptions.macroList[3].macroTriggerPin);
markAddonPinIfUsed(config.addonOptions.macroOptions.macroList[4].macroTriggerPin);
markAddonPinIfUsed(config.addonOptions.macroOptions.macroList[5].macroTriggerPin);
INIT_UNSET_PROPERTY(config.gpioMappings, pin00, actions[0]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin01, actions[1]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin02, actions[2]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin03, actions[3]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin04, actions[4]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin05, actions[5]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin06, actions[6]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin07, actions[7]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin08, actions[8]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin09, actions[9]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin10, actions[10]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin11, actions[11]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin12, actions[12]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin13, actions[13]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin14, actions[14]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin15, actions[15]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin16, actions[16]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin17, actions[17]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin18, actions[18]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin19, actions[19]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin20, actions[20]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin21, actions[21]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin22, actions[22]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin23, actions[23]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin24, actions[24]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin25, actions[25]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin26, actions[26]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin27, actions[27]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin28, actions[28]);
INIT_UNSET_PROPERTY(config.gpioMappings, pin29, actions[29]);
config.migrations.gpioMappingsMigrated = true;
}
// populate existing configurations' buttonsMask and auxMask to mirror behavior
// from the behavior before this code merged. totally new configs get their
// board defaults via initUnsetPropertiesWithDefaults
@@ -599,6 +953,8 @@ void hotkeysMigration(Config& config)
INIT_UNSET_PROPERTY(hotkeys.hotkey08, auxMask, 0);
INIT_UNSET_PROPERTY(hotkeys.hotkey08, buttonsMask, GAMEPAD_MASK_S2 | GAMEPAD_MASK_A1);
}
config.migrations.hotkeysMigrated = true;
}
// -----------------------------------------------------
@@ -684,7 +1040,8 @@ void ConfigUtils::load(Config& config)
}
// run migrations
hotkeysMigration(config);
if (!config.migrations.hotkeysMigrated) hotkeysMigration(config);
if (!config.migrations.gpioMappingsMigrated) gpioMappingsMigrationCore(config);
// Make sure that fields that were not deserialized are properly initialized.
// They were probably added with a newer version of the firmware.
@@ -839,7 +1196,7 @@ static void __attribute__((noinline)) appendAsString(std::string& str, uint32_t
#define TO_JSON_BYTES(fieldname, submessageType) str.push_back('"'); str.append(Base64::Encode(reinterpret_cast<const char*>(s.fieldname.bytes), s.fieldname.size)); str.push_back('"');
#define TO_JSON_MESSAGE(fieldname, submessageType) PREPROCESSOR_JOIN(toJSON, submessageType)(str, s.fieldname, indentLevel + 1);
#define TO_JSON_REPEATED_RENUM(fieldname, submessageType) appendAsString(str, static_cast<int32_t>(s.fieldname[i]));
#define TO_JSON_REPEATED_ENUM(fieldname, submessageType) appendAsString(str, static_cast<int32_t>(s.fieldname[i]));
#define TO_JSON_REPEATED_UENUM(fieldname, submessageType) appendAsString(str, static_cast<uint32_t>(s.fieldname[i]));
#define TO_JSON_REPEATED_INT32(fieldname, submessageType) appendAsString(str, s.fieldname[i]);
#define TO_JSON_REPEATED_UINT32(fieldname, submessageType) appendAsString(str, s.fieldname[i]);

View File

@@ -7,6 +7,7 @@
#include "AnimationStorage.hpp"
#include "system.h"
#include "config_utils.h"
#include "types.h"
#include <cstring>
#include <string>
@@ -103,15 +104,22 @@ static void __attribute__((noinline)) docToPinLegacy(uint8_t& pin, const Dynamic
}
// Don't inline this function, we do not want to consume stack space in the calling function
static void __attribute__((noinline)) docToPin(int32_t& pin, const DynamicJsonDocument& doc, const char* key)
static void __attribute__((noinline)) docToPin(Pin_t& pin, const DynamicJsonDocument& doc, const char* key)
{
Pin_t oldPin = pin;
if (doc.containsKey(key))
{
pin = doc[key];
}
if (!isValidPin(pin))
{
pin = -1;
GpioAction** gpioMappings = Storage::getInstance().getGpioMappingsArray();
if (isValidPin(pin))
{
*gpioMappings[pin] = GpioAction::ASSIGNED_TO_ADDON;
}
else
{
pin = -1;
*gpioMappings[oldPin] = GpioAction::NONE;
}
}
}
@@ -340,36 +348,14 @@ void addUsedPinsArray(DynamicJsonDocument& doc)
{
auto usedPins = doc.createNestedArray("usedPins");
const auto addPinIfValid = [&](int pin)
{
int numBank0GPIOS = NUM_BANK0_GPIOS;
if (pin >= 0 && pin < numBank0GPIOS)
{
GpioAction** gpioMappings = Storage::getInstance().getGpioMappingsArray();
for (unsigned int pin = 0; pin < NUM_BANK0_GPIOS; pin++) {
// NOTE: addons in webconfig break by seeing their own pins here; if/when they
// are refactored to ignore their own pins from this list, we can include them
if (*gpioMappings[pin] != GpioAction::NONE && *gpioMappings[pin] != GpioAction::ASSIGNED_TO_ADDON) {
usedPins.add(pin);
}
};
const PinMappings& pinMappings = Storage::getInstance().getPinMappings();
addPinIfValid(pinMappings.pinDpadUp);
addPinIfValid(pinMappings.pinDpadDown);
addPinIfValid(pinMappings.pinDpadLeft);
addPinIfValid(pinMappings.pinDpadRight);
addPinIfValid(pinMappings.pinButtonB1);
addPinIfValid(pinMappings.pinButtonB2);
addPinIfValid(pinMappings.pinButtonB3);
addPinIfValid(pinMappings.pinButtonB4);
addPinIfValid(pinMappings.pinButtonL1);
addPinIfValid(pinMappings.pinButtonR1);
addPinIfValid(pinMappings.pinButtonL2);
addPinIfValid(pinMappings.pinButtonR2);
addPinIfValid(pinMappings.pinButtonS1);
addPinIfValid(pinMappings.pinButtonS2);
addPinIfValid(pinMappings.pinButtonL3);
addPinIfValid(pinMappings.pinButtonR3);
addPinIfValid(pinMappings.pinButtonA1);
addPinIfValid(pinMappings.pinButtonA2);
addPinIfValid(pinMappings.pinButtonFn);
}
// TODO: Exclude non-button pins from validation for now, fix this when validation reworked
// addPinIfValid(boardOptions.i2cSDAPin);
@@ -617,8 +603,13 @@ std::string getGamepadOptions()
writeDoc(doc, "profileNumber", gamepadOptions.profileNumber);
writeDoc(doc, "ps4ControllerType", gamepadOptions.ps4ControllerType);
const PinMappings& pinMappings = Storage::getInstance().getPinMappings();
writeDoc(doc, "fnButtonPin", pinMappings.pinButtonFn);
writeDoc(doc, "fnButtonPin", -1);
GpioAction** gpioMappings = Storage::getInstance().getGpioMappingsArray();
for (unsigned int pin = 0; pin < NUM_BANK0_GPIOS; pin++) {
if (*gpioMappings[pin] == GpioAction::BUTTON_PRESS_FN) {
writeDoc(doc, "fnButtonPin", pin);
}
}
HotkeyOptions& hotkeyOptions = Storage::getInstance().getHotkeyOptions();
load_hotkey(&hotkeyOptions.hotkey01, doc, "hotkey01");
@@ -850,34 +841,19 @@ std::string setPinMappings()
{
DynamicJsonDocument doc = get_post_data();
// PinMappings uses -1 to denote unassigned pins
const auto convertPin = [&] (const char* key) -> int32_t
{
int pin = 0;
readDoc(pin, doc, key);
return isValidPin(pin) ? pin : -1;
};
GpioAction** gpioMappings = Storage::getInstance().getGpioMappingsArray();
PinMappings& pinMappings = Storage::getInstance().getPinMappings();
pinMappings.pinDpadUp = convertPin("Up");
pinMappings.pinDpadDown = convertPin("Down");
pinMappings.pinDpadLeft = convertPin("Left");
pinMappings.pinDpadRight = convertPin("Right");
pinMappings.pinButtonB1 = convertPin("B1");
pinMappings.pinButtonB2 = convertPin("B2");
pinMappings.pinButtonB3 = convertPin("B3");
pinMappings.pinButtonB4 = convertPin("B4");
pinMappings.pinButtonL1 = convertPin("L1");
pinMappings.pinButtonR1 = convertPin("R1");
pinMappings.pinButtonL2 = convertPin("L2");
pinMappings.pinButtonR2 = convertPin("R2");
pinMappings.pinButtonS1 = convertPin("S1");
pinMappings.pinButtonS2 = convertPin("S2");
pinMappings.pinButtonL3 = convertPin("L3");
pinMappings.pinButtonR3 = convertPin("R3");
pinMappings.pinButtonA1 = convertPin("A1");
pinMappings.pinButtonA2 = convertPin("A2");
pinMappings.pinButtonFn = convertPin("Fn");
char pinName[6];
for (Pin_t pin = 0; pin < (Pin_t)NUM_BANK0_GPIOS; pin++) {
snprintf(pinName, 6, "pin%0*d", 2, pin);
// setting a pin shouldn't change a new existing addon/reserved pin
if (*gpioMappings[pin] != GpioAction::RESERVED &&
*gpioMappings[pin] != GpioAction::ASSIGNED_TO_ADDON &&
(Pin_t)doc[pinName] != GpioAction::RESERVED &&
(Pin_t)doc[pinName] != GpioAction::ASSIGNED_TO_ADDON) {
readDoc(*gpioMappings[pin], doc, pinName);
}
}
Storage::getInstance().save();
@@ -888,26 +864,38 @@ std::string getPinMappings()
{
DynamicJsonDocument doc(LWIP_HTTPD_POST_MAX_PAYLOAD_LEN);
const PinMappings& pinMappings = Storage::getInstance().getPinMappings();
writeDoc(doc, "Up", cleanPin(pinMappings.pinDpadUp));
writeDoc(doc, "Down", cleanPin(pinMappings.pinDpadDown));
writeDoc(doc, "Left", cleanPin(pinMappings.pinDpadLeft));
writeDoc(doc, "Right", cleanPin(pinMappings.pinDpadRight));
writeDoc(doc, "B1", cleanPin(pinMappings.pinButtonB1));
writeDoc(doc, "B2", cleanPin(pinMappings.pinButtonB2));
writeDoc(doc, "B3", cleanPin(pinMappings.pinButtonB3));
writeDoc(doc, "B4", cleanPin(pinMappings.pinButtonB4));
writeDoc(doc, "L1", cleanPin(pinMappings.pinButtonL1));
writeDoc(doc, "R1", cleanPin(pinMappings.pinButtonR1));
writeDoc(doc, "L2", cleanPin(pinMappings.pinButtonL2));
writeDoc(doc, "R2", cleanPin(pinMappings.pinButtonR2));
writeDoc(doc, "S1", cleanPin(pinMappings.pinButtonS1));
writeDoc(doc, "S2", cleanPin(pinMappings.pinButtonS2));
writeDoc(doc, "L3", cleanPin(pinMappings.pinButtonL3));
writeDoc(doc, "R3", cleanPin(pinMappings.pinButtonR3));
writeDoc(doc, "A1", cleanPin(pinMappings.pinButtonA1));
writeDoc(doc, "A2", cleanPin(pinMappings.pinButtonA2));
writeDoc(doc, "Fn", cleanPin(pinMappings.pinButtonFn));
GpioAction** gpioMappings = Storage::getInstance().getGpioMappingsArray();
writeDoc(doc, "pin00", *gpioMappings[0]);
writeDoc(doc, "pin01", *gpioMappings[1]);
writeDoc(doc, "pin02", *gpioMappings[2]);
writeDoc(doc, "pin03", *gpioMappings[3]);
writeDoc(doc, "pin04", *gpioMappings[4]);
writeDoc(doc, "pin05", *gpioMappings[5]);
writeDoc(doc, "pin06", *gpioMappings[6]);
writeDoc(doc, "pin07", *gpioMappings[7]);
writeDoc(doc, "pin08", *gpioMappings[8]);
writeDoc(doc, "pin09", *gpioMappings[9]);
writeDoc(doc, "pin10", *gpioMappings[10]);
writeDoc(doc, "pin11", *gpioMappings[11]);
writeDoc(doc, "pin12", *gpioMappings[12]);
writeDoc(doc, "pin13", *gpioMappings[13]);
writeDoc(doc, "pin14", *gpioMappings[14]);
writeDoc(doc, "pin15", *gpioMappings[15]);
writeDoc(doc, "pin16", *gpioMappings[16]);
writeDoc(doc, "pin17", *gpioMappings[17]);
writeDoc(doc, "pin18", *gpioMappings[18]);
writeDoc(doc, "pin19", *gpioMappings[19]);
writeDoc(doc, "pin20", *gpioMappings[20]);
writeDoc(doc, "pin21", *gpioMappings[21]);
writeDoc(doc, "pin22", *gpioMappings[22]);
writeDoc(doc, "pin23", *gpioMappings[23]);
writeDoc(doc, "pin24", *gpioMappings[24]);
writeDoc(doc, "pin25", *gpioMappings[25]);
writeDoc(doc, "pin26", *gpioMappings[26]);
writeDoc(doc, "pin27", *gpioMappings[27]);
writeDoc(doc, "pin28", *gpioMappings[28]);
writeDoc(doc, "pin29", *gpioMappings[29]);
return serialize_json(doc);
}
@@ -973,6 +961,8 @@ std::string setAddonOptions()
{
DynamicJsonDocument doc = get_post_data();
GpioAction** gpioMappings = Storage::getInstance().getGpioMappingsArray();
AnalogOptions& analogOptions = Storage::getInstance().getAddonOptions().analogOptions;
docToPin(analogOptions.analogAdc1PinX, doc, "analogAdc1PinX");
docToPin(analogOptions.analogAdc1PinY, doc, "analogAdc1PinY");
@@ -996,11 +986,7 @@ std::string setAddonOptions()
docToValue(buzzerOptions.volume, doc, "buzzerVolume");
docToValue(buzzerOptions.enabled, doc, "BuzzerSpeakerAddonEnabled");
DualDirectionalOptions& dualDirectionalOptions = Storage::getInstance().getAddonOptions().dualDirectionalOptions;
docToPin(dualDirectionalOptions.downPin, doc, "dualDirDownPin");
docToPin(dualDirectionalOptions.upPin, doc, "dualDirUpPin");
docToPin(dualDirectionalOptions.leftPin, doc, "dualDirLeftPin");
docToPin(dualDirectionalOptions.rightPin, doc, "dualDirRightPin");
DualDirectionalOptions& dualDirectionalOptions = Storage::getInstance().getAddonOptions().dualDirectionalOptions;
docToValue(dualDirectionalOptions.dpadMode, doc, "dualDirDpadMode");
docToValue(dualDirectionalOptions.combineMode, doc, "dualDirCombineMode");
docToValue(dualDirectionalOptions.fourWayMode, doc, "dualDirFourWayMode");
@@ -1028,11 +1014,6 @@ std::string setAddonOptions()
docToValue(tiltOptions.tiltSOCDMode, doc, "tiltSOCDMode");
docToValue(tiltOptions.enabled, doc, "TiltInputEnabled");
ExtraButtonOptions& extraButtonOptions = Storage::getInstance().getAddonOptions().extraButtonOptions;
docToPin(extraButtonOptions.pin, doc, "extraButtonPin");
docToValue(extraButtonOptions.buttonMap, doc, "extraButtonMap");
docToValue(extraButtonOptions.enabled, doc, "ExtraButtonAddonEnabled");
FocusModeOptions& focusModeOptions = Storage::getInstance().getAddonOptions().focusModeOptions;
docToPin(focusModeOptions.pin, doc, "focusModePin");
docToValue(focusModeOptions.buttonLockMask, doc, "focusModeButtonLockMask");
@@ -1051,12 +1032,8 @@ std::string setAddonOptions()
docToValue(analogADS1219Options.enabled, doc, "I2CAnalog1219InputEnabled");
SliderOptions& sliderOptions = Storage::getInstance().getAddonOptions().sliderOptions;
docToPin(sliderOptions.pinSliderOne, doc, "sliderPinOne");
docToPin(sliderOptions.pinSliderTwo, doc, "sliderPinTwo");
docToValue(sliderOptions.modeZero, doc, "sliderModeZero");
docToValue(sliderOptions.modeOne, doc, "sliderModeOne");
docToValue(sliderOptions.modeTwo, doc, "sliderModeTwo");
docToValue(sliderOptions.enabled, doc, "JSliderInputEnabled");
docToValue(sliderOptions.modeDefault, doc, "sliderModeZero");
docToValue(sliderOptions.enabled, doc, "JSliderInputEnabled");
PlayerNumberOptions& playerNumberOptions = Storage::getInstance().getAddonOptions().playerNumberOptions;
docToValue(playerNumberOptions.number, doc, "playerNumber");
@@ -1072,12 +1049,8 @@ std::string setAddonOptions()
docToValue(reverseOptions.actionRight, doc, "reverseActionRight");
SOCDSliderOptions& socdSliderOptions = Storage::getInstance().getAddonOptions().socdSliderOptions;
docToValue(socdSliderOptions.enabled, doc, "SliderSOCDInputEnabled");
docToPin(socdSliderOptions.pinOne, doc, "sliderSOCDPinOne");
docToPin(socdSliderOptions.pinTwo, doc, "sliderSOCDPinTwo");
docToValue(socdSliderOptions.modeOne, doc, "sliderSOCDModeOne");
docToValue(socdSliderOptions.modeTwo, doc, "sliderSOCDModeTwo");
docToValue(socdSliderOptions.modeDefault, doc, "sliderSOCDModeDefault");
docToValue(socdSliderOptions.enabled, doc, "SliderSOCDInputEnabled");
docToValue(socdSliderOptions.modeDefault, doc, "sliderSOCDModeDefault");
OnBoardLedOptions& onBoardLedOptions = Storage::getInstance().getAddonOptions().onBoardLedOptions;
docToValue(onBoardLedOptions.mode, doc, "onBoardLedMode");
@@ -1122,7 +1095,13 @@ std::string setAddonOptions()
KeyboardHostOptions& keyboardHostOptions = Storage::getInstance().getAddonOptions().keyboardHostOptions;
docToValue(keyboardHostOptions.enabled, doc, "KeyboardHostAddonEnabled");
Pin_t oldKbPinDplus = keyboardHostOptions.pinDplus;
docToPin(keyboardHostOptions.pinDplus, doc, "keyboardHostPinDplus");
if (isValidPin(keyboardHostOptions.pinDplus))
*gpioMappings[keyboardHostOptions.pinDplus+1] = GpioAction::ASSIGNED_TO_ADDON;
else if (isValidPin(oldKbPinDplus))
// if D+ pin was set, and is no longer, also unset the pin that was used for D-
*gpioMappings[oldKbPinDplus+1] = GpioAction::NONE;
docToPin(keyboardHostOptions.pin5V, doc, "keyboardHostPin5V");
docToValue(keyboardHostOptions.mapping.keyDpadUp, doc, "keyboardHostMap", "Up");
docToValue(keyboardHostOptions.mapping.keyDpadDown, doc, "keyboardHostMap", "Down");
@@ -1145,7 +1124,13 @@ std::string setAddonOptions()
PSPassthroughOptions& psPassthroughOptions = Storage::getInstance().getAddonOptions().psPassthroughOptions;
docToValue(psPassthroughOptions.enabled, doc, "PSPassthroughAddonEnabled");
Pin_t oldPsPinDplus = psPassthroughOptions.pinDplus;
docToPin(psPassthroughOptions.pinDplus, doc, "psPassthroughPinDplus");
if (isValidPin(psPassthroughOptions.pinDplus))
*gpioMappings[psPassthroughOptions.pinDplus+1] = GpioAction::ASSIGNED_TO_ADDON;
else if (isValidPin(oldPsPinDplus))
// if D+ pin was set, and is no longer, also unset the pin that was used for D-
*gpioMappings[oldPsPinDplus+1] = GpioAction::NONE;
docToPin(psPassthroughOptions.pin5V, doc, "psPassthroughPin5V");
Storage::getInstance().save();
@@ -1417,11 +1402,7 @@ std::string getAddonOptions()
writeDoc(doc, "buzzerVolume", buzzerOptions.volume);
writeDoc(doc, "BuzzerSpeakerAddonEnabled", buzzerOptions.enabled);
const DualDirectionalOptions& dualDirectionalOptions = Storage::getInstance().getAddonOptions().dualDirectionalOptions;
writeDoc(doc, "dualDirDownPin", cleanPin(dualDirectionalOptions.downPin));
writeDoc(doc, "dualDirUpPin", cleanPin(dualDirectionalOptions.upPin));
writeDoc(doc, "dualDirLeftPin", cleanPin(dualDirectionalOptions.leftPin));
writeDoc(doc, "dualDirRightPin", cleanPin(dualDirectionalOptions.rightPin));
const DualDirectionalOptions& dualDirectionalOptions = Storage::getInstance().getAddonOptions().dualDirectionalOptions;
writeDoc(doc, "dualDirDpadMode", dualDirectionalOptions.dpadMode);
writeDoc(doc, "dualDirCombineMode", dualDirectionalOptions.combineMode);
writeDoc(doc, "dualDirFourWayMode", dualDirectionalOptions.fourWayMode);
@@ -1449,11 +1430,6 @@ std::string getAddonOptions()
writeDoc(doc, "tiltSOCDMode", tiltOptions.tiltSOCDMode);
writeDoc(doc, "TiltInputEnabled", tiltOptions.enabled);
const ExtraButtonOptions& extraButtonOptions = Storage::getInstance().getAddonOptions().extraButtonOptions;
writeDoc(doc, "extraButtonPin", cleanPin(extraButtonOptions.pin));
writeDoc(doc, "extraButtonMap", extraButtonOptions.buttonMap);
writeDoc(doc, "ExtraButtonAddonEnabled", extraButtonOptions.enabled);
const AnalogADS1219Options& analogADS1219Options = Storage::getInstance().getAddonOptions().analogADS1219Options;
writeDoc(doc, "i2cAnalog1219SDAPin", cleanPin(analogADS1219Options.i2cSDAPin));
writeDoc(doc, "i2cAnalog1219SCLPin", cleanPin(analogADS1219Options.i2cSCLPin));
@@ -1463,12 +1439,8 @@ std::string getAddonOptions()
writeDoc(doc, "I2CAnalog1219InputEnabled", analogADS1219Options.enabled);
const SliderOptions& sliderOptions = Storage::getInstance().getAddonOptions().sliderOptions;
writeDoc(doc, "sliderPinOne", cleanPin(sliderOptions.pinSliderOne));
writeDoc(doc, "sliderPinTwo", cleanPin(sliderOptions.pinSliderTwo));
writeDoc(doc, "sliderModeZero", sliderOptions.modeZero);
writeDoc(doc, "sliderModeOne", sliderOptions.modeOne);
writeDoc(doc, "sliderModeTwo", sliderOptions.modeTwo);
writeDoc(doc, "JSliderInputEnabled", sliderOptions.enabled);
writeDoc(doc, "sliderModeZero", sliderOptions.modeDefault);
writeDoc(doc, "JSliderInputEnabled", sliderOptions.enabled);
const PlayerNumberOptions& playerNumberOptions = Storage::getInstance().getAddonOptions().playerNumberOptions;
writeDoc(doc, "playerNumber", playerNumberOptions.number);
@@ -1484,12 +1456,8 @@ std::string getAddonOptions()
writeDoc(doc, "ReverseInputEnabled", reverseOptions.enabled);
const SOCDSliderOptions& socdSliderOptions = Storage::getInstance().getAddonOptions().socdSliderOptions;
writeDoc(doc, "sliderSOCDPinOne", cleanPin(socdSliderOptions.pinOne));
writeDoc(doc, "sliderSOCDPinTwo", cleanPin(socdSliderOptions.pinTwo));
writeDoc(doc, "sliderSOCDModeOne", socdSliderOptions.modeOne);
writeDoc(doc, "sliderSOCDModeTwo", socdSliderOptions.modeTwo);
writeDoc(doc, "sliderSOCDModeDefault", socdSliderOptions.modeDefault);
writeDoc(doc, "SliderSOCDInputEnabled", socdSliderOptions.enabled);
writeDoc(doc, "sliderSOCDModeDefault", socdSliderOptions.modeDefault);
writeDoc(doc, "SliderSOCDInputEnabled", socdSliderOptions.enabled);
const OnBoardLedOptions& onBoardLedOptions = Storage::getInstance().getAddonOptions().onBoardLedOptions;
writeDoc(doc, "onBoardLedMode", onBoardLedOptions.mode);

View File

@@ -7,6 +7,7 @@
#include "gamepad.h"
#include "enums.pb.h"
#include "storagemanager.h"
#include "types.h"
#include "FlashPROM.h"
#include "CRC32.h"
@@ -104,55 +105,61 @@ Gamepad::Gamepad(int debounceMS) :
void Gamepad::setup()
{
// Configure pin mapping
const PinMappings& pinMappings = Storage::getInstance().getProfilePinMappings();
GpioAction* pinMappings = Storage::getInstance().getProfilePinMappings();
const GamepadOptions& gamepadOptions = Storage::getInstance().getGamepadOptions();
const auto convertPin = [](int32_t pin) -> uint8_t { return isValidPin(pin) ? pin : 0xff; };
mapDpadUp = new GamepadButtonMapping(convertPin(pinMappings.pinDpadUp), GAMEPAD_MASK_UP);
mapDpadDown = new GamepadButtonMapping(convertPin(pinMappings.pinDpadDown), GAMEPAD_MASK_DOWN);
mapDpadLeft = new GamepadButtonMapping(convertPin(pinMappings.pinDpadLeft), GAMEPAD_MASK_LEFT);
mapDpadRight = new GamepadButtonMapping(convertPin(pinMappings.pinDpadRight), GAMEPAD_MASK_RIGHT);
mapButtonB1 = new GamepadButtonMapping(convertPin(pinMappings.pinButtonB1), GAMEPAD_MASK_B1);
mapButtonB2 = new GamepadButtonMapping(convertPin(pinMappings.pinButtonB2), GAMEPAD_MASK_B2);
mapButtonB3 = new GamepadButtonMapping(convertPin(pinMappings.pinButtonB3), GAMEPAD_MASK_B3);
mapButtonB4 = new GamepadButtonMapping(convertPin(pinMappings.pinButtonB4), GAMEPAD_MASK_B4);
mapButtonL1 = new GamepadButtonMapping(convertPin(pinMappings.pinButtonL1), GAMEPAD_MASK_L1);
mapButtonR1 = new GamepadButtonMapping(convertPin(pinMappings.pinButtonR1), GAMEPAD_MASK_R1);
mapButtonL2 = new GamepadButtonMapping(convertPin(pinMappings.pinButtonL2), GAMEPAD_MASK_L2);
mapButtonR2 = new GamepadButtonMapping(convertPin(pinMappings.pinButtonR2), GAMEPAD_MASK_R2);
mapButtonS1 = new GamepadButtonMapping(convertPin(pinMappings.pinButtonS1), GAMEPAD_MASK_S1);
mapButtonS2 = new GamepadButtonMapping(convertPin(pinMappings.pinButtonS2), GAMEPAD_MASK_S2);
mapButtonL3 = new GamepadButtonMapping(convertPin(pinMappings.pinButtonL3), GAMEPAD_MASK_L3);
mapButtonR3 = new GamepadButtonMapping(convertPin(pinMappings.pinButtonR3), GAMEPAD_MASK_R3);
mapButtonA1 = new GamepadButtonMapping(convertPin(pinMappings.pinButtonA1), GAMEPAD_MASK_A1);
mapButtonA2 = new GamepadButtonMapping(convertPin(pinMappings.pinButtonA2), GAMEPAD_MASK_A2);
mapDpadUp = new GamepadButtonMapping(GAMEPAD_MASK_UP);
mapDpadDown = new GamepadButtonMapping(GAMEPAD_MASK_DOWN);
mapDpadLeft = new GamepadButtonMapping(GAMEPAD_MASK_LEFT);
mapDpadRight = new GamepadButtonMapping(GAMEPAD_MASK_RIGHT);
mapButtonB1 = new GamepadButtonMapping(GAMEPAD_MASK_B1);
mapButtonB2 = new GamepadButtonMapping(GAMEPAD_MASK_B2);
mapButtonB3 = new GamepadButtonMapping(GAMEPAD_MASK_B3);
mapButtonB4 = new GamepadButtonMapping(GAMEPAD_MASK_B4);
mapButtonL1 = new GamepadButtonMapping(GAMEPAD_MASK_L1);
mapButtonR1 = new GamepadButtonMapping(GAMEPAD_MASK_R1);
mapButtonL2 = new GamepadButtonMapping(GAMEPAD_MASK_L2);
mapButtonR2 = new GamepadButtonMapping(GAMEPAD_MASK_R2);
mapButtonS1 = new GamepadButtonMapping(GAMEPAD_MASK_S1);
mapButtonS2 = new GamepadButtonMapping(GAMEPAD_MASK_S2);
mapButtonL3 = new GamepadButtonMapping(GAMEPAD_MASK_L3);
mapButtonR3 = new GamepadButtonMapping(GAMEPAD_MASK_R3);
mapButtonA1 = new GamepadButtonMapping(GAMEPAD_MASK_A1);
mapButtonA2 = new GamepadButtonMapping(GAMEPAD_MASK_A2);
mapButtonFn = new GamepadButtonMapping(AUX_MASK_FUNCTION);
gamepadMappings = new GamepadButtonMapping *[GAMEPAD_DIGITAL_INPUT_COUNT]
for (Pin_t pin = 0; pin < (Pin_t)NUM_BANK0_GPIOS; pin++)
{
mapDpadUp, mapDpadDown, mapDpadLeft, mapDpadRight,
mapButtonB1, mapButtonB2, mapButtonB3, mapButtonB4,
mapButtonL1, mapButtonR1, mapButtonL2, mapButtonR2,
mapButtonS1, mapButtonS2, mapButtonL3, mapButtonR3,
mapButtonA1, mapButtonA2
};
for (int i = 0; i < GAMEPAD_DIGITAL_INPUT_COUNT; i++)
{
if (gamepadMappings[i]->isAssigned())
if (pinMappings[pin] > 0)
{
gpio_init(gamepadMappings[i]->pin); // Initialize pin
gpio_set_dir(gamepadMappings[i]->pin, GPIO_IN); // Set as INPUT
gpio_pull_up(gamepadMappings[i]->pin); // Set as PULLUP
gpio_init(pin); // Initialize pin
gpio_set_dir(pin, GPIO_IN); // Set as INPUT
gpio_pull_up(pin); // Set as PULLUP
switch (pinMappings[pin]) {
case GpioAction::BUTTON_PRESS_UP: mapDpadUp->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_DOWN: mapDpadDown->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_LEFT: mapDpadLeft->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_RIGHT: mapDpadRight->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_B1: mapButtonB1->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_B2: mapButtonB2->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_B3: mapButtonB3->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_B4: mapButtonB4->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_L1: mapButtonL1->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_R1: mapButtonR1->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_L2: mapButtonL2->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_R2: mapButtonR2->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_S1: mapButtonS1->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_S2: mapButtonS2->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_L3: mapButtonL3->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_R3: mapButtonR3->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_A1: mapButtonA1->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_A2: mapButtonA2->pinMask |= 1 << pin; break;
case GpioAction::BUTTON_PRESS_FN: mapButtonFn->pinMask |= 1 << pin; break;
default: break;
}
}
}
// initialize the Function pin button/switch if it is configured
if (isValidPin(pinMappings.pinButtonFn)) {
gpio_init(pinMappings.pinButtonFn); // Initialize pin
gpio_set_dir(pinMappings.pinButtonFn, GPIO_IN); // Set as INPUT
gpio_pull_up(pinMappings.pinButtonFn); // Set as PULLUP
}
// setup PS5 compatibility
PS4Data::getInstance().ps4ControllerType = gamepadOptions.ps4ControllerType;
}
@@ -162,18 +169,36 @@ void Gamepad::setup()
*/
void Gamepad::teardown_and_reinit(const uint32_t profileNum)
{
const PinMappings& pinMappings = Storage::getInstance().getProfilePinMappings();
GpioAction* pinMappings = Storage::getInstance().getProfilePinMappings();
delete mapDpadUp;
delete mapDpadDown;
delete mapDpadLeft;
delete mapDpadRight;
delete mapButtonB1;
delete mapButtonB2;
delete mapButtonB3;
delete mapButtonB4;
delete mapButtonL1;
delete mapButtonR1;
delete mapButtonL2;
delete mapButtonR2;
delete mapButtonS1;
delete mapButtonS2;
delete mapButtonL3;
delete mapButtonR3;
delete mapButtonA1;
delete mapButtonA2;
delete mapButtonFn;
// deinitialize the GPIO pins so we don't have orphans
for (int i = 0; i < GAMEPAD_DIGITAL_INPUT_COUNT; i++)
for (Pin_t pin = 0; pin < (Pin_t)NUM_BANK0_GPIOS; pin++)
{
if (gamepadMappings[i]->isAssigned())
if (pinMappings[pin] > 0)
{
gpio_deinit(gamepadMappings[i]->pin);
gpio_deinit(pin);
}
}
if (isValidPin(pinMappings.pinButtonFn)) {
gpio_deinit(pinMappings.pinButtonFn);
}
// set to new profile
Storage::getInstance().setProfile(profileNum);
@@ -253,14 +278,13 @@ void Gamepad::process()
void Gamepad::read()
{
const PinMappings& pinMappings = Storage::getInstance().getProfilePinMappings();
// Need to invert since we're using pullups
uint32_t values = ~gpio_get_all();
Mask_t values = ~gpio_get_all();
// Get the midpoint value for the current mode
uint16_t joystickMid = GetJoystickMidValue(options.inputMode);
state.aux = 0
| (values & (1 << pinMappings.pinButtonFn)) ? AUX_MASK_FUNCTION : 0;
| (values & mapButtonFn->pinMask) ? mapButtonFn->buttonMask : 0;
state.dpad = 0
| ((values & mapDpadUp->pinMask) ? mapDpadUp->buttonMask : 0)

View File

@@ -15,7 +15,6 @@
#include "addons/focus_mode.h"
#include "addons/dualdirectional.h"
#include "addons/tilt.h"
#include "addons/extra_button.h"
#include "addons/keyboard_host.h"
#include "addons/i2canalog1219.h"
#include "addons/jslider.h"
@@ -69,7 +68,6 @@ void GP2040::setup() {
addons.LoadAddon(new AnalogInput(), CORE0_INPUT);
addons.LoadAddon(new BootselButtonAddon(), CORE0_INPUT);
addons.LoadAddon(new DualDirectionalInput(), CORE0_INPUT);
addons.LoadAddon(new ExtraButtonAddon(), CORE0_INPUT);
addons.LoadAddon(new FocusModeAddon(), CORE0_INPUT);
addons.LoadAddon(new I2CAnalog1219Input(), CORE0_INPUT);
addons.LoadAddon(new JSliderInput(), CORE0_INPUT);

View File

@@ -13,13 +13,13 @@
#include "hardware/watchdog.h"
#include "Animation.hpp"
#include "CRC32.h"
#include "types.h"
#include "addons/analog.h"
#include "addons/board_led.h"
#include "addons/bootsel_button.h"
#include "addons/buzzerspeaker.h"
#include "addons/dualdirectional.h"
#include "addons/extra_button.h"
#include "addons/i2canalog1219.h"
#include "addons/i2cdisplay.h"
#include "addons/jslider.h"
@@ -44,8 +44,40 @@
Storage::Storage()
{
EEPROM.start();
gpioMappingsArray[0] = &config.gpioMappings.pin00;
gpioMappingsArray[1] = &config.gpioMappings.pin01;
gpioMappingsArray[2] = &config.gpioMappings.pin02;
gpioMappingsArray[3] = &config.gpioMappings.pin03;
gpioMappingsArray[4] = &config.gpioMappings.pin04;
gpioMappingsArray[5] = &config.gpioMappings.pin05;
gpioMappingsArray[6] = &config.gpioMappings.pin06;
gpioMappingsArray[7] = &config.gpioMappings.pin07;
gpioMappingsArray[8] = &config.gpioMappings.pin08;
gpioMappingsArray[9] = &config.gpioMappings.pin09;
gpioMappingsArray[10] = &config.gpioMappings.pin10;
gpioMappingsArray[11] = &config.gpioMappings.pin11;
gpioMappingsArray[12] = &config.gpioMappings.pin12;
gpioMappingsArray[13] = &config.gpioMappings.pin13;
gpioMappingsArray[14] = &config.gpioMappings.pin14;
gpioMappingsArray[15] = &config.gpioMappings.pin15;
gpioMappingsArray[16] = &config.gpioMappings.pin16;
gpioMappingsArray[17] = &config.gpioMappings.pin17;
gpioMappingsArray[18] = &config.gpioMappings.pin18;
gpioMappingsArray[19] = &config.gpioMappings.pin19;
gpioMappingsArray[20] = &config.gpioMappings.pin20;
gpioMappingsArray[21] = &config.gpioMappings.pin21;
gpioMappingsArray[22] = &config.gpioMappings.pin22;
gpioMappingsArray[23] = &config.gpioMappings.pin23;
gpioMappingsArray[24] = &config.gpioMappings.pin24;
gpioMappingsArray[25] = &config.gpioMappings.pin25;
gpioMappingsArray[26] = &config.gpioMappings.pin26;
gpioMappingsArray[27] = &config.gpioMappings.pin27;
gpioMappingsArray[28] = &config.gpioMappings.pin28;
gpioMappingsArray[29] = &config.gpioMappings.pin29;
critical_section_init(&animationOptionsCs);
ConfigUtils::load(config);
setFunctionalPinMappings(config.gamepadOptions.profileNumber);
}
bool Storage::save()
@@ -134,14 +166,6 @@ void Storage::ResetSettings()
watchdog_reboot(0, SRAM_END, 2000);
}
PinMappings& Storage::getProfilePinMappings() {
if (functionalPinMappings == nullptr) {
functionalPinMappings = (PinMappings*)malloc(sizeof(PinMappings));
setFunctionalPinMappings(config.gamepadOptions.profileNumber);
}
return *functionalPinMappings;
}
void Storage::setProfile(const uint32_t profileNum)
{
if (profileNum < 1 || profileNum > 4) return;
@@ -151,22 +175,37 @@ void Storage::setProfile(const uint32_t profileNum)
void Storage::setFunctionalPinMappings(const uint32_t profileNum)
{
memcpy(functionalPinMappings, &config.pinMappings, sizeof(PinMappings));
for (Pin_t pin = 0; pin < (Pin_t)NUM_BANK0_GPIOS; pin++) {
functionalPinMappings[pin] = *gpioMappingsArray[pin];
}
if (profileNum < 2 || profileNum > 4) return;
AlternativePinMappings alts = this->config.profileOptions.alternativePinMappings[profileNum-2];
if (isValidPin(alts.pinButtonB1)) functionalPinMappings->pinButtonB1 = alts.pinButtonB1;
if (isValidPin(alts.pinButtonB2)) functionalPinMappings->pinButtonB2 = alts.pinButtonB2;
if (isValidPin(alts.pinButtonB3)) functionalPinMappings->pinButtonB3 = alts.pinButtonB3;
if (isValidPin(alts.pinButtonB4)) functionalPinMappings->pinButtonB4 = alts.pinButtonB4;
if (isValidPin(alts.pinButtonL1)) functionalPinMappings->pinButtonL1 = alts.pinButtonL1;
if (isValidPin(alts.pinButtonR1)) functionalPinMappings->pinButtonR1 = alts.pinButtonR1;
if (isValidPin(alts.pinButtonL2)) functionalPinMappings->pinButtonL2 = alts.pinButtonL2;
if (isValidPin(alts.pinButtonR2)) functionalPinMappings->pinButtonR2 = alts.pinButtonR2;
if (isValidPin(alts.pinDpadUp)) functionalPinMappings->pinDpadUp = alts.pinDpadUp;
if (isValidPin(alts.pinDpadDown)) functionalPinMappings->pinDpadDown = alts.pinDpadDown;
if (isValidPin(alts.pinDpadLeft)) functionalPinMappings->pinDpadLeft = alts.pinDpadLeft;
if (isValidPin(alts.pinDpadRight)) functionalPinMappings->pinDpadRight = alts.pinDpadRight;
const auto reassignProfilePin = [&](Pin_t targetPin, GpioAction newAction) -> void {
// reassign the functional pin if:
// 1: it's a real pin (this only matters until profiles are refactored)
// 2: the new action isn't RESERVED or ASSIGNED_TO_ADDON (profiles can't affect special addons)
// 3: the old action isn't RESERVED or ASSIGNED_TO_ADDON (profiles can't affect special addons)
if (isValidPin(targetPin) && newAction != GpioAction::RESERVED &&
newAction != GpioAction::ASSIGNED_TO_ADDON &&
functionalPinMappings[targetPin] != GpioAction::RESERVED &&
functionalPinMappings[targetPin] != GpioAction::ASSIGNED_TO_ADDON) {
functionalPinMappings[targetPin] = newAction;
}
};
reassignProfilePin(alts.pinButtonB1, GpioAction::BUTTON_PRESS_B1);
reassignProfilePin(alts.pinButtonB2, GpioAction::BUTTON_PRESS_B2);
reassignProfilePin(alts.pinButtonB3, GpioAction::BUTTON_PRESS_B3);
reassignProfilePin(alts.pinButtonB4, GpioAction::BUTTON_PRESS_B4);
reassignProfilePin(alts.pinButtonL1, GpioAction::BUTTON_PRESS_L1);
reassignProfilePin(alts.pinButtonR1, GpioAction::BUTTON_PRESS_R1);
reassignProfilePin(alts.pinButtonL2, GpioAction::BUTTON_PRESS_L2);
reassignProfilePin(alts.pinButtonR2, GpioAction::BUTTON_PRESS_R2);
reassignProfilePin(alts.pinDpadUp, GpioAction::BUTTON_PRESS_UP);
reassignProfilePin(alts.pinDpadDown, GpioAction::BUTTON_PRESS_DOWN);
reassignProfilePin(alts.pinDpadLeft, GpioAction::BUTTON_PRESS_LEFT);
reassignProfilePin(alts.pinDpadRight, GpioAction::BUTTON_PRESS_RIGHT);
}
void Storage::SetConfigMode(bool mode) { // hack for config mode
@@ -272,4 +311,4 @@ AnimationOptions AnimationStorage::getAnimationOptions()
void AnimationStorage::save()
{
Storage::getInstance().enqueueAnimationOptionsSave(AnimationStation::options);
}
}

375
www/package-lock.json generated
View File

@@ -24,7 +24,9 @@
"react-dom": "^18.2.0",
"react-i18next": "^12.3.1",
"react-router-dom": "^6.10.0",
"yup": "^1.1.1"
"react-select": "^5.7.5",
"yup": "^1.1.1",
"zustand": "^4.4.1"
},
"devDependencies": {
"@types/node": "^20.5.9",
@@ -63,7 +65,6 @@
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz",
"integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==",
"dev": true,
"dependencies": {
"@babel/highlight": "^7.22.5"
},
@@ -182,7 +183,6 @@
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz",
"integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==",
"dev": true,
"dependencies": {
"@babel/types": "^7.22.5"
},
@@ -246,7 +246,6 @@
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
"integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
@@ -255,7 +254,6 @@
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz",
"integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
@@ -287,7 +285,6 @@
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz",
"integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==",
"dev": true,
"dependencies": {
"@babel/helper-validator-identifier": "^7.22.5",
"chalk": "^2.0.0",
@@ -389,7 +386,6 @@
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz",
"integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==",
"dev": true,
"dependencies": {
"@babel/helper-string-parser": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.5",
@@ -399,6 +395,128 @@
"node": ">=6.9.0"
}
},
"node_modules/@emotion/babel-plugin": {
"version": "11.11.0",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz",
"integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==",
"dependencies": {
"@babel/helper-module-imports": "^7.16.7",
"@babel/runtime": "^7.18.3",
"@emotion/hash": "^0.9.1",
"@emotion/memoize": "^0.8.1",
"@emotion/serialize": "^1.1.2",
"babel-plugin-macros": "^3.1.0",
"convert-source-map": "^1.5.0",
"escape-string-regexp": "^4.0.0",
"find-root": "^1.1.0",
"source-map": "^0.5.7",
"stylis": "4.2.0"
}
},
"node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@emotion/babel-plugin/node_modules/source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/@emotion/cache": {
"version": "11.11.0",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz",
"integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==",
"dependencies": {
"@emotion/memoize": "^0.8.1",
"@emotion/sheet": "^1.2.2",
"@emotion/utils": "^1.2.1",
"@emotion/weak-memoize": "^0.3.1",
"stylis": "4.2.0"
}
},
"node_modules/@emotion/hash": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz",
"integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ=="
},
"node_modules/@emotion/memoize": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
"integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
},
"node_modules/@emotion/react": {
"version": "11.11.1",
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz",
"integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==",
"dependencies": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.11.0",
"@emotion/cache": "^11.11.0",
"@emotion/serialize": "^1.1.2",
"@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
"@emotion/utils": "^1.2.1",
"@emotion/weak-memoize": "^0.3.1",
"hoist-non-react-statics": "^3.3.1"
},
"peerDependencies": {
"react": ">=16.8.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@emotion/serialize": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz",
"integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==",
"dependencies": {
"@emotion/hash": "^0.9.1",
"@emotion/memoize": "^0.8.1",
"@emotion/unitless": "^0.8.1",
"@emotion/utils": "^1.2.1",
"csstype": "^3.0.2"
}
},
"node_modules/@emotion/sheet": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz",
"integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA=="
},
"node_modules/@emotion/unitless": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
"integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ=="
},
"node_modules/@emotion/use-insertion-effect-with-fallbacks": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz",
"integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==",
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/@emotion/utils": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz",
"integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg=="
},
"node_modules/@emotion/weak-memoize": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz",
"integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww=="
},
"node_modules/@esbuild/android-arm": {
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz",
@@ -834,6 +952,28 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@floating-ui/core": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz",
"integrity": "sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==",
"dependencies": {
"@floating-ui/utils": "^0.1.3"
}
},
"node_modules/@floating-ui/dom": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz",
"integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==",
"dependencies": {
"@floating-ui/core": "^1.4.2",
"@floating-ui/utils": "^0.1.3"
}
},
"node_modules/@floating-ui/utils": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.4.tgz",
"integrity": "sha512-qprfWkn82Iw821mcKofJ5Pk9wgioHicxcQMxx+5zt5GSKoqdWvgG5AxVmpmUUjzTLPVSH5auBrhI93Deayn/DA=="
},
"node_modules/@hello-pangea/color-picker": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/@hello-pangea/color-picker/-/color-picker-3.2.2.tgz",
@@ -1067,6 +1207,11 @@
"integrity": "sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ==",
"dev": true
},
"node_modules/@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
},
"node_modules/@types/prop-types": {
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
@@ -1198,7 +1343,6 @@
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"dependencies": {
"color-convert": "^1.9.0"
},
@@ -1327,6 +1471,36 @@
"proxy-from-env": "^1.1.0"
}
},
"node_modules/babel-plugin-macros": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
"integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
"dependencies": {
"@babel/runtime": "^7.12.5",
"cosmiconfig": "^7.0.0",
"resolve": "^1.19.0"
},
"engines": {
"node": ">=10",
"npm": ">=6"
}
},
"node_modules/babel-plugin-macros/node_modules/resolve": {
"version": "1.22.6",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz",
"integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==",
"dependencies": {
"is-core-module": "^2.13.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -1491,7 +1665,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true,
"engines": {
"node": ">=6"
}
@@ -1520,7 +1693,6 @@
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
@@ -1592,7 +1764,6 @@
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"dependencies": {
"color-name": "1.1.3"
}
@@ -1600,8 +1771,7 @@
"node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"dev": true
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
},
"node_modules/combined-stream": {
"version": "1.0.8",
@@ -1756,8 +1926,7 @@
"node_modules/convert-source-map": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
"dev": true
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
},
"node_modules/cookie": {
"version": "0.5.0",
@@ -1805,6 +1974,21 @@
"yarn": ">=1"
}
},
"node_modules/cosmiconfig": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
"integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
"dependencies": {
"@types/parse-json": "^4.0.0",
"import-fresh": "^3.2.1",
"parse-json": "^5.0.0",
"path-type": "^4.0.0",
"yaml": "^1.10.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -2016,6 +2200,14 @@
"node": ">= 0.8"
}
},
"node_modules/error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"dependencies": {
"is-arrayish": "^0.2.1"
}
},
"node_modules/es-abstract": {
"version": "1.21.2",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz",
@@ -2160,7 +2352,6 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"dev": true,
"engines": {
"node": ">=0.8.0"
}
@@ -2672,6 +2863,11 @@
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"dev": true
},
"node_modules/find-root": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
},
"node_modules/find-up": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@@ -2812,8 +3008,7 @@
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
"node_modules/function.prototype.name": {
"version": "1.1.5",
@@ -2984,7 +3179,6 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.1"
},
@@ -3005,7 +3199,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true,
"engines": {
"node": ">=4"
}
@@ -3160,7 +3353,6 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
"dev": true,
"dependencies": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
@@ -3242,6 +3434,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
},
"node_modules/is-bigint": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
@@ -3295,10 +3492,9 @@
}
},
"node_modules/is-core-module": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz",
"integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==",
"dev": true,
"version": "2.13.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
"integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
"dependencies": {
"has": "^1.0.3"
},
@@ -3645,6 +3841,11 @@
"node": ">=4"
}
},
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -3695,6 +3896,11 @@
"node": ">= 0.8.0"
}
},
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
},
"node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -4120,7 +4326,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
"dependencies": {
"callsites": "^3.0.0"
},
@@ -4128,6 +4333,23 @@
"node": ">=6"
}
},
"node_modules/parse-json": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
"dependencies": {
"@babel/code-frame": "^7.0.0",
"error-ex": "^1.3.1",
"json-parse-even-better-errors": "^2.3.0",
"lines-and-columns": "^1.1.6"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -4167,8 +4389,7 @@
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"node_modules/path-to-regexp": {
"version": "0.1.7",
@@ -4176,6 +4397,14 @@
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==",
"dev": true
},
"node_modules/path-type": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
"engines": {
"node": ">=8"
}
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -4544,6 +4773,31 @@
"react-dom": ">=16.8"
}
},
"node_modules/react-select": {
"version": "5.7.5",
"resolved": "https://registry.npmjs.org/react-select/-/react-select-5.7.5.tgz",
"integrity": "sha512-jgYZa2xgKP0DVn5GZk7tZwbRx7kaVz1VqU41S8z1KWmshRDhlrpKS0w80aS1RaK5bVIXpttgSou7XCjWw1ncKA==",
"dependencies": {
"@babel/runtime": "^7.12.0",
"@emotion/cache": "^11.4.0",
"@emotion/react": "^11.8.1",
"@floating-ui/dom": "^1.0.1",
"@types/react-transition-group": "^4.4.0",
"memoize-one": "^6.0.0",
"prop-types": "^15.6.0",
"react-transition-group": "^4.3.0",
"use-isomorphic-layout-effect": "^1.1.2"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-select/node_modules/memoize-one": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
},
"node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
@@ -4640,7 +4894,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true,
"engines": {
"node": ">=4"
}
@@ -5199,11 +5452,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/stylis": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
},
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"dependencies": {
"has-flag": "^3.0.0"
},
@@ -5215,7 +5472,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
@@ -5290,7 +5546,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
"dev": true,
"engines": {
"node": ">=4"
}
@@ -5495,6 +5750,19 @@
"punycode": "^2.1.0"
}
},
"node_modules/use-isomorphic-layout-effect": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz",
"integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/use-memo-one": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz",
@@ -5503,6 +5771,14 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@@ -5716,6 +5992,14 @@
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true
},
"node_modules/yaml": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
"engines": {
"node": ">= 6"
}
},
"node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
@@ -5765,6 +6049,33 @@
"toposort": "^2.0.2",
"type-fest": "^2.19.0"
}
},
"node_modules/zustand": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.1.tgz",
"integrity": "sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw==",
"dependencies": {
"use-sync-external-store": "1.2.0"
},
"engines": {
"node": ">=12.7.0"
},
"peerDependencies": {
"@types/react": ">=16.8",
"immer": ">=9.0",
"react": ">=16.8"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"immer": {
"optional": true
},
"react": {
"optional": true
}
}
}
}
}

View File

@@ -20,7 +20,9 @@
"react-dom": "^18.2.0",
"react-i18next": "^12.3.1",
"react-router-dom": "^6.10.0",
"yup": "^1.1.1"
"react-select": "^5.7.5",
"yup": "^1.1.1",
"zustand": "^4.4.1"
},
"scripts": {
"analyze": "vite build --sourcemap true && source-map-explorer 'build/assets/*.js' --no-border-checks",

View File

@@ -355,13 +355,7 @@ app.get('/api/getAddonsOptions', (req, res) => {
return res.send({
turboPin: -1,
turboPinLED: -1,
sliderPinOne: -1,
sliderPinTwo: -1,
sliderModeZero: 0,
sliderModeOne: 1,
sliderModeTwo: 2,
sliderSOCDPinOne: -1,
sliderSOCDPinTwo: -1,
turboShotCount: 20,
reversePin: -1,
reversePinLED: -1,
@@ -375,10 +369,6 @@ app.get('/api/getAddonsOptions', (req, res) => {
i2cAnalog1219Speed: 400000,
i2cAnalog1219Address: 0x40,
onBoardLedMode: 0,
dualDirUpPin: -1,
dualDirDownPin: -1,
dualDirLeftPin: -1,
dualDirRightPin: -1,
dualDirDpadMode: 0,
dualDirCombineMode: 0,
dualDirFourWayMode: 0,
@@ -415,8 +405,6 @@ app.get('/api/getAddonsOptions', (req, res) => {
bootselButtonMap: 0,
buzzerPin: -1,
buzzerVolume: 100,
extraButtonPin: -1,
extraButtonMap: 0,
focusModePin: -1,
focusModeButtonLockMask: 0,
focusModeButtonLockEnabled: 0,
@@ -438,8 +426,6 @@ app.get('/api/getAddonsOptions', (req, res) => {
shmupBtnMask3: 0,
shmupBtnMask4: 0,
pinShmupDial: -1,
sliderSOCDModeOne: 0,
sliderSOCDModeTwo: 2,
sliderSOCDModeDefault: 1,
wiiExtensionSDAPin: -1,
wiiExtensionSCLPin: -1,
@@ -463,7 +449,6 @@ app.get('/api/getAddonsOptions', (req, res) => {
BootselButtonAddonEnabled: 1,
DualDirectionalInputEnabled: 1,
TiltInputEnabled: 1,
ExtraButtonAddonEnabled: 1,
I2CAnalog1219InputEnabled: 1,
JSliderInputEnabled: 1,
KeyboardHostAddonEnabled: 1,

View File

@@ -21,22 +21,6 @@ export const dualDirectionScheme = {
.number()
.required()
.label('Dual Directional Input Enabled'),
dualDirUpPin: yup
.number()
.label('Dual Directional Up Pin')
.validatePinWhenValue('DualDirectionalInputEnabled'),
dualDirDownPin: yup
.number()
.label('Dual Directional Down Pin')
.validatePinWhenValue('DualDirectionalInputEnabled'),
dualDirLeftPin: yup
.number()
.label('Dual Directional Left Pin')
.validatePinWhenValue('DualDirectionalInputEnabled'),
dualDirRightPin: yup
.number()
.label('Dual Directional Right Pin')
.validatePinWhenValue('DualDirectionalInputEnabled'),
dualDirDpadMode: yup
.number()
.label('Dual Stick Mode')
@@ -59,10 +43,6 @@ export const dualDirectionScheme = {
export const dualDirectionState = {
DualDirectionalInputEnabled: 0,
dualDirUpPin: -1,
dualDirDownPin: -1,
dualDirLeftPin: -1,
dualDirRightPin: -1,
dualDirDpadMode: 0,
dualDirCombineMode: 0,
dualDirFourWayMode: 0,
@@ -77,60 +57,11 @@ const DualDirection = ({ values, errors, handleChange, handleCheckbox }) => {
hidden={!values.DualDirectionalInputEnabled}
>
<Row className="mb-3">
<FormControl
type="number"
label={t('AddonsConfig:dual-directional-input-up-pin-label')}
name="dualDirUpPin"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.dualDirUpPin}
error={errors.dualDirUpPin}
isInvalid={errors.dualDirUpPin}
onChange={handleChange}
min={-1}
max={29}
/>
<FormControl
type="number"
label={t('AddonsConfig:dual-directional-input-down-pin-label')}
name="dualDirDownPin"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.dualDirDownPin}
error={errors.dualDirDownPin}
isInvalid={errors.dualDirDownPin}
onChange={handleChange}
min={-1}
max={29}
/>
<FormControl
type="number"
label={t('AddonsConfig:dual-directional-input-left-pin-label')}
name="dualDirLeftPin"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.dualDirLeftPin}
error={errors.dualDirLeftPin}
isInvalid={errors.dualDirLeftPin}
onChange={handleChange}
min={-1}
max={29}
/>
<FormControl
type="number"
label={t('AddonsConfig:dual-directional-input-right-pin-label')}
name="dualDirRightPin"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.dualDirRightPin}
error={errors.dualDirRightPin}
isInvalid={errors.dualDirRightPin}
onChange={handleChange}
min={-1}
max={29}
/>
</Row>
<Row className="mb-3">
<p>
{t(
'AddonsConfig:pin-config-moved-to-core-text',
)}
</p>
<FormSelect
label={t('AddonsConfig:dual-directional-input-dpad-mode-label')}
name="dualDirDpadMode"

View File

@@ -1,89 +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';
import FormSelect from '../Components/FormSelect';
import { BUTTON_MASKS } from '../Data/Buttons';
export const extraButtonScheme = {
ExtraButtonAddonEnabled: yup
.number()
.required()
.label('Extra Button Add-On Enabled'),
extraButtonPin: yup
.number()
.label('Extra Button Pin')
.validatePinWhenValue('ExtraButtonAddonEnabled'),
extraButtonMap: yup
.number()
.label('Extra Button Map')
.validateSelectionWhenValue('ExtraButtonAddonEnabled', BUTTON_MASKS),
};
export const extraButtonState = {
ExtraButtonAddonEnabled: 0,
extraButtonPin: -1,
extraButtonMap: 0,
};
const ExtraButton = ({ values, errors, handleChange, handleCheckbox }) => {
const { t } = useTranslation();
return (
<Section title={t('AddonsConfig:extra-button-header-text')}>
<div
id="ExtraButtonAddonOptions"
hidden={!values.ExtraButtonAddonEnabled}
>
<Row className="mb-3">
<FormControl
type="number"
label={t('AddonsConfig:extra-button-pin-label')}
name="extraButtonPin"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.extraButtonPin}
error={errors.extraButtonPin}
isInvalid={errors.extraButtonPin}
onChange={handleChange}
min={-1}
max={29}
/>
<FormSelect
label={t('AddonsConfig:extra-button-map-label')}
name="extraButtonMap"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.extraButtonMap}
error={errors.extraButtonMap}
isInvalid={errors.extraButtonMap}
onChange={handleChange}
>
{BUTTON_MASKS.map((o, i) => (
<option key={`extraButtonMap-option-${i}`} value={o.value}>
{o.label}
</option>
))}
</FormSelect>
</Row>
</div>
<FormCheck
label={t('Common:switch-enabled')}
type="switch"
id="ExtraButtonAddonButton"
reverse
isInvalid={false}
checked={Boolean(values.ExtraButtonAddonEnabled)}
onChange={(e) => {
handleCheckbox('ExtraButtonAddonEnabled', values);
handleChange(e);
}}
/>
</Section>
);
};
export default ExtraButton;

View File

@@ -10,35 +10,15 @@ import { DPAD_MODES } from '../Data/Addons';
export const joystickScheme = {
JSliderInputEnabled: yup.number().required().label('JSlider Input Enabled'),
sliderPinOne: yup
.number()
.label('Slider Pin One')
.validatePinWhenValue('JSliderInputEnabled'),
sliderPinTwo: yup
.number()
.label('Slider Pin Two')
.validatePinWhenValue('JSliderInputEnabled'),
sliderModeZero: yup
.number()
.label('Slider Mode Zero')
.validateSelectionWhenValue('JSliderInputEnabled', DPAD_MODES),
sliderModeOne: yup
.number()
.label('Slider Mode One')
.validateSelectionWhenValue('JSliderInputEnabled', DPAD_MODES),
sliderModeTwo: yup
.number()
.label('Slider Mode Two')
.label('Default Mode')
.validateSelectionWhenValue('JSliderInputEnabled', DPAD_MODES),
};
export const joystickState = {
JSliderInputEnabled: 0,
sliderPinOne: -1,
sliderPinTwo: -1,
sliderModeZero: 0,
sliderModeOne: 1,
sliderModeTwo: 2,
};
const Joystick = ({ values, errors, handleChange, handleCheckbox }) => {
@@ -47,6 +27,11 @@ const Joystick = ({ values, errors, handleChange, handleCheckbox }) => {
<Section title={t('AddonsConfig:joystick-selection-slider-header-text')}>
<div id="JSliderInputOptions" hidden={!values.JSliderInputEnabled}>
<Row className="mb-3">
<p>
{t(
'AddonsConfig:pin-config-moved-to-core-text',
)}
</p>
<FormSelect
label={t('AddonsConfig:joystick-selection-slider-mode-zero-label')}
name="sliderModeZero"
@@ -63,64 +48,6 @@ const Joystick = ({ values, errors, handleChange, handleCheckbox }) => {
</option>
))}
</FormSelect>
<FormSelect
label={t('AddonsConfig:joystick-selection-slider-mode-one-label')}
name="sliderModeOne"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.sliderModeOne}
error={errors.sliderModeOne}
isInvalid={errors.sliderModeOne}
onChange={handleChange}
>
{DPAD_MODES.map((o, i) => (
<option key={`sliderModeOne-option-${i}`} value={o.value}>
{o.label}
</option>
))}
</FormSelect>
<FormControl
type="number"
label={t('AddonsConfig:joystick-selection-slider-pin-one-label')}
name="sliderPinOne"
className="form-select-sm"
groupClassName="col-sm-1 mb-3"
value={values.sliderPinOne}
error={errors.sliderPinOne}
isInvalid={errors.sliderPinOne}
onChange={handleChange}
min={-1}
max={29}
/>
<FormSelect
label={t('AddonsConfig:joystick-selection-slider-mode-two-label')}
name="sliderModeTwo"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.sliderModeTwo}
error={errors.sliderModeTwo}
isInvalid={errors.sliderModeTwo}
onChange={handleChange}
>
{DPAD_MODES.map((o, i) => (
<option key={`sliderModeTwo-option-${i}`} value={o.value}>
{o.label}
</option>
))}
</FormSelect>
<FormControl
type="number"
label={t('AddonsConfig:joystick-selection-slider-pin-two-label')}
name="sliderPinTwo"
className="form-control-sm"
groupClassName="col-sm-1 mb-3"
value={values.sliderPinTwo}
error={errors.sliderPinTwo}
isInvalid={errors.sliderPinTwo}
onChange={handleChange}
min={-1}
max={29}
/>
</Row>
</div>
<FormCheck

View File

@@ -13,35 +13,14 @@ export const socdScheme = {
.number()
.required()
.label('Slider SOCD Input Enabled'),
sliderSOCDModeOne: yup
.number()
.label('SOCD Slider Mode One')
.validateSelectionWhenValue('SliderSOCDInputEnabled', SOCD_MODES),
sliderSOCDModeTwo: yup
.number()
.label('SOCD Slider Mode Two')
.validateSelectionWhenValue('SliderSOCDInputEnabled', SOCD_MODES),
sliderSOCDModeDefault: yup
.number()
.label('SOCD Slider Mode Default')
.validateSelectionWhenValue('SliderSOCDInputEnabled', SOCD_MODES),
sliderSOCDPinOne: yup
.number()
.label('Slider SOCD Up Priority Pin')
.validatePinWhenValue('SliderSOCDInputEnabled'),
sliderSOCDPinTwo: yup
.number()
.label('Slider SOCD Second Priority Pin')
.validatePinWhenValue('SliderSOCDInputEnabled'),
};
export const socdState = {
SliderSOCDInputEnabled: 0,
sliderSOCDPinOne: -1,
sliderSOCDPinTwo: -1,
sliderSOCDModeOne: 0,
sliderSOCDModeTwo: 2,
sliderSOCDModeDefault: 1,
};
@@ -56,6 +35,9 @@ const SOCD = ({ values, errors, handleChange, handleCheckbox }) => {
<p>
{t(
'AddonsConfig:socd-cleaning-mode-selection-slider-sub-header-text',
)}<br />
{t(
'AddonsConfig:pin-config-moved-to-core-text',
)}
</p>
<FormSelect
@@ -76,72 +58,6 @@ const SOCD = ({ values, errors, handleChange, handleCheckbox }) => {
</option>
))}
</FormSelect>
<FormSelect
label={t(
'AddonsConfig:socd-cleaning-mode-selection-slider-mode-one-label',
)}
name="sliderSOCDModeOne"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.sliderSOCDModeOne}
error={errors.sliderSOCDModeOne}
isInvalid={errors.sliderSOCDModeOne}
onChange={handleChange}
>
{SOCD_MODES.map((o, i) => (
<option key={`sliderSOCDModeOne-option-${i}`} value={o.value}>
{o.label}
</option>
))}
</FormSelect>
<FormControl
type="number"
label={t(
'AddonsConfig:socd-cleaning-mode-selection-slider-pin-one-label',
)}
name="sliderSOCDPinOne"
className="form-select-sm"
groupClassName="col-sm-1 mb-3"
value={values.sliderSOCDPinOne}
error={errors.sliderSOCDPinOne}
isInvalid={errors.sliderSOCDPinOne}
onChange={handleChange}
min={-1}
max={29}
/>
<FormSelect
label={t(
'AddonsConfig:socd-cleaning-mode-selection-slider-mode-two-label',
)}
name="sliderSOCDModeTwo"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.sliderSOCDModeTwo}
error={errors.sliderSOCDModeTwo}
isInvalid={errors.sliderSOCDModeTwo}
onChange={handleChange}
>
{SOCD_MODES.map((o, i) => (
<option key={`sliderSOCDModeTwo-option-${i}`} value={o.value}>
{o.label}
</option>
))}
</FormSelect>
<FormControl
type="number"
label={t(
'AddonsConfig:socd-cleaning-mode-selection-slider-pin-two-label',
)}
name="sliderSOCDPinTwo"
className="form-control-sm"
groupClassName="col-sm-1 mb-3"
value={values.sliderSOCDPinTwo}
error={errors.sliderSOCDPinTwo}
isInvalid={errors.sliderSOCDPinTwo}
onChange={handleChange}
min={-1}
max={29}
/>
</Row>
</div>
<FormCheck

View File

@@ -1,89 +0,0 @@
import React, { useEffect, useRef, useState } from 'react';
import { Button, Modal } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
import WebApi from '../Services/WebApi';
const CaptureButton = ({ buttonName, onChange, abortSignalRef, triggerCapture, onTriggeredCaptureComplete, onStopCaptureSequence, size }) => {
const { t } = useTranslation('');
const controller = abortSignalRef || useRef();
const triggerNextRef = useRef(true);
const [modalVisible, setModalVisible] = useState(false);
const newAbortSignal = () => {
controller.current = new AbortController();
return controller.current.signal;
};
const timeout = (ms) => (new Promise(resolve => setTimeout(resolve, ms)));
const getHeldPins = async () => {
const data = await WebApi.getHeldPins(setModalVisible, newAbortSignal);
const pin = data?.heldPins?.at(0);
if (!isNaN(pin))
onChange(pin);
if (data.canceled) {
await timeout(50);
await WebApi.abortGetHeldPins();
await timeout(50);
}
if (triggerNextRef.current) {
triggerNextRef.current = false;
onTriggeredCaptureComplete();
}
};
useEffect(() => () => controller?.current?.abort(), []);
const signalAbort = async () => {
await timeout(50);
controller?.current?.abort();
setModalVisible(false);
};
useEffect(() => {
if (triggerCapture) {
triggerNextRef.current = true;
getHeldPins().then();
}
}, [triggerCapture]);
return (
<>
<Modal centered show={modalVisible} onHide={() => signalAbort()}>
<Modal.Header closeButton>
<Modal.Title className="me-auto">{`${t('CaptureButton:capture-button-modal-title')} (${buttonName})`}</Modal.Title>
</Modal.Header>
<Modal.Body className="row">
<span className="col-sm-10">{`${t('CaptureButton:capture-button-modal-content')}`}</span>
<span className="col-sm-1">
<span className="spinner-border" />
</span>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={() => signalAbort()}>
Cancel
</Button>
{triggerCapture && onStopCaptureSequence &&
<Button variant="danger"
onClick={async () => {
triggerNextRef.current = false;
onStopCaptureSequence();
await signalAbort();
}}>
Stop Capture
</Button>}
</Modal.Footer>
</Modal>
<Button
className="ms-3"
size={size}
variant="secondary"
onClick={() => getHeldPins()}
>
{t('CaptureButton:capture-button-button-label')}
</Button>
</>
);
};
export default CaptureButton;

View File

@@ -0,0 +1,106 @@
import React, { useEffect, useRef, useState } from 'react';
import { Button, Modal } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
import WebApi from '../Services/WebApi';
type CaptureButtonTypes = {
labels: string[];
onChange: (label: string, pin: string) => void;
};
const CaptureButton = ({ labels, onChange }: CaptureButtonTypes) => {
const { t } = useTranslation('');
const controller = useRef<null | AbortController>(null);
const stopRef = useRef(false);
const [showModal, setShowModal] = useState(false);
const [labelIndex, setLabelIndex] = useState(0);
const [triggerCapture, setTriggerCapture] = useState(false);
useEffect(() => () => controller?.current?.abort(), []);
const currentLabel = labels[labelIndex] || '';
const hasNext = Boolean(labels[labelIndex + 1]);
const timeout = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const closeAndReset = () => {
stopRef.current = false;
setShowModal(false);
setLabelIndex(0);
};
const getHeldPins = async () => {
setTriggerCapture(false);
controller.current = new AbortController();
const data = await WebApi.getHeldPins(controller.current.signal);
const pin = data?.heldPins?.at(0);
if (!isNaN(pin)) onChange(currentLabel, pin);
// Gets called for both skip and stop
if (data.canceled) {
await timeout(50);
await WebApi.abortGetHeldPins();
await timeout(50);
}
if (stopRef.current || !hasNext) return closeAndReset();
setLabelIndex((index) => index + 1);
setTriggerCapture(true);
};
const stopCapture = async () => {
await timeout(50);
stopRef.current = true;
controller?.current?.abort();
};
const skipButton = async () => {
await timeout(50);
controller?.current?.abort();
};
useEffect(() => {
if (triggerCapture) {
setShowModal(true);
getHeldPins();
}
}, [triggerCapture]);
return (
<>
<Modal centered show={showModal} onHide={() => stopCapture()}>
<Modal.Header closeButton>
<Modal.Title className="me-auto">{`${t(
'CaptureButton:capture-button-modal-title',
)} ${currentLabel}`}</Modal.Title>
</Modal.Header>
<Modal.Body className="row">
<span className="col-sm-10">
{t('CaptureButton:capture-button-modal-content')}
</span>
<span className="col-sm-1">
<span className="spinner-border" />
</span>
</Modal.Body>
<Modal.Footer>
{hasNext && (
<Button variant="secondary" onClick={() => skipButton()}>
{t('CaptureButton:capture-button-modal-skip')}
</Button>
)}
<Button variant="danger" onClick={() => stopCapture()}>
{t('CaptureButton:capture-button-modal-stop')}
</Button>
</Modal.Footer>
</Modal>
<Button variant="secondary" onClick={() => setTriggerCapture(true)}>
{t('CaptureButton:capture-button-button-label')}
</Button>
</>
);
};
export default CaptureButton;

View File

@@ -1,22 +1,34 @@
{
"pico": {
"Up": 2,
"Down": 3,
"Left": 5,
"Right": 4,
"B1": 6,
"B2": 7,
"B3": 10,
"B4": 11,
"L1": 13,
"R1": 12,
"L2": 9,
"R2": 8,
"S1": 16,
"S2": 17,
"L3": 18,
"R3": 19,
"A1": 20,
"A2": 21
"pin00": -10,
"pin01": -10,
"pin02": 1,
"pin03": 2,
"pin04": 4,
"pin05": 3,
"pin06": 5,
"pin07": 6,
"pin08": 12,
"pin09": 11,
"pin10": 7,
"pin11": 8,
"pin12": 10,
"pin13": 9,
"pin14": -10,
"pin15": -10,
"pin16": 13,
"pin17": 14,
"pin18": 17,
"pin19": 18,
"pin20": 15,
"pin21": 16,
"pin22": -10,
"pin23": -10,
"pin24": -10,
"pin25": -10,
"pin26": -10,
"pin27": -10,
"pin28": -10,
"pin29": -10
}
}

View File

@@ -143,4 +143,5 @@ export default {
'pspassthrough-d-plus-label': 'D+',
'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.',
};

View File

@@ -1,5 +1,7 @@
export default {
'capture-button-button-label': '🎮',
export default {
'capture-button-button-label': 'Map buttons with 🎮',
'capture-button-modal-title': 'Waiting for button press',
'capture-button-modal-content': 'Press ESC to skip this button.',
'capture-button-modal-content': 'Press a button on the gamepad.',
'capture-button-modal-skip': 'Skip Button',
'capture-button-modal-stop': 'Stop Capture',
};

View File

@@ -12,4 +12,40 @@ export default {
used: '{{pin}} is already assigned to another feature',
},
'all-capture-button-label': 'Assign Gamepad Pins\u00A0\u00A0🎮',
actions: {
NONE: 'None',
RESERVED: 'Reserved',
ASSIGNED_TO_ADDON: 'Assigned to addon',
BUTTON_PRESS_UP: 'Up',
BUTTON_PRESS_DOWN: 'Down',
BUTTON_PRESS_LEFT: 'Left',
BUTTON_PRESS_RIGHT: 'Right',
BUTTON_PRESS_B1: 'B1',
BUTTON_PRESS_B2: 'B2',
BUTTON_PRESS_B3: 'B3',
BUTTON_PRESS_B4: 'B4',
BUTTON_PRESS_L1: 'L1',
BUTTON_PRESS_R1: 'R1',
BUTTON_PRESS_L2: 'L2',
BUTTON_PRESS_R2: 'R2',
BUTTON_PRESS_S1: 'S1',
BUTTON_PRESS_S2: 'S2',
BUTTON_PRESS_A1: 'A1',
BUTTON_PRESS_A2: 'A2',
BUTTON_PRESS_L3: 'L3',
BUTTON_PRESS_R3: 'R3',
BUTTON_PRESS_FN: 'Function',
BUTTON_PRESS_DDI_UP: 'DDI Up',
BUTTON_PRESS_DDI_DOWN: 'DDI Down',
BUTTON_PRESS_DDI_LEFT: 'DDI Left',
BUTTON_PRESS_DDI_RIGHT: 'DDI Right',
SUSTAIN_DP_MODE_DP: 'D-Pad Mode: D-Pad',
SUSTAIN_DP_MODE_LS: 'D-Pad Mode: Left Stick',
SUSTAIN_DP_MODE_RS: 'D-Pad Mode: Right Stick',
SUSTAIN_SOCD_MODE_UP_PRIO: 'Up Priority SOCD Cleaning',
SUSTAIN_SOCD_MODE_NEUTRAL: 'Neutral SOCD Cleaning',
SUSTAIN_SOCD_MODE_SECOND_WIN: 'Last Win SOCD Cleaning',
SUSTAIN_SOCD_MODE_FIRST_WIN: 'First Win SOCD Cleaning',
SUSTAIN_SOCD_MODE_BYPASS: 'SOCD Cleaning Off',
},
};

View File

@@ -17,10 +17,6 @@ import DualDirection, {
dualDirectionScheme,
dualDirectionState,
} from '../Addons/DualDirection';
import ExtraButton, {
extraButtonScheme,
extraButtonState,
} from '../Addons/ExtraButton';
import I2c, { i2cScheme, i2cState } from '../Addons/I2c';
import Joystick, { joystickScheme, joystickState } from '../Addons/Joystick';
import OnBoardLed, {
@@ -59,7 +55,6 @@ const schema = yup.object().shape({
...dualDirectionScheme,
...tiltScheme,
...buzzerScheme,
...extraButtonScheme,
...playerNumberScheme,
...socdScheme,
...ps4Scheme,
@@ -80,7 +75,6 @@ const defaultValues = {
...dualDirectionState,
...tiltState,
...buzzerState,
...extraButtonState,
...playerNumberState,
...socdState,
...ps4State,
@@ -102,7 +96,6 @@ const ADDONS = [
DualDirection,
Tilt,
Buzzer,
ExtraButton,
PlayerNumber,
SOCD,
Ps4,

View File

@@ -1,277 +1,130 @@
import React, { useContext, useEffect, useState, useRef } from 'react';
import React, { useContext, useEffect, useState } from 'react';
import Select from 'react-select';
import { NavLink } from 'react-router-dom';
import { Button, Form } from 'react-bootstrap';
import { Alert, Button, Form } from 'react-bootstrap';
import { Trans, useTranslation } from 'react-i18next';
import invert from 'lodash/invert';
import map from 'lodash/map';
import omit from 'lodash/omit';
import { AppContext } from '../Contexts/AppContext';
import Section from '../Components/Section';
import CaptureButton from '../Components/CaptureButton';
import WebApi, { baseButtonMappings } from '../Services/WebApi';
import boards from '../Data/Boards.json';
import { getButtonLabels} from '../Data/Buttons';
import './PinMappings.scss';
import { Trans, useTranslation } from 'react-i18next';
import usePinStore, {
BUTTON_ACTIONS,
NON_SELECTABLE_BUTTON_ACTIONS,
} from '../Store/usePinStore';
const requiredButtons = ['S2'];
const errorType = {
required: 'errors.required',
conflict: 'errors.conflict',
invalid: 'errors.invalid',
used: 'errors.used',
};
import './PinMappings.scss';
import CaptureButton from '../Components/CaptureButton';
import { getButtonLabels } from '../Data/Buttons';
const isNonSelectable = (value) =>
NON_SELECTABLE_BUTTON_ACTIONS.includes(value);
const options = Object.entries(BUTTON_ACTIONS)
.filter(([_, value]) => !isNonSelectable(value))
.map(([key, value]) => ({
label: key,
value,
}));
const getOption = (actionId) => ({
label: invert(BUTTON_ACTIONS)[actionId],
value: actionId,
});
export default function PinMappingPage() {
const { buttonLabels, setButtonLabels, usedPins, updateUsedPins } =
useContext(AppContext);
const [validated, setValidated] = useState(false);
const { pins, setPinAction, fetchPins, savePins } = usePinStore();
const { buttonLabels, updateUsedPins } = useContext(AppContext);
const [saveMessage, setSaveMessage] = useState('');
const [buttonMappings, setButtonMappings] = useState(baseButtonMappings);
const [selectedController] = useState(import.meta.env.VITE_GP2040_CONTROLLER);
const [selectedBoard] = useState(import.meta.env.VITE_GP2040_BOARD);
const [capturingButtonIndex, setCapturingButtonIndex] = useState(-1);
const [isCapturingPinsInProgress, setIsCapturingPinsInProgress] = useState(false);
const controller = useRef();
const { buttonLabelType, swapTpShareLabels } = buttonLabels;
const CURRENT_BUTTONS = getButtonLabels(buttonLabelType, swapTpShareLabels)
const { setLoading } = useContext(AppContext);
const buttonNames = Object.keys(CURRENT_BUTTONS)?.filter((p) => p !== 'label' && p !== 'value');
const CURRENT_BUTTONS = getButtonLabels(buttonLabelType, swapTpShareLabels);
const buttonNames = omit(CURRENT_BUTTONS, ['label', 'value']);
const { t } = useTranslation('');
const translatedErrorType = Object.keys(errorType).reduce(
(a, k) => ({ ...a, [k]: t(`PinMapping:${errorType[k]}`) }),
{},
);
// Todo move all required fetching above navigation to preload
useEffect(() => {
async function fetchData() {
setButtonMappings(await WebApi.getPinMappings(setLoading));
const options = await WebApi.getGamepadOptions(setLoading);
setButtonLabels({
swapTpShareLabels:
options.switchTpShareForDs4 && options.inputMode === 4,
});
}
fetchData();
}, [setButtonMappings, selectedController]);
const handlePinChange = (e, prop, unassignOthersOnConflict) => {
const newMappings = { ...buttonMappings };
if (e.target.value) newMappings[prop].pin = parseInt(e.target.value);
else newMappings[prop].pin = '';
validateMappings(newMappings, unassignOthersOnConflict, prop);
};
fetchPins();
}, []);
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
let mappings = { ...buttonMappings };
validateMappings(mappings);
if (Object.keys(mappings).filter((p) => mappings[p].error).length > 0) {
setSaveMessage(t('Common:errors.validation-error'));
return;
try {
await savePins();
updateUsedPins();
setSaveMessage(t('Common:saved-success-message'));
} catch (error) {
setSaveMessage(t('Common:saved-error-message'));
}
const success = await WebApi.setPinMappings(mappings);
if (success) updateUsedPins();
setSaveMessage(
success
? t('Common:saved-success-message')
: t('Common:saved-error-message'),
);
};
const validateMappings = (mappings, unassignOthersOnConflict, targetButton) => {
const buttons = Object.keys(mappings);
// Create some mapped pin groups for easier error checking
const mappedPins = buttons
.filter((p) => mappings[p].pin > -1)
.reduce((a, p) => {
a.push(mappings[p].pin);
return a;
}, []);
const mappedPinCounts = mappedPins.reduce(
(a, p) => ({ ...a, [p]: (a[p] || 0) + 1 }),
{},
);
const uniquePins = mappedPins.filter((p, i, a) => a.indexOf(p) === i);
const conflictedPins = Object.keys(mappedPinCounts)
.filter((p) => mappedPinCounts[p] > 1)
.map(parseInt);
const invalidPins = uniquePins.filter(
(p) => boards[selectedBoard].invalidPins.indexOf(p) > -1,
);
const otherPins = usedPins.filter((p) => uniquePins.indexOf(p) === -1);
for (let button of buttons) {
mappings[button].error = '';
// Validate required button
if (
(mappings[button].pin < boards[selectedBoard].minPin ||
mappings[button].pin > boards[selectedBoard].maxPin) &&
requiredButtons.filter((b) => b === button).length
)
mappings[button].error = translatedErrorType.required;
// Identify conflicted pins
else if (conflictedPins.indexOf(mappings[button].pin) > -1)
if (unassignOthersOnConflict && targetButton !== button) mappings[button].pin = -1;
else if (!unassignOthersOnConflict) mappings[button].error = translatedErrorType.conflict;
// Identify invalid pin assignments
else if (invalidPins.indexOf(mappings[button].pin) > -1)
mappings[button].error = translatedErrorType.invalid;
// Identify used pins
else if (otherPins.indexOf(mappings[button].pin) > -1)
mappings[button].error = translatedErrorType.used;
}
setButtonMappings(mappings);
setValidated(true);
};
const renderError = (button) => {
if (buttonMappings[button].error === translatedErrorType.required) {
return (
<span key="required" className="error-message">
{t('PinMapping:errors.required', {
button: CURRENT_BUTTONS[button],
})}
</span>
);
} else if (buttonMappings[button].error === translatedErrorType.conflict) {
const conflictedMappings = Object.keys(buttonMappings)
.filter((b) => b !== button)
.filter((b) => buttonMappings[b].pin === buttonMappings[button].pin)
.map((b) => CURRENT_BUTTONS[b]);
return (
<span key="conflict" className="error-message">
{t('PinMapping:errors.conflict', {
pin: buttonMappings[button].pin,
conflictedMappings: conflictedMappings.join(', '),
})}
</span>
);
} else if (buttonMappings[button].error === translatedErrorType.invalid) {
console.log(buttonMappings[button].pin);
return (
<span key="invalid" className="error-message">
{t('PinMapping:errors.invalid', {
pin: buttonMappings[button].pin,
})}
</span>
);
} else if (buttonMappings[button].error === translatedErrorType.used) {
return (
<span key="used" className="error-message">
{t('PinMapping:errors.used', {
pin: buttonMappings[button].pin,
})}
</span>
);
}
return <></>;
};
const setAllButtonsPins = () => {
setIsCapturingPinsInProgress(true);
setCapturingButtonIndex(0);
}
const onTriggeredCaptureComplete = () => {
if (!isCapturingPinsInProgress) return;
if (capturingButtonIndex + 1 < buttonNames.length)
setCapturingButtonIndex(capturingButtonIndex + 1);
else {
setIsCapturingPinsInProgress(false);
setCapturingButtonIndex(-1);
}
}
const onStopCaptureSequence = () => {
if (!isCapturingPinsInProgress) return;
setCapturingButtonIndex(-1);
setIsCapturingPinsInProgress(false);
};
return (
<Section title={t('PinMapping:header-text')}>
<Form noValidate validated={validated} onSubmit={handleSubmit}>
<Form onSubmit={handleSubmit}>
<p>{t('PinMapping:sub-header-text')}</p>
{/* <div className="alert alert-warning">
Mapping buttons to pins that aren't connected or available can leave the device in non-functional state. To clear the
the invalid configuration go to the <NavLink exact="true" to="/reset-settings">Reset Settings</NavLink> page.
</div> */}
<div className="alert alert-warning">
<Trans ns="PinMapping" i18nKey="alert-text">
Mapping buttons to pins that aren&apos;t connected or available can
leave the device in non-functional state. To clear the the invalid
configuration go to the{' '}
<NavLink exact="true" to="/reset-settings">
Reset Settings
</NavLink>{' '}
page.
<NavLink to="/reset-settings">Reset Settings</NavLink> page.
</Trans>
</div>
<Button className="mb-3"
onClick={setAllButtonsPins}>
{t("PinMapping:all-capture-button-label")}
<CaptureButton
labels={Object.values(buttonNames)}
onChange={(label, pin) =>
setPinAction(
// Convert getHeldPins format to setPinMappings format
parseInt(pin) < 10 ? `pin0${pin}` : `pin${pin}`,
// Maps current mode buttons to actions
BUTTON_ACTIONS[
`BUTTON_PRESS_${invert(buttonNames)[label].toUpperCase()}`
],
)
}
/>
<div className="row row-cols-lg-3 row-cols-md-2 gx-3">
{map(pins, (pinAction, pin) => (
<div
key={`pin-${pin}`}
className="d-flex justify-content-center py-2"
>
<div className="d-flex align-items-center pe-2">
<label htmlFor={pin}>{pin.toUpperCase()}</label>
</div>
<Select
inputId={pin}
className="flex-grow-1 text-primary"
isClearable
isSearchable
options={options}
value={getOption(pinAction)}
isDisabled={isNonSelectable(pinAction)}
getOptionLabel={(option) => {
const labelKey = option.label.split('BUTTON_PRESS_').pop();
// Need to fallback as some button actions are not part of button names
return (
buttonNames[labelKey] ||
t(`PinMapping:actions.${option.label}`)
);
}}
onChange={(change) =>
setPinAction(
pin,
change?.value === undefined ? -10 : change.value, // On clear set to -10
)
}
/>
</div>
))}
</div>
<Button type="submit" className="my-4">
{t('Common:button-save-label')}
</Button>
<table className="table table-sm pin-mapping-table">
<thead className="table">
<tr>
<th className="table-header-button-label">
{CURRENT_BUTTONS.label}
</th>
<th>{t('PinMapping:pin-header-label')}</th>
</tr>
</thead>
<tbody>
{buttonNames.map((button, i) => {
let label = CURRENT_BUTTONS[button];
return (
<tr
key={`button-map-${i}`}
className={
validated && !!buttonMappings[button].error
? 'table-danger'
: ''
}
>
<td>{label}</td>
<td>
<Form.Control
type="number"
className="pin-input form-control-sm"
value={buttonMappings[button].pin}
min={-1}
max={boards[selectedBoard].maxPin}
isInvalid={buttonMappings[button].error}
onChange={(e) => handlePinChange(e, button)}
></Form.Control>
<CaptureButton
onChange={(pin) =>
handlePinChange({ target: { value: pin } }, button, true)}
abortRef={controller}
triggerCapture={button === buttonNames[capturingButtonIndex]}
buttonName={label}
onTriggeredCaptureComplete={onTriggeredCaptureComplete}
onStopCaptureSequence={onStopCaptureSequence}
/>
<Form.Control.Feedback type="invalid">
{renderError(button)}
</Form.Control.Feedback>
</td>
</tr>
);
})}
</tbody>
</table>
<Button type="submit">{t('Common:button-save-label')}</Button>
{saveMessage ? <span className="alert">{saveMessage}</span> : null}
{saveMessage && <Alert variant="secondary">{saveMessage}</Alert>}
</Form>
</Section>
);

View File

@@ -1,16 +1,24 @@
import React, { useContext, useEffect, useState } from 'react';
import { NavLink } from 'react-router-dom';
import { Button, Form } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
import reduce from 'lodash/reduce';
import invert from 'lodash/invert';
import { AppContext } from '../Contexts/AppContext';
import Section from '../Components/Section';
import WebApi, {
baseProfileOptions,
baseButtonMappings,
baseUrl,
} from '../Services/WebApi';
import boards from '../Data/Boards.json';
import { BUTTONS } from '../Data/Buttons';
import './PinMappings.scss';
import { Trans, useTranslation } from 'react-i18next';
import axios from 'axios';
import { BUTTON_ACTIONS } from '../Store/usePinStore';
const selectedBoard = import.meta.env.VITE_GP2040_BOARD;
const requiredButtons = ['S2'];
const errorType = {
@@ -21,15 +29,18 @@ const errorType = {
};
export default function ProfileOptionsPage() {
const { buttonLabels, setButtonLabels, usedPins, updateUsedPins } =
useContext(AppContext);
const {
setLoading,
buttonLabels,
setButtonLabels,
usedPins,
updateUsedPins,
} = useContext(AppContext);
const [validated, setValidated] = useState(false);
const [saveMessage, setSaveMessage] = useState('');
const [buttonMappings, setButtonMappings] = useState(baseButtonMappings);
const [profileOptions, setProfileOptions] = useState(baseProfileOptions);
const [selectedBoard] = useState(import.meta.env.VITE_GP2040_BOARD);
const { buttonLabelType } = buttonLabels;
const { setLoading } = useContext(AppContext);
const { t } = useTranslation('');
@@ -40,7 +51,39 @@ export default function ProfileOptionsPage() {
useEffect(() => {
async function fetchData() {
setButtonMappings(await WebApi.getPinMappings(setLoading));
const pinMappings = await axios
.get(`${baseUrl}/api/getPinMappings`)
.then(({ data }) => data);
/*
Converts pinMapping format
{pin01: 2} -> {'down': 1}
*/
const pinActions = reduce(
pinMappings,
(acc, value, key) => ({
...acc,
[invert(BUTTON_ACTIONS)
[value].split('BUTTON_PRESS_')
.pop()
.toLowerCase()]: parseInt(key.split('pin').pop()),
}),
{},
);
/*
Map buttonMappings with pinActions
Down: { pin: -1, ...} -> Down: { pin: 1, ...}
*/
const mergedButtonActions = Object.entries(buttonMappings).reduce(
(acc, [key, value]) => ({
...acc,
[key]: { ...value, pin: pinActions[key.toLowerCase()] || -1 },
}),
{},
);
setButtonMappings(mergedButtonActions);
setProfileOptions(await WebApi.getProfileOptions(setLoading));
setButtonLabels({});
}
@@ -252,9 +295,6 @@ export default function ProfileOptionsPage() {
</tr>
</thead>
<tbody>
{console.log(
Object.keys(profileOptions['alternativePinMappings'][0]),
)}
{Object.keys(profileOptions['alternativePinMappings'][0]).map(
(key) => (
<tr key={key}>

View File

@@ -1,7 +1,7 @@
import axios from 'axios';
import { hexToInt, rgbIntToHex } from './Utilities';
const baseUrl =
export const baseUrl =
process.env.NODE_ENV === 'production'
? ''
: import.meta.env.VITE_DEV_BASE_URL;
@@ -76,72 +76,72 @@ export const baseProfileOptions = {
};
export const baseWiiControls = {
"nunchuk.analogStick.axisType": 1,
"nunchuk.buttonC": 1,
"nunchuk.buttonZ": 2,
"classic.analogLeftStick.x.axisType": 1,
"classic.analogLeftStick.y.axisType": 2,
"classic.analogRightStick.x.axisType": 3,
"classic.analogRightStick.y.axisType": 4,
"classic.analogLeftTrigger.axisType": 7,
"classic.analogRightTrigger.axisType": 8,
"classic.buttonA": 2,
"classic.buttonB": 1,
"classic.buttonX": 8,
"classic.buttonY": 4,
"classic.buttonL": 64,
"classic.buttonR": 128,
"classic.buttonZL": 16,
"classic.buttonZR": 32,
"classic.buttonMinus": 256,
"classic.buttonHome": 4096,
"classic.buttonPlus": 512,
"classic.buttonUp": 65536,
"classic.buttonDown": 131072,
"classic.buttonLeft": 262144,
"classic.buttonRight": 524288,
"guitar.analogStick.x.axisType": 1,
"guitar.analogStick.y.axisType": 2,
"guitar.analogWhammyBar.axisType": 14,
"guitar.buttonOrange": 64,
"guitar.buttonRed": 2,
"guitar.buttonBlue": 4,
"guitar.buttonGreen": 1,
"guitar.buttonYellow": 8,
"guitar.buttonPedal": 128,
"guitar.buttonMinus": 256,
"guitar.buttonPlus": 512,
"guitar.buttonStrumUp": 65536,
"guitar.buttonStrumDown": 131072,
"drum.analogStick.x.axisType": 1,
"drum.analogStick.y.axisType": 2,
"drum.buttonOrange": 64,
"drum.buttonRed": 2,
"drum.buttonBlue": 8,
"drum.buttonGreen": 1,
"drum.buttonYellow": 4,
"drum.buttonPedal": 128,
"drum.buttonMinus": 256,
"drum.buttonPlus": 512,
"turntable.analogStick.x.axisType": 1,
"turntable.analogStick.y.axisType": 2,
"turntable.analogLeftTurntable.axisType": 13,
"turntable.analogRightTurntable.axisType": 15,
"turntable.analogFader.axisType": 7,
"turntable.analogEffects.axisType": 8,
"turntable.buttonLeftGreen": 262144,
"turntable.buttonLeftRed": 65536,
"turntable.buttonLeftBlue": 524288,
"turntable.buttonRightGreen": 4,
"turntable.buttonRightRed": 8,
"turntable.buttonRightBlue": 2,
"turntable.buttonEuphoria": 32,
"turntable.buttonMinus": 256,
"turntable.buttonPlus": 512,
"taiko.buttonDonLeft": 262144,
"taiko.buttonKatLeft": 64,
"taiko.buttonDonRight": 1,
"taiko.buttonKatRight": 128,
'nunchuk.analogStick.axisType': 1,
'nunchuk.buttonC': 1,
'nunchuk.buttonZ': 2,
'classic.analogLeftStick.x.axisType': 1,
'classic.analogLeftStick.y.axisType': 2,
'classic.analogRightStick.x.axisType': 3,
'classic.analogRightStick.y.axisType': 4,
'classic.analogLeftTrigger.axisType': 7,
'classic.analogRightTrigger.axisType': 8,
'classic.buttonA': 2,
'classic.buttonB': 1,
'classic.buttonX': 8,
'classic.buttonY': 4,
'classic.buttonL': 64,
'classic.buttonR': 128,
'classic.buttonZL': 16,
'classic.buttonZR': 32,
'classic.buttonMinus': 256,
'classic.buttonHome': 4096,
'classic.buttonPlus': 512,
'classic.buttonUp': 65536,
'classic.buttonDown': 131072,
'classic.buttonLeft': 262144,
'classic.buttonRight': 524288,
'guitar.analogStick.x.axisType': 1,
'guitar.analogStick.y.axisType': 2,
'guitar.analogWhammyBar.axisType': 14,
'guitar.buttonOrange': 64,
'guitar.buttonRed': 2,
'guitar.buttonBlue': 4,
'guitar.buttonGreen': 1,
'guitar.buttonYellow': 8,
'guitar.buttonPedal': 128,
'guitar.buttonMinus': 256,
'guitar.buttonPlus': 512,
'guitar.buttonStrumUp': 65536,
'guitar.buttonStrumDown': 131072,
'drum.analogStick.x.axisType': 1,
'drum.analogStick.y.axisType': 2,
'drum.buttonOrange': 64,
'drum.buttonRed': 2,
'drum.buttonBlue': 8,
'drum.buttonGreen': 1,
'drum.buttonYellow': 4,
'drum.buttonPedal': 128,
'drum.buttonMinus': 256,
'drum.buttonPlus': 512,
'turntable.analogStick.x.axisType': 1,
'turntable.analogStick.y.axisType': 2,
'turntable.analogLeftTurntable.axisType': 13,
'turntable.analogRightTurntable.axisType': 15,
'turntable.analogFader.axisType': 7,
'turntable.analogEffects.axisType': 8,
'turntable.buttonLeftGreen': 262144,
'turntable.buttonLeftRed': 65536,
'turntable.buttonLeftBlue': 524288,
'turntable.buttonRightGreen': 4,
'turntable.buttonRightRed': 8,
'turntable.buttonRightBlue': 2,
'turntable.buttonEuphoria': 32,
'turntable.buttonMinus': 256,
'turntable.buttonPlus': 512,
'taiko.buttonDonLeft': 262144,
'taiko.buttonKatLeft': 64,
'taiko.buttonDonRight': 1,
'taiko.buttonKatRight': 128,
};
async function resetSettings() {
@@ -339,22 +339,6 @@ async function setCustomTheme(customThemeOptions) {
});
}
async function getPinMappings(setLoading) {
setLoading(true);
try {
const response = await axios.get(`${baseUrl}/api/getPinMappings`);
let mappings = { ...baseButtonMappings };
for (let prop of Object.keys(response.data))
mappings[prop].pin = parseInt(response.data[prop]);
return mappings;
} catch (error) {
console.error(error);
return false;
}
}
async function setPinMappings(mappings) {
let data = {};
Object.keys(mappings).map(
@@ -536,7 +520,7 @@ async function setPS4Options(options) {
async function getWiiControls(setLoading) {
setLoading(true);
try {
try {
const response = await axios.get(`${baseUrl}/api/getWiiControls`);
setLoading(false);
@@ -549,7 +533,7 @@ async function getWiiControls(setLoading) {
}
async function setWiiControls(mappings) {
console.dir(mappings);
console.dir(mappings);
return axios
.post(`${baseUrl}/api/setWiiControls`, sanitizeRequest(mappings))
@@ -602,17 +586,13 @@ async function getUsedPins(setLoading) {
}
}
async function getHeldPins(setLoading, createAbortSignal) {
setLoading && setLoading(true);
async function getHeldPins(abortSignal) {
try {
const response = await axios.get(`${baseUrl}/api/getHeldPins`, {
signal: createAbortSignal(),
signal: abortSignal,
});
setLoading && setLoading(false);
return response.data;
} catch (error) {
setLoading && setLoading(false);
if (error?.code === 'ERR_CANCELED') return { canceled: true };
else console.error(error);
}
@@ -653,7 +633,6 @@ const WebApi = {
setLedOptions,
getCustomTheme,
setCustomTheme,
getPinMappings,
setPinMappings,
getProfileOptions,
setProfileOptions,

View File

@@ -0,0 +1,111 @@
import axios from 'axios';
import { create } from 'zustand';
import { baseUrl } from '../Services/WebApi';
// Hide from select options / Disable select if returned from board
export const NON_SELECTABLE_BUTTON_ACTIONS = [-5, 0];
// These could theoretically be created from enums.proto
export const BUTTON_ACTIONS = {
NONE: -10,
RESERVED: -5,
ASSIGNED_TO_ADDON: 0,
BUTTON_PRESS_UP: 1,
BUTTON_PRESS_DOWN: 2,
BUTTON_PRESS_LEFT: 3,
BUTTON_PRESS_RIGHT: 4,
BUTTON_PRESS_B1: 5,
BUTTON_PRESS_B2: 6,
BUTTON_PRESS_B3: 7,
BUTTON_PRESS_B4: 8,
BUTTON_PRESS_L1: 9,
BUTTON_PRESS_R1: 10,
BUTTON_PRESS_L2: 11,
BUTTON_PRESS_R2: 12,
BUTTON_PRESS_S1: 13,
BUTTON_PRESS_S2: 14,
BUTTON_PRESS_A1: 15,
BUTTON_PRESS_A2: 16,
BUTTON_PRESS_L3: 17,
BUTTON_PRESS_R3: 18,
BUTTON_PRESS_FN: 19,
BUTTON_PRESS_DDI_UP: 20,
BUTTON_PRESS_DDI_DOWN: 21,
BUTTON_PRESS_DDI_LEFT: 22,
BUTTON_PRESS_DDI_RIGHT: 23,
SUSTAIN_DP_MODE_DP: 24,
SUSTAIN_DP_MODE_LS: 25,
SUSTAIN_DP_MODE_RS: 26,
SUSTAIN_SOCD_MODE_UP_PRIO: 27,
SUSTAIN_SOCD_MODE_NEUTRAL: 28,
SUSTAIN_SOCD_MODE_SECOND_WIN: 29,
SUSTAIN_SOCD_MODE_FIRST_WIN: 30,
SUSTAIN_SOCD_MODE_BYPASS: 31,
} as const;
type PinActionKeys = keyof typeof BUTTON_ACTIONS;
type PinActionValues = (typeof BUTTON_ACTIONS)[PinActionKeys];
type State = {
pins: { [key: string]: PinActionValues };
};
type Actions = {
fetchPins: () => void;
setPinAction: (pin: string, action: PinActionValues) => void;
savePins: () => Promise<{}>;
};
const INITIAL_STATE: State = {
pins: {
pin00: -10,
pin01: -10,
pin02: -10,
pin03: -10,
pin04: -10,
pin05: -10,
pin06: -10,
pin07: -10,
pin08: -10,
pin09: -10,
pin10: -10,
pin11: -10,
pin12: -10,
pin13: -10,
pin14: -10,
pin15: -10,
pin16: -10,
pin17: -10,
pin18: -10,
pin19: -10,
pin20: -10,
pin21: -10,
pin22: -10,
pin23: -10,
pin24: -10,
pin25: -10,
pin26: -10,
pin27: -10,
pin28: -10,
pin29: -10,
},
};
const usePinStore = create<State & Actions>()((set, get) => ({
...INITIAL_STATE,
fetchPins: async () => {
const { data } = await axios.get(`${baseUrl}/api/getPinMappings`);
set((state) => ({
...state,
pins: { ...state.pins, ...data }, // Merge saved pins with defaults
}));
},
setPinAction: (pin, action) =>
set((state) => ({
...state,
pins: { ...state.pins, [pin]: action },
})),
savePins: async () => axios.post(`${baseUrl}/api/setPinMappings`, get().pins),
}));
export default usePinStore;