terminal: renderstate now has terminal colors

pull/9662/head
Mitchell Hashimoto 2025-11-19 06:36:33 -10:00
parent ebc8bff8f1
commit d1e87c73fb
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
2 changed files with 59 additions and 52 deletions

View File

@ -1062,18 +1062,14 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
) !void {
// Data we extract out of the critical area.
const Critical = struct {
bg: terminal.color.RGB,
fg: terminal.color.RGB,
mouse: renderer.State.Mouse,
preedit: ?renderer.State.Preedit,
cursor_color: ?terminal.color.RGB,
cursor_style: ?renderer.CursorStyle,
color_palette: terminal.color.Palette,
scrollbar: terminal.Scrollbar,
};
// Update all our data as tightly as possible within the mutex.
var critical: Critical = critical: {
const critical: Critical = critical: {
// const start = try std.time.Instant.now();
// const start_micro = std.time.microTimestamp();
// defer {
@ -1100,17 +1096,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// cross-thread mailbox message within the IO path.
const scrollbar = state.terminal.screens.active.pages.scrollbar();
// Get our bg/fg, swap them if reversed.
const RGB = terminal.color.RGB;
const bg: RGB, const fg: RGB = colors: {
const bg = state.terminal.colors.background.get().?;
const fg = state.terminal.colors.foreground.get().?;
break :colors if (state.terminal.modes.get(.reverse_colors))
.{ fg, bg }
else
.{ bg, fg };
};
// Whether to draw our cursor or not.
const cursor_style = if (state.terminal.flags.password_input)
.lock
@ -1143,13 +1128,9 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
}
break :critical .{
.bg = bg,
.fg = fg,
.mouse = state.mouse,
.preedit = preedit,
.cursor_color = state.terminal.colors.cursor.get(),
.cursor_style = cursor_style,
.color_palette = state.terminal.colors.palette.current,
.scrollbar = scrollbar,
};
};
@ -1161,10 +1142,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
try self.rebuildCells(
critical.preedit,
critical.cursor_style,
&critical.color_palette,
critical.bg,
critical.fg,
critical.cursor_color,
);
// Notify our shaper we're done for the frame. For some shapers,
@ -1186,9 +1163,9 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// Update our background color
self.uniforms.bg_color = .{
critical.bg.r,
critical.bg.g,
critical.bg.b,
self.terminal_state.colors.background.r,
self.terminal_state.colors.background.g,
self.terminal_state.colors.background.b,
@intFromFloat(@round(self.config.background_opacity * 255.0)),
};
}
@ -2248,10 +2225,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
self: *Self,
preedit: ?renderer.State.Preedit,
cursor_style_: ?renderer.CursorStyle,
color_palette: *const terminal.color.Palette,
background: terminal.color.RGB,
foreground: terminal.color.RGB,
terminal_cursor_color: ?terminal.color.RGB,
) !void {
const state: *terminal.RenderState = &self.terminal_state;
defer state.redraw = false;
@ -2515,11 +2488,11 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// configuration, inversions, selections, etc.
const bg_style = style.bg(
cell,
color_palette,
&state.colors.palette,
);
const fg_style = style.fg(.{
.default = foreground,
.palette = color_palette,
.default = state.colors.foreground,
.palette = &state.colors.palette,
.bold = self.config.bold_color,
});
@ -2538,7 +2511,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// If no configuration, then our selection background
// is our foreground color.
break :bg foreground;
break :bg state.colors.foreground;
}
// Not selected
@ -2560,7 +2533,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
const fg = fg: {
// Our happy-path non-selection background color
// is our style or our configured defaults.
const final_bg = bg_style orelse background;
const final_bg = bg_style orelse state.colors.background;
// Whether we need to use the bg color as our fg color:
// - Cell is selected, inverted, and set to cell-foreground
@ -2576,7 +2549,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
};
}
break :fg background;
break :fg state.colors.background;
}
break :fg if (style.flags.inverse)
@ -2590,7 +2563,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// Set the cell's background color.
{
const rgb = bg orelse background;
const rgb = bg orelse state.colors.background;
// Determine our background alpha. If we have transparency configured
// then this is dynamic depending on some situations. This is all
@ -2658,7 +2631,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
@intCast(x),
@intCast(y),
underline,
style.underlineColor(color_palette) orelse fg,
style.underlineColor(&state.colors.palette) orelse fg,
alpha,
) catch |err| {
log.warn(
@ -2779,7 +2752,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
const style = cursor_style_ orelse break :cursor;
const cursor_color = cursor_color: {
// If an explicit cursor color was set by OSC 12, use that.
if (terminal_cursor_color) |v| break :cursor_color v;
if (state.colors.cursor) |v| break :cursor_color v;
// Use our configured color if specified
if (self.config.cursor_color) |v| switch (v) {
@ -2789,14 +2762,14 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
=> |_, tag| {
const sty: terminal.Style = state.cursor.style;
const fg_style = sty.fg(.{
.default = foreground,
.palette = color_palette,
.default = state.colors.foreground,
.palette = &state.colors.palette,
.bold = self.config.bold_color,
});
const bg_style = sty.bg(
&state.cursor.cell,
color_palette,
) orelse background;
&state.colors.palette,
) orelse state.colors.background;
break :cursor_color switch (tag) {
.color => unreachable,
@ -2806,7 +2779,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
},
};
break :cursor_color foreground;
break :cursor_color state.colors.foreground;
};
self.addCursor(
@ -2847,14 +2820,14 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
const sty = state.cursor.style;
const fg_style = sty.fg(.{
.default = foreground,
.palette = color_palette,
.default = state.colors.foreground,
.palette = &state.colors.palette,
.bold = self.config.bold_color,
});
const bg_style = sty.bg(
&state.cursor.cell,
color_palette,
) orelse background;
&state.colors.palette,
) orelse state.colors.background;
break :blk switch (txt) {
// If the cell is reversed, use the opposite cell color instead.
@ -2862,7 +2835,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
.@"cell-background" => if (sty.flags.inverse) fg_style else bg_style,
else => unreachable,
};
} else background;
} else state.colors.background;
self.uniforms.cursor_color = .{
uniform_color.r,
@ -2881,8 +2854,8 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
self.addPreeditCell(
cp,
.{ .x = x, .y = range.y },
background,
foreground,
state.colors.background,
state.colors.foreground,
) catch |err| {
log.warn("error building preedit cell, will be invalid x={} y={}, err={}", .{
x,

View File

@ -3,6 +3,7 @@ const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const ArenaAllocator = std.heap.ArenaAllocator;
const fastmem = @import("../fastmem.zig");
const color = @import("color.zig");
const point = @import("point.zig");
const size = @import("size.zig");
const page = @import("page.zig");
@ -14,6 +15,8 @@ const Terminal = @import("Terminal.zig");
// TODO:
// - tests for cursor state
// - tests for dirty state
// - tests for colors
// Developer note: this is in src/terminal and not src/renderer because
// the goal is that this remains generic to multiple renderers. This can
@ -48,6 +51,9 @@ pub const RenderState = struct {
/// area and scrolling with new output.
viewport_is_bottom: bool,
/// The color state for the terminal.
colors: Colors,
/// Cursor state within the viewport.
cursor: Cursor,
@ -79,6 +85,12 @@ pub const RenderState = struct {
.rows = 0,
.cols = 0,
.viewport_is_bottom = false,
.colors = .{
.background = .{},
.foreground = .{},
.cursor = null,
.palette = color.default,
},
.cursor = .{
.active = .{ .x = 0, .y = 0 },
.viewport = null,
@ -90,6 +102,17 @@ pub const RenderState = struct {
.screen = .primary,
};
/// The color state for the terminal.
///
/// The background/foreground will be reversed if the terminal reverse
/// color mode is on! You do not need to handle that manually!
pub const Colors = struct {
background: color.RGB,
foreground: color.RGB,
cursor: ?color.RGB,
palette: color.Palette,
};
pub const Cursor = struct {
/// The x/y position of the cursor within the active area.
active: point.Coordinate,
@ -236,6 +259,17 @@ pub const RenderState = struct {
// but may not be worth it.
self.cursor.viewport = null;
// Colors.
self.colors.cursor = t.colors.cursor.get();
self.colors.palette = t.colors.palette.current;
if (t.modes.get(.reverse_colors)) {
self.colors.background = t.colors.foreground.get().?;
self.colors.foreground = t.colors.background.get().?;
} else {
self.colors.background = t.colors.background.get().?;
self.colors.foreground = t.colors.foreground.get().?;
}
// Ensure our row length is exactly our height, freeing or allocating
// data as necessary. In most cases we'll have a perfectly matching
// size.