a158fe1696
- Restore MCP system (McpSystem 1900+ LOC, McpHandler, McpScreensaver, McpSettings, McpOverride apps) - Use audio-stream instead of direct I2S for MP3 playback (fixes fast playback, uses native resampler) - Register McpSettings + McpOverride in Tactility.cpp so Settings shows MCP Screen - DisplaySettings: add ScreensaverType::McpScreen and disableScreensaverWhenCharging flag - Display app: add 'Disable on charging' toggle, handles McpScreen screensaver type - DisplayIdle: skip screensaver while charging, support McpScreen screensaver, isDeviceCharging check via PowerDevice - WebServer: coexistence with MCP (McpHandler) - Avoid formatting churn from previous commits Squashed from range eaa248b0..8970143f (6 commits) into single clean commit
101 lines
3.6 KiB
C++
101 lines
3.6 KiB
C++
#ifdef ESP_PLATFORM
|
|
|
|
#include "McpScreensaver.h"
|
|
#include <Tactility/mcp/McpSystem.h>
|
|
#include <tactility/log.h>
|
|
|
|
constexpr auto* TAG = "McpScreensaver";
|
|
#include <esp_heap_caps.h>
|
|
|
|
namespace tt::service::displayidle {
|
|
|
|
|
|
void McpScreensaver::start(lv_obj_t* overlay, lv_coord_t screenW, lv_coord_t screenH) {
|
|
auto& state = mcp::getState();
|
|
|
|
// Full-screen canvas on the overlay
|
|
lv_obj_t* canvas = lv_canvas_create(overlay);
|
|
lv_obj_set_size(canvas, screenW, screenH);
|
|
lv_obj_set_pos(canvas, 0, 0);
|
|
lv_obj_set_style_radius(canvas, 0, LV_PART_MAIN);
|
|
lv_obj_set_style_border_width(canvas, 0, LV_PART_MAIN);
|
|
lv_obj_set_style_pad_all(canvas, 0, LV_PART_MAIN);
|
|
lv_obj_remove_flag(canvas, LV_OBJ_FLAG_SCROLLABLE);
|
|
|
|
// Allocate framebuffer (prefer SPIRAM)
|
|
size_t requiredSize = (size_t)screenW * screenH * sizeof(uint16_t);
|
|
framebuffer = (uint16_t*)heap_caps_malloc(requiredSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
|
if (framebuffer == nullptr) {
|
|
framebuffer = (uint16_t*)heap_caps_malloc(requiredSize, MALLOC_CAP_8BIT);
|
|
}
|
|
framebufferSize = (framebuffer != nullptr) ? requiredSize : 0;
|
|
|
|
if (framebuffer == nullptr) {
|
|
LOG_E(TAG, "Failed to allocate %uB framebuffer", (unsigned)requiredSize);
|
|
lv_obj_t* err = lv_label_create(canvas);
|
|
lv_label_set_text(err, "Framebuffer alloc failed");
|
|
lv_obj_center(err);
|
|
return;
|
|
}
|
|
|
|
// Fill with a dark slate background (inverted for display path)
|
|
size_t pixelCount = (size_t)screenW * screenH;
|
|
for (size_t i = 0; i < pixelCount; ++i) {
|
|
framebuffer[i] = ~0x18E3; // dark blue-grey
|
|
}
|
|
|
|
lv_canvas_set_buffer(canvas, framebuffer, screenW, screenH, LV_COLOR_FORMAT_RGB565);
|
|
|
|
// Waiting label (removed on first MCP draw via lv_obj_clean)
|
|
lv_obj_t* waitLabel = lv_label_create(canvas);
|
|
lv_label_set_text(waitLabel, "Waiting for LLM...");
|
|
lv_obj_set_style_text_color(waitLabel, lv_color_black(), LV_PART_MAIN); // white on screen (inverted)
|
|
lv_obj_align(waitLabel, LV_ALIGN_CENTER, 0, -20);
|
|
|
|
lv_obj_t* resLabel = lv_label_create(canvas);
|
|
lv_label_set_text_fmt(resLabel, "Display: %dx%d", (int)screenW, (int)screenH);
|
|
lv_color_t resColor = lv_palette_lighten(LV_PALETTE_BLUE, 3);
|
|
lv_obj_set_style_text_color(resLabel, lv_color_make(~resColor.red, ~resColor.green, ~resColor.blue), LV_PART_MAIN);
|
|
lv_obj_align(resLabel, LV_ALIGN_CENTER, 0, 10);
|
|
|
|
// Register with McpSystemState
|
|
std::lock_guard<std::mutex> lock(state.mutex);
|
|
state.drawArea = canvas;
|
|
state.framebuffer = framebuffer;
|
|
state.framebufferSize = framebufferSize;
|
|
state.displayWidth = (uint16_t)screenW;
|
|
state.displayHeight = (uint16_t)screenH;
|
|
state.drawWidth = (uint16_t)screenW;
|
|
state.drawHeight = (uint16_t)screenH;
|
|
// Don't reset overrideActive — if the LLM already drew, we keep the content
|
|
|
|
LOG_I(TAG, "McpScreensaver started (%dx%d)", (int)screenW, (int)screenH);
|
|
}
|
|
|
|
void McpScreensaver::stop() {
|
|
auto& state = mcp::getState();
|
|
{
|
|
std::lock_guard<std::mutex> lock(state.mutex);
|
|
state.drawArea = nullptr;
|
|
state.framebuffer = nullptr;
|
|
state.framebufferSize = 0;
|
|
state.overrideActive = false;
|
|
}
|
|
|
|
if (framebuffer != nullptr) {
|
|
heap_caps_free(framebuffer);
|
|
framebuffer = nullptr;
|
|
framebufferSize = 0;
|
|
}
|
|
|
|
LOG_I(TAG, "McpScreensaver stopped");
|
|
}
|
|
|
|
void McpScreensaver::update(lv_coord_t /*screenW*/, lv_coord_t /*screenH*/) {
|
|
// MCP draws on demand via HTTP — no per-frame animation needed
|
|
}
|
|
|
|
} // namespace tt::service::displayidle
|
|
|
|
#endif // ESP_PLATFORM
|