Rewrite the Installer app on the Montauk Toolkit (Canvas + gui/mtk widgets, theme, hover states, scrollbar) replacing the hand-rolled px_* renderer and bespoke TrueType usage. Add a new "Software" step: an expandable checkbox tree for optional components (Montauk SDK with gcc/g++, binutils, tcc, lua sub-items; Games; Office; Internet apps; Printing; experimental httpd and SDR). Unchecked components' paths are excluded from the install copy, with a longest-path ownership rule so e.g. sdk/tcc installs even when the rest of the SDK is deselected. The update flow refreshes only the components the target already has. Add tri-state checkbox and disclosure-arrow widgets to the MTK toolkit (anti-aliased, supersampled). Merge tcc and lua into 0:/sdk (0:/sdk/tcc, 0:/sdk/lua) and drop 0:/lib: update tcc config.h, lua luaconf.h, both Makefiles, the devkit clean scope, man pages and montaukos.org notices. Make printing optional: init skips printd when 0:/os/printd.elf is absent. Co-Authored-By: Claude Opus 4.8 <[email protected]>
279 lines
8.3 KiB
C++
279 lines
8.3 KiB
C++
/*
|
|
* installer.h
|
|
* Shared header for the MontaukOS Installer
|
|
* 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/font.hpp>
|
|
#include <gui/mtk.hpp>
|
|
|
|
extern "C" {
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
}
|
|
|
|
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);
|