summaryrefslogtreecommitdiff
path: root/src/ninep
diff options
context:
space:
mode:
Diffstat (limited to 'src/ninep')
-rw-r--r--src/ninep/ctl.zig55
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();