#include #include #include #include #include "lvgl.h" #include "esp_lvgl_port.h" #include "lcd/ui_log.h" #include "lcd/ui_internal.h" #define UI_LOG_MAX_LINES 41 #define UI_LOG_LINE_MAX_LENGTH 160 static lv_obj_t *log_textarea = NULL; static char log_lines[UI_LOG_MAX_LINES][UI_LOG_LINE_MAX_LENGTH]; static size_t log_line_length = 0; static size_t log_line_start = 0; static size_t ui_log_buffer_index(size_t line_offset){ return (log_line_start + line_offset) % UI_LOG_MAX_LINES; } // Stores text in the ring buffer, sets *out_evicted when the oldest line had to // be dropped, and returns the stored (possibly truncated) line. static const char *ui_log_store_line(const char *text, bool *out_evicted){ size_t line_index; bool evicted = false; if(log_line_length < UI_LOG_MAX_LINES){ line_index = ui_log_buffer_index(log_line_length); log_line_length++; } else{ line_index = log_line_start; log_line_start = ui_log_buffer_index(1); evicted = true; } snprintf(log_lines[line_index], sizeof(log_lines[line_index]), "%s", text); *out_evicted = evicted; return log_lines[line_index]; } static void ui_log_append_line_locked(const char *line){ lv_textarea_add_text(log_textarea, line); lv_textarea_add_text(log_textarea, "\n"); lv_textarea_set_cursor_pos(log_textarea, LV_TEXTAREA_CURSOR_LAST); } static void ui_log_render_all_locked(void){ lv_textarea_set_text(log_textarea, ""); for(size_t line_offset = 0; line_offset < log_line_length; line_offset++){ lv_textarea_add_text(log_textarea, log_lines[ui_log_buffer_index(line_offset)]); lv_textarea_add_text(log_textarea, "\n"); } lv_textarea_set_cursor_pos(log_textarea, LV_TEXTAREA_CURSOR_LAST); } void ui_log_append(const char *text){ if(text == NULL) return; bool evicted = false; const char *stored_line = ui_log_store_line(text, &evicted); if(log_textarea == NULL) return; lvgl_port_lock(0); // Appending a line is O(1); only a dropped oldest line forces a full rebuild. if(evicted) ui_log_render_all_locked(); else ui_log_append_line_locked(stored_line); lvgl_port_unlock(); } void ui_log_appendf(const char *format, ...){ if(format == NULL) return; char line[UI_LOG_LINE_MAX_LENGTH]; va_list args; va_start(args, format); vsnprintf(line, sizeof(line), format, args); va_end(args); ui_log_append(line); } void ui_log_detach(void){ log_textarea = NULL; } void ui_log_build_view(lv_obj_t *screen){ ui_attach_toggle(screen); lv_obj_set_style_bg_color(screen, lv_color_hex(0x101418), 0); lv_obj_t *title = lv_label_create(screen); ui_attach_toggle(title); lv_label_set_text(title, "SD Image Viewer by Timo Niemann"); lv_obj_set_style_text_color(title, lv_color_white(), 0); lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 14); log_textarea = lv_textarea_create(screen); ui_attach_toggle(log_textarea); lv_obj_set_size(log_textarea, 440, 730); lv_obj_align(log_textarea, LV_ALIGN_TOP_LEFT, 20, 40); lv_textarea_set_one_line(log_textarea, false); lv_textarea_set_cursor_click_pos(log_textarea, false); lv_textarea_set_text(log_textarea, ""); lv_obj_set_style_bg_color(log_textarea, lv_color_hex(0x182028), 0); lv_obj_set_style_text_color(log_textarea, lv_color_hex(0xdde7ef), 0); lv_obj_set_style_border_color(log_textarea, lv_color_hex(0x4b6478), 0); lv_obj_set_style_radius(log_textarea, 4, 0); lv_obj_set_style_pad_all(log_textarea, 12, 0); ui_log_render_all_locked(); }