feat: add Tactility MCP Screen foundation
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
#include "McpScreen.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <tt_lvgl.h>
|
||||
#include <tt_lvgl_toolbar.h>
|
||||
#include <tt_wifi.h>
|
||||
|
||||
static const char* TAG = "McpScreen";
|
||||
|
||||
static void set_label_text_locked(lv_obj_t* label, const char* text) {
|
||||
if (label != NULL && lv_obj_is_valid(label)) {
|
||||
lv_label_set_text(label, text);
|
||||
}
|
||||
}
|
||||
|
||||
void mcp_ui_set_server_status(McpScreenState* state, const char* status) {
|
||||
if (state == NULL || !state->visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tt_lvgl_lock(pdMS_TO_TICKS(1000))) {
|
||||
if (state->visible) {
|
||||
set_label_text_locked(state->server_label, status);
|
||||
}
|
||||
tt_lvgl_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void mcp_ui_set_last_tool(McpScreenState* state, const char* tool) {
|
||||
if (state == NULL || !state->visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tt_lvgl_lock(pdMS_TO_TICKS(1000))) {
|
||||
if (state->visible && state->tool_label != NULL && lv_obj_is_valid(state->tool_label)) {
|
||||
lv_label_set_text_fmt(state->tool_label, "Last: %s", tool);
|
||||
}
|
||||
tt_lvgl_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
bool mcp_ui_clear(McpScreenState* state, int color) {
|
||||
if (state == NULL || !state->visible) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
if (tt_lvgl_lock(pdMS_TO_TICKS(1500))) {
|
||||
if (state->visible && state->draw_area != NULL && lv_obj_is_valid(state->draw_area)) {
|
||||
lv_obj_clean(state->draw_area);
|
||||
lv_obj_set_style_bg_color(
|
||||
state->draw_area,
|
||||
color == 1 ? lv_color_black() : lv_color_white(),
|
||||
LV_PART_MAIN
|
||||
);
|
||||
lv_obj_set_style_bg_opa(state->draw_area, LV_OPA_COVER, LV_PART_MAIN);
|
||||
state->draw_color = color;
|
||||
state->override_active = true;
|
||||
success = true;
|
||||
}
|
||||
tt_lvgl_unlock();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool mcp_ui_draw_text(
|
||||
McpScreenState* state,
|
||||
const char* text,
|
||||
int x,
|
||||
int y,
|
||||
int size
|
||||
) {
|
||||
if (state == NULL || text == NULL || !state->visible) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
if (tt_lvgl_lock(pdMS_TO_TICKS(1500))) {
|
||||
if (state->visible && state->draw_area != NULL && lv_obj_is_valid(state->draw_area)) {
|
||||
int max_x = state->draw_width > 0 ? state->draw_width - 1 : 0;
|
||||
int max_y = state->draw_height > 0 ? state->draw_height - 1 : 0;
|
||||
if (x < 0) x = 0;
|
||||
if (y < 0) y = 0;
|
||||
if (x > max_x) x = max_x;
|
||||
if (y > max_y) y = max_y;
|
||||
|
||||
lv_obj_t* label = lv_label_create(state->draw_area);
|
||||
lv_label_set_text(label, text);
|
||||
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
|
||||
lv_obj_set_width(label, LV_MAX(1, state->draw_width - x));
|
||||
lv_obj_set_pos(label, x, y);
|
||||
|
||||
lv_obj_set_style_text_color(
|
||||
label,
|
||||
state->draw_color == 0 ? lv_color_black() : lv_color_white(),
|
||||
LV_PART_MAIN
|
||||
);
|
||||
|
||||
#if LV_FONT_MONTSERRAT_24
|
||||
if (size >= 2) {
|
||||
lv_obj_set_style_text_font(label, &lv_font_montserrat_24, LV_PART_MAIN);
|
||||
}
|
||||
#else
|
||||
(void)size;
|
||||
#endif
|
||||
|
||||
state->override_active = true;
|
||||
success = true;
|
||||
}
|
||||
tt_lvgl_unlock();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
static void* create_data(void) {
|
||||
McpScreenState* state = calloc(1, sizeof(McpScreenState));
|
||||
if (state != NULL) {
|
||||
state->http_socket = -1;
|
||||
state->client_socket = -1;
|
||||
state->discovery_socket = -1;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
static void destroy_data(void* data) {
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void on_create(AppHandle app, void* data) {
|
||||
McpScreenState* state = data;
|
||||
state->app = app;
|
||||
if (!mcp_services_start(state)) {
|
||||
ESP_LOGE(TAG, "One or more MCP services failed to start");
|
||||
}
|
||||
}
|
||||
|
||||
static void on_destroy(AppHandle app, void* data) {
|
||||
(void)app;
|
||||
mcp_services_stop((McpScreenState*)data);
|
||||
}
|
||||
|
||||
static void on_show(AppHandle app, void* data, lv_obj_t* parent) {
|
||||
McpScreenState* state = data;
|
||||
state->visible = true;
|
||||
|
||||
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);
|
||||
|
||||
lv_obj_t* toolbar = tt_lvgl_toolbar_create_for_app(parent, app);
|
||||
lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0);
|
||||
|
||||
state->server_label = lv_label_create(toolbar);
|
||||
lv_label_set_text(state->server_label, state->http_ready ? "MCP :80 ready" : "MCP starting");
|
||||
lv_obj_set_style_text_color(
|
||||
state->server_label,
|
||||
state->http_ready ? lv_palette_main(LV_PALETTE_GREEN) : lv_palette_main(LV_PALETTE_ORANGE),
|
||||
LV_PART_MAIN
|
||||
);
|
||||
|
||||
state->tool_label = lv_label_create(toolbar);
|
||||
lv_label_set_text(state->tool_label, "Last: none");
|
||||
|
||||
state->draw_area = lv_obj_create(parent);
|
||||
lv_obj_set_width(state->draw_area, LV_PCT(100));
|
||||
lv_obj_set_flex_grow(state->draw_area, 1);
|
||||
lv_obj_set_style_radius(state->draw_area, 0, LV_PART_MAIN);
|
||||
lv_obj_set_style_border_width(state->draw_area, 0, LV_PART_MAIN);
|
||||
lv_obj_set_style_pad_all(state->draw_area, 0, LV_PART_MAIN);
|
||||
lv_obj_remove_flag(state->draw_area, LV_OBJ_FLAG_SCROLLABLE);
|
||||
|
||||
lv_display_t* display = lv_obj_get_display(parent);
|
||||
state->display_width = lv_display_get_horizontal_resolution(display);
|
||||
state->display_height = lv_display_get_vertical_resolution(display);
|
||||
|
||||
lv_obj_update_layout(parent);
|
||||
state->draw_width = lv_obj_get_content_width(state->draw_area);
|
||||
state->draw_height = lv_obj_get_content_height(state->draw_area);
|
||||
|
||||
if (!state->override_active) {
|
||||
lv_obj_set_style_bg_color(state->draw_area, lv_color_hex(0x101820), LV_PART_MAIN);
|
||||
lv_obj_set_style_bg_opa(state->draw_area, LV_OPA_COVER, LV_PART_MAIN);
|
||||
state->draw_color = 1;
|
||||
|
||||
lv_obj_t* title = lv_label_create(state->draw_area);
|
||||
lv_label_set_text(title, "MCP Screen");
|
||||
lv_obj_set_style_text_color(title, lv_color_white(), LV_PART_MAIN);
|
||||
#if LV_FONT_MONTSERRAT_24
|
||||
lv_obj_set_style_text_font(title, &lv_font_montserrat_24, LV_PART_MAIN);
|
||||
#endif
|
||||
lv_obj_align(title, LV_ALIGN_CENTER, 0, -55);
|
||||
|
||||
lv_obj_t* dimensions = lv_label_create(state->draw_area);
|
||||
lv_label_set_text_fmt(
|
||||
dimensions,
|
||||
"%ux%u display\nHTTP :80\nDiscovery: bridge subnet scan",
|
||||
state->display_width,
|
||||
state->display_height
|
||||
);
|
||||
lv_obj_set_style_text_align(dimensions, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
|
||||
lv_obj_set_style_text_color(dimensions, lv_palette_lighten(LV_PALETTE_BLUE, 3), LV_PART_MAIN);
|
||||
lv_obj_align(dimensions, LV_ALIGN_CENTER, 0, 0);
|
||||
|
||||
lv_obj_t* wifi = lv_label_create(state->draw_area);
|
||||
lv_label_set_text_fmt(
|
||||
wifi,
|
||||
"Wi-Fi: %s",
|
||||
tt_wifi_radio_state_to_string(tt_wifi_get_radio_state())
|
||||
);
|
||||
lv_obj_set_style_text_color(wifi, lv_color_white(), LV_PART_MAIN);
|
||||
lv_obj_align(wifi, LV_ALIGN_CENTER, 0, 48);
|
||||
}
|
||||
}
|
||||
|
||||
static void on_hide(AppHandle app, void* data) {
|
||||
(void)app;
|
||||
McpScreenState* state = data;
|
||||
state->visible = false;
|
||||
state->draw_area = NULL;
|
||||
state->server_label = NULL;
|
||||
state->tool_label = NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
tt_app_register((AppRegistration) {
|
||||
.createData = create_data,
|
||||
.destroyData = destroy_data,
|
||||
.onCreate = on_create,
|
||||
.onDestroy = on_destroy,
|
||||
.onShow = on_show,
|
||||
.onHide = on_hide
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user