From f5eaddb2cf28a9b72d5dd5b3b10502a316d4de85 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Mon, 28 Sep 2026 09:41:55 -0300 Subject: Split control messages by scope: the root ctl takes the session's builtins and reads the settings, a pane's ctl its own Every builtin could only be clicked, or written to exec, and the settings could be read only as the Config window's prose. acme keeps window verbs on a window's ctl, and webfs and upas/fs keep session settings on a root ctl. Each builtin now declares its scope (scope = .session; settings are all session, the rest pane), read by the registry. The root /ctl takes session builtins and reads every setting in the words a write takes, so its read written back changes nothing (panel and scene effects now take on/off like the toggles, to make that true); a pane's ctl takes the pane's builtins beside get, lock and unlock. Writes are checked whole and refused in Plan 9's ctl words (unknown control message "X", wrong #args ...), which 9ns now maps to EINVAL (cloud9 re-pinned at a8c7a715). A builtin that would prompt for its argument fails the write instead, and a refusal is answered at once, not after the frame. Co-Authored-By: Claude Opus 5.5 --- src/9p_io.zig | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/9p_io.zig') diff --git a/src/9p_io.zig b/src/9p_io.zig index 7b0976bc..02b9efb5 100644 --- a/src/9p_io.zig +++ b/src/9p_io.zig @@ -258,6 +258,10 @@ pub const Listener = struct { // file written. A change carries no payload, so the reply is still // whole after the wait. l.kick(); + // A refusal answers at once: its text may be in the core's one + // buffer for it, which a request run while this one waited would + // write over. + if (reply.status == .err) return conn.reply(&reply, ""); const restoring = core.restore_req != null; if (core.effects_len != 0) pardes.turn.awaitSettled(epoch); // Once a wait returns `core` may be gone: a Restore meanwhile put a -- cgit v1.3