Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ endif()
if(DEFINED ENV{GP2040_BOARDCONFIG})
set(GP2040_BOARDCONFIG $ENV{GP2040_BOARDCONFIG})
elseif(NOT DEFINED GP2040_BOARDCONFIG)
set(GP2040_BOARDCONFIG Pico)
set(GP2040_BOARDCONFIG Haute42COSMOX)
endif()

# check for existing cmake files in the event of changing PICO_BOARD or PICO_PLATFORM
Expand Down
3 changes: 1 addition & 2 deletions headers/display/GPGFX_UI.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ class GPGFX_UI {
GPGFX* getRenderer() { return _renderer; }
Gamepad* getGamepad();
Gamepad* getProcessedGamepad();
DisplayOptions getDisplayOptions();
const DisplayOptions& getDisplayOptions();
private:
GPGFX* _renderer;
DisplayOptions _displayOptions{};
bool _configMode = false;
};

Expand Down
7 changes: 7 additions & 0 deletions headers/layoutmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ typedef struct {

#define LAYOUTMGR LayoutManager::getInstance()

// Packed custom layout element: 12 little-endian uint16 values
#define CUSTOM_LAYOUT_ELEMENT_BYTES 24

class LayoutManager {
public:
typedef std::vector<GPButtonLayout> LayoutList;
Expand Down Expand Up @@ -103,6 +106,8 @@ class LayoutManager {
LayoutList drawSticklessButtons14B();
LayoutList drawButtonLayoutLeft();
LayoutList drawButtonLayoutRight();
LayoutList drawCustomDefinedA();
LayoutList drawCustomDefinedB();
LayoutList drawBoardDefinedA();
LayoutList drawBoardDefinedAlt0A();
LayoutList drawBoardDefinedAlt1A();
Expand Down Expand Up @@ -156,6 +161,8 @@ class LayoutManager {
private:
LayoutManager(){}

LayoutList drawCustomDefined(const uint8_t* bytes, uint16_t size);

std::string getLayoutNameByID();
};

Expand Down
7 changes: 4 additions & 3 deletions pico_sdk_import.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ if (NOT PICO_SDK_PATH)
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
endif ()
FetchContent_Declare(
pico_sdk
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
)

if (NOT pico_sdk)
Expand Down Expand Up @@ -95,6 +95,7 @@ if (NOT PICO_SDK_PATH)
SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild
)
endif ()

set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
endif ()
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
Expand Down
6 changes: 6 additions & 0 deletions proto/config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ message ButtonLayoutCustomOptions
optional ButtonLayoutParamsRight paramsRight = 2;
}


message PinMappings
{
optional int32 pinDpadUp = 1;
Expand Down Expand Up @@ -285,6 +286,11 @@ message DisplayOptions
optional uint32 inputHistoryRow = 30;

optional uint32 contrast = 31;

// Packed custom layouts: 24 bytes per element,
// 12 little-endian uint16 values: elementType, x1, y1, x2, y2, stroke, fill, value, shape, angleStart, angleEnd, closed
optional bytes customLayoutA = 32 [(nanopb).max_size = 2400];
optional bytes customLayoutB = 33 [(nanopb).max_size = 2400];
}

message LEDOptions
Expand Down
2 changes: 2 additions & 0 deletions proto/enums.proto
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ enum ButtonLayout
BUTTON_LAYOUT_BOARD_DEFINED_ALT5_A = 39;
BUTTON_LAYOUT_BOARD_DEFINED_ALT6_A = 40;
BUTTON_LAYOUT_BOARD_DEFINED_ALT7_A = 41;
BUTTON_LAYOUT_CUSTOM_DEFINED_A = 42;
}

enum ButtonLayoutRight
Expand Down Expand Up @@ -102,6 +103,7 @@ enum ButtonLayoutRight
BUTTON_LAYOUT_BOARD_DEFINED_ALT5_B = 45;
BUTTON_LAYOUT_BOARD_DEFINED_ALT6_B = 46;
BUTTON_LAYOUT_BOARD_DEFINED_ALT7_B = 47;
BUTTON_LAYOUT_CUSTOM_DEFINED_B = 48;
}

enum SplashMode
Expand Down
10 changes: 10 additions & 0 deletions src/config_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,16 @@ void ConfigUtils::initUnsetPropertiesWithDefaults(Config& config)
INIT_UNSET_PROPERTY(config.displayOptions, splashDuration, SPLASH_DURATION);
const unsigned char defaultSplash[] = { DEFAULT_SPLASH };
INIT_UNSET_PROPERTY_BYTES(config.displayOptions, splashImage, defaultSplash);

// custom display layouts default to empty
if (!config.displayOptions.has_customLayoutA) {
config.displayOptions.customLayoutA.size = 0;
config.displayOptions.has_customLayoutA = true;
}
if (!config.displayOptions.has_customLayoutB) {
config.displayOptions.customLayoutB.size = 0;
config.displayOptions.has_customLayoutB = true;
}
INIT_UNSET_PROPERTY(config.displayOptions, size, DISPLAY_SIZE);
INIT_UNSET_PROPERTY(config.displayOptions, flip, DISPLAY_FLIP);
INIT_UNSET_PROPERTY(config.displayOptions, invert, !!DISPLAY_INVERT);
Expand Down
2 changes: 1 addition & 1 deletion src/display/GPGFX_UI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ Gamepad* GPGFX_UI::getProcessedGamepad() {
return Storage::getInstance().GetProcessedGamepad();
}

DisplayOptions GPGFX_UI::getDisplayOptions() {
const DisplayOptions& GPGFX_UI::getDisplayOptions() {
return Storage::getInstance().getDisplayOptions();
}
37 changes: 36 additions & 1 deletion src/layoutmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,9 @@ LayoutManager::LayoutList LayoutManager::getLeftLayout(uint16_t index) {
case BUTTON_LAYOUT_BOARD_DEFINED_ALT6_A:
return drawBoardDefinedAlt6A();
case BUTTON_LAYOUT_BOARD_DEFINED_ALT7_A:
return drawBoardDefinedAlt7A();
return drawBoardDefinedAlt7A();
case BUTTON_LAYOUT_CUSTOM_DEFINED_A:
return drawCustomDefinedA();
default:
break;
}
Expand Down Expand Up @@ -276,13 +278,46 @@ LayoutManager::LayoutList LayoutManager::getRightLayout(uint16_t index) {
return this->drawBoardDefinedAlt6B();
case BUTTON_LAYOUT_BOARD_DEFINED_ALT7_B:
return this->drawBoardDefinedAlt7B();
case BUTTON_LAYOUT_CUSTOM_DEFINED_B:
return drawCustomDefinedB();
default:
break;
}

return {};
}

// Packed custom layouts: 24 bytes per element, 12 little-endian uint16 values:
// elementType, x1, y1, x2, y2, stroke, fill, value, shape, angleStart, angleEnd, closed
LayoutManager::LayoutList LayoutManager::drawCustomDefined(const uint8_t* bytes, uint16_t size) {
LayoutList layout;
const uint16_t elementSize = CUSTOM_LAYOUT_ELEMENT_BYTES;
uint16_t count = size / elementSize;
for (uint16_t i = 0; i < count; i++) {
const uint8_t* p = bytes + (i * elementSize);
uint16_t values[12];
for (uint8_t j = 0; j < 12; j++) {
values[j] = (uint16_t)p[j * 2] | ((uint16_t)p[(j * 2) + 1] << 8);
}
layout.push_back({(GPElement)values[0], {
values[1], values[2], values[3], values[4],
values[5], values[6], values[7], values[8],
values[9], values[10], values[11]
}});
}
return layout;
}

LayoutManager::LayoutList LayoutManager::drawCustomDefinedA() {
const DisplayOptions& options = Storage::getInstance().getDisplayOptions();
return drawCustomDefined(options.customLayoutA.bytes, options.customLayoutA.size);
}

LayoutManager::LayoutList LayoutManager::drawCustomDefinedB() {
const DisplayOptions& options = Storage::getInstance().getDisplayOptions();
return drawCustomDefined(options.customLayoutB.bytes, options.customLayoutB.size);
}

LayoutManager::LayoutList LayoutManager::drawButtonLayoutLeft()
{
const DisplayOptions& options = Storage::getInstance().getDisplayOptions();
Expand Down
36 changes: 33 additions & 3 deletions src/webconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,29 @@ std::string getUsedPins()
return serialize_json(doc);
}

// Custom display layouts are serialized as base64-encoded packed bytes,
// 24 bytes per element: 12 little-endian uint16 values
// (elementType, x1, y1, x2, y2, stroke, fill, value, shape, angleStart, angleEnd, closed)
static void __attribute__((noinline)) readCustomLayout(bool& hasLayout, pb_byte_t* bytes, pb_size_t& size, size_t maxSize, const DynamicJsonDocument& doc, const char* key)
{
if (!doc.containsKey(key))
return;
std::string decoded;
std::string base64String = doc[key];
if (!Base64::Decode(base64String, decoded))
return;
size_t length = std::min(decoded.length(), maxSize);
length -= length % CUSTOM_LAYOUT_ELEMENT_BYTES; // whole elements only
memcpy(bytes, decoded.data(), length);
size = length;
hasLayout = true;
}

static void __attribute__((noinline)) writeCustomLayout(const pb_byte_t* bytes, pb_size_t size, DynamicJsonDocument& doc, const char* key)
{
doc[key] = Base64::Encode(reinterpret_cast<const char*>(bytes), size);
}

std::string setDisplayOptions(DisplayOptions& displayOptions)
{
DynamicJsonDocument doc = get_post_data();
Expand Down Expand Up @@ -463,6 +486,9 @@ std::string setDisplayOptions(DisplayOptions& displayOptions)
readDoc(displayOptions.buttonLayoutCustomOptions.paramsRight.common.buttonRadius, doc, "buttonLayoutCustomOptions", "paramsRight", "buttonRadius");
readDoc(displayOptions.buttonLayoutCustomOptions.paramsRight.common.buttonPadding, doc, "buttonLayoutCustomOptions", "paramsRight", "buttonPadding");

readCustomLayout(displayOptions.has_customLayoutA, displayOptions.customLayoutA.bytes, displayOptions.customLayoutA.size, sizeof(displayOptions.customLayoutA.bytes), doc, "customLayoutA");
readCustomLayout(displayOptions.has_customLayoutB, displayOptions.customLayoutB.bytes, displayOptions.customLayoutB.size, sizeof(displayOptions.customLayoutB.bytes), doc, "customLayoutB");

return serialize_json(doc);
}

Expand All @@ -481,7 +507,8 @@ std::string setPreviewDisplayOptions()

std::string getDisplayOptions() // Manually set Document Attributes for the display
{
const size_t capacity = JSON_OBJECT_SIZE(100);
// base64-encoded custom layouts are up to ~3.2KB each
const size_t capacity = JSON_OBJECT_SIZE(100) + 7168;
DynamicJsonDocument doc(capacity);
const DisplayOptions& displayOptions = Storage::getInstance().getDisplayOptions();
writeDoc(doc, "enabled", displayOptions.enabled ? 1 : 0);
Expand Down Expand Up @@ -520,6 +547,9 @@ std::string getDisplayOptions() // Manually set Document Attributes for the disp
writeDoc(doc, "buttonLayoutCustomOptions", "paramsRight", "buttonRadius", displayOptions.buttonLayoutCustomOptions.paramsRight.common.buttonRadius);
writeDoc(doc, "buttonLayoutCustomOptions", "paramsRight", "buttonPadding", displayOptions.buttonLayoutCustomOptions.paramsRight.common.buttonPadding);

writeCustomLayout(displayOptions.customLayoutA.bytes, displayOptions.customLayoutA.size, doc, "customLayoutA");
writeCustomLayout(displayOptions.customLayoutB.bytes, displayOptions.customLayoutB.size, doc, "customLayoutB");

return serialize_json(doc);
}

Expand Down Expand Up @@ -921,12 +951,12 @@ std::string getButtonLayoutDefs()

for (layoutCtr = _ButtonLayout_MIN; layoutCtr < _ButtonLayout_ARRAYSIZE; layoutCtr++) {
LayoutManager::LayoutList leftLayout = LayoutManager::getInstance().getLeftLayout((ButtonLayout)layoutCtr);
if ((leftLayout.size() > 0) || (layoutCtr == ButtonLayout::BUTTON_LAYOUT_BLANKA)) writeDoc(doc, "buttonLayout", LayoutManager::getInstance().getButtonLayoutName((ButtonLayout)layoutCtr), layoutCtr);
if ((leftLayout.size() > 0) || (layoutCtr == ButtonLayout::BUTTON_LAYOUT_BLANKA) || (layoutCtr == ButtonLayout::BUTTON_LAYOUT_CUSTOM_DEFINED_A)) writeDoc(doc, "buttonLayout", LayoutManager::getInstance().getButtonLayoutName((ButtonLayout)layoutCtr), layoutCtr);
}

for (layoutCtr = _ButtonLayoutRight_MIN; layoutCtr < _ButtonLayoutRight_ARRAYSIZE; layoutCtr++) {
LayoutManager::LayoutList rightLayout = LayoutManager::getInstance().getRightLayout((ButtonLayoutRight)layoutCtr);
if ((rightLayout.size() > 0) || (layoutCtr == ButtonLayoutRight::BUTTON_LAYOUT_BLANKB)) writeDoc(doc, "buttonLayoutRight", LayoutManager::getInstance().getButtonLayoutRightName((ButtonLayoutRight)layoutCtr), layoutCtr);
if ((rightLayout.size() > 0) || (layoutCtr == ButtonLayoutRight::BUTTON_LAYOUT_BLANKB) || (layoutCtr == ButtonLayoutRight::BUTTON_LAYOUT_CUSTOM_DEFINED_B)) writeDoc(doc, "buttonLayoutRight", LayoutManager::getInstance().getButtonLayoutRightName((ButtonLayoutRight)layoutCtr), layoutCtr);
}

return serialize_json(doc);
Expand Down
4 changes: 4 additions & 0 deletions www/server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ app.get('/api/getDisplayOptions', (req, res) => {
inputHistoryCol: 0,
inputHistoryRow: 7,
displayContrast: 255,
customLayoutA: '',
customLayoutB: '',
};
console.log('data', data);
return res.send(data);
Expand Down Expand Up @@ -823,6 +825,7 @@ app.get('/api/getButtonLayoutDefs', (req, res) => {
BUTTON_LAYOUT_BOARD_DEFINED_ALT5_A: 39,
BUTTON_LAYOUT_BOARD_DEFINED_ALT6_A: 40,
BUTTON_LAYOUT_BOARD_DEFINED_ALT7_A: 41,
BUTTON_LAYOUT_CUSTOM_DEFINED_A: 42,
},
buttonLayoutRight: {
BUTTON_LAYOUT_ARCADE: 0,
Expand Down Expand Up @@ -873,6 +876,7 @@ app.get('/api/getButtonLayoutDefs', (req, res) => {
BUTTON_LAYOUT_BOARD_DEFINED_ALT5_B: 45,
BUTTON_LAYOUT_BOARD_DEFINED_ALT6_B: 46,
BUTTON_LAYOUT_BOARD_DEFINED_ALT7_B: 47,
BUTTON_LAYOUT_CUSTOM_DEFINED_B: 48,
},
});
});
Expand Down
9 changes: 9 additions & 0 deletions www/src/Locales/en/DisplayConfig.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default {
'layout-header': 'Layout Options',
'mode-header': 'Mode Options',
'button-layout-header': 'Button Layout',
'custom-layout-header': 'Custom Layouts (Advanced)',
'status-layout-header': 'Status Bar Layout',
'history-layout-header': 'Input History Layout',
},
Expand Down Expand Up @@ -37,6 +38,14 @@ export default {
'button-layout-custom-start-y-label': 'Start Y',
'button-layout-custom-button-radius-label': 'Button Radius',
'button-layout-custom-button-padding-label': 'Button Padding',
'custom-layouts-toggle': 'Custom Layouts',
'custom-layout-description':
'Paste button layout code below to define your own display layouts without compiling custom firmware. The code format is the same as board configurations and the layout builder, linked here:',
'custom-layout-link-text': 'GP2040-CE Layout Viewer',
'custom-layout-left-label': 'Custom Layout Code (Left)',
'custom-layout-right-label': 'Custom Layout Code (Right)',
'custom-layout-element-count': '{{count}} element(s) parsed',
'custom-layout-empty': 'No custom layout set',
'splash-mode-label': 'Splash Mode',
'splash-duration-label': 'Splash Duration (seconds, 0 for Always On)',
'display-saver-timeout-label': 'Display Saver Timeout (minutes, 0 for Off)',
Expand Down
2 changes: 2 additions & 0 deletions www/src/Locales/en/LayoutConfig.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export default {
BUTTON_LAYOUT_BOARD_DEFINED_ALT5_A: 'Board Defined Alternate 5',
BUTTON_LAYOUT_BOARD_DEFINED_ALT6_A: 'Board Defined Alternate 6',
BUTTON_LAYOUT_BOARD_DEFINED_ALT7_A: 'Board Defined Alternate 7',
BUTTON_LAYOUT_CUSTOM_DEFINED_A: 'Custom Defined',
},
right: {
BUTTON_LAYOUT_ARCADE: 'Arcade',
Expand Down Expand Up @@ -98,6 +99,7 @@ export default {
BUTTON_LAYOUT_BOARD_DEFINED_ALT5_B: 'Board Defined Alternate 5',
BUTTON_LAYOUT_BOARD_DEFINED_ALT6_B: 'Board Defined Alternate 6',
BUTTON_LAYOUT_BOARD_DEFINED_ALT7_B: 'Board Defined Alternate 7',
BUTTON_LAYOUT_CUSTOM_DEFINED_B: 'Custom Defined',
},
},
};
Loading