Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c28d2aa
Map Random.nextInt through LCG high bits and fail on a non-positive b…
SeanCheatham Sep 4, 2026
16f99a5
Grow inject and dump buffers and escape signal strings so Timeline cl…
SeanCheatham Sep 4, 2026
804bd73
Make scuzz lsp honest on the wire so editors overlay buffers, read fu…
SeanCheatham Sep 4, 2026
4990fa9
Inspect List heads by RC kind instead of emit's as_int guess so bound…
SeanCheatham Sep 4, 2026
e27aef8
Reject illegal *.scuzz_verify drives and leftover *.scuzz_intent in s…
SeanCheatham Sep 4, 2026
065a579
Close the Fs root-delete guard with canonical path and inode checks s…
SeanCheatham Sep 4, 2026
d7c3407
Make TestRuntime TCP/UDP match live contracts so parked tcpRead respe…
SeanCheatham Sep 4, 2026
d256385
Share one call aligner and one _1.._8 field index so Check and Emit a…
SeanCheatham Sep 4, 2026
1332b75
Pull Stream.take through dropping combinators and stop head/forall ea…
SeanCheatham Sep 4, 2026
992e01b
Typecheck packages before disk emit so scuzz build cannot write IR fo…
SeanCheatham Sep 4, 2026
62bf596
Pin Resource.make and Resource.use on the closed kit so Check no long…
SeanCheatham Sep 4, 2026
9a9f950
Fail check on leftover and unknown tokens so a truncated file cannot …
SeanCheatham Sep 4, 2026
a084e26
Close the Json kit so unknown Json.* fails check and documented ops e…
SeanCheatham Sep 4, 2026
e704bc0
Close the Str kit so unknown Str.* fails check, length and pad walk t…
SeanCheatham Sep 4, 2026
f8ec361
Keep Sys.kill and Sys.exec children until waitpid reaps them so watch…
SeanCheatham Sep 4, 2026
4ba0fd3
Make scuzz ide find the packaged editor and pass the user path, and f…
SeanCheatham Sep 4, 2026
2c50266
Export SCUZZ_UI_RUNTIME from [ui].default_runtime on scuzz run and sc…
SeanCheatham Sep 4, 2026
7399555
Check function apply against the argument type and drop funBodyEq so …
SeanCheatham Sep 4, 2026
750d562
Make mutation count and apply share one walk so pickSites hits the nu…
SeanCheatham Sep 4, 2026
6c86f53
Close the Deferred kit so unknown Deferred.* fails check and Deferred…
SeanCheatham Sep 4, 2026
98d8b89
Make the default Skia archive match sk_capi.h so peek is RGBA and cli…
SeanCheatham Sep 4, 2026
a3c9f0f
Pass the product scuzz binary into android/ios package shells and quo…
SeanCheatham Sep 4, 2026
9df77b7
Export SCUZZ_UI_RECORD and SCUZZ_UI_DEBUG_DUMP on Desktop/Mobile scuz…
SeanCheatham Sep 4, 2026
3745ba3
Pretty-print `::` with the parser’s precedence and right-assoc parens…
SeanCheatham Sep 4, 2026
a1ecf81
Infer Set map/toSet key kind from the actual key, not a hardcoded emi…
SeanCheatham Sep 4, 2026
0588e47
Map.get still treats a stored empty list as a miss because NULL means…
SeanCheatham Sep 4, 2026
ea2eea0
Expose mkdtemp on Darwin with _DARWIN_C_SOURCE so macos-smoke can com…
SeanCheatham Sep 5, 2026
0f43832
Seed SCUZZ_SERVE and SCUZZ_KIT into the sealed getenv map so scuzz te…
SeanCheatham Sep 5, 2026
c01bc2f
Accept kebab package names in scuzz.toml so Manifest matches the sche…
SeanCheatham Sep 5, 2026
69afc11
Emit tuple slot projection from any receiver so f()._1 compiles inste…
SeanCheatham Sep 5, 2026
4e0170f
Treat an empty Timeline.signalStrHas needle as an empty string value …
SeanCheatham Sep 5, 2026
9198fa6
Replay only corpus that passed on the original during mutation so an …
SeanCheatham Sep 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -466,15 +466,16 @@ jobs:
grep -q "needs formatting" /tmp/fmt-check.err
"$SCUZZ" build examples/kernel
"$SCUZZ" build examples/kernel 2>&1 | tee /tmp/incr.out
grep -q "up-to-date" /tmp/incr.out
if grep -q "^ok$" /tmp/incr.out; then echo "fingerprint hit should not rebuild" && exit 1; fi
test -x examples/kernel/build/kernel
# Path-dep invalidation: a change in a dependency source must rebuild the root.
"$SCUZZ" build --full examples/counter
"$SCUZZ" build examples/counter 2>&1 | tee /tmp/counter-incr.out
grep -q "up-to-date" /tmp/counter-incr.out
if grep -q "^ok$" /tmp/counter-incr.out; then echo "fingerprint hit should not rebuild" && exit 1; fi
cp examples/shared/src/Shared.scuzz /tmp/shared-orig.scuzz
printf 'def counterTitle(): String =\n "Counter"\n\ndef countLabel(n: Int): String =\n s"count = $n!"\n' > examples/shared/src/Shared.scuzz
"$SCUZZ" build examples/counter 2>&1 | tee /tmp/counter-inval.out
if grep -q "up-to-date" /tmp/counter-inval.out; then echo "dependency edit should invalidate fingerprint" && exit 1; fi
if ! grep -q "^ok$" /tmp/counter-inval.out; then echo "dependency edit should invalidate fingerprint" && exit 1; fi
cp /tmp/shared-orig.scuzz examples/shared/src/Shared.scuzz
"$SCUZZ" build --full examples/counter

Expand Down
72 changes: 60 additions & 12 deletions crates/ffi-skia/Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
CC ?= clang
CXX ?= clang++
CFLAGS ?= -std=c11 -Wall -Wextra -Werror -O2
ROOT := $(abspath ../..)
TRIPLE ?= $(shell $(ROOT)/scripts/skia_triple.sh)
PREBUILT_DIR := $(ROOT)/third_party/skia/prebuilt/$(TRIPLE)
PREBUILT_LIB := $(PREBUILT_DIR)/libsk_capi.a
PIN := $(ROOT)/third_party/skia/PIN
SKIA_BRANCH := $(shell awk -F= '/^skia_branch=/{print substr($$0,13); exit}' "$(PIN)")
SKIA_HDR_URL := https://skia.googlesource.com/skia/+archive/$(SKIA_BRANCH)/include.tar.gz
INCLUDES := -Iinclude -Isrc
SRC := src/sk_sw.c src/png_enc.c src/sk_mono.c
OBJ := $(SRC:src/%.c=build/%.o)
SHIM_OBJS := build/sk_capi_skia.o build/sk_capi_skia_bridge.o

.PHONY: all clean test lib lib-skia lib-sk-sw lib-gpu ensure-prebuilt

Expand Down Expand Up @@ -39,8 +44,10 @@ ensure-prebuilt:
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
GPU_LIBS := -framework OpenGL
SKIA_LIBS := -lc++ -lm -lz -lbz2 -framework CoreFoundation -framework CoreGraphics -framework CoreText -framework Foundation -framework Carbon
else
GPU_LIBS := -lEGL -lGLESv2
SKIA_LIBS := -lstdc++ -lm -lz -lbz2
endif

build/%.o: src/%.c include/sk_capi.h src/png_enc.h src/sk_gpu.h | build
Expand All @@ -49,6 +56,24 @@ build/%.o: src/%.c include/sk_capi.h src/png_enc.h src/sk_gpu.h | build
build/sk_gpu.o: src/sk_gpu.c src/sk_gpu.h | build
$(CC) $(CFLAGS) -Wno-deprecated-declarations $(INCLUDES) -c $< -o $@

# Headers that match the pin's Skia branch. Not a vendored tree.
build/skia-src/.stamp: | build
@mkdir -p build/skia-src/include
@echo "ffi-skia: fetch $(SKIA_BRANCH) include for shim relink"
@curl -fsSL "$(SKIA_HDR_URL)" -o build/skia-include.tar.gz
@rm -rf build/skia-src/include
@mkdir -p build/skia-src/include
@tar -xzf build/skia-include.tar.gz -C build/skia-src/include
@test -f build/skia-src/include/core/SkCanvas.h
@echo "$(SKIA_BRANCH)" > $@

build/sk_capi_skia.o: src/sk_capi_skia.cpp include/sk_capi.h build/skia-src/.stamp
$(CXX) -std=c++17 -O2 -fPIC -c src/sk_capi_skia.cpp -o $@ \
-Ibuild/skia-src -DSK_RELEASE -DSCUZZ_SKIA_EMBEDDED_FONT

build/sk_capi_skia_bridge.o: src/sk_capi_skia_bridge.c include/sk_capi.h | build
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@

lib-sk-sw: $(OBJ) build/sk_gpu_none.o | build
@if [ -f build/libsk_capi.a ] && [ -f build/sk_capi_backend ] && grep -qx sk_sw build/sk_capi_backend; then \
fresh=1; \
Expand All @@ -73,35 +98,58 @@ lib-gpu: $(OBJ) build/sk_gpu.o | build
ar rcs build/libsk_capi.a $(OBJ) build/sk_gpu.o; \
echo gpu > build/sk_capi_backend

lib-skia: build/sk_gpu_skia_stub.o build/sk_mono.o | build
# Pin supplies Skia objects + font. Replace the shim so the C ABI matches
# sk_capi.h (save/clip/restore, RGBA peek). Fail if clip symbols are missing.
lib-skia: build/sk_gpu_skia_stub.o build/sk_mono.o $(SHIM_OBJS) | build
@test -f "$(PREBUILT_LIB)"
@if [ -f build/libsk_capi.a ] && [ -f build/sk_capi_backend ] && grep -qx skia build/sk_capi_backend \
&& [ ! "$(PREBUILT_LIB)" -nt build/libsk_capi.a ] \
&& [ ! build/sk_gpu_skia_stub.o -nt build/libsk_capi.a ] \
&& [ ! build/sk_mono.o -nt build/libsk_capi.a ]; then \
exit 0; \
&& [ ! build/sk_mono.o -nt build/libsk_capi.a ] \
&& [ ! build/sk_capi_skia.o -nt build/libsk_capi.a ] \
&& [ ! build/sk_capi_skia_bridge.o -nt build/libsk_capi.a ]; then \
if nm build/libsk_capi.a | grep -E ' T _?sk_canvas_clip_rect' >/dev/null && \
nm build/libsk_capi.a | grep -E ' T _?sk_canvas_save' >/dev/null && \
nm build/libsk_capi.a | grep -E ' T _?sk_canvas_restore' >/dev/null; then \
exit 0; \
fi; \
fi; \
rm -f build/*.a; \
cp -f "$(PREBUILT_LIB)" build/libsk_capi.a; \
ar rcs build/libsk_capi.a build/sk_gpu_skia_stub.o build/sk_mono.o; \
ar d build/libsk_capi.a sk_capi_skia.o sk_capi_skia_bridge.o; \
ar rcs build/libsk_capi.a $(SHIM_OBJS) build/sk_gpu_skia_stub.o build/sk_mono.o; \
if ! nm build/libsk_capi.a | grep -E ' T _?sk_canvas_clip_rect' >/dev/null; then \
echo "ffi-skia: linked archive lacks sk_canvas_clip_rect" >&2; \
exit 1; \
fi; \
if ! nm build/libsk_capi.a | grep -E ' T _?sk_canvas_save' >/dev/null; then \
echo "ffi-skia: linked archive lacks sk_canvas_save" >&2; \
exit 1; \
fi; \
if ! nm build/libsk_capi.a | grep -E ' T _?sk_canvas_restore' >/dev/null; then \
echo "ffi-skia: linked archive lacks sk_canvas_restore" >&2; \
exit 1; \
fi; \
echo skia > build/sk_capi_backend

build/test_skia: tests/test_skia.c lib
@extra=""; \
if [ -f build/sk_capi_backend ] && grep -qx skia build/sk_capi_backend; then \
if [ "$$(uname -s)" = Darwin ]; then \
extra="-lc++ -lm -lz -lbz2 -framework CoreFoundation -framework CoreGraphics -framework CoreText -framework Foundation -framework Carbon"; \
else \
extra="-lstdc++ -lm -lz -lbz2"; \
fi; \
extra="$(SKIA_LIBS)"; \
elif [ -f build/sk_capi_backend ] && grep -qx gpu build/sk_capi_backend; then \
extra="$(GPU_LIBS)"; \
fi; \
$(CC) $(CFLAGS) $(INCLUDES) tests/test_skia.c -Lbuild -lsk_capi $$extra -lpthread -o $@

# Canvas clip against in-tree sk_sw. The pinned Skia archive does not export save/clip.
build/test_sk_clip: tests/test_sk_clip.c src/sk_sw.c src/png_enc.c src/sk_gpu_none.c include/sk_capi.h src/png_enc.h | build
$(CC) $(CFLAGS) $(INCLUDES) tests/test_sk_clip.c src/sk_sw.c src/png_enc.c src/sk_gpu_none.c -o $@
# Clip / RGBA / UTF-8 against the archive `lib` actually built.
build/test_sk_clip: tests/test_sk_clip.c lib
@extra=""; \
if [ -f build/sk_capi_backend ] && grep -qx skia build/sk_capi_backend; then \
extra="$(SKIA_LIBS)"; \
elif [ -f build/sk_capi_backend ] && grep -qx gpu build/sk_capi_backend; then \
extra="$(GPU_LIBS)"; \
fi; \
$(CC) $(CFLAGS) $(INCLUDES) tests/test_sk_clip.c -Lbuild -lsk_capi $$extra -lpthread -o $@

build/test_sk_gpu: tests/test_sk_gpu.c lib
$(CC) $(CFLAGS) $(INCLUDES) tests/test_sk_gpu.c -Lbuild -lsk_capi $(GPU_LIBS) -lpthread -o $@
Expand Down
3 changes: 2 additions & 1 deletion crates/ffi-skia/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
Thin Skia-shaped C ABI (`include/sk_capi.h`) for Headless/Desktop paint.

**Default:** pinned Skia CPU prebuilt through `scripts/fetch_skia.sh` /
`third_party/skia/PIN` → `build/sk_capi_backend` = `skia`.
`third_party/skia/PIN` → `build/sk_capi_backend` = `skia`. `make lib` compiles
the in-tree shim into that archive so the C ABI matches `sk_capi.h`.
**Opt out:** `SCUZZ_SKIA=sk_sw` builds in-tree `src/sk_sw.c`.
**GPU presenter:** `SCUZZ_SKIA=gpu` paints with `sk_sw` and presents through
OpenGL (upload + readback). Missing OpenGL fails with one install line.
Expand Down
5 changes: 4 additions & 1 deletion crates/ffi-skia/src/png_enc.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "png_enc.h"

#include <stdint.h>
#include <stdlib.h>
#include <string.h>

Expand Down Expand Up @@ -159,8 +160,10 @@ uint8_t *sz_png_encode_rgba(const uint8_t *pixels, int width, int height,
return NULL;
if (stride <= 0)
stride = width * 4;
if ((size_t)width > (SIZE_MAX - 1u) / 4u)
return NULL;

filt_len = (size_t)(width * 4 + 1) * (size_t)height;
filt_len = ((size_t)width * 4u + 1u) * (size_t)height;
filt = (uint8_t *)malloc(filt_len);
if (!filt)
return NULL;
Expand Down
6 changes: 4 additions & 2 deletions crates/ffi-skia/src/sk_capi_skia.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ extern "C" {
void *scuzz_skia_surface_make(int width, int height) {
if (width <= 0 || height <= 0)
return nullptr;
sk_sp<SkSurface> surf =
SkSurfaces::Raster(SkImageInfo::MakeN32Premul(width, height));
sk_sp<SkSurface> surf = SkSurfaces::Raster(SkImageInfo::Make(
width, height, kRGBA_8888_SkColorType, kPremul_SkAlphaType));
if (!surf)
return nullptr;
auto *out = new CapSurface();
Expand Down Expand Up @@ -246,6 +246,8 @@ int scuzz_skia_encode_png(const void *surface, uint8_t **out_bytes,
int scuzz_skia_encode_png_to_file(const void *surface, const char *path) {
uint8_t *bytes = nullptr;
size_t len = 0;
if (!path)
return 0;
if (!scuzz_skia_encode_png(surface, &bytes, &len))
return 0;
SkFILEWStream stream(path);
Expand Down
28 changes: 24 additions & 4 deletions crates/ffi-skia/src/sk_gpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,16 +106,36 @@ static int gpu_make_current(void) {
}
if (!gpu_init_display())
return 0;
if (!eglChooseConfig(g_dpy, cfg_attr, &cfg, 1, &n) || n < 1)
if (!eglChooseConfig(g_dpy, cfg_attr, &cfg, 1, &n) || n < 1) {
eglTerminate(g_dpy);
g_dpy = EGL_NO_DISPLAY;
return 0;
}
g_surf = eglCreatePbufferSurface(g_dpy, cfg, pb_attr);
if (g_surf == EGL_NO_SURFACE)
if (g_surf == EGL_NO_SURFACE) {
eglTerminate(g_dpy);
g_dpy = EGL_NO_DISPLAY;
return 0;
}
eglBindAPI(EGL_OPENGL_ES_API);
g_ctx = eglCreateContext(g_dpy, cfg, EGL_NO_CONTEXT, ctx_attr);
if (g_ctx == EGL_NO_CONTEXT)
if (g_ctx == EGL_NO_CONTEXT) {
eglDestroySurface(g_dpy, g_surf);
g_surf = EGL_NO_SURFACE;
eglTerminate(g_dpy);
g_dpy = EGL_NO_DISPLAY;
return 0;
}
if (eglMakeCurrent(g_dpy, g_surf, g_surf, g_ctx) != EGL_TRUE) {
eglDestroyContext(g_dpy, g_ctx);
eglDestroySurface(g_dpy, g_surf);
g_ctx = EGL_NO_CONTEXT;
g_surf = EGL_NO_SURFACE;
eglTerminate(g_dpy);
g_dpy = EGL_NO_DISPLAY;
return 0;
return eglMakeCurrent(g_dpy, g_surf, g_surf, g_ctx) == EGL_TRUE;
}
return 1;
}

#endif
Expand Down
42 changes: 37 additions & 5 deletions crates/ffi-skia/src/sk_sw.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,12 @@ void sk_canvas_draw_rect(SkCanvas *canvas, float x, float y, float w, float h,
}

void sk_canvas_save(SkCanvas *canvas) {
if (!canvas || canvas->save_n >= SK_CLIP_STACK)
if (!canvas)
return;
if (canvas->save_n >= SK_CLIP_STACK) {
fputs("sk_capi: clip save stack is full\n", stderr);
abort();
}
canvas->save_x0[canvas->save_n] = canvas->clip_x0;
canvas->save_y0[canvas->save_n] = canvas->clip_y0;
canvas->save_x1[canvas->save_n] = canvas->clip_x1;
Expand Down Expand Up @@ -328,12 +332,36 @@ static const uint8_t FONT8[95][8] = {
{0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
};

/* Same walk as sk_mono.c: one Unicode code point is one cell. */
static int sw_utf8_clen(const char *s) {
unsigned char c;
if (!s || !s[0])
return 0;
c = (unsigned char)s[0];
if (c < 0x80)
return 1;
if ((c & 0xe0) == 0xc0)
return s[1] ? 2 : 1;
if ((c & 0xf0) == 0xe0)
return (s[1] && s[2]) ? 3 : 1;
if ((c & 0xf8) == 0xf0)
return (s[1] && s[2] && s[3]) ? 4 : 1;
return 1;
}

float sk_font_measure_string(const char *text, float font_px) {
size_t n;
const char *p;
int n = 0;
float px = font_px > 0.f ? font_px : 8.f;
if (!text)
return 0.f;
n = strlen(text);
for (p = text; *p;) {
int clen = sw_utf8_clen(p);
if (clen < 1)
clen = 1;
p += clen;
n++;
}
return (float)n * px;
}

Expand All @@ -353,10 +381,13 @@ void sk_canvas_draw_string(SkCanvas *canvas, const char *text, float x, float y,
if (baseline < 0)
baseline = 0;
cx = (int)x;
for (p = text; *p; p++) {
for (p = text; *p;) {
int clen = sw_utf8_clen(p);
unsigned char ch = (unsigned char)*p;
const uint8_t *glyph;
int gy = (int)y - baseline;
if (clen < 1)
clen = 1;
if (ch < 32 || ch > 126)
ch = '?';
glyph = FONT8[ch - 32];
Expand Down Expand Up @@ -384,6 +415,7 @@ void sk_canvas_draw_string(SkCanvas *canvas, const char *text, float x, float y,
}
}
cx += advance;
p += clen;
}
}

Expand Down Expand Up @@ -448,7 +480,7 @@ int sk_encode_png_to_file(const SkSurface *surface, const char *path) {
size_t len = 0;
FILE *f;
size_t n;
if (!sk_encode_png(surface, &bytes, &len))
if (!path || !sk_encode_png(surface, &bytes, &len))
return 0;
f = fopen(path, "wb");
if (!f) {
Expand Down
5 changes: 2 additions & 3 deletions crates/ffi-skia/tests/test_sk_clip.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/* Canvas clip against in-tree sk_sw. The pinned Skia archive does not
* export save/clip/restore. */
/* Canvas clip against the archive `make lib` actually built. */
#include "sk_capi.h"

#include <assert.h>
Expand All @@ -25,7 +24,7 @@ int main(void) {
sk_canvas_restore(canvas);
px = sk_surface_peek_pixels(surf, &px_len);
assert(px && px_len == 32 * 32 * 4);
/* Inside clip: fill. Outside: clear color. */
/* Inside clip: fill. Outside: clear color. Peek is RGBA. */
assert(px[(10 * 32 + 10) * 4] == 240);
assert(px[(10 * 32 + 10) * 4 + 1] == 240);
assert(px[(10 * 32 + 10) * 4 + 2] == 240);
Expand Down
Loading
Loading