/* * main.cpp * MontaukOS Disk Tool — entry point, event loop, state * Copyright (c) 2026 Daniel Hammer */ #include "disks.h" #include #include // ============================================================================ // 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); }