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]>
This commit is contained in:
2026-07-17 20:52:19 +02:00
co-authored by Claude Opus 4.8
parent 8e6b619b02
commit 0377be4524
18 changed files with 1647 additions and 813 deletions
+1 -1
View File
@@ -12,4 +12,4 @@
#pragma once #pragma once
#define MONTAUK_BUILD_NUMBER 29 #define MONTAUK_BUILD_NUMBER 31
+2 -2
View File
@@ -252,7 +252,7 @@ exception, is included on the MontaukOS ISO at 0:/os/licenses/AGPL-3.0-C059.txt.
10. Tiny C Compiler (TCC) 10. Tiny C Compiler (TCC)
================================================================================ ================================================================================
Shipped as the bundled C compiler (tcc.elf) with its runtime libraries and Shipped as the bundled C compiler (tcc.elf) with its runtime libraries and
headers on the ISO (0:/lib/tcc/). MontaukOS ships a modified version of TCC headers on the ISO (0:/sdk/tcc/). MontaukOS ships a modified version of TCC
0.9.28rc, ported to target MontaukOS (added platform startup/compatibility 0.9.28rc, ported to target MontaukOS (added platform startup/compatibility
code and a MontaukOS sysroot). code and a MontaukOS sysroot).
@@ -281,7 +281,7 @@ License: GNU Lesser General Public License, version 2.1 (LGPL-2.1)
11. Lua 11. Lua
================================================================================ ================================================================================
Shipped as the bundled Lua interpreter (lua.elf), Lua 5.4.8, with its static Shipped as the bundled Lua interpreter (lua.elf), Lua 5.4.8, with its static
library and headers on the ISO (0:/lib/lua/). library and headers on the ISO (0:/sdk/lua/).
License: MIT License License: MIT License
+2 -2
View File
@@ -153,9 +153,9 @@
* Roboto font, Copyright The Roboto Project Authors - OFL-1.1 * Roboto font, Copyright The Roboto Project Authors - OFL-1.1
* C059 font (URW Base 35), Copyright (C) (URW)++ Design and Development * C059 font (URW Base 35), Copyright (C) (URW)++ Design and Development
GmbH - AGPLv3 with font-embedding exception GmbH - AGPLv3 with font-embedding exception
* Tiny C Compiler (tcc.elf, 0:/lib/tcc), Copyright (c) Fabrice Bellard and * Tiny C Compiler (tcc.elf, 0:/sdk/tcc), Copyright (c) Fabrice Bellard and
contributors - LGPL-2.1 contributors - LGPL-2.1
* Lua (lua.elf, 0:/lib/lua), Copyright (C) Lua.org, PUC-Rio - MIT * Lua (lua.elf, 0:/sdk/lua), Copyright (C) Lua.org, PUC-Rio - MIT
* Mozilla CA certificate bundle (0:/os/certs), Mozilla CA Certificate * Mozilla CA certificate bundle (0:/os/certs), Mozilla CA Certificate
Program - MPL-2.0 Program - MPL-2.0
* Intel Bluetooth firmware (0:/os/firmware/intel), Copyright (c) Intel * Intel Bluetooth firmware (0:/os/firmware/intel), Copyright (c) Intel
+9 -6
View File
@@ -295,11 +295,11 @@ gen-syscalls:
# check-syscalls guards against shipping a stale C API header into the ramdisk. # check-syscalls guards against shipping a stale C API header into the ramdisk.
tcc: libc check-syscalls tcc: libc check-syscalls
$(MAKE) -C src/tcc $(MAKE) -C src/tcc
mkdir -p $(BINDIR)/lib/tcc/include mkdir -p $(BINDIR)/sdk/tcc/include
cp -r src/tcc/tcc_include/* $(BINDIR)/lib/tcc/include/ cp -r src/tcc/tcc_include/* $(BINDIR)/sdk/tcc/include/
cp include/libc/*.h $(BINDIR)/lib/tcc/include/ cp include/libc/*.h $(BINDIR)/sdk/tcc/include/
mkdir -p $(BINDIR)/lib/tcc/include/sys mkdir -p $(BINDIR)/sdk/tcc/include/sys
cp include/libc/sys/*.h $(BINDIR)/lib/tcc/include/sys/ cp include/libc/sys/*.h $(BINDIR)/sdk/tcc/include/sys/
# Build Lua interpreter and static library (depends on libc). # Build Lua interpreter and static library (depends on libc).
lua: libc lua: libc
@@ -388,7 +388,10 @@ DEVKIT_TOOLS := as ld ar nm objcopy objdump readelf ranlib strip size strings ad
.PHONY: devkit .PHONY: devkit
devkit: libc devkit: libc
ifneq ($(wildcard $(NATIVE_BIN)/as),) ifneq ($(wildcard $(NATIVE_BIN)/as),)
rm -rf $(BINDIR)/sdk # Clean only the devkit-owned subtrees; sdk/tcc and sdk/lua are
# installed into bin/sdk/ by their own targets.
rm -rf $(BINDIR)/sdk/bin $(BINDIR)/sdk/lib $(BINDIR)/sdk/include \
$(BINDIR)/sdk/libexec $(BINDIR)/sdk/x86_64-montauk
mkdir -p $(BINDIR)/sdk/bin $(BINDIR)/sdk/lib $(BINDIR)/sdk/include mkdir -p $(BINDIR)/sdk/bin $(BINDIR)/sdk/lib $(BINDIR)/sdk/include
for t in $(DEVKIT_TOOLS); do \ for t in $(DEVKIT_TOOLS); do \
cp $(NATIVE_BIN)/$$t $(BINDIR)/sdk/bin/$$t.elf || exit 1; \ cp $(NATIVE_BIN)/$$t $(BINDIR)/sdk/bin/$$t.elf || exit 1; \
+211
View File
@@ -942,6 +942,217 @@ inline void draw_radio(Canvas& c,
label, theme.text); label, theme.text);
} }
// ============================================================================
// Checkboxes and disclosure arrows
// ============================================================================
enum CheckState : uint8_t {
CHECK_OFF = 0,
CHECK_ON,
CHECK_MIXED,
};
inline CheckState check_state(bool checked) {
return checked ? CHECK_ON : CHECK_OFF;
}
inline Rect checkbox_indicator_rect(const Rect& option, int size = 16) {
return {option.x, option.y + (option.h - size) / 2, size, size};
}
// Coverage-based pixel blend used by the anti-aliased checkbox drawing.
// `cov` is 0..255; the destination is treated as opaque.
inline void checkbox_blend_px(Canvas& c, int x, int y, Color color, int cov) {
if (x < 0 || x >= c.w || y < 0 || y >= c.h || cov <= 0) return;
if (cov > 255) cov = 255;
uint32_t dst = c.pixels[y * c.w + x];
uint32_t dr = (dst >> 16) & 0xFF, dg = (dst >> 8) & 0xFF, db = dst & 0xFF;
uint32_t a = (uint32_t)cov, ia = 255 - a;
uint32_t nr = (color.r * a + dr * ia + 127) / 255;
uint32_t ng = (color.g * a + dg * ia + 127) / 255;
uint32_t nb = (color.b * a + db * ia + 127) / 255;
c.pixels[y * c.w + x] = 0xFF000000u | (nr << 16) | (ng << 8) | nb;
}
// Point (px, py) inside a rounded rect of size w x h with corner radius r,
// evaluated in the rect's local coordinate space.
inline bool round_rect_contains(double px, double py, double w, double h, double r) {
if (px < 0 || py < 0 || px > w || py > h) return false;
double rx = 0, ry = 0;
bool corner = false;
if (px < r && py < r) { rx = r - px; ry = r - py; corner = true; }
else if (px > w - r && py < r) { rx = px - (w - r); ry = r - py; corner = true; }
else if (px < r && py > h - r) { rx = r - px; ry = py - (h - r); corner = true; }
else if (px > w - r && py > h - r) { rx = px - (w - r); ry = py - (h - r); corner = true; }
if (!corner) return true;
return rx * rx + ry * ry <= r * r;
}
// Anti-aliased rounded-rect fill via 4x4 supersampling. Kept local to the
// checkbox so the rest of the toolkit's crisp fills are unchanged.
inline void checkbox_fill_round_aa(Canvas& c, const Rect& box, double radius, Color color) {
if (box.w <= 0 || box.h <= 0) return;
if (radius < 0) radius = 0;
const int S = 4;
for (int row = -1; row <= box.h; row++) {
int py = box.y + row;
if (py < 0 || py >= c.h) continue;
for (int col = -1; col <= box.w; col++) {
int px = box.x + col;
if (px < 0 || px >= c.w) continue;
int hits = 0;
for (int sy = 0; sy < S; sy++)
for (int sx = 0; sx < S; sx++) {
double fx = col + (sx + 0.5) / S;
double fy = row + (sy + 0.5) / S;
if (round_rect_contains(fx, fy, box.w, box.h, radius)) hits++;
}
if (hits) checkbox_blend_px(c, px, py, color, hits * 255 / (S * S));
}
}
}
// Squared distance from a point to a line segment (no sqrt; used for the
// thickness test of the checkmark strokes).
inline double checkbox_seg_dist2(double px, double py,
double ax, double ay, double bx, double by) {
double dx = bx - ax, dy = by - ay;
double len2 = dx * dx + dy * dy;
double t = len2 > 0 ? ((px - ax) * dx + (py - ay) * dy) / len2 : 0.0;
if (t < 0) t = 0; else if (t > 1) t = 1;
double ex = px - (ax + t * dx), ey = py - (ay + t * dy);
return ex * ex + ey * ey;
}
// Anti-aliased two-stroke checkmark via 4x4 supersampling of a fixed-width
// polyline.
inline void draw_check_mark(Canvas& c, const Rect& box, Color color) {
double ax = box.x + box.w * 0.24, ay = box.y + box.h * 0.50;
double bx = box.x + box.w * 0.42, by = box.y + box.h * 0.68;
double cx = box.x + box.w * 0.74, cy = box.y + box.h * 0.26;
double hw = box.w * 0.085 + 0.35; // half stroke width
double hw2 = hw * hw;
const int S = 4;
for (int py = box.y - 1; py <= box.y + box.h + 1; py++) {
if (py < 0 || py >= c.h) continue;
for (int px = box.x - 1; px <= box.x + box.w + 1; px++) {
if (px < 0 || px >= c.w) continue;
int hits = 0;
for (int sy = 0; sy < S; sy++)
for (int sx = 0; sx < S; sx++) {
double fx = px + (sx + 0.5) / S;
double fy = py + (sy + 0.5) / S;
double d1 = checkbox_seg_dist2(fx, fy, ax, ay, bx, by);
double d2 = checkbox_seg_dist2(fx, fy, bx, by, cx, cy);
if ((d1 < d2 ? d1 : d2) <= hw2) hits++;
}
if (hits) checkbox_blend_px(c, px, py, color, hits * 255 / (S * S));
}
}
}
inline void draw_checkbox(Canvas& c,
const Rect& option,
const char* label,
CheckState state,
const Theme& theme,
bool enabled = true,
bool hovered = false) {
Rect box = checkbox_indicator_rect(option);
double radius = box.w * 0.22;
Color fill, border, mark;
if (!enabled) {
fill = theme.disabled_bg;
border = theme.disabled_bg;
mark = theme.disabled_fg;
} else if (state == CHECK_OFF) {
fill = colors::WHITE;
border = hovered ? theme.accent : theme.border;
mark = theme.accent_fg;
} else {
fill = hovered ? theme.accent_hover : theme.accent;
border = fill;
mark = theme.accent_fg;
}
if (state == CHECK_OFF) {
// Colored ring with a white interior.
checkbox_fill_round_aa(c, box, radius, border);
Rect inner = {box.x + 1, box.y + 1, box.w - 2, box.h - 2};
checkbox_fill_round_aa(c, inner, radius - 1, fill);
} else {
checkbox_fill_round_aa(c, box, radius, fill);
}
if (state == CHECK_ON) {
draw_check_mark(c, box, mark);
} else if (state == CHECK_MIXED) {
Rect dash = {box.x + 4, box.y + box.h / 2 - 1, box.w - 8, 2};
checkbox_fill_round_aa(c, dash, 1, mark);
}
if (label && label[0]) {
int fh = system_font_height();
c.text(box.x + box.w + theme.gap_sm,
option.y + (option.h - fh) / 2,
label, enabled ? theme.text : theme.text_muted);
}
}
inline Rect disclosure_rect(const Rect& option, int size = 16) {
return {option.x, option.y + (option.h - size) / 2, size, size};
}
inline void draw_disclosure(Canvas& c,
const Rect& box,
bool expanded,
const Theme& theme,
bool hovered = false) {
Color color = hovered ? theme.text : theme.text_subtle;
double cx = box.x + box.w / 2.0;
double cy = box.y + box.h / 2.0;
double s = box.w * 0.26;
if (s < 3) s = 3;
// Triangle vertices (equilateral-ish), rotated per state.
double vx[3], vy[3];
if (expanded) { // pointing down
vx[0] = cx - s; vy[0] = cy - s * 0.6;
vx[1] = cx + s; vy[1] = cy - s * 0.6;
vx[2] = cx; vy[2] = cy + s * 0.75;
} else { // pointing right
vx[0] = cx - s * 0.6; vy[0] = cy - s;
vx[1] = cx - s * 0.6; vy[1] = cy + s;
vx[2] = cx + s * 0.75; vy[2] = cy;
}
// Anti-aliased fill via 4x4 supersampling of the triangle's half-plane test.
auto edge = [](double ax, double ay, double bx, double by, double px, double py) {
return (px - ax) * (by - ay) - (py - ay) * (bx - ax);
};
const int S = 4;
for (int py = box.y; py < box.y + box.h; py++) {
if (py < 0 || py >= c.h) continue;
for (int px = box.x; px < box.x + box.w; px++) {
if (px < 0 || px >= c.w) continue;
int hits = 0;
for (int sy = 0; sy < S; sy++)
for (int sx = 0; sx < S; sx++) {
double fx = px + (sx + 0.5) / S;
double fy = py + (sy + 0.5) / S;
double e0 = edge(vx[0], vy[0], vx[1], vy[1], fx, fy);
double e1 = edge(vx[1], vy[1], vx[2], vy[2], fx, fy);
double e2 = edge(vx[2], vy[2], vx[0], vy[0], fx, fy);
bool inside = (e0 >= 0 && e1 >= 0 && e2 >= 0) ||
(e0 <= 0 && e1 <= 0 && e2 <= 0);
if (inside) hits++;
}
if (hits) checkbox_blend_px(c, px, py, color, hits * 255 / (S * S));
}
}
}
inline Rect swatch_rect(int row_x, int row_y, int index, int size = 24, int gap = 6) { inline Rect swatch_rect(int row_x, int row_y, int index, int size = 24, int gap = 6) {
return {row_x + index * (size + gap), row_y, size, size}; return {row_x + index * (size + gap), row_y, size, size};
} }
+4 -3
View File
@@ -52,13 +52,14 @@
0:/icons/ Shared icons 0:/icons/ Shared icons
0:/man/ Manual pages 0:/man/ Manual pages
0:/www/ Web server content 0:/www/ Web server content
0:/lib/ Lua and TinyCC toolchain payloads 0:/sdk/ Montauk SDK (native gcc/binutils, TinyCC,
Lua, headers and libraries), when installed
0:/boot/ Kernel, bootloader, ramdisk image 0:/boot/ Kernel, bootloader, ramdisk image
There is no 0:/games/, 0:/common/, 0:/home/, or 0:/etc/ -- There is no 0:/games/, 0:/common/, 0:/home/, or 0:/etc/ --
these were used by earlier single-user releases and no longer these were used by earlier single-user releases and no longer
exist. Games and other GUI programs (including doom) ship as exist. 0:/lib/ was merged into 0:/sdk/. Games and other GUI
bundles under 0:/apps/. programs (including doom) ship as bundles under 0:/apps/.
.SH SHELL .SH SHELL
The interactive shell is the primary way to interact with The interactive shell is the primary way to interact with
+2 -2
View File
@@ -46,9 +46,9 @@
* Roboto font, Copyright The Roboto Project Authors - OFL-1.1 * Roboto font, Copyright The Roboto Project Authors - OFL-1.1
* C059 font (URW Base 35), Copyright (C) (URW)++ Design and Development * C059 font (URW Base 35), Copyright (C) (URW)++ Design and Development
GmbH - AGPLv3 with font-embedding exception GmbH - AGPLv3 with font-embedding exception
* Tiny C Compiler (tcc.elf, 0:/lib/tcc), Copyright (c) Fabrice Bellard and * Tiny C Compiler (tcc.elf, 0:/sdk/tcc), Copyright (c) Fabrice Bellard and
contributors - LGPL-2.1 contributors - LGPL-2.1
* Lua (lua.elf, 0:/lib/lua), Copyright (C) Lua.org, PUC-Rio - MIT * Lua (lua.elf, 0:/sdk/lua), Copyright (C) Lua.org, PUC-Rio - MIT
* Mozilla CA certificate bundle (0:/os/certs), Mozilla CA Certificate * Mozilla CA certificate bundle (0:/os/certs), Mozilla CA Certificate
Program - MPL-2.0 Program - MPL-2.0
* Intel Bluetooth firmware (0:/os/firmware/intel), Copyright (c) Intel * Intel Bluetooth firmware (0:/os/firmware/intel), Copyright (c) Intel
+13 -1
View File
@@ -124,6 +124,13 @@ static void log_err(const char* msg) { log(LOG_ERR, msg); }
// ---- Service runner ---- // ---- Service runner ----
static bool service_installed(const char* path) {
int fd = montauk::open(path);
if (fd < 0) return false;
montauk::close(fd);
return true;
}
static bool run_service(const char* path, const char* name, bool wait = true) { static bool run_service(const char* path, const char* name, bool wait = true) {
char msg[128]; char msg[128];
@@ -156,7 +163,12 @@ extern "C" void _start() {
// ---- Stage 1: Network configuration (non-blocking) ---- // ---- Stage 1: Network configuration (non-blocking) ----
run_service("0:/os/dhcp.elf", "dhcp", false); run_service("0:/os/dhcp.elf", "dhcp", false);
run_service("0:/os/printd.elf", "print spooler", false);
// Printing is an optional install component; skip quietly when absent.
if (service_installed("0:/os/printd.elf"))
run_service("0:/os/printd.elf", "print spooler", false);
else
log_info("Print spooler not installed, skipping");
// ---- Stage 2: Login screen -> desktop (falls back to desktop, then shell) ---- // ---- Stage 2: Login screen -> desktop (falls back to desktop, then shell) ----
if (!run_service("0:/os/login.elf", "login")) { if (!run_service("0:/os/login.elf", "login")) {
+1 -1
View File
@@ -52,7 +52,7 @@ LDFLAGS := \
# ---- Source files ---- # ---- Source files ----
SRCS := main.cpp render.cpp actions.cpp stb_truetype_impl.cpp SRCS := main.cpp render.cpp actions.cpp components.cpp stb_truetype_impl.cpp
OBJS := $(addprefix $(OBJDIR)/,$(SRCS:.cpp=.o)) OBJS := $(addprefix $(OBJDIR)/,$(SRCS:.cpp=.o))
# ---- Target ---- # ---- Target ----
+141 -84
View File
@@ -81,6 +81,96 @@ static void path_join(char* out, int outsize, const char* dir, const char* name)
out[i] = '\0'; out[i] = '\0';
} }
static const char* rel_basename(const char* rel) {
const char* base = rel;
for (int i = 0; rel[i]; i++)
if (rel[i] == '/') base = rel + i + 1;
return base;
}
// ============================================================================
// Filesystem probes and heap-backed directory listing
// ============================================================================
bool installer_path_exists(const char* abs_path) {
int fd = montauk::open(abs_path);
if (fd >= 0) {
montauk::close(fd);
return true;
}
const char* names[4];
return montauk::readdir_at(abs_path, names, 4, 0) >= 0;
}
void installer_free_dir_list(DirList* list) {
if (!list) return;
for (int i = 0; i < list->count; i++)
montauk::mfree(list->entries[i].rel);
montauk::mfree(list->entries);
list->entries = nullptr;
list->count = 0;
}
// The kernel serves readdir strings from a small ring of scratch pages, so
// nested listings invalidate earlier results. Copy each batch to the heap
// and page with readdir_at so directories of any size enumerate fully.
bool installer_list_dir(const char* abs_dir, DirList* out) {
out->entries = nullptr;
out->count = 0;
static constexpr int BATCH = 128;
const char* batch[BATCH];
int scanned = 0;
int capacity = 0;
int got = montauk::readdir_at(abs_dir, batch, BATCH, 0);
if (got < 0) return false;
while (got > 0) {
for (int i = 0; i < got; i++) {
const char* raw = batch[i];
int len = slen(raw);
bool is_dir = (len > 0 && raw[len - 1] == '/');
if (is_dir) len--;
if (len <= 0) continue;
// Skip "." and ".."
const char* base = raw;
for (int k = 0; k < len; k++)
if (raw[k] == '/') base = raw + k + 1;
int base_len = (int)(len - (base - raw));
if (base_len == 1 && base[0] == '.') continue;
if (base_len == 2 && base[0] == '.' && base[1] == '.') continue;
if (out->count >= capacity) {
int new_cap = capacity ? capacity * 2 : 64;
DirEntry* grown = (DirEntry*)montauk::malloc(new_cap * sizeof(DirEntry));
if (!grown) { installer_free_dir_list(out); return false; }
for (int k = 0; k < out->count; k++) grown[k] = out->entries[k];
montauk::mfree(out->entries);
out->entries = grown;
capacity = new_cap;
}
char* rel = (char*)montauk::malloc(len + 1);
if (!rel) { installer_free_dir_list(out); return false; }
montauk::memcpy(rel, raw, len);
rel[len] = '\0';
out->entries[out->count].rel = rel;
out->entries[out->count].is_dir = is_dir;
out->count++;
}
scanned += got;
got = montauk::readdir_at(abs_dir, batch, BATCH, scanned);
}
return true;
}
// ============================================================================ // ============================================================================
// Copy a single file from src_path to dst_path // Copy a single file from src_path to dst_path
// ============================================================================ // ============================================================================
@@ -159,113 +249,78 @@ static bool copy_file(const char* src_path, const char* dst_path) {
static int g_files_copied; static int g_files_copied;
static int g_dirs_created; static int g_dirs_created;
// Copy all entries from src_dir (on ramdisk) to dst_dir (on target drive). // Copy all entries from src_dir (on ramdisk) to dst_dir (on target drive),
// honoring the component selection: paths owned by an unchecked component
// are skipped, and directories are still entered when a checked component
// lives deeper inside (e.g. sdk/tcc with the rest of sdk deselected).
// If skip_toplevel is non-null, skip that directory name at the top level. // If skip_toplevel is non-null, skip that directory name at the top level.
static bool copy_recursive(const char* src_dir, const char* dst_dir, static bool copy_recursive(const char* src_dir, const char* dst_dir,
const char* skip_toplevel = nullptr) { const char* skip_toplevel = nullptr) {
const char* names[256]; DirList list;
int count = montauk::readdir(src_dir, names, 256); if (!installer_list_dir(src_dir, &list))
if (count < 0) return true; // not a directory or empty, skip return true; // not a directory, skip
// Ramdisk readdir returns names relative to the drive root with the bool ok = true;
// full internal path (e.g., for readdir("0:/os"), names come back as
// "os/init.elf", "os/shell.elf", etc.). We need to strip the prefix
// that corresponds to the local path portion of src_dir.
//
// Find the local path after the "N:/" prefix.
const char* src_local = src_dir;
for (int k = 0; src_local[k]; k++) {
if (src_local[k] == ':') {
src_local += k + 1;
if (src_local[0] == '/') src_local++;
break;
}
}
int prefix_len = slen(src_local);
// If prefix is non-empty, we also skip the trailing '/'
if (prefix_len > 0 && src_local[prefix_len - 1] != '/') prefix_len++;
for (int i = 0; i < count; i++) { for (int i = 0; i < list.count && ok; i++) {
const char* raw_name = names[i]; const char* rel = list.entries[i].rel;
const char* base = rel_basename(rel);
// Strip prefix to get basename char src_path[256];
const char* basename = raw_name; snprintf(src_path, sizeof(src_path), "0:/%s", rel);
if (prefix_len > 0 && slen(raw_name) > prefix_len) {
basename = raw_name + prefix_len;
}
// Skip "." and ".."
if (basename[0] == '.' && (basename[1] == '\0' || basename[1] == '/')) continue;
if (basename[0] == '.' && basename[1] == '.' && (basename[2] == '\0' || basename[2] == '/')) continue;
int blen = slen(basename);
// Check if this is a directory (trailing '/')
bool is_dir = (blen > 0 && basename[blen - 1] == '/');
if (is_dir) {
// Strip trailing '/' for the name
char dir_name[256];
int j = 0;
for (; j < blen - 1 && j < 255; j++) dir_name[j] = basename[j];
dir_name[j] = '\0';
if (list.entries[i].is_dir) {
// Skip the installer app — no need on the installed system // Skip the installer app — no need on the installed system
if (strcmp(dir_name, "installer") == 0 && if (strcmp(rel, "apps/installer") == 0) continue;
strcmp(src_local, "apps") == 0) continue;
// Skip requested top-level directory // Skip requested top-level directory
if (skip_toplevel && strcmp(dir_name, skip_toplevel) == 0) continue; if (skip_toplevel && strcmp(base, skip_toplevel) == 0) continue;
bool allowed = component_allows(rel);
if (!allowed && !component_subtree_needed(rel)) continue;
// Create directory on target
char target_path[256]; char target_path[256];
path_join(target_path, sizeof(target_path), dst_dir, dir_name); path_join(target_path, sizeof(target_path), dst_dir, base);
montauk::fmkdir(target_path); montauk::fmkdir(target_path);
g_dirs_created++; g_dirs_created++;
char log_msg[64]; char log_msg[64];
snprintf(log_msg, sizeof(log_msg), " mkdir %s", dir_name); snprintf(log_msg, sizeof(log_msg), " mkdir %s", base);
add_log(log_msg); add_log(log_msg);
flush_ui(); flush_ui();
// Recurse into this directory ok = copy_recursive(src_path, target_path);
char src_subdir[256];
path_join(src_subdir, sizeof(src_subdir), src_dir, dir_name);
if (!copy_recursive(src_subdir, target_path))
return false;
} else { } else {
// Skip ramdisk and limine.conf — installed system boots from // Skip ramdisk and limine.conf — installed system boots from
// disk and gets a fresh config without the ramdisk module. // disk and gets a fresh config without the ramdisk module.
// Skip setup.toml — live/setup environment config that should // Skip setup.toml — live/setup environment config that should
// not be present on the installed system. // not be present on the installed system.
if (strcmp(basename, "ramdisk.tar") == 0) continue; if (strcmp(base, "ramdisk.tar") == 0) continue;
if (strcmp(basename, "limine.conf") == 0) continue; if (strcmp(base, "limine.conf") == 0) continue;
if (strcmp(basename, "setup.toml") == 0) continue; if (strcmp(base, "setup.toml") == 0) continue;
// It's a file — copy it if (!component_allows(rel)) continue;
char src_path[256];
// Build source path: drive prefix + raw_name (which is the full internal path)
// src_dir starts with "0:/" so we need "0:/" + raw_name
snprintf(src_path, sizeof(src_path), "0:/%s", raw_name);
char dst_path[256]; char dst_path[256];
path_join(dst_path, sizeof(dst_path), dst_dir, basename); path_join(dst_path, sizeof(dst_path), dst_dir, base);
char log_msg[64]; char log_msg[64];
snprintf(log_msg, sizeof(log_msg), " copy %s", basename); snprintf(log_msg, sizeof(log_msg), " copy %s", base);
add_log(log_msg); add_log(log_msg);
flush_ui(); flush_ui();
if (!copy_file(src_path, dst_path)) if (!copy_file(src_path, dst_path)) {
return false; ok = false;
break;
}
g_files_copied++; g_files_copied++;
} }
} }
return true; installer_free_dir_list(&list);
return ok;
} }
// ============================================================================ // ============================================================================
@@ -412,8 +467,7 @@ static void install_efi_ext2(int disk) {
add_log(" Mounted"); add_log(" Mounted");
flush_ui(); flush_ui();
char efi_root[8], ext2_root[8]; char ext2_root[8];
snprintf(efi_root, sizeof(efi_root), "%d:/", efi_drive);
snprintf(ext2_root, sizeof(ext2_root), "%d:/", root_drive); snprintf(ext2_root, sizeof(ext2_root), "%d:/", root_drive);
// Step 8: Create EFI boot directory structure // Step 8: Create EFI boot directory structure
@@ -705,13 +759,14 @@ void do_update() {
add_log("ERROR: Mount failed"); add_log("ERROR: Mount failed");
flush_ui(); st.step = STEP_ERROR; return; flush_ui(); st.step = STEP_ERROR; return;
} }
char drive_root[8];
snprintf(drive_root, sizeof(drive_root), "%d:/", drive_num);
add_log(" Mounted"); add_log(" Mounted");
flush_ui(); flush_ui();
// Step 2: Update os/ — remove old and copy fresh // Step 2: Detect which optional components the target has, so the
// update refreshes those without re-adding deselected ones.
components_set_from_target(drive_num);
// Step 3: Update os/ — overwrite existing and add new
add_log("Updating os/..."); add_log("Updating os/...");
flush_ui(); flush_ui();
@@ -724,7 +779,7 @@ void do_update() {
flush_ui(); st.step = STEP_ERROR; return; flush_ui(); st.step = STEP_ERROR; return;
} }
// Step 3: Update apps/ — copy all, overwriting existing and adding new // Step 4: Update apps/ — copy all, overwriting existing and adding new
add_log("Updating apps/..."); add_log("Updating apps/...");
flush_ui(); flush_ui();
@@ -736,16 +791,18 @@ void do_update() {
flush_ui(); st.step = STEP_ERROR; return; flush_ui(); st.step = STEP_ERROR; return;
} }
// Step 4: Update lib/ — compiler runtime, headers, libc // Step 5: Update sdk/ — dev tools, headers, tcc, lua
add_log("Updating lib/..."); if (component_allows("sdk") || component_subtree_needed("sdk")) {
flush_ui(); add_log("Updating sdk/...");
flush_ui();
snprintf(path_buf, sizeof(path_buf), "%d:/lib", drive_num); snprintf(path_buf, sizeof(path_buf), "%d:/sdk", drive_num);
montauk::fmkdir(path_buf); montauk::fmkdir(path_buf);
if (!copy_recursive("0:/lib", path_buf)) { if (!copy_recursive("0:/sdk", path_buf)) {
add_log("ERROR: Failed to update lib/"); add_log("ERROR: Failed to update sdk/");
flush_ui(); st.step = STEP_ERROR; return; flush_ui(); st.step = STEP_ERROR; return;
}
} }
char copy_msg[64]; char copy_msg[64];
+439
View File
@@ -0,0 +1,439 @@
/*
* components.cpp
* MontaukOS Installer — optional component selection
* Copyright (c) 2026 Daniel Hammer
*/
#include "installer.h"
// ============================================================================
// Component table
// ============================================================================
enum ComponentIndex {
CI_SDK = 0,
CI_GCC,
CI_BINUTILS,
CI_TCC,
CI_LUA,
CI_GAMES,
CI_DOOM,
CI_2048,
CI_RPGDEMO,
CI_MANDELBROT,
CI_OFFICE,
CI_WORDPROCESSOR,
CI_SPREADSHEET,
CI_PDFVIEWER,
CI_INTERNET,
CI_WIKIPEDIA,
CI_WEATHER,
CI_IRC,
CI_PRINTING,
CI_HTTPD,
CI_SDR,
CI_COUNT,
};
Component g_components[] = {
{ "sdk", "Montauk SDK",
"Native development kit under 0:/sdk: headers, libc, libstdc++ and tools",
-1, "sdk/include", {"sdk", nullptr}, false, -1,
false, false, false, false, 0 },
{ "gcc", "GNU C/C++ compiler (gcc, g++)",
"Native GCC 14 with C and C++ support (needs binutils)",
CI_SDK, "sdk/bin/gcc.elf",
{"sdk/bin/gcc.elf", "sdk/bin/g++.elf", "sdk/bin/cpp.elf",
"sdk/libexec", "sdk/lib/gcc", nullptr},
true, CI_BINUTILS,
false, false, false, false, 0 },
{ "binutils", "GNU binutils (as, ld, ar, ...)",
"Assembler, linker and object file tools",
CI_SDK, "sdk/bin/as.elf",
{"sdk/bin/as.elf", "sdk/bin/ld.elf", "sdk/bin/ar.elf", "sdk/bin/nm.elf",
"sdk/bin/objcopy.elf", "sdk/bin/objdump.elf", "sdk/bin/readelf.elf",
"sdk/bin/ranlib.elf", "sdk/bin/strip.elf", "sdk/bin/size.elf",
"sdk/bin/strings.elf", "sdk/bin/addr2line.elf", "sdk/bin/c++filt.elf",
"sdk/bin/elfedit.elf", nullptr},
true, -1,
false, false, false, false, 0 },
{ "tcc", "Tiny C Compiler",
"Lightweight single-pass C compiler (tcc) with its own headers",
CI_SDK, "sdk/tcc", {"sdk/tcc", "os/tcc.elf", nullptr}, false, -1,
false, false, false, false, 0 },
{ "lua", "Lua 5.4",
"Lua interpreter (lua) with static library and headers",
CI_SDK, "sdk/lua", {"sdk/lua", "os/lua.elf", nullptr}, false, -1,
false, false, false, false, 0 },
{ "games", "Games & demos",
"Games and graphics demos",
-1, nullptr, {nullptr}, false, -1,
false, false, false, false, 0 },
{ "doom", "DOOM",
"id Software's DOOM on the doomgeneric engine",
CI_GAMES, "apps/doom", {"apps/doom", nullptr}, false, -1,
false, false, false, false, 0 },
{ "2048", "2048",
"Sliding tile puzzle game",
CI_GAMES, "apps/2048", {"apps/2048", nullptr}, false, -1,
false, false, false, false, 0 },
{ "rpgdemo", "RPG demo",
"Top-down RPG game engine demo",
CI_GAMES, "apps/rpgdemo", {"apps/rpgdemo", nullptr}, false, -1,
false, false, false, false, 0 },
{ "mandelbrot", "Mandelbrot",
"Fractal explorer",
CI_GAMES, "apps/mandelbrot", {"apps/mandelbrot", nullptr}, false, -1,
false, false, false, false, 0 },
{ "office", "Office & productivity",
"Document, spreadsheet and PDF applications",
-1, nullptr, {nullptr}, false, -1,
false, false, false, false, 0 },
{ "wordprocessor", "Word Processor",
"Rich text documents with printing support",
CI_OFFICE, "apps/wordprocessor", {"apps/wordprocessor", nullptr}, false, -1,
false, false, false, false, 0 },
{ "spreadsheet", "Spreadsheet",
"Formula-based spreadsheet",
CI_OFFICE, "apps/spreadsheet", {"apps/spreadsheet", nullptr}, false, -1,
false, false, false, false, 0 },
{ "pdfviewer", "PDF Viewer",
"PDF document viewer",
CI_OFFICE, "apps/pdfviewer", {"apps/pdfviewer", nullptr}, false, -1,
false, false, false, false, 0 },
{ "internet", "Internet apps",
"Optional network applications",
-1, nullptr, {nullptr}, false, -1,
false, false, false, false, 0 },
{ "wikipedia", "Wikipedia",
"Wikipedia reader (GUI app and wiki CLI)",
CI_INTERNET, "apps/wikipedia", {"apps/wikipedia", "os/wiki.elf", nullptr}, false, -1,
false, false, false, false, 0 },
{ "weather", "Weather",
"Weather forecast app",
CI_INTERNET, "apps/weather", {"apps/weather", nullptr}, false, -1,
false, false, false, false, 0 },
{ "irc", "IRC client",
"Command-line IRC client",
CI_INTERNET, "os/irc.elf", {"os/irc.elf", nullptr}, false, -1,
false, false, false, false, 0 },
{ "printing", "Printing support",
"Print spooler daemon (printd), printer setup app and print tools",
-1, "os/printd.elf",
{"os/printd.elf", "os/printctl.elf", "os/printersapplet.lib",
"apps/printers", nullptr},
false, -1,
false, false, false, false, 0 },
{ "httpd", "HTTP server (experimental)",
"Experimental web server (httpd) with 0:/www content",
-1, "os/httpd.elf", {"os/httpd.elf", "www", nullptr}, false, -1,
false, false, false, false, 0 },
{ "sdr", "SDR tools (experimental)",
"RTL-SDR software defined radio demo (sdr CLI)",
-1, "os/sdr.elf", {"os/sdr.elf", nullptr}, false, -1,
false, false, false, false, 0 },
};
const int g_component_count = CI_COUNT;
static bool g_sizes_ready = false;
// ============================================================================
// Helpers
// ============================================================================
static bool comp_is_group(int idx) {
return g_components[idx].paths[0] == nullptr;
}
bool component_has_children(int idx) {
for (int i = 0; i < g_component_count; i++)
if (g_components[i].parent == idx) return true;
return false;
}
// True when rel equals prefix or lies inside the prefix directory.
static bool path_under(const char* prefix, const char* rel) {
int pl = 0;
while (prefix[pl]) {
if (rel[pl] != prefix[pl]) return false;
pl++;
}
return rel[pl] == '\0' || rel[pl] == '/';
}
static int str_len(const char* s) {
int n = 0;
while (s[n]) n++;
return n;
}
// Component owning rel, resolved by the longest matching owned path. This is
// what lets "sdk/tcc" stay installable while the rest of "sdk" is skipped.
static int component_owning(const char* rel) {
int best = -1, best_len = -1;
for (int i = 0; i < g_component_count; i++) {
for (int p = 0; g_components[i].paths[p]; p++) {
const char* path = g_components[i].paths[p];
if (!path_under(path, rel)) continue;
int len = str_len(path);
if (len > best_len) { best_len = len; best = i; }
}
}
return best;
}
bool component_allows(const char* rel) {
int owner = component_owning(rel);
return owner < 0 || g_components[owner].checked;
}
bool component_subtree_needed(const char* rel) {
int rel_len = str_len(rel);
for (int i = 0; i < g_component_count; i++) {
if (!g_components[i].checked) continue;
for (int p = 0; g_components[i].paths[p]; p++) {
const char* path = g_components[i].paths[p];
if (str_len(path) > rel_len && path_under(rel, path)) return true;
}
}
return false;
}
// ============================================================================
// Presence detection and defaults
// ============================================================================
static bool rel_exists(const char* rel) {
char abs[160];
snprintf(abs, sizeof(abs), "0:/%s", rel);
return installer_path_exists(abs);
}
void components_init() {
for (int i = 0; i < g_component_count; i++) {
Component& c = g_components[i];
c.expanded = false;
c.bytes = 0;
c.base_present = c.probe ? rel_exists(c.probe) : false;
c.present = c.base_present;
c.checked = c.base_present;
}
// A parent counts as present when its own payload or any child exists.
for (int i = 0; i < g_component_count; i++) {
int p = g_components[i].parent;
if (p >= 0 && g_components[i].present)
g_components[p].present = true;
}
g_sizes_ready = false;
}
// ============================================================================
// Check state and toggling
// ============================================================================
mtk::CheckState component_state(int idx) {
const Component& c = g_components[idx];
if (!component_has_children(idx))
return mtk::check_state(c.checked);
int total = 0, on = 0;
if (c.base_present) { total++; if (c.checked) on++; }
for (int i = 0; i < g_component_count; i++) {
if (g_components[i].parent != idx || !g_components[i].present) continue;
total++;
if (g_components[i].checked) on++;
}
if (total == 0 || on == 0) return mtk::CHECK_OFF;
if (on == total) return mtk::CHECK_ON;
return mtk::CHECK_MIXED;
}
void component_toggle(int idx) {
Component& c = g_components[idx];
if (!c.present) return;
if (component_has_children(idx)) {
bool on = component_state(idx) != mtk::CHECK_ON;
c.checked = on && c.base_present;
for (int i = 0; i < g_component_count; i++) {
if (g_components[i].parent == idx && g_components[i].present)
g_components[i].checked = on;
}
return;
}
c.checked = !c.checked;
if (c.checked) {
if (c.depends_on >= 0 && g_components[c.depends_on].present)
g_components[c.depends_on].checked = true;
if (c.requires_parent && c.parent >= 0 &&
g_components[c.parent].base_present)
g_components[c.parent].checked = true;
} else {
// Drop anything that declared a dependency on this component.
for (int i = 0; i < g_component_count; i++) {
if (g_components[i].depends_on == idx)
g_components[i].checked = false;
}
}
}
int component_visible_rows(int* out, int max) {
int count = 0;
for (int i = 0; i < g_component_count && count < max; i++) {
int p = g_components[i].parent;
if (p >= 0 && !g_components[p].expanded) continue;
out[count++] = i;
}
return count;
}
// ============================================================================
// Size measurement
// ============================================================================
static uint64_t file_size_abs(const char* abs) {
int fd = montauk::open(abs);
if (fd < 0) return 0;
uint64_t size = montauk::getsize(fd);
montauk::close(fd);
return size;
}
// Recursive size of rel, counting only entries owned by component `owner`
// (entries claimed by a more specific component are counted there instead).
static uint64_t du_walk(const char* rel, int owner) {
if (component_owning(rel) != owner) return 0;
char abs[192];
snprintf(abs, sizeof(abs), "0:/%s", rel);
DirList list;
if (installer_list_dir(abs, &list)) {
uint64_t sum = 0;
for (int i = 0; i < list.count; i++)
sum += du_walk(list.entries[i].rel, owner);
installer_free_dir_list(&list);
return sum;
}
return file_size_abs(abs);
}
void components_compute_sizes() {
if (g_sizes_ready) return;
for (int i = 0; i < g_component_count; i++) {
Component& c = g_components[i];
c.bytes = 0;
if (!c.present || comp_is_group(i)) continue;
for (int p = 0; c.paths[p]; p++)
c.bytes += du_walk(c.paths[p], i);
}
// Pure groups display the sum of their children.
for (int i = 0; i < g_component_count; i++) {
if (!comp_is_group(i)) continue;
for (int j = 0; j < g_component_count; j++)
if (g_components[j].parent == i)
g_components[i].bytes += g_components[j].bytes;
}
g_sizes_ready = true;
}
bool components_sizes_ready() {
return g_sizes_ready;
}
uint64_t components_selected_bytes() {
uint64_t sum = 0;
for (int i = 0; i < g_component_count; i++) {
if (comp_is_group(i)) continue;
if (g_components[i].checked) sum += g_components[i].bytes;
}
return sum;
}
uint64_t components_unselected_bytes() {
uint64_t sum = 0;
for (int i = 0; i < g_component_count; i++) {
if (comp_is_group(i)) continue;
if (g_components[i].present && !g_components[i].checked)
sum += g_components[i].bytes;
}
return sum;
}
// ============================================================================
// Summaries and the update flow
// ============================================================================
void components_excluded_summary(char* buf, int bufsize) {
int excluded = 0;
int named = 0;
int pos = 0;
buf[0] = '\0';
for (int i = 0; i < g_component_count; i++) {
if (comp_is_group(i)) continue;
if (!g_components[i].present || g_components[i].checked) continue;
excluded++;
if (named < 3) {
pos += snprintf(buf + pos, bufsize - pos, "%s%s",
named ? ", " : "Skipping: ",
g_components[i].label);
named++;
}
}
if (excluded == 0) {
snprintf(buf, bufsize, "All optional components selected");
} else if (excluded > named) {
snprintf(buf + pos, bufsize - pos, " (+%d more)", excluded - named);
}
}
// For the update flow: refresh only the optional components the target
// already has; never re-add ones that were deselected at install time.
void components_set_from_target(int drive) {
for (int i = 0; i < g_component_count; i++) {
Component& c = g_components[i];
if (!c.probe) continue;
char abs[160];
snprintf(abs, sizeof(abs), "%d:/%s", drive, c.probe);
c.checked = c.present && installer_path_exists(abs);
// Migration: pre-sdk installs kept tcc/lua under 0:/lib.
if (!c.checked && c.present &&
(strcmp(c.id, "tcc") == 0 || strcmp(c.id, "lua") == 0)) {
snprintf(abs, sizeof(abs), "%d:/lib/%s", drive, c.id);
c.checked = installer_path_exists(abs);
}
}
}
+149 -32
View File
@@ -10,7 +10,8 @@
#include <montauk/string.h> #include <montauk/string.h>
#include <montauk/heap.h> #include <montauk/heap.h>
#include <gui/gui.hpp> #include <gui/gui.hpp>
#include <gui/truetype.hpp> #include <gui/font.hpp>
#include <gui/mtk.hpp>
extern "C" { extern "C" {
#include <string.h> #include <string.h>
@@ -23,34 +24,20 @@ using namespace gui;
// Constants // Constants
// ============================================================================ // ============================================================================
static constexpr int INIT_W = 520; static constexpr int INIT_W = 560;
static constexpr int INIT_H = 440; static constexpr int INIT_H = 500;
static constexpr int TOOLBAR_H = 36; static constexpr int HEADER_H = 44;
static constexpr int STEP_BAR_H = 32; static constexpr int STEP_BAR_H = 30;
static constexpr int CONTENT_TOP = TOOLBAR_H + STEP_BAR_H; 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_DISKS = 32;
static constexpr int MAX_PARTS = 32; static constexpr int MAX_PARTS = 32;
static constexpr int STATUS_H = 26;
// Font sizes — adjusted at runtime by apply_scale() static constexpr int COMP_ROW_H = 28;
extern int FONT_SIZE; static constexpr int COMP_INDENT = 26;
extern int FONT_SM; static constexpr int COMP_TOGGLE_W = 18;
static constexpr int TB_BTN_Y = 5;
static constexpr int TB_BTN_H = 26;
static constexpr int TB_BTN_RAD = 8;
static constexpr Color BG_COLOR = Color::from_rgb(0xFF, 0xFF, 0xFF);
static constexpr Color TOOLBAR_BG = Color::from_rgb(0xF5, 0xF5, 0xF5);
static constexpr Color BORDER_COLOR = Color::from_rgb(0xCC, 0xCC, 0xCC);
static constexpr Color TEXT_COLOR = Color::from_rgb(0x22, 0x22, 0x22);
static constexpr Color DIM_TEXT = Color::from_rgb(0x66, 0x66, 0x66);
static constexpr Color FAINT_TEXT = Color::from_rgb(0x88, 0x88, 0x88);
static constexpr Color WHITE = Color::from_rgb(0xFF, 0xFF, 0xFF);
static constexpr Color ACCENT = Color::from_rgb(0x33, 0x66, 0xCC);
static constexpr Color DANGER = Color::from_rgb(0xCC, 0x33, 0x33);
static constexpr Color SUCCESS_COLOR = Color::from_rgb(0x33, 0x99, 0x33);
static constexpr Color DISABLED_BG = Color::from_rgb(0xBB, 0xBB, 0xBB);
// ============================================================================ // ============================================================================
// Install steps // Install steps
@@ -65,6 +52,7 @@ enum InstallStep {
STEP_MODE_SELECT, STEP_MODE_SELECT,
STEP_SELECT_DISK, STEP_SELECT_DISK,
STEP_PARTITION_SCHEME, STEP_PARTITION_SCHEME,
STEP_COMPONENTS,
STEP_CONFIRM, STEP_CONFIRM,
STEP_INSTALLING, STEP_INSTALLING,
STEP_DONE, STEP_DONE,
@@ -95,6 +83,55 @@ static const char* scheme_descs[] = {
"One partition for boot, OS, and data. Simple and compatible.", "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 // State
// ============================================================================ // ============================================================================
@@ -120,8 +157,12 @@ struct InstallerState {
char log[LOG_LINES][LOG_LINE_LEN]; char log[LOG_LINES][LOG_LINE_LEN];
int log_count; int log_count;
char status[80]; char status[96];
uint64_t status_time; uint64_t status_time;
int mouse_x;
int mouse_y;
int comp_scroll;
}; };
// ============================================================================ // ============================================================================
@@ -130,10 +171,68 @@ struct InstallerState {
extern int g_win_w, g_win_h; extern int g_win_w, g_win_h;
extern InstallerState g_state; extern InstallerState g_state;
extern TrueTypeFont* g_font; extern Color g_accent;
extern uint32_t* g_pixels;
extern uint32_t* g_backbuf; // ============================================================================
extern int g_win_id; // 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) // Function declarations — helpers (main.cpp)
@@ -143,7 +242,7 @@ void set_status(const char* msg);
void add_log(const char* msg); void add_log(const char* msg);
void flush_ui(); void flush_ui();
void format_disk_size(char* buf, int bufsize, uint64_t sectors, uint16_t sectorSize); void format_disk_size(char* buf, int bufsize, uint64_t sectors, uint16_t sectorSize);
void apply_scale(int scale); void format_bytes(char* buf, int bufsize, uint64_t bytes);
// ============================================================================ // ============================================================================
// Function declarations — render.cpp // Function declarations — render.cpp
@@ -159,3 +258,21 @@ void installer_refresh_disks();
void installer_refresh_parts(); void installer_refresh_parts();
void do_install(); void do_install();
void do_update(); 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);
+212 -169
View File
@@ -5,6 +5,7 @@
*/ */
#include "installer.h" #include "installer.h"
#include <gui/mtk/settings.hpp>
#include <gui/standalone.hpp> #include <gui/standalone.hpp>
// ============================================================================ // ============================================================================
@@ -14,23 +15,10 @@
int g_win_w = INIT_W; int g_win_w = INIT_W;
int g_win_h = INIT_H; int g_win_h = INIT_H;
int FONT_SIZE = 18;
int FONT_SM = 14;
InstallerState g_state; InstallerState g_state;
TrueTypeFont* g_font = nullptr; Color g_accent = colors::ACCENT;
uint32_t* g_pixels = nullptr;
uint32_t* g_backbuf = nullptr;
int g_win_id = -1;
static WsWindow g_window;
void apply_scale(int scale) { static WsWindow g_window;
switch (scale) {
case 0: FONT_SIZE = 14; FONT_SM = 11; break;
case 2: FONT_SIZE = 22; FONT_SM = 17; break;
default: FONT_SIZE = 18; FONT_SM = 14; break;
}
}
// ============================================================================ // ============================================================================
// Helpers // Helpers
@@ -38,7 +26,8 @@ void apply_scale(int scale) {
void set_status(const char* msg) { void set_status(const char* msg) {
int i = 0; int i = 0;
for (; i < 79 && msg[i]; i++) g_state.status[i] = msg[i]; for (; i < (int)sizeof(g_state.status) - 1 && msg[i]; i++)
g_state.status[i] = msg[i];
g_state.status[i] = '\0'; g_state.status[i] = '\0';
g_state.status_time = montauk::get_milliseconds(); g_state.status_time = montauk::get_milliseconds();
} }
@@ -58,9 +47,8 @@ void add_log(const char* msg) {
} }
void flush_ui() { void flush_ui() {
if (g_pixels && g_backbuf && g_window.id >= 0) { if (g_window.pixels && g_window.id >= 0) {
render(g_backbuf); render(g_window.pixels);
montauk::memcpy(g_pixels, g_backbuf, g_win_w * g_win_h * 4);
g_window.present(); g_window.present();
} }
} }
@@ -71,13 +59,95 @@ void format_disk_size(char* buf, int bufsize, uint64_t sectors, uint16_t sectorS
if (gb >= 1024) { if (gb >= 1024) {
uint64_t tb = gb / 1024; uint64_t tb = gb / 1024;
uint64_t frac = ((gb % 1024) * 10) / 1024; uint64_t frac = ((gb % 1024) * 10) / 1024;
snprintf(buf, bufsize, "%lu.%lu TB", (unsigned)tb, (unsigned)frac); snprintf(buf, bufsize, "%lu.%lu TB", (unsigned long)tb, (unsigned long)frac);
} else if (gb > 0) { } else if (gb > 0) {
uint64_t frac = ((bytes % (1024ULL * 1024 * 1024)) * 10) / (1024ULL * 1024 * 1024); uint64_t frac = ((bytes % (1024ULL * 1024 * 1024)) * 10) / (1024ULL * 1024 * 1024);
snprintf(buf, bufsize, "%lu.%lu GB", (unsigned)gb, (unsigned)frac); snprintf(buf, bufsize, "%lu.%lu GB", (unsigned long)gb, (unsigned long)frac);
} else { } else {
uint64_t mb = bytes / (1024ULL * 1024); uint64_t mb = bytes / (1024ULL * 1024);
snprintf(buf, bufsize, "%lu MB", (unsigned)mb); snprintf(buf, bufsize, "%lu MB", (unsigned long)mb);
}
}
void format_bytes(char* buf, int bufsize, uint64_t bytes) {
if (bytes >= 1024ULL * 1024 * 1024) {
uint64_t gb = bytes / (1024ULL * 1024 * 1024);
uint64_t frac = ((bytes % (1024ULL * 1024 * 1024)) * 10) / (1024ULL * 1024 * 1024);
snprintf(buf, bufsize, "%lu.%lu GB", (unsigned long)gb, (unsigned long)frac);
} else if (bytes >= 1024 * 1024) {
snprintf(buf, bufsize, "%lu MB", (unsigned long)(bytes / (1024 * 1024)));
} else if (bytes >= 1024) {
snprintf(buf, bufsize, "%lu KB", (unsigned long)(bytes / 1024));
} else {
snprintf(buf, bufsize, "%lu B", (unsigned long)bytes);
}
}
// ============================================================================
// Step transitions
// ============================================================================
static void clamp_comp_scroll() {
int vis[32];
int n = component_visible_rows(vis, 32);
Rect list = components_list_rect();
int max_scroll = n * COMP_ROW_H - list.h;
if (max_scroll < 0) max_scroll = 0;
// Round up to a row boundary so the top visible row stays aligned.
max_scroll = ((max_scroll + COMP_ROW_H - 1) / COMP_ROW_H) * COMP_ROW_H;
if (g_state.comp_scroll > max_scroll) g_state.comp_scroll = max_scroll;
if (g_state.comp_scroll < 0) g_state.comp_scroll = 0;
}
static void goto_components() {
g_state.step = STEP_COMPONENTS;
g_state.comp_scroll = 0;
if (!components_sizes_ready()) {
set_status("Measuring component sizes...");
flush_ui();
components_compute_sizes();
set_status("");
}
}
static void step_advance() {
auto& st = g_state;
switch (st.step) {
case STEP_SELECT_DISK:
if (st.selected_disk >= 0) st.step = STEP_PARTITION_SCHEME;
break;
case STEP_PARTITION_SCHEME:
goto_components();
break;
case STEP_COMPONENTS:
st.step = STEP_CONFIRM;
break;
case STEP_CONFIRM:
st.step = STEP_INSTALLING;
st.log_count = 0;
break;
case STEP_UPDATE_SELECT_PART:
if (st.selected_part >= 0) st.step = STEP_UPDATE_CONFIRM;
break;
case STEP_UPDATE_CONFIRM:
st.step = STEP_UPDATING;
st.log_count = 0;
break;
default:
break;
}
}
static void step_back() {
auto& st = g_state;
switch (st.step) {
case STEP_SELECT_DISK: st.step = STEP_MODE_SELECT; break;
case STEP_PARTITION_SCHEME: st.step = STEP_SELECT_DISK; break;
case STEP_COMPONENTS: st.step = STEP_PARTITION_SCHEME; break;
case STEP_CONFIRM: st.step = STEP_COMPONENTS; break;
case STEP_UPDATE_SELECT_PART: st.step = STEP_MODE_SELECT; break;
case STEP_UPDATE_CONFIRM: st.step = STEP_UPDATE_SELECT_PART; break;
default: break;
} }
} }
@@ -85,166 +155,141 @@ void format_disk_size(char* buf, int bufsize, uint64_t sectors, uint16_t sectorS
// Mouse handling // Mouse handling
// ============================================================================ // ============================================================================
// Returns false to signal quit.
static bool handle_click(int mx, int my) { static bool handle_click(int mx, int my) {
auto& st = g_state; auto& st = g_state;
int fh = g_font ? g_font->get_cache(FONT_SIZE)->ascent - g_font->get_cache(FONT_SIZE)->descent : 16;
int fh_sm = g_font ? g_font->get_cache(FONT_SM)->ascent - g_font->get_cache(FONT_SM)->descent : 12;
// Common bottom button area Rect primary = primary_btn_rect();
int btn_w = 120, btn_h = 34; Rect secondary = secondary_btn_rect();
int btn_y = g_win_h - STATUS_H - btn_h - 12; Rect tertiary = tertiary_btn_rect();
if (st.step == STEP_MODE_SELECT) { switch (st.step) {
// Two mode cards case STEP_MODE_SELECT:
int y = CONTENT_TOP + 16 + fh + 4 + fh_sm + 16; if (mode_card_rect(0).contains(mx, my)) {
int card_h = 52, card_x = 16, card_w = g_win_w - 32;
// Install card
if (mx >= card_x && mx < card_x + card_w && my >= y && my < y + card_h) {
st.mode = MODE_INSTALL; st.mode = MODE_INSTALL;
installer_refresh_disks(); installer_refresh_disks();
st.step = STEP_SELECT_DISK; st.step = STEP_SELECT_DISK;
return true; } else if (mode_card_rect(1).contains(mx, my)) {
}
y += card_h + 8;
// Update card
if (mx >= card_x && mx < card_x + card_w && my >= y && my < y + card_h) {
st.mode = MODE_UPDATE; st.mode = MODE_UPDATE;
installer_refresh_parts(); installer_refresh_parts();
st.step = STEP_UPDATE_SELECT_PART; st.step = STEP_UPDATE_SELECT_PART;
return true;
} }
} else if (st.step == STEP_SELECT_DISK) { break;
// Disk list items
int y = CONTENT_TOP + 40 + fh + 12; case STEP_SELECT_DISK:
for (int i = 0; i < st.disk_count; i++) { for (int i = 0; i < st.disk_count; i++) {
int item_h = 48; if (list_row_rect(i, DISK_ROW_H).contains(mx, my)) {
if (mx >= 16 && mx < g_win_w - 16 && my >= y && my < y + item_h) {
st.selected_disk = i; st.selected_disk = i;
return true; return true;
} }
y += item_h + 4;
} }
if (primary.contains(mx, my)) step_advance();
// "Next" button else if (secondary.contains(mx, my)) step_back();
int next_x = g_win_w - btn_w - 16; else if (tertiary.contains(mx, my)) {
if (st.selected_disk >= 0 &&
mx >= next_x && mx < next_x + btn_w &&
my >= btn_y && my < btn_y + btn_h) {
st.step = STEP_PARTITION_SCHEME;
return true;
}
// "Refresh" button
int ref_w = 80;
int ref_x = next_x - ref_w - 8;
if (mx >= ref_x && mx < ref_x + ref_w &&
my >= btn_y && my < btn_y + btn_h) {
installer_refresh_disks(); installer_refresh_disks();
return true; set_status("Disk list refreshed");
} }
} else if (st.step == STEP_PARTITION_SCHEME) { break;
// Scheme radio buttons
int y = CONTENT_TOP + 40 + fh + 12; case STEP_PARTITION_SCHEME:
for (int i = 0; i < SCHEME_COUNT; i++) { for (int i = 0; i < SCHEME_COUNT; i++) {
int item_h = 52; if (list_row_rect(i, SCHEME_ROW_H).contains(mx, my)) {
if (mx >= 16 && mx < g_win_w - 16 && my >= y && my < y + item_h) {
st.partition_scheme = i; st.partition_scheme = i;
return true; return true;
} }
y += item_h + 4;
} }
if (primary.contains(mx, my)) step_advance();
else if (secondary.contains(mx, my)) step_back();
break;
// "Next" button case STEP_COMPONENTS: {
int next_x = g_win_w - btn_w - 16; int vis[32];
if (mx >= next_x && mx < next_x + btn_w && int n = component_visible_rows(vis, 32);
my >= btn_y && my < btn_y + btn_h) { Rect list = components_list_rect();
st.step = STEP_CONFIRM; for (int k = 0; k < n; k++) {
Rect row = component_row_rect(k);
if (row.y + row.h <= list.y || row.y >= list.y + list.h) continue;
if (!row.contains(mx, my)) continue;
int idx = vis[k];
bool child = g_components[idx].parent >= 0;
int x = row.x + 4 + (child ? COMP_INDENT : 0);
if (component_has_children(idx) && mx < x + COMP_TOGGLE_W + 4) {
g_components[idx].expanded = !g_components[idx].expanded;
clamp_comp_scroll();
} else {
component_toggle(idx);
}
return true; return true;
} }
if (primary.contains(mx, my)) step_advance();
else if (secondary.contains(mx, my)) step_back();
break;
}
// "Back" button case STEP_CONFIRM:
int back_x = next_x - btn_w - 8; case STEP_UPDATE_CONFIRM:
if (mx >= back_x && mx < back_x + btn_w && if (primary.contains(mx, my)) step_advance();
my >= btn_y && my < btn_y + btn_h) { else if (secondary.contains(mx, my)) step_back();
st.step = STEP_SELECT_DISK; break;
return true;
}
} else if (st.step == STEP_CONFIRM) {
int center_x = g_win_w / 2;
int gap = 16;
// "Install" button case STEP_UPDATE_SELECT_PART:
int conf_x = center_x - btn_w - gap / 2;
if (mx >= conf_x && mx < conf_x + btn_w &&
my >= btn_y && my < btn_y + btn_h) {
st.step = STEP_INSTALLING;
st.log_count = 0;
return true;
}
// "Back" button
int canc_x = center_x + gap / 2;
if (mx >= canc_x && mx < canc_x + btn_w &&
my >= btn_y && my < btn_y + btn_h) {
st.step = STEP_PARTITION_SCHEME;
return true;
}
} else if (st.step == STEP_DONE || st.step == STEP_ERROR) {
int close_x = (g_win_w - 100) / 2;
if (mx >= close_x && mx < close_x + 100 &&
my >= btn_y && my < btn_y + btn_h) {
return false; // signal quit
}
} else if (st.step == STEP_UPDATE_SELECT_PART) {
// Partition list items
int y = CONTENT_TOP + 16 + fh + 4 + fh_sm + 12;
for (int i = 0; i < st.part_count; i++) { for (int i = 0; i < st.part_count; i++) {
int item_h = 48; if (list_row_rect(i, PART_ROW_H).contains(mx, my)) {
if (mx >= 16 && mx < g_win_w - 16 && my >= y && my < y + item_h) {
st.selected_part = i; st.selected_part = i;
return true; return true;
} }
y += item_h + 4;
} }
if (primary.contains(mx, my)) step_advance();
else if (secondary.contains(mx, my)) step_back();
break;
// "Next" button case STEP_DONE:
int next_x = g_win_w - btn_w - 16; case STEP_ERROR:
if (st.selected_part >= 0 && if (primary.contains(mx, my)) return false;
mx >= next_x && mx < next_x + btn_w && break;
my >= btn_y && my < btn_y + btn_h) {
st.step = STEP_UPDATE_CONFIRM;
return true;
}
// "Back" button default:
int back_x = next_x - btn_w - 8; break;
if (mx >= back_x && mx < back_x + btn_w && }
my >= btn_y && my < btn_y + btn_h) {
st.step = STEP_MODE_SELECT;
return true;
}
} else if (st.step == STEP_UPDATE_CONFIRM) {
int center_x = g_win_w / 2;
int gap = 16;
// "Update" button return true;
int upd_x = center_x - btn_w - gap / 2; }
if (mx >= upd_x && mx < upd_x + btn_w &&
my >= btn_y && my < btn_y + btn_h) {
st.step = STEP_UPDATING;
st.log_count = 0;
return true;
}
// "Back" button // ============================================================================
int back_x = center_x + gap / 2; // Key handling — returns false to signal quit
if (mx >= back_x && mx < back_x + btn_w && // ============================================================================
my >= btn_y && my < btn_y + btn_h) {
st.step = STEP_UPDATE_SELECT_PART; static bool handle_key(const montauk::abi::KeyEvent& key) {
auto& st = g_state;
if (key.scancode == 0x01) { // Escape
if (st.step == STEP_INSTALLING || st.step == STEP_UPDATING)
return true; return true;
} return false;
}
if (key.ascii == '\n' || key.ascii == '\r') {
if (st.step == STEP_DONE || st.step == STEP_ERROR) return false;
step_advance();
return true;
}
if (key.scancode == 0x48) { // Up
if (st.step == STEP_SELECT_DISK && st.selected_disk > 0)
st.selected_disk--;
else if (st.step == STEP_UPDATE_SELECT_PART && st.selected_part > 0)
st.selected_part--;
else if (st.step == STEP_PARTITION_SCHEME && st.partition_scheme > 0)
st.partition_scheme--;
} else if (key.scancode == 0x50) { // Down
if (st.step == STEP_SELECT_DISK && st.selected_disk < st.disk_count - 1)
st.selected_disk++;
else if (st.step == STEP_UPDATE_SELECT_PART && st.selected_part < st.part_count - 1)
st.selected_part++;
else if (st.step == STEP_PARTITION_SCHEME && st.partition_scheme < SCHEME_COUNT - 1)
st.partition_scheme++;
} }
return true; return true;
@@ -260,31 +305,21 @@ extern "C" void _start() {
g_state.selected_part = -1; g_state.selected_part = -1;
g_state.partition_scheme = SCHEME_EFI_EXT2; g_state.partition_scheme = SCHEME_EFI_EXT2;
g_state.step = STEP_MODE_SELECT; g_state.step = STEP_MODE_SELECT;
g_state.mouse_x = -1;
g_state.mouse_y = -1;
// Load font if (!fonts::init())
{ montauk::exit(1);
TrueTypeFont* f = (TrueTypeFont*)montauk::malloc(sizeof(TrueTypeFont));
if (f) {
montauk::memset(f, 0, sizeof(TrueTypeFont));
if (!f->init("0:/fonts/Roboto-Medium.ttf")) { montauk::mfree(f); f = nullptr; }
}
g_font = f;
}
apply_scale(montauk::win_getscale()); g_accent = mtk::load_system_accent();
components_init();
installer_refresh_disks(); installer_refresh_disks();
if (!g_window.create("Install MontaukOS", INIT_W, INIT_H)) if (!g_window.create("Install MontaukOS", INIT_W, INIT_H))
montauk::exit(1); montauk::exit(1);
g_pixels = g_window.pixels; flush_ui();
g_win_id = g_window.id;
g_backbuf = (uint32_t*)montauk::malloc(g_win_w * g_win_h * 4);
render(g_backbuf);
montauk::memcpy(g_pixels, g_backbuf, g_win_w * g_win_h * 4);
g_window.present();
bool install_triggered = false; bool install_triggered = false;
@@ -314,32 +349,40 @@ extern "C" void _start() {
if (ev.type == 3) break; if (ev.type == 3) break;
if (ev.type == 4) { if (ev.type == 4) redraw = true;
apply_scale(g_window.scale_factor);
redraw = true;
}
if (ev.type == 2) { if (ev.type == 2) {
g_win_w = g_window.width; g_win_w = g_window.width;
g_win_h = g_window.height; g_win_h = g_window.height;
g_pixels = g_window.pixels; if (g_state.step == STEP_COMPONENTS) clamp_comp_scroll();
montauk::mfree(g_backbuf);
g_backbuf = (uint32_t*)montauk::malloc(g_win_w * g_win_h * 4);
redraw = true; redraw = true;
} }
if (ev.type == 0 && ev.key.pressed) { if (ev.type == 0 && ev.key.pressed) {
if (ev.key.scancode == 0x01) break; if (!handle_key(ev.key)) break;
redraw = true; redraw = true;
} }
if (ev.type == 1) { if (ev.type == 1) {
bool moved = (ev.mouse.x != g_state.mouse_x ||
ev.mouse.y != g_state.mouse_y);
g_state.mouse_x = ev.mouse.x;
g_state.mouse_y = ev.mouse.y;
if (ev.mouse.scroll != 0 && g_state.step == STEP_COMPONENTS) {
g_state.comp_scroll -= ev.mouse.scroll * COMP_ROW_H;
clamp_comp_scroll();
redraw = true;
}
bool clicked = (ev.mouse.buttons & 1) && !(ev.mouse.prev_buttons & 1); bool clicked = (ev.mouse.buttons & 1) && !(ev.mouse.prev_buttons & 1);
if (clicked) { if (clicked) {
if (!handle_click(ev.mouse.x, ev.mouse.y)) if (!handle_click(ev.mouse.x, ev.mouse.y))
break; break;
redraw = true; redraw = true;
} }
redraw |= moved;
} }
if (redraw) flush_ui(); if (redraw) flush_ui();
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -14,7 +14,7 @@ SRCDIR := src
OBJDIR := obj OBJDIR := obj
LIBC_DIR := ../../lib/libc LIBC_DIR := ../../lib/libc
BINDIR := ../../bin/os BINDIR := ../../bin/os
LIBDIR := ../../bin/lib/lua LIBDIR := ../../bin/sdk/lua
CFLAGS := \ CFLAGS := \
-std=gnu11 \ -std=gnu11 \
+2 -2
View File
@@ -223,8 +223,8 @@
#elif defined(__MONTAUKOS__) /* }{ */ #elif defined(__MONTAUKOS__) /* }{ */
#define LUA_LDIR "0:/lib/lua/" #define LUA_LDIR "0:/sdk/lua/"
#define LUA_CDIR "0:/lib/lua/" #define LUA_CDIR "0:/sdk/lua/"
#if !defined(LUA_PATH_DEFAULT) #if !defined(LUA_PATH_DEFAULT)
#define LUA_PATH_DEFAULT \ #define LUA_PATH_DEFAULT \
+1 -1
View File
@@ -89,7 +89,7 @@ TARGET := $(BINDIR)/tcc.elf
# ---- CRT objects (staged from shared libc) ---- # ---- CRT objects (staged from shared libc) ----
SHARED_CRT_DIR := $(LIBDIR)/libc SHARED_CRT_DIR := $(LIBDIR)/libc
CRT_DIR := ../../bin/lib/tcc/lib CRT_DIR := ../../bin/sdk/tcc/lib
CRT_OBJS := $(CRT_DIR)/crt1.o $(CRT_DIR)/crti.o $(CRT_DIR)/crtn.o CRT_OBJS := $(CRT_DIR)/crt1.o $(CRT_DIR)/crti.o $(CRT_DIR)/crtn.o
# ---- Rules ---- # ---- Rules ----
+4 -4
View File
@@ -16,10 +16,10 @@
#undef CONFIG_TCC_BCHECK #undef CONFIG_TCC_BCHECK
/* Paths on MontaukOS ramdisk */ /* Paths on MontaukOS ramdisk */
#define CONFIG_TCCDIR "0:/lib/tcc" #define CONFIG_TCCDIR "0:/sdk/tcc"
#define CONFIG_TCC_SYSINCLUDEPATHS "0:/lib/tcc/include" #define CONFIG_TCC_SYSINCLUDEPATHS "0:/sdk/tcc/include"
#define CONFIG_TCC_LIBPATHS "0:/lib/tcc/lib" #define CONFIG_TCC_LIBPATHS "0:/sdk/tcc/lib"
#define CONFIG_TCC_CRTPREFIX "0:/lib/tcc/lib" #define CONFIG_TCC_CRTPREFIX "0:/sdk/tcc/lib"
#define CONFIG_TCC_ELFINTERP "" #define CONFIG_TCC_ELFINTERP ""
/* We are not native -- no -run support */ /* We are not native -- no -run support */