Code Your Application (C++)
Native C++ apps are built into Matrix OS firmware. Use this path when your app needs native performance, direct Matrix OS APIs, deeper device integration, or C/C++ libraries that should run on-device.
For most app ideas, start in Matrix OS Developer Toolkit or Python first, then move to C++ when you need native behavior.
The examples on this page use the current Application, MatrixOS::Input, InputEvent, and KeypadInfo APIs.
What A Native App Containsβ
A minimal native app usually has:
Applications/HelloNative/
HelloNative.h
HelloNative.cpp
CMakeLists.txt
Then the app folder is enabled from the target device family's ApplicationList.txt, such as Devices/MystrixSim/ApplicationList.txt.
Minimal Headerβ
#pragma once
#include "MatrixOS.h"
#include "Application.h"
class HelloNative : public Application {
public:
inline static Application_Info info = {
.name = "Hello Native",
.author = "Your Name",
.color = Color::Cyan,
.version = 1,
.visibility = true,
};
void Setup(const vector<string>& args) override;
void Loop() override;
void End() override;
private:
Color color = Color::Cyan;
};
Minimal Sourceβ
#include "HelloNative.h"
void HelloNative::Setup(const vector<string>& args) {
(void)args;
MLOGI("HelloNative", "started");
MatrixOS::LED::Fill(Color::Black);
MatrixOS::LED::SetColor(Point(0, 0), color);
MatrixOS::LED::Update();
}
void HelloNative::Loop() {
InputEvent event;
while (MatrixOS::Input::Get(&event))
{
if (event.inputClass != InputClass::Keypad)
{
continue;
}
Point point;
bool hasPoint = MatrixOS::Input::GetPosition(event.id, &point);
if (hasPoint && event.keypad.state == KeypadState::Pressed)
{
MatrixOS::LED::SetColor(point, color);
MatrixOS::LED::Update();
}
else if (hasPoint && event.keypad.state == KeypadState::Released)
{
MatrixOS::LED::SetColor(point, Color::Black);
MatrixOS::LED::Update();
}
else if (event.id == InputId::FunctionKey() && event.keypad.state == KeypadState::Hold)
{
Exit();
}
}
}
void HelloNative::End() {
MatrixOS::LED::Fill(Color::Black);
MatrixOS::LED::Update();
MLOGI("HelloNative", "ended");
}
Expected behavior: when the app starts, LED (0, 0) turns cyan. Pressing a grid key lights that key cyan. Releasing it clears that LED. Holding the function key exits the app.
App Lifecycleβ
| Method | When it runs | Use it for |
|---|---|---|
Setup(const vector<string>& args) | once when the app starts | initialize state, create layers, log startup, parse launch args |
Loop() | repeatedly while the app is active | drain input/MIDI queues and update app state |
End() | when the app exits | clear LEDs, release resources, stop background work |
Exit() | call from app code | request Matrix OS to close the current app |
Loop() should return regularly. Matrix OS calls it again, so do not block forever inside one call.
Input And LED Patternβ
Use MatrixOS::Input for physical controls:
InputEvent event;
while (MatrixOS::Input::Get(&event))
{
if (event.inputClass != InputClass::Keypad)
{
continue;
}
Point point;
if (MatrixOS::Input::GetPosition(event.id, &point))
{
MLOGD("HelloNative", "key %d %d state %d", point.x, point.y, (uint8_t)event.keypad.state);
}
}
For keypad input, event.keypad contains state, pressure, and velocity. Use MatrixOS::Input::GetPosition(...) when your logic needs a coordinate.
Use MatrixOS::LED for drawing:
MatrixOS::LED::SetColor(Point(0, 0), Color::Red);
MatrixOS::LED::Update();
Draw first, then call MatrixOS::LED::Update() to show the frame.
CMakeLists.txtβ
file(GLOB_RECURSE HELLO_NATIVE_SOURCES "*.cpp" "*.c")
file(GLOB_RECURSE HELLO_NATIVE_HEADERS "*.h")
add_library(HelloNativeAPP ${HELLO_NATIVE_SOURCES} ${HELLO_NATIVE_HEADERS})
target_include_directories(HelloNativeAPP PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(HelloNativeAPP PUBLIC
MatrixOSInterface
MatrixOSAPPInterface
)
RegisterApplicationClass(HelloNative)
RegisterApplicationClass(HelloNative) records the class for the generated Applications.h file used by the firmware build.
Enable The App For A Deviceβ
Add the app folder name to the target device family's application list.
For Matrix OS Developer Toolkit:
Devices/MystrixSim/ApplicationList.txt
Add:
HelloNative
Use [System]HelloNative only if the app should run with system-level privilege.
For hardware builds, edit the matching device family list, for example Devices/Mystrix1/ApplicationList.txt or Devices/Mystrix2/ApplicationList.txt.
Build And Testβ
From the Matrix OS repository:
make DEVICE=MystrixSim build-dev
make DEVICE=MystrixSim run
For hardware, build the target device and upload using the Matrix OS build flow for that device.
UI Componentsβ
Matrix OS UI components are owned by your app code while the UI is running. The current C++ API takes component pointers:
#include "UI/UI.h"
void HelloNative::OpenMenu() {
UI menu("Menu", Color::Cyan, true);
UIButton button;
button.SetName("Color");
button.SetColor(color);
button.OnPress([&]() {
color = Color::Magenta;
});
menu.AddUIComponent(&button, Point(0, 0));
menu.Start();
}
Keep UI component objects alive until menu.Start() returns.
Use The API Referenceβ
Start with these API pages while building native apps:
- C++ API Overview
- Input API
- LED API
- MIDI API
- NVS API
- FileSystem API
- UI Framework
- Debug Your Application (C++)
If you are updating older native app code, read Migration From 3.x to 4.x after you understand the current input model.
Native App Notesβ
- Keep long-running logic cooperative. Drain queues, update state, then return from
Loop(). - Store app state inside the application class instead of global variables when possible.
- Clean up dynamic memory, extra tasks, mutexes, peripheral state, and LED output in
End(). - Test timing, USB, MIDI, HID, storage, and pressure-sensitive input on real hardware before release.
Comments