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.