diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/layout.zig | 14 | ||||
| -rw-r--r-- | src/ninep/ctl.zig | 32 | ||||
| -rw-r--r-- | src/pardes.zig | 5 |
3 files changed, 45 insertions, 6 deletions
diff --git a/src/layout.zig b/src/layout.zig index 4c974fb7..bdef9675 100644 --- a/src/layout.zig +++ b/src/layout.zig @@ -433,6 +433,20 @@ pub fn placedRoomy(p: *Pardes, id: usize, before: *const [MAX_PANES]Rect) bool { return true; } +/// Whether every unfolded pane sharing its column has its tag and +/// `min_body_rows` (one alone in its column always fits). +pub fn everyPaneRoomy(p: *Pardes) bool { + compute(p); + for (0..p.ncol) |c| { + if (p.col_n[c] < 2) continue; + for (p.col_panes[c][0..p.col_n[c]]) |k| { + const pane = p.panes[k] orelse continue; + if (!pane.collapsed and p.rects[k].h < minRows(p, k)) return false; + } + } + return true; +} + /// Places `id` in column `c` by halving its tallest unfolded pane, when /// that leaves both their rows; false, and `id` in no column, when not. pub fn splitTallest(p: *Pardes, c: usize, id: usize) bool { diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index dd2d9ed0..dd382290 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -473,11 +473,22 @@ pub fn writeRoot(p: *Pardes, req: Req) Reply { while (it.next()) |line| { if (line.len == 0) continue; if (std.mem.startsWith(u8, line, "size ") or std.mem.eql(u8, line, "size")) { - const size = sizeOf(line) orelse return refuse(p, req, "size takes <cols> <rows>", line); + const size = sizeOf(line) orelse return refuse(p, req, e_size, line); // A frontend's window is the size; only a session none is // attached to (--detach, unattended) takes one written. if (!p.unattended) return refuse(p, req, "size: a frontend is attached and owns the size", line); - if (apply) p.update(.{ .resize = .{ .cols = size[0], .rows = size[1] } }); + if (apply) { + const was: [2]u16 = .{ p.screen_w, p.screen_h }; + 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)) { + 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); + } + } continue; } if (!apply) { @@ -493,12 +504,16 @@ pub fn writeRoot(p: *Pardes, req: Req) Reply { return .{ .tag = req.tag, .written = @intCast(req.data.len) }; } -/// `size <cols> <rows>`, each at least a few cells and at most 4096. +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] }); + +/// `size <cols> <rows>`, within size_min and size_max. fn sizeOf(line: []const u8) ?[2]u16 { var it = std.mem.tokenizeAny(u8, line["size".len..], " \t"); const cols = std.fmt.parseInt(u16, it.next() orelse return null, 10) catch return null; const rows = std.fmt.parseInt(u16, it.next() orelse return null, 10) catch return null; - if (it.next() != null or cols < 20 or rows < 4 or cols > 4096 or rows > 4096) return null; + if (it.next() != null or cols < size_min[0] or rows < size_min[1] or cols > size_max[0] or rows > size_max[1]) return null; return .{ cols, rows }; } @@ -1490,5 +1505,12 @@ test "size sets the screen of a session no frontend is attached to, and only of try testing.expectEqual(@as(u16, 120), p.screen_w); try testing.expectEqual(@as(u16, 40), p.screen_h); try testing.expectEqual(E.INVAL, wr(p, root_ctl, "size 120\n").errno()); - try testing.expectEqual(E.INVAL, wr(p, root_ctl, "size 5 2\n").errno()); + try testing.expectEqual(E.INVAL, wr(p, root_ctl, "size\n").errno()); + try testing.expect(std.mem.indexOf(u8, wr(p, root_ctl, "size 5 2\n").reply.ename, "at least 20x6, at most 4096x4096") != null); + // Too small for the panes it has, each its tag and two rows: refused, + // the size as it was. + for (0..4) |_| p.newScratchBelow(p.active); + p.sync(); + try testing.expect(std.mem.indexOf(u8, wr(p, root_ctl, "size 80 8\n").reply.ename, "too small for the panes") != null); + try testing.expectEqual(@as(u16, 40), p.screen_h); } diff --git a/src/pardes.zig b/src/pardes.zig index 72fd4d46..c103b9d7 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -6527,7 +6527,10 @@ pub const Pardes = struct { } else pane.tag_top = 0; const cols = @max(1, r.w -| config.GUTTER); const rows = @max(1, r.h -| pane.tag_rows); // the tag steals the top rows - if ((cols != pane.cols or rows != pane.rows) and r.h > pane.tag_rows) { + // A terminal's pty follows its pane down to a row, whatever the + // resize; only a folded one keeps the size it had, and one not + // placed yet the grid it was made with. + if ((cols != pane.cols or rows != pane.rows) and (r.h > pane.tag_rows or (pane.isTerminal() and !pane.collapsed and r.h > 0))) { // doc panes have no pty/emulator grid to reflow; just record // the size so bodyText renders the right number of rows if (pane.isTerminal()) { |
