Skip to content
Merged
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
4 changes: 2 additions & 2 deletions projects/APPLaunch/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ neither business path.
The screensaver filter also observes one key it does not own: while idle it
tracks the press and release of the long-press gesture that enters the lock and
reports them as *not* consumed, so the page underneath keeps the short-press
meaning of that key. The gesture is TAB held for 5000 ms; the model announces it
with `Hold TAB for 5s to lock` on the shared launcher toast from its hint delay (500 ms)
meaning of that key. The gesture is TAB held for 3000 ms; the model announces it
with `Hold TAB for 3s to lock` on the shared launcher toast from its hint delay (500 ms)
until the hold ends. Clear that toast on release, on a different key, and when
the lock is entered or left. Once the gesture matures the lock takes every key over, and
because only a fresh press changes lock state, the held key's repeats and its
Expand Down
17 changes: 14 additions & 3 deletions projects/APPLaunch/main/ui/builtin_app_registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@

#include "app_display_order.hpp"
#include "app_registry.h"
#include "cli_terminal_help.hpp"
#include "launch.h"
#include "ui.h"
#include "generated/page_app.h"
#include "launcher_platform.hpp"
#include "model/dynamic_app_registry.hpp"
#include "python_terminal_help.hpp"
#include "settings/settings_page.hpp"
#include <array>
#include <cstring>
Expand All @@ -30,6 +32,7 @@ struct BuiltinAppRegistration {
bool sysplause;
bool run_as_root;
BuiltinAppAppender append;
TerminalHelpFactory help_factory = nullptr;
};

template <class PageT>
Expand All @@ -38,6 +41,12 @@ void append_page_app(std::list<app> &apps, const AppDescriptor &desc)
apps.emplace_back(desc.label, launcher_platform::path(desc.icon), page_v<PageT>);
}

void append_cli_app(std::list<app> &apps, const AppDescriptor &desc)
{
apps.emplace_back(desc.label, launcher_platform::path(desc.icon), page_v<UISTPage>,
create_cli_help);
}

std::string resolved_exec(const BuiltinAppRegistration &registration)
{
std::string exec = registration.exec ? registration.exec : "";
Expand All @@ -56,7 +65,8 @@ void append_builtin_app(std::list<app> &apps, const BuiltinAppRegistration &regi
resolved_exec(registration),
registration.terminal,
registration.sysplause,
registration.run_as_root);
registration.run_as_root,
registration.help_factory);
}

constexpr BuiltinAppRegistration BUILTIN_APPS[] = {
Expand All @@ -65,8 +75,9 @@ constexpr BuiltinAppRegistration BUILTIN_APPS[] = {
{{"Store", "store_100.png", "app_Store", false, true},
"@appstore_exec", false, true, false, nullptr},
{{"CLI", "cli_100.png", "app_CLI", false, true},
nullptr, false, true, false, append_page_app<UISTPage>},
{{"Python", "python_100.png", "app_Python", true, false}, "python3", true, false, false, nullptr},
nullptr, false, true, false, append_cli_app},
{{"Python", "python_100.png", "app_Python", true, false},
"python3", true, false, false, nullptr, create_python_help},
#if defined(__linux__) && !defined(HAL_PLATFORM_SDL)
{{"SSH", "ssh_100.png", "app_SSH", true, false},
nullptr, false, true, false, append_page_app<UISSHPage>},
Expand Down
50 changes: 50 additions & 0 deletions projects/APPLaunch/main/ui/cli_terminal_help.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/

#include "cli_terminal_help.hpp"

lv_obj_t *create_cli_help(lv_obj_t *parent)
{
if (!parent) return nullptr;

lv_obj_t *overlay = lv_obj_create(parent);
if (!overlay) return nullptr;
lv_obj_remove_style_all(overlay);
lv_obj_set_size(overlay, 320, 150);
lv_obj_set_pos(overlay, 0, 0);
lv_obj_set_style_bg_color(overlay, lv_color_hex(0x10151C), 0);
lv_obj_set_style_bg_opa(overlay, LV_OPA_COVER, 0);
lv_obj_clear_flag(overlay, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_flag(overlay, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_scroll_dir(overlay, LV_DIR_VER);
lv_obj_set_scrollbar_mode(overlay, LV_SCROLLBAR_MODE_OFF);

lv_obj_t *label = lv_label_create(overlay);
if (!label) {
lv_obj_delete(overlay);
return nullptr;
}
lv_obj_set_pos(label, 6, 1);
lv_obj_set_size(label, 308, LV_SIZE_CONTENT);
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
lv_obj_set_style_text_font(label, &lv_font_montserrat_12, 0);
lv_obj_set_style_text_color(label, lv_color_hex(0xF0F6FC), 0);
lv_obj_set_style_text_line_space(label, 0, 0);
lv_label_set_text(label,
"A terminal for command-line tools.\n\n"
"Supports two display modes: Fit to Width, which may be cramped on the "
"small screen, and Viewport, which lets you pan around a larger terminal "
"area.\n\n"
"fn + Z / C: move cursor left / right\n"
"fn + F / X: previous / next command\n"
"fn + K: move cursor to line start (Home)\n"
"fn + N: move cursor to line end (End)\n"
"shift + fn + L: page up\n"
"shift + fn + M: page down\n\n"
"F6 (fn + 6): enter / exit Viewport mode\n"
"F4/F5/F7/F8 (fn + 4/5/7/8): pan the viewport");
return overlay;
}
11 changes: 11 additions & 0 deletions projects/APPLaunch/main/ui/cli_terminal_help.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/

#pragma once

#include "lvgl/lvgl.h"

lv_obj_t *create_cli_help(lv_obj_t *parent);
5 changes: 3 additions & 2 deletions projects/APPLaunch/main/ui/launch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,14 @@ void Launch::abort_page_launch() noexcept
}
}

void Launch::launch_Exec_in_terminal(const std::string &exec, bool sysplause)
void Launch::launch_Exec_in_terminal(const std::string &exec, bool sysplause,
TerminalHelpFactory help_factory)
{
if (!begin_page_launch()) return;
SLOGI("Launching terminal app: %s", exec.c_str());
ui_loading::show("Loading...");
lv_refr_now(nullptr);
auto p = std::make_shared<UISTPage>();
auto p = std::make_shared<UISTPage>(help_factory);
if (!p->screen())
throw std::runtime_error("terminal page creation failed");
app_Page = p;
Expand Down
32 changes: 31 additions & 1 deletion projects/APPLaunch/main/ui/launch.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
#include "ui_loading.h"
#include "esc_ui_watchdog.h"
#include "model/launcher_navigation_model.hpp"
#include "terminal_help_factory.hpp"

#include <cstddef>
#include <functional>
#include <list>
#include <memory>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <vector>

class Launch;
Expand All @@ -43,9 +45,14 @@ struct app
app(std::string name, std::string icon, std::string exec, bool terminal);
app(std::string name, std::string icon, std::string exec, bool terminal, bool sysplause);
app(std::string name, std::string icon, std::string exec, bool terminal, bool sysplause, bool run_as_root);
app(std::string name, std::string icon, std::string exec, bool terminal, bool sysplause,
bool run_as_root, TerminalHelpFactory help_factory);

template <class PageT>
app(std::string name, std::string icon, page_t<PageT> tag);
template <class PageT>
app(std::string name, std::string icon, page_t<PageT> tag,
TerminalHelpFactory help_factory);
};

class Launch
Expand All @@ -69,7 +76,8 @@ class Launch
void go_back_home();
bool begin_page_launch();
void abort_page_launch() noexcept;
void launch_Exec_in_terminal(const std::string &exec, bool sysplause = true);
void launch_Exec_in_terminal(const std::string &exec, bool sysplause = true,
TerminalHelpFactory help_factory = nullptr);
void launch_Exec(const std::string &exec, bool keep_root = false);
void applications_load();
void refresh_home_carousel();
Expand Down Expand Up @@ -111,3 +119,25 @@ app::app(std::string name, std::string icon, page_t<PageT>)
cp0_lvgl_start_app_page(*page);
};
}

template <class PageT>
app::app(std::string name, std::string icon, page_t<PageT>, TerminalHelpFactory help_factory)
: Name(std::move(name)), Icon(std::move(icon))
{
launch = [help_factory](Launch *owner) {
if (!owner->begin_page_launch()) return;
ui_loading::show("Loading...");
lv_refr_now(nullptr);
std::shared_ptr<PageT> page;
if constexpr (std::is_constructible_v<PageT, TerminalHelpFactory>)
page = std::make_shared<PageT>(help_factory);
else
page = std::make_shared<PageT>();
if (!page->screen())
throw std::runtime_error("application page creation failed");
owner->app_Page = page;
page->navigate_home = std::bind(&Launch::go_back_home, owner);
ui_loading::hide();
cp0_lvgl_start_app_page(*page);
};
}
17 changes: 17 additions & 0 deletions projects/APPLaunch/main/ui/launcher_app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,20 @@ app::app(std::string name,
else owner->launch_Exec(exec, run_as_root);
};
}

app::app(std::string name,
std::string icon,
std::string exec,
bool terminal,
bool sysplause,
bool run_as_root,
TerminalHelpFactory help_factory)
: Name(std::move(name)), Icon(std::move(icon)), Exec(exec)
{
launch = [exec = std::move(exec), terminal, sysplause, run_as_root,
help_factory](Launch *owner) {
if (!owner || exec.empty()) return;
if (terminal) owner->launch_Exec_in_terminal(exec, sysplause, help_factory);
else owner->launch_Exec(exec, run_as_root);
};
}
2 changes: 1 addition & 1 deletion projects/APPLaunch/main/ui/model/screensaver_model.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class ScreensaverModel
Count = 8,
};
enum class ScreenOffMetric : uint32_t {
HoldMs = 5000,
HoldMs = 3000,
};
enum class HoldHintMetric : uint32_t {
HintMs = 500,
Expand Down
6 changes: 4 additions & 2 deletions projects/APPLaunch/main/ui/page_app/ui_app_st.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@
#include <cstdlib>
#include <cstring>

UISTPage::UISTPage()
: AppPage()
UISTPage::UISTPage(TerminalHelpFactory help_factory)
: AppPage(), help_factory_(help_factory)
{
set_page_title("CLI");
reset_terminal();
create_ui();
create_help();
if (!renderer_ready()) {
if (terminal_container_) lv_obj_delete(terminal_container_);
return;
Expand Down Expand Up @@ -55,6 +56,7 @@ void UISTPage::exec(const std::string &command, const std::list<std::string> &ar

terminal_active_ = true;
waiting_key_to_exit_ = false;
hide_help();
big_mode_ = false;
term_cols_ = NORMAL_COLS;
term_rows_ = NORMAL_ROWS;
Expand Down
10 changes: 9 additions & 1 deletion projects/APPLaunch/main/ui/page_app/ui_app_st.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "../launcher_ui_app_page.hpp"
#include "../model/page_timer_lifecycle.hpp"
#include "../model/st_page_contract.hpp"
#include "../terminal_help_factory.hpp"
#include "../model/terminal_unicode.hpp"
#include "cp0_lvgl_app.h"
#include "input_keys.h"
Expand Down Expand Up @@ -132,7 +133,7 @@ class UISTPage : public AppPage
public:
bool terminal_sysplause = true;

UISTPage();
explicit UISTPage(TerminalHelpFactory help_factory = nullptr);
~UISTPage();

void exec(std::string cmd);
Expand Down Expand Up @@ -170,6 +171,7 @@ class UISTPage : public AppPage
lv_obj_t *scrollbar_thumb_ = nullptr;
lv_obj_t *hscrollbar_track_ = nullptr;
lv_obj_t *hscrollbar_thumb_ = nullptr;
lv_obj_t *help_overlay_ = nullptr;
std::array<lv_obj_t *, BOTTOM_BAR_SLOTS> bottom_labels_{};
std::array<lv_obj_t *, BOTTOM_BAR_SLOTS> bottom_indicators_{};
const lv_font_t *mono_font_ = nullptr;
Expand All @@ -178,6 +180,7 @@ class UISTPage : public AppPage
PageTimerLifecycle<lv_timer_t *> cursor_timer_;

std::string pty_handle_;
TerminalHelpFactory help_factory_ = nullptr;
bool terminal_active_ = false;
bool waiting_key_to_exit_ = false;
bool cursor_blink_visible_ = false;
Expand Down Expand Up @@ -240,6 +243,11 @@ class UISTPage : public AppPage
void process_bytes(const char *data, int length);

void create_ui();
void create_help();
void show_help();
void hide_help();
void scroll_help(int direction);
bool help_visible() const;
bool renderer_ready() const;
void bind_events();
void detach_renderer_callbacks();
Expand Down
Loading
Loading