Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
c24e48b95This file is pinned to the selected release. Download the original to open it in your design tool.
#include "utils/Menu.h"
#include "peripherals/Drum.h"
namespace Doncon::Utils {
// NOLINTBEGIN(modernize-use-designated-initializers)
const std::map<Menu::Page, const Menu::Descriptor> Menu::descriptors = {
{Menu::Page::Main, //
{Menu::Descriptor::Type::Menu, //
"Settings", //
{{"Mode", Menu::Descriptor::Action::GotoPageDeviceMode}, //
{"Drum", Menu::Descriptor::Action::GotoPageDrum}, //
{"Led", Menu::Descriptor::Action::GotoPageLed}, //
{"Reset", Menu::Descriptor::Action::GotoPageReset}, //
{"USB Flash", Menu::Descriptor::Action::GotoPageBootsel}}, //
0}}, //
{Menu::Page::DeviceMode, //
{Menu::Descriptor::Type::Selection, //
"Mode", //
{{"Swtch Tata", Menu::Descriptor::Action::SetUsbMode}, //
{"Swtch Pro", Menu::Descriptor::Action::SetUsbMode}, //
{"Dualshock3", Menu::Descriptor::Action::SetUsbMode}, //
{"PS4 Tata", Menu::Descriptor::Action::SetUsbMode}, //
{"Dualshock4", Menu::Descriptor::Action::SetUsbMode}, //
{"Keybrd P1", Menu::Descriptor::Action::SetUsbMode}, //
{"Keybrd P2", Menu::Descriptor::Action::SetUsbMode}, //
{"Xbox 360", Menu::Descriptor::Action::SetUsbMode}, //
{"Analog P1", Menu::Descriptor::Action::SetUsbMode}, //
{"Analog P2", Menu::Descriptor::Action::SetUsbMode}, //
{"MIDI", Menu::Descriptor::Action::SetUsbMode}, //
{"Debug", Menu::Descriptor::Action::SetUsbMode}}, //
0}}, //
{Menu::Page::Drum, //
{Menu::Descriptor::Type::Menu, //
"Drum Settings", //
{{"Hold Time", Menu::Descriptor::Action::GotoPageDrumDebounceDelay}, //
{"Thresholds", Menu::Descriptor::Action::GotoPageDrumTriggerThresholds}, //
{"Double Trg", Menu::Descriptor::Action::GotoPageDrumDoubleTrigger}}, //
0}}, //
{Menu::Page::DrumTriggerThresholds, //
{Menu::Descriptor::Type::Menu, //
"Thresholds", //
{{"Left Ka", Menu::Descriptor::Action::GotoPageDrumTriggerThresholdKaLeft}, //
{"Left Don", Menu::Descriptor::Action::GotoPageDrumTriggerThresholdDonLeft}, //
{"Right Don", Menu::Descriptor::Action::GotoPageDrumTriggerThresholdDonRight}, //
{"Right Ka", Menu::Descriptor::Action::GotoPageDrumTriggerThresholdKaRight}}, //
0}}, //
{Menu::Page::DrumDoubleTrigger, //
{Menu::Descriptor::Type::Menu, //
"Double Hit Mode", //
{{"Off", Menu::Descriptor::Action::SetDoubleTriggerOff}, //
{"Threshold", Menu::Descriptor::Action::GotoPageDrumDoubleTriggerThresholds}, //
{"Always", Menu::Descriptor::Action::SetDoubleTriggerAlways}}, //
0}}, //
{Menu::Page::DrumDoubleTriggerThresholds, //
{Menu::Descriptor::Type::Menu, //
"Double Thresholds", //
{{"Left Ka", Menu::Descriptor::Action::GotoPageDrumDoubleTriggerThresholdKaLeft}, //
{"Left Don", Menu::Descriptor::Action::GotoPageDrumDoubleTriggerThresholdDonLeft}, //
{"Right Don", Menu::Descriptor::Action::GotoPageDrumDoubleTriggerThresholdDonRight}, //
{"Right Ka", Menu::Descriptor::Action::GotoPageDrumDoubleTriggerThresholdKaRight}}, //
0}},
{Menu::Page::DrumDebounceDelay, //
{Menu::Descriptor::Type::Value, //
"Hit Hold Time (ms)", //
{{"", Menu::Descriptor::Action::SetDrumDebounceDelay}}, //
UINT8_MAX}},
{Menu::Page::DrumTriggerThresholdKaLeft, //
{Menu::Descriptor::Type::Value, //
"Trg Level Left Ka", //
{{"", Menu::Descriptor::Action::SetDrumTriggerThresholdKaLeft}}, //
4095}},
{Menu::Page::DrumTriggerThresholdDonLeft, //
{Menu::Descriptor::Type::Value, //
"Trg Level Left Don", //
{{"", Menu::Descriptor::Action::SetDrumTriggerThresholdDonLeft}}, //
4095}},
{Menu::Page::DrumTriggerThresholdDonRight, //
{Menu::Descriptor::Type::Value, //
"Trg Level Right Don", //
{{"", Menu::Descriptor::Action::SetDrumTriggerThresholdDonRight}}, //
4095}},
{Menu::Page::DrumTriggerThresholdKaRight, //
{Menu::Descriptor::Type::Value, //
"Trg Level Right Ka", //
{{"", Menu::Descriptor::Action::SetDrumTriggerThresholdKaRight}}, //
4095}},
{Menu::Page::DrumDoubleTriggerThresholdKaLeft, //
{Menu::Descriptor::Type::Value, //
"Trg Level Left Ka", //
{{"", Menu::Descriptor::Action::SetDrumDoubleTriggerThresholdKaLeft}}, //
4095}},
{Menu::Page::DrumDoubleTriggerThresholdDonLeft, //
{Menu::Descriptor::Type::Value, //
"Trg Level Left Don", //
{{"", Menu::Descriptor::Action::SetDrumDoubleTriggerThresholdDonLeft}}, //
4095}},
{Menu::Page::DrumDoubleTriggerThresholdDonRight, //
{Menu::Descriptor::Type::Value, //
"Trg Level Right Don", //
{{"", Menu::Descriptor::Action::SetDrumDoubleTriggerThresholdDonRight}}, //
4095}},
{Menu::Page::DrumDoubleTriggerThresholdKaRight, //
{Menu::Descriptor::Type::Value, //
"Trg Level Right Ka", //
{{"", Menu::Descriptor::Action::SetDrumDoubleTriggerThresholdKaRight}}, //
4095}},
{Menu::Page::Led, //
{Menu::Descriptor::Type::Menu, //
"LED Settings", //
{{"Brightness", Menu::Descriptor::Action::GotoPageLedBrightness}, //
{"Plyr Color", Menu::Descriptor::Action::GotoPageLedEnablePlayerColor}}, //
0}}, //
{Menu::Page::LedBrightness, //
{Menu::Descriptor::Type::Value, //
"LED Brightness", //
{{"", Menu::Descriptor::Action::SetLedBrightness}}, //
UINT8_MAX}}, //
{Menu::Page::LedEnablePlayerColor, //
{Menu::Descriptor::Type::Toggle, //
"Player Color (PS4)", //
{{"", Menu::Descriptor::Action::SetLedEnablePlayerColor}}, //
0}}, //
{Menu::Page::Reset, //
{Menu::Descriptor::Type::Menu, //
"Reset all Settings?", //
{{"No", Menu::Descriptor::Action::GotoParent}, //
{"Yes", Menu::Descriptor::Action::DoReset}}, //
0}}, //
{Menu::Page::Bootsel, //
{Menu::Descriptor::Type::Menu, //
"Reboot to Flash Mode", //
{{"Reboot?", Menu::Descriptor::Action::DoRebootToBootsel}}, //
0}}, //
{Menu::Page::BootselMsg, //
{Menu::Descriptor::Type::RebootInfo, //
"Ready to Flash...", //
{{"BOOTSEL", Menu::Descriptor::Action::None}}, //
0}}, //
};
// NOLINTEND(modernize-use-designated-initializers)
Menu::Buttons::Buttons()
: m_states({{Id::Up, {}}, {Id::Down, {}}, {Id::Left, {}}, {Id::Right, {}}, {Id::Confirm, {}}, {Id::Back, {}}}) {}
void Menu::Buttons::update(const InputState::Controller &controller_state) {
static const uint32_t repeat_delay = 1000;
static const uint32_t repeat_interval = 20;
static const uint32_t fast_repeat_delay = 5000;
static const uint32_t fast_repeat_interval = 2;
auto handle_button = [](State &button_state, bool input_state) {
if (input_state) {
const uint32_t now = to_ms_since_boot(get_absolute_time());
switch (button_state.repeat) {
case State::Repeat::Idle:
button_state.pressed = true;
button_state.repeat = State::Repeat::RepeatDelay;
button_state.pressed_since = now;
break;
case State::Repeat::RepeatDelay:
if ((now - button_state.pressed_since) > repeat_delay) {
button_state.pressed = true;
button_state.repeat = State::Repeat::Repeat;
button_state.last_repeat = now;
} else {
button_state.pressed = false;
}
break;
case State::Repeat::Repeat:
if ((now - button_state.pressed_since) > fast_repeat_delay) {
button_state.pressed = true;
button_state.repeat = State::Repeat::FastRepeat;
button_state.last_repeat = now;
} else if ((now - button_state.last_repeat) > repeat_interval) {
button_state.pressed = true;
button_state.last_repeat = now;
} else {
button_state.pressed = false;
}
break;
case State::Repeat::FastRepeat:
if ((now - button_state.last_repeat) > fast_repeat_interval) {
button_state.pressed = true;
button_state.last_repeat = now;
} else {
button_state.pressed = false;
}
break;
}
} else {
button_state.pressed = false;
button_state.repeat = State::Repeat::Idle;
}
};
handle_button(m_states.at(Id::Up), controller_state.dpad.up);
handle_button(m_states.at(Id::Down), controller_state.dpad.down);
handle_button(m_states.at(Id::Left), controller_state.dpad.left);
handle_button(m_states.at(Id::Right), controller_state.dpad.right);
handle_button(m_states.at(Id::Confirm), controller_state.buttons.east);
handle_button(m_states.at(Id::Back), controller_state.buttons.south);
}
bool Menu::Buttons::getPressed(Id id) const { return m_states.at(id).pressed; }
Menu::Menu(std::shared_ptr<SettingsStore> settings_store) : m_store(std::move(settings_store)) {};
void Menu::activate() {
m_state_stack = std::stack<State>({{.page = Page::Main, .selected_value = 0, .original_value = 0}});
m_active = true;
}
uint16_t Menu::getCurrentValue(Menu::Page page) {
switch (page) {
case Page::DeviceMode:
return static_cast<uint16_t>(m_store->getUsbMode());
case Page::DrumDebounceDelay:
return m_store->getDebounceDelay();
case Page::DrumDoubleTrigger:
return static_cast<uint16_t>(m_store->getDoubleTriggerMode());
case Page::DrumTriggerThresholdKaLeft:
return m_store->getTriggerThresholds().ka_left;
case Page::DrumTriggerThresholdDonLeft:
return m_store->getTriggerThresholds().don_left;
case Page::DrumTriggerThresholdDonRight:
return m_store->getTriggerThresholds().don_right;
case Page::DrumTriggerThresholdKaRight:
return m_store->getTriggerThresholds().ka_right;
case Page::DrumDoubleTriggerThresholdKaLeft:
return m_store->getDoubleTriggerThresholds().ka_left;
case Page::DrumDoubleTriggerThresholdDonLeft:
return m_store->getDoubleTriggerThresholds().don_left;
case Page::DrumDoubleTriggerThresholdDonRight:
return m_store->getDoubleTriggerThresholds().don_right;
case Page::DrumDoubleTriggerThresholdKaRight:
return m_store->getDoubleTriggerThresholds().ka_right;
case Page::LedBrightness:
return m_store->getLedBrightness();
case Page::LedEnablePlayerColor:
return static_cast<uint16_t>(m_store->getLedEnablePlayerColor());
case Page::Main:
case Page::Drum:
case Page::DrumTriggerThresholds:
case Page::DrumDoubleTriggerThresholds:
case Page::Led:
case Page::Reset:
case Page::Bootsel:
case Page::BootselMsg:
break;
}
return 0;
}
void Menu::gotoPage(Menu::Page page) {
const auto current_value = getCurrentValue(page);
m_state_stack.push({.page = page, .selected_value = current_value, .original_value = current_value});
}
void Menu::gotoParent(bool do_restore) {
const auto current_state = m_state_stack.top();
if (current_state.page == Page::Main) {
m_active = false;
}
if (do_restore) {
switch (current_state.page) {
case Page::DeviceMode:
m_store->setUsbMode(static_cast<usb_mode_t>(current_state.original_value));
break;
case Page::DrumDebounceDelay:
m_store->setDebounceDelay(current_state.original_value);
break;
case Page::DrumTriggerThresholdKaLeft: {
auto thresholds = m_store->getTriggerThresholds();
thresholds.ka_left = current_state.original_value;
m_store->setTriggerThresholds(thresholds);
} break;
case Page::DrumTriggerThresholdDonLeft: {
auto thresholds = m_store->getTriggerThresholds();
thresholds.don_left = current_state.original_value;
m_store->setTriggerThresholds(thresholds);
} break;
case Page::DrumTriggerThresholdDonRight: {
auto thresholds = m_store->getTriggerThresholds();
thresholds.don_right = current_state.original_value;
m_store->setTriggerThresholds(thresholds);
} break;
case Page::DrumTriggerThresholdKaRight: {
auto thresholds = m_store->getTriggerThresholds();
thresholds.ka_right = current_state.original_value;
m_store->setTriggerThresholds(thresholds);
} break;
case Page::DrumDoubleTrigger:
m_store->setDoubleTriggerMode(
static_cast<Peripherals::Drum::Config::DoubleTriggerMode>(current_state.original_value));
break;
case Page::DrumDoubleTriggerThresholdKaLeft: {
auto thresholds = m_store->getDoubleTriggerThresholds();
thresholds.ka_left = current_state.original_value;
m_store->setDoubleTriggerThresholds(thresholds);
} break;
case Page::DrumDoubleTriggerThresholdDonLeft: {
auto thresholds = m_store->getDoubleTriggerThresholds();
thresholds.don_left = current_state.original_value;
m_store->setDoubleTriggerThresholds(thresholds);
} break;
case Page::DrumDoubleTriggerThresholdDonRight: {
auto thresholds = m_store->getDoubleTriggerThresholds();
thresholds.don_right = current_state.original_value;
m_store->setDoubleTriggerThresholds(thresholds);
} break;
case Page::DrumDoubleTriggerThresholdKaRight: {
auto thresholds = m_store->getDoubleTriggerThresholds();
thresholds.ka_right = current_state.original_value;
m_store->setDoubleTriggerThresholds(thresholds);
} break;
case Page::LedBrightness:
m_store->setLedBrightness(current_state.original_value);
break;
case Page::LedEnablePlayerColor:
m_store->setLedEnablePlayerColor(static_cast<bool>(current_state.original_value));
break;
case Page::Main:
case Page::Drum:
case Page::DrumTriggerThresholds:
case Page::DrumDoubleTriggerThresholds:
case Page::Led:
case Page::Reset:
case Page::Bootsel:
case Page::BootselMsg:
break;
}
}
m_state_stack.pop();
}
void Menu::performAction(Descriptor::Action action, uint16_t value) {
switch (action) {
case Descriptor::Action::None:
break;
case Descriptor::Action::GotoParent:
gotoParent(false);
break;
case Descriptor::Action::GotoPageDeviceMode:
gotoPage(Page::DeviceMode);
break;
case Descriptor::Action::GotoPageDrum:
gotoPage(Page::Drum);
break;
case Descriptor::Action::GotoPageDrumDoubleTrigger:
gotoPage(Page::DrumDoubleTrigger);
break;
case Descriptor::Action::GotoPageDrumTriggerThresholds:
gotoPage(Page::DrumTriggerThresholds);
break;
case Descriptor::Action::GotoPageDrumDoubleTriggerThresholds:
m_store->setDoubleTriggerMode(Peripherals::Drum::Config::DoubleTriggerMode::Threshold);
gotoPage(Page::DrumDoubleTriggerThresholds);
break;
case Descriptor::Action::GotoPageLed:
gotoPage(Page::Led);
break;
case Descriptor::Action::GotoPageReset:
gotoPage(Page::Reset);
break;
case Descriptor::Action::GotoPageBootsel:
gotoPage(Page::Bootsel);
break;
case Descriptor::Action::GotoPageDrumDebounceDelay:
gotoPage(Page::DrumDebounceDelay);
break;
case Descriptor::Action::GotoPageDrumTriggerThresholdKaLeft:
gotoPage(Page::DrumTriggerThresholdKaLeft);
break;
case Descriptor::Action::GotoPageDrumTriggerThresholdDonLeft:
gotoPage(Page::DrumTriggerThresholdDonLeft);
break;
case Descriptor::Action::GotoPageDrumTriggerThresholdDonRight:
gotoPage(Page::DrumTriggerThresholdDonRight);
break;
case Descriptor::Action::GotoPageDrumTriggerThresholdKaRight:
gotoPage(Page::DrumTriggerThresholdKaRight);
break;
case Descriptor::Action::GotoPageDrumDoubleTriggerThresholdKaLeft:
gotoPage(Page::DrumDoubleTriggerThresholdKaLeft);
break;
case Descriptor::Action::GotoPageDrumDoubleTriggerThresholdDonLeft:
gotoPage(Page::DrumDoubleTriggerThresholdDonLeft);
break;
case Descriptor::Action::GotoPageDrumDoubleTriggerThresholdDonRight:
gotoPage(Page::DrumDoubleTriggerThresholdDonRight);
break;
case Descriptor::Action::GotoPageDrumDoubleTriggerThresholdKaRight:
gotoPage(Page::DrumDoubleTriggerThresholdKaRight);
break;
case Descriptor::Action::GotoPageLedBrightness:
gotoPage(Page::LedBrightness);
break;
case Descriptor::Action::GotoPageLedEnablePlayerColor:
gotoPage(Page::LedEnablePlayerColor);
break;
case Descriptor::Action::SetUsbMode:
m_store->setUsbMode(static_cast<usb_mode_t>(value));
break;
case Descriptor::Action::SetDrumDebounceDelay:
m_store->setDebounceDelay(value);
break;
case Descriptor::Action::SetDoubleTriggerOff:
m_store->setDoubleTriggerMode(Peripherals::Drum::Config::DoubleTriggerMode::Off);
gotoParent(false);
break;
case Descriptor::Action::SetDoubleTriggerAlways:
m_store->setDoubleTriggerMode(Peripherals::Drum::Config::DoubleTriggerMode::Always);
gotoParent(false);
break;
case Descriptor::Action::SetDrumTriggerThresholdKaLeft: {
auto thresholds = m_store->getTriggerThresholds();
thresholds.ka_left = value;
m_store->setTriggerThresholds(thresholds);
} break;
case Descriptor::Action::SetDrumTriggerThresholdDonLeft: {
auto thresholds = m_store->getTriggerThresholds();
thresholds.don_left = value;
m_store->setTriggerThresholds(thresholds);
} break;
case Descriptor::Action::SetDrumTriggerThresholdDonRight: {
auto thresholds = m_store->getTriggerThresholds();
thresholds.don_right = value;
m_store->setTriggerThresholds(thresholds);
} break;
case Descriptor::Action::SetDrumTriggerThresholdKaRight: {
auto thresholds = m_store->getTriggerThresholds();
thresholds.ka_right = value;
m_store->setTriggerThresholds(thresholds);
} break;
case Descriptor::Action::SetDrumDoubleTriggerThresholdKaLeft: {
auto thresholds = m_store->getDoubleTriggerThresholds();
thresholds.ka_left = value;
m_store->setDoubleTriggerThresholds(thresholds);
} break;
case Descriptor::Action::SetDrumDoubleTriggerThresholdDonLeft: {
auto thresholds = m_store->getDoubleTriggerThresholds();
thresholds.don_left = value;
m_store->setDoubleTriggerThresholds(thresholds);
} break;
case Descriptor::Action::SetDrumDoubleTriggerThresholdDonRight: {
auto thresholds = m_store->getDoubleTriggerThresholds();
thresholds.don_right = value;
m_store->setDoubleTriggerThresholds(thresholds);
} break;
case Descriptor::Action::SetDrumDoubleTriggerThresholdKaRight: {
auto thresholds = m_store->getDoubleTriggerThresholds();
thresholds.ka_right = value;
m_store->setDoubleTriggerThresholds(thresholds);
} break;
case Descriptor::Action::SetLedBrightness:
m_store->setLedBrightness(value);
break;
case Descriptor::Action::SetLedEnablePlayerColor:
m_store->setLedEnablePlayerColor(static_cast<bool>(value));
break;
case Descriptor::Action::DoReset:
(*m_store).reset();
break;
case Descriptor::Action::DoRebootToBootsel:
m_store->scheduleReboot(true);
gotoPage(Page::BootselMsg);
break;
}
}
void Menu::update(const InputState::Controller &controller_state) {
m_buttons.update(controller_state);
State ¤t_state = m_state_stack.top();
auto descriptor_it = descriptors.find(current_state.page);
if (descriptor_it == descriptors.end()) {
assert(false);
return;
}
if (descriptor_it->second.type == Descriptor::Type::RebootInfo) {
m_active = false;
} else if (m_buttons.getPressed(Buttons::Id::Left)) {
switch (descriptor_it->second.type) {
case Descriptor::Type::Toggle:
current_state.selected_value = current_state.selected_value == 0 ? 1 : 0;
performAction(descriptor_it->second.items.at(0).second, current_state.selected_value);
break;
case Descriptor::Type::Selection:
if (current_state.selected_value == 0) {
current_state.selected_value = descriptor_it->second.items.size() - 1;
} else {
current_state.selected_value--;
}
performAction(descriptor_it->second.items.at(current_state.selected_value).second,
current_state.selected_value);
break;
case Descriptor::Type::Menu:
if (current_state.selected_value == 0) {
current_state.selected_value = descriptor_it->second.items.size() - 1;
} else {
current_state.selected_value--;
}
break;
case Descriptor::Type::Value:
case Descriptor::Type::RebootInfo:
break;
}
} else if (m_buttons.getPressed(Buttons::Id::Right)) {
switch (descriptor_it->second.type) {
case Descriptor::Type::Toggle:
current_state.selected_value = current_state.selected_value == 0 ? 1 : 0;
performAction(descriptor_it->second.items.at(0).second, current_state.selected_value);
break;
case Descriptor::Type::Selection:
if (current_state.selected_value == descriptor_it->second.items.size() - 1) {
current_state.selected_value = 0;
} else {
current_state.selected_value++;
}
performAction(descriptor_it->second.items.at(current_state.selected_value).second,
current_state.selected_value);
break;
case Descriptor::Type::Menu:
if (current_state.selected_value == descriptor_it->second.items.size() - 1) {
current_state.selected_value = 0;
} else {
current_state.selected_value++;
}
break;
case Descriptor::Type::Value:
case Descriptor::Type::RebootInfo:
break;
}
} else if (m_buttons.getPressed(Buttons::Id::Up)) {
switch (descriptor_it->second.type) {
case Descriptor::Type::Value:
if (current_state.selected_value < descriptor_it->second.max_value) {
current_state.selected_value++;
performAction(descriptor_it->second.items.at(0).second, current_state.selected_value);
}
break;
case Descriptor::Type::Toggle:
case Descriptor::Type::Selection:
case Descriptor::Type::Menu:
case Descriptor::Type::RebootInfo:
break;
}
} else if (m_buttons.getPressed(Buttons::Id::Down)) {
switch (descriptor_it->second.type) {
case Descriptor::Type::Value:
if (current_state.selected_value > 0) {
current_state.selected_value--;
performAction(descriptor_it->second.items.at(0).second, current_state.selected_value);
}
break;
case Descriptor::Type::Toggle:
case Descriptor::Type::Selection:
case Descriptor::Type::Menu:
case Descriptor::Type::RebootInfo:
break;
}
} else if (m_buttons.getPressed(Buttons::Id::Back)) {
switch (descriptor_it->second.type) {
case Descriptor::Type::Value:
case Descriptor::Type::Toggle:
case Descriptor::Type::Selection:
gotoParent(true);
break;
case Descriptor::Type::Menu:
gotoParent(false);
break;
case Descriptor::Type::RebootInfo:
break;
}
} else if (m_buttons.getPressed(Buttons::Id::Confirm)) {
switch (descriptor_it->second.type) {
case Descriptor::Type::Value:
case Descriptor::Type::Toggle:
case Descriptor::Type::Selection:
gotoParent(false);
break;
case Descriptor::Type::Menu:
performAction(descriptor_it->second.items.at(current_state.selected_value).second,
current_state.selected_value);
break;
case Descriptor::Type::RebootInfo:
break;
}
}
}
bool Menu::active() const { return m_active; }
Menu::State Menu::getState() const { return m_state_stack.top(); }
} // namespace Doncon::Utils