rumpk/hal/ui.zig

167 lines
4.8 KiB
Zig

// SPDX-License-Identifier: LCL-1.0
// Copyright (c) 2026 Markus Maiwald
// Stewardship: Self Sovereign Society Foundation
//
// This file is part of the Nexus Commonwealth.
// See legal/LICENSE_COMMONWEALTH.md for license terms.
//! Rumpk HAL: UI Layer (The Persona)
//!
//! Bridges microui (C) with the Zig HAL and Framebuffer.
//! Handles GUI rendering, layout management, and the boot animation loop.
//!
//! SAFETY: Integrates with a C-based GUI library via @cImport.
//! Global context is initialized by `init()` before use in the UI fiber.
const std = @import("std");
const fb = @import("framebuffer.zig");
// GPU Driver (extern)
extern fn virtio_gpu_flush() void;
pub const c = @cImport({
@cInclude("microui.h");
});
// --- STATIC MEMORY ---
// SAFETY(UI): Context is initialized by `c.mu_init(&ctx)` in `init()`.
var ctx: c.mu_Context = undefined;
// --- FONT (The "8-bit" classic) ---
// Minimal placeholder font (0-127 ascii) - 1KB
const font_bitmap: [128][8]u8 = [_][8]u8{[_]u8{0xFF} ** 8} ** 128; // Blocks for now
// --- CALL BACKS ---
fn text_width_cb(font: ?*anyopaque, str: [*c]const u8, len: c_int) callconv(.c) c_int {
_ = font;
// assuming 8x8 monospaced
// If len is -1, treat as null terminated
var l = len;
if (l == -1 and str != null) {
l = 0;
var s = str;
while (s[0] != 0) {
l += 1;
s += 1;
}
}
return l * 8;
}
fn text_height_cb(font: ?*anyopaque) callconv(.c) c_int {
_ = font;
return 8;
}
// --- THE RENDERER (The Paint Brush) ---
pub fn render() void {
var cmd: [*c]c.mu_Command = null;
// Iterate over microui command list
// Note: mu_next_command takes pointers
while (c.mu_next_command(&ctx, &cmd) != 0) {
switch (cmd.*.type) {
c.MU_COMMAND_TEXT => {
// draw_text(cmd.text.str, cmd.text.pos, cmd.text.color);
// For now, ignore text drawing to avoid font complexity in step 1
},
c.MU_COMMAND_RECT => {
draw_rect(cmd.*.rect.rect, cmd.*.rect.color);
},
c.MU_COMMAND_ICON => {
// TODO: Draw simple icons (close button X)
draw_rect(cmd.*.icon.rect, cmd.*.icon.color); // Placeholder
},
c.MU_COMMAND_CLIP => {
// fb.set_clip(cmd.clip.rect);
// Adaptation needed for C to Zig struct
fb.set_clip(.{
.x = cmd.*.clip.rect.x,
.y = cmd.*.clip.rect.y,
.w = cmd.*.clip.rect.w,
.h = cmd.*.clip.rect.h,
});
},
else => {},
}
}
// PUSH TO HOST (The Retina)
virtio_gpu_flush();
}
// --- HELPER: Draw Rect ---
fn draw_rect(r: c.mu_Rect, color: c.mu_Color) void {
// Convert microui Color to ARGB
const argb = (@as(u32, 255) << 24) |
(@as(u32, color.r) << 16) |
(@as(u32, color.g) << 8) |
@as(u32, color.b);
// Plot to L0 Framebuffer
fb.fill_rect(r.x, r.y, r.w, r.h, argb);
}
// --- INITIALIZATION ---
pub fn init() void {
c.mu_init(&ctx);
// Hook up text width callback (Required by microui)
ctx.text_width = text_width_cb;
ctx.text_height = text_height_cb;
}
// --- KERNEL IMPORTS ---
extern fn fiber_yield() void;
extern fn fiber_sleep(ms: i32) void;
extern fn matrix_init() void;
extern fn matrix_update() bool;
// --- THE UI LOOP (Fiber Entry) ---
export fn ui_fiber_entry() void {
init();
matrix_init();
var matrix_alive: bool = true;
while (true) {
if (matrix_alive) {
// --- THE GREETING (High Compute) ---
matrix_alive = matrix_update();
// Push the Rain to the GPU
virtio_gpu_flush();
// 33ms Frame Time (~30 FPS)
fiber_sleep(33);
} else {
// --- THE HUD / VOID (Low Compute) ---
// 1. Begin Frame
c.mu_begin(&ctx);
// 2. Define Layout (The Logic)
if (c.mu_begin_window(&ctx, "Nexus HUD", c.mu_rect(10, 10, 300, 200)) != 0) {
c.mu_layout_row(&ctx, 1, &[_]i32{-1}, 0);
c.mu_label(&ctx, "System Status: ONLINE");
c.mu_layout_row(&ctx, 2, &[_]i32{ 80, -1 }, 0);
c.mu_label(&ctx, "CPU:");
c.mu_draw_rect(&ctx, c.mu_rect(100, 50, 150, 20), c.mu_color(0, 255, 0, 255)); // Mock bar
if (c.mu_button(&ctx, "REBOOT") != 0) {
// hal_reboot();
}
c.mu_end_window(&ctx);
}
// 3. End Frame & Paint
c.mu_end(&ctx);
render();
// 1s Sleep - No need to render UI every 30ms if static
fiber_sleep(1000);
}
}
}