feat: desktop environment improvements
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* app_klog.cpp
|
||||
* ZenithOS Desktop - Kernel Log viewer (tails the kernel ring buffer)
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "apps_common.hpp"
|
||||
|
||||
// ============================================================================
|
||||
// Kernel Log state
|
||||
// ============================================================================
|
||||
|
||||
static constexpr int KLOG_READ_SIZE = 65536; // matches kernel ring buffer size
|
||||
static constexpr int KLOG_POLL_MS = 250;
|
||||
|
||||
struct KlogState {
|
||||
TerminalState term;
|
||||
char* klog_buf;
|
||||
int last_len;
|
||||
char last_tail_byte;
|
||||
uint64_t last_poll_ms;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Re-feed the last screenful of log into the terminal
|
||||
// ============================================================================
|
||||
|
||||
static void klog_refeed(KlogState* klog, int n) {
|
||||
// Find start of last `rows` lines from the end of the buffer
|
||||
int lines_needed = klog->term.rows;
|
||||
int start = 0;
|
||||
for (int i = n - 1; i >= 0 && lines_needed > 0; i--) {
|
||||
if (klog->klog_buf[i] == '\n') {
|
||||
lines_needed--;
|
||||
if (lines_needed == 0) { start = i + 1; break; }
|
||||
}
|
||||
}
|
||||
|
||||
// Clear terminal
|
||||
int total = klog->term.cols * klog->term.rows;
|
||||
for (int i = 0; i < total; i++)
|
||||
klog->term.cells[i] = {' ', colors::TERM_FG, colors::TERM_BG};
|
||||
klog->term.cursor_x = 0;
|
||||
klog->term.cursor_y = 0;
|
||||
klog->term.current_fg = colors::TERM_FG;
|
||||
klog->term.current_bg = colors::TERM_BG;
|
||||
|
||||
terminal_feed(&klog->term, klog->klog_buf + start, n - start);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Callbacks
|
||||
// ============================================================================
|
||||
|
||||
static void klog_on_draw(Window* win, Framebuffer& fb) {
|
||||
KlogState* klog = (KlogState*)win->app_data;
|
||||
if (!klog) return;
|
||||
|
||||
Rect cr = win->content_rect();
|
||||
terminal_render(&klog->term, win->content, cr.w, cr.h);
|
||||
}
|
||||
|
||||
static void klog_on_mouse(Window* win, MouseEvent& ev) {
|
||||
// Read-only viewer — no mouse interaction needed
|
||||
}
|
||||
|
||||
static void klog_on_key(Window* win, const Zenith::KeyEvent& key) {
|
||||
// Read-only viewer — no keyboard input
|
||||
}
|
||||
|
||||
static void klog_on_poll(Window* win) {
|
||||
KlogState* klog = (KlogState*)win->app_data;
|
||||
if (!klog) return;
|
||||
|
||||
uint64_t now = zenith::get_milliseconds();
|
||||
if (now - klog->last_poll_ms < KLOG_POLL_MS) return;
|
||||
klog->last_poll_ms = now;
|
||||
|
||||
int n = (int)zenith::read_klog(klog->klog_buf, KLOG_READ_SIZE);
|
||||
if (n <= 0 && klog->last_len <= 0) return;
|
||||
|
||||
if (n > klog->last_len) {
|
||||
// Buffer grew — feed only the new portion
|
||||
terminal_feed(&klog->term, klog->klog_buf + klog->last_len, n - klog->last_len);
|
||||
} else if (n == klog->last_len && n > 0) {
|
||||
// Same size — check if the tail changed (ring buffer wrapped)
|
||||
if (klog->klog_buf[n - 1] != klog->last_tail_byte) {
|
||||
klog_refeed(klog, n);
|
||||
} else {
|
||||
return; // nothing changed
|
||||
}
|
||||
} else if (n < klog->last_len) {
|
||||
// Unexpected shrink — full re-render
|
||||
klog_refeed(klog, n);
|
||||
}
|
||||
|
||||
klog->last_len = n;
|
||||
if (n > 0) klog->last_tail_byte = klog->klog_buf[n - 1];
|
||||
}
|
||||
|
||||
static void klog_on_close(Window* win) {
|
||||
KlogState* klog = (KlogState*)win->app_data;
|
||||
if (klog) {
|
||||
if (klog->term.cells) zenith::mfree(klog->term.cells);
|
||||
if (klog->term.alt_cells) zenith::mfree(klog->term.alt_cells);
|
||||
if (klog->klog_buf) zenith::mfree(klog->klog_buf);
|
||||
zenith::mfree(klog);
|
||||
win->app_data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Kernel Log launcher
|
||||
// ============================================================================
|
||||
|
||||
void open_klog(DesktopState* ds) {
|
||||
int idx = desktop_create_window(ds, "Kernel Log", 160, 60, 720, 480);
|
||||
if (idx < 0) return;
|
||||
|
||||
Window* win = &ds->windows[idx];
|
||||
Rect cr = win->content_rect();
|
||||
int cols = cr.w / FONT_WIDTH;
|
||||
int rows = cr.h / FONT_HEIGHT;
|
||||
|
||||
KlogState* klog = (KlogState*)zenith::malloc(sizeof(KlogState));
|
||||
zenith::memset(klog, 0, sizeof(KlogState));
|
||||
|
||||
// Initialize the terminal cell grid (reuse terminal infrastructure)
|
||||
terminal_init_cells(&klog->term, cols, rows);
|
||||
|
||||
// Allocate klog read buffer
|
||||
klog->klog_buf = (char*)zenith::malloc(KLOG_READ_SIZE);
|
||||
klog->last_len = 0;
|
||||
klog->last_tail_byte = 0;
|
||||
klog->last_poll_ms = 0;
|
||||
|
||||
// Do an initial read to show existing log content
|
||||
int n = (int)zenith::read_klog(klog->klog_buf, KLOG_READ_SIZE);
|
||||
if (n > 0) {
|
||||
klog_refeed(klog, n);
|
||||
klog->last_len = n;
|
||||
klog->last_tail_byte = klog->klog_buf[n - 1];
|
||||
}
|
||||
|
||||
win->app_data = klog;
|
||||
win->on_draw = klog_on_draw;
|
||||
win->on_mouse = klog_on_mouse;
|
||||
win->on_key = klog_on_key;
|
||||
win->on_close = klog_on_close;
|
||||
win->on_poll = klog_on_poll;
|
||||
}
|
||||
Reference in New Issue
Block a user