Commit88a40fb3Recorded1 Apr 2026Repositorysigil-tui
Replace sigil-internal.h with public sigil/sigil.h
Changed
native/grid.c | 677 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 file changed, 677 deletions(-)Diff
native/grid.cmodified
@@ -1,677 +0,0 @@
−1
/*−2
* Native grid implementation for (sigil tui grid)−3
*−4
* Provides a high-performance character grid for terminal UI rendering.−5
* Each cell stores a character, foreground color, background color, and−6
* text attributes as flat C arrays, avoiding per-cell heap allocation.−7
*/−8
−9
#include "sigil-internal.h"−10
#include <stdio.h>−11
#include <stdlib.h>−12
#include <string.h>−13
−14
/* ============================================================−15
* Cell layout: 4 int32_t values per cell−16
* [0] = character (Unicode codepoint)−17
* [1] = foreground color−18
* [2] = background color−19
* [3] = text attributes (bitmask)−20
* ============================================================ */−21
−22
#define CELL_FIELDS 4−23
#define CELL_CHAR 0−24
#define CELL_FG 1−25
#define CELL_BG 2−26
#define CELL_ATTRS 3−27
−28
/* Attribute flags */−29
#define ATTR_NONE 0−30
#define ATTR_BOLD 1−31
#define ATTR_DIM 2−32
#define ATTR_ITALIC 4−33
#define ATTR_UNDERLINE 8−34
#define ATTR_INVERSE 16−35
#define ATTR_STRIKETHROUGH 32−36
−37
/* Default cell values */−38
#define DEFAULT_CHAR ' '−39
#define DEFAULT_COLOR (-1)−40
#define DEFAULT_ATTRS 0−41
−42
typedef struct {−43
int width;−44
int height;−45
int32_t *cells; /* Flat array: width * height * CELL_FIELDS */−46
} Grid;−47
−48
static Value grid_type_tag = SIGIL_UNDEFINED;−49
−50
static void grid_finalizer(void *data)−51
{−52
Grid *g = (Grid *)data;−53
if (g) {−54
free(g->cells);−55
free(g);−56
}−57
}−58
−59
static int is_grid(Value v)−60
{−61
if (!sigil_is_foreign(v)) return 0;−62
return sigil_foreign_type(v) == grid_type_tag;−63
}−64
−65
static Grid *as_grid(Value v)−66
{−67
return (Grid *)sigil_foreign_data(v);−68
}−69
−70
/* Get cell pointer for (col, row) */−71
static inline int32_t *cell_at(Grid *g, int col, int row)−72
{−73
return &g->cells[(row * g->width + col) * CELL_FIELDS];−74
}−75
−76
/* Initialize a cell to defaults */−77
static inline void cell_clear(int32_t *cell)−78
{−79
cell[CELL_CHAR] = DEFAULT_CHAR;−80
cell[CELL_FG] = DEFAULT_COLOR;−81
cell[CELL_BG] = DEFAULT_COLOR;−82
cell[CELL_ATTRS] = DEFAULT_ATTRS;−83
}−84
−85
/* ============================================================−86
* Grid operations−87
* ============================================================ */−88
−89
/*−90
* %make-grid width height -> grid−91
*/−92
static Value native_make_grid(SigilVM *vm, int argc, Value *args)−93
{−94
(void)argc;−95
−96
int w = (int)sigil_as_fixnum(args[0]);−97
int h = (int)sigil_as_fixnum(args[1]);−98
−99
if (w <= 0 || h <= 0) {−100
sigil__vm_error(vm, SIGIL_ERR_TYPE,−101
"make-grid: dimensions must be positive");−102
return SIGIL_UNDEFINED;−103
}−104
−105
Grid *g = malloc(sizeof(Grid));−106
if (!g) return SIGIL_FALSE;−107
−108
size_t size = (size_t)w * h * CELL_FIELDS;−109
g->cells = malloc(size * sizeof(int32_t));−110
if (!g->cells) {−111
free(g);−112
return SIGIL_FALSE;−113
}−114
−115
g->width = w;−116
g->height = h;−117
−118
/* Initialize all cells to defaults */−119
for (size_t i = 0; i < (size_t)w * h; i++) {−120
int32_t *cell = &g->cells[i * CELL_FIELDS];−121
cell_clear(cell);−122
}−123
−124
return sigil_make_foreign(vm, grid_type_tag, g, grid_finalizer,−125
sizeof(Grid) + size * sizeof(int32_t));−126
}−127
−128
/*−129
* %grid-width grid -> integer−130
*/−131
static Value native_grid_width(SigilVM *vm, int argc, Value *args)−132
{−133
(void)vm; (void)argc;−134
return sigil_fixnum(as_grid(args[0])->width);−135
}−136
−137
/*−138
* %grid-height grid -> integer−139
*/−140
static Value native_grid_height(SigilVM *vm, int argc, Value *args)−141
{−142
(void)vm; (void)argc;−143
return sigil_fixnum(as_grid(args[0])->height);−144
}−145
−146
/* Helper: allocate a 4-element Scheme vector for cell data */−147
static Value make_cell_vector(SigilVM *vm, int32_t *cell)−148
{−149
SigilVector *vec = sigil__gc_alloc(vm, SIGIL_OBJ_VECTOR,−150
sizeof(SigilVector) + 4 * sizeof(Value));−151
vec->length = 4;−152
vec->elements[0] = sigil_char((uint32_t)cell[CELL_CHAR]);−153
vec->elements[1] = sigil_fixnum(cell[CELL_FG]);−154
vec->elements[2] = sigil_fixnum(cell[CELL_BG]);−155
vec->elements[3] = sigil_fixnum(cell[CELL_ATTRS]);−156
return sigil_ptr(vec);−157
}−158
−159
/*−160
* %grid-ref grid col row -> vector #(char fg bg attrs)−161
*−162
* Returns a fresh vector for compatibility with existing code.−163
*/−164
static Value native_grid_ref(SigilVM *vm, int argc, Value *args)−165
{−166
(void)argc;−167
−168
Grid *g = as_grid(args[0]);−169
int col = (int)sigil_as_fixnum(args[1]);−170
int row = (int)sigil_as_fixnum(args[2]);−171
−172
if (col < 0 || col >= g->width || row < 0 || row >= g->height) {−173
sigil__vm_error(vm, SIGIL_ERR_TYPE,−174
"grid-ref: index out of bounds");−175
return SIGIL_UNDEFINED;−176
}−177
−178
return make_cell_vector(vm, cell_at(g, col, row));−179
}−180
−181
/*−182
* %grid-set! grid col row ch fg bg attrs -> void−183
*/−184
static Value native_grid_set(SigilVM *vm, int argc, Value *args)−185
{−186
(void)argc;−187
−188
Grid *g = as_grid(args[0]);−189
int col = (int)sigil_as_fixnum(args[1]);−190
int row = (int)sigil_as_fixnum(args[2]);−191
−192
if (col < 0 || col >= g->width || row < 0 || row >= g->height) {−193
/* Silently ignore out-of-bounds writes (truncation) */−194
(void)vm;−195
return SIGIL_UNDEFINED;−196
}−197
−198
int32_t *cell = cell_at(g, col, row);−199
cell[CELL_CHAR] = (int32_t)sigil_as_char(args[3]);−200
cell[CELL_FG] = (int32_t)sigil_as_fixnum(args[4]);−201
cell[CELL_BG] = (int32_t)sigil_as_fixnum(args[5]);−202
cell[CELL_ATTRS] = (int32_t)sigil_as_fixnum(args[6]);−203
−204
return SIGIL_UNDEFINED;−205
}−206
−207
/*−208
* %grid-clear! grid -> void−209
*/−210
static Value native_grid_clear(SigilVM *vm, int argc, Value *args)−211
{−212
(void)vm; (void)argc;−213
−214
Grid *g = as_grid(args[0]);−215
size_t count = (size_t)g->width * g->height;−216
−217
for (size_t i = 0; i < count; i++) {−218
int32_t *cell = &g->cells[i * CELL_FIELDS];−219
cell_clear(cell);−220
}−221
−222
return SIGIL_UNDEFINED;−223
}−224
−225
/*−226
* %grid-write-string! grid col row str fg bg attrs -> void−227
*−228
* Write a string to the grid at (col, row), truncating at the grid edge.−229
*/−230
static Value native_grid_write_string(SigilVM *vm, int argc, Value *args)−231
{−232
(void)vm; (void)argc;−233
−234
Grid *g = as_grid(args[0]);−235
int col = (int)sigil_as_fixnum(args[1]);−236
int row = (int)sigil_as_fixnum(args[2]);−237
/* args[3] = string */−238
int32_t fg = (int32_t)sigil_as_fixnum(args[4]);−239
int32_t bg = (int32_t)sigil_as_fixnum(args[5]);−240
int32_t attrs = (int32_t)sigil_as_fixnum(args[6]);−241
−242
if (row < 0 || row >= g->height || col >= g->width) {−243
return SIGIL_UNDEFINED;−244
}−245
−246
SigilString *str = (SigilString *)sigil_as_ptr(args[3]);−247
const char *data = str->data;−248
size_t byte_len = str->byte_length;−249
int w = g->width;−250
int c = col;−251
size_t pos = 0;−252
−253
while (pos < byte_len && c < w) {−254
/* Decode UTF-8 codepoint */−255
uint32_t cp;−256
uint8_t b = (uint8_t)data[pos];−257
if (b < 0x80) {−258
cp = b;−259
pos += 1;−260
} else if ((b & 0xE0) == 0xC0) {−261
cp = (b & 0x1F) << 6;−262
if (pos + 1 < byte_len) cp |= ((uint8_t)data[pos + 1] & 0x3F);−263
pos += 2;−264
} else if ((b & 0xF0) == 0xE0) {−265
cp = (b & 0x0F) << 12;−266
if (pos + 1 < byte_len) cp |= ((uint8_t)data[pos + 1] & 0x3F) << 6;−267
if (pos + 2 < byte_len) cp |= ((uint8_t)data[pos + 2] & 0x3F);−268
pos += 3;−269
} else if ((b & 0xF8) == 0xF0) {−270
cp = (b & 0x07) << 18;−271
if (pos + 1 < byte_len) cp |= ((uint8_t)data[pos + 1] & 0x3F) << 12;−272
if (pos + 2 < byte_len) cp |= ((uint8_t)data[pos + 2] & 0x3F) << 6;−273
if (pos + 3 < byte_len) cp |= ((uint8_t)data[pos + 3] & 0x3F);−274
pos += 4;−275
} else {−276
cp = '?';−277
pos += 1;−278
}−279
−280
if (c >= 0) {−281
int32_t *cell = cell_at(g, c, row);−282
cell[CELL_CHAR] = (int32_t)cp;−283
cell[CELL_FG] = fg;−284
cell[CELL_BG] = bg;−285
cell[CELL_ATTRS] = attrs;−286
}−287
c++;−288
}−289
−290
return SIGIL_UNDEFINED;−291
}−292
−293
/*−294
* %grid-fill-rect! grid x y w h ch fg bg attrs -> void−295
*/−296
static Value native_grid_fill_rect(SigilVM *vm, int argc, Value *args)−297
{−298
(void)vm; (void)argc;−299
−300
Grid *g = as_grid(args[0]);−301
int x = (int)sigil_as_fixnum(args[1]);−302
int y = (int)sigil_as_fixnum(args[2]);−303
int w = (int)sigil_as_fixnum(args[3]);−304
int h = (int)sigil_as_fixnum(args[4]);−305
int32_t ch = (int32_t)sigil_as_char(args[5]);−306
int32_t fg = (int32_t)sigil_as_fixnum(args[6]);−307
int32_t bg = (int32_t)sigil_as_fixnum(args[7]);−308
int32_t attrs = (int32_t)sigil_as_fixnum(args[8]);−309
−310
int gw = g->width;−311
int gh = g->height;−312
−313
for (int r = y; r < y + h && r < gh; r++) {−314
if (r < 0) continue;−315
for (int c = x; c < x + w && c < gw; c++) {−316
if (c < 0) continue;−317
int32_t *cell = cell_at(g, c, r);−318
cell[CELL_CHAR] = ch;−319
cell[CELL_FG] = fg;−320
cell[CELL_BG] = bg;−321
cell[CELL_ATTRS] = attrs;−322
}−323
}−324
−325
return SIGIL_UNDEFINED;−326
}−327
−328
/*−329
* %grid-copy grid -> grid−330
*−331
* Create a deep copy of a grid.−332
*/−333
static Value native_grid_copy(SigilVM *vm, int argc, Value *args)−334
{−335
(void)argc;−336
−337
Grid *src = as_grid(args[0]);−338
Grid *dst = malloc(sizeof(Grid));−339
if (!dst) return SIGIL_FALSE;−340
−341
size_t size = (size_t)src->width * src->height * CELL_FIELDS;−342
dst->cells = malloc(size * sizeof(int32_t));−343
if (!dst->cells) {−344
free(dst);−345
return SIGIL_FALSE;−346
}−347
−348
dst->width = src->width;−349
dst->height = src->height;−350
memcpy(dst->cells, src->cells, size * sizeof(int32_t));−351
−352
return sigil_make_foreign(vm, grid_type_tag, dst, grid_finalizer,−353
sizeof(Grid) + size * sizeof(int32_t));−354
}−355
−356
/* ============================================================−357
* Grid diffing — the performance-critical hot path−358
*−359
* Scans cell-by-cell, emitting minimal ANSI escape sequences to−360
* update only changed cells. Returns a string to write atomically−361
* via terminal-write-raw.−362
* ============================================================ */−363
−364
/* Dynamic buffer for building diff output */−365
typedef struct {−366
char *data;−367
size_t len;−368
size_t cap;−369
} DiffBuf;−370
−371
static void buf_init(DiffBuf *buf)−372
{−373
buf->cap = 4096;−374
buf->data = malloc(buf->cap);−375
buf->len = 0;−376
}−377
−378
static void buf_ensure(DiffBuf *buf, size_t need)−379
{−380
if (buf->len + need > buf->cap) {−381
while (buf->len + need > buf->cap) {−382
buf->cap *= 2;−383
}−384
buf->data = realloc(buf->data, buf->cap);−385
}−386
}−387
−388
static void buf_append(DiffBuf *buf, const char *str, size_t len)−389
{−390
buf_ensure(buf, len);−391
memcpy(buf->data + buf->len, str, len);−392
buf->len += len;−393
}−394
−395
static void buf_append_str(DiffBuf *buf, const char *str)−396
{−397
buf_append(buf, str, strlen(str));−398
}−399
−400
static void buf_append_int(DiffBuf *buf, int n)−401
{−402
char tmp[16];−403
int len = snprintf(tmp, sizeof(tmp), "%d", n);−404
buf_append(buf, tmp, len);−405
}−406
−407
static void buf_append_char_utf8(DiffBuf *buf, uint32_t cp)−408
{−409
char tmp[4];−410
int len;−411
if (cp < 0x80) {−412
tmp[0] = (char)cp;−413
len = 1;−414
} else if (cp < 0x800) {−415
tmp[0] = (char)(0xC0 | (cp >> 6));−416
tmp[1] = (char)(0x80 | (cp & 0x3F));−417
len = 2;−418
} else if (cp < 0x10000) {−419
tmp[0] = (char)(0xE0 | (cp >> 12));−420
tmp[1] = (char)(0x80 | ((cp >> 6) & 0x3F));−421
tmp[2] = (char)(0x80 | (cp & 0x3F));−422
len = 3;−423
} else {−424
tmp[0] = (char)(0xF0 | (cp >> 18));−425
tmp[1] = (char)(0x80 | ((cp >> 12) & 0x3F));−426
tmp[2] = (char)(0x80 | ((cp >> 6) & 0x3F));−427
tmp[3] = (char)(0x80 | (cp & 0x3F));−428
len = 4;−429
}−430
buf_append(buf, tmp, len);−431
}−432
−433
/* Emit foreground color SGR to buffer */−434
static void emit_fg(DiffBuf *buf, int32_t color)−435
{−436
if (color == -1) {−437
buf_append_str(buf, "39");−438
} else if (color >= 0 && color <= 7) {−439
buf_append_int(buf, color + 30);−440
} else if (color >= 8 && color <= 15) {−441
buf_append_int(buf, color + 82); /* 90-97 */−442
} else if (color >= 16 && color <= 255) {−443
buf_append_str(buf, "38;5;");−444
buf_append_int(buf, color);−445
} else {−446
/* Truecolor: decode packed RGB */−447
int v = color - 65536; /* undo +1 offset from color-rgb */−448
int r = v / 65536;−449
int rem = v % 65536;−450
int g = rem / 256;−451
int b = rem % 256;−452
buf_append_str(buf, "38;2;");−453
buf_append_int(buf, r);−454
buf_append(buf, ";", 1);−455
buf_append_int(buf, g);−456
buf_append(buf, ";", 1);−457
buf_append_int(buf, b);−458
}−459
}−460
−461
/* Emit background color SGR to buffer */−462
static void emit_bg(DiffBuf *buf, int32_t color)−463
{−464
if (color == -1) {−465
buf_append_str(buf, "49");−466
} else if (color >= 0 && color <= 7) {−467
buf_append_int(buf, color + 40);−468
} else if (color >= 8 && color <= 15) {−469
buf_append_int(buf, color + 92); /* 100-107 */−470
} else if (color >= 16 && color <= 255) {−471
buf_append_str(buf, "48;5;");−472
buf_append_int(buf, color);−473
} else {−474
int v = color - 65536;−475
int r = v / 65536;−476
int rem = v % 65536;−477
int g = rem / 256;−478
int b = rem % 256;−479
buf_append_str(buf, "48;2;");−480
buf_append_int(buf, r);−481
buf_append(buf, ";", 1);−482
buf_append_int(buf, g);−483
buf_append(buf, ";", 1);−484
buf_append_int(buf, b);−485
}−486
}−487
−488
/* Emit attribute codes to buffer */−489
static void emit_attrs(DiffBuf *buf, int32_t attrs)−490
{−491
if (attrs & ATTR_BOLD) buf_append_str(buf, ";1");−492
if (attrs & ATTR_DIM) buf_append_str(buf, ";2");−493
if (attrs & ATTR_ITALIC) buf_append_str(buf, ";3");−494
if (attrs & ATTR_UNDERLINE) buf_append_str(buf, ";4");−495
if (attrs & ATTR_INVERSE) buf_append_str(buf, ";7");−496
if (attrs & ATTR_STRIKETHROUGH) buf_append_str(buf, ";9");−497
}−498
−499
/* 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.