feat: GPT, FAT32 driver, disks app, userspace adapted for multiple drives, and more

This commit is contained in:
2026-03-07 15:58:27 +01:00
parent 8c5c259f5d
commit 4d0177d55e
48 changed files with 5777 additions and 873 deletions
+86
View File
@@ -0,0 +1,86 @@
# Makefile for disks (standalone Disk Tool) on MontaukOS
# Copyright (c) 2026 Daniel Hammer
MAKEFLAGS += -rR
.SUFFIXES:
# ---- Toolchain ----
TOOLCHAIN_PREFIX := $(shell cd ../../.. && pwd)/toolchain/local/bin/x86_64-elf-
ifneq ($(wildcard $(TOOLCHAIN_PREFIX)gcc),)
CXX := $(TOOLCHAIN_PREFIX)g++
else
CXX := g++
endif
# ---- Paths ----
PROG_INC := ../../include
LINK_LD := ../../link.ld
BINDIR := ../../bin
OBJDIR := obj
LIBDIR := ../../lib
# ---- Compiler flags ----
CXXFLAGS := \
-std=gnu++20 \
-g -O2 -pipe \
-Wall \
-Wextra \
-Wno-unused-parameter \
-Wno-unused-function \
-nostdinc \
-ffreestanding \
-fno-stack-protector \
-fno-stack-check \
-fno-PIC \
-fno-rtti \
-fno-exceptions \
-ffunction-sections \
-fdata-sections \
-m64 \
-march=x86-64 \
-msse \
-msse2 \
-mno-red-zone \
-mcmodel=small \
-I $(PROG_INC) \
-isystem $(PROG_INC)/libc \
-isystem ../../../kernel/freestnd-c-hdrs/x86_64/include \
-isystem ../../../kernel/freestnd-cxx-hdrs/x86_64/include
# ---- Linker flags ----
LDFLAGS := \
-nostdlib \
-static \
-Wl,--build-id=none \
-Wl,--gc-sections \
-Wl,-m,elf_x86_64 \
-z max-page-size=0x1000 \
-T $(LINK_LD)
# ---- Source files ----
SRCS := main.cpp render.cpp actions.cpp stb_truetype_impl.cpp
OBJS := $(addprefix $(OBJDIR)/,$(SRCS:.cpp=.o))
# ---- Target ----
TARGET := $(BINDIR)/os/disks.elf
.PHONY: all clean
all: $(TARGET)
$(TARGET): $(OBJS) $(LINK_LD) Makefile
mkdir -p $(BINDIR)/os
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBDIR)/libc/liblibc.a -o $@
$(OBJDIR)/%.o: %.cpp Makefile
mkdir -p $(OBJDIR)
$(CXX) $(CXXFLAGS) -c $< -o $@
clean:
rm -rf $(OBJDIR) $(TARGET)
+179
View File
@@ -0,0 +1,179 @@
/*
* actions.cpp
* MontaukOS Disk Tool — disk operations
* Copyright (c) 2026 Daniel Hammer
*/
#include "disks.h"
// ============================================================================
// Refresh
// ============================================================================
void disktool_refresh() {
auto& dt = g_state;
dt.disk_count = 0;
for (int port = 0; port < MAX_DISKS; port++) {
Montauk::DiskInfo info;
montauk::memset(&info, 0, sizeof(info));
int r = montauk::diskinfo(&info, port);
if (r == 0 && info.type != 0) {
dt.disks[dt.disk_count++] = info;
}
}
dt.part_count = montauk::partlist(dt.parts, MAX_PARTS);
if (dt.selected_disk >= dt.disk_count)
dt.selected_disk = dt.disk_count > 0 ? 0 : -1;
dt.selected_part = -1;
dt.scroll_y = 0;
}
// ============================================================================
// Create partition
// ============================================================================
void do_create_partition() {
auto& dt = g_state;
if (dt.selected_disk < 0 || dt.selected_disk >= dt.disk_count) return;
int part_indices[MAX_PARTS];
int nparts = get_disk_parts(part_indices, MAX_PARTS);
if (nparts == 0) {
int r = montauk::gpt_init(dt.selected_disk);
if (r < 0) {
set_status("Failed to initialize GPT on disk");
return;
}
set_status("Initialized GPT");
}
Montauk::GptAddParams params;
montauk::memset(&params, 0, sizeof(params));
params.blockDev = dt.selected_disk;
params.startLba = 0;
params.endLba = 0;
params.typeGuid.Data1 = 0xEBD0A0A2;
params.typeGuid.Data2 = 0xB9E5;
params.typeGuid.Data3 = 0x4433;
params.typeGuid.Data4[0] = 0x87; params.typeGuid.Data4[1] = 0xC0;
params.typeGuid.Data4[2] = 0x68; params.typeGuid.Data4[3] = 0xB6;
params.typeGuid.Data4[4] = 0xB7; params.typeGuid.Data4[5] = 0x26;
params.typeGuid.Data4[6] = 0x99; params.typeGuid.Data4[7] = 0xC7;
const char* pname = "Data";
int i = 0;
for (; pname[i] && i < 71; i++) params.name[i] = pname[i];
params.name[i] = '\0';
int r = montauk::gpt_add(&params);
if (r < 0) {
set_status("Failed to create partition");
return;
}
set_status("Partition created successfully");
disktool_refresh();
}
// ============================================================================
// Mount partition
// ============================================================================
void do_mount_partition() {
auto& dt = g_state;
if (dt.selected_part < 0) {
set_status("Select a partition first");
return;
}
int part_indices[MAX_PARTS];
int nparts = get_disk_parts(part_indices, MAX_PARTS);
if (dt.selected_part >= nparts) return;
int global_idx = part_indices[dt.selected_part];
int driveNum = 1 + global_idx;
if (driveNum >= 16) driveNum = 15;
int r = montauk::fs_mount(global_idx, driveNum);
if (r < 0) {
set_status("Mount failed (no recognized filesystem)");
return;
}
char msg[80];
snprintf(msg, sizeof(msg), "Mounted as drive %d:/", driveNum);
set_status(msg);
}
// ============================================================================
// Format dialog
// ============================================================================
void open_format_dialog() {
auto& dt = g_state;
if (dt.selected_part < 0) {
set_status("Select a partition first");
return;
}
int part_indices[MAX_PARTS];
int nparts = get_disk_parts(part_indices, MAX_PARTS);
if (dt.selected_part >= nparts) return;
int global_idx = part_indices[dt.selected_part];
auto& dlg = dt.fmt_dlg;
dlg.global_part_index = global_idx;
dlg.selected_fs = 0;
dlg.hover_format = false;
dlg.hover_cancel = false;
// Build partition description
Montauk::PartInfo& p = dt.parts[global_idx];
Montauk::DiskInfo& disk = dt.disks[dt.selected_disk];
char sz[24];
format_disk_size(sz, sizeof(sz), p.sectorCount, disk.sectorSizeLog);
const char* pname = p.name[0] ? p.name : "Partition";
snprintf(dlg.part_desc, sizeof(dlg.part_desc), "%s (%s)", pname, sz);
// Create dialog window
Montauk::WinCreateResult wres;
if (montauk::win_create("Format Partition", FMT_DLG_W, FMT_DLG_H, &wres) < 0 || wres.id < 0) {
set_status("Failed to open format dialog");
return;
}
dlg.win_id = wres.id;
dlg.pixels = (uint32_t*)(uintptr_t)wres.pixelVa;
dlg.open = true;
render_format_window();
montauk::win_present(dlg.win_id);
}
void close_format_dialog() {
auto& dlg = g_state.fmt_dlg;
if (!dlg.open) return;
montauk::win_destroy(dlg.win_id);
dlg.open = false;
}
void format_dialog_do_format() {
auto& dt = g_state;
auto& dlg = dt.fmt_dlg;
Montauk::FsFormatParams params;
montauk::memset(&params, 0, sizeof(params));
params.partIndex = dlg.global_part_index;
params.fsType = g_fsTypes[dlg.selected_fs].id;
int r = montauk::fs_format(&params);
if (r == 0) {
set_status("Format complete");
disktool_refresh();
} else {
set_status("Format failed");
}
close_format_dialog();
}
+138
View File
@@ -0,0 +1,138 @@
/*
* disks.h
* Shared header for the MontaukOS Disk Tool
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <montauk/syscall.h>
#include <montauk/string.h>
#include <montauk/heap.h>
#include <gui/gui.hpp>
#include <gui/truetype.hpp>
extern "C" {
#include <string.h>
#include <stdio.h>
}
using namespace gui;
// ============================================================================
// Constants
// ============================================================================
static constexpr int INIT_W = 640;
static constexpr int INIT_H = 460;
static constexpr int TOOLBAR_H = 40;
static constexpr int HEADER_H = 26;
static constexpr int ITEM_H = 32;
static constexpr int MAP_H = 48;
static constexpr int MAP_PAD = 16;
static constexpr int MAX_PARTS = 32;
static constexpr int MAX_DISKS = 8;
static constexpr int STATUS_H = 26;
static constexpr int FONT_SIZE = 16;
static constexpr int TB_BTN_Y = 7;
static constexpr int TB_BTN_H = 26;
static constexpr int TB_BTN_RAD = 6;
static constexpr Color BG_COLOR = Color::from_rgb(0xFF, 0xFF, 0xFF);
static constexpr Color TOOLBAR_BG = Color::from_rgb(0xF5, 0xF5, 0xF5);
static constexpr Color HEADER_BG = Color::from_rgb(0xF0, 0xF0, 0xF0);
static constexpr Color BORDER_COLOR = Color::from_rgb(0xCC, 0xCC, 0xCC);
static constexpr Color TEXT_COLOR = Color::from_rgb(0x22, 0x22, 0x22);
static constexpr Color DIM_TEXT = Color::from_rgb(0x66, 0x66, 0x66);
static constexpr Color FAINT_TEXT = Color::from_rgb(0x88, 0x88, 0x88);
static constexpr Color HOVER_BG = Color::from_rgb(0xE8, 0xF0, 0xF8);
static constexpr Color STATUS_BG_COL = Color::from_rgb(0xF0, 0xF0, 0xF0);
static constexpr Color WHITE = Color::from_rgb(0xFF, 0xFF, 0xFF);
static constexpr int NUM_PART_COLORS = 8;
// ============================================================================
// Filesystem types
// ============================================================================
struct FsTypeEntry {
const char* name;
int id;
};
static const FsTypeEntry g_fsTypes[] = {
{ "FAT32", Montauk::FS_TYPE_FAT32 },
};
static constexpr int NUM_FS_TYPES = 1;
// ============================================================================
// State
// ============================================================================
static constexpr int FMT_DLG_W = 280;
static constexpr int FMT_DLG_H = 220;
struct FormatDialog {
bool open;
int global_part_index;
int selected_fs;
bool hover_format;
bool hover_cancel;
char part_desc[80];
int win_id;
uint32_t* pixels;
};
struct DiskToolState {
Montauk::DiskInfo disks[MAX_DISKS];
int disk_count;
Montauk::PartInfo parts[MAX_PARTS];
int part_count;
int selected_disk;
int selected_part;
int scroll_y;
char status[80];
uint64_t status_time;
FormatDialog fmt_dlg;
};
// ============================================================================
// Global state (extern — defined in main.cpp)
// ============================================================================
extern int g_win_w, g_win_h;
extern DiskToolState g_state;
extern TrueTypeFont* g_font;
// ============================================================================
// Partition colors
// ============================================================================
extern const Color part_colors[NUM_PART_COLORS];
// ============================================================================
// Function declarations — helpers (main.cpp)
// ============================================================================
void set_status(const char* msg);
int get_disk_parts(int* indices, int max);
void format_disk_size(char* buf, int bufsize, uint64_t sectors, uint16_t sectorSize);
// ============================================================================
// Function declarations — render.cpp
// ============================================================================
void render(uint32_t* pixels);
void render_format_window();
// ============================================================================
// Function declarations — actions.cpp
// ============================================================================
void disktool_refresh();
void do_create_partition();
void do_mount_partition();
void open_format_dialog();
void close_format_dialog();
void format_dialog_do_format();
+391
View File
@@ -0,0 +1,391 @@
/*
* main.cpp
* MontaukOS Disk Tool — entry point, event loop, state
* Copyright (c) 2026 Daniel Hammer
*/
#include "disks.h"
// ============================================================================
// Global state definitions
// ============================================================================
int g_win_w = INIT_W;
int g_win_h = INIT_H;
DiskToolState g_state;
TrueTypeFont* g_font = nullptr;
const Color part_colors[NUM_PART_COLORS] = {
Color::from_rgb(0x42, 0x7A, 0xB5),
Color::from_rgb(0x5C, 0xA0, 0x5C),
Color::from_rgb(0xCC, 0x88, 0x33),
Color::from_rgb(0x99, 0x55, 0xBB),
Color::from_rgb(0xCC, 0x55, 0x55),
Color::from_rgb(0x44, 0xAA, 0xAA),
Color::from_rgb(0xBB, 0xBB, 0x44),
Color::from_rgb(0x88, 0x66, 0x44),
};
// ============================================================================
// Helpers
// ============================================================================
void set_status(const char* msg) {
int i = 0;
for (; i < 79 && msg[i]; i++) g_state.status[i] = msg[i];
g_state.status[i] = '\0';
g_state.status_time = montauk::get_milliseconds();
}
int get_disk_parts(int* indices, int max) {
int count = 0;
for (int i = 0; i < g_state.part_count && count < max; i++) {
if (g_state.parts[i].blockDev == g_state.selected_disk) {
indices[count++] = i;
}
}
return count;
}
void format_disk_size(char* buf, int bufsize, uint64_t sectors, uint16_t sectorSize) {
uint64_t bytes = sectors * sectorSize;
uint64_t gb = bytes / (1024ULL * 1024 * 1024);
if (gb >= 1024) {
uint64_t tb = gb / 1024;
uint64_t frac = ((gb % 1024) * 10) / 1024;
snprintf(buf, bufsize, "%lu.%lu TB", (unsigned)tb, (unsigned)frac);
} else if (gb > 0) {
uint64_t frac = ((bytes % (1024ULL * 1024 * 1024)) * 10) / (1024ULL * 1024 * 1024);
snprintf(buf, bufsize, "%lu.%lu GB", (unsigned)gb, (unsigned)frac);
} else {
uint64_t mb = bytes / (1024ULL * 1024);
snprintf(buf, bufsize, "%lu MB", (unsigned)mb);
}
}
// ============================================================================
// Toolbar hit testing (returns button widths matching render layout)
// ============================================================================
static int disk_btn_width(int idx) {
char label[8];
snprintf(label, sizeof(label), "Disk %d", idx);
int lw = g_font ? g_font->measure_text(label, FONT_SIZE) + 16 : 48;
return lw;
}
static bool handle_toolbar_click(int mx, int my) {
if (my >= TOOLBAR_H || my < TB_BTN_Y) return false;
auto& dt = g_state;
// Disk selector buttons
int bx = 8;
for (int i = 0; i < dt.disk_count; i++) {
int lw = disk_btn_width(i);
if (mx >= bx && mx < bx + lw && my >= TB_BTN_Y && my < TB_BTN_Y + TB_BTN_H) {
dt.selected_disk = i;
dt.selected_part = -1;
dt.scroll_y = 0;
return true;
}
bx += lw + 6;
}
// Right-side buttons (must match render layout)
int rx = g_win_w - 8;
int ref_w = 64; rx -= ref_w;
if (mx >= rx && mx < rx + ref_w && my >= TB_BTN_Y && my < TB_BTN_Y + TB_BTN_H) {
disktool_refresh();
set_status("Refreshed");
return true;
}
rx -= 6;
int mnt_w = 60; rx -= mnt_w;
if (mx >= rx && mx < rx + mnt_w && my >= TB_BTN_Y && my < TB_BTN_Y + TB_BTN_H) {
do_mount_partition();
return true;
}
rx -= 6;
int fmt_w = 64; rx -= fmt_w;
if (mx >= rx && mx < rx + fmt_w && my >= TB_BTN_Y && my < TB_BTN_Y + TB_BTN_H) {
open_format_dialog();
return true;
}
rx -= 6;
int np_w = 74; rx -= np_w;
if (mx >= rx && mx < rx + np_w && my >= TB_BTN_Y && my < TB_BTN_Y + TB_BTN_H) {
do_create_partition();
return true;
}
return false;
}
// ============================================================================
// Mouse handling for content area
// ============================================================================
static bool handle_content_click(int mx, int my) {
auto& dt = g_state;
if (dt.disk_count == 0 || dt.selected_disk < 0) return false;
int fh = g_font ? g_font->get_cache(FONT_SIZE)->ascent - g_font->get_cache(FONT_SIZE)->descent : 16;
int y = TOOLBAR_H + 8 + fh + 8;
// Partition map bar click
int map_x = MAP_PAD;
int map_w = g_win_w - MAP_PAD * 2;
if (my >= y && my < y + MAP_H) {
Montauk::DiskInfo& disk = dt.disks[dt.selected_disk];
int part_indices[MAX_PARTS];
int nparts = get_disk_parts(part_indices, MAX_PARTS);
uint64_t total = disk.sectorCount;
if (total > 0) {
for (int pi = 0; pi < nparts; pi++) {
Montauk::PartInfo& p = dt.parts[part_indices[pi]];
int px = map_x + (int)((p.startLba * (uint64_t)map_w) / total);
int pw = (int)((p.sectorCount * (uint64_t)map_w) / total);
if (pw < 2) pw = 2;
if (mx >= px && mx < px + pw) {
dt.selected_part = pi;
return true;
}
}
}
dt.selected_part = -1;
return true;
}
y += MAP_H + 8 + HEADER_H;
// Partition list click
int list_bottom = g_win_h - STATUS_H;
if (my >= y && my < list_bottom) {
int row = (my - y + dt.scroll_y) / ITEM_H;
int part_indices[MAX_PARTS];
int nparts = get_disk_parts(part_indices, MAX_PARTS);
if (row >= 0 && row < nparts)
dt.selected_part = row;
else
dt.selected_part = -1;
return true;
}
return false;
}
// ============================================================================
// Format dialog mouse handling
// ============================================================================
static bool handle_format_dialog_click(int mx, int my, bool clicked) {
auto& dlg = g_state.fmt_dlg;
if (!dlg.open) return false;
int dw = FMT_DLG_W, dh = FMT_DLG_H;
int fh = g_font ? g_font->get_cache(FONT_SIZE)->ascent - g_font->get_cache(FONT_SIZE)->descent : 16;
// Button hit testing
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;
dlg.hover_format = (mx >= bx && mx < bx + btn_w && my >= btn_y && my < btn_y + btn_h);
dlg.hover_cancel = (mx >= bx + btn_w + gap && mx < bx + btn_w * 2 + gap && my >= btn_y && my < btn_y + btn_h);
if (!clicked) return true;
// FS type selector
int sel_y = 12 + fh * 2 + 18;
int opt_y = sel_y + fh + 8;
for (int i = 0; i < NUM_FS_TYPES; i++) {
int iy = opt_y + i * 32;
if (mx >= 24 && mx < dw - 24 && my >= iy && my < iy + 28) {
dlg.selected_fs = i;
return true;
}
}
if (dlg.hover_format) {
format_dialog_do_format();
return true;
}
if (dlg.hover_cancel) {
close_format_dialog();
return true;
}
return true;
}
// ============================================================================
// Key handling
// ============================================================================
static bool handle_key(const Montauk::KeyEvent& key) {
if (!key.pressed) return false;
auto& dt = g_state;
// Format dialog keys
if (dt.fmt_dlg.open) {
if (key.scancode == 0x01) { // Escape
close_format_dialog();
} else if (key.scancode == 0x48 && dt.fmt_dlg.selected_fs > 0) {
dt.fmt_dlg.selected_fs--;
} else if (key.scancode == 0x50 && dt.fmt_dlg.selected_fs < NUM_FS_TYPES - 1) {
dt.fmt_dlg.selected_fs++;
} else if (key.ascii == '\n' || key.ascii == '\r') {
format_dialog_do_format();
}
return true;
}
int part_indices[MAX_PARTS];
int nparts = get_disk_parts(part_indices, MAX_PARTS);
if (key.scancode == 0x01) { // Escape
return false; // signal quit
} else if (key.scancode == 0x48) { // Up
if (dt.selected_part > 0) dt.selected_part--;
else if (nparts > 0) dt.selected_part = 0;
} else if (key.scancode == 0x50) { // Down
if (dt.selected_part < nparts - 1) dt.selected_part++;
} else if (key.scancode == 0x4B) { // Left
if (dt.selected_disk > 0) { dt.selected_disk--; dt.selected_part = -1; dt.scroll_y = 0; }
} else if (key.scancode == 0x4D) { // Right
if (dt.selected_disk < dt.disk_count - 1) { dt.selected_disk++; dt.selected_part = -1; dt.scroll_y = 0; }
} else if (key.ascii == 'n' || key.ascii == 'N') {
do_create_partition();
} else if (key.ascii == 'm' || key.ascii == 'M') {
do_mount_partition();
} else if (key.ascii == 'f' || key.ascii == 'F') {
open_format_dialog();
} else {
return true; // consumed but no action
}
return true;
}
// ============================================================================
// Entry point
// ============================================================================
extern "C" void _start() {
montauk::memset(&g_state, 0, sizeof(g_state));
g_state.selected_disk = 0;
g_state.selected_part = -1;
// Load font
{
TrueTypeFont* f = (TrueTypeFont*)montauk::malloc(sizeof(TrueTypeFont));
if (f) {
montauk::memset(f, 0, sizeof(TrueTypeFont));
if (!f->init("0:/fonts/Roboto-Medium.ttf")) { montauk::mfree(f); f = nullptr; }
}
g_font = f;
}
disktool_refresh();
// Create window
Montauk::WinCreateResult wres;
if (montauk::win_create("Disks", INIT_W, INIT_H, &wres) < 0 || wres.id < 0)
montauk::exit(1);
int win_id = wres.id;
uint32_t* pixels = (uint32_t*)(uintptr_t)wres.pixelVa;
render(pixels);
montauk::win_present(win_id);
while (true) {
Montauk::WinEvent ev;
bool redraw_main = false;
bool redraw_dlg = false;
// Poll format dialog window
if (g_state.fmt_dlg.open) {
int dr = montauk::win_poll(g_state.fmt_dlg.win_id, &ev);
if (dr > 0) {
if (ev.type == 3) { // close
close_format_dialog();
redraw_main = true;
} else if (ev.type == 0 && ev.key.pressed) {
handle_key(ev.key);
redraw_dlg = g_state.fmt_dlg.open;
redraw_main = true;
} else if (ev.type == 1) {
bool clicked = (ev.mouse.buttons & 1) && !(ev.mouse.prev_buttons & 1);
handle_format_dialog_click(ev.mouse.x, ev.mouse.y, clicked);
redraw_dlg = g_state.fmt_dlg.open;
redraw_main = true;
}
}
}
// Poll main window
int r = montauk::win_poll(win_id, &ev);
if (r < 0) break;
if (r == 0) {
// Even with no main event, dialog may have triggered redraws
if (redraw_main) { render(pixels); montauk::win_present(win_id); }
if (redraw_dlg) { render_format_window(); montauk::win_present(g_state.fmt_dlg.win_id); }
if (!redraw_main && !redraw_dlg) montauk::sleep_ms(16);
continue;
}
if (ev.type == 3) break; // close
// Resize
if (ev.type == 2) {
g_win_w = ev.resize.w;
g_win_h = ev.resize.h;
pixels = (uint32_t*)(uintptr_t)montauk::win_resize(win_id, g_win_w, g_win_h);
redraw_main = true;
}
// Keyboard
if (ev.type == 0 && ev.key.pressed) {
if (!handle_key(ev.key) && ev.key.scancode == 0x01)
break; // Escape quits when not in dialog
redraw_main = true;
}
// Mouse
if (ev.type == 1) {
int mx = ev.mouse.x;
int my = ev.mouse.y;
bool clicked = (ev.mouse.buttons & 1) && !(ev.mouse.prev_buttons & 1);
// Scroll
if (ev.mouse.scroll != 0) {
g_state.scroll_y -= ev.mouse.scroll * 20;
if (g_state.scroll_y < 0) g_state.scroll_y = 0;
redraw_main = true;
}
if (clicked) {
if (handle_toolbar_click(mx, my) || handle_content_click(mx, my))
redraw_main = true;
}
}
if (redraw_main) { render(pixels); montauk::win_present(win_id); }
if (redraw_dlg) { render_format_window(); montauk::win_present(g_state.fmt_dlg.win_id); }
}
close_format_dialog();
montauk::win_destroy(win_id);
montauk::exit(0);
}
+357
View File
@@ -0,0 +1,357 @@
/*
* 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, Color bg, Color fg, int r) {
px_fill_rounded(px, bw, bh, x, y, w, h, r, 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;
Color bg = (i == dt.selected_disk)
? Color::from_rgb(0x42, 0x7A, 0xB5)
: Color::from_rgb(0xDD, 0xDD, 0xDD);
Color fg = (i == dt.selected_disk) ? WHITE : TEXT_COLOR;
px_button(px, g_win_w, g_win_h, bx, TB_BTN_Y, lw, TB_BTN_H, label, bg, fg, TB_BTN_RAD);
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", Color::from_rgb(0xE0, 0xE0, 0xE0), TEXT_COLOR, TB_BTN_RAD);
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 ? Color::from_rgb(0x42, 0x7A, 0xB5) : Color::from_rgb(0xBB, 0xBB, 0xBB),
WHITE, TB_BTN_RAD);
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 ? Color::from_rgb(0x42, 0x7A, 0xB5) : Color::from_rgb(0xBB, 0xBB, 0xBB),
WHITE, TB_BTN_RAD);
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", Color::from_rgb(0x42, 0x7A, 0xB5), WHITE, TB_BTN_RAD);
}
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;
Color fmt_bg = dlg.hover_format
? Color::from_rgb(0xDD, 0x44, 0x44)
: Color::from_rgb(0xCC, 0x33, 0x33);
px_button(px, dw, dh, bx, btn_y, btn_w, btn_h, "Format", fmt_bg, WHITE, 6);
Color can_bg = dlg.hover_cancel
? Color::from_rgb(0x99, 0x99, 0x99)
: Color::from_rgb(0x88, 0x88, 0x88);
px_button(px, dw, dh, bx + btn_w + gap, btn_y, btn_w, btn_h, "Cancel", can_bg, WHITE, 6);
}
// ============================================================================
// 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);
}
+35
View File
@@ -0,0 +1,35 @@
/*
* stb_truetype_impl.cpp
* Single compilation unit for stb_truetype in MontaukOS freestanding environment
* Copyright (c) 2026 Daniel Hammer
*/
#include <cstdint>
#include <cstddef>
#include <montauk/heap.h>
#include <montauk/string.h>
#include <gui/stb_math.h>
// Override all stb_truetype dependencies before including the implementation
#define STBTT_ifloor(x) ((int) stb_floor(x))
#define STBTT_iceil(x) ((int) stb_ceil(x))
#define STBTT_sqrt(x) stb_sqrt(x)
#define STBTT_pow(x,y) stb_pow(x,y)
#define STBTT_fmod(x,y) stb_fmod(x,y)
#define STBTT_cos(x) stb_cos(x)
#define STBTT_acos(x) stb_acos(x)
#define STBTT_fabs(x) stb_fabs(x)
#define STBTT_malloc(x,u) ((void)(u), montauk::malloc(x))
#define STBTT_free(x,u) ((void)(u), montauk::mfree(x))
#define STBTT_memcpy(d,s,n) montauk::memcpy(d,s,n)
#define STBTT_memset(d,v,n) montauk::memset(d,v,n)
#define STBTT_strlen(x) montauk::slen(x)
#define STBTT_assert(x) ((void)(x))
#define STB_TRUETYPE_IMPLEMENTATION
#include <gui/stb_truetype.h>