#ifdef ESP_PLATFORM #include #include #include #include #include #include #include #include namespace tt::app::mcpoverride { static const auto LOGGER = Logger("McpOverrideApp"); 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 { LOGGER.info("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) { LOGGER.error("Failed to allocate {} 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 { LOGGER.info("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 }; } // namespace #endif // ESP_PLATFORM