mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
terminal: track palette color in cell state
Rather than immediately converting a color palette index into an RGB value for a cell color, when a palette color is used track the palette color directly in the cell state and convert to an RGB value in the renderer. This causes palette color changes to take effect immediately instead of only for newly drawn cells.
This commit is contained in:
@ -260,8 +260,7 @@ test "run iterator: empty cells with background set" {
|
||||
// Make a screen with some data
|
||||
var screen = try terminal.Screen.init(alloc, 3, 5, 0);
|
||||
defer screen.deinit();
|
||||
screen.cursor.pen.bg = try terminal.color.Name.cyan.default();
|
||||
screen.cursor.pen.attrs.has_bg = true;
|
||||
screen.cursor.pen.bg = .{ .rgb = try terminal.color.Name.cyan.default() };
|
||||
try screen.testWriteString("A");
|
||||
|
||||
// Get our first row
|
||||
@ -836,10 +835,9 @@ test "shape cell attribute change" {
|
||||
{
|
||||
var screen = try terminal.Screen.init(alloc, 3, 10, 0);
|
||||
defer screen.deinit();
|
||||
screen.cursor.pen.attrs.has_fg = true;
|
||||
screen.cursor.pen.fg = .{ .r = 1, .g = 2, .b = 3 };
|
||||
screen.cursor.pen.fg = .{ .rgb = .{ .r = 1, .g = 2, .b = 3 } };
|
||||
try screen.testWriteString(">");
|
||||
screen.cursor.pen.fg = .{ .r = 3, .g = 2, .b = 1 };
|
||||
screen.cursor.pen.fg = .{ .rgb = .{ .r = 3, .g = 2, .b = 1 } };
|
||||
try screen.testWriteString("=");
|
||||
|
||||
var shaper = &testdata.shaper;
|
||||
@ -856,10 +854,9 @@ test "shape cell attribute change" {
|
||||
{
|
||||
var screen = try terminal.Screen.init(alloc, 3, 10, 0);
|
||||
defer screen.deinit();
|
||||
screen.cursor.pen.attrs.has_bg = true;
|
||||
screen.cursor.pen.bg = .{ .r = 1, .g = 2, .b = 3 };
|
||||
screen.cursor.pen.bg = .{ .rgb = .{ .r = 1, .g = 2, .b = 3 } };
|
||||
try screen.testWriteString(">");
|
||||
screen.cursor.pen.bg = .{ .r = 3, .g = 2, .b = 1 };
|
||||
screen.cursor.pen.bg = .{ .rgb = .{ .r = 3, .g = 2, .b = 1 } };
|
||||
try screen.testWriteString("=");
|
||||
|
||||
var shaper = &testdata.shaper;
|
||||
@ -876,8 +873,7 @@ test "shape cell attribute change" {
|
||||
{
|
||||
var screen = try terminal.Screen.init(alloc, 3, 10, 0);
|
||||
defer screen.deinit();
|
||||
screen.cursor.pen.attrs.has_bg = true;
|
||||
screen.cursor.pen.bg = .{ .r = 1, .g = 2, .b = 3 };
|
||||
screen.cursor.pen.bg = .{ .rgb = .{ .r = 1, .g = 2, .b = 3 } };
|
||||
try screen.testWriteString(">");
|
||||
try screen.testWriteString("=");
|
||||
|
||||
|
@ -87,8 +87,8 @@ pub const RunIterator = struct {
|
||||
const prev_attrs: Int = @bitCast(prev_cell.attrs.styleAttrs());
|
||||
const attrs: Int = @bitCast(cell.attrs.styleAttrs());
|
||||
if (prev_attrs != attrs) break;
|
||||
if (cell.attrs.has_bg and !cell.bg.eql(prev_cell.bg)) break;
|
||||
if (cell.attrs.has_fg and !cell.fg.eql(prev_cell.fg)) break;
|
||||
if (!cell.bg.eql(prev_cell.bg)) break;
|
||||
if (!cell.fg.eql(prev_cell.fg)) break;
|
||||
}
|
||||
|
||||
// Text runs break when font styles change so we need to get
|
||||
|
@ -299,7 +299,8 @@ fn renderScreenWindow(self: *Inspector) void {
|
||||
);
|
||||
defer cimgui.c.igEndTable();
|
||||
|
||||
inspector.cursor.renderInTable(&screen.cursor);
|
||||
const palette = self.surface.io.terminal.color_palette.colors;
|
||||
inspector.cursor.renderInTable(&screen.cursor, &palette);
|
||||
} // table
|
||||
|
||||
cimgui.c.igTextDisabled("(Any styles not shown are not currently set)");
|
||||
@ -871,49 +872,66 @@ fn renderCellWindow(self: *Inspector) void {
|
||||
}
|
||||
|
||||
// If we have a color then we show the color
|
||||
color: {
|
||||
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);
|
||||
_ = cimgui.c.igTableSetColumnIndex(0);
|
||||
cimgui.c.igText("Foreground Color");
|
||||
_ = cimgui.c.igTableSetColumnIndex(1);
|
||||
if (!selected.cell.attrs.has_fg) {
|
||||
cimgui.c.igText("default");
|
||||
break :color;
|
||||
}
|
||||
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);
|
||||
_ = cimgui.c.igTableSetColumnIndex(0);
|
||||
cimgui.c.igText("Foreground Color");
|
||||
_ = cimgui.c.igTableSetColumnIndex(1);
|
||||
switch (selected.cell.fg) {
|
||||
.none => cimgui.c.igText("default"),
|
||||
else => {
|
||||
const rgb = switch (selected.cell.fg) {
|
||||
.none => unreachable,
|
||||
.indexed => |idx| self.surface.io.terminal.color_palette.colors[idx],
|
||||
.rgb => |rgb| rgb,
|
||||
};
|
||||
|
||||
var color: [3]f32 = .{
|
||||
@as(f32, @floatFromInt(selected.cell.fg.r)) / 255,
|
||||
@as(f32, @floatFromInt(selected.cell.fg.g)) / 255,
|
||||
@as(f32, @floatFromInt(selected.cell.fg.b)) / 255,
|
||||
};
|
||||
_ = cimgui.c.igColorEdit3(
|
||||
"color_fg",
|
||||
&color,
|
||||
cimgui.c.ImGuiColorEditFlags_NoPicker |
|
||||
cimgui.c.ImGuiColorEditFlags_NoLabel,
|
||||
);
|
||||
if (selected.cell.fg == .indexed) {
|
||||
cimgui.c.igValue_Int("Palette", selected.cell.fg.indexed);
|
||||
}
|
||||
|
||||
var color: [3]f32 = .{
|
||||
@as(f32, @floatFromInt(rgb.r)) / 255,
|
||||
@as(f32, @floatFromInt(rgb.g)) / 255,
|
||||
@as(f32, @floatFromInt(rgb.b)) / 255,
|
||||
};
|
||||
_ = cimgui.c.igColorEdit3(
|
||||
"color_fg",
|
||||
&color,
|
||||
cimgui.c.ImGuiColorEditFlags_NoPicker |
|
||||
cimgui.c.ImGuiColorEditFlags_NoLabel,
|
||||
);
|
||||
},
|
||||
}
|
||||
color: {
|
||||
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);
|
||||
_ = cimgui.c.igTableSetColumnIndex(0);
|
||||
cimgui.c.igText("Background Color");
|
||||
_ = cimgui.c.igTableSetColumnIndex(1);
|
||||
if (!selected.cell.attrs.has_bg) {
|
||||
cimgui.c.igText("default");
|
||||
break :color;
|
||||
}
|
||||
|
||||
var color: [3]f32 = .{
|
||||
@as(f32, @floatFromInt(selected.cell.bg.r)) / 255,
|
||||
@as(f32, @floatFromInt(selected.cell.bg.g)) / 255,
|
||||
@as(f32, @floatFromInt(selected.cell.bg.b)) / 255,
|
||||
};
|
||||
_ = cimgui.c.igColorEdit3(
|
||||
"color_bg",
|
||||
&color,
|
||||
cimgui.c.ImGuiColorEditFlags_NoPicker |
|
||||
cimgui.c.ImGuiColorEditFlags_NoLabel,
|
||||
);
|
||||
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);
|
||||
_ = cimgui.c.igTableSetColumnIndex(0);
|
||||
cimgui.c.igText("Background Color");
|
||||
_ = cimgui.c.igTableSetColumnIndex(1);
|
||||
switch (selected.cell.bg) {
|
||||
.none => cimgui.c.igText("default"),
|
||||
else => {
|
||||
const rgb = switch (selected.cell.bg) {
|
||||
.none => unreachable,
|
||||
.indexed => |idx| self.surface.io.terminal.color_palette.colors[idx],
|
||||
.rgb => |rgb| rgb,
|
||||
};
|
||||
|
||||
if (selected.cell.bg == .indexed) {
|
||||
cimgui.c.igValue_Int("Palette", selected.cell.bg.indexed);
|
||||
}
|
||||
|
||||
var color: [3]f32 = .{
|
||||
@as(f32, @floatFromInt(rgb.r)) / 255,
|
||||
@as(f32, @floatFromInt(rgb.g)) / 255,
|
||||
@as(f32, @floatFromInt(rgb.b)) / 255,
|
||||
};
|
||||
_ = cimgui.c.igColorEdit3(
|
||||
"color_bg",
|
||||
&color,
|
||||
cimgui.c.ImGuiColorEditFlags_NoPicker |
|
||||
cimgui.c.ImGuiColorEditFlags_NoLabel,
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
// Boolean styles
|
||||
@ -1109,7 +1127,8 @@ fn renderTermioWindow(self: *Inspector) void {
|
||||
0,
|
||||
);
|
||||
defer cimgui.c.igEndTable();
|
||||
inspector.cursor.renderInTable(&ev.cursor);
|
||||
const palette = self.surface.io.terminal.color_palette.colors;
|
||||
inspector.cursor.renderInTable(&ev.cursor, &palette);
|
||||
|
||||
{
|
||||
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);
|
||||
|
@ -3,7 +3,10 @@ const cimgui = @import("cimgui");
|
||||
const terminal = @import("../terminal/main.zig");
|
||||
|
||||
/// Render cursor information with a table already open.
|
||||
pub fn renderInTable(cursor: *const terminal.Screen.Cursor) void {
|
||||
pub fn renderInTable(
|
||||
cursor: *const terminal.Screen.Cursor,
|
||||
palette: *const terminal.color.Palette,
|
||||
) void {
|
||||
{
|
||||
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);
|
||||
{
|
||||
@ -41,49 +44,66 @@ pub fn renderInTable(cursor: *const terminal.Screen.Cursor) void {
|
||||
}
|
||||
|
||||
// If we have a color then we show the color
|
||||
if (cursor.pen.attrs.has_fg) color: {
|
||||
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);
|
||||
_ = cimgui.c.igTableSetColumnIndex(0);
|
||||
cimgui.c.igText("Foreground Color");
|
||||
_ = cimgui.c.igTableSetColumnIndex(1);
|
||||
if (!cursor.pen.attrs.has_fg) {
|
||||
cimgui.c.igText("default");
|
||||
break :color;
|
||||
}
|
||||
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);
|
||||
_ = cimgui.c.igTableSetColumnIndex(0);
|
||||
cimgui.c.igText("Foreground Color");
|
||||
_ = cimgui.c.igTableSetColumnIndex(1);
|
||||
switch (cursor.pen.fg) {
|
||||
.none => cimgui.c.igText("default"),
|
||||
else => {
|
||||
const rgb = switch (cursor.pen.fg) {
|
||||
.none => unreachable,
|
||||
.indexed => |idx| palette[idx],
|
||||
.rgb => |rgb| rgb,
|
||||
};
|
||||
|
||||
var color: [3]f32 = .{
|
||||
@as(f32, @floatFromInt(cursor.pen.fg.r)) / 255,
|
||||
@as(f32, @floatFromInt(cursor.pen.fg.g)) / 255,
|
||||
@as(f32, @floatFromInt(cursor.pen.fg.b)) / 255,
|
||||
};
|
||||
_ = cimgui.c.igColorEdit3(
|
||||
"color_fg",
|
||||
&color,
|
||||
cimgui.c.ImGuiColorEditFlags_NoPicker |
|
||||
cimgui.c.ImGuiColorEditFlags_NoLabel,
|
||||
);
|
||||
if (cursor.pen.fg == .indexed) {
|
||||
cimgui.c.igValue_Int("Palette", cursor.pen.fg.indexed);
|
||||
}
|
||||
|
||||
var color: [3]f32 = .{
|
||||
@as(f32, @floatFromInt(rgb.r)) / 255,
|
||||
@as(f32, @floatFromInt(rgb.g)) / 255,
|
||||
@as(f32, @floatFromInt(rgb.b)) / 255,
|
||||
};
|
||||
_ = cimgui.c.igColorEdit3(
|
||||
"color_fg",
|
||||
&color,
|
||||
cimgui.c.ImGuiColorEditFlags_NoPicker |
|
||||
cimgui.c.ImGuiColorEditFlags_NoLabel,
|
||||
);
|
||||
},
|
||||
}
|
||||
if (cursor.pen.attrs.has_bg) color: {
|
||||
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);
|
||||
_ = cimgui.c.igTableSetColumnIndex(0);
|
||||
cimgui.c.igText("Background Color");
|
||||
_ = cimgui.c.igTableSetColumnIndex(1);
|
||||
if (!cursor.pen.attrs.has_bg) {
|
||||
cimgui.c.igText("default");
|
||||
break :color;
|
||||
}
|
||||
|
||||
var color: [3]f32 = .{
|
||||
@as(f32, @floatFromInt(cursor.pen.bg.r)) / 255,
|
||||
@as(f32, @floatFromInt(cursor.pen.bg.g)) / 255,
|
||||
@as(f32, @floatFromInt(cursor.pen.bg.b)) / 255,
|
||||
};
|
||||
_ = cimgui.c.igColorEdit3(
|
||||
"color_bg",
|
||||
&color,
|
||||
cimgui.c.ImGuiColorEditFlags_NoPicker |
|
||||
cimgui.c.ImGuiColorEditFlags_NoLabel,
|
||||
);
|
||||
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);
|
||||
_ = cimgui.c.igTableSetColumnIndex(0);
|
||||
cimgui.c.igText("Background Color");
|
||||
_ = cimgui.c.igTableSetColumnIndex(1);
|
||||
switch (cursor.pen.bg) {
|
||||
.none => cimgui.c.igText("default"),
|
||||
else => {
|
||||
const rgb = switch (cursor.pen.bg) {
|
||||
.none => unreachable,
|
||||
.indexed => |idx| palette[idx],
|
||||
.rgb => |rgb| rgb,
|
||||
};
|
||||
|
||||
if (cursor.pen.bg == .indexed) {
|
||||
cimgui.c.igValue_Int("Palette", cursor.pen.bg.indexed);
|
||||
}
|
||||
|
||||
var color: [3]f32 = .{
|
||||
@as(f32, @floatFromInt(rgb.r)) / 255,
|
||||
@as(f32, @floatFromInt(rgb.g)) / 255,
|
||||
@as(f32, @floatFromInt(rgb.b)) / 255,
|
||||
};
|
||||
_ = cimgui.c.igColorEdit3(
|
||||
"color_bg",
|
||||
&color,
|
||||
cimgui.c.ImGuiColorEditFlags_NoPicker |
|
||||
cimgui.c.ImGuiColorEditFlags_NoLabel,
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
// Boolean styles
|
||||
|
@ -579,6 +579,7 @@ pub fn updateFrame(
|
||||
mouse: renderer.State.Mouse,
|
||||
preedit: ?renderer.State.Preedit,
|
||||
cursor_style: ?renderer.CursorStyle,
|
||||
color_palette: terminal.color.Palette,
|
||||
};
|
||||
|
||||
// Update all our data as tightly as possible within the mutex.
|
||||
@ -655,6 +656,7 @@ pub fn updateFrame(
|
||||
.mouse = state.mouse,
|
||||
.preedit = preedit,
|
||||
.cursor_style = cursor_style,
|
||||
.color_palette = state.terminal.color_palette.colors,
|
||||
};
|
||||
};
|
||||
defer {
|
||||
@ -669,6 +671,7 @@ pub fn updateFrame(
|
||||
critical.mouse,
|
||||
critical.preedit,
|
||||
critical.cursor_style,
|
||||
&critical.color_palette,
|
||||
);
|
||||
|
||||
// Update our background color
|
||||
@ -1424,6 +1427,7 @@ fn rebuildCells(
|
||||
mouse: renderer.State.Mouse,
|
||||
preedit: ?renderer.State.Preedit,
|
||||
cursor_style_: ?renderer.CursorStyle,
|
||||
color_palette: *const terminal.color.Palette,
|
||||
) !void {
|
||||
// Bg cells at most will need space for the visible screen size
|
||||
self.cells_bg.clearRetainingCapacity();
|
||||
@ -1579,6 +1583,7 @@ fn rebuildCells(
|
||||
term_selection,
|
||||
screen,
|
||||
cell,
|
||||
color_palette,
|
||||
shaper_cell,
|
||||
run,
|
||||
shaper_cell.x,
|
||||
@ -1644,11 +1649,12 @@ fn rebuildCells(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updateCell(
|
||||
fn updateCell(
|
||||
self: *Metal,
|
||||
selection: ?terminal.Selection,
|
||||
screen: *terminal.Screen,
|
||||
cell: terminal.Screen.Cell,
|
||||
palette: *const terminal.color.Palette,
|
||||
shaper_cell: font.shape.Cell,
|
||||
shaper_run: font.shape.TextRun,
|
||||
x: usize,
|
||||
@ -1684,14 +1690,30 @@ pub fn updateCell(
|
||||
const cell_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 = if (cell.attrs.has_bg) cell.bg else null,
|
||||
.fg = if (cell.attrs.has_fg) cell.fg else self.foreground_color,
|
||||
.bg = switch (cell.bg) {
|
||||
.none => null,
|
||||
.indexed => |i| palette[i],
|
||||
.rgb => |rgb| rgb,
|
||||
},
|
||||
.fg = switch (cell.fg) {
|
||||
.none => self.foreground_color,
|
||||
.indexed => |i| palette[i],
|
||||
.rgb => |rgb| rgb,
|
||||
},
|
||||
} else .{
|
||||
// In inverted mode, the background MUST be set to something
|
||||
// (is never null) so it is either the fg or default fg. The
|
||||
// fg is either the bg or default background.
|
||||
.bg = if (cell.attrs.has_fg) cell.fg else self.foreground_color,
|
||||
.fg = if (cell.attrs.has_bg) cell.bg else self.background_color,
|
||||
.bg = switch (cell.fg) {
|
||||
.none => self.foreground_color,
|
||||
.indexed => |i| palette[i],
|
||||
.rgb => |rgb| rgb,
|
||||
},
|
||||
.fg = switch (cell.bg) {
|
||||
.none => self.background_color,
|
||||
.indexed => |i| palette[i],
|
||||
.rgb => |rgb| rgb,
|
||||
},
|
||||
};
|
||||
|
||||
// If we are selected, we our colors are just inverted fg/bg
|
||||
@ -1741,7 +1763,7 @@ pub fn updateCell(
|
||||
|
||||
// If we have a background and its not the default background
|
||||
// then we apply background opacity
|
||||
if (cell.attrs.has_bg and !std.meta.eql(rgb, self.background_color)) {
|
||||
if (cell.bg != .none and !rgb.eql(self.background_color)) {
|
||||
break :bg_alpha default;
|
||||
}
|
||||
|
||||
|
@ -649,6 +649,7 @@ pub fn updateFrame(
|
||||
mouse: renderer.State.Mouse,
|
||||
preedit: ?renderer.State.Preedit,
|
||||
cursor_style: ?renderer.CursorStyle,
|
||||
color_palette: terminal.color.Palette,
|
||||
};
|
||||
|
||||
// Update all our data as tightly as possible within the mutex.
|
||||
@ -730,6 +731,7 @@ pub fn updateFrame(
|
||||
.mouse = state.mouse,
|
||||
.preedit = preedit,
|
||||
.cursor_style = cursor_style,
|
||||
.color_palette = state.terminal.color_palette.colors,
|
||||
};
|
||||
};
|
||||
defer {
|
||||
@ -752,6 +754,7 @@ pub fn updateFrame(
|
||||
critical.mouse,
|
||||
critical.preedit,
|
||||
critical.cursor_style,
|
||||
&critical.color_palette,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -943,6 +946,7 @@ pub fn rebuildCells(
|
||||
mouse: renderer.State.Mouse,
|
||||
preedit: ?renderer.State.Preedit,
|
||||
cursor_style_: ?renderer.CursorStyle,
|
||||
color_palette: *const terminal.color.Palette,
|
||||
) !void {
|
||||
const t = trace(@src());
|
||||
defer t.end();
|
||||
@ -1097,6 +1101,7 @@ pub fn rebuildCells(
|
||||
term_selection,
|
||||
screen,
|
||||
cell,
|
||||
color_palette,
|
||||
shaper_cell,
|
||||
run,
|
||||
shaper_cell.x,
|
||||
@ -1330,11 +1335,12 @@ fn addCursor(
|
||||
/// Update a single cell. The bool returns whether the cell was updated
|
||||
/// or not. If the cell wasn't updated, a full refreshCells call is
|
||||
/// needed.
|
||||
pub fn updateCell(
|
||||
fn updateCell(
|
||||
self: *OpenGL,
|
||||
selection: ?terminal.Selection,
|
||||
screen: *terminal.Screen,
|
||||
cell: terminal.Screen.Cell,
|
||||
palette: *const terminal.color.Palette,
|
||||
shaper_cell: font.shape.Cell,
|
||||
shaper_run: font.shape.TextRun,
|
||||
x: usize,
|
||||
@ -1373,14 +1379,30 @@ pub fn updateCell(
|
||||
const cell_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 = if (cell.attrs.has_bg) cell.bg else null,
|
||||
.fg = if (cell.attrs.has_fg) cell.fg else self.foreground_color,
|
||||
.bg = switch (cell.bg) {
|
||||
.none => null,
|
||||
.indexed => |i| palette[i],
|
||||
.rgb => |rgb| rgb,
|
||||
},
|
||||
.fg = switch (cell.fg) {
|
||||
.none => self.foreground_color,
|
||||
.indexed => |i| palette[i],
|
||||
.rgb => |rgb| rgb,
|
||||
},
|
||||
} else .{
|
||||
// In inverted mode, the background MUST be set to something
|
||||
// (is never null) so it is either the fg or default fg. The
|
||||
// fg is either the bg or default background.
|
||||
.bg = if (cell.attrs.has_fg) cell.fg else self.foreground_color,
|
||||
.fg = if (cell.attrs.has_bg) cell.bg else self.background_color,
|
||||
.bg = switch (cell.fg) {
|
||||
.none => self.foreground_color,
|
||||
.indexed => |i| palette[i],
|
||||
.rgb => |rgb| rgb,
|
||||
},
|
||||
.fg = switch (cell.bg) {
|
||||
.none => self.background_color,
|
||||
.indexed => |i| palette[i],
|
||||
.rgb => |rgb| rgb,
|
||||
},
|
||||
};
|
||||
|
||||
// If we are selected, we our colors are just inverted fg/bg
|
||||
@ -1441,7 +1463,7 @@ pub fn updateCell(
|
||||
|
||||
// If we have a background and its not the default background
|
||||
// then we apply background opacity
|
||||
if (cell.attrs.has_bg and !std.meta.eql(rgb, self.background_color)) {
|
||||
if (cell.bg != .none and !rgb.eql(self.background_color)) {
|
||||
break :bg_alpha default;
|
||||
}
|
||||
|
||||
|
@ -211,6 +211,27 @@ pub const RowHeader = struct {
|
||||
};
|
||||
};
|
||||
|
||||
/// The color associated with a single cell's foreground or background.
|
||||
const CellColor = union(enum) {
|
||||
none,
|
||||
indexed: u8,
|
||||
rgb: color.RGB,
|
||||
|
||||
pub fn eql(self: CellColor, other: CellColor) bool {
|
||||
return switch (self) {
|
||||
.none => other == .none,
|
||||
.indexed => |i| switch (other) {
|
||||
.indexed => other.indexed == i,
|
||||
else => false,
|
||||
},
|
||||
.rgb => |rgb| switch (other) {
|
||||
.rgb => other.rgb.eql(rgb),
|
||||
else => false,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// Cell is a single cell within the screen.
|
||||
pub const Cell = struct {
|
||||
/// The primary unicode codepoint for this cell. Most cells (almost all)
|
||||
@ -224,10 +245,9 @@ pub const Cell = struct {
|
||||
/// waste memory for every cell, so we use a side lookup for it.
|
||||
char: u32 = 0,
|
||||
|
||||
/// Foreground and background color. attrs.has_{bg/fg} must be checked
|
||||
/// to see if these are useful values.
|
||||
fg: color.RGB = .{},
|
||||
bg: color.RGB = .{},
|
||||
/// Foreground and background color.
|
||||
fg: CellColor = .none,
|
||||
bg: CellColor = .none,
|
||||
|
||||
/// Underline color.
|
||||
/// NOTE(mitchellh): This is very rarely set so ideally we wouldn't waste
|
||||
@ -237,9 +257,6 @@ pub const Cell = struct {
|
||||
|
||||
/// On/off attributes that can be set
|
||||
attrs: packed struct {
|
||||
has_bg: bool = false,
|
||||
has_fg: bool = false,
|
||||
|
||||
bold: bool = false,
|
||||
italic: bool = false,
|
||||
faint: bool = false,
|
||||
@ -286,7 +303,10 @@ pub const Cell = struct {
|
||||
} });
|
||||
|
||||
// We're empty if we have no char AND we have no styling
|
||||
return self.char == 0 and @as(AttrInt, @bitCast(self.attrs)) == 0;
|
||||
return self.char == 0 and
|
||||
self.fg == .none and
|
||||
self.bg == .none and
|
||||
@as(AttrInt, @bitCast(self.attrs)) == 0;
|
||||
}
|
||||
|
||||
/// The width of the cell.
|
||||
@ -1297,9 +1317,9 @@ pub fn scrollRegionUp(self: *Screen, top: RowIndex, bottom: RowIndex, count_req:
|
||||
|
||||
// The pen we'll use for new cells (only the BG attribute is applied to new
|
||||
// cells)
|
||||
const pen: Cell = if (!self.cursor.pen.attrs.has_bg) .{} else .{
|
||||
.bg = self.cursor.pen.bg,
|
||||
.attrs = .{ .has_bg = true },
|
||||
const pen: Cell = switch (self.cursor.pen.bg) {
|
||||
.none => .{},
|
||||
else => |bg| .{ .bg = bg },
|
||||
};
|
||||
|
||||
// Fast-path is that we have a contiguous buffer in our circular buffer.
|
||||
@ -2190,9 +2210,9 @@ fn scrollDelta(self: *Screen, delta: isize, viewport_only: bool) Allocator.Error
|
||||
const dst = slices[0];
|
||||
// The pen we'll use for new cells (only the BG attribute is applied to new
|
||||
// cells)
|
||||
const pen: Cell = if (!self.cursor.pen.attrs.has_bg) .{} else .{
|
||||
.bg = self.cursor.pen.bg,
|
||||
.attrs = .{ .has_bg = true },
|
||||
const pen: Cell = switch (self.cursor.pen.bg) {
|
||||
.none => .{},
|
||||
else => |bg| .{ .bg = bg },
|
||||
};
|
||||
@memset(dst, .{ .cell = pen });
|
||||
|
||||
@ -3488,8 +3508,7 @@ test "Row: isEmpty with only styled cells" {
|
||||
const row = s.getRow(.{ .active = 0 });
|
||||
for (0..s.cols) |x| {
|
||||
const cell = row.getCellPtr(x);
|
||||
cell.*.bg = .{ .r = 0xAA, .g = 0xBB, .b = 0xCC };
|
||||
cell.*.attrs.has_bg = true;
|
||||
cell.*.bg = .{ .rgb = .{ .r = 0xAA, .g = 0xBB, .b = 0xCC } };
|
||||
}
|
||||
try testing.expect(row.isEmpty());
|
||||
}
|
||||
@ -3763,8 +3782,7 @@ test "Screen: scrolling" {
|
||||
|
||||
var s = try init(alloc, 3, 5, 0);
|
||||
defer s.deinit();
|
||||
s.cursor.pen.bg = .{ .r = 155 };
|
||||
s.cursor.pen.attrs.has_bg = true;
|
||||
s.cursor.pen.bg = .{ .rgb = .{ .r = 155 } };
|
||||
try s.testWriteString("1ABCD\n2EFGH\n3IJKL");
|
||||
try testing.expect(s.viewportIsBottom());
|
||||
|
||||
@ -3781,7 +3799,7 @@ test "Screen: scrolling" {
|
||||
{
|
||||
// Test that our new row has the correct background
|
||||
const cell = s.getCell(.active, 2, 0);
|
||||
try testing.expectEqual(@as(u8, 155), cell.bg.r);
|
||||
try testing.expectEqual(@as(u8, 155), cell.bg.rgb.r);
|
||||
}
|
||||
|
||||
// Scrolling to the bottom does nothing
|
||||
@ -5095,8 +5113,7 @@ test "Screen: scrollRegionUp single with pen" {
|
||||
try s.testWriteString("1ABCD\n2EFGH\n3IJKL\n4ABCD");
|
||||
|
||||
s.cursor.pen = .{ .char = 'X' };
|
||||
s.cursor.pen.bg = .{ .r = 155 };
|
||||
s.cursor.pen.attrs.has_bg = true;
|
||||
s.cursor.pen.bg = .{ .rgb = .{ .r = 155 } };
|
||||
s.cursor.pen.attrs.bold = true;
|
||||
s.scrollRegionUp(.{ .active = 1 }, .{ .active = 2 }, 1);
|
||||
{
|
||||
@ -5105,7 +5122,7 @@ test "Screen: scrollRegionUp single with pen" {
|
||||
defer alloc.free(contents);
|
||||
try testing.expectEqualStrings("1ABCD\n3IJKL\n\n4ABCD", contents);
|
||||
const cell = s.getCell(.active, 2, 0);
|
||||
try testing.expectEqual(@as(u8, 155), cell.bg.r);
|
||||
try testing.expectEqual(@as(u8, 155), cell.bg.rgb.r);
|
||||
try testing.expect(!cell.attrs.bold);
|
||||
try testing.expect(s.cursor.pen.attrs.bold);
|
||||
}
|
||||
@ -5170,8 +5187,7 @@ test "Screen: scrollRegionUp fills with pen" {
|
||||
try s.testWriteString("A\nB\nC\nD");
|
||||
|
||||
s.cursor.pen = .{ .char = 'X' };
|
||||
s.cursor.pen.bg = .{ .r = 155 };
|
||||
s.cursor.pen.attrs.has_bg = true;
|
||||
s.cursor.pen.bg = .{ .rgb = .{ .r = 155 } };
|
||||
s.cursor.pen.attrs.bold = true;
|
||||
s.scrollRegionUp(.{ .active = 0 }, .{ .active = 2 }, 1);
|
||||
{
|
||||
@ -5180,7 +5196,7 @@ test "Screen: scrollRegionUp fills with pen" {
|
||||
defer alloc.free(contents);
|
||||
try testing.expectEqualStrings("B\nC\n\nD", contents);
|
||||
const cell = s.getCell(.active, 2, 0);
|
||||
try testing.expectEqual(@as(u8, 155), cell.bg.r);
|
||||
try testing.expectEqual(@as(u8, 155), cell.bg.rgb.r);
|
||||
try testing.expect(!cell.attrs.bold);
|
||||
try testing.expect(s.cursor.pen.attrs.bold);
|
||||
}
|
||||
@ -5202,8 +5218,7 @@ test "Screen: scrollRegionUp buffer wrap" {
|
||||
|
||||
// Scroll
|
||||
s.cursor.pen = .{ .char = 'X' };
|
||||
s.cursor.pen.bg = .{ .r = 155 };
|
||||
s.cursor.pen.attrs.has_bg = true;
|
||||
s.cursor.pen.bg = .{ .rgb = .{ .r = 155 } };
|
||||
s.cursor.pen.attrs.bold = true;
|
||||
s.scrollRegionUp(.{ .screen = 0 }, .{ .screen = 2 }, 1);
|
||||
|
||||
@ -5213,7 +5228,7 @@ test "Screen: scrollRegionUp buffer wrap" {
|
||||
defer alloc.free(contents);
|
||||
try testing.expectEqualStrings("3IJKL\n4ABCD", contents);
|
||||
const cell = s.getCell(.active, 2, 0);
|
||||
try testing.expectEqual(@as(u8, 155), cell.bg.r);
|
||||
try testing.expectEqual(@as(u8, 155), cell.bg.rgb.r);
|
||||
try testing.expect(!cell.attrs.bold);
|
||||
try testing.expect(s.cursor.pen.attrs.bold);
|
||||
}
|
||||
@ -5235,8 +5250,7 @@ test "Screen: scrollRegionUp buffer wrap alternate" {
|
||||
|
||||
// Scroll
|
||||
s.cursor.pen = .{ .char = 'X' };
|
||||
s.cursor.pen.bg = .{ .r = 155 };
|
||||
s.cursor.pen.attrs.has_bg = true;
|
||||
s.cursor.pen.bg = .{ .rgb = .{ .r = 155 } };
|
||||
s.cursor.pen.attrs.bold = true;
|
||||
s.scrollRegionUp(.{ .screen = 0 }, .{ .screen = 2 }, 2);
|
||||
|
||||
@ -5246,7 +5260,7 @@ test "Screen: scrollRegionUp buffer wrap alternate" {
|
||||
defer alloc.free(contents);
|
||||
try testing.expectEqualStrings("4ABCD", contents);
|
||||
const cell = s.getCell(.active, 2, 0);
|
||||
try testing.expectEqual(@as(u8, 155), cell.bg.r);
|
||||
try testing.expectEqual(@as(u8, 155), cell.bg.rgb.r);
|
||||
try testing.expect(!cell.attrs.bold);
|
||||
try testing.expect(s.cursor.pen.attrs.bold);
|
||||
}
|
||||
@ -5964,8 +5978,7 @@ test "Screen: resize (no reflow) less rows trims blank lines" {
|
||||
const row = s.getRow(.{ .active = y });
|
||||
for (0..s.cols) |x| {
|
||||
const cell = row.getCellPtr(x);
|
||||
cell.*.bg = .{ .r = 0xFF, .g = 0, .b = 0 };
|
||||
cell.*.attrs.has_bg = true;
|
||||
cell.*.bg = .{ .rgb = .{ .r = 0xFF, .g = 0, .b = 0 } };
|
||||
}
|
||||
}
|
||||
|
||||
@ -6000,8 +6013,7 @@ test "Screen: resize (no reflow) more rows trims blank lines" {
|
||||
const row = s.getRow(.{ .active = y });
|
||||
for (0..s.cols) |x| {
|
||||
const cell = row.getCellPtr(x);
|
||||
cell.*.bg = .{ .r = 0xFF, .g = 0, .b = 0 };
|
||||
cell.*.attrs.has_bg = true;
|
||||
cell.*.bg = .{ .rgb = .{ .r = 0xFF, .g = 0, .b = 0 } };
|
||||
}
|
||||
}
|
||||
|
||||
@ -6934,7 +6946,7 @@ test "Screen: resize less cols trailing background colors" {
|
||||
const cursor = s.cursor;
|
||||
|
||||
// Color our cells red
|
||||
const pen: Cell = .{ .bg = .{ .r = 0xFF }, .attrs = .{ .has_bg = true } };
|
||||
const pen: Cell = .{ .bg = .{ .rgb = .{ .r = 0xFF } } };
|
||||
for (s.cursor.x..s.cols) |x| {
|
||||
const row = s.getRow(.{ .active = s.cursor.y });
|
||||
const cell = row.getCellPtr(x);
|
||||
|
@ -474,8 +474,8 @@ pub fn setAttribute(self: *Terminal, attr: sgr.Attribute) !void {
|
||||
|
||||
switch (attr) {
|
||||
.unset => {
|
||||
self.screen.cursor.pen.attrs.has_fg = false;
|
||||
self.screen.cursor.pen.attrs.has_bg = false;
|
||||
self.screen.cursor.pen.fg = .none;
|
||||
self.screen.cursor.pen.bg = .none;
|
||||
self.screen.cursor.pen.attrs = .{};
|
||||
},
|
||||
|
||||
@ -561,55 +561,51 @@ pub fn setAttribute(self: *Terminal, attr: sgr.Attribute) !void {
|
||||
},
|
||||
|
||||
.direct_color_fg => |rgb| {
|
||||
self.screen.cursor.pen.attrs.has_fg = true;
|
||||
self.screen.cursor.pen.fg = .{
|
||||
.r = rgb.r,
|
||||
.g = rgb.g,
|
||||
.b = rgb.b,
|
||||
.rgb = .{
|
||||
.r = rgb.r,
|
||||
.g = rgb.g,
|
||||
.b = rgb.b,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
.direct_color_bg => |rgb| {
|
||||
self.screen.cursor.pen.attrs.has_bg = true;
|
||||
self.screen.cursor.pen.bg = .{
|
||||
.r = rgb.r,
|
||||
.g = rgb.g,
|
||||
.b = rgb.b,
|
||||
.rgb = .{
|
||||
.r = rgb.r,
|
||||
.g = rgb.g,
|
||||
.b = rgb.b,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
.@"8_fg" => |n| {
|
||||
self.screen.cursor.pen.attrs.has_fg = true;
|
||||
self.screen.cursor.pen.fg = self.color_palette.colors[@intFromEnum(n)];
|
||||
self.screen.cursor.pen.fg = .{ .indexed = @intFromEnum(n) };
|
||||
},
|
||||
|
||||
.@"8_bg" => |n| {
|
||||
self.screen.cursor.pen.attrs.has_bg = true;
|
||||
self.screen.cursor.pen.bg = self.color_palette.colors[@intFromEnum(n)];
|
||||
self.screen.cursor.pen.bg = .{ .indexed = @intFromEnum(n) };
|
||||
},
|
||||
|
||||
.reset_fg => self.screen.cursor.pen.attrs.has_fg = false,
|
||||
.reset_fg => self.screen.cursor.pen.fg = .none,
|
||||
|
||||
.reset_bg => self.screen.cursor.pen.attrs.has_bg = false,
|
||||
.reset_bg => self.screen.cursor.pen.bg = .none,
|
||||
|
||||
.@"8_bright_fg" => |n| {
|
||||
self.screen.cursor.pen.attrs.has_fg = true;
|
||||
self.screen.cursor.pen.fg = self.color_palette.colors[@intFromEnum(n)];
|
||||
self.screen.cursor.pen.fg = .{ .indexed = @intFromEnum(n) };
|
||||
},
|
||||
|
||||
.@"8_bright_bg" => |n| {
|
||||
self.screen.cursor.pen.attrs.has_bg = true;
|
||||
self.screen.cursor.pen.bg = self.color_palette.colors[@intFromEnum(n)];
|
||||
self.screen.cursor.pen.bg = .{ .indexed = @intFromEnum(n) };
|
||||
},
|
||||
|
||||
.@"256_fg" => |idx| {
|
||||
self.screen.cursor.pen.attrs.has_fg = true;
|
||||
self.screen.cursor.pen.fg = self.color_palette.colors[idx];
|
||||
self.screen.cursor.pen.fg = .{ .indexed = idx };
|
||||
},
|
||||
|
||||
.@"256_bg" => |idx| {
|
||||
self.screen.cursor.pen.attrs.has_bg = true;
|
||||
self.screen.cursor.pen.bg = self.color_palette.colors[idx];
|
||||
self.screen.cursor.pen.bg = .{ .indexed = idx };
|
||||
},
|
||||
|
||||
.unknown => return error.InvalidAttribute,
|
||||
@ -676,12 +672,26 @@ pub fn printAttributes(self: *Terminal, buf: []u8) ![]const u8 {
|
||||
try writer.print(";{c}", .{c});
|
||||
}
|
||||
|
||||
if (pen.attrs.has_fg) {
|
||||
try writer.print(";38:2::{[r]}:{[g]}:{[b]}", pen.fg);
|
||||
switch (pen.fg) {
|
||||
.none => {},
|
||||
.indexed => |idx| if (idx >= 16)
|
||||
try writer.print(";38:5:{}", .{idx})
|
||||
else if (idx >= 8)
|
||||
try writer.print(";9{}", .{idx - 8})
|
||||
else
|
||||
try writer.print(";3{}", .{idx}),
|
||||
.rgb => |rgb| try writer.print(";38:2::{[r]}:{[g]}:{[b]}", rgb),
|
||||
}
|
||||
|
||||
if (pen.attrs.has_bg) {
|
||||
try writer.print(";48:2::{[r]}:{[g]}:{[b]}", pen.bg);
|
||||
switch (pen.bg) {
|
||||
.none => {},
|
||||
.indexed => |idx| if (idx >= 16)
|
||||
try writer.print(";48:5:{}", .{idx})
|
||||
else if (idx >= 8)
|
||||
try writer.print(";10{}", .{idx - 8})
|
||||
else
|
||||
try writer.print(";4{}", .{idx}),
|
||||
.rgb => |rgb| try writer.print(";48:2::{[r]}:{[g]}:{[b]}", rgb),
|
||||
}
|
||||
|
||||
return stream.getWritten();
|
||||
@ -1080,8 +1090,6 @@ pub fn decaln(self: *Terminal) !void {
|
||||
.bg = self.screen.cursor.pen.bg,
|
||||
.fg = self.screen.cursor.pen.fg,
|
||||
.attrs = .{
|
||||
.has_bg = self.screen.cursor.pen.attrs.has_bg,
|
||||
.has_fg = self.screen.cursor.pen.attrs.has_fg,
|
||||
.protected = self.screen.cursor.pen.attrs.protected,
|
||||
},
|
||||
};
|
||||
@ -1229,9 +1237,9 @@ pub fn eraseDisplay(
|
||||
defer tracy.end();
|
||||
|
||||
// Erasing clears all attributes / colors _except_ the background
|
||||
const pen: Screen.Cell = if (!self.screen.cursor.pen.attrs.has_bg) .{} else .{
|
||||
.bg = self.screen.cursor.pen.bg,
|
||||
.attrs = .{ .has_bg = true },
|
||||
const pen: Screen.Cell = switch (self.screen.cursor.pen.bg) {
|
||||
.none => .{},
|
||||
else => |bg| .{ .bg = bg },
|
||||
};
|
||||
|
||||
// We respect protected attributes if explicitly requested (probably
|
||||
@ -1380,9 +1388,9 @@ pub fn eraseLine(
|
||||
defer tracy.end();
|
||||
|
||||
// We always fill with the background
|
||||
const pen: Screen.Cell = if (!self.screen.cursor.pen.attrs.has_bg) .{} else .{
|
||||
.bg = self.screen.cursor.pen.bg,
|
||||
.attrs = .{ .has_bg = true },
|
||||
const pen: Screen.Cell = switch (self.screen.cursor.pen.bg) {
|
||||
.none => .{},
|
||||
else => |bg| .{ .bg = bg },
|
||||
};
|
||||
|
||||
// Get our start/end positions depending on mode.
|
||||
@ -1470,7 +1478,6 @@ pub fn deleteChars(self: *Terminal, count: usize) !void {
|
||||
|
||||
const pen: Screen.Cell = .{
|
||||
.bg = self.screen.cursor.pen.bg,
|
||||
.attrs = .{ .has_bg = self.screen.cursor.pen.attrs.has_bg },
|
||||
};
|
||||
|
||||
// If our X is a wide spacer tail then we need to erase the
|
||||
@ -1529,7 +1536,6 @@ pub fn eraseChars(self: *Terminal, count_req: usize) void {
|
||||
|
||||
const pen: Screen.Cell = .{
|
||||
.bg = self.screen.cursor.pen.bg,
|
||||
.attrs = .{ .has_bg = self.screen.cursor.pen.attrs.has_bg },
|
||||
};
|
||||
|
||||
// If we never had a protection mode, then we can assume no cells
|
||||
@ -1873,7 +1879,6 @@ pub fn insertBlanks(self: *Terminal, count: usize) void {
|
||||
// Insert blanks. The blanks preserve the background color.
|
||||
row.fillSlice(.{
|
||||
.bg = self.screen.cursor.pen.bg,
|
||||
.attrs = .{ .has_bg = self.screen.cursor.pen.attrs.has_bg },
|
||||
}, start, pivot);
|
||||
}
|
||||
|
||||
@ -1939,7 +1944,6 @@ pub fn insertLines(self: *Terminal, count: usize) !void {
|
||||
const row = self.screen.getRow(.{ .active = y });
|
||||
row.fillSlice(.{
|
||||
.bg = self.screen.cursor.pen.bg,
|
||||
.attrs = .{ .has_bg = self.screen.cursor.pen.attrs.has_bg },
|
||||
}, self.scrolling_region.left, self.scrolling_region.right + 1);
|
||||
}
|
||||
}
|
||||
@ -2014,7 +2018,6 @@ pub fn deleteLines(self: *Terminal, count: usize) !void {
|
||||
row.setWrapped(false);
|
||||
row.fillSlice(.{
|
||||
.bg = self.screen.cursor.pen.bg,
|
||||
.attrs = .{ .has_bg = self.screen.cursor.pen.attrs.has_bg },
|
||||
}, self.scrolling_region.left, self.scrolling_region.right + 1);
|
||||
}
|
||||
}
|
||||
@ -2247,13 +2250,13 @@ test "Terminal: fullReset with a non-empty pen" {
|
||||
var t = try init(testing.allocator, 80, 80);
|
||||
defer t.deinit(testing.allocator);
|
||||
|
||||
t.screen.cursor.pen.bg = .{ .r = 0xFF, .g = 0x00, .b = 0x7F };
|
||||
t.screen.cursor.pen.fg = .{ .r = 0xFF, .g = 0x00, .b = 0x7F };
|
||||
t.screen.cursor.pen.bg = .{ .rgb = .{ .r = 0xFF, .g = 0x00, .b = 0x7F } };
|
||||
t.screen.cursor.pen.fg = .{ .rgb = .{ .r = 0xFF, .g = 0x00, .b = 0x7F } };
|
||||
t.fullReset(testing.allocator);
|
||||
|
||||
const cell = t.screen.getCell(.active, t.screen.cursor.y, t.screen.cursor.x);
|
||||
try testing.expect(cell.bg.eql(.{}));
|
||||
try testing.expect(cell.fg.eql(.{}));
|
||||
try testing.expect(cell.bg == .none);
|
||||
try testing.expect(cell.fg == .none);
|
||||
}
|
||||
|
||||
test "Terminal: fullReset origin mode" {
|
||||
@ -4165,8 +4168,7 @@ test "Terminal: index bottom of primary screen background sgr" {
|
||||
defer t.deinit(alloc);
|
||||
|
||||
const pen: Screen.Cell = .{
|
||||
.bg = .{ .r = 0xFF, .g = 0x00, .b = 0x00 },
|
||||
.attrs = .{ .has_bg = true },
|
||||
.bg = .{ .rgb = .{ .r = 0xFF, .g = 0x00, .b = 0x00 } },
|
||||
};
|
||||
|
||||
t.setCursorPos(5, 1);
|
||||
@ -4338,8 +4340,7 @@ test "Terminal: decaln preserves color" {
|
||||
defer t.deinit(alloc);
|
||||
|
||||
const pen: Screen.Cell = .{
|
||||
.bg = .{ .r = 0xFF, .g = 0x00, .b = 0x00 },
|
||||
.attrs = .{ .has_bg = true },
|
||||
.bg = .{ .rgb = .{ .r = 0xFF, .g = 0x00, .b = 0x00 } },
|
||||
};
|
||||
|
||||
// Initial value
|
||||
@ -4443,8 +4444,7 @@ test "Terminal: insertBlanks preserves background sgr" {
|
||||
defer t.deinit(alloc);
|
||||
|
||||
const pen: Screen.Cell = .{
|
||||
.bg = .{ .r = 0xFF, .g = 0x00, .b = 0x00 },
|
||||
.attrs = .{ .has_bg = true },
|
||||
.bg = .{ .rgb = .{ .r = 0xFF, .g = 0x00, .b = 0x00 } },
|
||||
};
|
||||
|
||||
for ("ABC") |c| try t.print(c);
|
||||
@ -4836,8 +4836,7 @@ test "Terminal: deleteChars background sgr" {
|
||||
defer t.deinit(alloc);
|
||||
|
||||
const pen: Screen.Cell = .{
|
||||
.bg = .{ .r = 0xFF, .g = 0x00, .b = 0x00 },
|
||||
.attrs = .{ .has_bg = true },
|
||||
.bg = .{ .rgb = .{ .r = 0xFF, .g = 0x00, .b = 0x00 } },
|
||||
};
|
||||
|
||||
try t.printString("ABC123");
|
||||
@ -5001,8 +5000,7 @@ test "Terminal: eraseChars preserves background sgr" {
|
||||
defer t.deinit(alloc);
|
||||
|
||||
const pen: Screen.Cell = .{
|
||||
.bg = .{ .r = 0xFF, .g = 0x00, .b = 0x00 },
|
||||
.attrs = .{ .has_bg = true },
|
||||
.bg = .{ .rgb = .{ .r = 0xFF, .g = 0x00, .b = 0x00 } },
|
||||
};
|
||||
|
||||
for ("ABC") |c| try t.print(c);
|
||||
@ -5312,8 +5310,7 @@ test "Terminal: eraseLine right preserves background sgr" {
|
||||
defer t.deinit(alloc);
|
||||
|
||||
const pen: Screen.Cell = .{
|
||||
.bg = .{ .r = 0xFF, .g = 0x00, .b = 0x00 },
|
||||
.attrs = .{ .has_bg = true },
|
||||
.bg = .{ .rgb = .{ .r = 0xFF, .g = 0x00, .b = 0x00 } },
|
||||
};
|
||||
|
||||
for ("ABCDE") |c| try t.print(c);
|
||||
@ -5462,8 +5459,7 @@ test "Terminal: eraseLine left preserves background sgr" {
|
||||
defer t.deinit(alloc);
|
||||
|
||||
const pen: Screen.Cell = .{
|
||||
.bg = .{ .r = 0xFF, .g = 0x00, .b = 0x00 },
|
||||
.attrs = .{ .has_bg = true },
|
||||
.bg = .{ .rgb = .{ .r = 0xFF, .g = 0x00, .b = 0x00 } },
|
||||
};
|
||||
|
||||
for ("ABCDE") |c| try t.print(c);
|
||||
@ -5578,8 +5574,7 @@ test "Terminal: eraseLine complete preserves background sgr" {
|
||||
defer t.deinit(alloc);
|
||||
|
||||
const pen: Screen.Cell = .{
|
||||
.bg = .{ .r = 0xFF, .g = 0x00, .b = 0x00 },
|
||||
.attrs = .{ .has_bg = true },
|
||||
.bg = .{ .rgb = .{ .r = 0xFF, .g = 0x00, .b = 0x00 } },
|
||||
};
|
||||
|
||||
for ("ABCDE") |c| try t.print(c);
|
||||
@ -5707,8 +5702,7 @@ test "Terminal: eraseDisplay erase below preserves SGR bg" {
|
||||
t.setCursorPos(2, 2);
|
||||
|
||||
const pen: Screen.Cell = .{
|
||||
.bg = .{ .r = 0xFF, .g = 0x00, .b = 0x00 },
|
||||
.attrs = .{ .has_bg = true },
|
||||
.bg = .{ .rgb = .{ .r = 0xFF, .g = 0x00, .b = 0x00 } },
|
||||
};
|
||||
|
||||
t.screen.cursor.pen = pen;
|
||||
@ -5878,8 +5872,7 @@ test "Terminal: eraseDisplay erase above preserves SGR bg" {
|
||||
t.setCursorPos(2, 2);
|
||||
|
||||
const pen: Screen.Cell = .{
|
||||
.bg = .{ .r = 0xFF, .g = 0x00, .b = 0x00 },
|
||||
.attrs = .{ .has_bg = true },
|
||||
.bg = .{ .rgb = .{ .r = 0xFF, .g = 0x00, .b = 0x00 } },
|
||||
};
|
||||
|
||||
t.screen.cursor.pen = pen;
|
||||
@ -6018,16 +6011,16 @@ test "Terminal: eraseDisplay above" {
|
||||
const pink = color.RGB{ .r = 0xFF, .g = 0x00, .b = 0x7F };
|
||||
t.screen.cursor.pen = Screen.Cell{
|
||||
.char = 'a',
|
||||
.bg = pink,
|
||||
.fg = pink,
|
||||
.attrs = .{ .bold = true, .has_bg = true },
|
||||
.bg = .{ .rgb = pink },
|
||||
.fg = .{ .rgb = pink },
|
||||
.attrs = .{ .bold = true },
|
||||
};
|
||||
const cell_ptr = t.screen.getCellPtr(.active, 0, 0);
|
||||
cell_ptr.* = t.screen.cursor.pen;
|
||||
// verify the cell was set
|
||||
var cell = t.screen.getCell(.active, 0, 0);
|
||||
try testing.expect(cell.bg.eql(pink));
|
||||
try testing.expect(cell.fg.eql(pink));
|
||||
try testing.expect(cell.bg.rgb.eql(pink));
|
||||
try testing.expect(cell.fg.rgb.eql(pink));
|
||||
try testing.expect(cell.char == 'a');
|
||||
try testing.expect(cell.attrs.bold);
|
||||
// move the cursor below it
|
||||
@ -6037,18 +6030,17 @@ test "Terminal: eraseDisplay above" {
|
||||
t.eraseDisplay(testing.allocator, .above, false);
|
||||
// check it was erased
|
||||
cell = t.screen.getCell(.active, 0, 0);
|
||||
try testing.expect(cell.bg.eql(pink));
|
||||
try testing.expect(cell.fg.eql(.{}));
|
||||
try testing.expect(cell.bg.rgb.eql(pink));
|
||||
try testing.expect(cell.fg == .none);
|
||||
try testing.expect(cell.char == 0);
|
||||
try testing.expect(!cell.attrs.bold);
|
||||
try testing.expect(cell.attrs.has_bg);
|
||||
|
||||
// Check that our pen hasn't changed
|
||||
try testing.expect(t.screen.cursor.pen.attrs.bold);
|
||||
|
||||
// check that another cell got the correct bg
|
||||
cell = t.screen.getCell(.active, 0, 1);
|
||||
try testing.expect(cell.bg.eql(pink));
|
||||
try testing.expect(cell.bg.rgb.eql(pink));
|
||||
}
|
||||
|
||||
test "Terminal: eraseDisplay below" {
|
||||
@ -6058,31 +6050,30 @@ test "Terminal: eraseDisplay below" {
|
||||
const pink = color.RGB{ .r = 0xFF, .g = 0x00, .b = 0x7F };
|
||||
t.screen.cursor.pen = Screen.Cell{
|
||||
.char = 'a',
|
||||
.bg = pink,
|
||||
.fg = pink,
|
||||
.attrs = .{ .bold = true, .has_bg = true },
|
||||
.bg = .{ .rgb = pink },
|
||||
.fg = .{ .rgb = pink },
|
||||
.attrs = .{ .bold = true },
|
||||
};
|
||||
const cell_ptr = t.screen.getCellPtr(.active, 60, 60);
|
||||
cell_ptr.* = t.screen.cursor.pen;
|
||||
// verify the cell was set
|
||||
var cell = t.screen.getCell(.active, 60, 60);
|
||||
try testing.expect(cell.bg.eql(pink));
|
||||
try testing.expect(cell.fg.eql(pink));
|
||||
try testing.expect(cell.bg.rgb.eql(pink));
|
||||
try testing.expect(cell.fg.rgb.eql(pink));
|
||||
try testing.expect(cell.char == 'a');
|
||||
try testing.expect(cell.attrs.bold);
|
||||
// erase below the cursor
|
||||
t.eraseDisplay(testing.allocator, .below, false);
|
||||
// check it was erased
|
||||
cell = t.screen.getCell(.active, 60, 60);
|
||||
try testing.expect(cell.bg.eql(pink));
|
||||
try testing.expect(cell.fg.eql(.{}));
|
||||
try testing.expect(cell.bg.rgb.eql(pink));
|
||||
try testing.expect(cell.fg == .none);
|
||||
try testing.expect(cell.char == 0);
|
||||
try testing.expect(!cell.attrs.bold);
|
||||
try testing.expect(cell.attrs.has_bg);
|
||||
|
||||
// check that another cell got the correct bg
|
||||
cell = t.screen.getCell(.active, 0, 1);
|
||||
try testing.expect(cell.bg.eql(pink));
|
||||
try testing.expect(cell.bg.rgb.eql(pink));
|
||||
}
|
||||
|
||||
test "Terminal: eraseDisplay complete" {
|
||||
@ -6092,9 +6083,9 @@ test "Terminal: eraseDisplay complete" {
|
||||
const pink = color.RGB{ .r = 0xFF, .g = 0x00, .b = 0x7F };
|
||||
t.screen.cursor.pen = Screen.Cell{
|
||||
.char = 'a',
|
||||
.bg = pink,
|
||||
.fg = pink,
|
||||
.attrs = .{ .bold = true, .has_bg = true },
|
||||
.bg = .{ .rgb = pink },
|
||||
.fg = .{ .rgb = pink },
|
||||
.attrs = .{ .bold = true },
|
||||
};
|
||||
var cell_ptr = t.screen.getCellPtr(.active, 60, 60);
|
||||
cell_ptr.* = t.screen.cursor.pen;
|
||||
@ -6102,14 +6093,14 @@ test "Terminal: eraseDisplay complete" {
|
||||
cell_ptr.* = t.screen.cursor.pen;
|
||||
// verify the cell was set
|
||||
var cell = t.screen.getCell(.active, 60, 60);
|
||||
try testing.expect(cell.bg.eql(pink));
|
||||
try testing.expect(cell.fg.eql(pink));
|
||||
try testing.expect(cell.bg.rgb.eql(pink));
|
||||
try testing.expect(cell.fg.rgb.eql(pink));
|
||||
try testing.expect(cell.char == 'a');
|
||||
try testing.expect(cell.attrs.bold);
|
||||
// verify the cell was set
|
||||
cell = t.screen.getCell(.active, 0, 0);
|
||||
try testing.expect(cell.bg.eql(pink));
|
||||
try testing.expect(cell.fg.eql(pink));
|
||||
try testing.expect(cell.bg.rgb.eql(pink));
|
||||
try testing.expect(cell.fg.rgb.eql(pink));
|
||||
try testing.expect(cell.char == 'a');
|
||||
try testing.expect(cell.attrs.bold);
|
||||
// position our cursor between the cells
|
||||
@ -6118,17 +6109,15 @@ test "Terminal: eraseDisplay complete" {
|
||||
t.eraseDisplay(testing.allocator, .complete, false);
|
||||
// check they were erased
|
||||
cell = t.screen.getCell(.active, 60, 60);
|
||||
try testing.expect(cell.bg.eql(pink));
|
||||
try testing.expect(cell.fg.eql(.{}));
|
||||
try testing.expect(cell.bg.rgb.eql(pink));
|
||||
try testing.expect(cell.fg == .none);
|
||||
try testing.expect(cell.char == 0);
|
||||
try testing.expect(!cell.attrs.bold);
|
||||
try testing.expect(cell.attrs.has_bg);
|
||||
cell = t.screen.getCell(.active, 0, 0);
|
||||
try testing.expect(cell.bg.eql(pink));
|
||||
try testing.expect(cell.fg.eql(.{}));
|
||||
try testing.expect(cell.bg.rgb.eql(pink));
|
||||
try testing.expect(cell.fg == .none);
|
||||
try testing.expect(cell.char == 0);
|
||||
try testing.expect(!cell.attrs.bold);
|
||||
try testing.expect(cell.attrs.has_bg);
|
||||
}
|
||||
|
||||
test "Terminal: eraseDisplay protected complete" {
|
||||
@ -7041,8 +7030,7 @@ test "Terminal: DECCOLM preserves SGR bg" {
|
||||
defer t.deinit(alloc);
|
||||
|
||||
const pen: Screen.Cell = .{
|
||||
.bg = .{ .r = 0xFF, .g = 0x00, .b = 0x00 },
|
||||
.attrs = .{ .has_bg = true },
|
||||
.bg = .{ .rgb = .{ .r = 0xFF, .g = 0x00, .b = 0x00 } },
|
||||
};
|
||||
|
||||
t.screen.cursor.pen = pen;
|
||||
|
Reference in New Issue
Block a user