From 0a27e5a58b70ceebd4f58fb9c9ad569e5740f58a Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 18 Feb 2024 22:11:21 -0800 Subject: [PATCH] terminal/new: print some characters (test string) --- src/terminal/new/Screen.zig | 71 +++++++++++++++++++++++++++++++++++-- src/terminal/new/page.zig | 39 +++++++++++++++++++- 2 files changed, 106 insertions(+), 4 deletions(-) diff --git a/src/terminal/new/Screen.zig b/src/terminal/new/Screen.zig index fbef50aef..fe5cd660c 100644 --- a/src/terminal/new/Screen.zig +++ b/src/terminal/new/Screen.zig @@ -3,6 +3,7 @@ const Screen = @This(); const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; +const unicode = @import("../../unicode/main.zig"); const pagepkg = @import("page.zig"); const Page = pagepkg.Page; @@ -46,16 +47,31 @@ pages: PageList, viewport: *PageList.Node, viewport_row: usize, +/// The current cursor position +cursor: Cursor, + +/// The current desired screen dimensions. I say "desired" because individual +/// pages may still be a different size and not yet reflowed since we lazily +/// reflow text. +cols: usize, +rows: usize, + /// The cursor position. const Cursor = struct { // The x/y position within the viewport. - x: usize = 0, - y: usize = 0, + x: usize, + y: usize, + + /// The "last column flag (LCF)" as its called. If this is set then the + /// next character print will force a soft-wrap. + pending_wrap: bool = false, // The page that the cursor is on and the offset into that page that // the current y exists. page: *PageList.Node, page_row: usize, + page_row_ptr: *pagepkg.Row, + page_cell_ptr: *pagepkg.Cell, }; /// Initialize a new screen. @@ -88,12 +104,27 @@ pub fn init( var page_list: PageList = .{}; page_list.prepend(page); + const cursor_row_ptr, const cursor_cell_ptr = ptr: { + const rac = page.data.getRowAndCell(0, 0); + break :ptr .{ rac.row, rac.cell }; + }; + return .{ .alloc = alloc, + .cols = cols, + .rows = rows, .page_pool = pool, .pages = page_list, .viewport = page, .viewport_row = 0, + .cursor = .{ + .x = 0, + .y = 0, + .page = page, + .page_row = 0, + .page_row_ptr = cursor_row_ptr, + .page_cell_ptr = cursor_cell_ptr, + }, }; } @@ -104,10 +135,44 @@ pub fn deinit(self: *Screen) void { self.page_pool.deinit(); } -test { +fn testWriteString(self: *Screen, text: []const u8) !void { + const view = try std.unicode.Utf8View.init(text); + var iter = view.iterator(); + while (iter.nextCodepoint()) |c| { + if (self.cursor.x == self.cols) { + @panic("wrap not implemented"); + } + + const width: usize = if (c <= 0xFF) 1 else @intCast(unicode.table.get(c).width); + if (width == 0) { + @panic("zero-width todo"); + } + + assert(width == 1 or width == 2); + switch (width) { + 1 => { + self.cursor.page_cell_ptr.codepoint = c; + self.cursor.x += 1; + if (self.cursor.x < self.cols) { + const cell_ptr: [*]pagepkg.Cell = @ptrCast(self.cursor.page_cell_ptr); + self.cursor.page_cell_ptr = @ptrCast(cell_ptr + 1); + } else { + @panic("wrap not implemented"); + } + }, + + 2 => @panic("todo double-width"), + else => unreachable, + } + } +} + +test "Screen read and write" { const testing = std.testing; const alloc = testing.allocator; var s = try Screen.init(alloc, 80, 24, 1000); defer s.deinit(); + + try s.testWriteString("hello, world"); } diff --git a/src/terminal/new/page.zig b/src/terminal/new/page.zig index 273bfa19c..ad9d49326 100644 --- a/src/terminal/new/page.zig +++ b/src/terminal/new/page.zig @@ -107,6 +107,21 @@ pub const Page = struct { self.* = undefined; } + /// Get the row and cell for the given X/Y within this page. + pub fn getRowAndCell(self: *const Page, x: usize, y: usize) struct { + row: *Row, + cell: *Cell, + } { + assert(y < self.capacity.rows); + assert(x < self.capacity.cols); + + const rows = self.rows.ptr(self.memory); + const row = &rows[y]; + const cell = &row.cells.ptr(self.memory)[x]; + + return .{ .row = row, .cell = cell }; + } + const Layout = struct { total_size: usize, rows_start: usize, @@ -184,7 +199,7 @@ pub const Cell = packed struct(u32) { // }); // } -test "Page" { +test "Page init" { const testing = std.testing; const alloc = testing.allocator; var page = try Page.init(alloc, .{ @@ -194,3 +209,25 @@ test "Page" { }); defer page.deinit(alloc); } + +test "Page read and write cells" { + const testing = std.testing; + const alloc = testing.allocator; + var page = try Page.init(alloc, .{ + .cols = 10, + .rows = 10, + .styles = 8, + }); + defer page.deinit(alloc); + + for (0..page.capacity.rows) |y| { + const rac = page.getRowAndCell(1, y); + rac.cell.codepoint = @intCast(y); + } + + // Read it again + for (0..page.capacity.rows) |y| { + const rac = page.getRowAndCell(1, y); + try testing.expectEqual(@as(u21, @intCast(y)), rac.cell.codepoint); + } +}