#include "ui.h" #include "host_info.h" #include "pins.h" #include #include #include static constexpr uint32_t COL_BG = 0x0B121A; static constexpr uint32_t COL_INK = 0xE8EEF4; static constexpr uint32_t COL_MUTED = 0x7A8B9C; static constexpr uint32_t COL_LINE = 0x1A2734; static constexpr uint32_t COL_OK = 0x3DDC97; static constexpr uint32_t COL_WARN = 0xE8B84A; static constexpr uint32_t COL_BAD = 0xE85D4C; static constexpr uint32_t COL_ACCENT = 0x4FC3F7; static constexpr uint32_t COL_BAR_BG = 0x162230; struct MetricRow { lv_obj_t *lbl_tag; lv_obj_t *lbl_name; lv_obj_t *lbl_pct; lv_obj_t *bar; }; static lv_obj_t *scr = nullptr; static lv_obj_t *lbl_title = nullptr; static lv_obj_t *lbl_host = nullptr; static lv_obj_t *lbl_status = nullptr; static MetricRow row_cpu = {}; static MetricRow row_gpu = {}; static MetricRow row_ram = {}; static MetricRow row_disk = {}; static uint32_t last_paint_ms = 0; static int pct_of(float used, float total) { if (total <= 0.01f) { return 0; } int p = (int)((used * 100.0f) / total + 0.5f); if (p < 0) { p = 0; } if (p > 100) { p = 100; } return p; } static uint32_t color_for_pct(int pct) { if (pct >= 85) { return COL_BAD; } if (pct >= 65) { return COL_WARN; } return COL_OK; } static void style_bar(lv_obj_t *bar) { lv_obj_set_style_bg_color(bar, lv_color_hex(COL_BAR_BG), LV_PART_MAIN); lv_obj_set_style_bg_opa(bar, LV_OPA_COVER, LV_PART_MAIN); lv_obj_set_style_radius(bar, 4, LV_PART_MAIN); lv_obj_set_style_pad_all(bar, 0, LV_PART_MAIN); lv_obj_set_style_bg_color(bar, lv_color_hex(COL_OK), LV_PART_INDICATOR); lv_obj_set_style_bg_opa(bar, LV_OPA_COVER, LV_PART_INDICATOR); lv_obj_set_style_radius(bar, 4, LV_PART_INDICATOR); lv_bar_set_range(bar, 0, 100); lv_bar_set_value(bar, 0, LV_ANIM_OFF); } static MetricRow make_row(lv_obj_t *parent, const char *tag, int y) { MetricRow r = {}; r.lbl_tag = lv_label_create(parent); lv_label_set_text(r.lbl_tag, tag); lv_obj_set_style_text_color(r.lbl_tag, lv_color_hex(COL_ACCENT), 0); lv_obj_set_style_text_font(r.lbl_tag, &lv_font_montserrat_14, 0); lv_obj_set_pos(r.lbl_tag, UX(8), UY(y)); r.lbl_name = lv_label_create(parent); lv_label_set_text(r.lbl_name, "—"); lv_label_set_long_mode(r.lbl_name, LV_LABEL_LONG_DOT); lv_obj_set_width(r.lbl_name, UW(190)); lv_obj_set_style_text_color(r.lbl_name, lv_color_hex(COL_INK), 0); lv_obj_set_style_text_font(r.lbl_name, &lv_font_montserrat_14, 0); lv_obj_set_pos(r.lbl_name, UX(52), UY(y)); r.lbl_pct = lv_label_create(parent); lv_label_set_text(r.lbl_pct, "--%"); lv_obj_set_style_text_color(r.lbl_pct, lv_color_hex(COL_MUTED), 0); lv_obj_set_style_text_font(r.lbl_pct, &lv_font_montserrat_14, 0); lv_obj_align(r.lbl_pct, LV_ALIGN_TOP_RIGHT, UX(-8), UY(y)); r.bar = lv_bar_create(parent); lv_obj_set_size(r.bar, UI_HOR_RES - UW(16), UH(10)); lv_obj_set_pos(r.bar, UX(8), UY(y + 18)); style_bar(r.bar); return r; } static void set_row_pct(MetricRow &r, int pct, const char *name, const char *right) { if (r.lbl_name && name) { lv_label_set_text(r.lbl_name, name); } if (r.lbl_pct && right) { lv_label_set_text(r.lbl_pct, right); lv_obj_set_style_text_color(r.lbl_pct, lv_color_hex(color_for_pct(pct)), 0); } if (r.bar) { lv_bar_set_value(r.bar, pct, LV_ANIM_OFF); lv_obj_set_style_bg_color(r.bar, lv_color_hex(color_for_pct(pct)), LV_PART_INDICATOR); } } void ui_create() { scr = lv_obj_create(nullptr); lv_obj_set_style_bg_color(scr, lv_color_hex(COL_BG), 0); lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0); lv_obj_clear_flag(scr, LV_OBJ_FLAG_SCROLLABLE); lv_screen_load(scr); lbl_title = lv_label_create(scr); lv_label_set_text(lbl_title, "PC Monitor"); lv_obj_set_style_text_color(lbl_title, lv_color_hex(COL_INK), 0); lv_obj_set_style_text_font(lbl_title, &lv_font_montserrat_18, 0); lv_obj_set_pos(lbl_title, UX(8), UY(6)); lbl_status = lv_label_create(scr); lv_label_set_text(lbl_status, "waiting"); lv_obj_set_style_text_color(lbl_status, lv_color_hex(COL_WARN), 0); lv_obj_set_style_text_font(lbl_status, &lv_font_montserrat_14, 0); lv_obj_align(lbl_status, LV_ALIGN_TOP_RIGHT, UX(-8), UY(8)); lbl_host = lv_label_create(scr); lv_label_set_text(lbl_host, "Connect USB + run host script"); lv_label_set_long_mode(lbl_host, LV_LABEL_LONG_DOT); lv_obj_set_width(lbl_host, UI_HOR_RES - UW(16)); lv_obj_set_style_text_color(lbl_host, lv_color_hex(COL_MUTED), 0); lv_obj_set_style_text_font(lbl_host, &lv_font_montserrat_14, 0); lv_obj_set_pos(lbl_host, UX(8), UY(28)); lv_obj_t *line = lv_obj_create(scr); lv_obj_set_size(line, UI_HOR_RES - UW(16), 1); lv_obj_set_pos(line, UX(8), UY(48)); lv_obj_set_style_bg_color(line, lv_color_hex(COL_LINE), 0); lv_obj_set_style_bg_opa(line, LV_OPA_COVER, 0); lv_obj_set_style_border_width(line, 0, 0); lv_obj_set_style_pad_all(line, 0, 0); lv_obj_clear_flag(line, LV_OBJ_FLAG_SCROLLABLE); row_cpu = make_row(scr, "CPU", 56); row_gpu = make_row(scr, "GPU", 96); row_ram = make_row(scr, "RAM", 136); row_disk = make_row(scr, "DISK", 176); last_paint_ms = 0; } static void paint() { const HostInfo &h = host_info_get(); const bool live = host_info_is_live(5000); if (lbl_status) { if (!h.valid) { lv_label_set_text(lbl_status, "waiting"); lv_obj_set_style_text_color(lbl_status, lv_color_hex(COL_WARN), 0); } else if (live) { lv_label_set_text(lbl_status, "live"); lv_obj_set_style_text_color(lbl_status, lv_color_hex(COL_OK), 0); } else { lv_label_set_text(lbl_status, "stale"); lv_obj_set_style_text_color(lbl_status, lv_color_hex(COL_BAD), 0); } } if (!h.valid) { return; } if (lbl_host) { lv_label_set_text(lbl_host, h.host[0] ? h.host : "(unknown host)"); } char buf[48]; int cpu_pct = (int)(h.cpu_pct + 0.5f); snprintf(buf, sizeof(buf), "%d%%", cpu_pct); set_row_pct(row_cpu, cpu_pct, h.cpu[0] ? h.cpu : "CPU", buf); if (h.gpu_pct < 0) { set_row_pct(row_gpu, 0, h.gpu[0] ? h.gpu : "GPU", "n/a"); if (row_gpu.lbl_pct) { lv_obj_set_style_text_color(row_gpu.lbl_pct, lv_color_hex(COL_MUTED), 0); } } else { int gpu_pct = (int)(h.gpu_pct + 0.5f); snprintf(buf, sizeof(buf), "%d%%", gpu_pct); set_row_pct(row_gpu, gpu_pct, h.gpu[0] ? h.gpu : "GPU", buf); } int ram_pct = pct_of(h.ram_used_gb, h.ram_total_gb); snprintf(buf, sizeof(buf), "%.1f/%.0fG", (double)h.ram_used_gb, (double)h.ram_total_gb); set_row_pct(row_ram, ram_pct, "Memory", buf); int disk_pct = pct_of(h.disk_used_gb, h.disk_total_gb); snprintf(buf, sizeof(buf), "%.0f/%.0fG", (double)h.disk_used_gb, (double)h.disk_total_gb); set_row_pct(row_disk, disk_pct, "System drive", buf); } void ui_tick() { const uint32_t now = millis(); if (now - last_paint_ms < 200) { return; } last_paint_ms = now; paint(); }