Files
tactility/Tactility/Source/app/mcpoverride/McpOverrideApp.cpp
T
Adolfo Reyna e3eb3fd415 feat(mcp): restore MCP system with native log.h
- MCP 3.3K LOC: McpSystem 1899 + McpHandler 1029 + McpScreensaver + 2 apps
- Uses native tactility/log.h TAG macros, no old Logger.h
- DisplaySettings McpScreen enum, WebServer coexistence (MCP enabled starts HTTP)
- DisplayIdle: startMcpScreensaver + isDeviceCharging + lock inversion fix 3629ffef
- LVGL 512K PSRAM cache retained from previous perf patch
- Audio kept upstream es8311-module per note we might not need custom
2026-07-18 17:29:37 -04:00

142 lines
5.2 KiB
C++

#ifdef ESP_PLATFORM
#include <Tactility/Tactility.h>
#include <Tactility/mcp/McpSystem.h>
#include <Tactility/lvgl/Toolbar.h>
#include <Tactility/lvgl/LvglSync.h>
#include <tactility/log.h>
constexpr auto* TAG = "McpOverrideApp";
#include <lvgl.h>
#include <tactility/lvgl_icon_shared.h>
#include <esp_heap_caps.h>
namespace tt::app::mcpoverride {
class McpOverrideApp final : public App {
public:
void onCreate(AppContext& app) override {
// Prepare global state
auto& state = mcp::getState();
state.overrideActive = false;
}
void onShow(AppContext& app, lv_obj_t* parent) override {
LOG_I(TAG, "onShow: Starting MCP Override display canvas");
auto& state = mcp::getState();
lv_obj_remove_flag(parent, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_all(parent, 0, LV_PART_MAIN);
lv_obj_set_style_pad_row(parent, 0, LV_PART_MAIN);
// Standard toolbar so the user can navigate back
lv_obj_t* toolbar = lvgl::toolbar_create(parent, app);
lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0);
lv_obj_t* title_label = lv_label_create(toolbar);
lv_label_set_text(title_label, "MCP Override Screen");
// Create drawing canvas
state.drawArea = lv_canvas_create(parent);
lv_obj_set_width(state.drawArea, LV_PCT(100));
lv_obj_set_flex_grow(state.drawArea, 1);
lv_obj_set_style_radius(state.drawArea, 0, LV_PART_MAIN);
lv_obj_set_style_border_width(state.drawArea, 0, LV_PART_MAIN);
lv_obj_set_style_pad_all(state.drawArea, 0, LV_PART_MAIN);
lv_obj_remove_flag(state.drawArea, LV_OBJ_FLAG_SCROLLABLE);
// Get display metrics
lv_display_t* display = lv_obj_get_display(parent);
state.displayWidth = lv_display_get_horizontal_resolution(display);
state.displayHeight = lv_display_get_vertical_resolution(display);
lv_obj_update_layout(parent);
state.drawWidth = lv_obj_get_content_width(state.drawArea);
state.drawHeight = lv_obj_get_content_height(state.drawArea);
// Allocate framebuffer
size_t required_size = (size_t)state.drawWidth * state.drawHeight * sizeof(uint16_t);
if (state.framebuffer == nullptr || state.framebufferSize != required_size) {
if (state.framebuffer != nullptr) {
heap_caps_free(state.framebuffer);
state.framebuffer = nullptr;
}
state.framebuffer = (uint16_t*)heap_caps_malloc(required_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
if (state.framebuffer == nullptr) {
state.framebuffer = (uint16_t*)heap_caps_malloc(required_size, MALLOC_CAP_8BIT);
}
state.framebufferSize = state.framebuffer == nullptr ? 0 : required_size;
}
if (state.framebuffer == nullptr) {
LOG_E(TAG, "Failed to allocate %u bytes framebuffer", (unsigned)required_size);
lv_obj_t* error = lv_label_create(state.drawArea);
lv_label_set_text(error, "Framebuffer allocation failed");
lv_obj_center(error);
return;
}
lv_canvas_set_buffer(
state.drawArea,
state.framebuffer,
state.drawWidth,
state.drawHeight,
LV_COLOR_FORMAT_RGB565
);
// Initialize welcome/waiting screen if LLM hasn't written anything yet
if (!state.overrideActive) {
// Fill with a nice dark blue/slate color
for (size_t i = 0; i < (size_t)state.drawWidth * state.drawHeight; ++i) {
state.framebuffer[i] = 0x18E3;
}
state.drawColor = 1;
lv_obj_t* welcome_label = lv_label_create(state.drawArea);
lv_label_set_text(welcome_label, "Waiting for LLM...");
lv_obj_set_style_text_color(welcome_label, lv_color_white(), LV_PART_MAIN);
lv_obj_align(welcome_label, LV_ALIGN_CENTER, 0, -20);
lv_obj_t* desc_label = lv_label_create(state.drawArea);
lv_label_set_text_fmt(desc_label, "Display Resolution: %ux%u", state.drawWidth, state.drawHeight);
lv_obj_set_style_text_color(desc_label, lv_palette_lighten(LV_PALETTE_BLUE, 3), LV_PART_MAIN);
lv_obj_align(desc_label, LV_ALIGN_CENTER, 0, 10);
}
}
void onHide(AppContext& app) override {
LOG_I(TAG, "onHide: Tearing down MCP Override canvas");
auto& state = mcp::getState();
state.drawArea = nullptr;
if (state.framebuffer != nullptr) {
heap_caps_free(state.framebuffer);
state.framebuffer = nullptr;
state.framebufferSize = 0;
}
state.overrideActive = false;
// Stop any running tone or recording to prevent stuck state
state.audioRunning = false;
}
void onDestroy(AppContext& app) override {
onHide(app);
}
};
extern const AppManifest manifest = {
.appId = "one.tactility.mcpscreen", // Keep the original appId for compatibility
.appName = "MCP Override Screen",
.appIcon = LVGL_ICON_SHARED_TOOLBAR,
.appCategory = Category::System,
.createApp = create<McpOverrideApp>
};
} // namespace
#endif // ESP_PLATFORM