summaryrefslogtreecommitdiffstats
path: root/src/json.c
diff options
context:
space:
mode:
authorsl0p <sl0p@sl0p.foo>2026-08-13 22:26:02 +0200
committersl0p <sl0p@sl0p.foo>2026-08-13 22:26:02 +0200
commit34fcd16e59e3087e9bf84ebce4e05d439eea9198 (patch)
treee07b9dfc235e3a0b67047d59a9c521c6e98a28ae /src/json.c
parentsl0ptty: input decoder + per-pane re-encode (diff)
downloadslosh-34fcd16e59e3087e9bf84ebce4e05d439eea9198.tar.gz
slosh-34fcd16e59e3087e9bf84ebce4e05d439eea9198.tar.xz
slosh-34fcd16e59e3087e9bf84ebce4e05d439eea9198.zip
sl0ptty: M0.5 — the headless driver and the harness everything rests on
--script runs the whole compositor with no tty: commands in (send, raw, settle, resize, snapshot, alive), composited screen out as JSON — rows of text, style runs, cursor, and the hit list. Styles are emitted as runs rather than per-cell objects, because a test wants 'columns 0..5 of row 0 are bold red', not 1920 objects. The hit list is empty until M1 paints something clickable, but the type, the JSON, and hit_test() exist now: one geometry, established before there is anything to get wrong. Also fixes two host-policy defaults found by the cursor never appearing. lib-vt is a VT library, not a terminal, so it starts with DECTCEM (mode 25) off and grapheme clustering (2027) off. Both are now set via MODE_DEFAULT, so they survive RIS. tests/harness.py + tests/test_screen.py: 19 checks over composition, styling, wide chars/clusters, scrollback, resize and the full input round trip — including the kitty/legacy re-encoding matrix, which no longer needs a pty. make test is now 2.4s for 59 checks; make test-live keeps the four that genuinely need a tty.
Diffstat (limited to 'src/json.c')
-rw-r--r--src/json.c111
1 files changed, 111 insertions, 0 deletions
diff --git a/src/json.c b/src/json.c
new file mode 100644
index 0000000..efbe120
--- /dev/null
+++ b/src/json.c
@@ -0,0 +1,111 @@
+#define _GNU_SOURCE
+#include "json.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void reserve(json_t *j, size_t n) {
+ if (j->len + n <= j->cap) return;
+ size_t cap = j->cap ? j->cap : 4096;
+ while (cap < j->len + n) cap *= 2;
+ j->buf = realloc(j->buf, cap);
+ j->cap = cap;
+}
+
+static void raw(json_t *j, const char *s, size_t n) {
+ reserve(j, n + 1);
+ memcpy(j->buf + j->len, s, n);
+ j->len += n;
+ j->buf[j->len] = 0;
+}
+
+static void rawz(json_t *j, const char *s) { raw(j, s, strlen(s)); }
+
+void json_init(json_t *j) {
+ memset(j, 0, sizeof *j);
+ reserve(j, 4096);
+ j->buf[0] = 0;
+}
+
+void json_free(json_t *j) {
+ free(j->buf);
+ memset(j, 0, sizeof *j);
+}
+
+static void sep(json_t *j, const char *key) {
+ if (j->need_comma) rawz(j, ",");
+ if (key) {
+ rawz(j, "\"");
+ rawz(j, key);
+ rawz(j, "\":");
+ }
+ j->need_comma = true;
+}
+
+static void escaped(json_t *j, const char *s, size_t len) {
+ rawz(j, "\"");
+ for (size_t i = 0; i < len; i++) {
+ unsigned char c = (unsigned char)s[i];
+ switch (c) {
+ case '"': rawz(j, "\\\""); break;
+ case '\\': rawz(j, "\\\\"); break;
+ case '\n': rawz(j, "\\n"); break;
+ case '\r': rawz(j, "\\r"); break;
+ case '\t': rawz(j, "\\t"); break;
+ default:
+ if (c < 0x20) {
+ char b[8];
+ snprintf(b, sizeof b, "\\u%04x", c);
+ rawz(j, b);
+ } else {
+ raw(j, (const char *)&c, 1); /* UTF-8 passes through */
+ }
+ }
+ }
+ rawz(j, "\"");
+}
+
+void json_obj_open(json_t *j, const char *key) {
+ sep(j, key);
+ rawz(j, "{");
+ j->need_comma = false;
+}
+
+void json_obj_close(json_t *j) {
+ rawz(j, "}");
+ j->need_comma = true;
+}
+
+void json_arr_open(json_t *j, const char *key) {
+ sep(j, key);
+ rawz(j, "[");
+ j->need_comma = false;
+}
+
+void json_arr_close(json_t *j) {
+ rawz(j, "]");
+ j->need_comma = true;
+}
+
+void json_str(json_t *j, const char *key, const char *val, size_t len) {
+ sep(j, key);
+ escaped(j, val, len);
+}
+
+void json_int(json_t *j, const char *key, long long val) {
+ sep(j, key);
+ char b[32];
+ snprintf(b, sizeof b, "%lld", val);
+ rawz(j, b);
+}
+
+void json_bool(json_t *j, const char *key, bool val) {
+ sep(j, key);
+ rawz(j, val ? "true" : "false");
+}
+
+void json_null(json_t *j, const char *key) {
+ sep(j, key);
+ rawz(j, "null");
+}