1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//! The constructor must honour the row floor `resize` already enforces.
//!
//! This is **not** the mirror of `min_columns.rs`, and the difference is the whole point.
//! `MIN_COLUMNS = 2` was a *contract change* (#547): one column used to be a supported size and
//! is not any more, so `resize(1, r)` silently returns two and every consumer had to be told.
//! The row floor changes no contract — `Term::resize` has always done `rows.max(1)`, and the
//! comment beside it states the rule outright: *"A terminal is never 0-tall"*. The constructor
//! simply never enforced it, while carrying the identical `scroll_bottom: rows - 1` expression,
//! so `Engine::new(cols, 0)` panicked with a subtract overflow before reaching any of it.
//!
//! No `MIN_ROWS` constant is published, deliberately. `MIN_COLUMNS` earned publication because
//! it is surprising (ask for one, get two) and its reason is non-obvious (a width-2 glyph needs
//! a lead *and* a spacer); a floor of one row is neither. alacritty publishes both
//! (`MIN_COLUMNS`, `MIN_SCREEN_LINES`) because its *app* reads them across a crate boundary —
//! justerm clamps internally, so nothing outside needs the value.
//!
//! **One leg of that has since gone stale, and the conclusion survives on the rest (#663).** The
//! row floor is no longer only clamped internally: `decode` now *rejects* `rows == 0`, so half of
//! a published rejection set is stated by a named constant (`MIN_COLUMNS`) and half by a bare
//! literal. That is real asymmetry and it is chosen, not overlooked — publication is earned here
//! by a consumer reading the value across a boundary, and no consumer reads either floor to
//! *build* a frame (the only producer is `encode`, in this crate). What a consumer needs is the
//! error, and it gets one name for both halves. Revisit the day something outside this crate
//! constructs a `Frame` header, which is the condition that would make the literal a trap.
//!
//! xterm.js's `MINIMUM_ROWS = 1`
//! is likewise internal to `common/services/BufferService.ts`, applied in its constructor
//! (`:42`) exactly as here, and ghostty rejects a zero dimension outright
//! (`Terminal.zig:3721`, `error.InvalidValue`). All three floor or reject; none of them panics.
use Engine;
/// A zero-row screen is clamped, not fatal. The panic this replaces was a subtract overflow in
/// `Term::with_scrollback`, so it fired during construction — before a caller could observe
/// anything about the engine it asked for.
/// Both dimensions degenerate at once — the case that reaches the column clamp and the row
/// clamp in the same call.
/// The constructor now agrees with `resize`, which is the whole claim: the same screen shape
/// results whether a zero row count arrives at construction or at resize.