Files
MontaukOS/programs/src/installer/installer.h
T
danielandClaude Opus 4.8 0377be4524 feat: redesign installer on MTK toolkit with component selection
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]>
2026-07-17 20:52:19 +02:00

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);