diff options
| -rw-r--r-- | src/detached/server.zig | 10 | ||||
| -rw-r--r-- | src/host_io.zig | 4 | ||||
| -rw-r--r-- | src/pardes.zig | 30 |
3 files changed, 44 insertions, 0 deletions
diff --git a/src/detached/server.zig b/src/detached/server.zig index d8379793..001acc47 100644 --- a/src/detached/server.zig +++ b/src/detached/server.zig @@ -376,6 +376,7 @@ pub const Session = struct { .now = clockNow, .present = present, .poll_frame = pollFrame, + .watched = watched, .spawn = spawn, .pty_write = ptyWrite, .pty_resize = ptyResize, @@ -641,6 +642,12 @@ pub const Session = struct { s.mailbox.signal(); } + fn watched(ctx: ?*anyopaque) bool { + const s = of(ctx); + for (&s.clients) |*c| if (c.attached) return true; + return false; + } + fn pollFrame(ctx: ?*anyopaque) void { const s = of(ctx); if (s.ninep) |l| s.ninep_pending = l.tick().pending; @@ -1052,6 +1059,9 @@ pub const Session = struct { c.attached = true; c.need_full = true; c.greet = true; + // Unwatched until now, it may have drawn nothing: a frame + // for the one attaching (watched). + s.core.needs_frame = true; }, .bye => s.close(c, .bye), .event => |ev| { diff --git a/src/host_io.zig b/src/host_io.zig index ac25e63c..c8c537b9 100644 --- a/src/host_io.zig +++ b/src/host_io.zig @@ -19,6 +19,10 @@ pub const Host = struct { present: ?*const fn (ctx: ?*anyopaque, surface: *const pardes.Surface) void = null, post_present: ?*const fn (ctx: ?*anyopaque) void = null, poll_frame: ?*const fn (ctx: ?*anyopaque) void = null, + /// Whether anyone sees a frame drawn now. A detached session with no + /// client attached draws none (a /screen read draws its own), and + /// the frame owed is drawn when one attaches. Absent: always. + watched: ?*const fn (ctx: ?*anyopaque) bool = null, detach: ?*const fn (ctx: ?*anyopaque) void = null, spawn: ?*const fn (ctx: ?*anyopaque, pane: u8, cwd: []const u8) void = null, pty_write: ?*const fn (ctx: ?*anyopaque, pane: u8, bytes: []const u8) void = null, diff --git a/src/pardes.zig b/src/pardes.zig index 841faf7a..b16d39b7 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -1112,6 +1112,32 @@ test "first document fallback splits like Tty from the originating pane" { }; } +test "a frame no one watches is not drawn, and is drawn once someone does" { + const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const Watch = struct { + var presents: usize = 0; + var seen = false; + const vtable: Host.VTable = .{ .present = present, .watched = watched }; + fn present(_: ?*anyopaque, _: *const Surface) void { + presents += 1; + } + fn watched(_: ?*anyopaque) bool { + return seen; + } + }; + Watch.presents = 0; + Watch.seen = false; + p.needs_frame = true; + try p.pump(.{ .vtable = &Watch.vtable }); + try std.testing.expectEqual(@as(usize, 0), Watch.presents); + try std.testing.expect(p.needs_frame); // still owed + Watch.seen = true; + try p.pump(.{ .vtable = &Watch.vtable }); + try std.testing.expectEqual(@as(usize, 1), Watch.presents); +} + test "Lspwhy after a Hover asks to explain that hover, in its file at its place, run from any pane" { if (!lsp.supports.contains(.hover)) return error.SkipZigTest; const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true }); @@ -5625,6 +5651,10 @@ pub const Pardes = struct { // A shell with a clock draws what `advance` stepped; one without // steps its own `.tick`s and draws while anything animates. if (!p.needs_frame and !p.continuous() and (v.now != null or !p.animationActive())) return; + // No one to see it: the frame stays owed (needs_frame), not drawn. + // A pty streaming into an unwatched session spent most of its time + // drawing what nobody looked at. + if (v.watched) |f| if (!f(h.ctx)) return; _ = p.frame_arena.reset(.retain_capacity); const surface = try p.render(p.frame_arena.allocator()); if (v.present) |f| f(h.ctx, surface); |
