/* * installer.h * Shared header for the MontaukOS Installer * Copyright (c) 2026 Daniel Hammer */ #pragma once #include #include #include #include #include #include extern "C" { #include #include } using namespace gui; // ============================================================================ // Constants // ============================================================================ static constexpr int INIT_W = 560; static constexpr int INIT_H = 500; static constexpr int HEADER_H = 44; static constexpr int STEP_BAR_H = 30; static constexpr int CONTENT_TOP = HEADER_H + STEP_BAR_H; static constexpr int BOTTOM_H = 48; static constexpr int PAD = 16; static constexpr int BTN_H = 30; static constexpr int MAX_DISKS = 32; static constexpr int MAX_PARTS = 32; static constexpr int COMP_ROW_H = 28; static constexpr int COMP_INDENT = 26; static constexpr int COMP_TOGGLE_W = 18; // ============================================================================ // Install steps // ============================================================================ enum InstallerMode { MODE_INSTALL = 0, MODE_UPDATE, }; enum InstallStep { STEP_MODE_SELECT, STEP_SELECT_DISK, STEP_PARTITION_SCHEME, STEP_COMPONENTS, STEP_CONFIRM, STEP_INSTALLING, STEP_DONE, STEP_ERROR, // Update flow STEP_UPDATE_SELECT_PART, STEP_UPDATE_CONFIRM, STEP_UPDATING, }; // ============================================================================ // Partition schemes // ============================================================================ enum PartScheme { SCHEME_EFI_EXT2 = 0, SCHEME_SINGLE_FAT32, SCHEME_COUNT, }; static const char* scheme_names[] = { "EFI + ext2 (recommended)", "Single FAT32 partition (entire disk)", }; static const char* scheme_descs[] = { "EFI boot partition + ext2 root filesystem.", "One partition for boot, OS, and data. Simple and compatible.", }; // ============================================================================ // Optional components // ============================================================================ static constexpr int MAX_COMPONENT_PATHS = 16; struct Component { const char* id; const char* label; const char* desc; // shown in the status bar on hover int parent; // index of parent component, or -1 for top level // Path that identifies this component in an image / on a target drive. const char* probe; // Files or directories (relative to the drive root, no "0:/" prefix) // owned by this component; nullptr-terminated. Empty for pure groups. const char* paths[MAX_COMPONENT_PATHS]; // A child that needs its parent's base payload (e.g. gcc needs the // SDK sysroot). Checking it re-checks the parent. bool requires_parent; // Another component this one cannot work without (e.g. gcc drives the // binutils assembler/linker), or -1. int depends_on; // Runtime state bool checked; bool expanded; // parents only bool present; // this component (or a child) exists in the image bool base_present; // own probe exists (parents with own payload) uint64_t bytes; // exclusive payload size in the source image }; extern Component g_components[]; extern const int g_component_count; // components.cpp void components_init(); void components_compute_sizes(); bool component_has_children(int idx); mtk::CheckState component_state(int idx); void component_toggle(int idx); int component_visible_rows(int* out, int max); bool component_allows(const char* rel); bool component_subtree_needed(const char* rel); void components_set_from_target(int drive); uint64_t components_selected_bytes(); uint64_t components_unselected_bytes(); void components_excluded_summary(char* buf, int bufsize); bool components_sizes_ready(); // ============================================================================ // State // ============================================================================ static constexpr int LOG_LINES = 16; static constexpr int LOG_LINE_LEN = 64; struct InstallerState { int mode; // InstallerMode // Install flow montauk::abi::DiskInfo disks[MAX_DISKS]; int disk_count; int selected_disk; int partition_scheme; // Update flow montauk::abi::PartInfo parts[MAX_PARTS]; int part_count; int selected_part; InstallStep step; char log[LOG_LINES][LOG_LINE_LEN]; int log_count; char status[96]; uint64_t status_time; int mouse_x; int mouse_y; int comp_scroll; }; // ============================================================================ // Global state (extern — defined in main.cpp) // ============================================================================ extern int g_win_w, g_win_h; extern InstallerState g_state; extern Color g_accent; // ============================================================================ // Shared layout helpers // ============================================================================ inline int content_x() { return PAD; } inline int content_w() { return g_win_w - PAD * 2; } inline int content_bottom() { return g_win_h - BOTTOM_H; } // Heading block: title line + smaller subtitle line inline int heading_bottom() { return CONTENT_TOP + 14 + system_font_height() * 2 + 16; } inline Rect bottom_bar_rect() { return {0, g_win_h - BOTTOM_H, g_win_w, BOTTOM_H}; } inline Rect primary_btn_rect() { int y = g_win_h - BOTTOM_H + (BOTTOM_H - BTN_H) / 2; return {g_win_w - PAD - 108, y, 108, BTN_H}; } inline Rect secondary_btn_rect() { Rect p = primary_btn_rect(); return {p.x - 8 - 96, p.y, 96, BTN_H}; } inline Rect tertiary_btn_rect() { Rect s = secondary_btn_rect(); return {s.x - 8 - 84, s.y, 84, BTN_H}; } // Mode select cards inline Rect mode_card_rect(int index) { int card_h = 56; int y = heading_bottom() + index * (card_h + 10); return {content_x(), y, content_w(), card_h}; } // Disk / partition / scheme list rows inline Rect list_row_rect(int index, int row_h) { int y = heading_bottom() + index * (row_h + 6); return {content_x(), y, content_w(), row_h}; } static constexpr int DISK_ROW_H = 48; static constexpr int SCHEME_ROW_H = 50; static constexpr int PART_ROW_H = 48; // Component tree viewport inline Rect components_list_rect() { int top = heading_bottom(); return {content_x(), top, content_w(), content_bottom() - top - 8}; } inline Rect component_row_rect(int visible_index) { Rect list = components_list_rect(); return {list.x, list.y + visible_index * COMP_ROW_H - g_state.comp_scroll, list.w, COMP_ROW_H}; } // ============================================================================ // Function declarations — helpers (main.cpp) // ============================================================================ void set_status(const char* msg); void add_log(const char* msg); void flush_ui(); void format_disk_size(char* buf, int bufsize, uint64_t sectors, uint16_t sectorSize); void format_bytes(char* buf, int bufsize, uint64_t bytes); // ============================================================================ // Function declarations — render.cpp // ============================================================================ void render(uint32_t* pixels); // ============================================================================ // Function declarations — actions.cpp // ============================================================================ void installer_refresh_disks(); void installer_refresh_parts(); void do_install(); void do_update(); bool installer_path_exists(const char* abs_path); // Heap-backed directory listing. Entries are full paths relative to the // drive root (e.g. "apps/doom"), with is_dir derived from the trailing '/' // the VFS appends to directory names. Survives nested listings, unlike the // kernel's rotating readdir scratch pages. struct DirEntry { char* rel; // path relative to drive root, no trailing '/' bool is_dir; }; struct DirList { DirEntry* entries; int count; }; bool installer_list_dir(const char* abs_dir, DirList* out); void installer_free_dir_list(DirList* list);