summaryrefslogtreecommitdiff
path: root/src/ninep/ctl.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-28 13:00:06 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:14 -0300
commit047a2b50a7ac0162d7ef38f912d41c15a47479c7 (patch)
treea2fb902819402ac11093a23cf5dc72f2bae68f0f /src/ninep/ctl.zig
parent6fc9f416938b59b665b1fddc052402770a21cc3f (diff)
downloadpardes-047a2b50a7ac0162d7ef38f912d41c15a47479c7.tar.gz
pardes-047a2b50a7ac0162d7ef38f912d41c15a47479c7.zip
A line written to a pane's own look or exec goes to the event reader holding the pane
A script writing to pane/N/exec bypassed the client holding that pane's event file, which a middle click on it would not: acme sends a window's clicks to its event reader (look.c:53, exec.c:173). Such a line is now an F record at 0 0 carrying its text; the root's look and exec still act at once. acme takes back only origin, action and range, which a record with no place in the text cannot use, so the whole record as read is taken too, and for an empty range acts on its text. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep/ctl.zig')
-rw-r--r--src/ninep/ctl.zig40
1 files changed, 38 insertions, 2 deletions
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index c1f78bab..70e06d4b 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -51,8 +51,20 @@ fn noteResult(p: *Pardes, serial: u32) void {
/// Runs one look or exec from the pane `id`, then records the panes it
/// created; failing that, a look records the pane it focused and an exec
/// the pane it acted on (even one it closed).
-fn run(p: *Pardes, id: usize, exec: bool, text: []const u8) void {
+fn run(p: *Pardes, id: usize, exec: bool, text: []const u8, own: bool) void {
const context = p.panes[id].?.serial;
+ // A pane an event reader holds hears its clicks instead of acting on
+ // them (editors/acme/look.c:53 and exec.c:173 send them to winevent),
+ // and a line written to its own look or exec is a click on it; the
+ // reader writes the record back to have it done.
+ if (own and p.panes[id].?.fs.readers > 0) {
+ const named = std.meta.stringToEnum(Builtin, exec_line.commandText(text)) != null;
+ var flag: u32 = pardes.ctlfs.events.flag_expansion;
+ if (named) flag |= pardes.ctlfs.events.flag_builtin;
+ if (!exec and !named and std.mem.indexOfAny(u8, text, "/.:") != null) flag |= pardes.ctlfs.events.flag_filename;
+ _ = pardes.ctlfs.events.noteAction(p, id, if (exec) .body_exec else .body_look, 0, 0, flag, text);
+ return noteResult(p, context);
+ }
const before = Serials.snapshot(p);
if (exec) _ = pardes.exec.execute(p, id, text) else look.lookAt(p, id, text);
const after = Serials.snapshot(p);
@@ -82,7 +94,7 @@ pub fn command(p: *Pardes, req: Req, serial: ?u32, exec: bool) Reply {
if (!apply) continue;
const id = if (serial) |s| p.paneBySerial(s) orelse break else p.active;
if (p.panes[id] == null) return Reply.fail(req.tag, E.NOENT);
- run(p, id, exec, text);
+ run(p, id, exec, text, serial != null);
}
}
return .{ .tag = req.tag, .written = @intCast(req.data.len) };
@@ -858,3 +870,27 @@ test "exec Save writes the named file through the host" {
try testing.expect(!pane_files.dirtyOf(pane));
try testing.expectEqualStrings(pane.file.?.content, p.fallback.get("/tmp/renamed.txt").?);
}
+
+test "a pane's own look and exec go to the event reader holding it, which writes them back to act" {
+ const gpa = testing.allocator;
+ const p = try withFile(gpa, "Msg routed\n");
+ defer p.deinit();
+ const serial = serialOf(p);
+ const event = Node.of(serial, .event);
+ const h = call(p, .{ .tag = 1, .op = .open, .node = event }).reply.handle;
+ try testing.expectEqual(tree.Status.ok, wr(p, Node.of(serial, .exec), "Msg routed\n").reply.status);
+ // Not done: the reader has it, as it would a middle click.
+ const pane = p.panes[0].?;
+ try testing.expect(std.mem.indexOf(u8, pane.msg[0..pane.msg_len], "routed") == null);
+ const record = call(p, .{ .tag = 2, .op = .read, .node = event, .handle = h, .size = 256 }).bytes;
+ try testing.expectEqualStrings("FX0 0 2 10 Msg routed\n", record);
+ // Written back, it is done.
+ try testing.expectEqual(tree.Status.ok, wr(p, event, record).reply.status);
+ try testing.expect(std.mem.indexOf(u8, pane.msg[0..pane.msg_len], "routed") != null);
+ try testing.expectEqual(tree.Status.ok, wr(p, Node.of(serial, .look), "one.txt:3\n").reply.status);
+ try testing.expectEqualStrings("FL0 0 6 9 one.txt:3\n", call(p, .{ .tag = 3, .op = .read, .node = event, .handle = h, .size = 256 }).bytes);
+ _ = call(p, .{ .tag = 4, .op = .release, .node = event, .handle = h });
+ // With no reader, the root's and the pane's act at once.
+ try testing.expectEqual(tree.Status.ok, wr(p, Node.of(serial, .exec), "Msg direct\n").reply.status);
+ try testing.expect(std.mem.indexOf(u8, pane.msg[0..pane.msg_len], "direct") != null);
+}