mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
core: implement OSC 12 and OSC 112 to query/set/reset cursor color
This commit is contained in:
@ -72,6 +72,10 @@ foreground_color: terminal.color.RGB,
|
||||
/// changed by a terminal application
|
||||
background_color: terminal.color.RGB,
|
||||
|
||||
/// The actual cursor color. May differ from the config cursor color if changed
|
||||
/// by a terminal application
|
||||
cursor_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.
|
||||
@ -264,6 +268,7 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
||||
.focused = true,
|
||||
.foreground_color = options.config.foreground,
|
||||
.background_color = options.config.background,
|
||||
.cursor_color = options.config.cursor_color,
|
||||
|
||||
// Render state
|
||||
.cells_bg = .{},
|
||||
@ -1444,7 +1449,7 @@ fn addCursor(
|
||||
), screen.cursor.x - 1 };
|
||||
};
|
||||
|
||||
const color = self.config.cursor_color orelse self.foreground_color;
|
||||
const color = self.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));
|
||||
|
@ -75,6 +75,10 @@ foreground_color: terminal.color.RGB,
|
||||
/// changed by a terminal application
|
||||
background_color: terminal.color.RGB,
|
||||
|
||||
/// The actual cursor color. May differ from the config cursor color if changed
|
||||
/// by a terminal application
|
||||
cursor_color: ?terminal.color.RGB,
|
||||
|
||||
/// Padding options
|
||||
padding: renderer.Options.Padding,
|
||||
|
||||
@ -320,6 +324,7 @@ pub fn init(alloc: Allocator, options: renderer.Options) !OpenGL {
|
||||
.focused = true,
|
||||
.foreground_color = options.config.foreground,
|
||||
.background_color = options.config.background,
|
||||
.cursor_color = options.config.cursor_color,
|
||||
.padding = options.padding,
|
||||
.surface_mailbox = options.surface_mailbox,
|
||||
.deferred_font_size = .{ .metrics = metrics },
|
||||
@ -888,7 +893,7 @@ fn addCursor(
|
||||
), screen.cursor.x - 1 };
|
||||
};
|
||||
|
||||
const color = self.config.cursor_color orelse self.foreground_color;
|
||||
const color = self.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));
|
||||
|
@ -270,6 +270,10 @@ fn drainMailbox(self: *Thread) !void {
|
||||
self.renderer.background_color = color;
|
||||
},
|
||||
|
||||
.cursor_color => |color| {
|
||||
self.renderer.cursor_color = color;
|
||||
},
|
||||
|
||||
.resize => |v| {
|
||||
try self.renderer.setScreenSize(v.screen_size, v.padding);
|
||||
},
|
||||
|
@ -29,6 +29,10 @@ pub const Message = union(enum) {
|
||||
/// the config file in response to an OSC 11 command.
|
||||
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 screen size.
|
||||
resize: struct {
|
||||
/// The full screen (drawable) size. This does NOT include padding.
|
||||
|
@ -827,7 +827,8 @@ test "osc: 112 incomplete sequence" {
|
||||
try testing.expect(a[2] == null);
|
||||
|
||||
const cmd = a[0].?.osc_dispatch;
|
||||
try testing.expect(cmd == .reset_cursor_color);
|
||||
try testing.expect(cmd == .reset_color);
|
||||
try testing.expectEqual(cmd.reset_color.kind, .cursor);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,10 +64,6 @@ pub const Command = union(enum) {
|
||||
// TODO: err option
|
||||
},
|
||||
|
||||
/// Reset the color for the cursor. This reverts changes made with
|
||||
/// change/read cursor color.
|
||||
reset_cursor_color: void,
|
||||
|
||||
/// Set or get clipboard contents. If data is null, then the current
|
||||
/// clipboard contents are sent to the pty. If data is set, this
|
||||
/// contents is set on the clipboard.
|
||||
@ -129,12 +125,14 @@ pub const Command = union(enum) {
|
||||
palette: u8,
|
||||
foreground,
|
||||
background,
|
||||
cursor,
|
||||
|
||||
pub fn code(self: ColorKind) []const u8 {
|
||||
return switch (self) {
|
||||
.palette => "4",
|
||||
.foreground => "10",
|
||||
.background => "11",
|
||||
.cursor => "12",
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -218,6 +216,7 @@ pub const Parser = struct {
|
||||
@"1",
|
||||
@"10",
|
||||
@"11",
|
||||
@"12",
|
||||
@"13",
|
||||
@"133",
|
||||
@"2",
|
||||
@ -233,6 +232,9 @@ pub const Parser = struct {
|
||||
// OSC 11 is used to query or set the current background color.
|
||||
query_bg_color,
|
||||
|
||||
// OSC 12 is used to query or set the current cursor color.
|
||||
query_cursor_color,
|
||||
|
||||
// We're in a semantic prompt OSC command but we aren't sure
|
||||
// what the command is yet, i.e. `133;`
|
||||
semantic_prompt,
|
||||
@ -326,6 +328,7 @@ pub const Parser = struct {
|
||||
.@"1" => switch (c) {
|
||||
'0' => self.state = .@"10",
|
||||
'1' => self.state = .@"11",
|
||||
'2' => self.state = .@"12",
|
||||
'3' => self.state = .@"13",
|
||||
else => self.state = .invalid,
|
||||
},
|
||||
@ -357,13 +360,18 @@ pub const Parser = struct {
|
||||
self.state = .invalid;
|
||||
},
|
||||
'2' => {
|
||||
self.command = .{ .reset_color = .{ .kind = .cursor, .value = undefined } };
|
||||
self.complete = true;
|
||||
self.command = .{ .reset_cursor_color = {} };
|
||||
self.state = .invalid;
|
||||
},
|
||||
else => self.state = .invalid,
|
||||
},
|
||||
|
||||
.@"12" => switch (c) {
|
||||
';' => self.state = .query_cursor_color,
|
||||
else => self.state = .invalid,
|
||||
},
|
||||
|
||||
.@"13" => switch (c) {
|
||||
'3' => self.state = .@"133",
|
||||
else => self.state = .invalid,
|
||||
@ -534,6 +542,24 @@ pub const Parser = struct {
|
||||
},
|
||||
},
|
||||
|
||||
.query_cursor_color => switch (c) {
|
||||
'?' => {
|
||||
self.command = .{ .report_color = .{ .kind = .cursor } };
|
||||
self.complete = true;
|
||||
self.state = .invalid;
|
||||
},
|
||||
else => {
|
||||
self.command = .{ .set_color = .{
|
||||
.kind = .cursor,
|
||||
.value = "",
|
||||
} };
|
||||
|
||||
self.state = .string;
|
||||
self.temp_state = .{ .str = &self.command.set_color.value };
|
||||
self.buf_start = self.buf_idx - 1;
|
||||
},
|
||||
},
|
||||
|
||||
.semantic_prompt => switch (c) {
|
||||
'A' => {
|
||||
self.state = .semantic_option_start;
|
||||
@ -912,7 +938,7 @@ test "OSC: end_of_input" {
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
}
|
||||
|
||||
test "OSC: reset_cursor_color" {
|
||||
test "OSC: reset cursor color" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .{};
|
||||
@ -921,7 +947,8 @@ test "OSC: reset_cursor_color" {
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?;
|
||||
try testing.expect(cmd == .reset_cursor_color);
|
||||
try testing.expect(cmd == .reset_color);
|
||||
try testing.expectEqual(cmd.reset_color.kind, .cursor);
|
||||
}
|
||||
|
||||
test "OSC: get/set clipboard" {
|
||||
|
@ -69,6 +69,10 @@ grid_size: renderer.GridSize,
|
||||
/// it when a CSI q with default is called.
|
||||
default_cursor_style: terminal.Cursor.Style,
|
||||
default_cursor_blink: ?bool,
|
||||
default_cursor_color: ?terminal.color.RGB,
|
||||
|
||||
/// Actual cursor color
|
||||
cursor_color: ?terminal.color.RGB,
|
||||
|
||||
/// Default foreground color as set by the config file
|
||||
default_foreground_color: terminal.color.RGB,
|
||||
@ -96,6 +100,7 @@ pub const DerivedConfig = struct {
|
||||
image_storage_limit: usize,
|
||||
cursor_style: terminal.Cursor.Style,
|
||||
cursor_blink: ?bool,
|
||||
cursor_color: ?configpkg.Config.Color,
|
||||
foreground: configpkg.Config.Color,
|
||||
background: configpkg.Config.Color,
|
||||
osc_color_report_format: configpkg.Config.OSCColorReportFormat,
|
||||
@ -112,6 +117,7 @@ pub const DerivedConfig = struct {
|
||||
.image_storage_limit = config.@"image-storage-limit",
|
||||
.cursor_style = config.@"cursor-style",
|
||||
.cursor_blink = config.@"cursor-style-blink",
|
||||
.cursor_color = config.@"cursor-color",
|
||||
.foreground = config.foreground,
|
||||
.background = config.background,
|
||||
.osc_color_report_format = config.@"osc-color-report-format",
|
||||
@ -175,6 +181,14 @@ pub fn init(alloc: Allocator, opts: termio.Options) !Exec {
|
||||
.grid_size = opts.grid_size,
|
||||
.default_cursor_style = opts.config.cursor_style,
|
||||
.default_cursor_blink = opts.config.cursor_blink,
|
||||
.default_cursor_color = if (opts.config.cursor_color) |col|
|
||||
col.toTerminalRGB()
|
||||
else
|
||||
null,
|
||||
.cursor_color = if (opts.config.cursor_color) |col|
|
||||
col.toTerminalRGB()
|
||||
else
|
||||
null,
|
||||
.default_foreground_color = config.foreground.toTerminalRGB(),
|
||||
.default_background_color = config.background.toTerminalRGB(),
|
||||
.foreground_color = config.foreground.toTerminalRGB(),
|
||||
@ -244,6 +258,8 @@ pub fn threadEnter(self: *Exec, thread: *termio.Thread) !ThreadData {
|
||||
.grid_size = &self.grid_size,
|
||||
.default_cursor_style = self.default_cursor_style,
|
||||
.default_cursor_blink = self.default_cursor_blink,
|
||||
.default_cursor_color = self.default_cursor_color,
|
||||
.cursor_color = self.cursor_color,
|
||||
.default_foreground_color = self.default_foreground_color,
|
||||
.default_background_color = self.default_background_color,
|
||||
.foreground_color = self.foreground_color,
|
||||
@ -337,6 +353,10 @@ pub fn changeConfig(self: *Exec, config: *DerivedConfig) !void {
|
||||
// Update our default cursor style
|
||||
self.default_cursor_style = config.cursor_style;
|
||||
self.default_cursor_blink = config.cursor_blink;
|
||||
self.default_cursor_color = if (config.cursor_color) |col|
|
||||
col.toTerminalRGB()
|
||||
else
|
||||
null;
|
||||
|
||||
// Update default foreground and background colors
|
||||
self.default_foreground_color = config.foreground.toTerminalRGB();
|
||||
@ -1340,6 +1360,10 @@ const StreamHandler = struct {
|
||||
default_cursor: bool = true,
|
||||
default_cursor_style: terminal.Cursor.Style,
|
||||
default_cursor_blink: ?bool,
|
||||
default_cursor_color: ?terminal.color.RGB,
|
||||
|
||||
/// Actual cursor color. This can be changed with OSC 12.
|
||||
cursor_color: ?terminal.color.RGB,
|
||||
|
||||
/// The default foreground and background color are those set by the user's
|
||||
/// config file. These can be overridden by terminal applications using OSC
|
||||
@ -2188,6 +2212,7 @@ const StreamHandler = struct {
|
||||
.palette => |i| self.terminal.color_palette.colors[i],
|
||||
.foreground => self.foreground_color,
|
||||
.background => self.background_color,
|
||||
.cursor => self.cursor_color orelse self.foreground_color,
|
||||
};
|
||||
|
||||
var msg: termio.Message = .{ .write_small = .{} };
|
||||
@ -2246,6 +2271,12 @@ const StreamHandler = struct {
|
||||
.background_color = color,
|
||||
}, .{ .forever = {} });
|
||||
},
|
||||
.cursor => {
|
||||
self.cursor_color = color;
|
||||
_ = self.ev.renderer_mailbox.push(.{
|
||||
.cursor_color = color,
|
||||
}, .{ .forever = {} });
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -2295,6 +2326,12 @@ const StreamHandler = struct {
|
||||
.background_color = self.background_color,
|
||||
}, .{ .forever = {} });
|
||||
},
|
||||
.cursor => {
|
||||
self.cursor_color = self.default_cursor_color;
|
||||
_ = self.ev.renderer_mailbox.push(.{
|
||||
.cursor_color = self.cursor_color,
|
||||
}, .{ .forever = {} });
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
Reference in New Issue
Block a user