Files
MontaukOS/programs/src/disks/render.cpp
T

400 lines
14 KiB
C++

/*
* render.cpp
* MontaukOS Disk Tool — rendering
* Copyright (c) 2026 Daniel Hammer
*/
#include "disks.h"
// ============================================================================
// Pixel helpers
// ============================================================================
static void px_fill(uint32_t* px, int bw, int bh,
int x, int y, int w, int h, Color c) {
uint32_t v = c.to_pixel();
int x0 = x < 0 ? 0 : x, y0 = y < 0 ? 0 : y;
int x1 = x + w > bw ? bw : x + w;
int y1 = y + h > bh ? bh : y + h;
for (int row = y0; row < y1; row++)
for (int col = x0; col < x1; col++)
px[row * bw + col] = v;
}
static void px_hline(uint32_t* px, int bw, int bh, int x, int y, int w, Color c) {
if (y < 0 || y >= bh) return;
uint32_t v = c.to_pixel();
int x0 = x < 0 ? 0 : x;
int x1 = x + w > bw ? bw : x + w;
for (int col = x0; col < x1; col++)
px[y * bw + col] = v;
}
static void px_fill_rounded(uint32_t* px, int bw, int bh,
int x, int y, int w, int h, int r, Color c) {
uint32_t v = c.to_pixel();
for (int row = 0; row < h; row++) {
int dy = y + row;
if (dy < 0 || dy >= bh) continue;
for (int col = 0; col < w; col++) {
int dx = x + col;
if (dx < 0 || dx >= bw) continue;
bool skip = false;
int cx, cy;
if (col < r && row < r) { cx = r - col - 1; cy = r - row - 1; if (cx*cx + cy*cy >= r*r) skip = true; }
else if (col >= w - r && row < r) { cx = col - (w - r); cy = r - row - 1; if (cx*cx + cy*cy >= r*r) skip = true; }
else if (col < r && row >= h - r) { cx = r - col - 1; cy = row - (h - r); if (cx*cx + cy*cy >= r*r) skip = true; }
else if (col >= w - r && row >= h - r) { cx = col - (w - r); cy = row - (h - r); if (cx*cx + cy*cy >= r*r) skip = true; }
if (!skip) px[dy * bw + dx] = v;
}
}
}
static void px_rect(uint32_t* px, int bw, int bh,
int x, int y, int w, int h, Color c) {
px_hline(px, bw, bh, x, y, w, c);
px_hline(px, bw, bh, x, y + h - 1, w, c);
for (int row = y; row < y + h; row++) {
if (row < 0 || row >= bh) continue;
if (x >= 0 && x < bw) px[row * bw + x] = c.to_pixel();
int rx = x + w - 1;
if (rx >= 0 && rx < bw) px[row * bw + rx] = c.to_pixel();
}
}
static void px_text(uint32_t* px, int bw, int bh,
int x, int y, const char* text, Color c) {
if (g_font)
g_font->draw_to_buffer(px, bw, bh, x, y, text, c, FONT_SIZE);
}
static int text_w(const char* text) {
return g_font ? g_font->measure_text(text, FONT_SIZE) : 0;
}
static int font_h() {
if (!g_font) return 16;
auto* cache = g_font->get_cache(FONT_SIZE);
return cache->ascent - cache->descent;
}
// ============================================================================
// Button helper
// ============================================================================
static void px_button(uint32_t* px, int bw, int bh,
int x, int y, int w, int h,
const char* label, bool enabled, bool hovered, bool primary) {
Color bg, fg;
if (!enabled) {
bg = Color::from_rgb(0xF0, 0xF0, 0xF0);
fg = Color::from_rgb(0x9A, 0x9A, 0x9A);
} else if (primary) {
bg = hovered ? Color::from_rgb(0x2B, 0x6B, 0xE0) : colors::ACCENT;
fg = WHITE;
} else {
bg = hovered ? Color::from_rgb(0xDC, 0xDC, 0xDC) : Color::from_rgb(0xE8, 0xE8, 0xE8);
fg = TEXT_COLOR;
}
px_fill_rounded(px, bw, bh, x, y, w, h, 6, bg);
int tw = text_w(label);
int fh = font_h();
px_text(px, bw, bh, x + (w - tw) / 2, y + (h - fh) / 2, label, fg);
}
// ============================================================================
// Render: main view
// ============================================================================
static void render_toolbar(uint32_t* px) {
auto& dt = g_state;
int fh = font_h();
px_fill(px, g_win_w, g_win_h, 0, 0, g_win_w, TOOLBAR_H, TOOLBAR_BG);
px_hline(px, g_win_w, g_win_h, 0, TOOLBAR_H - 1, g_win_w, BORDER_COLOR);
int bx = 8;
for (int i = 0; i < dt.disk_count; i++) {
char label[8];
snprintf(label, sizeof(label), "Disk %d", i);
int lw = text_w(label) + 16;
px_button(px, g_win_w, g_win_h, bx, TB_BTN_Y, lw, TB_BTN_H, label, true, false, false);
bx += lw + 6;
}
if (dt.disk_count == 0) {
px_text(px, g_win_w, g_win_h, 8, (TOOLBAR_H - fh) / 2, "No disks detected", TEXT_COLOR);
return;
}
// Right-side action buttons
bool has_sel = dt.selected_part >= 0;
int rx = g_win_w - 8;
int ref_w = 64; rx -= ref_w;
px_button(px, g_win_w, g_win_h, rx, TB_BTN_Y, ref_w, TB_BTN_H,
"Refresh", true, false, false);
rx -= 6;
int mnt_w = 60; rx -= mnt_w;
px_button(px, g_win_w, g_win_h, rx, TB_BTN_Y, mnt_w, TB_BTN_H,
"Mount", has_sel, false, true);
rx -= 6;
int fmt_w = 64; rx -= fmt_w;
px_button(px, g_win_w, g_win_h, rx, TB_BTN_Y, fmt_w, TB_BTN_H,
"Format", has_sel, false, true);
rx -= 6;
int np_w = 74; rx -= np_w;
px_button(px, g_win_w, g_win_h, rx, TB_BTN_Y, np_w, TB_BTN_H,
"New Part", true, false, true);
}
static void render_content(uint32_t* px) {
auto& dt = g_state;
int fh = font_h();
if (dt.disk_count == 0 || dt.selected_disk < 0 || dt.selected_disk >= dt.disk_count)
return;
Montauk::DiskInfo& disk = dt.disks[dt.selected_disk];
int y = TOOLBAR_H + 8;
// Disk model name
px_text(px, g_win_w, g_win_h, MAP_PAD, y, disk.model, TEXT_COLOR);
// Size and type on the right
char info_str[64], size_str[24];
format_disk_size(size_str, sizeof(size_str), disk.sectorCount, disk.sectorSizeLog);
const char* dtype = disk.rpm == 1 ? "SSD" : "HDD";
snprintf(info_str, sizeof(info_str), "%s %s", size_str, dtype);
int iw = text_w(info_str);
px_text(px, g_win_w, g_win_h, g_win_w - iw - MAP_PAD, y, info_str, DIM_TEXT);
y += fh + 8;
// Partition map bar
int map_x = MAP_PAD;
int map_w = g_win_w - MAP_PAD * 2;
px_fill_rounded(px, g_win_w, g_win_h, map_x, y, map_w, MAP_H, 6,
Color::from_rgb(0xE4, 0xE4, 0xE4));
int part_indices[MAX_PARTS];
int nparts = get_disk_parts(part_indices, MAX_PARTS);
uint64_t total_sectors = disk.sectorCount;
if (total_sectors > 0 && nparts > 0) {
for (int pi = 0; pi < nparts; pi++) {
Montauk::PartInfo& p = dt.parts[part_indices[pi]];
int ppx = map_x + (int)((p.startLba * (uint64_t)map_w) / total_sectors);
int pw = (int)((p.sectorCount * (uint64_t)map_w) / total_sectors);
if (pw < 2) pw = 2;
if (ppx + pw > map_x + map_w) pw = map_x + map_w - ppx;
Color col = part_colors[pi % NUM_PART_COLORS];
if (pi == dt.selected_part) {
col = Color::from_rgb((col.r + 255) / 2, (col.g + 255) / 2, (col.b + 255) / 2);
}
px_fill_rounded(px, g_win_w, g_win_h, ppx, y + 2, pw, MAP_H - 4, 3, col);
char plabel[8];
snprintf(plabel, sizeof(plabel), "%d", pi);
int plw = text_w(plabel);
if (pw > plw + 4)
px_text(px, g_win_w, g_win_h, ppx + (pw - plw) / 2, y + (MAP_H - fh) / 2, plabel, WHITE);
}
}
y += MAP_H + 8;
// Column header
px_fill(px, g_win_w, g_win_h, 0, y, g_win_w, HEADER_H, HEADER_BG);
int ty = y + (HEADER_H - fh) / 2;
int col_idx = 12;
int col_name = 40;
int col_type = g_win_w / 2 - 20;
int col_size = g_win_w - 160;
int col_lba = g_win_w - 80;
px_text(px, g_win_w, g_win_h, col_idx, ty, "#", DIM_TEXT);
px_text(px, g_win_w, g_win_h, col_name, ty, "Name", DIM_TEXT);
px_text(px, g_win_w, g_win_h, col_type, ty, "Type", DIM_TEXT);
px_text(px, g_win_w, g_win_h, col_size, ty, "Size", DIM_TEXT);
px_text(px, g_win_w, g_win_h, col_lba, ty, "LBA", DIM_TEXT);
px_hline(px, g_win_w, g_win_h, 0, y + HEADER_H - 1, g_win_w, BORDER_COLOR);
y += HEADER_H;
// Partition rows
int list_bottom = g_win_h - STATUS_H;
int list_y = y;
for (int pi = 0; pi < nparts; pi++) {
int row_y = list_y + pi * ITEM_H - dt.scroll_y;
if (row_y + ITEM_H <= list_y) continue;
if (row_y >= list_bottom) break;
Montauk::PartInfo& p = dt.parts[part_indices[pi]];
if (pi == dt.selected_part)
px_fill(px, g_win_w, g_win_h, 0, row_y, g_win_w, ITEM_H, HOVER_BG);
px_fill_rounded(px, g_win_w, g_win_h, col_idx, row_y + (ITEM_H - 10) / 2, 10, 10, 5,
part_colors[pi % NUM_PART_COLORS]);
int ry = row_y + (ITEM_H - fh) / 2;
px_text(px, g_win_w, g_win_h, col_name, ry, p.name[0] ? p.name : "(unnamed)", TEXT_COLOR);
px_text(px, g_win_w, g_win_h, col_type, ry, p.typeName, DIM_TEXT);
char sz[24];
format_disk_size(sz, sizeof(sz), p.sectorCount, disk.sectorSizeLog);
px_text(px, g_win_w, g_win_h, col_size, ry, sz, TEXT_COLOR);
char lba_str[24];
snprintf(lba_str, sizeof(lba_str), "%lu", (unsigned)p.startLba);
px_text(px, g_win_w, g_win_h, col_lba, ry, lba_str, FAINT_TEXT);
}
if (nparts == 0)
px_text(px, g_win_w, g_win_h, col_name, list_y + 8, "No partitions found", FAINT_TEXT);
}
static void render_status(uint32_t* px) {
auto& dt = g_state;
int fh = font_h();
int sy = g_win_h - STATUS_H;
px_fill(px, g_win_w, g_win_h, 0, sy, g_win_w, STATUS_H, STATUS_BG_COL);
px_hline(px, g_win_w, g_win_h, 0, sy, g_win_w, BORDER_COLOR);
if (dt.status[0]) {
uint64_t age = montauk::get_milliseconds() - dt.status_time;
Color sc = (age < 5000)
? Color::from_rgb(0x33, 0x33, 0x33)
: Color::from_rgb(0xAA, 0xAA, 0xAA);
px_text(px, g_win_w, g_win_h, 8, sy + (STATUS_H - fh) / 2, dt.status, sc);
}
}
// ============================================================================
// Render: format dialog window
// ============================================================================
void render_format_window() {
auto& dlg = g_state.fmt_dlg;
if (!dlg.open) return;
uint32_t* px = dlg.pixels;
int dw = FMT_DLG_W, dh = FMT_DLG_H;
int fh = font_h();
px_fill(px, dw, dh, 0, 0, dw, dh, BG_COLOR);
// Title
const char* title = "Format Partition";
int tw = text_w(title);
px_text(px, dw, dh, (dw - tw) / 2, 12, title, TEXT_COLOR);
// Partition description
int ddw = text_w(dlg.part_desc);
px_text(px, dw, dh, (dw - ddw) / 2, 12 + fh + 6, dlg.part_desc, DIM_TEXT);
// Filesystem selector
int sel_y = 12 + fh * 2 + 18;
px_text(px, dw, dh, 16, sel_y, "Filesystem:", TEXT_COLOR);
int opt_y = sel_y + fh + 8;
for (int i = 0; i < NUM_FS_TYPES; i++) {
int ox = 24;
int ow = dw - 48;
int oh = 28;
int iy = opt_y + i * (oh + 4);
if (i == dlg.selected_fs) {
px_fill_rounded(px, dw, dh, ox, iy, ow, oh, 6,
Color::from_rgb(0xD0, 0xE0, 0xF0));
px_fill_rounded(px, dw, dh, ox - 1, iy - 1, ow + 2, oh + 2, 7, Color::from_rgb(0x42, 0x7A, 0xB5));
px_fill_rounded(px, dw, dh, ox, iy, ow, oh, 6, Color::from_rgb(0xD0, 0xE0, 0xF0));
} else {
px_fill_rounded(px, dw, dh, ox - 1, iy - 1, ow + 2, oh + 2, 7, BORDER_COLOR);
px_fill_rounded(px, dw, dh, ox, iy, ow, oh, 6, BG_COLOR);
}
// Radio indicator (circular)
int cx = ox + 14;
int cy = iy + oh / 2;
px_fill_rounded(px, dw, dh, cx - 5, cy - 5, 10, 10, 5, DIM_TEXT);
px_fill_rounded(px, dw, dh, cx - 4, cy - 4, 8, 8, 4, BG_COLOR);
if (i == dlg.selected_fs)
px_fill_rounded(px, dw, dh, cx - 3, cy - 3, 6, 6, 3, Color::from_rgb(0x42, 0x7A, 0xB5));
px_text(px, dw, dh, cx + 12, iy + (oh - fh) / 2, g_fsTypes[i].name, TEXT_COLOR);
}
// Buttons
int btn_w = 90, btn_h = 30;
int btn_y = dh - btn_h - 16;
int gap = 16;
int total_w = btn_w * 2 + gap;
int bx = (dw - total_w) / 2;
px_button(px, dw, dh, bx, btn_y, btn_w, btn_h, "Format", true, dlg.hover_format, true);
px_button(px, dw, dh, bx + btn_w + gap, btn_y, btn_w, btn_h, "Cancel", true, dlg.hover_cancel, false);
}
// ============================================================================
// Render: new partition confirmation dialog
// ============================================================================
void render_newpart_window() {
auto& dlg = g_state.np_dlg;
if (!dlg.open) return;
uint32_t* px = dlg.pixels;
int dw = NP_DLG_W, dh = NP_DLG_H;
int fh = font_h();
px_fill(px, dw, dh, 0, 0, dw, dh, BG_COLOR);
// Title
const char* title = dlg.will_init_gpt ? "Initialize Disk" : "New Partition";
int tw = text_w(title);
px_text(px, dw, dh, (dw - tw) / 2, 12, title, TEXT_COLOR);
// Disk description
int ddw = text_w(dlg.disk_desc);
px_text(px, dw, dh, (dw - ddw) / 2, 12 + fh + 6, dlg.disk_desc, DIM_TEXT);
// Warning lines
Color warn_color = dlg.will_init_gpt
? Color::from_rgb(0xCC, 0x22, 0x22)
: TEXT_COLOR;
int wy = 12 + fh * 2 + 20;
int w1w = text_w(dlg.warn_line1);
px_text(px, dw, dh, (dw - w1w) / 2, wy, dlg.warn_line1, warn_color);
int w2w = text_w(dlg.warn_line2);
px_text(px, dw, dh, (dw - w2w) / 2, wy + fh + 4, dlg.warn_line2, warn_color);
// Buttons
int btn_w = 90, btn_h = 30;
int btn_y = dh - btn_h - 16;
int gap = 16;
int total_w = btn_w * 2 + gap;
int bx = (dw - total_w) / 2;
const char* confirm_label = dlg.will_init_gpt ? "Initialize" : "Create";
px_button(px, dw, dh, bx, btn_y, btn_w, btn_h, confirm_label, true, dlg.hover_confirm, true);
px_button(px, dw, dh, bx + btn_w + gap, btn_y, btn_w, btn_h, "Cancel", true, dlg.hover_cancel, false);
}
// ============================================================================
// Top-level render
// ============================================================================
void render(uint32_t* pixels) {
px_fill(pixels, g_win_w, g_win_h, 0, 0, g_win_w, g_win_h, BG_COLOR);
render_toolbar(pixels);
render_content(pixels);
render_status(pixels);
}