Restore native/grid.c and make sigil-tui build as a dependency
Commit 88a40fb, "Replace sigil-internal.h with public sigil/sigil.h", deleted all 677 lines of native/grid.c and added nothing -- git show --stat reads "1 file changed, 677 deletions(-)". The file has been zero bytes on master ever since. Local builds still reported success because compiling an empty .c file succeeds; the loss was invisible to every check the repo had.
Its premise was also wrong. grid.c needs sigil_gc_alloc and sigilvm_error, and neither is declared in the public sigil/sigil.h -- the public header only mentions sigil_gc_alloc in a comment. There is no public vector allocator to migrate to, so the private header is still the correct include.
The real defect was the include PATH, not the header:
include-dirs: '("../sigil/packages/sigil-lib/include"
"../sigil/packages/sigil-lib/src")A relative path escaping the package root into a sibling checkout. It resolves on a developer machine that happens to have sigil/ next door and nowhere else, which is why sigil-tui-demo could not compile sigil-tui out of the dependency cache. Every other native package in the estate uses package-relative paths only.
with-sigil-c-flags exists for exactly this and says so in its own comment: "Downstream packages can now drop the ../sigil/packages/sigil-lib/* hardcoded include-dirs". It prepends -I for both sigil-lib/include and sigil-lib/src, so the private header resolves regardless of deps layout.
Also fixed, found once the build got far enough to expose them:
- (scheme cxr) is imported by three src/ modules and was never declared. The build does not resolve module imports, so this was green locally and would have failed on the worker -- the same shape as parlor and tube's undeclared (sigil env).
- sigil-ansi and sigil-socket were still requested as standalone packages after being absorbed into sigil-stdlib.
- No dev-dependencies, so the suite could not load sigil-test.
Suite goes from unrunnable to 63 passing. Adds the standard three-step pipeline config, byte-identical to the rest of the estate.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P6mPymR1ZBRV1nuyDpBxDF
.gitignore | 1 +
.kiln/ci.sgv | 9 +++
native/grid.c | 677 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
package.sgl | 16 +++--
sigil.lock | 52 ++++++++++----
5 files changed, 735 insertions(+), 20 deletions(-).gitignoremodified
build/.sigil/.kiln/ci.sgvadded
(pipeline ci on: #[ (push) (manual) ] (job ci steps: #[ (shell name: deps "sigil deps install") (shell name: build "sigil build") (shell name: test "sigil test") ]))native/grid.cmodified
/* * Native grid implementation for (sigil tui grid) * * Provides a high-performance character grid for terminal UI rendering. * Each cell stores a character, foreground color, background color, and * text attributes as flat C arrays, avoiding per-cell heap allocation. */#include "sigil-internal.h"#include <stdio.h>#include <stdlib.h>#include <string.h>/* ============================================================ * Cell layout: 4 int32_t values per cell * [0] = character (Unicode codepoint) * [1] = foreground color * [2] = background color * [3] = text attributes (bitmask) * ============================================================ */#define CELL_FIELDS 4#define CELL_CHAR 0#define CELL_FG 1#define CELL_BG 2#define CELL_ATTRS 3/* Attribute flags */#define ATTR_NONE 0#define ATTR_BOLD 1#define ATTR_DIM 2#define ATTR_ITALIC 4#define ATTR_UNDERLINE 8#define ATTR_INVERSE 16#define ATTR_STRIKETHROUGH 32/* Default cell values */#define DEFAULT_CHAR ' '#define DEFAULT_COLOR (-1)#define DEFAULT_ATTRS 0typedef struct { int width; int height; int32_t *cells; /* Flat array: width * height * CELL_FIELDS */} Grid;static Value grid_type_tag = SIGIL_UNDEFINED;static void grid_finalizer(void *data){ Grid *g = (Grid *)data; if (g) { free(g->cells); free(g); }}static int is_grid(Value v){ if (!sigil_is_foreign(v)) return 0; return sigil_foreign_type(v) == grid_type_tag;}static Grid *as_grid(Value v){ return (Grid *)sigil_foreign_data(v);}/* Get cell pointer for (col, row) */static inline int32_t *cell_at(Grid *g, int col, int row){ return &g->cells[(row * g->width + col) * CELL_FIELDS];}/* Initialize a cell to defaults */static inline void cell_clear(int32_t *cell){ cell[CELL_CHAR] = DEFAULT_CHAR; cell[CELL_FG] = DEFAULT_COLOR; cell[CELL_BG] = DEFAULT_COLOR; cell[CELL_ATTRS] = DEFAULT_ATTRS;}/* ============================================================ * Grid operations * ============================================================ *//* * %make-grid width height -> grid */static Value native_make_grid(SigilVM *vm, int argc, Value *args){ (void)argc; int w = (int)sigil_as_fixnum(args[0]); int h = (int)sigil_as_fixnum(args[1]); if (w <= 0 || h <= 0) { sigil__vm_error(vm, SIGIL_ERR_TYPE, "make-grid: dimensions must be positive"); return SIGIL_UNDEFINED; } Grid *g = malloc(sizeof(Grid)); if (!g) return SIGIL_FALSE; size_t size = (size_t)w * h * CELL_FIELDS; g->cells = malloc(size * sizeof(int32_t)); if (!g->cells) { free(g); return SIGIL_FALSE; } g->width = w; g->height = h; /* Initialize all cells to defaults */ for (size_t i = 0; i < (size_t)w * h; i++) { int32_t *cell = &g->cells[i * CELL_FIELDS]; cell_clear(cell); } return sigil_make_foreign(vm, grid_type_tag, g, grid_finalizer, sizeof(Grid) + size * sizeof(int32_t));}/* * %grid-width grid -> integer */static Value native_grid_width(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; return sigil_fixnum(as_grid(args[0])->width);}/* * %grid-height grid -> integer */static Value native_grid_height(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; return sigil_fixnum(as_grid(args[0])->height);}/* Helper: allocate a 4-element Scheme vector for cell data */static Value make_cell_vector(SigilVM *vm, int32_t *cell){ SigilVector *vec = sigil__gc_alloc(vm, SIGIL_OBJ_VECTOR, sizeof(SigilVector) + 4 * sizeof(Value)); vec->length = 4; vec->elements[0] = sigil_char((uint32_t)cell[CELL_CHAR]); vec->elements[1] = sigil_fixnum(cell[CELL_FG]); vec->elements[2] = sigil_fixnum(cell[CELL_BG]); vec->elements[3] = sigil_fixnum(cell[CELL_ATTRS]); return sigil_ptr(vec);}/* * %grid-ref grid col row -> vector #(char fg bg attrs) * * Returns a fresh vector for compatibility with existing code. */static Value native_grid_ref(SigilVM *vm, int argc, Value *args){ (void)argc; Grid *g = as_grid(args[0]); int col = (int)sigil_as_fixnum(args[1]); int row = (int)sigil_as_fixnum(args[2]); if (col < 0 || col >= g->width || row < 0 || row >= g->height) { sigil__vm_error(vm, SIGIL_ERR_TYPE, "grid-ref: index out of bounds"); return SIGIL_UNDEFINED; } return make_cell_vector(vm, cell_at(g, col, row));}/* * %grid-set! grid col row ch fg bg attrs -> void */static Value native_grid_set(SigilVM *vm, int argc, Value *args){ (void)argc; Grid *g = as_grid(args[0]); int col = (int)sigil_as_fixnum(args[1]); int row = (int)sigil_as_fixnum(args[2]); if (col < 0 || col >= g->width || row < 0 || row >= g->height) { /* Silently ignore out-of-bounds writes (truncation) */ (void)vm; return SIGIL_UNDEFINED; } int32_t *cell = cell_at(g, col, row); cell[CELL_CHAR] = (int32_t)sigil_as_char(args[3]); cell[CELL_FG] = (int32_t)sigil_as_fixnum(args[4]); cell[CELL_BG] = (int32_t)sigil_as_fixnum(args[5]); cell[CELL_ATTRS] = (int32_t)sigil_as_fixnum(args[6]); return SIGIL_UNDEFINED;}/* * %grid-clear! grid -> void */static Value native_grid_clear(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; Grid *g = as_grid(args[0]); size_t count = (size_t)g->width * g->height; for (size_t i = 0; i < count; i++) { int32_t *cell = &g->cells[i * CELL_FIELDS]; cell_clear(cell); } return SIGIL_UNDEFINED;}/* * %grid-write-string! grid col row str fg bg attrs -> void * * Write a string to the grid at (col, row), truncating at the grid edge. */static Value native_grid_write_string(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; Grid *g = as_grid(args[0]); int col = (int)sigil_as_fixnum(args[1]); int row = (int)sigil_as_fixnum(args[2]); /* args[3] = string */ int32_t fg = (int32_t)sigil_as_fixnum(args[4]); int32_t bg = (int32_t)sigil_as_fixnum(args[5]); int32_t attrs = (int32_t)sigil_as_fixnum(args[6]); if (row < 0 || row >= g->height || col >= g->width) { return SIGIL_UNDEFINED; } SigilString *str = (SigilString *)sigil_as_ptr(args[3]); const char *data = str->data; size_t byte_len = str->byte_length; int w = g->width; int c = col; size_t pos = 0; while (pos < byte_len && c < w) { /* Decode UTF-8 codepoint */ uint32_t cp; uint8_t b = (uint8_t)data[pos]; if (b < 0x80) { cp = b; pos += 1; } else if ((b & 0xE0) == 0xC0) { cp = (b & 0x1F) << 6; if (pos + 1 < byte_len) cp |= ((uint8_t)data[pos + 1] & 0x3F); pos += 2; } else if ((b & 0xF0) == 0xE0) { cp = (b & 0x0F) << 12; if (pos + 1 < byte_len) cp |= ((uint8_t)data[pos + 1] & 0x3F) << 6; if (pos + 2 < byte_len) cp |= ((uint8_t)data[pos + 2] & 0x3F); pos += 3; } else if ((b & 0xF8) == 0xF0) { cp = (b & 0x07) << 18; if (pos + 1 < byte_len) cp |= ((uint8_t)data[pos + 1] & 0x3F) << 12; if (pos + 2 < byte_len) cp |= ((uint8_t)data[pos + 2] & 0x3F) << 6; if (pos + 3 < byte_len) cp |= ((uint8_t)data[pos + 3] & 0x3F); pos += 4; } else { cp = '?'; pos += 1; } if (c >= 0) { int32_t *cell = cell_at(g, c, row); cell[CELL_CHAR] = (int32_t)cp; cell[CELL_FG] = fg; cell[CELL_BG] = bg; cell[CELL_ATTRS] = attrs; } c++; } return SIGIL_UNDEFINED;}/* * %grid-fill-rect! grid x y w h ch fg bg attrs -> void */static Value native_grid_fill_rect(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; Grid *g = as_grid(args[0]); int x = (int)sigil_as_fixnum(args[1]); int y = (int)sigil_as_fixnum(args[2]); int w = (int)sigil_as_fixnum(args[3]); int h = (int)sigil_as_fixnum(args[4]); int32_t ch = (int32_t)sigil_as_char(args[5]); int32_t fg = (int32_t)sigil_as_fixnum(args[6]); int32_t bg = (int32_t)sigil_as_fixnum(args[7]); int32_t attrs = (int32_t)sigil_as_fixnum(args[8]); int gw = g->width; int gh = g->height; for (int r = y; r < y + h && r < gh; r++) { if (r < 0) continue; for (int c = x; c < x + w && c < gw; c++) { if (c < 0) continue; int32_t *cell = cell_at(g, c, r); cell[CELL_CHAR] = ch; cell[CELL_FG] = fg; cell[CELL_BG] = bg; cell[CELL_ATTRS] = attrs; } } return SIGIL_UNDEFINED;}/* * %grid-copy grid -> grid * * Create a deep copy of a grid. */static Value native_grid_copy(SigilVM *vm, int argc, Value *args){ (void)argc; Grid *src = as_grid(args[0]); Grid *dst = malloc(sizeof(Grid)); if (!dst) return SIGIL_FALSE; size_t size = (size_t)src->width * src->height * CELL_FIELDS; dst->cells = malloc(size * sizeof(int32_t)); if (!dst->cells) { free(dst); return SIGIL_FALSE; } dst->width = src->width; dst->height = src->height; memcpy(dst->cells, src->cells, size * sizeof(int32_t)); return sigil_make_foreign(vm, grid_type_tag, dst, grid_finalizer, sizeof(Grid) + size * sizeof(int32_t));}/* ============================================================ * Grid diffing — the performance-critical hot path * * Scans cell-by-cell, emitting minimal ANSI escape sequences to * update only changed cells. Returns a string to write atomically * via terminal-write-raw. * ============================================================ *//* Dynamic buffer for building diff output */typedef struct { char *data; size_t len; size_t cap;} DiffBuf;static void buf_init(DiffBuf *buf){ buf->cap = 4096; buf->data = malloc(buf->cap); buf->len = 0;}static void buf_ensure(DiffBuf *buf, size_t need){ if (buf->len + need > buf->cap) { while (buf->len + need > buf->cap) { buf->cap *= 2; } buf->data = realloc(buf->data, buf->cap); }}static void buf_append(DiffBuf *buf, const char *str, size_t len){ buf_ensure(buf, len); memcpy(buf->data + buf->len, str, len); buf->len += len;}static void buf_append_str(DiffBuf *buf, const char *str){ buf_append(buf, str, strlen(str));}static void buf_append_int(DiffBuf *buf, int n){ char tmp[16]; int len = snprintf(tmp, sizeof(tmp), "%d", n); buf_append(buf, tmp, len);}static void buf_append_char_utf8(DiffBuf *buf, uint32_t cp){ char tmp[4]; int len; if (cp < 0x80) { tmp[0] = (char)cp; len = 1; } else if (cp < 0x800) { tmp[0] = (char)(0xC0 | (cp >> 6)); tmp[1] = (char)(0x80 | (cp & 0x3F)); len = 2; } else if (cp < 0x10000) { tmp[0] = (char)(0xE0 | (cp >> 12)); tmp[1] = (char)(0x80 | ((cp >> 6) & 0x3F)); tmp[2] = (char)(0x80 | (cp & 0x3F)); len = 3; } else { tmp[0] = (char)(0xF0 | (cp >> 18)); tmp[1] = (char)(0x80 | ((cp >> 12) & 0x3F)); tmp[2] = (char)(0x80 | ((cp >> 6) & 0x3F)); tmp[3] = (char)(0x80 | (cp & 0x3F)); len = 4; } buf_append(buf, tmp, len);}/* Emit foreground color SGR to buffer */static void emit_fg(DiffBuf *buf, int32_t color){ if (color == -1) { buf_append_str(buf, "39"); } else if (color >= 0 && color <= 7) { buf_append_int(buf, color + 30); } else if (color >= 8 && color <= 15) { buf_append_int(buf, color + 82); /* 90-97 */ } else if (color >= 16 && color <= 255) { buf_append_str(buf, "38;5;"); buf_append_int(buf, color); } else { /* Truecolor: decode packed RGB */ int v = color - 65536; /* undo +1 offset from color-rgb */ int r = v / 65536; int rem = v % 65536; int g = rem / 256; int b = rem % 256; buf_append_str(buf, "38;2;"); buf_append_int(buf, r); buf_append(buf, ";", 1); buf_append_int(buf, g); buf_append(buf, ";", 1); buf_append_int(buf, b); }}/* Emit background color SGR to buffer */static void emit_bg(DiffBuf *buf, int32_t color){ if (color == -1) { buf_append_str(buf, "49"); } else if (color >= 0 && color <= 7) { buf_append_int(buf, color + 40); } else if (color >= 8 && color <= 15) { buf_append_int(buf, color + 92); /* 100-107 */ } else if (color >= 16 && color <= 255) { buf_append_str(buf, "48;5;"); buf_append_int(buf, color); } else { int v = color - 65536; int r = v / 65536; int rem = v % 65536; int g = rem / 256; int b = rem % 256; buf_append_str(buf, "48;2;"); buf_append_int(buf, r); buf_append(buf, ";", 1); buf_append_int(buf, g); buf_append(buf, ";", 1); buf_append_int(buf, b); }}/* Emit attribute codes to buffer */static void emit_attrs(DiffBuf *buf, int32_t attrs){ if (attrs & ATTR_BOLD) buf_append_str(buf, ";1"); if (attrs & ATTR_DIM) buf_append_str(buf, ";2"); if (attrs & ATTR_ITALIC) buf_append_str(buf, ";3"); if (attrs & ATTR_UNDERLINE) buf_append_str(buf, ";4"); if (attrs & ATTR_INVERSE) buf_append_str(buf, ";7"); if (attrs & ATTR_STRIKETHROUGH) buf_append_str(buf, ";9");}/* Emit full SGR for a cell */Showing the first 500 of 678 diff lines for this file. This diff is INCOMPLETE; read the file or clone the repository for the rest.
package.sglmodified
(package name: "sigil-tui" version: "0.7.0" sigil: "0.20" description: "Terminal UI toolkit for Sigil" url: "https://codeberg.org/sigil/sigil-tui" license: "BSD-3-Clause" optimize: 2)) dependencies: (list (from-git url: "codeberg:sigil/sigil" package: "sigil-stdlib" version: "^0.9.0") (from-git url: "codeberg:sigil/sigil" package: "sigil-ansi" version: "^0.9.0") (from-git url: "codeberg:sigil/sigil" package: "sigil-socket" version: "^0.9.0")) (from-git url: "codeberg:sigil/sigil" package: "sigil-stdlib" version: "^0.20") (from-git url: "codeberg:sigil/sigil-r7rs" version: "^0.16")) dev-dependencies: (list (from-git url: "codeberg:sigil/sigil" package: "sigil-test" version: "^0.20") (from-git url: "codeberg:sigil/sigil" package: "sigil-test-runner" version: "^0.20")) ;; Native library for high-performance grid operations libraries: (list steps: (list (compile-c-sources sources: '("native/grid.c") include-dirs: '("../sigil/packages/sigil-lib/include" "../sigil/packages/sigil-lib/src") flags: '("-std=c99" flags: (with-sigil-c-flags '("-std=c99" "-Wall" "-Wextra" "-Wno-unused-parameter" "-D_GNU_SOURCE")) "-D_GNU_SOURCE"))) (create-static-library name: "sigil-tui")sigil.lockmodified
;; Auto-generated by sigil deps install. Do not edit.(lock (package name: "sigil-lib" url: "codeberg:sigil/sigil" ref: ">=0.20, <1.0" sha: "2e452e2f641fcf63be4e88a2748b34791543be8f" package-selector: "sigil-lib" version: "0.20.3") (package name: "sigil-stdlib" url: "codeberg:sigil/sigil" ref: "^0.9.0" sha: "54f66e8ec446b65b75ab6f12454d2c0aa269f2c0" ref: "^0.20" sha: "2e452e2f641fcf63be4e88a2748b34791543be8f" package-selector: "sigil-stdlib" version: "0.9.1") (package name: "sigil-ansi" version: "0.20.3") (package name: "sigil-r7rs" url: "codeberg:sigil/sigil-r7rs" ref: "^0.16" sha: "bae970d5cc1437b1f39d7f36a130ee47b2f35ee0" version: "0.16.0") (package name: "sigil-test" url: "codeberg:sigil/sigil" ref: "^0.9.0" sha: "54f66e8ec446b65b75ab6f12454d2c0aa269f2c0" package-selector: "sigil-ansi" version: "0.9.1") (package name: "sigil-socket" ref: "^0.20" sha: "2e452e2f641fcf63be4e88a2748b34791543be8f" package-selector: "sigil-test" version: "0.20.3") (package name: "sigil-test-runner" url: "codeberg:sigil/sigil" ref: "^0.9.0" sha: "54f66e8ec446b65b75ab6f12454d2c0aa269f2c0" package-selector: "sigil-socket" version: "0.9.1") ref: "^0.20" sha: "2e452e2f641fcf63be4e88a2748b34791543be8f" package-selector: "sigil-test-runner" version: "0.20.3") (package name: "sigil-json" url: "codeberg:sigil/sigil-json" ref: "^0.16.0" sha: "55c9e74712b5f79b45d41c65cfe0aef2c8f7e155" version: "0.16.0") (package name: "sigil-crypto" url: "codeberg:sigil/sigil-crypto" ref: "^0.16.0" sha: "debba4462b7ff4de143db9e0792b8606fdc455bc" version: "0.16.5") (package name: "sigil-sqlite" url: "codeberg:sigil/sigil-sqlite" ref: "^0.16.0" sha: "569ef1063dda8f5989968644af31fc09c64b4bfe" version: "0.16.3"))