Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
83bb55c
Evaluator slice 4 step 2: scuzz fuzz probes run on the evaluator
SeanCheatham Sep 18, 2026
98f80b3
Evaluator slice 5 steps A and B: probe server and one effect per sche…
SeanCheatham Sep 18, 2026
5d2b54a
Evaluator slice 5 step C: kernel and fmt idle probes under the probe …
SeanCheatham Sep 18, 2026
ffad09b
Evaluator slice 5 step D: comparison distance feedback steers search
SeanCheatham Sep 18, 2026
a6b0d1e
Evaluator slice 6 step 1: UI kits at Value
SeanCheatham Sep 19, 2026
1ff54bb
Evaluator slice 6 step 2: Try it page in Docs, headless
SeanCheatham Sep 19, 2026
9d41d6f
Evaluator slice 6 step 3: Try it in the browser
SeanCheatham Sep 19, 2026
3601d8a
Evaluator slice 7: guided tutorial, and drain OS events on idle deskt…
SeanCheatham Sep 19, 2026
619bd5b
Evaluator slice 8: in-page Fuzz search on How it runs.
SeanCheatham Sep 19, 2026
977cf2b
Evaluator slices 9 and 10: tutorial path and schedule branches.
SeanCheatham Sep 19, 2026
9a1f3b1
Evaluator slice 11: two scheduler worlds as a pair of cards.
SeanCheatham Sep 19, 2026
91ceb4d
Evaluator slice 12: Docs is a gated walkthrough, not a painted manual.
SeanCheatham Sep 19, 2026
cbfd1b4
Evaluator slice 13: grow one Counter across Docs stages.
SeanCheatham Sep 19, 2026
313795c
Require oracle for Bool drives and teach Check from the verify file.
SeanCheatham Sep 20, 2026
a1bc58c
Keep CI Bool-drive fixtures on oracle so search still fails.
SeanCheatham Sep 20, 2026
eb0d05a
Skip evaluator trace formatting for huge for-bindings.
SeanCheatham Sep 20, 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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ See [compatibility](docs/compatibility.md) for platform details and [known gaps]

## Learn more and contribute

- **Read the hosted docs:** open [scuzz.build](https://scuzz.build).
- **Open the hosted walkthrough:** [scuzz.build](https://scuzz.build). The technical manual is `scuzz docs`.
- **Start building:** run `scuzz docs start` after installation.
- **Learn the language:** run `scuzz docs language`.
- **Build interfaces:** run `scuzz docs gui`.
Expand Down
6 changes: 5 additions & 1 deletion crates/embedder-desktop/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@ build/libscuzz_embedder.a: $(OBJ)
build/test_headless: tests/test_headless.c build/libscuzz_embedder.a | build
$(CC) $(CFLAGS) $(INCLUDES) tests/test_headless.c -Lbuild -lscuzz_embedder -lX11 -o $@

test: lib build/test_headless
build/test_idle_events: tests/test_idle_events.c build/libscuzz_embedder.a | build
$(CC) $(CFLAGS) $(INCLUDES) tests/test_idle_events.c -Lbuild -lscuzz_embedder -lX11 -o $@

test: lib build/test_headless build/test_idle_events
env -u DISPLAY ./build/test_headless
@if [ -n "$$DISPLAY" ]; then ./build/test_idle_events; else echo "embedder-desktop: skip idle events (no DISPLAY)"; fi

else ifeq ($(UNAME_S),Darwin)

Expand Down
5 changes: 3 additions & 2 deletions crates/embedder-desktop/include/scuzz_embedder.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ int sz_embedder_present(const char *title, int point_w, int point_h,
/* Destroy the window / display connection. */
void sz_embedder_shutdown(void);

/* Pop one queued OS event into out. Returns 1 if an event was written.
* present() enqueues pointer / scroll / key events. pump drains through this. */
/* Drain pending OS events into the queue, then pop one into out.
* Returns 1 if an event was written. Idle pumps call this so a static
* frame still receives clicks and close. */
int sz_embedder_poll_event(SzInputEvent *out);

/* Session clipboard sync. `set` stores UTF-8 on the OS pasteboard when a
Expand Down
142 changes: 83 additions & 59 deletions crates/embedder-desktop/src/macos_present.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
static void enqueue_compose(const char *text);
static void enqueue_text_edit(const char *text);
static void enqueue_key(const char *name, const char *text, int mods, int repeat);
static void enqueue_pointer(SzPointerPhase phase, float x, float y, int button);
static void enqueue_scroll(float x, float y, float dx, float dy);
static int event_content_xy(NSEvent *ev, float *x, float *y);
static void mark_user_quit(void);

/* Finder launch has no CLI environment. Read the packaged UI configuration. */
__attribute__((constructor)) static void configure_bundle(void) {
Expand Down Expand Up @@ -259,7 +263,83 @@ static int q_push(const SzInputEvent *ev) {
return 1;
}

static int cocoa_drain_events(void) {
int quit = 0;
for (;;) {
NSEvent *ev = [NSApp nextEventMatchingMask:NSEventMaskAny
untilDate:[NSDate distantPast]
inMode:NSDefaultRunLoopMode
dequeue:YES];
if (!ev)
break;

{
NSEventType t = [ev type];
float x, y;
if (t == NSEventTypeLeftMouseDown && event_content_xy(ev, &x, &y)) {
enqueue_pointer(SZ_POINTER_DOWN, x, y, 1);
continue;
}
if (t == NSEventTypeLeftMouseDragged && event_content_xy(ev, &x, &y)) {
enqueue_pointer(SZ_POINTER_MOVE, x, y, 1);
continue;
}
if (t == NSEventTypeLeftMouseUp && event_content_xy(ev, &x, &y)) {
enqueue_pointer(SZ_POINTER_UP, x, y, 1);
continue;
}
if (t == NSEventTypeRightMouseDown && event_content_xy(ev, &x, &y)) {
enqueue_pointer(SZ_POINTER_DOWN, x, y, 3);
continue;
}
if (t == NSEventTypeRightMouseDragged && event_content_xy(ev, &x, &y)) {
enqueue_pointer(SZ_POINTER_MOVE, x, y, 3);
continue;
}
if (t == NSEventTypeRightMouseUp && event_content_xy(ev, &x, &y)) {
enqueue_pointer(SZ_POINTER_UP, x, y, 3);
continue;
}
if (t == NSEventTypeMouseMoved && event_content_xy(ev, &x, &y)) {
enqueue_pointer(SZ_POINTER_MOVE, x, y, 0);
continue;
}
if (t == NSEventTypeScrollWheel && event_content_xy(ev, &x, &y)) {
float dx = (float)[ev scrollingDeltaX];
float dy = (float)[ev scrollingDeltaY];
if (([ev modifierFlags] & NSEventModifierFlagShift) && dx == 0.f) {
dx = dy;
dy = 0.f;
}
enqueue_scroll(x, y, dx, dy);
continue;
}
}

if ([ev type] == NSEventTypeKeyDown) {
if (g_content)
[(ScuzzContentView *)g_content interpretKeyEvents:@[ev]];
continue;
}

[NSApp sendEvent:ev];
}
if (g_win && ![g_win isVisible])
quit = 1;
return quit;
}

int sz_embedder_poll_event(SzInputEvent *out) {
if (g_ready && !g_user_quit) {
__block int quit = 0;
on_main(^{
@autoreleasepool {
quit = cocoa_drain_events();
}
});
if (quit)
mark_user_quit();
}
if (!out || g_q_head == g_q_tail)
return 0;
*out = g_queue[g_q_head];
Expand All @@ -278,12 +358,13 @@ static void enqueue_pointer(SzPointerPhase phase, float x, float y, int button)
q_push(&ev);
}

static void enqueue_scroll(float x, float y, float dy) {
static void enqueue_scroll(float x, float y, float dx, float dy) {
SzInputEvent ev;
memset(&ev, 0, sizeof(ev));
ev.kind = SZ_INPUT_SCROLL;
ev.x = x;
ev.y = y;
ev.dx = dx;
ev.dy = dy;
q_push(&ev);
}
Expand Down Expand Up @@ -481,64 +562,7 @@ int sz_embedder_present(const char *title, int point_w, int point_h,
[g_view setImage:image];
[g_view setNeedsDisplay:YES];
[g_win displayIfNeeded];

/* Drain pending events: quit handled here; input only enqueued. */
for (;;) {
NSEvent *ev = [NSApp nextEventMatchingMask:NSEventMaskAny
untilDate:[NSDate distantPast]
inMode:NSDefaultRunLoopMode
dequeue:YES];
if (!ev)
break;

{
NSEventType t = [ev type];
float x, y;
if (t == NSEventTypeLeftMouseDown && event_content_xy(ev, &x, &y)) {
enqueue_pointer(SZ_POINTER_DOWN, x, y, 1);
continue;
}
if (t == NSEventTypeLeftMouseDragged && event_content_xy(ev, &x, &y)) {
enqueue_pointer(SZ_POINTER_MOVE, x, y, 1);
continue;
}
if (t == NSEventTypeLeftMouseUp && event_content_xy(ev, &x, &y)) {
enqueue_pointer(SZ_POINTER_UP, x, y, 1);
continue;
}
if (t == NSEventTypeRightMouseDown && event_content_xy(ev, &x, &y)) {
enqueue_pointer(SZ_POINTER_DOWN, x, y, 3);
continue;
}
if (t == NSEventTypeRightMouseDragged && event_content_xy(ev, &x, &y)) {
enqueue_pointer(SZ_POINTER_MOVE, x, y, 3);
continue;
}
if (t == NSEventTypeRightMouseUp && event_content_xy(ev, &x, &y)) {
enqueue_pointer(SZ_POINTER_UP, x, y, 3);
continue;
}
if (t == NSEventTypeMouseMoved && event_content_xy(ev, &x, &y)) {
enqueue_pointer(SZ_POINTER_MOVE, x, y, 0);
continue;
}
/* scrollingDeltaY: positive = content up (matches SZ_INPUT_SCROLL). */
if (t == NSEventTypeScrollWheel && event_content_xy(ev, &x, &y)) {
enqueue_scroll(x, y, (float)[ev scrollingDeltaY]);
continue;
}
}

if ([ev type] == NSEventTypeKeyDown) {
if (g_content)
[(ScuzzContentView *)g_content interpretKeyEvents:@[ev]];
continue;
}

[NSApp sendEvent:ev];
}

if (!quit && g_win && ![g_win isVisible])
if (cocoa_drain_events())
quit = 1;
ok = 1;
}
Expand Down
46 changes: 32 additions & 14 deletions crates/embedder-desktop/src/x11_present.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ static XIC g_xic;
* (destroy or WM close); the caller must stop using g_dpy/g_win.
* Defined below; the clipboard wait needs it before its definition. */
static int x11_dispatch_event(XEvent *ev);
static void x11_drain_pending(void);

/* X errors are async: the default handler exits the process. A clipboard
* requestor can die between XChangeProperty and delivery; a stale window
Expand Down Expand Up @@ -344,7 +345,19 @@ static int q_push(const SzInputEvent *ev) {
return 1;
}

static void x11_drain_pending(void) {
if (!g_dpy || g_user_quit)
return;
while (XPending(g_dpy)) {
XEvent ev;
XNextEvent(g_dpy, &ev);
if (x11_dispatch_event(&ev))
return;
}
}

int sz_embedder_poll_event(SzInputEvent *out) {
x11_drain_pending();
if (!out || g_q_head == g_q_tail)
return 0;
*out = g_queue[g_q_head];
Expand Down Expand Up @@ -375,12 +388,13 @@ static void enqueue_pointer(SzPointerPhase phase, float x, float y, int button)
q_push(&ev);
}

static void enqueue_scroll(float x, float y, float dy) {
static void enqueue_scroll(float x, float y, float dx, float dy) {
SzInputEvent ev;
memset(&ev, 0, sizeof(ev));
ev.kind = SZ_INPUT_SCROLL;
ev.x = x;
ev.y = y;
ev.dx = dx;
ev.dy = dy;
q_push(&ev);
}
Expand Down Expand Up @@ -929,11 +943,22 @@ static int x11_dispatch_event(XEvent *ev) {
else if (ev->type == ButtonRelease && ev->xbutton.button == 3)
enqueue_pointer(SZ_POINTER_UP, (float)ev->xbutton.x, (float)ev->xbutton.y,
3);
/* Wheel: 4 = up, 5 = down. Positive dy = content up (matches SZ_INPUT_SCROLL). */
else if (ev->type == ButtonPress && ev->xbutton.button == 4)
enqueue_scroll((float)ev->xbutton.x, (float)ev->xbutton.y, 40.f);
else if (ev->type == ButtonPress && ev->xbutton.button == 5)
enqueue_scroll((float)ev->xbutton.x, (float)ev->xbutton.y, -40.f);
/* Wheel: 4 = up, 5 = down. Shift or buttons 6/7 pan x. Positive dy = content
* up. Positive dx = content left (matches SZ_INPUT_SCROLL). */
else if (ev->type == ButtonPress && ev->xbutton.button == 4) {
if (ev->xbutton.state & ShiftMask)
enqueue_scroll((float)ev->xbutton.x, (float)ev->xbutton.y, 40.f, 0.f);
else
enqueue_scroll((float)ev->xbutton.x, (float)ev->xbutton.y, 0.f, 40.f);
} else if (ev->type == ButtonPress && ev->xbutton.button == 5) {
if (ev->xbutton.state & ShiftMask)
enqueue_scroll((float)ev->xbutton.x, (float)ev->xbutton.y, -40.f, 0.f);
else
enqueue_scroll((float)ev->xbutton.x, (float)ev->xbutton.y, 0.f, -40.f);
} else if (ev->type == ButtonPress && ev->xbutton.button == 6)
enqueue_scroll((float)ev->xbutton.x, (float)ev->xbutton.y, -40.f, 0.f);
else if (ev->type == ButtonPress && ev->xbutton.button == 7)
enqueue_scroll((float)ev->xbutton.x, (float)ev->xbutton.y, 40.f, 0.f);
else if (ev->type == KeyRelease)
x11_handle_key_release(&ev->xkey);
else if (ev->type == KeyPress)
Expand Down Expand Up @@ -991,14 +1016,7 @@ int sz_embedder_present(const char *title, int point_w, int point_h,
XPutImage(g_dpy, g_win, g_gc, g_img, 0, 0, 0, 0, (unsigned)width,
(unsigned)height);
XFlush(g_dpy);

/* Drain pending events: quit/close shutdown; input only enqueued. */
while (XPending(g_dpy)) {
XEvent ev;
XNextEvent(g_dpy, &ev);
if (x11_dispatch_event(&ev))
return 1;
}
x11_drain_pending();
return 1;
}

Expand Down
5 changes: 5 additions & 0 deletions crates/embedder-desktop/tests/test_headless.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ int main(void) {

check(sz_embedder_available() == 0, "available: no DISPLAY");
check(sz_embedder_alive() == 0, "alive: no DISPLAY");
{
SzInputEvent ev;
memset(&ev, 0, sizeof ev);
check(sz_embedder_poll_event(&ev) == 0, "poll without DISPLAY is empty");
}

/* Session clipboard works before any window exists. */
check(sz_embedder_clipboard_set("scuzz clip") == 1, "clipboard_set");
Expand Down
Loading
Loading