mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
core: separate default colors from modifiable colors
Default colors are those set by the user in the config file, or an actual default value if unset. The actual colors are modifiable and can be changed using the OSC 4, 10, and 11 sequences.
This commit is contained in:
@ -64,6 +64,14 @@ padding: renderer.Options.Padding,
|
||||
/// True if the window is focused
|
||||
focused: bool,
|
||||
|
||||
/// The actual foreground color. May differ from the config foreground color if
|
||||
/// changed by a terminal application
|
||||
foreground_color: terminal.color.RGB,
|
||||
|
||||
/// The actual background color. May differ from the config background color if
|
||||
/// changed by a terminal application
|
||||
background_color: terminal.color.RGB,
|
||||
|
||||
/// 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
|
||||
/// cells goes into a separate shader.
|
||||
@ -254,6 +262,8 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
||||
.screen_size = null,
|
||||
.padding = options.padding,
|
||||
.focused = true,
|
||||
.foreground_color = options.config.foreground,
|
||||
.background_color = options.config.background,
|
||||
|
||||
// Render state
|
||||
.cells_bg = .{},
|
||||
@ -461,15 +471,15 @@ pub fn render(
|
||||
}
|
||||
|
||||
// Swap bg/fg if the terminal is reversed
|
||||
const bg = self.config.background;
|
||||
const fg = self.config.foreground;
|
||||
const bg = self.background_color;
|
||||
const fg = self.foreground_color;
|
||||
defer {
|
||||
self.config.background = bg;
|
||||
self.config.foreground = fg;
|
||||
self.background_color = bg;
|
||||
self.foreground_color = fg;
|
||||
}
|
||||
if (state.terminal.modes.get(.reverse_colors)) {
|
||||
self.config.background = fg;
|
||||
self.config.foreground = bg;
|
||||
self.background_color = fg;
|
||||
self.foreground_color = bg;
|
||||
}
|
||||
|
||||
// We used to share terminal state, but we've since learned through
|
||||
@ -509,7 +519,7 @@ pub fn render(
|
||||
}
|
||||
|
||||
break :critical .{
|
||||
.bg = self.config.background,
|
||||
.bg = self.background_color,
|
||||
.selection = selection,
|
||||
.screen = screen_copy,
|
||||
.preedit = if (cursor_style != null) state.preedit else null,
|
||||
@ -1267,21 +1277,21 @@ pub fn updateCell(
|
||||
const colors: BgFg = colors: {
|
||||
// If we are selected, we our colors are just inverted fg/bg
|
||||
var selection_res: ?BgFg = if (selected) .{
|
||||
.bg = self.config.selection_background orelse self.config.foreground,
|
||||
.fg = self.config.selection_foreground orelse self.config.background,
|
||||
.bg = self.config.selection_background orelse self.foreground_color,
|
||||
.fg = self.config.selection_foreground orelse self.background_color,
|
||||
} else null;
|
||||
|
||||
const res: BgFg = selection_res orelse 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.config.foreground,
|
||||
.fg = if (cell.attrs.has_fg) cell.fg else self.foreground_color,
|
||||
} 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.config.foreground,
|
||||
.fg = if (cell.attrs.has_bg) cell.bg else self.config.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,
|
||||
};
|
||||
|
||||
// If the cell is "invisible" then we just make fg = bg so that
|
||||
@ -1289,7 +1299,7 @@ pub fn updateCell(
|
||||
if (cell.attrs.invisible) {
|
||||
break :colors BgFg{
|
||||
.bg = res.bg,
|
||||
.fg = res.bg orelse self.config.background,
|
||||
.fg = res.bg orelse self.background_color,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1316,7 +1326,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.config.background)) {
|
||||
if (cell.attrs.has_bg and !std.meta.eql(rgb, self.background_color)) {
|
||||
break :bg_alpha alpha;
|
||||
}
|
||||
|
||||
@ -1434,7 +1444,7 @@ fn addCursor(
|
||||
), screen.cursor.x - 1 };
|
||||
};
|
||||
|
||||
const color = self.config.cursor_color orelse self.config.foreground;
|
||||
const color = self.config.cursor_color orelse self.foreground_color;
|
||||
const alpha: u8 = if (!self.focused) 255 else alpha: {
|
||||
const alpha = 255 * self.config.cursor_opacity;
|
||||
break :alpha @intFromFloat(@ceil(alpha));
|
||||
|
@ -263,11 +263,11 @@ fn drainMailbox(self: *Thread) !void {
|
||||
},
|
||||
|
||||
.foreground_color => |color| {
|
||||
self.renderer.config.foreground = color;
|
||||
self.renderer.foreground_color = color;
|
||||
},
|
||||
|
||||
.background_color => |color| {
|
||||
self.renderer.config.background = color;
|
||||
self.renderer.background_color = color;
|
||||
},
|
||||
|
||||
.resize => |v| {
|
||||
|
@ -94,8 +94,8 @@ pub const Command = union(enum) {
|
||||
value: []const u8,
|
||||
},
|
||||
|
||||
/// OSC 4, OSC 10, and OSC 11 default color report.
|
||||
report_default_color: struct {
|
||||
/// OSC 4, OSC 10, and OSC 11 color report.
|
||||
report_color: struct {
|
||||
/// OSC 4 requests a palette color, OSC 10 requests the foreground
|
||||
/// color, OSC 11 the background color.
|
||||
kind: DefaultColorKind,
|
||||
@ -105,7 +105,7 @@ pub const Command = union(enum) {
|
||||
terminator: Terminator = .st,
|
||||
},
|
||||
|
||||
set_default_color: struct {
|
||||
set_color: struct {
|
||||
/// OSC 4 sets a palette color, OSC 10 sets the foreground color, OSC 11
|
||||
/// the background color.
|
||||
kind: DefaultColorKind,
|
||||
@ -390,20 +390,20 @@ pub const Parser = struct {
|
||||
|
||||
.color_palette_index_end => switch (c) {
|
||||
'?' => {
|
||||
self.command = .{ .report_default_color = .{
|
||||
self.command = .{ .report_color = .{
|
||||
.kind = .{ .palette = @intCast(self.temp_state.num) },
|
||||
} };
|
||||
|
||||
self.complete = true;
|
||||
},
|
||||
else => {
|
||||
self.command = .{ .set_default_color = .{
|
||||
self.command = .{ .set_color = .{
|
||||
.kind = .{ .palette = @intCast(self.temp_state.num) },
|
||||
.value = "",
|
||||
} };
|
||||
|
||||
self.state = .string;
|
||||
self.temp_state = .{ .str = &self.command.set_default_color.value };
|
||||
self.temp_state = .{ .str = &self.command.set_color.value };
|
||||
self.buf_start = self.buf_idx - 1;
|
||||
},
|
||||
},
|
||||
@ -456,34 +456,34 @@ pub const Parser = struct {
|
||||
|
||||
.query_default_fg => switch (c) {
|
||||
'?' => {
|
||||
self.command = .{ .report_default_color = .{ .kind = .foreground } };
|
||||
self.command = .{ .report_color = .{ .kind = .foreground } };
|
||||
self.complete = true;
|
||||
},
|
||||
else => {
|
||||
self.command = .{ .set_default_color = .{
|
||||
self.command = .{ .set_color = .{
|
||||
.kind = .foreground,
|
||||
.value = "",
|
||||
} };
|
||||
|
||||
self.state = .string;
|
||||
self.temp_state = .{ .str = &self.command.set_default_color.value };
|
||||
self.temp_state = .{ .str = &self.command.set_color.value };
|
||||
self.buf_start = self.buf_idx - 1;
|
||||
},
|
||||
},
|
||||
|
||||
.query_default_bg => switch (c) {
|
||||
'?' => {
|
||||
self.command = .{ .report_default_color = .{ .kind = .background } };
|
||||
self.command = .{ .report_color = .{ .kind = .background } };
|
||||
self.complete = true;
|
||||
},
|
||||
else => {
|
||||
self.command = .{ .set_default_color = .{
|
||||
self.command = .{ .set_color = .{
|
||||
.kind = .background,
|
||||
.value = "",
|
||||
} };
|
||||
|
||||
self.state = .string;
|
||||
self.temp_state = .{ .str = &self.command.set_default_color.value };
|
||||
self.temp_state = .{ .str = &self.command.set_color.value };
|
||||
self.buf_start = self.buf_idx - 1;
|
||||
},
|
||||
},
|
||||
@ -694,7 +694,7 @@ pub const Parser = struct {
|
||||
}
|
||||
|
||||
switch (self.command) {
|
||||
.report_default_color => |*c| c.terminator = Terminator.init(terminator_ch),
|
||||
.report_color => |*c| c.terminator = Terminator.init(terminator_ch),
|
||||
else => {},
|
||||
}
|
||||
|
||||
@ -980,9 +980,9 @@ test "OSC: report default foreground color" {
|
||||
|
||||
// This corresponds to ST = ESC followed by \
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .report_default_color);
|
||||
try testing.expectEqual(cmd.report_default_color.kind, .foreground);
|
||||
try testing.expectEqual(cmd.report_default_color.terminator, .st);
|
||||
try testing.expect(cmd == .report_color);
|
||||
try testing.expectEqual(cmd.report_color.kind, .foreground);
|
||||
try testing.expectEqual(cmd.report_color.terminator, .st);
|
||||
}
|
||||
|
||||
test "OSC: set foreground color" {
|
||||
@ -994,9 +994,9 @@ test "OSC: set foreground color" {
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x07').?;
|
||||
try testing.expect(cmd == .set_default_color);
|
||||
try testing.expectEqual(cmd.set_default_color.kind, .foreground);
|
||||
try testing.expectEqualStrings(cmd.set_default_color.value, "rgbi:0.0/0.5/1.0");
|
||||
try testing.expect(cmd == .set_color);
|
||||
try testing.expectEqual(cmd.set_color.kind, .foreground);
|
||||
try testing.expectEqualStrings(cmd.set_color.value, "rgbi:0.0/0.5/1.0");
|
||||
}
|
||||
|
||||
test "OSC: report default background color" {
|
||||
@ -1009,9 +1009,9 @@ test "OSC: report default background color" {
|
||||
|
||||
// This corresponds to ST = BEL character
|
||||
const cmd = p.end('\x07').?;
|
||||
try testing.expect(cmd == .report_default_color);
|
||||
try testing.expectEqual(cmd.report_default_color.kind, .background);
|
||||
try testing.expectEqual(cmd.report_default_color.terminator, .bel);
|
||||
try testing.expect(cmd == .report_color);
|
||||
try testing.expectEqual(cmd.report_color.kind, .background);
|
||||
try testing.expectEqual(cmd.report_color.terminator, .bel);
|
||||
}
|
||||
|
||||
test "OSC: set background color" {
|
||||
@ -1023,9 +1023,9 @@ test "OSC: set background color" {
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .set_default_color);
|
||||
try testing.expectEqual(cmd.set_default_color.kind, .background);
|
||||
try testing.expectEqualStrings(cmd.set_default_color.value, "rgb:f/ff/ffff");
|
||||
try testing.expect(cmd == .set_color);
|
||||
try testing.expectEqual(cmd.set_color.kind, .background);
|
||||
try testing.expectEqualStrings(cmd.set_color.value, "rgb:f/ff/ffff");
|
||||
}
|
||||
|
||||
test "OSC: get palette color" {
|
||||
@ -1037,9 +1037,9 @@ test "OSC: get palette color" {
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .report_default_color);
|
||||
try testing.expectEqual(cmd.report_default_color.kind, .{ .palette = 1 });
|
||||
try testing.expectEqual(cmd.report_default_color.terminator, .st);
|
||||
try testing.expect(cmd == .report_color);
|
||||
try testing.expectEqual(cmd.report_color.kind, .{ .palette = 1 });
|
||||
try testing.expectEqual(cmd.report_color.terminator, .st);
|
||||
}
|
||||
|
||||
test "OSC: set palette color" {
|
||||
@ -1051,7 +1051,7 @@ test "OSC: set palette color" {
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?;
|
||||
try testing.expect(cmd == .set_default_color);
|
||||
try testing.expectEqual(cmd.set_default_color.kind, .{ .palette = 17 });
|
||||
try testing.expectEqualStrings(cmd.set_default_color.value, "rgb:aa/bb/cc");
|
||||
try testing.expect(cmd == .set_color);
|
||||
try testing.expectEqual(cmd.set_color.kind, .{ .palette = 17 });
|
||||
try testing.expectEqualStrings(cmd.set_color.value, "rgb:aa/bb/cc");
|
||||
}
|
||||
|
@ -1045,16 +1045,16 @@ pub fn Stream(comptime Handler: type) type {
|
||||
} else log.warn("unimplemented OSC callback: {}", .{cmd});
|
||||
},
|
||||
|
||||
.report_default_color => |v| {
|
||||
if (@hasDecl(T, "reportDefaultColor")) {
|
||||
try self.handler.reportDefaultColor(v.kind, v.terminator);
|
||||
.report_color => |v| {
|
||||
if (@hasDecl(T, "reportColor")) {
|
||||
try self.handler.reportColor(v.kind, v.terminator);
|
||||
return;
|
||||
} else log.warn("unimplemented OSC callback: {}", .{cmd});
|
||||
},
|
||||
|
||||
.set_default_color => |v| {
|
||||
if (@hasDecl(T, "setDefaultColor")) {
|
||||
try self.handler.setDefaultColor(v.kind, v.value);
|
||||
.set_color => |v| {
|
||||
if (@hasDecl(T, "setColor")) {
|
||||
try self.handler.setColor(v.kind, v.value);
|
||||
return;
|
||||
} else log.warn("unimplemented OSC callback: {}", .{cmd});
|
||||
},
|
||||
|
@ -70,12 +70,18 @@ grid_size: renderer.GridSize,
|
||||
default_cursor_style: terminal.Cursor.Style,
|
||||
default_cursor_blink: ?bool,
|
||||
|
||||
/// Default foreground color for OSC 10 reporting.
|
||||
/// Default foreground color as set by the config file
|
||||
default_foreground_color: terminal.color.RGB,
|
||||
|
||||
/// Default background color for OSC 11 reporting.
|
||||
/// Default background color as set by the config file
|
||||
default_background_color: terminal.color.RGB,
|
||||
|
||||
/// Actual foreground color
|
||||
foreground_color: terminal.color.RGB,
|
||||
|
||||
/// Actual background color
|
||||
background_color: terminal.color.RGB,
|
||||
|
||||
/// The OSC 10/11 reply style.
|
||||
osc_color_report_format: configpkg.Config.OSCColorReportFormat,
|
||||
|
||||
@ -171,6 +177,8 @@ pub fn init(alloc: Allocator, opts: termio.Options) !Exec {
|
||||
.default_cursor_blink = opts.config.cursor_blink,
|
||||
.default_foreground_color = config.foreground.toTerminalRGB(),
|
||||
.default_background_color = config.background.toTerminalRGB(),
|
||||
.foreground_color = config.foreground.toTerminalRGB(),
|
||||
.background_color = config.background.toTerminalRGB(),
|
||||
.osc_color_report_format = config.osc_color_report_format,
|
||||
.data = null,
|
||||
};
|
||||
@ -238,6 +246,8 @@ pub fn threadEnter(self: *Exec, thread: *termio.Thread) !ThreadData {
|
||||
.default_cursor_blink = self.default_cursor_blink,
|
||||
.default_foreground_color = self.default_foreground_color,
|
||||
.default_background_color = self.default_background_color,
|
||||
.foreground_color = self.foreground_color,
|
||||
.background_color = self.background_color,
|
||||
.osc_color_report_format = self.osc_color_report_format,
|
||||
},
|
||||
|
||||
@ -328,7 +338,7 @@ pub fn changeConfig(self: *Exec, config: *DerivedConfig) !void {
|
||||
self.default_cursor_style = config.cursor_style;
|
||||
self.default_cursor_blink = config.cursor_blink;
|
||||
|
||||
// Update foreground and background colors
|
||||
// Update default foreground and background colors
|
||||
self.default_foreground_color = config.foreground.toTerminalRGB();
|
||||
self.default_background_color = config.background.toTerminalRGB();
|
||||
|
||||
@ -1330,8 +1340,19 @@ const StreamHandler = struct {
|
||||
default_cursor: bool = true,
|
||||
default_cursor_style: terminal.Cursor.Style,
|
||||
default_cursor_blink: ?bool,
|
||||
|
||||
/// The default foreground and background color are those set by the user's
|
||||
/// config file. These can be overridden by terminal applications using OSC
|
||||
/// 10 and OSC 11, respectively.
|
||||
default_foreground_color: terminal.color.RGB,
|
||||
default_background_color: terminal.color.RGB,
|
||||
|
||||
/// The actual foreground and background color. Normally this will be the
|
||||
/// same as the default foreground and background color, unless changed by a
|
||||
/// terminal application.
|
||||
foreground_color: terminal.color.RGB,
|
||||
background_color: terminal.color.RGB,
|
||||
|
||||
osc_color_report_format: configpkg.Config.OSCColorReportFormat,
|
||||
|
||||
pub fn deinit(self: *StreamHandler) void {
|
||||
@ -2156,7 +2177,7 @@ const StreamHandler = struct {
|
||||
|
||||
/// Implements OSC 4, OSC 10, and OSC 11, which reports palette color,
|
||||
/// default foreground color, and background color respectively.
|
||||
pub fn reportDefaultColor(
|
||||
pub fn reportColor(
|
||||
self: *StreamHandler,
|
||||
kind: terminal.osc.Command.DefaultColorKind,
|
||||
terminator: terminal.osc.Terminator,
|
||||
@ -2164,8 +2185,8 @@ const StreamHandler = struct {
|
||||
if (self.osc_color_report_format == .none) return;
|
||||
|
||||
const color = switch (kind) {
|
||||
.foreground => self.default_foreground_color,
|
||||
.background => self.default_background_color,
|
||||
.foreground => self.foreground_color,
|
||||
.background => self.background_color,
|
||||
.palette => |i| self.terminal.color_palette[i],
|
||||
};
|
||||
|
||||
@ -2201,7 +2222,7 @@ const StreamHandler = struct {
|
||||
self.messageWriter(msg);
|
||||
}
|
||||
|
||||
pub fn setDefaultColor(
|
||||
pub fn setColor(
|
||||
self: *StreamHandler,
|
||||
kind: terminal.osc.Command.DefaultColorKind,
|
||||
value: []const u8,
|
||||
@ -2210,13 +2231,13 @@ const StreamHandler = struct {
|
||||
|
||||
switch (kind) {
|
||||
.foreground => {
|
||||
self.default_foreground_color = color;
|
||||
self.foreground_color = color;
|
||||
_ = self.ev.renderer_mailbox.push(.{
|
||||
.foreground_color = color,
|
||||
}, .{ .forever = {} });
|
||||
},
|
||||
.background => {
|
||||
self.default_background_color = color;
|
||||
self.background_color = color;
|
||||
_ = self.ev.renderer_mailbox.push(.{
|
||||
.background_color = color,
|
||||
}, .{ .forever = {} });
|
||||
|
Reference in New Issue
Block a user