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
18 changes: 18 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,21 @@
path = Library/ArduinoJson
url = https://github.com/bblanchon/ArduinoJson.git
branch = 7.x
[submodule "Library/FreeRTOS-Kernel"]
path = Library/FreeRTOS-Kernel
url = https://github.com/FreeRTOS/FreeRTOS-Kernel.git
[submodule "Library/cmsis_device_f1"]
path = Library/cmsis_device_f1
url = https://github.com/STMicroelectronics/cmsis_device_f1.git
[submodule "Library/stm32f1xx_hal_driver"]
path = Library/stm32f1xx_hal_driver
url = https://github.com/STMicroelectronics/stm32f1xx_hal_driver.git
[submodule "Library/CMSIS_5"]
path = Library/CMSIS_5
url = https://github.com/ARM-software/CMSIS_5.git
[submodule "Platform/STM32F1/cmsis_device_f1"]
path = Platform/STM32F1/cmsis_device_f1
url = https://github.com/STMicroelectronics/cmsis_device_f1.git
[submodule "Platform/STM32F1/stm32f1xx_hal_driver"]
path = Platform/STM32F1/stm32f1xx_hal_driver
url = https://github.com/STMicroelectronics/stm32f1xx_hal_driver.git
49 changes: 32 additions & 17 deletions Applications/Application.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,31 +47,46 @@ class Application {
#define APPLICATION_HELPER_CLASS(CLASS) APPLICATION_HELPER_CLASS_IMPL(CLASS)

// Stores all the applications - Optimal for ID lookup
inline std::unordered_map<uint32_t, Application_Info*> applications;
// Using function-local static to ensure proper initialization order
inline std::unordered_map<uint32_t, Application_Info*>& GetApplications() {
static std::unordered_map<uint32_t, Application_Info*> applications;
return applications;
}

// Store all the application id in order of registration - Preserves order and is optimal for iteration
inline std::map<uint32_t, uint32_t> application_ids;
inline std::map<uint32_t, uint32_t>& GetApplicationIDs() {
static std::map<uint32_t, uint32_t> application_ids;
return application_ids;
}

template <typename APPLICATION_CLASS>
static inline void register_application(Application_Info info, uint32_t order, bool is_system) {
static inline void register_application(uint32_t order, bool is_system) {
APPLICATION_CLASS::info.is_system = is_system; // Set system flag
APPLICATION_CLASS::info.factory = []() -> Application* { \
return new APPLICATION_CLASS(); \
}; \
APPLICATION_CLASS::info.destructor = [](Application* app) { \
delete (APPLICATION_CLASS*)app; \
}; \
MLOGI("Application", "Registering application: %s%s", APPLICATION_CLASS::info.name.c_str(), is_system ? " (system)" : ""); \
uint32_t app_id = StringHash(APPLICATION_CLASS::info.author + '-' + APPLICATION_CLASS::info.name); \
if (applications.find(app_id) != applications.end()) { \
return; \
} \
applications.insert({app_id, &APPLICATION_CLASS::info}); \
application_ids[order] = app_id; \
APPLICATION_CLASS::info.factory = []() -> Application* {
void* mem = pvPortMalloc(sizeof(APPLICATION_CLASS));
if (mem == nullptr) {
return nullptr;
}
return new(mem) APPLICATION_CLASS(); // Placement new
};
APPLICATION_CLASS::info.destructor = [](Application* app) {
if (app != nullptr) {
app->~Application(); // Call destructor
vPortFree(app); // Free memory
}
};
MLOGI("Application", "Registering application: %s%s", APPLICATION_CLASS::info.name.c_str(), is_system ? " (system)" : "");
uint32_t app_id = StringHash(APPLICATION_CLASS::info.author + '-' + APPLICATION_CLASS::info.name);
auto& applications = GetApplications();
if (applications.find(app_id) != applications.end()) {
return;
}
applications.insert({app_id, &APPLICATION_CLASS::info});
GetApplicationIDs()[order] = app_id;
}


#define REGISTER_APPLICATION(APPLICATION_CLASS, IS_SYSTEM) \
__attribute__((__constructor__)) inline void APPLICATION_HELPER_CLASS(APPLICATION_CLASS)(void) { \
register_application<APPLICATION_CLASS>(APPLICATION_CLASS::info, __COUNTER__, IS_SYSTEM); \
register_application<APPLICATION_CLASS>(__COUNTER__, IS_SYSTEM); \
}
12 changes: 6 additions & 6 deletions Applications/Reversi/Reversi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ void Reversi::Loop()

uint8_t Reversi::Flip(Point pos, uint8_t currentPlayer, bool update)
{
uint8_t opponentPlayer = currentPlayer == 1 ? 2 : 1;
uint8_t opponentPlayer = (1 == currentPlayer) ? 2 : 1;
uint8_t flipped = 0;

// Check all 8 directions
Expand Down Expand Up @@ -548,7 +548,7 @@ bool Reversi::ResetGame(bool confirmed)

firstPlayer.Load(); // For some reason, the firstPlayer is not auto loaded correctly

uint8_t secondPlayer = (firstPlayer == 1) ? 2 : 1;
uint8_t secondPlayer = (1 == firstPlayer) ? 2 : 1;

board[3][3].player = firstPlayer;
board[3][3].wasPlayer = firstPlayer;
Expand Down Expand Up @@ -637,14 +637,14 @@ void Reversi::Settings() {

UIButton player1FirstHand;
player1FirstHand.SetName("Player 1 First Hand");
player1FirstHand.SetColorFunc([&]() -> Color { return Color::White.DimIfNot(firstPlayer == 1); });
player1FirstHand.OnPress([&]() -> void { if(firstPlayer == 1) {return;} if (gameState == Ended || !started || ConfirmMenu()) { firstPlayer = 1; ResetGame(true);} });
player1FirstHand.SetColorFunc([&]() -> Color { return Color::White.DimIfNot(1 == firstPlayer); });
player1FirstHand.OnPress([&]() -> void { if(1 == firstPlayer) {return;} if (gameState == Ended || !started || ConfirmMenu()) { firstPlayer = 1; ResetGame(true);} });
settingsUI.AddUIComponent(player1FirstHand, Point(7, 6));

UIButton player2FirstHand;
player2FirstHand.SetName("Player 2 First Hand");
player2FirstHand.SetColorFunc([&]() -> Color { return Color::White.DimIfNot(firstPlayer == 2); });
player2FirstHand.OnPress([&]() -> void { if(firstPlayer == 2) {return;} if (gameState == Ended || !started || ConfirmMenu()) { firstPlayer = 2; ResetGame(true);} });
player2FirstHand.SetColorFunc([&]() -> Color { return Color::White.DimIfNot(2 == firstPlayer); });
player2FirstHand.OnPress([&]() -> void { if(2 == firstPlayer) {return;} if (gameState == Ended || !started || ConfirmMenu()) { firstPlayer = 2; ResetGame(true);} });
settingsUI.AddUIComponent(player2FirstHand, Point(0, 1));

UIToggle hintToggle;
Expand Down
6 changes: 6 additions & 0 deletions Applications/Shell/Shell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,15 @@ void Shell::InitializeFolderSystem() {
}

// First, add all native apps to all_applications
auto& applications = GetApplications();
for (const auto& [app_id, app_info] : applications) {
all_applications.emplace(app_id, ApplicationEntry(app_info));
}

// Then discover and add Python applications
#if DEVICE_STORAGE
DiscoverPythonApps();
#endif

// Try to load existing folder vectors from NVS
bool folders_loaded = false;
Expand Down Expand Up @@ -94,6 +97,7 @@ void Shell::InitializeFolderSystem() {
bool missing_apps_found = false;

// First, check native apps in registration order
auto& application_ids = GetApplicationIDs();
for (const auto& [order_id, app_id] : application_ids) {
// If app is not in any folder, add it
if (apps_in_folders.find(app_id) == apps_in_folders.end()) {
Expand Down Expand Up @@ -359,6 +363,7 @@ uint8_t Shell::GetAppFolder(uint32_t app_id, const ApplicationEntry& app_entry)
return 0;
}

#if DEVICE_STORAGE
void Shell::DiscoverPythonApps() {
MLOGI("Shell", "Starting Python application discovery");

Expand All @@ -385,6 +390,7 @@ void Shell::DiscoverPythonApps() {

MLOGI("Shell", "Python application discovery completed: %d apps added", python_app_infos.size());
}
#endif

void Shell::ApplicationLauncher() {
uint8_t tap_counter = 0;
Expand Down
4 changes: 3 additions & 1 deletion Applications/Shell/Shell.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,10 @@ class Shell : public Application {
void MoveAppToFolder(uint32_t app_id, uint8_t folder_id);

// Python application discovery
#if DEVICE_STORAGE
void DiscoverPythonApps();

#endif

// Helper functions for NVS
void SaveFolderVector(uint8_t folder_id);
void LoadFolderVector(uint8_t folder_id);
Expand Down
31 changes: 30 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
cmake_minimum_required(VERSION 3.16)

if(NOT DEFINED DEVICE)
message(FATAL_ERROR "You must specify -DDEVICE=<device>")
endif()

if(NOT DEFINED FAMILY)
set(FAMILY_PATH ${CMAKE_SOURCE_DIR}/Devices/${DEVICE})
else()
set(FAMILY_PATH ${CMAKE_SOURCE_DIR}/Devices/${FAMILY})
endif()

project(MatrixOS-${DEVICE})

# # Global C flags
Expand Down Expand Up @@ -83,7 +93,26 @@ else()
message( FATAL_ERROR "MODE is not defined" )
endif()

# Load device feature flags before processing subdirectories
if(NOT EXISTS ${FAMILY_PATH}/Config.cmake)
message(FATAL_ERROR "Config.cmake not found in ${FAMILY_PATH}. Please create this file to define DEVICE_STORAGE and DEVICE_BATTERY.")
endif()
include(${FAMILY_PATH}/Config.cmake)

# Pass device feature flags as compile definitions to all targets
add_compile_definitions(
DEVICE_STORAGE=${DEVICE_STORAGE}
DEVICE_BATTERY=${DEVICE_BATTERY}
)

# Set debug flags globally for all targets before adding subdirectories
if(MODE STREQUAL "DEVELOPMENT" OR MODE STREQUAL "UNDEFINED")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3 -Og")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3 -Og")
message(STATUS "Building with debug symbols: -g3 -Og")
endif()

add_subdirectory(${FAMILY_PATH})
add_subdirectory(Devices)
add_subdirectory(OS)
add_subdirectory(${FAMILY_PATH})
add_subdirectory(Applications)
45 changes: 45 additions & 0 deletions Devices/MatrixBlock5/ApplicationList.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Applications List - MatrixBlock5
# Format:
# FolderName - Regular application (sandboxed)
# [System]FolderName - System application (privileged)
# git:URL@branch/hash - Clone git repository at specific branch/commit
# [System]git:URL@branch - System application from git repository
# Applications register their class via RegisterApplicationClass() in their CMakeLists.txt
# Git applications are cloned to {FAMILY_PATH}/Applications/ directory
# Use # for comments and empty lines are allowed
#
# Git URL Examples:
# git:https://github.com/user/myapp@main
# git:https://github.com/user/myapp@v1.2.3
# git:https://github.com/user/myapp@abc123d
# [System]git:https://github.com/user/sysapp@feature-branch

# Device UI
Setting
BrightnessControl

# System Apps
[System]Shell
#MSC

# User Facing Apps
Performance
#Note
#REDACTED
#Companion
#CustomControlMap
#Lighting
#Dice
#Reversi
#Strum
#[System]Python

# Boot animation
{FAMILY_PATH}/Applications/MystrixBoot

# Device-specific applications (if any)
# {FAMILY_PATH}/Applications/FactoryMenu
# {FAMILY_PATH}/Applications/ForceCalibration

# Example application template
#git:https://github.com/203-Systems/Matrix-OS-APP-Template.git@master
18 changes: 18 additions & 0 deletions Devices/MatrixBlock5/Applications/MystrixBoot/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# MystrixBoot Application
file(GLOB_RECURSE MYSTRIXBOOT_SOURCES "*.cpp" "*.c")
file(GLOB_RECURSE MYSTRIXBOOT_HEADERS "*.h")

add_library(MystrixBootAPP ${MYSTRIXBOOT_SOURCES} ${MYSTRIXBOOT_HEADERS})

target_include_directories(MystrixBootAPP PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)

target_link_libraries(MystrixBootAPP PUBLIC
MatrixOSInterface
MatrixOSAPPInterface
BootAnimationTemplate
)

# Register the application class
RegisterApplicationClass(MystrixBoot)
Loading