renderer: use terminal color state, remove color messages

pull/9412/head
Mitchell Hashimoto 2025-10-30 09:52:36 -07:00
parent 77343bb06e
commit 2daecd94a5
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
4 changed files with 75 additions and 200 deletions

View File

@ -437,21 +437,6 @@ fn drainMailbox(self: *Thread) !void {
grid.set.deref(grid.old_key); grid.set.deref(grid.old_key);
}, },
.foreground_color => |color| {
self.renderer.foreground_color = color;
self.renderer.markDirty();
},
.background_color => |color| {
self.renderer.background_color = color;
self.renderer.markDirty();
},
.cursor_color => |color| {
self.renderer.cursor_color = color;
self.renderer.markDirty();
},
.resize => |v| self.renderer.setScreenSize(v), .resize => |v| self.renderer.setScreenSize(v),
.change_config => |config| { .change_config => |config| {

View File

@ -120,30 +120,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
scrollbar: terminal.Scrollbar, scrollbar: terminal.Scrollbar,
scrollbar_dirty: bool, scrollbar_dirty: bool,
/// The foreground color set by an OSC 10 sequence. If unset then
/// default_foreground_color is used.
foreground_color: ?terminal.color.RGB,
/// Foreground color set in the user's config file.
default_foreground_color: terminal.color.RGB,
/// The background color set by an OSC 11 sequence. If unset then
/// default_background_color is used.
background_color: ?terminal.color.RGB,
/// Background color set in the user's config file.
default_background_color: terminal.color.RGB,
/// The cursor color set by an OSC 12 sequence. If unset then
/// default_cursor_color is used.
cursor_color: ?terminal.color.RGB,
/// Default cursor color when no color is set explicitly by an OSC 12 command.
/// This is cursor color as set in the user's config, if any. If no cursor color
/// is set in the user's config, then the cursor color is determined by the
/// current foreground color.
default_cursor_color: ?configpkg.Config.TerminalColor,
/// The current set of cells to render. This is rebuilt on every frame /// The current set of cells to render. This is rebuilt on every frame
/// but we keep this around so that we don't reallocate. Each set of /// but we keep this around so that we don't reallocate. Each set of
/// cells goes into a separate shader. /// cells goes into a separate shader.
@ -691,12 +667,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
.focused = true, .focused = true,
.scrollbar = .zero, .scrollbar = .zero,
.scrollbar_dirty = false, .scrollbar_dirty = false,
.foreground_color = null,
.default_foreground_color = options.config.foreground,
.background_color = null,
.default_background_color = options.config.background,
.cursor_color = null,
.default_cursor_color = options.config.cursor_color,
// Render state // Render state
.cells = .{}, .cells = .{},
@ -1094,10 +1064,12 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// Data we extract out of the critical area. // Data we extract out of the critical area.
const Critical = struct { const Critical = struct {
bg: terminal.color.RGB, bg: terminal.color.RGB,
fg: terminal.color.RGB,
screen: terminal.Screen, screen: terminal.Screen,
screen_type: terminal.ScreenType, screen_type: terminal.ScreenType,
mouse: renderer.State.Mouse, mouse: renderer.State.Mouse,
preedit: ?renderer.State.Preedit, preedit: ?renderer.State.Preedit,
cursor_color: ?terminal.color.RGB,
cursor_style: ?renderer.CursorStyle, cursor_style: ?renderer.CursorStyle,
color_palette: terminal.color.Palette, color_palette: terminal.color.Palette,
scrollbar: terminal.Scrollbar, scrollbar: terminal.Scrollbar,
@ -1132,36 +1104,16 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// cross-thread mailbox message within the IO path. // cross-thread mailbox message within the IO path.
const scrollbar = state.terminal.screen.pages.scrollbar(); const scrollbar = state.terminal.screen.pages.scrollbar();
// Swap bg/fg if the terminal is reversed // Get our bg/fg, swap them if reversed.
const bg = self.background_color orelse self.default_background_color; const RGB = terminal.color.RGB;
const fg = self.foreground_color orelse self.default_foreground_color; const bg: RGB, const fg: RGB = colors: {
defer { const bg = state.terminal.colors.background.get().?;
if (self.background_color) |*c| { const fg = state.terminal.colors.foreground.get().?;
c.* = bg; break :colors if (state.terminal.modes.get(.reverse_colors))
} else { .{ fg, bg }
self.default_background_color = bg; else
} .{ bg, fg };
};
if (self.foreground_color) |*c| {
c.* = fg;
} else {
self.default_foreground_color = fg;
}
}
if (state.terminal.modes.get(.reverse_colors)) {
if (self.background_color) |*c| {
c.* = fg;
} else {
self.default_background_color = fg;
}
if (self.foreground_color) |*c| {
c.* = bg;
} else {
self.default_foreground_color = bg;
}
}
// Get the viewport pin so that we can compare it to the current. // Get the viewport pin so that we can compare it to the current.
const viewport_pin = state.terminal.screen.pages.pin(.{ .viewport = .{} }).?; const viewport_pin = state.terminal.screen.pages.pin(.{ .viewport = .{} }).?;
@ -1252,11 +1204,13 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
self.cells_viewport = viewport_pin; self.cells_viewport = viewport_pin;
break :critical .{ break :critical .{
.bg = self.background_color orelse self.default_background_color, .bg = bg,
.fg = fg,
.screen = screen_copy, .screen = screen_copy,
.screen_type = state.terminal.active_screen, .screen_type = state.terminal.active_screen,
.mouse = state.mouse, .mouse = state.mouse,
.preedit = preedit, .preedit = preedit,
.cursor_color = state.terminal.colors.cursor.get(),
.cursor_style = cursor_style, .cursor_style = cursor_style,
.color_palette = state.terminal.colors.palette.current, .color_palette = state.terminal.colors.palette.current,
.scrollbar = scrollbar, .scrollbar = scrollbar,
@ -1277,6 +1231,9 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
critical.preedit, critical.preedit,
critical.cursor_style, critical.cursor_style,
&critical.color_palette, &critical.color_palette,
critical.bg,
critical.fg,
critical.cursor_color,
); );
// Notify our shaper we're done for the frame. For some shapers, // Notify our shaper we're done for the frame. For some shapers,
@ -2104,11 +2061,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
self.uniforms.bools.use_linear_blending = config.blending.isLinear(); self.uniforms.bools.use_linear_blending = config.blending.isLinear();
self.uniforms.bools.use_linear_correction = config.blending == .@"linear-corrected"; self.uniforms.bools.use_linear_correction = config.blending == .@"linear-corrected";
// Set our new colors
self.default_background_color = config.background;
self.default_foreground_color = config.foreground;
self.default_cursor_color = config.cursor_color;
const bg_image_config_changed = const bg_image_config_changed =
self.config.bg_image_fit != config.bg_image_fit or self.config.bg_image_fit != config.bg_image_fit or
self.config.bg_image_position != config.bg_image_position or self.config.bg_image_position != config.bg_image_position or
@ -2370,6 +2322,9 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
preedit: ?renderer.State.Preedit, preedit: ?renderer.State.Preedit,
cursor_style_: ?renderer.CursorStyle, cursor_style_: ?renderer.CursorStyle,
color_palette: *const terminal.color.Palette, color_palette: *const terminal.color.Palette,
background: terminal.color.RGB,
foreground: terminal.color.RGB,
terminal_cursor_color: ?terminal.color.RGB,
) !void { ) !void {
self.draw_mutex.lock(); self.draw_mutex.lock();
defer self.draw_mutex.unlock(); defer self.draw_mutex.unlock();
@ -2503,12 +2458,12 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
.extend => if (y == 0) { .extend => if (y == 0) {
self.uniforms.padding_extend.up = !row.neverExtendBg( self.uniforms.padding_extend.up = !row.neverExtendBg(
color_palette, color_palette,
self.background_color orelse self.default_background_color, background,
); );
} else if (y == self.cells.size.rows - 1) { } else if (y == self.cells.size.rows - 1) {
self.uniforms.padding_extend.down = !row.neverExtendBg( self.uniforms.padding_extend.down = !row.neverExtendBg(
color_palette, color_palette,
self.background_color orelse self.default_background_color, background,
); );
}, },
} }
@ -2629,7 +2584,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// configuration, inversions, selections, etc. // configuration, inversions, selections, etc.
const bg_style = style.bg(cell, color_palette); const bg_style = style.bg(cell, color_palette);
const fg_style = style.fg(.{ const fg_style = style.fg(.{
.default = self.foreground_color orelse self.default_foreground_color, .default = foreground,
.palette = color_palette, .palette = color_palette,
.bold = self.config.bold_color, .bold = self.config.bold_color,
}); });
@ -2649,7 +2604,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// If no configuration, then our selection background // If no configuration, then our selection background
// is our foreground color. // is our foreground color.
break :bg self.foreground_color orelse self.default_foreground_color; break :bg foreground;
} }
// Not selected // Not selected
@ -2671,9 +2626,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
const fg = fg: { const fg = fg: {
// Our happy-path non-selection background color // Our happy-path non-selection background color
// is our style or our configured defaults. // is our style or our configured defaults.
const final_bg = bg_style orelse const final_bg = bg_style orelse background;
self.background_color orelse
self.default_background_color;
// Whether we need to use the bg color as our fg color: // Whether we need to use the bg color as our fg color:
// - Cell is selected, inverted, and set to cell-foreground // - Cell is selected, inverted, and set to cell-foreground
@ -2689,7 +2642,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
}; };
} }
break :fg self.background_color orelse self.default_background_color; break :fg background;
} }
break :fg if (style.flags.inverse) break :fg if (style.flags.inverse)
@ -2703,7 +2656,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// Set the cell's background color. // Set the cell's background color.
{ {
const rgb = bg orelse self.background_color orelse self.default_background_color; const rgb = bg orelse background;
// Determine our background alpha. If we have transparency configured // Determine our background alpha. If we have transparency configured
// then this is dynamic depending on some situations. This is all // then this is dynamic depending on some situations. This is all
@ -2888,24 +2841,24 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
const style = cursor_style_ orelse break :cursor; const style = cursor_style_ orelse break :cursor;
const cursor_color = cursor_color: { const cursor_color = cursor_color: {
// If an explicit cursor color was set by OSC 12, use that. // If an explicit cursor color was set by OSC 12, use that.
if (self.cursor_color) |v| break :cursor_color v; if (terminal_cursor_color) |v| break :cursor_color v;
// Use our configured color if specified // Use our configured color if specified
if (self.default_cursor_color) |v| switch (v) { if (self.config.cursor_color) |v| switch (v) {
.color => |color| break :cursor_color color.toTerminalRGB(), .color => |color| break :cursor_color color.toTerminalRGB(),
inline .@"cell-foreground", inline .@"cell-foreground",
.@"cell-background", .@"cell-background",
=> |_, tag| { => |_, tag| {
const sty = screen.cursor.page_pin.style(screen.cursor.page_cell); const sty = screen.cursor.page_pin.style(screen.cursor.page_cell);
const fg_style = sty.fg(.{ const fg_style = sty.fg(.{
.default = self.foreground_color orelse self.default_foreground_color, .default = foreground,
.palette = color_palette, .palette = color_palette,
.bold = self.config.bold_color, .bold = self.config.bold_color,
}); });
const bg_style = sty.bg( const bg_style = sty.bg(
screen.cursor.page_cell, screen.cursor.page_cell,
color_palette, color_palette,
) orelse self.background_color orelse self.default_background_color; ) orelse background;
break :cursor_color switch (tag) { break :cursor_color switch (tag) {
.color => unreachable, .color => unreachable,
@ -2915,7 +2868,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
}, },
}; };
break :cursor_color self.foreground_color orelse self.default_foreground_color; break :cursor_color foreground;
}; };
self.addCursor(screen, style, cursor_color); self.addCursor(screen, style, cursor_color);
@ -2950,11 +2903,14 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
const sty = screen.cursor.page_pin.style(screen.cursor.page_cell); const sty = screen.cursor.page_pin.style(screen.cursor.page_cell);
const fg_style = sty.fg(.{ const fg_style = sty.fg(.{
.default = self.foreground_color orelse self.default_foreground_color, .default = foreground,
.palette = color_palette, .palette = color_palette,
.bold = self.config.bold_color, .bold = self.config.bold_color,
}); });
const bg_style = sty.bg(screen.cursor.page_cell, color_palette) orelse self.background_color orelse self.default_background_color; const bg_style = sty.bg(
screen.cursor.page_cell,
color_palette,
) orelse background;
break :blk switch (txt) { break :blk switch (txt) {
// If the cell is reversed, use the opposite cell color instead. // If the cell is reversed, use the opposite cell color instead.
@ -2962,7 +2918,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
.@"cell-background" => if (sty.flags.inverse) fg_style else bg_style, .@"cell-background" => if (sty.flags.inverse) fg_style else bg_style,
else => unreachable, else => unreachable,
}; };
} else self.background_color orelse self.default_background_color; } else background;
self.uniforms.cursor_color = .{ self.uniforms.cursor_color = .{
uniform_color.r, uniform_color.r,
@ -2978,7 +2934,12 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
const range = preedit_range.?; const range = preedit_range.?;
var x = range.x[0]; var x = range.x[0];
for (preedit_v.codepoints[range.cp_offset..]) |cp| { for (preedit_v.codepoints[range.cp_offset..]) |cp| {
self.addPreeditCell(cp, .{ .x = x, .y = range.y }) catch |err| { self.addPreeditCell(
cp,
.{ .x = x, .y = range.y },
background,
foreground,
) catch |err| {
log.warn("error building preedit cell, will be invalid x={} y={}, err={}", .{ log.warn("error building preedit cell, will be invalid x={} y={}, err={}", .{
x, x,
range.y, range.y,
@ -3253,10 +3214,12 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
self: *Self, self: *Self,
cp: renderer.State.Preedit.Codepoint, cp: renderer.State.Preedit.Codepoint,
coord: terminal.Coordinate, coord: terminal.Coordinate,
screen_bg: terminal.color.RGB,
screen_fg: terminal.color.RGB,
) !void { ) !void {
// Preedit is rendered inverted // Preedit is rendered inverted
const bg = self.foreground_color orelse self.default_foreground_color; const bg = screen_fg;
const fg = self.background_color orelse self.default_background_color; const fg = screen_bg;
// Render the glyph for our preedit text // Render the glyph for our preedit text
const render_ = self.font_grid.renderCodepoint( const render_ = self.font_grid.renderCodepoint(

View File

@ -42,16 +42,6 @@ pub const Message = union(enum) {
old_key: font.SharedGridSet.Key, old_key: font.SharedGridSet.Key,
}, },
/// Change the foreground color as set by an OSC 10 command, if any.
foreground_color: ?terminal.color.RGB,
/// Change the background color as set by an OSC 11 command, if any.
background_color: ?terminal.color.RGB,
/// Change the cursor color. This can be done separately from changing the
/// config file in response to an OSC 12 command.
cursor_color: ?terminal.color.RGB,
/// Changes the size. The screen size might change, padding, grid, etc. /// Changes the size. The screen size might change, padding, grid, etc.
resize: renderer.Size, resize: renderer.Size,

View File

@ -196,7 +196,6 @@ pub const StreamHandler = struct {
.erase_display_above => self.terminal.eraseDisplay(.above, value), .erase_display_above => self.terminal.eraseDisplay(.above, value),
.erase_display_complete => { .erase_display_complete => {
try self.terminal.scrollViewport(.{ .bottom = {} }); try self.terminal.scrollViewport(.{ .bottom = {} });
try self.queueRender();
self.terminal.eraseDisplay(.complete, value); self.terminal.eraseDisplay(.complete, value);
}, },
.erase_display_scrollback => self.terminal.eraseDisplay(.scrollback, value), .erase_display_scrollback => self.terminal.eraseDisplay(.scrollback, value),
@ -569,10 +568,7 @@ pub const StreamHandler = struct {
.autorepeat => {}, .autorepeat => {},
// Schedule a render since we changed colors // Schedule a render since we changed colors
.reverse_colors => { .reverse_colors => self.terminal.flags.dirty.reverse_colors = true,
self.terminal.flags.dirty.reverse_colors = true;
try self.queueRender();
},
// Origin resets cursor pos. This is called whether or not // Origin resets cursor pos. This is called whether or not
// we're enabling or disabling origin mode and whether or // we're enabling or disabling origin mode and whether or
@ -588,17 +584,14 @@ pub const StreamHandler = struct {
.alt_screen_legacy => { .alt_screen_legacy => {
self.terminal.switchScreenMode(.@"47", enabled); self.terminal.switchScreenMode(.@"47", enabled);
try self.queueRender();
}, },
.alt_screen => { .alt_screen => {
self.terminal.switchScreenMode(.@"1047", enabled); self.terminal.switchScreenMode(.@"1047", enabled);
try self.queueRender();
}, },
.alt_screen_save_cursor_clear_enter => { .alt_screen_save_cursor_clear_enter => {
self.terminal.switchScreenMode(.@"1049", enabled); self.terminal.switchScreenMode(.@"1049", enabled);
try self.queueRender();
}, },
// Mode 1048 is xterm's conditional save cursor depending // Mode 1048 is xterm's conditional save cursor depending
@ -634,7 +627,6 @@ pub const StreamHandler = struct {
// forever. // forever.
.synchronized_output => { .synchronized_output => {
if (enabled) self.messageWriter(.{ .start_synchronized_output = {} }); if (enabled) self.messageWriter(.{ .start_synchronized_output = {} });
try self.queueRender();
}, },
.linefeed => { .linefeed => {
@ -1108,24 +1100,9 @@ pub const StreamHandler = struct {
self.terminal.colors.palette.set(i, set.color); self.terminal.colors.palette.set(i, set.color);
}, },
.dynamic => |dynamic| switch (dynamic) { .dynamic => |dynamic| switch (dynamic) {
.foreground => { .foreground => self.terminal.colors.foreground.set(set.color),
self.terminal.colors.foreground.set(set.color); .background => self.terminal.colors.background.set(set.color),
self.rendererMessageWriter(.{ .cursor => self.terminal.colors.cursor.set(set.color),
.foreground_color = set.color,
});
},
.background => {
self.terminal.colors.background.set(set.color);
self.rendererMessageWriter(.{
.background_color = set.color,
});
},
.cursor => {
self.terminal.colors.cursor.set(set.color);
self.rendererMessageWriter(.{
.cursor_color = set.color,
});
},
.pointer_foreground, .pointer_foreground,
.pointer_background, .pointer_background,
.tektronix_foreground, .tektronix_foreground,
@ -1162,9 +1139,6 @@ pub const StreamHandler = struct {
.dynamic => |dynamic| switch (dynamic) { .dynamic => |dynamic| switch (dynamic) {
.foreground => { .foreground => {
self.terminal.colors.foreground.reset(); self.terminal.colors.foreground.reset();
self.rendererMessageWriter(.{
.foreground_color = null,
});
if (self.terminal.colors.foreground.default) |c| { if (self.terminal.colors.foreground.default) |c| {
self.surfaceMessageWriter(.{ .color_change = .{ self.surfaceMessageWriter(.{ .color_change = .{
@ -1175,9 +1149,6 @@ pub const StreamHandler = struct {
}, },
.background => { .background => {
self.terminal.colors.background.reset(); self.terminal.colors.background.reset();
self.rendererMessageWriter(.{
.background_color = null,
});
if (self.terminal.colors.background.default) |c| { if (self.terminal.colors.background.default) |c| {
self.surfaceMessageWriter(.{ .color_change = .{ self.surfaceMessageWriter(.{ .color_change = .{
@ -1189,10 +1160,6 @@ pub const StreamHandler = struct {
.cursor => { .cursor => {
self.terminal.colors.cursor.reset(); self.terminal.colors.cursor.reset();
self.rendererMessageWriter(.{
.cursor_color = null,
});
if (self.terminal.colors.cursor.default) |c| { if (self.terminal.colors.cursor.default) |c| {
self.surfaceMessageWriter(.{ .color_change = .{ self.surfaceMessageWriter(.{ .color_change = .{
.target = target, .target = target,
@ -1396,32 +1363,17 @@ pub const StreamHandler = struct {
self.terminal.colors.palette.set(palette, v.color); self.terminal.colors.palette.set(palette, v.color);
}, },
.special => |special| { .special => |special| switch (special) {
const msg: renderer.Message = switch (special) { .foreground => self.terminal.colors.foreground.set(v.color),
.foreground => msg: { .background => self.terminal.colors.background.set(v.color),
self.terminal.colors.foreground.set(v.color); .cursor => self.terminal.colors.cursor.set(v.color),
break :msg .{ .foreground_color = v.color }; else => {
}, log.warn(
.background => msg: { "ignoring unsupported kitty color protocol key: {f}",
self.terminal.colors.background.set(v.color); .{v.key},
break :msg .{ .background_color = v.color }; );
}, continue;
.cursor => msg: { },
self.terminal.colors.cursor.set(v.color);
break :msg .{ .cursor_color = v.color };
},
else => {
log.warn(
"ignoring unsupported kitty color protocol key: {f}",
.{v.key},
);
continue;
},
};
// See messageWriter which has similar logic and
// explains why we may have to do this.
self.rendererMessageWriter(msg);
}, },
}, },
.reset => |key| switch (key) { .reset => |key| switch (key) {
@ -1430,32 +1382,17 @@ pub const StreamHandler = struct {
self.terminal.colors.palette.reset(palette); self.terminal.colors.palette.reset(palette);
}, },
.special => |special| { .special => |special| switch (special) {
const msg: renderer.Message = switch (special) { .foreground => self.terminal.colors.foreground.reset(),
.foreground => msg: { .background => self.terminal.colors.background.reset(),
self.terminal.colors.foreground.reset(); .cursor => self.terminal.colors.cursor.reset(),
break :msg .{ .foreground_color = null }; else => {
}, log.warn(
.background => msg: { "ignoring unsupported kitty color protocol key: {f}",
self.terminal.colors.background.reset(); .{key},
break :msg .{ .background_color = null }; );
}, continue;
.cursor => msg: { },
self.terminal.colors.cursor.reset();
break :msg .{ .cursor_color = null };
},
else => {
log.warn(
"ignoring unsupported kitty color protocol key: {f}",
.{key},
);
continue;
},
};
// See messageWriter which has similar logic and
// explains why we may have to do this.
self.rendererMessageWriter(msg);
}, },
}, },
} }