feat: support selection foreground being cell foreground (#5219)

This resolves #2685.

## Changes

* Created `SelectionColor` tagged union that can take a `Color`,
`cell-foreground`, or `cell-background`
* Used the new union to implement the feature for Metal and OpenGL
* Removed the use of `invert_selection_fg_bg` during rendering as
suggested in the issue

## Demo

macOS:


https://github.com/user-attachments/assets/b5b2db83-bb62-4929-8e3c-870a1e1a7a5c

Ubuntu:


https://github.com/user-attachments/assets/259dd707-d9d9-4590-8f3c-a67ccdef34bc


Any feedback would be helpful, I'm sure there's a lot of room for
improvement here.
This commit is contained in:
Mitchell Hashimoto
2025-07-03 09:31:27 -07:00
committed by GitHub
4 changed files with 302 additions and 122 deletions

View File

@ -62,6 +62,13 @@ pub const compatibility = std.StaticStringMap(
// Ghostty 1.2 removed the `hidden` value from `gtk-tabs-location` and // Ghostty 1.2 removed the `hidden` value from `gtk-tabs-location` and
// moved it to `window-show-tab-bar`. // moved it to `window-show-tab-bar`.
.{ "gtk-tabs-location", compatGtkTabsLocation }, .{ "gtk-tabs-location", compatGtkTabsLocation },
// Ghostty 1.2 lets you set `cell-foreground` and `cell-background`
// to match the cell foreground and background colors, respectively.
// This can be used with `cursor-color` and `cursor-text` to recreate
// this behavior. This applies to selection too.
.{ "cursor-invert-fg-bg", compatCursorInvertFgBg },
.{ "selection-invert-fg-bg", compatSelectionInvertFgBg },
}); });
/// The font families to use. /// The font families to use.
@ -591,16 +598,11 @@ foreground: Color = .{ .r = 0xFF, .g = 0xFF, .b = 0xFF },
/// the selection color is just the inverted window background and foreground /// the selection color is just the inverted window background and foreground
/// (note: not to be confused with the cell bg/fg). /// (note: not to be confused with the cell bg/fg).
/// Specified as either hex (`#RRGGBB` or `RRGGBB`) or a named X11 color. /// Specified as either hex (`#RRGGBB` or `RRGGBB`) or a named X11 color.
@"selection-foreground": ?Color = null, /// Since version 1.2.0, this can also be set to `cell-foreground` to match
@"selection-background": ?Color = null, /// the cell foreground color, or `cell-background` to match the cell
/// background color.
/// Swap the foreground and background colors of cells for selection. This @"selection-foreground": ?TerminalColor = null,
/// option overrides the `selection-foreground` and `selection-background` @"selection-background": ?TerminalColor = null,
/// options.
///
/// If you select across cells with differing foregrounds and backgrounds, the
/// selection color will vary across the selection.
@"selection-invert-fg-bg": bool = false,
/// Whether to clear selected text when typing. This defaults to `true`. /// Whether to clear selected text when typing. This defaults to `true`.
/// This is typical behavior for most terminal emulators as well as /// This is typical behavior for most terminal emulators as well as
@ -644,12 +646,20 @@ foreground: Color = .{ .r = 0xFF, .g = 0xFF, .b = 0xFF },
palette: Palette = .{}, palette: Palette = .{},
/// The color of the cursor. If this is not set, a default will be chosen. /// The color of the cursor. If this is not set, a default will be chosen.
/// Specified as either hex (`#RRGGBB` or `RRGGBB`) or a named X11 color. ///
@"cursor-color": ?Color = null, /// Direct colors can be specified as either hex (`#RRGGBB` or `RRGGBB`)
/// or a named X11 color.
/// Swap the foreground and background colors of the cell under the cursor. This ///
/// option overrides the `cursor-color` and `cursor-text` options. /// Additionally, special values can be used to set the color to match
@"cursor-invert-fg-bg": bool = false, /// other colors at runtime:
///
/// * `cell-foreground` - Match the cell foreground color.
/// (Available since version 1.2.0)
///
/// * `cell-background` - Match the cell background color.
/// (Available since version 1.2.0)
///
@"cursor-color": ?TerminalColor = null,
/// The opacity level (opposite of transparency) of the cursor. A value of 1 /// The opacity level (opposite of transparency) of the cursor. A value of 1
/// is fully opaque and a value of 0 is fully transparent. A value less than 0 /// is fully opaque and a value of 0 is fully transparent. A value less than 0
@ -699,7 +709,10 @@ palette: Palette = .{},
/// The color of the text under the cursor. If this is not set, a default will /// The color of the text under the cursor. If this is not set, a default will
/// be chosen. /// be chosen.
/// Specified as either hex (`#RRGGBB` or `RRGGBB`) or a named X11 color. /// Specified as either hex (`#RRGGBB` or `RRGGBB`) or a named X11 color.
@"cursor-text": ?Color = null, /// Since version 1.2.0, this can also be set to `cell-foreground` to match
/// the cell foreground color, or `cell-background` to match the cell
/// background color.
@"cursor-text": ?TerminalColor = null,
/// Enables the ability to move the cursor at prompts by using `alt+click` on /// Enables the ability to move the cursor at prompts by using `alt+click` on
/// Linux and `option+click` on macOS. /// Linux and `option+click` on macOS.
@ -3826,10 +3839,6 @@ pub fn parseManuallyHook(
return true; return true;
} }
/// parseFieldManuallyFallback is a fallback called only when
/// parsing the field directly failed. It can be used to implement
/// backward compatibility. Since this is only called when parsing
/// fails, it doesn't impact happy-path performance.
fn compatGtkTabsLocation( fn compatGtkTabsLocation(
self: *Config, self: *Config,
alloc: Allocator, alloc: Allocator,
@ -3847,6 +3856,51 @@ fn compatGtkTabsLocation(
return false; return false;
} }
fn compatCursorInvertFgBg(
self: *Config,
alloc: Allocator,
key: []const u8,
value_: ?[]const u8,
) bool {
_ = alloc;
assert(std.mem.eql(u8, key, "cursor-invert-fg-bg"));
// We don't do anything if the value is unset, which is technically
// not EXACTLY the same as prior behavior since it would fallback
// to doing whatever cursor-color/cursor-text were set to, but
// I don't want to store what that is separately so this is close
// enough.
//
// Realistically, these fields were mutually exclusive so anyone
// relying on that behavior should just upgrade to the new
// cursor-color/cursor-text fields.
const set = cli.args.parseBool(value_ orelse "t") catch return false;
if (set) {
self.@"cursor-color" = .@"cell-foreground";
self.@"cursor-text" = .@"cell-background";
}
return true;
}
fn compatSelectionInvertFgBg(
self: *Config,
alloc: Allocator,
key: []const u8,
value_: ?[]const u8,
) bool {
_ = alloc;
assert(std.mem.eql(u8, key, "selection-invert-fg-bg"));
const set = cli.args.parseBool(value_ orelse "t") catch return false;
if (set) {
self.@"selection-foreground" = .@"cell-background";
self.@"selection-background" = .@"cell-foreground";
}
return true;
}
/// Create a shallow copy of this config. This will share all the memory /// Create a shallow copy of this config. This will share all the memory
/// allocated with the previous config but will have a new arena for /// allocated with the previous config but will have a new arena for
/// any changes or new allocations. The config should have `deinit` /// any changes or new allocations. The config should have `deinit`
@ -4409,6 +4463,65 @@ pub const Color = struct {
} }
}; };
/// Represents color values that can also reference special color
/// values such as "cell-foreground" or "cell-background".
pub const TerminalColor = union(enum) {
color: Color,
@"cell-foreground",
@"cell-background",
pub fn parseCLI(input_: ?[]const u8) !TerminalColor {
const input = input_ orelse return error.ValueRequired;
if (std.mem.eql(u8, input, "cell-foreground")) return .@"cell-foreground";
if (std.mem.eql(u8, input, "cell-background")) return .@"cell-background";
return .{ .color = try Color.parseCLI(input) };
}
/// Used by Formatter
pub fn formatEntry(self: TerminalColor, formatter: anytype) !void {
switch (self) {
.color => try self.color.formatEntry(formatter),
.@"cell-foreground",
.@"cell-background",
=> try formatter.formatEntry([:0]const u8, @tagName(self)),
}
}
test "parseCLI" {
const testing = std.testing;
try testing.expectEqual(
TerminalColor{ .color = Color{ .r = 78, .g = 42, .b = 132 } },
try TerminalColor.parseCLI("#4e2a84"),
);
try testing.expectEqual(
TerminalColor{ .color = Color{ .r = 0, .g = 0, .b = 0 } },
try TerminalColor.parseCLI("black"),
);
try testing.expectEqual(
TerminalColor.@"cell-foreground",
try TerminalColor.parseCLI("cell-foreground"),
);
try testing.expectEqual(
TerminalColor.@"cell-background",
try TerminalColor.parseCLI("cell-background"),
);
try testing.expectError(error.InvalidValue, TerminalColor.parseCLI("a"));
}
test "formatConfig" {
const testing = std.testing;
var buf = std.ArrayList(u8).init(testing.allocator);
defer buf.deinit();
var sc: TerminalColor = .@"cell-foreground";
try sc.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
try testing.expectEqualSlices(u8, "a = cell-foreground\n", buf.items);
}
};
pub const ColorList = struct { pub const ColorList = struct {
const Self = @This(); const Self = @This();
@ -8042,3 +8155,51 @@ test "theme specifying light/dark sets theme usage in conditional state" {
try testing.expect(cfg._conditional_set.contains(.theme)); try testing.expect(cfg._conditional_set.contains(.theme));
} }
} }
test "compatibility: removed cursor-invert-fg-bg" {
const testing = std.testing;
const alloc = testing.allocator;
{
var cfg = try Config.default(alloc);
defer cfg.deinit();
var it: TestIterator = .{ .data = &.{
"--cursor-invert-fg-bg",
} };
try cfg.loadIter(alloc, &it);
try cfg.finalize();
try testing.expectEqual(
TerminalColor.@"cell-foreground",
cfg.@"cursor-color",
);
try testing.expectEqual(
TerminalColor.@"cell-background",
cfg.@"cursor-text",
);
}
}
test "compatibility: removed selection-invert-fg-bg" {
const testing = std.testing;
const alloc = testing.allocator;
{
var cfg = try Config.default(alloc);
defer cfg.deinit();
var it: TestIterator = .{ .data = &.{
"--selection-invert-fg-bg",
} };
try cfg.loadIter(alloc, &it);
try cfg.finalize();
try testing.expectEqual(
TerminalColor.@"cell-background",
cfg.@"selection-foreground",
);
try testing.expectEqual(
TerminalColor.@"cell-foreground",
cfg.@"selection-background",
);
}
}

View File

@ -133,12 +133,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
/// This is cursor color as set in the user's config, if any. If no cursor color /// 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 /// is set in the user's config, then the cursor color is determined by the
/// current foreground color. /// current foreground color.
default_cursor_color: ?terminal.color.RGB, default_cursor_color: ?configpkg.Config.TerminalColor,
/// When `cursor_color` is null, swap the foreground and background colors of
/// the cell under the cursor for the cursor color. Otherwise, use the default
/// foreground color as the cursor color.
cursor_invert: bool,
/// 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
@ -514,16 +509,14 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
font_features: std.ArrayListUnmanaged([:0]const u8), font_features: std.ArrayListUnmanaged([:0]const u8),
font_styles: font.CodepointResolver.StyleStatus, font_styles: font.CodepointResolver.StyleStatus,
font_shaping_break: configpkg.FontShapingBreak, font_shaping_break: configpkg.FontShapingBreak,
cursor_color: ?terminal.color.RGB, cursor_color: ?configpkg.Config.TerminalColor,
cursor_invert: bool,
cursor_opacity: f64, cursor_opacity: f64,
cursor_text: ?terminal.color.RGB, cursor_text: ?configpkg.Config.TerminalColor,
background: terminal.color.RGB, background: terminal.color.RGB,
background_opacity: f64, background_opacity: f64,
foreground: terminal.color.RGB, foreground: terminal.color.RGB,
selection_background: ?terminal.color.RGB, selection_background: ?configpkg.Config.TerminalColor,
selection_foreground: ?terminal.color.RGB, selection_foreground: ?configpkg.Config.TerminalColor,
invert_selection_fg_bg: bool,
bold_is_bright: bool, bold_is_bright: bool,
min_contrast: f32, min_contrast: f32,
padding_color: configpkg.WindowPaddingColor, padding_color: configpkg.WindowPaddingColor,
@ -571,8 +564,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
config.link.links.items, config.link.links.items,
); );
const cursor_invert = config.@"cursor-invert-fg-bg";
return .{ return .{
.background_opacity = @max(0, @min(1, config.@"background-opacity")), .background_opacity = @max(0, @min(1, config.@"background-opacity")),
.font_thicken = config.@"font-thicken", .font_thicken = config.@"font-thicken",
@ -581,36 +572,18 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
.font_styles = font_styles, .font_styles = font_styles,
.font_shaping_break = config.@"font-shaping-break", .font_shaping_break = config.@"font-shaping-break",
.cursor_color = if (!cursor_invert and config.@"cursor-color" != null) .cursor_color = config.@"cursor-color",
config.@"cursor-color".?.toTerminalRGB() .cursor_text = config.@"cursor-text",
else
null,
.cursor_invert = cursor_invert,
.cursor_text = if (config.@"cursor-text") |txt|
txt.toTerminalRGB()
else
null,
.cursor_opacity = @max(0, @min(1, config.@"cursor-opacity")), .cursor_opacity = @max(0, @min(1, config.@"cursor-opacity")),
.background = config.background.toTerminalRGB(), .background = config.background.toTerminalRGB(),
.foreground = config.foreground.toTerminalRGB(), .foreground = config.foreground.toTerminalRGB(),
.invert_selection_fg_bg = config.@"selection-invert-fg-bg",
.bold_is_bright = config.@"bold-is-bright", .bold_is_bright = config.@"bold-is-bright",
.min_contrast = @floatCast(config.@"minimum-contrast"), .min_contrast = @floatCast(config.@"minimum-contrast"),
.padding_color = config.@"window-padding-color", .padding_color = config.@"window-padding-color",
.selection_background = if (config.@"selection-background") |bg| .selection_background = config.@"selection-background",
bg.toTerminalRGB() .selection_foreground = config.@"selection-foreground",
else
null,
.selection_foreground = if (config.@"selection-foreground") |bg|
bg.toTerminalRGB()
else
null,
.custom_shaders = custom_shaders, .custom_shaders = custom_shaders,
.bg_image = bg_image, .bg_image = bg_image,
@ -703,7 +676,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
.default_background_color = options.config.background, .default_background_color = options.config.background,
.cursor_color = null, .cursor_color = null,
.default_cursor_color = options.config.cursor_color, .default_cursor_color = options.config.cursor_color,
.cursor_invert = options.config.cursor_invert,
// Render state // Render state
.cells = .{}, .cells = .{},
@ -2079,8 +2051,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// Set our new colors // Set our new colors
self.default_background_color = config.background; self.default_background_color = config.background;
self.default_foreground_color = config.foreground; self.default_foreground_color = config.foreground;
self.default_cursor_color = if (!config.cursor_invert) config.cursor_color else null; self.default_cursor_color = config.cursor_color;
self.cursor_invert = config.cursor_invert;
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
@ -2577,28 +2548,31 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
else else
false; false;
// The `_style` suffixed values are the colors based on
// the cell style (SGR), before applying any additional
// 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(color_palette, self.config.bold_is_bright) orelse self.foreground_color orelse self.default_foreground_color; const fg_style = style.fg(
color_palette,
self.config.bold_is_bright,
) orelse self.foreground_color orelse self.default_foreground_color;
// The final background color for the cell. // The final background color for the cell.
const bg = bg: { const bg = bg: {
if (selected) { if (selected) {
break :bg if (self.config.invert_selection_fg_bg) // If we have an explicit selection background color
if (style.flags.inverse) // specified int he config, use that
// Cell is selected with invert selection fg/bg if (self.config.selection_background) |v| {
// enabled, and the cell has the inverse style break :bg switch (v) {
// flag, so they cancel out and we get the normal .color => |color| color.toTerminalRGB(),
// bg color. .@"cell-foreground" => if (style.flags.inverse) bg_style else fg_style,
bg_style .@"cell-background" => if (style.flags.inverse) fg_style else bg_style,
else };
// If it doesn't have the inverse style }
// flag then we use the fg color instead.
fg_style // If no configuration, then our selection background
else // is our foreground color.
// If we don't have invert selection fg/bg set then we break :bg self.foreground_color orelse self.default_foreground_color;
// just use the selection background if set, otherwise
// the default fg color.
break :bg self.config.selection_background orelse self.foreground_color orelse self.default_foreground_color;
} }
// Not selected // Not selected
@ -2618,20 +2592,31 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
}; };
const fg = fg: { const fg = fg: {
if (selected and !self.config.invert_selection_fg_bg) { // Our happy-path non-selection background color
// If we don't have invert selection fg/bg set // is our style or our configured defaults.
// then we just use the selection foreground if const final_bg = bg_style orelse
// set, otherwise the default bg color. self.background_color orelse
break :fg self.config.selection_foreground orelse self.background_color orelse self.default_background_color; 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, not inverted, and set to cell-background
// - Cell is inverted and not selected // - Cell is inverted and not selected
// - Cell is selected and not inverted if (selected) {
// Note: if selected then invert sel fg / bg must be // Use the selection foreground if set
// false since we separately handle it if true above. if (self.config.selection_foreground) |v| {
break :fg if (style.flags.inverse != selected) break :fg switch (v) {
bg_style orelse self.background_color orelse self.default_background_color .color => |color| color.toTerminalRGB(),
.@"cell-foreground" => if (style.flags.inverse) final_bg else fg_style,
.@"cell-background" => if (style.flags.inverse) fg_style else final_bg,
};
}
break :fg self.background_color orelse self.default_background_color;
}
break :fg if (style.flags.inverse)
final_bg
else else
fg_style; fg_style;
}; };
@ -2817,18 +2802,35 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// Prepare the cursor cell contents. // Prepare the cursor cell contents.
const style = cursor_style_ orelse break :cursor; const style = cursor_style_ orelse break :cursor;
const cursor_color = self.cursor_color orelse self.default_cursor_color orelse color: { const cursor_color = cursor_color: {
if (self.cursor_invert) { // If an explicit cursor color was set by OSC 12, use that.
// Use the foreground color from the cell under the cursor, if any. if (self.cursor_color) |v| break :cursor_color v;
const sty = screen.cursor.page_pin.style(screen.cursor.page_cell);
break :color if (sty.flags.inverse) // Use our configured color if specified
// If the cell is reversed, use background color instead. if (self.default_cursor_color) |v| switch (v) {
(sty.bg(screen.cursor.page_cell, color_palette) orelse self.background_color orelse self.default_background_color) .color => |color| break :cursor_color color.toTerminalRGB(),
else inline .@"cell-foreground",
(sty.fg(color_palette, self.config.bold_is_bright) orelse self.foreground_color orelse self.default_foreground_color); .@"cell-background",
} else { => |_, tag| {
break :color self.foreground_color orelse self.default_foreground_color; const sty = screen.cursor.page_pin.style(screen.cursor.page_cell);
} const fg_style = sty.fg(
color_palette,
self.config.bold_is_bright,
) orelse self.foreground_color orelse self.default_foreground_color;
const bg_style = sty.bg(
screen.cursor.page_cell,
color_palette,
) orelse self.background_color orelse self.default_background_color;
break :cursor_color switch (tag) {
.color => unreachable,
.@"cell-foreground" => if (sty.flags.inverse) bg_style else fg_style,
.@"cell-background" => if (sty.flags.inverse) fg_style else bg_style,
};
},
};
break :cursor_color self.foreground_color orelse self.default_foreground_color;
}; };
self.addCursor(screen, style, cursor_color); self.addCursor(screen, style, cursor_color);
@ -2853,18 +2855,25 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
.wide, .spacer_tail => true, .wide, .spacer_tail => true,
}; };
const uniform_color = if (self.cursor_invert) blk: { const uniform_color = if (self.config.cursor_text) |txt| blk: {
// Use the background color from the cell under the cursor, if any. // If cursor-text is set, then compute the correct color.
// Otherwise, use the background color.
if (txt == .color) {
// Use the color set by cursor-text, if any.
break :blk txt.color.toTerminalRGB();
}
const sty = screen.cursor.page_pin.style(screen.cursor.page_cell); const sty = screen.cursor.page_pin.style(screen.cursor.page_cell);
break :blk if (sty.flags.inverse) const fg_style = sty.fg(color_palette, self.config.bold_is_bright) orelse self.foreground_color orelse self.default_foreground_color;
// If the cell is reversed, use foreground color instead. const bg_style = sty.bg(screen.cursor.page_cell, color_palette) orelse self.background_color orelse self.default_background_color;
(sty.fg(color_palette, self.config.bold_is_bright) orelse self.foreground_color orelse self.default_foreground_color)
else break :blk switch (txt) {
(sty.bg(screen.cursor.page_cell, color_palette) orelse self.background_color orelse self.default_background_color); // If the cell is reversed, use the opposite cell color instead.
} else if (self.config.cursor_text) |txt| .@"cell-foreground" => if (sty.flags.inverse) bg_style else fg_style,
txt .@"cell-background" => if (sty.flags.inverse) fg_style else bg_style,
else else => unreachable,
self.background_color orelse self.default_background_color; };
} else self.background_color orelse self.default_background_color;
self.uniforms.cursor_color = .{ self.uniforms.cursor_color = .{
uniform_color.r, uniform_color.r,

View File

@ -163,8 +163,7 @@ pub const DerivedConfig = struct {
image_storage_limit: usize, image_storage_limit: usize,
cursor_style: terminalpkg.CursorStyle, cursor_style: terminalpkg.CursorStyle,
cursor_blink: ?bool, cursor_blink: ?bool,
cursor_color: ?configpkg.Config.Color, cursor_color: ?configpkg.Config.TerminalColor,
cursor_invert: bool,
foreground: configpkg.Config.Color, foreground: configpkg.Config.Color,
background: configpkg.Config.Color, background: configpkg.Config.Color,
osc_color_report_format: configpkg.Config.OSCColorReportFormat, osc_color_report_format: configpkg.Config.OSCColorReportFormat,
@ -185,7 +184,6 @@ pub const DerivedConfig = struct {
.cursor_style = config.@"cursor-style", .cursor_style = config.@"cursor-style",
.cursor_blink = config.@"cursor-style-blink", .cursor_blink = config.@"cursor-style-blink",
.cursor_color = config.@"cursor-color", .cursor_color = config.@"cursor-color",
.cursor_invert = config.@"cursor-invert-fg-bg",
.foreground = config.foreground, .foreground = config.foreground,
.background = config.background, .background = config.background,
.osc_color_report_format = config.@"osc-color-report-format", .osc_color_report_format = config.@"osc-color-report-format",
@ -265,10 +263,16 @@ pub fn init(self: *Termio, alloc: Allocator, opts: termio.Options) !void {
// Create our stream handler. This points to memory in self so it // Create our stream handler. This points to memory in self so it
// isn't safe to use until self.* is set. // isn't safe to use until self.* is set.
const handler: StreamHandler = handler: { const handler: StreamHandler = handler: {
const default_cursor_color = if (!opts.config.cursor_invert and opts.config.cursor_color != null) const default_cursor_color: ?terminalpkg.color.RGB = color: {
opts.config.cursor_color.?.toTerminalRGB() if (opts.config.cursor_color) |color| switch (color) {
else .color => break :color color.color.toTerminalRGB(),
null; .@"cell-foreground",
.@"cell-background",
=> {},
};
break :color null;
};
break :handler .{ break :handler .{
.alloc = alloc, .alloc = alloc,

View File

@ -121,10 +121,16 @@ pub const StreamHandler = struct {
self.default_background_color = config.background.toTerminalRGB(); self.default_background_color = config.background.toTerminalRGB();
self.default_cursor_style = config.cursor_style; self.default_cursor_style = config.cursor_style;
self.default_cursor_blink = config.cursor_blink; self.default_cursor_blink = config.cursor_blink;
self.default_cursor_color = if (!config.cursor_invert and config.cursor_color != null) self.default_cursor_color = color: {
config.cursor_color.?.toTerminalRGB() if (config.cursor_color) |color| switch (color) {
else .color => break :color color.color.toTerminalRGB(),
null; .@"cell-foreground",
.@"cell-background",
=> {},
};
break :color null;
};
// If our cursor is the default, then we update it immediately. // If our cursor is the default, then we update it immediately.
if (self.default_cursor) self.setCursorStyle(.default) catch |err| { if (self.default_cursor) self.setCursorStyle(.default) catch |err| {