summaryrefslogtreecommitdiff
path: root/src/ninep/events.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/events.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/events.zig')
-rw-r--r--src/ninep/events.zig21
1 files changed, 17 insertions, 4 deletions
diff --git a/src/ninep/events.zig b/src/ninep/events.zig
index 1cab5605..3f36c65f 100644
--- a/src/ninep/events.zig
+++ b/src/ninep/events.zig
@@ -468,7 +468,7 @@ pub fn notePtyOutput(p: *Pardes, id: usize, bytes: []const u8) void {
p.fs.news = true;
}
-const EventRecord = struct { action: Action, q0: u32, q1: u32 };
+const EventRecord = struct { action: Action, q0: u32, q1: u32, text: ?[]const u8 = null };
const EventReader = struct {
data: []const u8,
@@ -484,9 +484,20 @@ const EventReader = struct {
const q0 = scanNumber(er.data, &i) orelse return null;
const q1 = scanNumber(er.data, &i) orelse return null;
while (i < er.data.len and er.data[i] == ' ') i += 1;
+ // acme takes only `origin action q0 q1`; the record whole, as read,
+ // is taken too, so a click with no place in the text (one written
+ // to a pane's own look or exec) can be written back and done.
+ var text: ?[]const u8 = null;
+ if (i < er.data.len and er.data[i] != '\n') {
+ _ = scanNumber(er.data, &i) orelse return null;
+ const n = scanNumber(er.data, &i) orelse return null;
+ if (i >= er.data.len or er.data[i] != ' ' or er.data.len - (i + 1) < n) return null;
+ text = er.data[i + 1 ..][0..n];
+ i += 1 + n;
+ }
if (i >= er.data.len or er.data[i] != '\n') return null;
er.i = i + 1;
- return .{ .action = action, .q0 = q0, .q1 = q1 };
+ return .{ .action = action, .q0 = q0, .q1 = q1, .text = text };
}
};
@@ -525,7 +536,8 @@ pub fn writeEvent(p: *Pardes, req: Req, id: usize) Reply {
const whole = if (r.action.onTag()) pane_files.tagOf(p, pane) else pane_files.bodyOf(pane);
const lo = @min(@as(usize, r.q0), whole.len);
const hi = @max(lo, @min(@as(usize, r.q1), whole.len));
- const text = p.scratch.allocator().dupe(u8, whole[lo..hi]) catch continue;
+ const said = if (r.q0 == r.q1) r.text orelse whole[lo..hi] else whole[lo..hi];
+ const text = p.scratch.allocator().dupe(u8, said) catch continue;
switch (r.action) {
.body_exec, .tag_exec => _ = exec.execute(p, live, text),
.body_look, .tag_look => look.lookAt(p, live, text),
@@ -672,7 +684,8 @@ test "a pane deleted while its event file is open leaves no suppression behind"
const b = call(p, .{ .tag = 19, .op = .open, .node = event, .omode = 1 });
try testing.expectEqual(@as(u16, 2), p.fs.listeners);
- _ = wr(p, Node.of(serial, .exec), "Del\n");
+ // Through ctl: a line written to its exec would go to the reader.
+ _ = wr(p, Node.of(serial, .ctl), "Del\n");
try testing.expect(p.paneBySerial(serial) == null);
try testing.expectEqual(@as(u16, 0), p.fs.listeners);
// The reader's next read hears what acme says of a window gone under it.