From 65df657b4e8d1f884d9d13959893bd6179b93162 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 26 Aug 2022 11:09:48 -0700 Subject: [PATCH] make cell attrs bools instead of u1s --- src/Grid.zig | 18 ++++++------ src/terminal/Screen.zig | 61 +++++++++++++++++++-------------------- src/terminal/Terminal.zig | 35 +++++++++++++--------- 3 files changed, 61 insertions(+), 53 deletions(-) diff --git a/src/Grid.zig b/src/Grid.zig index 967402a8d..9b5f2d1b7 100644 --- a/src/Grid.zig +++ b/src/Grid.zig @@ -428,7 +428,7 @@ fn addCursor(self: *Grid, term: Terminal) void { GPUCellMode, @enumToInt(self.cursor_style), ); - if (cell.attrs.wide == 1) mode = mode.mask(.wide_mask); + if (cell.attrs.wide) mode = mode.mask(.wide_mask); self.cells.appendAssumeCapacity(.{ .mode = mode, @@ -492,7 +492,7 @@ pub fn updateCell( } } - const res: BgFg = if (cell.attrs.inverse == 0) .{ + const res: BgFg = if (!cell.attrs.inverse) .{ // In normal mode, background and fg match the cell. We // un-optionalize the fg by defaulting to our fg color. .bg = cell.bg, @@ -508,14 +508,14 @@ pub fn updateCell( }; // If we are a trailing spacer, we never render anything. - if (cell.attrs.wide_spacer_tail == 1) return true; + if (cell.attrs.wide_spacer_tail) return true; // Calculate the amount of space we need in the cells list. const needed = needed: { var i: usize = 0; if (colors.bg != null) i += 1; if (!cell.empty()) i += 1; - if (cell.attrs.underline == 1) i += 1; + if (cell.attrs.underline) i += 1; break :needed i; }; if (self.cells.items.len + needed > self.cells.capacity) return false; @@ -523,7 +523,7 @@ pub fn updateCell( // If the cell has a background, we always draw it. if (colors.bg) |rgb| { var mode: GPUCellMode = .bg; - if (cell.attrs.wide == 1) mode = mode.mask(.wide_mask); + if (cell.attrs.wide) mode = mode.mask(.wide_mask); self.cells.appendAssumeCapacity(.{ .mode = mode, @@ -549,7 +549,7 @@ pub fn updateCell( // If the cell is empty then we draw nothing in the box. if (!cell.empty()) { // Determine our glyph styling - const style: font.Style = if (cell.attrs.bold == 1) + const style: font.Style = if (cell.attrs.bold) .bold else .regular; @@ -562,7 +562,7 @@ pub fn updateCell( const glyph = goa.glyph; // If the cell is wide, we need to note that in the mode - if (cell.attrs.wide == 1) mode = mode.mask(.wide_mask); + if (cell.attrs.wide) mode = mode.mask(.wide_mask); self.cells.appendAssumeCapacity(.{ .mode = mode, @@ -585,9 +585,9 @@ pub fn updateCell( }); } - if (cell.attrs.underline == 1) { + if (cell.attrs.underline) { var mode: GPUCellMode = .underline; - if (cell.attrs.wide == 1) mode = mode.mask(.wide_mask); + if (cell.attrs.wide) mode = mode.mask(.wide_mask); self.cells.appendAssumeCapacity(.{ .mode = mode, diff --git a/src/terminal/Screen.zig b/src/terminal/Screen.zig index 8feb335d3..e2a704340 100644 --- a/src/terminal/Screen.zig +++ b/src/terminal/Screen.zig @@ -64,25 +64,24 @@ pub const Cell = struct { /// On/off attributes that can be set attrs: packed struct { - bold: u1 = 0, - underline: u1 = 0, - inverse: u1 = 0, + bold: bool = false, + faint: bool = false, + underline: bool = false, + inverse: bool = false, /// If 1, this line is soft-wrapped. Only the last cell in a row /// should have this set. The first cell of the next row is actually /// part of this row in raw input. - wrap: u1 = 0, + wrap: bool = false, /// True if this is a wide character. This char takes up /// two cells. The following cell ALWAYS is a space. - wide: u1 = 0, + wide: bool = false, /// Notes that this only exists to be blank for a preceeding /// wide character (tail) or following (head). - wide_spacer_tail: u1 = 0, - wide_spacer_head: u1 = 0, - - _padding: u1 = 0, + wide_spacer_tail: bool = false, + wide_spacer_head: bool = false, } = .{}, /// True if the cell should be skipped for drawing @@ -573,7 +572,7 @@ pub fn resize(self: *Screen, alloc: Allocator, rows: usize, cols: usize) !void { } // If no reflow, just keep going - if (row[row.len - 1].attrs.wrap == 0) { + if (!row[row.len - 1].attrs.wrap) { y += 1; continue; } @@ -583,7 +582,7 @@ pub fn resize(self: *Screen, alloc: Allocator, rows: usize, cols: usize) !void { // only reloop when we're back to clean non-wrapped lines. // Mark the last element as not wrapped - new_row[row.len - 1].attrs.wrap = 0; + new_row[row.len - 1].attrs.wrap = false; // We maintain an x coord so that we can set cursors properly var x: usize = row.len; @@ -611,7 +610,7 @@ pub fn resize(self: *Screen, alloc: Allocator, rows: usize, cols: usize) !void { } // If this row isn't also wrapped, we're done! - if (wrapped_rem[wrapped_rem.len - 1].attrs.wrap == 0) { + if (!wrapped_rem[wrapped_rem.len - 1].attrs.wrap) { y += 1; // If we were able to copy the entire row then @@ -628,7 +627,7 @@ pub fn resize(self: *Screen, alloc: Allocator, rows: usize, cols: usize) !void { } // Wrapped again! - new_row[wrapped_rem.len - 1].attrs.wrap = 0; + new_row[wrapped_rem.len - 1].attrs.wrap = false; new_row = new_row[wrapped_rem.len..]; x += wrapped_rem.len; break; @@ -637,7 +636,7 @@ pub fn resize(self: *Screen, alloc: Allocator, rows: usize, cols: usize) !void { // The row doesn't fit, meaning we have to soft-wrap the // new row but probably at a diff boundary. std.mem.copy(Cell, new_row, wrapped_rem[0..new_row.len]); - new_row[new_row.len - 1].attrs.wrap = 1; + new_row[new_row.len - 1].attrs.wrap = true; // We still need to copy the remainder wrapped_rem = wrapped_rem[new_row.len..]; @@ -799,7 +798,7 @@ pub fn resize(self: *Screen, alloc: Allocator, rows: usize, cols: usize) !void { // Soft wrap if we have to if (x == self.cols) { var last_cell = self.getCell(y, x - 1); - last_cell.attrs.wrap = 1; + last_cell.attrs.wrap = true; x = 0; y += 1; } @@ -823,7 +822,7 @@ pub fn resize(self: *Screen, alloc: Allocator, rows: usize, cols: usize) !void { // log.warn("y={} x={} rows={}", .{ y, x, self.rows }); var new_cell = self.getCell(y, x); new_cell.* = cell; - new_cell.attrs.wrap = 0; + new_cell.attrs.wrap = false; // Next x += 1; @@ -843,7 +842,7 @@ pub fn resize(self: *Screen, alloc: Allocator, rows: usize, cols: usize) !void { // If we aren't wrapping, then move to the next row if (trimmed_row.len == 0 or - trimmed_row[trimmed_row.len - 1].attrs.wrap == 0) + !trimmed_row[trimmed_row.len - 1].attrs.wrap) { y += 1; x = 0; @@ -946,15 +945,15 @@ pub fn selectionString(self: Screen, alloc: Allocator, sel: Selection) ![:0]cons // at a newline and we add it. if (idx > 0 and @mod(idx + slices.top_offset, self.cols) == 0 and - slices.top[idx - 1].attrs.wrap == 0) + !slices.top[idx - 1].attrs.wrap) { buf[i] = '\n'; i += 1; } // Skip spacers - if (cell.attrs.wide_spacer_head == 1 or - cell.attrs.wide_spacer_tail == 1) continue; + if (cell.attrs.wide_spacer_head or + cell.attrs.wide_spacer_tail) continue; const char = if (cell.char > 0) cell.char else ' '; i += try std.unicode.utf8Encode(@intCast(u21, char), buf[i..]); @@ -969,9 +968,9 @@ pub fn selectionString(self: Screen, alloc: Allocator, sel: Selection) ![:0]cons // a bit unique because if we're at idx 0, we actually need to // check the end of the top. const wrapped = if (idx > 0) - slices.bot[idx - 1].attrs.wrap == 1 + slices.bot[idx - 1].attrs.wrap else - slices.top[slices.top.len - 1].attrs.wrap == 1; + slices.top[slices.top.len - 1].attrs.wrap; if (!wrapped) { buf[i] = '\n'; @@ -980,8 +979,8 @@ pub fn selectionString(self: Screen, alloc: Allocator, sel: Selection) ![:0]cons } // Skip spacers - if (cell.attrs.wide_spacer_head == 1 or - cell.attrs.wide_spacer_tail == 1) continue; + if (cell.attrs.wide_spacer_head or + cell.attrs.wide_spacer_tail) continue; const char = if (cell.char > 0) cell.char else ' '; i += try std.unicode.utf8Encode(@intCast(u21, char), buf[i..]); @@ -1019,7 +1018,7 @@ fn selectionSlices(self: Screen, sel_raw: Selection) struct { // first part of the next line. if (sel.end.x == self.cols - 1) { const row = self.getRow(.{ .screen = sel.end.y }); - if (row[sel.end.x].attrs.wide_spacer_head == 1) { + if (row[sel.end.x].attrs.wide_spacer_head) { sel.end.y += 1; sel.end.x = 0; } @@ -1029,7 +1028,7 @@ fn selectionSlices(self: Screen, sel_raw: Selection) struct { // wide char. if (sel.start.x > 0) { const row = self.getRow(.{ .screen = sel.start.y }); - if (row[sel.start.x].attrs.wide_spacer_tail == 1) { + if (row[sel.start.x].attrs.wide_spacer_tail) { sel.end.x -= 1; } } @@ -1119,7 +1118,7 @@ fn testWriteString(self: *Screen, text: []const u8) void { // If we're writing past the end, we need to soft wrap. if (x == self.cols) { - row[x - 1].attrs.wrap = 1; + row[x - 1].attrs.wrap = true; y += 1; x = 0; if (y >= self.rows) { @@ -1138,10 +1137,10 @@ fn testWriteString(self: *Screen, text: []const u8) void { 2 => { if (x == self.cols - 1) { row[x].char = ' '; - row[x].attrs.wide_spacer_head = 1; + row[x].attrs.wide_spacer_head = true; // wrap - row[x].attrs.wrap = 1; + row[x].attrs.wrap = true; y += 1; x = 0; if (y >= self.rows) { @@ -1152,11 +1151,11 @@ fn testWriteString(self: *Screen, text: []const u8) void { } row[x].char = @intCast(u32, c); - row[x].attrs.wide = 1; + row[x].attrs.wide = true; x += 1; row[x].char = ' '; - row[x].attrs.wide_spacer_tail = 1; + row[x].attrs.wide_spacer_tail = true; }, else => unreachable, diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 5a860e5b3..f105cb740 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -59,12 +59,21 @@ scrolling_region: ScrollingRegion, /// Modes - This isn't exhaustive, since some modes (i.e. cursor origin) /// are applied to the cursor and others aren't boolean yes/no. modes: packed struct { + const Self = @This(); + reverse_colors: u1 = 0, // 5, origin: u1 = 0, // 6 autowrap: u1 = 1, // 7 deccolm: u1 = 0, // 3, deccolm_supported: u1 = 0, // 40 + + test { + // We have this here so that we explicitly fail when we change the + // size of modes. The size of modes is NOT particularly important, + // we just want to be mentally aware when it happens. + try std.testing.expectEqual(1, @sizeOf(Self)); + } } = .{}, /// Scrolling region is the area of the screen designated where scrolling @@ -289,19 +298,19 @@ pub fn setAttribute(self: *Terminal, attr: sgr.Attribute) !void { }, .bold => { - self.screen.cursor.pen.attrs.bold = 1; + self.screen.cursor.pen.attrs.bold = true; }, .underline => { - self.screen.cursor.pen.attrs.underline = 1; + self.screen.cursor.pen.attrs.underline = true; }, .inverse => { - self.screen.cursor.pen.attrs.inverse = 1; + self.screen.cursor.pen.attrs.inverse = true; }, .reset_inverse => { - self.screen.cursor.pen.attrs.inverse = 0; + self.screen.cursor.pen.attrs.inverse = false; }, .direct_color_fg => |rgb| { @@ -374,17 +383,17 @@ pub fn print(self: *Terminal, c: u21) !void { // char as normal. if (self.screen.cursor.x == self.cols - 1) { const spacer_head = self.printCell(' '); - spacer_head.attrs.wide_spacer_head = 1; + spacer_head.attrs.wide_spacer_head = true; _ = self.printWrap(); } const wide_cell = self.printCell(c); - wide_cell.attrs.wide = 1; + wide_cell.attrs.wide = true; // Write our spacer self.screen.cursor.x += 1; const spacer = self.printCell(' '); - spacer.attrs.wide_spacer_tail = 1; + spacer.attrs.wide_spacer_tail = true; }, else => unreachable, @@ -411,22 +420,22 @@ fn printCell(self: *Terminal, c: u21) *Screen.Cell { // If this cell is wide char then we need to clear it. // We ignore wide spacer HEADS because we can just write // single-width characters into that. - if (cell.attrs.wide == 1) { + if (cell.attrs.wide) { const x = self.screen.cursor.x + 1; assert(x < self.cols); const spacer_cell = self.screen.getCell(self.screen.cursor.y, x); - spacer_cell.attrs.wide_spacer_tail = 0; + spacer_cell.attrs.wide_spacer_tail = false; if (self.screen.cursor.x <= 1) { self.clearWideSpacerHead(); } - } else if (cell.attrs.wide_spacer_tail == 1) { + } else if (cell.attrs.wide_spacer_tail) { assert(self.screen.cursor.x > 0); const x = self.screen.cursor.x - 1; const wide_cell = self.screen.getCell(self.screen.cursor.y, x); - wide_cell.attrs.wide = 0; + wide_cell.attrs.wide = false; if (self.screen.cursor.x <= 1) { self.clearWideSpacerHead(); @@ -446,7 +455,7 @@ fn printWrap(self: *Terminal) *Screen.Cell { self.screen.cursor.y, self.screen.cursor.x, ); - cell.attrs.wrap = 1; + cell.attrs.wrap = true; // Move to the next line self.index(); @@ -462,7 +471,7 @@ fn clearWideSpacerHead(self: *Terminal) void { self.screen.cursor.y - 1, self.cols - 1, ); - cell.attrs.wide_spacer_head = 0; + cell.attrs.wide_spacer_head = false; } /// Resets all margins and fills the whole screen with the character 'E'