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

498 lines
16 KiB
C++

/*
* main.cpp
* MontaukOS Disk Tool — entry point, event loop, state
* Copyright (c) 2026 Daniel Hammer
*/
#include "disks.h"
#include <gui/mtk/settings.hpp>
#include <gui/standalone.hpp>
// ============================================================================
// Global state definitions
// ============================================================================
int g_win_w = INIT_W;
int g_win_h = INIT_H;
DiskToolState g_state;
Color g_accent = colors::ACCENT;
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 disk_block_dev(int diskIndex) {
if (diskIndex < 0 || diskIndex >= g_state.disk_count) return -1;
return (int)g_state.disks[diskIndex].port;
}
int selected_block_dev() {
return disk_block_dev(g_state.selected_disk);
}
int get_disk_parts(int* indices, int max) {
int blockDev = selected_block_dev();
if (blockDev < 0) return 0;
int count = 0;
for (int i = 0; i < g_state.part_count && count < max; i++) {
if (g_state.parts[i].blockDev == blockDev) {
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
// ============================================================================
static bool handle_toolbar_click(int mx, int my) {
if (my >= TOOLBAR_H || my < TB_BTN_Y) return false;
auto& dt = g_state;
if (dt.disk_count == 0) return false;
// Disk selector buttons
for (int i = 0; i < dt.disk_count; i++) {
if (toolbar_disk_button_rect(i).contains(mx, my)) {
dt.selected_disk = i;
dt.selected_part = -1;
dt.scroll_y = 0;
return true;
}
}
return false;
}
static bool handle_action_click(int mx, int my) {
if (my < g_win_h - STATUS_H) return false;
auto& dt = g_state;
if (dt.disk_count == 0) return false;
bool has_sel = dt.selected_part >= 0;
if (toolbar_refresh_button_rect().contains(mx, my)) {
disktool_refresh();
set_status("Refreshed");
return true;
}
if (has_sel && toolbar_mount_button_rect().contains(mx, my)) {
do_mount_partition();
return true;
}
if (has_sel && toolbar_format_button_rect().contains(mx, my)) {
open_format_dialog();
return true;
}
if (toolbar_newpart_button_rect().contains(mx, my)) {
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 y = partition_map_y();
// 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::abi::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::abi::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;
}
// Partition list click
if (my >= partition_list_y() && my < g_win_h - STATUS_H) {
int row = (my - partition_list_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;
}
// ============================================================================
// New partition dialog mouse handling
// ============================================================================
static bool handle_newpart_dialog_click(int mx, int my, bool clicked) {
auto& dlg = g_state.np_dlg;
if (!dlg.open) return false;
Rect confirm = dialog_primary_button_rect(NP_DLG_W, NP_DLG_H);
Rect cancel = dialog_secondary_button_rect(NP_DLG_W, NP_DLG_H);
dlg.hover_confirm = confirm.contains(mx, my);
dlg.hover_cancel = cancel.contains(mx, my);
if (!clicked) return true;
if (dlg.hover_confirm) {
newpart_dialog_confirm();
return true;
}
if (dlg.hover_cancel) {
close_newpart_dialog();
return true;
}
return true;
}
// ============================================================================
// 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;
// Button hit testing
Rect format_btn = dialog_primary_button_rect(FMT_DLG_W, FMT_DLG_H);
Rect cancel_btn = dialog_secondary_button_rect(FMT_DLG_W, FMT_DLG_H);
dlg.hover_format = format_btn.contains(mx, my);
dlg.hover_cancel = cancel_btn.contains(mx, my);
if (!clicked) return true;
// FS type selector
for (int i = 0; i < NUM_FS_TYPES; i++) {
if (format_option_rect(FMT_DLG_W, i).contains(mx, my)) {
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;
}
static int hovered_disk_button(int mx, int my) {
for (int i = 0; i < g_state.disk_count; i++) {
if (toolbar_disk_button_rect(i).contains(mx, my))
return i;
}
return -1;
}
static int hovered_partition_row(int mx, int my) {
if (g_state.disk_count == 0 || g_state.selected_disk < 0) return -1;
if (my < partition_list_y() || my >= g_win_h - STATUS_H) return -1;
int part_indices[MAX_PARTS];
int nparts = get_disk_parts(part_indices, MAX_PARTS);
int row = (my - partition_list_y() + g_state.scroll_y) / ITEM_H;
if (row < 0 || row >= nparts) return -1;
return row;
}
static int max_scroll_y() {
int part_indices[MAX_PARTS];
int nparts = get_disk_parts(part_indices, MAX_PARTS);
int visible_h = g_win_h - STATUS_H - partition_list_y();
int total_h = nparts * ITEM_H;
if (total_h <= visible_h) return 0;
return total_h - visible_h;
}
static void clamp_scroll() {
g_state.scroll_y = gui_clamp(g_state.scroll_y, 0, max_scroll_y());
}
static bool handle_mouse(const montauk::abi::WinEvent& ev) {
int prev_x = g_state.mouse_x;
int prev_y = g_state.mouse_y;
int prev_disk_hover = hovered_disk_button(prev_x, prev_y);
int prev_part_hover = hovered_partition_row(prev_x, prev_y);
bool prev_refresh_hover = toolbar_refresh_button_rect().contains(prev_x, prev_y);
bool prev_mount_hover = toolbar_mount_button_rect().contains(prev_x, prev_y);
bool prev_format_hover = toolbar_format_button_rect().contains(prev_x, prev_y);
bool prev_newpart_hover = toolbar_newpart_button_rect().contains(prev_x, prev_y);
g_state.mouse_x = ev.mouse.x;
g_state.mouse_y = ev.mouse.y;
bool redraw = false;
redraw |= (prev_disk_hover != hovered_disk_button(g_state.mouse_x, g_state.mouse_y));
redraw |= (prev_part_hover != hovered_partition_row(g_state.mouse_x, g_state.mouse_y));
redraw |= (prev_refresh_hover != toolbar_refresh_button_rect().contains(g_state.mouse_x, g_state.mouse_y));
redraw |= (prev_mount_hover != toolbar_mount_button_rect().contains(g_state.mouse_x, g_state.mouse_y));
redraw |= (prev_format_hover != toolbar_format_button_rect().contains(g_state.mouse_x, g_state.mouse_y));
redraw |= (prev_newpart_hover != toolbar_newpart_button_rect().contains(g_state.mouse_x, g_state.mouse_y));
if (ev.mouse.scroll != 0) {
g_state.scroll_y -= ev.mouse.scroll * 20;
clamp_scroll();
return true;
}
bool clicked = (ev.mouse.buttons & 1) && !(ev.mouse.prev_buttons & 1);
if (clicked) {
if (handle_toolbar_click(ev.mouse.x, ev.mouse.y) ||
handle_action_click(ev.mouse.x, ev.mouse.y) ||
handle_content_click(ev.mouse.x, ev.mouse.y))
return true;
}
return redraw;
}
// ============================================================================
// Key handling
// ============================================================================
static bool handle_key(const montauk::abi::KeyEvent& key) {
if (!key.pressed) return false;
auto& dt = g_state;
// New partition dialog keys
if (dt.np_dlg.open) {
if (key.scancode == 0x01) { // Escape
close_newpart_dialog();
} else if (key.ascii == '\n' || key.ascii == '\r') {
newpart_dialog_confirm();
}
return true;
}
// 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;
g_state.mouse_x = -1;
g_state.mouse_y = -1;
if (!fonts::init())
montauk::exit(1);
g_accent = mtk::load_system_accent();
disktool_refresh();
WsWindow win;
if (!win.create("Disks", INIT_W, INIT_H))
montauk::exit(1);
uint32_t* pixels = win.pixels;
render(pixels);
win.present();
while (true) {
montauk::abi::WinEvent ev;
bool redraw_main = false;
bool redraw_dlg = false;
bool redraw_np = false;
// Poll new partition dialog window
if (g_state.np_dlg.open) {
int dr = montauk::win_poll(g_state.np_dlg.win_id, &ev);
if (dr > 0) {
if (ev.type == 3) { // close
close_newpart_dialog();
redraw_main = true;
} else if (ev.type == 0 && ev.key.pressed) {
handle_key(ev.key);
redraw_np = g_state.np_dlg.open;
redraw_main = true;
} else if (ev.type == 1) {
bool clicked = (ev.mouse.buttons & 1) && !(ev.mouse.prev_buttons & 1);
handle_newpart_dialog_click(ev.mouse.x, ev.mouse.y, clicked);
redraw_np = g_state.np_dlg.open;
redraw_main = true;
}
}
}
// 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 = win.poll(&ev);
if (r < 0) break;
if (r == 0) {
// Even with no main event, dialog may have triggered redraws
if (redraw_main) { render(pixels); win.present(); }
if (redraw_dlg) { render_format_window(); montauk::win_present(g_state.fmt_dlg.win_id); }
if (redraw_np) { render_newpart_window(); montauk::win_present(g_state.np_dlg.win_id); }
if (!redraw_main && !redraw_dlg && !redraw_np) montauk::sleep_ms(16);
continue;
}
if (ev.type == 3) break; // close
// Resize
if (ev.type == 2) {
g_win_w = win.width;
g_win_h = win.height;
pixels = win.pixels;
clamp_scroll();
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) {
redraw_main |= handle_mouse(ev);
}
if (redraw_main) { render(pixels); win.present(); }
if (redraw_dlg) { render_format_window(); montauk::win_present(g_state.fmt_dlg.win_id); }
if (redraw_np) { render_newpart_window(); montauk::win_present(g_state.np_dlg.win_id); }
}
close_newpart_dialog();
close_format_dialog();
win.destroy();
montauk::exit(0);
}