diff options
Diffstat (limited to 'src/ninep')
| -rw-r--r-- | src/ninep/ctl.zig | 55 |
1 files changed, 52 insertions, 3 deletions
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 02201de0..665913fc 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -521,15 +521,20 @@ pub fn writeRoot(p: *Pardes, req: Req) Reply { if (!p.unattended) return refuse(p, req, "size: a frontend is attached and owns the size", line); if (apply) { const was: [2]u16 = .{ p.screen_w, p.screen_h }; + const before = p.rects; p.update(.{ .resize = .{ .cols = size[0], .rows = size[1] } }); p.sync(); // Every pane keeps its tag and two rows, as placement - // leaves them: a size too small for that is refused. - if (!pardes.layout.everyPaneRoomy(p)) { + // leaves them (layout.minRows, the one minimum): a size + // too small for the panes' minima is refused, and one + // they fit gives back rows the resize took under them. + if (!pardes.layout.minimaFit(p)) { p.update(.{ .resize = .{ .cols = was[0], .rows = was[1] } }); p.sync(); return refuse(p, req, "size: too small for the panes, each its tag and 2 rows", line); } + pardes.layout.restoreMinima(p, &before); + p.sync(); } continue; } @@ -548,7 +553,8 @@ pub fn writeRoot(p: *Pardes, req: Req) Reply { const size_min = [2]u16{ 20, 6 }; const size_max = [2]u16{ 4096, 4096 }; -const e_size = std.fmt.comptimePrint("size takes <cols> <rows>, at least {d}x{d}, at most {d}x{d}", .{ size_min[0], size_min[1], size_max[0], size_max[1] }); +// "invalid": the word 9ns reads as EINVAL (enameToErrno), as winsize's. +const e_size = std.fmt.comptimePrint("invalid size: takes <cols> <rows>, at least {d}x{d}, at most {d}x{d}", .{ size_min[0], size_min[1], size_max[0], size_max[1] }); /// `size <cols> <rows>`, within size_min and size_max. fn sizeOf(line: []const u8) ?[2]u16 { @@ -1703,6 +1709,49 @@ test "size sets the screen of a session no frontend is attached to, and only of try testing.expectEqual(@as(u16, 40), p.screen_h); } +test "size is monotonic: growing is never refused, and a size once taken is taken again" { + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 160, .rows = 60 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + p.unattended = true; + const root_ctl = @intFromEnum(tree.TopFile.ctl); + const new = @intFromEnum(tree.TopFile.new); + // The dogfood's repro: Newcol, three pane/new, a Tty, then pane/new + // until a column has no room (ENOSPC). + try testing.expectEqual(Status.ok, wr(p, root_ctl, "Newcol\n").reply.status); + for (0..3) |_| try testing.expectEqual(Status.ok, call(p, .{ .tag = 1, .op = .open, .node = new }).reply.status); + try testing.expectEqual(Status.ok, wr(p, Node.of(p.panes[p.active].?.serial, .ctl), "Tty\n").reply.status); + p.sync(); + while (call(p, .{ .tag = 1, .op = .open, .node = new }).reply.status == .ok) p.sync(); + p.sync(); + var line: [32]u8 = undefined; + // Down a row at a time (59, 58, 57, 56 and on) until one is refused: + // once refused, every smaller one is too. + var taken: [60]bool = @splat(false); + var rows: u16 = 59; + var refused = false; + while (rows >= 20) : (rows -= 1) { + taken[rows] = wr(p, root_ctl, try std.fmt.bufPrint(&line, "size 160 {d}\n", .{rows})).reply.status == .ok; + if (refused) try testing.expect(!taken[rows]); + if (!taken[rows]) refused = true; + } + try testing.expect(refused); // the columns are full: some size is too small + // Back up: each size is taken again exactly when it was on the way down. + rows = 20; + while (rows <= 59) : (rows += 1) { + const ok = wr(p, root_ctl, try std.fmt.bufPrint(&line, "size 160 {d}\n", .{rows})).reply.status == .ok; + try testing.expectEqual(taken[rows], ok); + } + for ([_]u16{ 60, 61, 64, 70, 100 }) |grown| { + try testing.expectEqual(Status.ok, wr(p, root_ctl, try std.fmt.bufPrint(&line, "size 160 {d}\n", .{grown})).reply.status); + try testing.expectEqual(grown, p.screen_h); + } + // Out of range is EINVAL, in words 9ns reads as that. + const bad = wr(p, root_ctl, "size 5 2\n"); + try testing.expectEqual(E.INVAL, bad.errno()); + try testing.expectStringStartsWith(bad.reply.ename, "invalid size"); +} + test "Repl with no such language names a few whole, and where the rest are" { const p = try th.withTerm(testing.allocator); defer p.deinit(); |
