summaryrefslogtreecommitdiff
path: root/src/p4.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/p4.zig')
-rw-r--r--src/p4.zig51
1 files changed, 38 insertions, 13 deletions
diff --git a/src/p4.zig b/src/p4.zig
index cb8a5790..05610eca 100644
--- a/src/p4.zig
+++ b/src/p4.zig
@@ -207,19 +207,39 @@ var in_paste: bool = false;
/// starves input.
var dirty: bool = true;
-/// The largest grid this board can render, and the reason it is not the host's terminal size.
+/// The largest grid this board can render, and the reason it is not just the host's terminal size.
///
-/// Every cell is paid for four times over: vaxis keeps a `Screen` and an `InternalScreen`, pardes
-/// keeps its own `Surface` and `previous_cells`. Against a 384 KiB heap that puts a hard ceiling on
-/// the geometry, and it was measured rather than guessed - 40x12 initialises with room to spare,
-/// 80x24 exhausts the heap and `Pardes.init` returns OutOfMemory with 9,128 bytes left.
+/// A cell is paid for TWICE now, not four times: pardes keeps its `Surface` and this shell keeps a
+/// shadow copy of it to diff against. vaxis used to keep a `Screen` and an `InternalScreen` as well,
+/// and with `direct_emit` neither is ever read - the emitter diffs against the Surface and writes
+/// the wire itself - so `init` sizes vaxis to a single cell and those two grids cost nothing.
///
-/// Raising these is what PSRAM would buy: this board has 32 MB fitted and untrained.
-pub const max_cols: u16 = 40;
-pub const max_rows: u16 = 12;
+/// Set with `-Dp4-cols` / `-Dp4-rows`, because the ceiling is a measurement rather than a constant
+/// and it moves for two independent reasons: the 384 KiB heap, and the round trip, which grows with
+/// the cell count because every frame walks the whole grid. See the geometry table in
+/// `05-zig-p4/experiments/report.typ` for both curves.
+///
+/// Raising these further is what PSRAM would buy: this board has 32 MB fitted and untrained.
+pub const max_cols: u16 = @import("pardes_config").p4_cols;
+pub const max_rows: u16 = @import("pardes_config").p4_rows;
var cur_winsize: vaxis.Winsize = .{ .rows = max_rows, .cols = max_cols, .x_pixel = 0, .y_pixel = 0 };
+/// How big vaxis's own grids need to be.
+///
+/// ONE CELL under `direct_emit`, because neither of them is ever read: vaxis keeps a `Screen` and an
+/// `InternalScreen`, and the emitter diffs the Surface against its own shadow and writes the escapes
+/// itself. Those two grids were the largest single claim on a 384 KiB heap and the reason the board
+/// was held to 40x12 - the comment above `max_cols` used to say a cell was paid for four times over,
+/// and this is what took it down to two. vaxis is still doing the work only it can do here: entering
+/// the alternate screen, asking the terminal what it is, and parsing everything that comes back.
+fn vaxisSize() vaxis.Winsize {
+ return if (direct_emit)
+ .{ .rows = 1, .cols = 1, .x_pixel = 0, .y_pixel = 0 }
+ else
+ cur_winsize;
+}
+
// -------------------------------------------------------------------------------------- exports
/// Hand over the allocator and the output sink, state the initial window size, and bring the editor
@@ -253,7 +273,7 @@ export fn pardes_p4_init(
pardes.syntax.start(allocs.tree_sitter);
vx = vaxis.init(std.Io.failing, a, &env_map, .{}) catch |err| return errCode(err);
- vx.resize(a, &out, cur_winsize) catch |err| return errCode(err);
+ vx.resize(a, &out, vaxisSize()) catch |err| return errCode(err);
// Ask the terminal what it is. Both halves are pure byte writers, which is the whole reason this
// works over a serial line: the replies arrive as ordinary input and are parsed like any key.
@@ -452,10 +472,15 @@ fn apply(c: *pardes.Pardes, ev: vaxis.Event) void {
};
if (want.cols == cur_winsize.cols and want.rows == cur_winsize.rows) return;
const previous = cur_winsize;
- vx.resize(gpa(), &out, want) catch {
- vx.resize(gpa(), &out, previous) catch {};
- return;
- };
+ // vaxis is only resized when it is the thing doing the rendering. Under `direct_emit` its
+ // grids are a single cell and stay that way - see `vaxisSize` - so there is nothing here
+ // to reallocate, which also means a resize can no longer fail for want of two grids.
+ if (!direct_emit) {
+ vx.resize(gpa(), &out, want) catch {
+ vx.resize(gpa(), &out, previous) catch {};
+ return;
+ };
+ }
cur_winsize = want;
c.update(.{ .resize = .{ .cols = want.cols, .rows = want.rows } });
dirty = true;