summaryrefslogtreecommitdiff
path: root/src/ninep/screen.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/ninep/screen.zig')
-rw-r--r--src/ninep/screen.zig79
1 files changed, 19 insertions, 60 deletions
diff --git a/src/ninep/screen.zig b/src/ninep/screen.zig
index 89ca369b..f72043d4 100644
--- a/src/ninep/screen.zig
+++ b/src/ninep/screen.zig
@@ -1,5 +1,5 @@
-//! /screen, /listeners and the snapshot handles that freeze a screen or a
-//! terminal history between open and close.
+//! /screen, /listeners and the snapshots that freeze a screen or a terminal
+//! history between open and close (tree.zig's open records hold them).
const std = @import("std");
const pardes = @import("../pardes.zig");
const panes = @import("../panes.zig");
@@ -11,21 +11,6 @@ const Req = tree.Req;
const Reply = tree.Reply;
const E = tree.E;
-pub const Snapshot = struct {
- node: u64 = 0,
- bytes: ?[]const u8 = null,
- /// /log only: this open waits for records after `bytes`, from `next`.
- follow: bool = false,
- next: u64 = 0,
- /// How much of record `next` a short read already took.
- part: u32 = 0,
- /// /log only: how much of `bytes` this open has read. Reads walk it as a
- /// stream and ignore their offset, since `exec 3<>log; echo follow >&3`
- /// leaves the shared offset past the word it wrote.
- pos: u64 = 0,
-};
-pub const snapshot_slots = 32;
-
pub fn render(p: *Pardes) ![]u8 {
var arena: std.heap.ArenaAllocator = .init(p.gpa);
defer arena.deinit();
@@ -58,26 +43,13 @@ pub fn render(p: *Pardes) ![]u8 {
return out.toOwnedSlice();
}
-/// A screen freezes at open; a terminal body freezes at its first read.
-pub fn openSnapshot(p: *Pardes, req: Req, of_screen: bool) Reply {
- for (&p.fs.snapshots, 0..) |*slot, i| {
- if (slot.node != 0) continue;
- const bytes = if (of_screen) render(p) catch return Reply.fail(req.tag, E.NOMEM) else null;
- slot.* = .{ .node = req.node, .bytes = bytes };
- return .{ .tag = req.tag, .handle = @intCast(i + 1) };
- }
- return Reply.fail(req.tag, E.NFILE);
-}
-
pub fn readSnapshot(p: *Pardes, req: Req, pane: ?*Pane) Reply {
- if (req.handle == 0 or req.handle > p.fs.snapshots.len) return Reply.fail(req.tag, E.INVAL);
- const snapshot = &p.fs.snapshots[req.handle - 1];
- if (snapshot.node == 0 or snapshot.node != req.node) return Reply.fail(req.tag, E.INVAL);
- if (snapshot.bytes == null) {
+ const snapshot = &(tree.openOf(p, req) orelse return Reply.fail(req.tag, E.INVAL)).what.snapshot;
+ if (snapshot.* == null) {
const terminal = pane orelse return Reply.fail(req.tag, E.INVAL);
- snapshot.bytes = panes.Terminal.screenTextAlloc(terminal, p.gpa) catch return Reply.fail(req.tag, E.NOMEM);
+ snapshot.* = panes.Terminal.screenTextAlloc(terminal, p.gpa) catch return Reply.fail(req.tag, E.NOMEM);
}
- const bytes = snapshot.bytes.?;
+ const bytes = snapshot.*.?;
const off = @min(req.off, bytes.len);
const len = @min(bytes.len - off, req.size);
const out = p.fs.stage(p.gpa);
@@ -85,16 +57,6 @@ pub fn readSnapshot(p: *Pardes, req: Req, pane: ?*Pane) Reply {
return .{ .tag = req.tag, .payload = .{ .staged = @intCast(len) } };
}
-/// True when the handle named a snapshot, which is now released.
-pub fn releaseSnapshot(p: *Pardes, req: Req) bool {
- if (req.handle == 0 or req.handle > p.fs.snapshots.len) return false;
- const snapshot = &p.fs.snapshots[req.handle - 1];
- if (snapshot.node != req.node) return false;
- if (snapshot.bytes) |bytes| p.gpa.free(bytes);
- snapshot.* = .{};
- return true;
-}
-
const listeners_cap = 512;
fn listenersText(p: *Pardes, buf: []u8) []const u8 {
@@ -199,7 +161,7 @@ test "an empty terminal body snapshot stays empty while output continues" {
const node = Node.of(serialOf(p), .body);
const opened = call(p, .{ .tag = 1, .op = .open, .node = node });
try testing.expectEqualStrings("", call(p, .{ .tag = 2, .op = .read, .node = node, .handle = opened.reply.handle, .size = 32 }).bytes);
- try testing.expect(p.fs.snapshots[opened.reply.handle - 1].bytes != null);
+ try testing.expect(p.fs.opens[opened.reply.handle - 1].what.snapshot != null);
p.update(.{ .output = .{ .pane = 0, .bytes = "new output" } });
while (p.nextEffect()) |_| {}
try testing.expectEqualStrings("", call(p, .{ .tag = 3, .op = .read, .node = node, .handle = opened.reply.handle, .size = 32 }).bytes);
@@ -214,15 +176,15 @@ test "terminal body snapshots are lazy bounded and released after the pane close
const p = try withTerm(gpa);
defer p.deinit();
const node = Node.of(serialOf(p), .body);
- var handles: [snapshot_slots]u32 = undefined;
+ var handles: [tree.open_slots]u32 = undefined;
for (&handles) |*opened_handle| {
const opened = call(p, .{ .tag = 1, .op = .open, .node = node });
try testing.expectEqual(Status.ok, opened.reply.status);
opened_handle.* = opened.reply.handle;
}
- for (p.fs.snapshots) |snapshot| {
- try testing.expectEqual(node, snapshot.node);
- try testing.expect(snapshot.bytes == null);
+ for (p.fs.opens) |o| {
+ try testing.expectEqual(node, o.node);
+ try testing.expect(o.what.snapshot == null);
}
try testing.expectEqual(E.NFILE, call(p, .{ .tag = 2, .op = .open, .node = node }).errno());
const scratch = try th.newPane(p);
@@ -235,26 +197,23 @@ test "terminal body snapshots are lazy bounded and released after the pane close
const failed = call(p, .{ .tag = 3, .op = .read, .node = node, .handle = handles[0], .size = 32 });
p.gpa = gpa;
try testing.expectEqual(E.NOMEM, failed.errno());
- try testing.expect(p.fs.snapshots[handles[0] - 1].bytes == null);
+ try testing.expect(p.fs.opens[handles[0] - 1].what.snapshot == null);
const first = call(p, .{ .tag = 4, .op = .read, .node = node, .handle = handles[0], .size = 32 });
try testing.expectEqualStrings("latest output", first.bytes);
- const saved = p.fs.snapshots[handles[0] - 1].bytes.?;
+ const saved = p.fs.opens[handles[0] - 1].what.snapshot.?;
failing = std.testing.FailingAllocator.init(gpa, .{ .fail_index = 0 });
p.gpa = failing.allocator();
const second = call(p, .{ .tag = 5, .op = .read, .node = node, .handle = handles[0], .off = 7, .size = 32 });
p.gpa = gpa;
try testing.expectEqualStrings("output", second.bytes);
try testing.expect(!failing.has_induced_failure);
- try testing.expectEqual(saved.ptr, p.fs.snapshots[handles[0] - 1].bytes.?.ptr);
+ try testing.expectEqual(saved.ptr, p.fs.opens[handles[0] - 1].what.snapshot.?.ptr);
try testing.expectEqual(E.INVAL, call(p, .{ .tag = 6, .op = .read, .node = screen_node, .handle = handles[0], .size = 32 }).errno());
_ = call(p, .{ .tag = 7, .op = .release, .node = screen_node, .handle = handles[0] });
- try testing.expectEqual(node, p.fs.snapshots[handles[0] - 1].node);
+ try testing.expectEqual(node, p.fs.opens[handles[0] - 1].node);
try p.removePane(0, null);
for (handles) |opened_handle| _ = call(p, .{ .tag = 8, .op = .release, .node = node, .handle = opened_handle });
- for (p.fs.snapshots) |snapshot| {
- try testing.expectEqual(@as(u64, 0), snapshot.node);
- try testing.expect(snapshot.bytes == null);
- }
+ for (p.fs.opens) |o| try testing.expectEqual(@as(u64, 0), o.node);
}
test "screen snapshots preserve rendered cells and styles until their handle is released" {
@@ -263,7 +222,7 @@ test "screen snapshots preserve rendered cells and styles until their handle is
defer p.deinit();
const opened = call(p, .{ .tag = 1, .op = .open, .node = screen_node });
try testing.expectEqual(Status.ok, opened.reply.status);
- const snapshot = p.fs.snapshots[opened.reply.handle - 1].bytes.?;
+ const snapshot = p.fs.opens[opened.reply.handle - 1].what.snapshot.?;
const original = try gpa.dupe(u8, snapshot);
defer gpa.free(original);
const parsed = try std.json.parseFromSlice(std.json.Value, gpa, original, .{});
@@ -276,7 +235,7 @@ test "screen snapshots preserve rendered cells and styles until their handle is
_ = wr(p, Node.of(serialOf(p), .body), "changed\n");
const newer = call(p, .{ .tag = 2, .op = .open, .node = screen_node });
try testing.expectEqual(Status.ok, newer.reply.status);
- try testing.expect(!std.mem.eql(u8, original, p.fs.snapshots[newer.reply.handle - 1].bytes.?));
+ try testing.expect(!std.mem.eql(u8, original, p.fs.opens[newer.reply.handle - 1].what.snapshot.?));
var off: usize = 0;
while (off < original.len) {
const result = call(p, .{ .tag = 3, .op = .read, .node = screen_node, .handle = opened.reply.handle, .off = off, .size = 13 });
@@ -286,7 +245,7 @@ test "screen snapshots preserve rendered cells and styles until their handle is
}
_ = call(p, .{ .tag = 4, .op = .release, .node = screen_node, .handle = opened.reply.handle });
_ = call(p, .{ .tag = 5, .op = .release, .node = screen_node, .handle = newer.reply.handle });
- for (p.fs.snapshots) |slot| try testing.expect(slot.node == 0);
+ for (p.fs.opens) |o| try testing.expect(o.node == 0);
}
test "screen inspection preserves acknowledged presentation and the next real frame" {