From ccaf75193b22cd2bb486e4a6414a0c33696a209f Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 12 May 2022 16:34:38 -0700 Subject: [PATCH] connect setAttribute --- src/Window.zig | 9 ++++++ src/terminal/Terminal.zig | 61 +++++++++++++++++++++++++++------------ src/terminal/stream.zig | 17 +++-------- 3 files changed, 55 insertions(+), 32 deletions(-) diff --git a/src/Window.zig b/src/Window.zig index 72258d62e..913941b47 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -584,3 +584,12 @@ pub fn setMode(self: *Window, mode: terminal.Mode, enabled: bool) !void { else => if (enabled) log.warn("unimplemented mode: {}", .{mode}), } } + +pub fn setAttribute(self: *Window, attr: terminal.Attribute) !void { + switch (attr) { + .unknown => |unk| log.warn("unimplemented or unknown attribute: {any}", .{unk}), + + else => self.terminal.setAttribute(attr) catch |err| + log.warn("error setting attribute {}: {}", .{ attr, err }), + } +} diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 1cff792dc..009dce471 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -10,6 +10,7 @@ const testing = std.testing; const Allocator = std.mem.Allocator; const ansi = @import("ansi.zig"); const csi = @import("csi.zig"); +const sgr = @import("sgr.zig"); const Tabstops = @import("Tabstops.zig"); const trace = @import("../tracy/tracy.zig").trace; @@ -52,7 +53,9 @@ const Cell = struct { /// Each cell contains exactly one character. The character is UTF-8 encoded. char: u32, - // TODO(mitchellh): this is where we'll track fg/bg and other attrs. + /// Foreground and background color. null means to use the default. + fg: ?RGB = null, + bg: ?RGB = null, /// True if the cell should be skipped for drawing pub fn empty(self: Cell) bool { @@ -66,9 +69,14 @@ const Cursor = struct { x: usize, y: usize, - // Bold specifies that text written should be bold - // TODO: connect to render - bold: bool = false, + // pen is the current cell styling to apply to new cells. + pen: Cell = .{ .char = 0 }, +}; + +pub const RGB = struct { + r: u8, + g: u8, + b: u8, }; /// Initialize a new terminal. @@ -125,15 +133,42 @@ pub fn plainString(self: Terminal, alloc: Allocator) ![]const u8 { return buffer[0..i]; } +/// TODO: test +pub fn setAttribute(self: *Terminal, attr: sgr.Attribute) !void { + switch (attr) { + .unset => { + self.cursor.pen.fg = null; + self.cursor.pen.bg = null; + }, + + .direct_color_fg => |rgb| { + self.cursor.pen.fg = .{ + .r = rgb.r, + .g = rgb.g, + .b = rgb.g, + }; + }, + + .direct_color_bg => |rgb| { + self.cursor.pen.bg = .{ + .r = rgb.r, + .g = rgb.g, + .b = rgb.g, + }; + }, + + else => return error.InvalidAttribute, + } +} + pub fn print(self: *Terminal, alloc: Allocator, c: u8) !void { const tracy = trace(@src()); defer tracy.end(); // Build our cell const cell = try self.getOrPutCell(alloc, self.cursor.x, self.cursor.y); - cell.* = .{ - .char = @intCast(u32, c), - }; + cell.* = self.cursor.pen; + cell.char = @intCast(u32, c); // Move the cursor self.cursor.x += 1; @@ -144,18 +179,6 @@ pub fn print(self: *Terminal, alloc: Allocator, c: u8) !void { } } -pub fn selectGraphicRendition(self: *Terminal, aspect: ansi.RenditionAspect) !void { - switch (aspect) { - .default => self.cursor.bold = false, - .bold => self.cursor.bold = true, - .default_fg => {}, // TODO - .default_bg => {}, // TODO - else => { - //log.warn("invalid or unimplemented rendition aspect: {}", .{aspect}); - }, - } -} - // TODO: test pub fn reverseIndex(self: *Terminal, alloc: Allocator) !void { if (self.cursor.y == 0) diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index 736425c65..044b95cfb 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -3,6 +3,7 @@ const testing = std.testing; const Parser = @import("Parser.zig"); const ansi = @import("ansi.zig"); const csi = @import("csi.zig"); +const sgr = @import("sgr.zig"); const trace = @import("../tracy/tracy.zig").trace; const log = std.log.scoped(.stream); @@ -251,19 +252,9 @@ pub fn Stream(comptime Handler: type) type { } else log.warn("unimplemented CSI callback: {}", .{action}), // SGR - Select Graphic Rendition - 'm' => if (@hasDecl(T, "selectGraphicRendition")) { - if (action.params.len == 0) { - // No values defaults to code 0 - try self.handler.selectGraphicRendition(.default); - } else { - // Each parameter sets a separate aspect - for (action.params) |param| { - try self.handler.selectGraphicRendition(@intToEnum( - ansi.RenditionAspect, - param, - )); - } - } + 'm' => if (@hasDecl(T, "setAttribute")) { + var p: sgr.Parser = .{ .params = action.params }; + while (p.next()) |attr| try self.handler.setAttribute(attr); } else log.warn("unimplemented CSI callback: {}", .{action}), // DECSTBM - Set Top and Bottom Margins