796 lines
27 KiB
C
796 lines
27 KiB
C
#include "ui_screen.h"
|
|
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "lvgl.h"
|
|
|
|
#define CPU_HISTORY_POINTS 64
|
|
#define CPU_CHART_W 184
|
|
#define CPU_CHART_H 58
|
|
#define HALO_LABEL_COUNT 8
|
|
#define DISK_ROW_COUNT 4
|
|
#define CODEX_DAILY_BAR_W 8
|
|
#define CODEX_DAILY_BAR_GAP 5
|
|
#define CODEX_DAILY_BAR_BASE_Y 292
|
|
#define CODEX_DAILY_BAR_MAX_H 40
|
|
|
|
typedef enum {
|
|
CONN_USB = 0,
|
|
CONN_WIFI,
|
|
CONN_BLE,
|
|
} connection_slot_t;
|
|
|
|
static lv_obj_t *s_status_label;
|
|
static lv_obj_t *s_battery_label;
|
|
static lv_obj_t *s_conn_label;
|
|
static lv_obj_t *s_env_label;
|
|
static lv_obj_t *s_cpu_value_label;
|
|
static lv_obj_t *s_cpu_freq_label;
|
|
static lv_obj_t *s_cpu_value_halo[HALO_LABEL_COUNT];
|
|
static lv_obj_t *s_cpu_freq_halo[HALO_LABEL_COUNT];
|
|
static lv_obj_t *s_cpu_line;
|
|
static lv_point_precise_t s_cpu_points[CPU_HISTORY_POINTS];
|
|
static int s_cpu_history[CPU_HISTORY_POINTS];
|
|
static lv_obj_t *s_memory_value_label;
|
|
static lv_obj_t *s_memory_value_halo[HALO_LABEL_COUNT];
|
|
static lv_obj_t *s_memory_detail_label;
|
|
static lv_obj_t *s_memory_bar;
|
|
static lv_obj_t *s_disk_name_labels[DISK_ROW_COUNT];
|
|
static lv_obj_t *s_disk_rate_labels[DISK_ROW_COUNT];
|
|
static lv_obj_t *s_disk_bars[DISK_ROW_COUNT];
|
|
static lv_obj_t *s_net_line_label;
|
|
static lv_obj_t *s_codex_5h_label;
|
|
static lv_obj_t *s_codex_week_label;
|
|
static lv_obj_t *s_codex_5h_value_label;
|
|
static lv_obj_t *s_codex_week_value_label;
|
|
static lv_obj_t *s_codex_5h_reset_label;
|
|
static lv_obj_t *s_codex_week_reset_label;
|
|
static lv_obj_t *s_codex_token_label;
|
|
static lv_obj_t *s_codex_5h_bar;
|
|
static lv_obj_t *s_codex_week_bar;
|
|
static lv_obj_t *s_codex_daily_bars[UI_CODEX_DAILY_BAR_COUNT];
|
|
static lv_obj_t *s_zhipu_status_label;
|
|
static lv_obj_t *s_zhipu_5h_label;
|
|
static lv_obj_t *s_zhipu_week_label;
|
|
static lv_obj_t *s_zhipu_5h_value_label;
|
|
static lv_obj_t *s_zhipu_week_value_label;
|
|
static lv_obj_t *s_zhipu_5h_reset_label;
|
|
static lv_obj_t *s_zhipu_week_reset_label;
|
|
static lv_obj_t *s_zhipu_5h_bar;
|
|
static lv_obj_t *s_zhipu_week_bar;
|
|
static int s_age_seconds;
|
|
static connection_slot_t s_active_connection = CONN_USB;
|
|
|
|
typedef struct {
|
|
int x;
|
|
int y;
|
|
} halo_offset_t;
|
|
|
|
static const halo_offset_t s_halo_offsets[HALO_LABEL_COUNT] = {
|
|
{ -1, 0 },
|
|
{ 1, 0 },
|
|
{ 0, -1 },
|
|
{ 0, 1 },
|
|
{ -1, -1 },
|
|
{ 1, -1 },
|
|
{ -1, 1 },
|
|
{ 1, 1 },
|
|
};
|
|
|
|
static lv_color_t ink(void)
|
|
{
|
|
return lv_color_black();
|
|
}
|
|
|
|
static lv_color_t paper(void)
|
|
{
|
|
return lv_color_white();
|
|
}
|
|
|
|
static int clamp_percent(int value)
|
|
{
|
|
if (value < 0) {
|
|
return 0;
|
|
}
|
|
if (value > 100) {
|
|
return 100;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static lv_obj_t *make_colored_label(lv_obj_t *parent, const char *text, const lv_font_t *font, lv_color_t color)
|
|
{
|
|
lv_obj_t *label = lv_label_create(parent);
|
|
lv_label_set_text(label, text);
|
|
lv_label_set_long_mode(label, LV_LABEL_LONG_DOT);
|
|
lv_obj_set_style_text_font(label, font, 0);
|
|
lv_obj_set_style_text_color(label, color, 0);
|
|
lv_obj_set_style_pad_all(label, 0, 0);
|
|
return label;
|
|
}
|
|
|
|
static lv_obj_t *make_label(lv_obj_t *parent, const char *text, const lv_font_t *font)
|
|
{
|
|
return make_colored_label(parent, text, font, ink());
|
|
}
|
|
|
|
static void make_label_halo(lv_obj_t *parent,
|
|
lv_obj_t *halos[HALO_LABEL_COUNT],
|
|
const char *text,
|
|
const lv_font_t *font,
|
|
int width,
|
|
lv_align_t align,
|
|
int x,
|
|
int y,
|
|
int spread,
|
|
lv_text_align_t text_align)
|
|
{
|
|
for (int i = 0; i < HALO_LABEL_COUNT; i++) {
|
|
halos[i] = make_colored_label(parent, text, font, paper());
|
|
lv_obj_set_width(halos[i], width);
|
|
lv_obj_set_style_text_align(halos[i], text_align, 0);
|
|
lv_obj_align(halos[i], align, x + s_halo_offsets[i].x * spread, y + s_halo_offsets[i].y * spread);
|
|
}
|
|
}
|
|
|
|
static void set_label_with_halo(lv_obj_t *label, lv_obj_t *halos[HALO_LABEL_COUNT], const char *text)
|
|
{
|
|
for (int i = 0; i < HALO_LABEL_COUNT; i++) {
|
|
if (halos[i] != NULL) {
|
|
lv_label_set_text(halos[i], text);
|
|
}
|
|
}
|
|
lv_label_set_text(label, text);
|
|
}
|
|
|
|
static void set_bar_percent(lv_obj_t *bar, int percent)
|
|
{
|
|
if (bar == NULL) {
|
|
return;
|
|
}
|
|
lv_bar_set_value(bar, percent >= 0 ? clamp_percent(percent) : 0, LV_ANIM_OFF);
|
|
}
|
|
|
|
static lv_obj_t *make_rule(lv_obj_t *parent, int x, int y, int w, int h)
|
|
{
|
|
lv_obj_t *rule = lv_obj_create(parent);
|
|
lv_obj_set_size(rule, w, h);
|
|
lv_obj_align(rule, LV_ALIGN_TOP_LEFT, x, y);
|
|
lv_obj_set_style_bg_color(rule, ink(), 0);
|
|
lv_obj_set_style_bg_opa(rule, LV_OPA_COVER, 0);
|
|
lv_obj_set_style_border_width(rule, 0, 0);
|
|
lv_obj_set_style_radius(rule, 0, 0);
|
|
lv_obj_set_style_pad_all(rule, 0, 0);
|
|
lv_obj_clear_flag(rule, LV_OBJ_FLAG_SCROLLABLE);
|
|
return rule;
|
|
}
|
|
|
|
static lv_obj_t *make_bar(lv_obj_t *parent, int w, int h)
|
|
{
|
|
lv_obj_t *bar = lv_bar_create(parent);
|
|
lv_obj_set_size(bar, w, h);
|
|
lv_bar_set_range(bar, 0, 100);
|
|
lv_bar_set_value(bar, 0, LV_ANIM_OFF);
|
|
lv_obj_set_style_bg_color(bar, paper(), 0);
|
|
lv_obj_set_style_border_color(bar, ink(), 0);
|
|
lv_obj_set_style_border_width(bar, 1, 0);
|
|
lv_obj_set_style_radius(bar, 1, 0);
|
|
lv_obj_set_style_bg_color(bar, ink(), LV_PART_INDICATOR);
|
|
lv_obj_set_style_radius(bar, 1, LV_PART_INDICATOR);
|
|
return bar;
|
|
}
|
|
|
|
static void format_capacity(int mb, char *out, size_t out_size)
|
|
{
|
|
if (mb <= 0) {
|
|
snprintf(out, out_size, "--");
|
|
return;
|
|
}
|
|
|
|
if (mb >= 1024) {
|
|
int gb10 = (mb * 10 + 512) / 1024;
|
|
snprintf(out, out_size, "%d.%dGB", gb10 / 10, gb10 % 10);
|
|
} else {
|
|
snprintf(out, out_size, "%dMB", mb);
|
|
}
|
|
}
|
|
|
|
static void format_rate_short(int kbps, char *out, size_t out_size)
|
|
{
|
|
if (kbps < 0) {
|
|
snprintf(out, out_size, "--");
|
|
return;
|
|
}
|
|
|
|
if (kbps >= 1024 * 1024) {
|
|
int gb10 = (kbps * 10 + (1024 * 512)) / (1024 * 1024);
|
|
if (gb10 >= 100) {
|
|
snprintf(out, out_size, "%dG", gb10 / 10);
|
|
} else {
|
|
snprintf(out, out_size, "%d.%dG", gb10 / 10, gb10 % 10);
|
|
}
|
|
} else if (kbps >= 1000) {
|
|
int mb10 = (kbps * 10 + 512) / 1024;
|
|
if (mb10 >= 100) {
|
|
snprintf(out, out_size, "%dM", mb10 / 10);
|
|
} else {
|
|
snprintf(out, out_size, "%d.%dM", mb10 / 10, mb10 % 10);
|
|
}
|
|
} else {
|
|
snprintf(out, out_size, "%dK", kbps);
|
|
}
|
|
}
|
|
|
|
static void format_reset_mins(int mins, char *out, size_t out_size)
|
|
{
|
|
if (mins < 0) {
|
|
snprintf(out, out_size, "--");
|
|
} else if (mins >= 1440) {
|
|
int days = mins / 1440;
|
|
int hours = (mins % 1440) / 60;
|
|
snprintf(out, out_size, "%dd%dh", days, hours);
|
|
} else if (mins >= 60) {
|
|
snprintf(out, out_size, "%dh%02d", mins / 60, mins % 60);
|
|
} else {
|
|
snprintf(out, out_size, "%dm", mins);
|
|
}
|
|
}
|
|
|
|
static void format_percent_compact(int percent, char *out, size_t out_size)
|
|
{
|
|
if (percent < 0) {
|
|
snprintf(out, out_size, "--");
|
|
} else {
|
|
snprintf(out, out_size, "%d%%", clamp_percent(percent));
|
|
}
|
|
}
|
|
|
|
static void format_tokens_m10(int tokens_m10, char *out, size_t out_size)
|
|
{
|
|
if (tokens_m10 < 0) {
|
|
snprintf(out, out_size, "--");
|
|
} else if (tokens_m10 >= 10000) {
|
|
snprintf(out, out_size, "%d.%dB", tokens_m10 / 10000, (tokens_m10 % 10000) / 1000);
|
|
} else if (tokens_m10 >= 1000) {
|
|
snprintf(out, out_size, "%dM", (tokens_m10 + 5) / 10);
|
|
} else {
|
|
snprintf(out, out_size, "%d.%dM", tokens_m10 / 10, tokens_m10 % 10);
|
|
}
|
|
}
|
|
|
|
static const char *zhipu_status_text(int status)
|
|
{
|
|
switch (status) {
|
|
case 1:
|
|
return "KEY";
|
|
case 2:
|
|
return "ERR";
|
|
default:
|
|
return "WAIT";
|
|
}
|
|
}
|
|
|
|
static const char *connection_symbol(connection_slot_t active)
|
|
{
|
|
switch (active) {
|
|
case CONN_BLE:
|
|
return LV_SYMBOL_BLUETOOTH;
|
|
case CONN_WIFI:
|
|
return LV_SYMBOL_WIFI;
|
|
case CONN_USB:
|
|
default:
|
|
return LV_SYMBOL_USB;
|
|
}
|
|
}
|
|
|
|
static void set_connection(connection_slot_t active)
|
|
{
|
|
s_active_connection = active;
|
|
if (s_conn_label != NULL) {
|
|
lv_label_set_text(s_conn_label, connection_symbol(active));
|
|
}
|
|
}
|
|
|
|
static connection_slot_t connection_from_source(const char *source)
|
|
{
|
|
if (source == NULL) {
|
|
return CONN_USB;
|
|
}
|
|
if (strstr(source, "BLE") != NULL) {
|
|
return CONN_BLE;
|
|
}
|
|
if (strstr(source, "WIFI") != NULL || strstr(source, "AP") != NULL) {
|
|
return CONN_WIFI;
|
|
}
|
|
return CONN_USB;
|
|
}
|
|
|
|
static void set_history_curve(int *history, lv_point_precise_t *points, int count,
|
|
int chart_w, int chart_h, int percent, lv_obj_t *line)
|
|
{
|
|
for (int i = 0; i < count - 1; i++) {
|
|
history[i] = history[i + 1];
|
|
}
|
|
history[count - 1] = clamp_percent(percent);
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
points[i].x = (i * chart_w) / (count - 1);
|
|
points[i].y = chart_h - ((history[i] * chart_h) / 100);
|
|
}
|
|
|
|
lv_line_set_points_mutable(line, points, count);
|
|
lv_obj_invalidate(line);
|
|
}
|
|
|
|
static void set_cpu_curve(int cpu_percent)
|
|
{
|
|
set_history_curve(s_cpu_history, s_cpu_points, CPU_HISTORY_POINTS,
|
|
CPU_CHART_W, CPU_CHART_H, cpu_percent, s_cpu_line);
|
|
}
|
|
|
|
static int parse_int_field(char *field, int fallback)
|
|
{
|
|
if (field == NULL || field[0] == '\0') {
|
|
return fallback;
|
|
}
|
|
|
|
char *end = NULL;
|
|
long value = strtol(field, &end, 10);
|
|
if (end == field) {
|
|
return fallback;
|
|
}
|
|
|
|
return (int)value;
|
|
}
|
|
|
|
static int parse_disk_token(const char *token, char *name, size_t name_size,
|
|
int *percent, int *read_kbps, int *write_kbps)
|
|
{
|
|
char buffer[40];
|
|
snprintf(buffer, sizeof(buffer), "%s", token);
|
|
|
|
char *cursor = buffer;
|
|
char *disk_name = strsep(&cursor, ":");
|
|
char *usage = strsep(&cursor, ":");
|
|
char *read_rate = strsep(&cursor, ":");
|
|
char *write_rate = strsep(&cursor, ":");
|
|
|
|
if (disk_name == NULL || disk_name[0] == '\0' || usage == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
size_t len = strlen(disk_name);
|
|
if (len >= name_size) {
|
|
len = name_size - 1;
|
|
}
|
|
memcpy(name, disk_name, len);
|
|
name[len] = '\0';
|
|
|
|
*percent = clamp_percent(parse_int_field(usage, 0));
|
|
*read_kbps = parse_int_field(read_rate, -1);
|
|
*write_kbps = parse_int_field(write_rate, -1);
|
|
return 1;
|
|
}
|
|
|
|
static void update_disk_rows(const char *disk_text)
|
|
{
|
|
char buffer[UI_MAX_DISK_TEXT];
|
|
char name[8];
|
|
int row = 0;
|
|
|
|
snprintf(buffer, sizeof(buffer), "%s", disk_text && disk_text[0] ? disk_text : "");
|
|
char *cursor = buffer;
|
|
char *token = strtok(cursor, " ");
|
|
while (token != NULL && row < DISK_ROW_COUNT) {
|
|
int percent = 0;
|
|
int read_kbps = -1;
|
|
int write_kbps = -1;
|
|
if (parse_disk_token(token, name, sizeof(name), &percent, &read_kbps, &write_kbps)) {
|
|
char text[28];
|
|
char read[10];
|
|
char write[10];
|
|
snprintf(text, sizeof(text), "%s", name);
|
|
lv_label_set_text(s_disk_name_labels[row], text);
|
|
format_rate_short(read_kbps, read, sizeof(read));
|
|
format_rate_short(write_kbps, write, sizeof(write));
|
|
snprintf(text, sizeof(text), "%s/%s", read, write);
|
|
lv_label_set_text(s_disk_rate_labels[row], text);
|
|
lv_bar_set_value(s_disk_bars[row], percent, LV_ANIM_OFF);
|
|
lv_obj_clear_flag(s_disk_name_labels[row], LV_OBJ_FLAG_HIDDEN);
|
|
lv_obj_clear_flag(s_disk_rate_labels[row], LV_OBJ_FLAG_HIDDEN);
|
|
lv_obj_clear_flag(s_disk_bars[row], LV_OBJ_FLAG_HIDDEN);
|
|
row++;
|
|
}
|
|
token = strtok(NULL, " ");
|
|
}
|
|
|
|
for (int i = row; i < DISK_ROW_COUNT; i++) {
|
|
lv_obj_add_flag(s_disk_name_labels[i], LV_OBJ_FLAG_HIDDEN);
|
|
lv_obj_add_flag(s_disk_rate_labels[i], LV_OBJ_FLAG_HIDDEN);
|
|
lv_obj_add_flag(s_disk_bars[i], LV_OBJ_FLAG_HIDDEN);
|
|
}
|
|
}
|
|
|
|
static void update_codex_panel(const ui_metrics_t *metrics)
|
|
{
|
|
char text[40];
|
|
char value[12];
|
|
char reset_5h[12];
|
|
char reset_week[12];
|
|
|
|
format_percent_compact(metrics->codex_5h_percent, value, sizeof(value));
|
|
lv_label_set_text(s_codex_5h_value_label, value);
|
|
set_bar_percent(s_codex_5h_bar, metrics->codex_5h_percent);
|
|
|
|
format_percent_compact(metrics->codex_week_percent, value, sizeof(value));
|
|
lv_label_set_text(s_codex_week_value_label, value);
|
|
set_bar_percent(s_codex_week_bar, metrics->codex_week_percent);
|
|
|
|
format_reset_mins(metrics->codex_5h_reset_mins, reset_5h, sizeof(reset_5h));
|
|
format_reset_mins(metrics->codex_week_reset_mins, reset_week, sizeof(reset_week));
|
|
lv_label_set_text(s_codex_5h_reset_label, reset_5h);
|
|
lv_label_set_text(s_codex_week_reset_label, reset_week);
|
|
|
|
format_tokens_m10(metrics->codex_today_tokens_m10, value, sizeof(value));
|
|
snprintf(text, sizeof(text), "TOK %s", value);
|
|
lv_label_set_text(s_codex_token_label, text);
|
|
|
|
for (int i = 0; i < UI_CODEX_DAILY_BAR_COUNT; i++) {
|
|
lv_obj_t *bar = s_codex_daily_bars[i];
|
|
if (bar == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (i >= metrics->codex_daily_count || metrics->codex_daily_percent[i] < 0) {
|
|
lv_obj_add_flag(bar, LV_OBJ_FLAG_HIDDEN);
|
|
continue;
|
|
}
|
|
|
|
int percent = clamp_percent(metrics->codex_daily_percent[i]);
|
|
int height = percent <= 0 ? 1 : 1 + ((percent * (CODEX_DAILY_BAR_MAX_H - 1)) + 50) / 100;
|
|
int x = 10 + i * (CODEX_DAILY_BAR_W + CODEX_DAILY_BAR_GAP);
|
|
int y = CODEX_DAILY_BAR_BASE_Y - height;
|
|
lv_obj_set_size(bar, CODEX_DAILY_BAR_W, height);
|
|
lv_obj_align(bar, LV_ALIGN_TOP_LEFT, x, y);
|
|
lv_obj_clear_flag(bar, LV_OBJ_FLAG_HIDDEN);
|
|
}
|
|
}
|
|
|
|
static void update_zhipu_panel(const ui_metrics_t *metrics)
|
|
{
|
|
char value[12];
|
|
char reset[12];
|
|
char text[24];
|
|
|
|
snprintf(text, sizeof(text), "STAT %s", zhipu_status_text(metrics->zhipu_status));
|
|
lv_label_set_text(s_zhipu_status_label, text);
|
|
|
|
format_percent_compact(metrics->zhipu_5h_percent, value, sizeof(value));
|
|
lv_label_set_text(s_zhipu_5h_value_label, value);
|
|
set_bar_percent(s_zhipu_5h_bar, metrics->zhipu_5h_percent);
|
|
format_reset_mins(metrics->zhipu_5h_reset_mins, reset, sizeof(reset));
|
|
lv_label_set_text(s_zhipu_5h_reset_label, reset);
|
|
|
|
format_percent_compact(metrics->zhipu_week_percent, value, sizeof(value));
|
|
lv_label_set_text(s_zhipu_week_value_label, value);
|
|
set_bar_percent(s_zhipu_week_bar, metrics->zhipu_week_percent);
|
|
format_reset_mins(metrics->zhipu_week_reset_mins, reset, sizeof(reset));
|
|
lv_label_set_text(s_zhipu_week_reset_label, reset);
|
|
}
|
|
|
|
static void update_net_line(const ui_metrics_t *metrics)
|
|
{
|
|
char rx[12];
|
|
char tx[12];
|
|
char text[44];
|
|
|
|
format_rate_short(metrics->rx_kbps, rx, sizeof(rx));
|
|
format_rate_short(metrics->tx_kbps, tx, sizeof(tx));
|
|
snprintf(text, sizeof(text), "D %s U %s", rx, tx);
|
|
|
|
size_t text_len = strlen(text);
|
|
const lv_font_t *font = &lv_font_montserrat_20;
|
|
if (text_len >= 18) {
|
|
font = &lv_font_montserrat_14;
|
|
}
|
|
|
|
lv_obj_set_style_text_font(s_net_line_label, font, 0);
|
|
lv_label_set_long_mode(s_net_line_label, LV_LABEL_LONG_CLIP);
|
|
lv_label_set_text(s_net_line_label, text);
|
|
}
|
|
|
|
void ui_screen_create(void)
|
|
{
|
|
lv_obj_t *screen = lv_screen_active();
|
|
lv_obj_set_style_bg_color(screen, paper(), 0);
|
|
lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, 0);
|
|
lv_obj_clear_flag(screen, LV_OBJ_FLAG_SCROLLABLE);
|
|
lv_obj_clean(screen);
|
|
|
|
make_rule(screen, 200, 6, 1, 288);
|
|
make_rule(screen, 6, 174, 388, 1);
|
|
|
|
s_status_label = make_label(screen, "WAITING", &lv_font_montserrat_14);
|
|
lv_obj_add_flag(s_status_label, LV_OBJ_FLAG_HIDDEN);
|
|
|
|
s_cpu_line = lv_line_create(screen);
|
|
lv_obj_set_size(s_cpu_line, CPU_CHART_W + 2, CPU_CHART_H + 2);
|
|
lv_obj_align(s_cpu_line, LV_ALIGN_TOP_LEFT, 8, 112);
|
|
lv_obj_set_style_line_color(s_cpu_line, ink(), 0);
|
|
lv_obj_set_style_line_width(s_cpu_line, 3, 0);
|
|
for (int i = 0; i < CPU_HISTORY_POINTS; i++) {
|
|
s_cpu_history[i] = 0;
|
|
}
|
|
set_cpu_curve(0);
|
|
|
|
s_battery_label = make_label(screen, LV_SYMBOL_BATTERY_EMPTY " --%", &lv_font_montserrat_14);
|
|
lv_obj_set_width(s_battery_label, 54);
|
|
lv_obj_align(s_battery_label, LV_ALIGN_TOP_LEFT, 8, 8);
|
|
|
|
s_conn_label = make_label(screen, LV_SYMBOL_USB, &lv_font_montserrat_14);
|
|
lv_obj_set_width(s_conn_label, 22);
|
|
lv_obj_align(s_conn_label, LV_ALIGN_TOP_LEFT, 58, 8);
|
|
|
|
s_env_label = make_label(screen, "--C --%", &lv_font_montserrat_14);
|
|
lv_obj_set_width(s_env_label, 110);
|
|
lv_obj_align(s_env_label, LV_ALIGN_TOP_LEFT, 82, 8);
|
|
|
|
lv_obj_t *cpu_title = make_label(screen, "CPU", &lv_font_montserrat_14);
|
|
lv_obj_align(cpu_title, LV_ALIGN_TOP_LEFT, 8, 36);
|
|
|
|
make_label_halo(screen, s_cpu_value_halo, "--%", &lv_font_montserrat_48, 126,
|
|
LV_ALIGN_TOP_LEFT, 8, 52, 3, LV_TEXT_ALIGN_LEFT);
|
|
s_cpu_value_label = make_label(screen, "--%", &lv_font_montserrat_48);
|
|
lv_obj_set_width(s_cpu_value_label, 126);
|
|
lv_obj_set_style_text_align(s_cpu_value_label, LV_TEXT_ALIGN_LEFT, 0);
|
|
lv_obj_align(s_cpu_value_label, LV_ALIGN_TOP_LEFT, 8, 52);
|
|
|
|
make_label_halo(screen, s_cpu_freq_halo, "--GHz", &lv_font_montserrat_28, 128,
|
|
LV_ALIGN_TOP_LEFT, 8, 96, 2, LV_TEXT_ALIGN_LEFT);
|
|
s_cpu_freq_label = make_label(screen, "--GHz", &lv_font_montserrat_28);
|
|
lv_obj_set_width(s_cpu_freq_label, 128);
|
|
lv_obj_align(s_cpu_freq_label, LV_ALIGN_TOP_LEFT, 8, 96);
|
|
|
|
lv_obj_t *memory_title = make_label(screen, "MEM", &lv_font_montserrat_14);
|
|
lv_obj_align(memory_title, LV_ALIGN_TOP_LEFT, 208, 8);
|
|
make_label_halo(screen, s_memory_value_halo, "--%", &lv_font_montserrat_20, 50,
|
|
LV_ALIGN_TOP_RIGHT, -8, 8, 2, LV_TEXT_ALIGN_RIGHT);
|
|
s_memory_value_label = make_label(screen, "--%", &lv_font_montserrat_20);
|
|
lv_obj_set_width(s_memory_value_label, 50);
|
|
lv_obj_set_style_text_align(s_memory_value_label, LV_TEXT_ALIGN_RIGHT, 0);
|
|
lv_obj_align(s_memory_value_label, LV_ALIGN_TOP_RIGHT, -8, 8);
|
|
s_memory_detail_label = make_label(screen, "--/--", &lv_font_montserrat_14);
|
|
lv_obj_set_width(s_memory_detail_label, 178);
|
|
lv_obj_align(s_memory_detail_label, LV_ALIGN_TOP_LEFT, 208, 24);
|
|
s_memory_bar = make_bar(screen, 126, 10);
|
|
lv_obj_align(s_memory_bar, LV_ALIGN_TOP_LEFT, 262, 46);
|
|
|
|
lv_obj_t *disk_title = make_label(screen, "DSK", &lv_font_montserrat_14);
|
|
lv_obj_align(disk_title, LV_ALIGN_TOP_LEFT, 208, 64);
|
|
|
|
for (int i = 0; i < DISK_ROW_COUNT; i++) {
|
|
int y = 80 + i * 16;
|
|
s_disk_name_labels[i] = make_label(screen, "--", &lv_font_montserrat_14);
|
|
lv_obj_set_width(s_disk_name_labels[i], 16);
|
|
lv_obj_align(s_disk_name_labels[i], LV_ALIGN_TOP_LEFT, 208, y);
|
|
s_disk_bars[i] = make_bar(screen, 56, 8);
|
|
lv_obj_align(s_disk_bars[i], LV_ALIGN_TOP_LEFT, 226, y + 4);
|
|
s_disk_rate_labels[i] = make_label(screen, "--/--", &lv_font_montserrat_14);
|
|
lv_label_set_long_mode(s_disk_rate_labels[i], LV_LABEL_LONG_CLIP);
|
|
lv_obj_set_width(s_disk_rate_labels[i], 104);
|
|
lv_obj_align(s_disk_rate_labels[i], LV_ALIGN_TOP_LEFT, 288, y);
|
|
}
|
|
|
|
s_net_line_label = make_label(screen, "D -- U --", &lv_font_montserrat_20);
|
|
lv_label_set_long_mode(s_net_line_label, LV_LABEL_LONG_CLIP);
|
|
lv_obj_set_width(s_net_line_label, 184);
|
|
lv_obj_set_height(s_net_line_label, 24);
|
|
lv_obj_align(s_net_line_label, LV_ALIGN_TOP_LEFT, 208, 150);
|
|
|
|
lv_obj_t *codex_title = make_label(screen, "CODEX", &lv_font_montserrat_14);
|
|
lv_obj_align(codex_title, LV_ALIGN_TOP_LEFT, 8, 184);
|
|
|
|
s_codex_5h_label = make_label(screen, "5H", &lv_font_montserrat_14);
|
|
lv_obj_set_width(s_codex_5h_label, 28);
|
|
lv_obj_align(s_codex_5h_label, LV_ALIGN_TOP_LEFT, 8, 204);
|
|
s_codex_5h_bar = make_bar(screen, 76, 8);
|
|
lv_obj_align(s_codex_5h_bar, LV_ALIGN_TOP_LEFT, 36, 208);
|
|
s_codex_5h_value_label = make_label(screen, "--", &lv_font_montserrat_14);
|
|
lv_obj_set_width(s_codex_5h_value_label, 38);
|
|
lv_obj_align(s_codex_5h_value_label, LV_ALIGN_TOP_LEFT, 118, 204);
|
|
s_codex_5h_reset_label = make_label(screen, "--", &lv_font_montserrat_14);
|
|
lv_label_set_long_mode(s_codex_5h_reset_label, LV_LABEL_LONG_CLIP);
|
|
lv_obj_set_width(s_codex_5h_reset_label, 38);
|
|
lv_obj_align(s_codex_5h_reset_label, LV_ALIGN_TOP_LEFT, 158, 204);
|
|
|
|
s_codex_week_label = make_label(screen, "WK", &lv_font_montserrat_14);
|
|
lv_obj_set_width(s_codex_week_label, 28);
|
|
lv_obj_align(s_codex_week_label, LV_ALIGN_TOP_LEFT, 8, 224);
|
|
s_codex_week_bar = make_bar(screen, 76, 8);
|
|
lv_obj_align(s_codex_week_bar, LV_ALIGN_TOP_LEFT, 36, 228);
|
|
s_codex_week_value_label = make_label(screen, "--", &lv_font_montserrat_14);
|
|
lv_obj_set_width(s_codex_week_value_label, 38);
|
|
lv_obj_align(s_codex_week_value_label, LV_ALIGN_TOP_LEFT, 118, 224);
|
|
s_codex_week_reset_label = make_label(screen, "--", &lv_font_montserrat_14);
|
|
lv_label_set_long_mode(s_codex_week_reset_label, LV_LABEL_LONG_CLIP);
|
|
lv_obj_set_width(s_codex_week_reset_label, 38);
|
|
lv_obj_align(s_codex_week_reset_label, LV_ALIGN_TOP_LEFT, 158, 224);
|
|
|
|
s_codex_token_label = make_label(screen, "TOK --", &lv_font_montserrat_14);
|
|
lv_obj_set_width(s_codex_token_label, 96);
|
|
lv_obj_set_style_text_align(s_codex_token_label, LV_TEXT_ALIGN_RIGHT, 0);
|
|
lv_obj_align(s_codex_token_label, LV_ALIGN_TOP_RIGHT, -204, 184);
|
|
|
|
make_rule(screen, 8, CODEX_DAILY_BAR_BASE_Y, 188, 1);
|
|
for (int i = 0; i < UI_CODEX_DAILY_BAR_COUNT; i++) {
|
|
s_codex_daily_bars[i] = lv_obj_create(screen);
|
|
lv_obj_set_size(s_codex_daily_bars[i], CODEX_DAILY_BAR_W, 1);
|
|
lv_obj_align(s_codex_daily_bars[i], LV_ALIGN_TOP_LEFT,
|
|
10 + i * (CODEX_DAILY_BAR_W + CODEX_DAILY_BAR_GAP),
|
|
CODEX_DAILY_BAR_BASE_Y - 1);
|
|
lv_obj_set_style_bg_color(s_codex_daily_bars[i], ink(), 0);
|
|
lv_obj_set_style_bg_opa(s_codex_daily_bars[i], LV_OPA_COVER, 0);
|
|
lv_obj_set_style_border_width(s_codex_daily_bars[i], 0, 0);
|
|
lv_obj_set_style_radius(s_codex_daily_bars[i], 0, 0);
|
|
lv_obj_set_style_pad_all(s_codex_daily_bars[i], 0, 0);
|
|
lv_obj_clear_flag(s_codex_daily_bars[i], LV_OBJ_FLAG_SCROLLABLE);
|
|
lv_obj_add_flag(s_codex_daily_bars[i], LV_OBJ_FLAG_HIDDEN);
|
|
}
|
|
|
|
lv_obj_t *zhipu_title = make_label(screen, "ZHIPU", &lv_font_montserrat_14);
|
|
lv_obj_align(zhipu_title, LV_ALIGN_TOP_LEFT, 208, 184);
|
|
s_zhipu_status_label = make_label(screen, "STAT WAIT", &lv_font_montserrat_14);
|
|
lv_obj_set_width(s_zhipu_status_label, 96);
|
|
lv_obj_set_style_text_align(s_zhipu_status_label, LV_TEXT_ALIGN_RIGHT, 0);
|
|
lv_obj_align(s_zhipu_status_label, LV_ALIGN_TOP_RIGHT, -8, 184);
|
|
s_zhipu_5h_label = make_label(screen, "5H", &lv_font_montserrat_14);
|
|
lv_obj_set_width(s_zhipu_5h_label, 28);
|
|
lv_obj_align(s_zhipu_5h_label, LV_ALIGN_TOP_LEFT, 208, 214);
|
|
s_zhipu_5h_bar = make_bar(screen, 74, 9);
|
|
lv_obj_align(s_zhipu_5h_bar, LV_ALIGN_TOP_LEFT, 238, 218);
|
|
s_zhipu_5h_value_label = make_label(screen, "--", &lv_font_montserrat_14);
|
|
lv_obj_set_width(s_zhipu_5h_value_label, 38);
|
|
lv_obj_align(s_zhipu_5h_value_label, LV_ALIGN_TOP_LEFT, 318, 214);
|
|
s_zhipu_5h_reset_label = make_label(screen, "--", &lv_font_montserrat_14);
|
|
lv_label_set_long_mode(s_zhipu_5h_reset_label, LV_LABEL_LONG_CLIP);
|
|
lv_obj_set_width(s_zhipu_5h_reset_label, 38);
|
|
lv_obj_align(s_zhipu_5h_reset_label, LV_ALIGN_TOP_LEFT, 358, 214);
|
|
|
|
s_zhipu_week_label = make_label(screen, "WK", &lv_font_montserrat_14);
|
|
lv_obj_set_width(s_zhipu_week_label, 28);
|
|
lv_obj_align(s_zhipu_week_label, LV_ALIGN_TOP_LEFT, 208, 242);
|
|
s_zhipu_week_bar = make_bar(screen, 74, 9);
|
|
lv_obj_align(s_zhipu_week_bar, LV_ALIGN_TOP_LEFT, 238, 246);
|
|
s_zhipu_week_value_label = make_label(screen, "--", &lv_font_montserrat_14);
|
|
lv_obj_set_width(s_zhipu_week_value_label, 38);
|
|
lv_obj_align(s_zhipu_week_value_label, LV_ALIGN_TOP_LEFT, 318, 242);
|
|
s_zhipu_week_reset_label = make_label(screen, "--", &lv_font_montserrat_14);
|
|
lv_label_set_long_mode(s_zhipu_week_reset_label, LV_LABEL_LONG_CLIP);
|
|
lv_obj_set_width(s_zhipu_week_reset_label, 38);
|
|
lv_obj_align(s_zhipu_week_reset_label, LV_ALIGN_TOP_LEFT, 358, 242);
|
|
}
|
|
|
|
void ui_screen_tick(void)
|
|
{
|
|
s_age_seconds++;
|
|
if (s_age_seconds > 5 && s_status_label != NULL) {
|
|
lv_label_set_text(s_status_label, "STALE");
|
|
}
|
|
}
|
|
|
|
void ui_screen_set_status(const char *status)
|
|
{
|
|
if (s_status_label == NULL || status == NULL) {
|
|
return;
|
|
}
|
|
|
|
lv_lock();
|
|
lv_label_set_text(s_status_label, status);
|
|
lv_unlock();
|
|
}
|
|
|
|
void ui_screen_update_battery(int percent, int millivolts)
|
|
{
|
|
if (s_battery_label == NULL) {
|
|
return;
|
|
}
|
|
|
|
const char *symbol = LV_SYMBOL_BATTERY_EMPTY;
|
|
percent = clamp_percent(percent);
|
|
if (percent >= 90) {
|
|
symbol = LV_SYMBOL_BATTERY_FULL;
|
|
} else if (percent >= 65) {
|
|
symbol = LV_SYMBOL_BATTERY_3;
|
|
} else if (percent >= 35) {
|
|
symbol = LV_SYMBOL_BATTERY_2;
|
|
} else if (percent >= 10) {
|
|
symbol = LV_SYMBOL_BATTERY_1;
|
|
}
|
|
|
|
char text[24];
|
|
if (millivolts > 0) {
|
|
snprintf(text, sizeof(text), "%s %d%%", symbol, percent);
|
|
} else {
|
|
snprintf(text, sizeof(text), "%s --%%", symbol);
|
|
}
|
|
|
|
lv_lock();
|
|
lv_label_set_text(s_battery_label, text);
|
|
lv_unlock();
|
|
}
|
|
|
|
void ui_screen_update_environment(int temperature_c10, int humidity_percent)
|
|
{
|
|
if (s_env_label == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (humidity_percent < 0) {
|
|
humidity_percent = 0;
|
|
} else if (humidity_percent > 100) {
|
|
humidity_percent = 100;
|
|
}
|
|
|
|
char text[32];
|
|
int abs_temp_c10 = abs(temperature_c10);
|
|
if (temperature_c10 < 0) {
|
|
snprintf(text, sizeof(text), "-%d.%dC %d%%", abs_temp_c10 / 10, abs_temp_c10 % 10, humidity_percent);
|
|
} else {
|
|
snprintf(text, sizeof(text), "%d.%dC %d%%", abs_temp_c10 / 10, abs_temp_c10 % 10, humidity_percent);
|
|
}
|
|
|
|
lv_lock();
|
|
lv_label_set_text(s_env_label, text);
|
|
lv_unlock();
|
|
}
|
|
|
|
void ui_screen_update_metrics_from_source(const ui_metrics_t *metrics, const char *source)
|
|
{
|
|
if (metrics == NULL || s_cpu_value_label == NULL) {
|
|
return;
|
|
}
|
|
|
|
char text[96];
|
|
char used[16];
|
|
char total[16];
|
|
|
|
lv_lock();
|
|
s_age_seconds = 0;
|
|
lv_label_set_text(s_status_label, source ? source : "LIVE");
|
|
set_connection(connection_from_source(source));
|
|
|
|
snprintf(text, sizeof(text), "%d%%", clamp_percent(metrics->cpu_percent));
|
|
set_label_with_halo(s_cpu_value_label, s_cpu_value_halo, text);
|
|
if (metrics->cpu_mhz > 0) {
|
|
snprintf(text, sizeof(text), "%d.%02dGHz", metrics->cpu_mhz / 1000, (metrics->cpu_mhz % 1000) / 10);
|
|
} else {
|
|
snprintf(text, sizeof(text), "--GHz");
|
|
}
|
|
set_label_with_halo(s_cpu_freq_label, s_cpu_freq_halo, text);
|
|
set_cpu_curve(metrics->cpu_percent);
|
|
|
|
snprintf(text, sizeof(text), "%d%%", clamp_percent(metrics->memory_percent));
|
|
set_label_with_halo(s_memory_value_label, s_memory_value_halo, text);
|
|
format_capacity(metrics->memory_used_mb, used, sizeof(used));
|
|
format_capacity(metrics->memory_total_mb, total, sizeof(total));
|
|
snprintf(text, sizeof(text), "%s / %s", used, total);
|
|
lv_label_set_text(s_memory_detail_label, text);
|
|
set_bar_percent(s_memory_bar, metrics->memory_percent);
|
|
|
|
update_disk_rows(metrics->disk_text);
|
|
|
|
update_net_line(metrics);
|
|
update_codex_panel(metrics);
|
|
update_zhipu_panel(metrics);
|
|
lv_obj_invalidate(lv_screen_active());
|
|
lv_unlock();
|
|
}
|
|
|
|
void ui_screen_update_metrics(const ui_metrics_t *metrics)
|
|
{
|
|
ui_screen_update_metrics_from_source(metrics, "USB");
|
|
}
|