diff --git a/build.zig b/build.zig index 4c84e38..c1573d7 100644 --- a/build.zig +++ b/build.zig @@ -24,6 +24,8 @@ pub fn build(b: *std.Build) void { palette, // error handling errors, + // non alternate screen applications + direct, }; const example = b.option(Examples, "example", "Example to build and/or run. (default: all)") orelse .all; @@ -73,6 +75,8 @@ pub fn build(b: *std.Build) void { .palette => "examples/styles/palette.zig", // error handling .errors => "examples/errors.zig", + // non-alternate screen + .direct => "examples/direct.zig", .all => unreachable, // should never happen }), .target = target, diff --git a/src/render.zig b/src/render.zig index 74f167d..48b5ee8 100644 --- a/src/render.zig +++ b/src/render.zig @@ -91,6 +91,7 @@ pub const Buffered = struct { var writer = terminal.writer(); const s = this.screen; const vs = this.virtual_screen; + for (0..this.size.y) |row| { for (0..this.size.x) |col| { const idx = (row * this.size.x) + col; @@ -123,6 +124,87 @@ pub const Buffered = struct { } }; +pub const Direct = struct { + gpa: Allocator, + size: Point, + resized: bool, + screen: []Cell, + + pub fn init(gpa: Allocator) @This() { + return .{ + .gpa = gpa, + .size = .{}, + .resized = true, + .screen = undefined, + }; + } + + pub fn deinit(this: *@This()) void { + this.gpa.free(this.screen); + } + + pub fn resize(this: *@This()) !Point { + // TODO the size in the y-axis is *not fixed* and *not limited* + // how can I achive this? + this.size = .{}; + if (!this.resized) { + this.gpa.free(this.screen); + this.screen = undefined; + } + this.resized = true; + return terminal.getTerminalSize(); + } + + pub fn clear(this: *@This()) !void { + _ = this; + try terminal.clearScreen(); + } + + /// Render provided cells at size (anchor and dimension) into the *virtual screen*. + pub fn render(this: *@This(), comptime Container: type, container: *Container, comptime Model: type, model: *const Model) !void { + const size: Point = container.size; + const origin: Point = container.origin; + + if (this.resized) { + this.size = size; + const n = @as(usize, this.size.x) * @as(usize, this.size.y); + this.screen = try this.gpa.alloc(Cell, n); + @memset(this.screen, .{}); + this.resized = false; + } + + const cells: []const Cell = try container.content(model); + + var idx: usize = 0; + var vs = this.screen; + const anchor: usize = (@as(usize, origin.y) * @as(usize, this.size.x)) + @as(usize, origin.x); + + + blk: for (0..size.y) |row| { + for (0..size.x) |col| { + vs[anchor + (row * this.size.x) + col] = cells[idx]; + idx += 1; + + if (cells.len == idx) break :blk; + } + } + // free immediately + container.allocator.free(cells); + for (container.elements.items) |*element| try this.render(Container, element, Model, model); + } + + pub fn flush(this: *@This()) !void { + var writer = terminal.writer(); + for (0..this.size.y) |row| { + for (0..this.size.x) |col| { + const idx = (row * this.size.x) + col; + const cvs = this.screen[idx]; + try cvs.value(&writer); + } + } + } +}; + const std = @import("std"); const meta = std.meta; const assert = std.debug.assert;