mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
Merge pull request #453 from mitchellh/cc-osc-10-11
Implement OSC 10 and OSC 11 default color queries
This commit is contained in:
@ -305,6 +305,22 @@ keybind: Keybinds = .{},
|
|||||||
/// The default value is "detect".
|
/// The default value is "detect".
|
||||||
@"shell-integration": ShellIntegration = .detect,
|
@"shell-integration": ShellIntegration = .detect,
|
||||||
|
|
||||||
|
/// Sets the reporting format for OSC sequences that request color information.
|
||||||
|
/// Ghostty currently supports OSC 10 (foreground) and OSC 11 (background) queries,
|
||||||
|
/// and by default the reported values are scaled-up RGB values, where each component
|
||||||
|
/// are 16 bits. This is how most terminals report these values. However, some legacy
|
||||||
|
/// applications may require 8-bit, unscaled, components. We also support turning off
|
||||||
|
/// reporting alltogether. The components are lowercase hex values.
|
||||||
|
///
|
||||||
|
/// Allowable values are:
|
||||||
|
///
|
||||||
|
/// * "none" - OSC 10/11 queries receive no reply
|
||||||
|
/// * "8-bit" - Color components are return unscaled, i.e. rr/gg/bb
|
||||||
|
/// * "16-bit" - Color components are returned scaled, e.g. rrrr/gggg/bbbb
|
||||||
|
///
|
||||||
|
/// The default value is "16-bit".
|
||||||
|
@"osc-color-report-format": OSCColorReportFormat = .@"16-bit",
|
||||||
|
|
||||||
/// If anything other than false, fullscreen mode on macOS will not use the
|
/// If anything other than false, fullscreen mode on macOS will not use the
|
||||||
/// native fullscreen, but make the window fullscreen without animations and
|
/// native fullscreen, but make the window fullscreen without animations and
|
||||||
/// using a new space. It's faster than the native fullscreen mode since it
|
/// using a new space. It's faster than the native fullscreen mode since it
|
||||||
@ -1480,3 +1496,10 @@ pub const ShellIntegration = enum {
|
|||||||
fish,
|
fish,
|
||||||
zsh,
|
zsh,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// OSC 10 and 11 default color reporting format.
|
||||||
|
pub const OSCColorReportFormat = enum {
|
||||||
|
none,
|
||||||
|
@"8-bit",
|
||||||
|
@"16-bit",
|
||||||
|
};
|
||||||
|
@ -248,7 +248,7 @@ pub fn next(self: *Parser, c: u8) [3]?Action {
|
|||||||
return [3]?Action{
|
return [3]?Action{
|
||||||
// Exit depends on current state
|
// Exit depends on current state
|
||||||
if (self.state == next_state) null else switch (self.state) {
|
if (self.state == next_state) null else switch (self.state) {
|
||||||
.osc_string => if (self.osc_parser.end()) |cmd|
|
.osc_string => if (self.osc_parser.end(c)) |cmd|
|
||||||
Action{ .osc_dispatch = cmd }
|
Action{ .osc_dispatch = cmd }
|
||||||
else
|
else
|
||||||
null,
|
null,
|
||||||
|
@ -6,6 +6,7 @@ const ansi = @import("ansi.zig");
|
|||||||
const csi = @import("csi.zig");
|
const csi = @import("csi.zig");
|
||||||
const sgr = @import("sgr.zig");
|
const sgr = @import("sgr.zig");
|
||||||
pub const apc = @import("apc.zig");
|
pub const apc = @import("apc.zig");
|
||||||
|
pub const osc = @import("osc.zig");
|
||||||
pub const point = @import("point.zig");
|
pub const point = @import("point.zig");
|
||||||
pub const color = @import("color.zig");
|
pub const color = @import("color.zig");
|
||||||
pub const kitty = @import("kitty.zig");
|
pub const kitty = @import("kitty.zig");
|
||||||
|
@ -91,6 +91,57 @@ pub const Command = union(enum) {
|
|||||||
mouse_shape: struct {
|
mouse_shape: struct {
|
||||||
value: []const u8,
|
value: []const u8,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// OSC 10 and OSC 11 default color report.
|
||||||
|
report_default_color: struct {
|
||||||
|
/// OSC 10 requests the foreground color, OSC 11 the background color.
|
||||||
|
kind: DefaultColorKind,
|
||||||
|
|
||||||
|
/// We must reply with the same string terminator (ST) as used in the
|
||||||
|
/// request.
|
||||||
|
terminator: Terminator = .st,
|
||||||
|
},
|
||||||
|
|
||||||
|
pub const DefaultColorKind = enum {
|
||||||
|
foreground,
|
||||||
|
background,
|
||||||
|
|
||||||
|
pub fn code(self: DefaultColorKind) []const u8 {
|
||||||
|
return switch (self) {
|
||||||
|
.foreground => "10",
|
||||||
|
.background => "11",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The terminator used to end an OSC command. For OSC commands that demand
|
||||||
|
/// a response, we try to match the terminator used in the request since that
|
||||||
|
/// is most likely to be accepted by the calling program.
|
||||||
|
pub const Terminator = enum {
|
||||||
|
/// The preferred string terminator is ESC followed by \
|
||||||
|
st,
|
||||||
|
|
||||||
|
/// Some applications and terminals use BELL (0x07) as the string terminator.
|
||||||
|
bel,
|
||||||
|
|
||||||
|
/// Initialize the terminator based on the last byte seen. If the
|
||||||
|
/// last byte is a BEL then we use BEL, otherwise we just assume ST.
|
||||||
|
pub fn init(ch: ?u8) Terminator {
|
||||||
|
return switch (ch orelse return .st) {
|
||||||
|
0x07 => .bel,
|
||||||
|
else => .st,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The terminator as a string. This is static memory so it doesn't
|
||||||
|
/// need to be freed.
|
||||||
|
pub fn string(self: Terminator) []const u8 {
|
||||||
|
return switch (self) {
|
||||||
|
.st => "\x1b\\",
|
||||||
|
.bel => "\x07",
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Parser = struct {
|
pub const Parser = struct {
|
||||||
@ -134,6 +185,7 @@ pub const Parser = struct {
|
|||||||
// but the state space is small enough that we just build it up this way.
|
// but the state space is small enough that we just build it up this way.
|
||||||
@"0",
|
@"0",
|
||||||
@"1",
|
@"1",
|
||||||
|
@"10",
|
||||||
@"11",
|
@"11",
|
||||||
@"13",
|
@"13",
|
||||||
@"133",
|
@"133",
|
||||||
@ -143,6 +195,14 @@ pub const Parser = struct {
|
|||||||
@"52",
|
@"52",
|
||||||
@"7",
|
@"7",
|
||||||
|
|
||||||
|
// OSC 10 is used to query the default foreground color, and to set the default foreground color.
|
||||||
|
// Only querying is currently supported.
|
||||||
|
query_default_fg,
|
||||||
|
|
||||||
|
// OSC 11 is used to query the default background color, and to set the default background color.
|
||||||
|
// Only querying is currently supported.
|
||||||
|
query_default_bg,
|
||||||
|
|
||||||
// We're in a semantic prompt OSC command but we aren't sure
|
// We're in a semantic prompt OSC command but we aren't sure
|
||||||
// what the command is yet, i.e. `133;`
|
// what the command is yet, i.e. `133;`
|
||||||
semantic_prompt,
|
semantic_prompt,
|
||||||
@ -210,12 +270,19 @@ pub const Parser = struct {
|
|||||||
},
|
},
|
||||||
|
|
||||||
.@"1" => switch (c) {
|
.@"1" => switch (c) {
|
||||||
|
'0' => self.state = .@"10",
|
||||||
'1' => self.state = .@"11",
|
'1' => self.state = .@"11",
|
||||||
'3' => self.state = .@"13",
|
'3' => self.state = .@"13",
|
||||||
else => self.state = .invalid,
|
else => self.state = .invalid,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.@"10" => switch (c) {
|
||||||
|
';' => self.state = .query_default_fg,
|
||||||
|
else => self.state = .invalid,
|
||||||
|
},
|
||||||
|
|
||||||
.@"11" => switch (c) {
|
.@"11" => switch (c) {
|
||||||
|
';' => self.state = .query_default_bg,
|
||||||
'2' => {
|
'2' => {
|
||||||
self.complete = true;
|
self.complete = true;
|
||||||
self.command = .{ .reset_cursor_color = {} };
|
self.command = .{ .reset_cursor_color = {} };
|
||||||
@ -303,6 +370,22 @@ pub const Parser = struct {
|
|||||||
else => self.state = .invalid,
|
else => self.state = .invalid,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.query_default_fg => switch (c) {
|
||||||
|
'?' => {
|
||||||
|
self.command = .{ .report_default_color = .{ .kind = .foreground } };
|
||||||
|
self.complete = true;
|
||||||
|
},
|
||||||
|
else => self.state = .invalid,
|
||||||
|
},
|
||||||
|
|
||||||
|
.query_default_bg => switch (c) {
|
||||||
|
'?' => {
|
||||||
|
self.command = .{ .report_default_color = .{ .kind = .background } };
|
||||||
|
self.complete = true;
|
||||||
|
},
|
||||||
|
else => self.state = .invalid,
|
||||||
|
},
|
||||||
|
|
||||||
.semantic_prompt => switch (c) {
|
.semantic_prompt => switch (c) {
|
||||||
'A' => {
|
'A' => {
|
||||||
self.state = .semantic_option_start;
|
self.state = .semantic_option_start;
|
||||||
@ -449,8 +532,10 @@ pub const Parser = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// End the sequence and return the command, if any. If the return value
|
/// End the sequence and return the command, if any. If the return value
|
||||||
/// is null, then no valid command was found.
|
/// is null, then no valid command was found. The optional terminator_ch
|
||||||
pub fn end(self: *Parser) ?Command {
|
/// is the final character in the OSC sequence. This is used to determine
|
||||||
|
/// the response terminator.
|
||||||
|
pub fn end(self: *Parser, terminator_ch: ?u8) ?Command {
|
||||||
if (!self.complete) {
|
if (!self.complete) {
|
||||||
log.warn("invalid OSC command: {s}", .{self.buf[0..self.buf_idx]});
|
log.warn("invalid OSC command: {s}", .{self.buf[0..self.buf_idx]});
|
||||||
return null;
|
return null;
|
||||||
@ -464,6 +549,11 @@ pub const Parser = struct {
|
|||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (self.command) {
|
||||||
|
.report_default_color => |*c| c.terminator = Terminator.init(terminator_ch),
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
|
||||||
return self.command;
|
return self.command;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -476,7 +566,7 @@ test "OSC: change_window_title" {
|
|||||||
p.next(';');
|
p.next(';');
|
||||||
p.next('a');
|
p.next('a');
|
||||||
p.next('b');
|
p.next('b');
|
||||||
const cmd = p.end().?;
|
const cmd = p.end(null).?;
|
||||||
try testing.expect(cmd == .change_window_title);
|
try testing.expect(cmd == .change_window_title);
|
||||||
try testing.expectEqualStrings("ab", cmd.change_window_title);
|
try testing.expectEqualStrings("ab", cmd.change_window_title);
|
||||||
}
|
}
|
||||||
@ -489,7 +579,7 @@ test "OSC: change_window_title with 2" {
|
|||||||
p.next(';');
|
p.next(';');
|
||||||
p.next('a');
|
p.next('a');
|
||||||
p.next('b');
|
p.next('b');
|
||||||
const cmd = p.end().?;
|
const cmd = p.end(null).?;
|
||||||
try testing.expect(cmd == .change_window_title);
|
try testing.expect(cmd == .change_window_title);
|
||||||
try testing.expectEqualStrings("ab", cmd.change_window_title);
|
try testing.expectEqualStrings("ab", cmd.change_window_title);
|
||||||
}
|
}
|
||||||
@ -502,7 +592,7 @@ test "OSC: prompt_start" {
|
|||||||
const input = "133;A";
|
const input = "133;A";
|
||||||
for (input) |ch| p.next(ch);
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
const cmd = p.end().?;
|
const cmd = p.end(null).?;
|
||||||
try testing.expect(cmd == .prompt_start);
|
try testing.expect(cmd == .prompt_start);
|
||||||
try testing.expect(cmd.prompt_start.aid == null);
|
try testing.expect(cmd.prompt_start.aid == null);
|
||||||
try testing.expect(cmd.prompt_start.redraw);
|
try testing.expect(cmd.prompt_start.redraw);
|
||||||
@ -516,7 +606,7 @@ test "OSC: prompt_start with single option" {
|
|||||||
const input = "133;A;aid=14";
|
const input = "133;A;aid=14";
|
||||||
for (input) |ch| p.next(ch);
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
const cmd = p.end().?;
|
const cmd = p.end(null).?;
|
||||||
try testing.expect(cmd == .prompt_start);
|
try testing.expect(cmd == .prompt_start);
|
||||||
try testing.expectEqualStrings("14", cmd.prompt_start.aid.?);
|
try testing.expectEqualStrings("14", cmd.prompt_start.aid.?);
|
||||||
}
|
}
|
||||||
@ -529,7 +619,7 @@ test "OSC: prompt_start with redraw disabled" {
|
|||||||
const input = "133;A;redraw=0";
|
const input = "133;A;redraw=0";
|
||||||
for (input) |ch| p.next(ch);
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
const cmd = p.end().?;
|
const cmd = p.end(null).?;
|
||||||
try testing.expect(cmd == .prompt_start);
|
try testing.expect(cmd == .prompt_start);
|
||||||
try testing.expect(!cmd.prompt_start.redraw);
|
try testing.expect(!cmd.prompt_start.redraw);
|
||||||
}
|
}
|
||||||
@ -542,7 +632,7 @@ test "OSC: prompt_start with redraw invalid value" {
|
|||||||
const input = "133;A;redraw=42";
|
const input = "133;A;redraw=42";
|
||||||
for (input) |ch| p.next(ch);
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
const cmd = p.end().?;
|
const cmd = p.end(null).?;
|
||||||
try testing.expect(cmd == .prompt_start);
|
try testing.expect(cmd == .prompt_start);
|
||||||
try testing.expect(cmd.prompt_start.redraw);
|
try testing.expect(cmd.prompt_start.redraw);
|
||||||
try testing.expect(cmd.prompt_start.kind == .primary);
|
try testing.expect(cmd.prompt_start.kind == .primary);
|
||||||
@ -556,7 +646,7 @@ test "OSC: prompt_start with continuation" {
|
|||||||
const input = "133;A;k=c";
|
const input = "133;A;k=c";
|
||||||
for (input) |ch| p.next(ch);
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
const cmd = p.end().?;
|
const cmd = p.end(null).?;
|
||||||
try testing.expect(cmd == .prompt_start);
|
try testing.expect(cmd == .prompt_start);
|
||||||
try testing.expect(cmd.prompt_start.kind == .continuation);
|
try testing.expect(cmd.prompt_start.kind == .continuation);
|
||||||
}
|
}
|
||||||
@ -569,7 +659,7 @@ test "OSC: end_of_command no exit code" {
|
|||||||
const input = "133;D";
|
const input = "133;D";
|
||||||
for (input) |ch| p.next(ch);
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
const cmd = p.end().?;
|
const cmd = p.end(null).?;
|
||||||
try testing.expect(cmd == .end_of_command);
|
try testing.expect(cmd == .end_of_command);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -581,7 +671,7 @@ test "OSC: end_of_command with exit code" {
|
|||||||
const input = "133;D;25";
|
const input = "133;D;25";
|
||||||
for (input) |ch| p.next(ch);
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
const cmd = p.end().?;
|
const cmd = p.end(null).?;
|
||||||
try testing.expect(cmd == .end_of_command);
|
try testing.expect(cmd == .end_of_command);
|
||||||
try testing.expectEqual(@as(u8, 25), cmd.end_of_command.exit_code.?);
|
try testing.expectEqual(@as(u8, 25), cmd.end_of_command.exit_code.?);
|
||||||
}
|
}
|
||||||
@ -594,7 +684,7 @@ test "OSC: prompt_end" {
|
|||||||
const input = "133;B";
|
const input = "133;B";
|
||||||
for (input) |ch| p.next(ch);
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
const cmd = p.end().?;
|
const cmd = p.end(null).?;
|
||||||
try testing.expect(cmd == .prompt_end);
|
try testing.expect(cmd == .prompt_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -606,7 +696,7 @@ test "OSC: end_of_input" {
|
|||||||
const input = "133;C";
|
const input = "133;C";
|
||||||
for (input) |ch| p.next(ch);
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
const cmd = p.end().?;
|
const cmd = p.end(null).?;
|
||||||
try testing.expect(cmd == .end_of_input);
|
try testing.expect(cmd == .end_of_input);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -618,7 +708,7 @@ test "OSC: reset_cursor_color" {
|
|||||||
const input = "112";
|
const input = "112";
|
||||||
for (input) |ch| p.next(ch);
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
const cmd = p.end().?;
|
const cmd = p.end(null).?;
|
||||||
try testing.expect(cmd == .reset_cursor_color);
|
try testing.expect(cmd == .reset_cursor_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -630,7 +720,7 @@ test "OSC: get/set clipboard" {
|
|||||||
const input = "52;s;?";
|
const input = "52;s;?";
|
||||||
for (input) |ch| p.next(ch);
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
const cmd = p.end().?;
|
const cmd = p.end(null).?;
|
||||||
try testing.expect(cmd == .clipboard_contents);
|
try testing.expect(cmd == .clipboard_contents);
|
||||||
try testing.expect(cmd.clipboard_contents.kind == 's');
|
try testing.expect(cmd.clipboard_contents.kind == 's');
|
||||||
try testing.expect(std.mem.eql(u8, "?", cmd.clipboard_contents.data));
|
try testing.expect(std.mem.eql(u8, "?", cmd.clipboard_contents.data));
|
||||||
@ -644,7 +734,7 @@ test "OSC: get/set clipboard (optional parameter)" {
|
|||||||
const input = "52;;?";
|
const input = "52;;?";
|
||||||
for (input) |ch| p.next(ch);
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
const cmd = p.end().?;
|
const cmd = p.end(null).?;
|
||||||
try testing.expect(cmd == .clipboard_contents);
|
try testing.expect(cmd == .clipboard_contents);
|
||||||
try testing.expect(cmd.clipboard_contents.kind == 'c');
|
try testing.expect(cmd.clipboard_contents.kind == 'c');
|
||||||
try testing.expect(std.mem.eql(u8, "?", cmd.clipboard_contents.data));
|
try testing.expect(std.mem.eql(u8, "?", cmd.clipboard_contents.data));
|
||||||
@ -658,7 +748,7 @@ test "OSC: report pwd" {
|
|||||||
const input = "7;file:///tmp/example";
|
const input = "7;file:///tmp/example";
|
||||||
for (input) |ch| p.next(ch);
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
const cmd = p.end().?;
|
const cmd = p.end(null).?;
|
||||||
try testing.expect(cmd == .report_pwd);
|
try testing.expect(cmd == .report_pwd);
|
||||||
try testing.expect(std.mem.eql(u8, "file:///tmp/example", cmd.report_pwd.value));
|
try testing.expect(std.mem.eql(u8, "file:///tmp/example", cmd.report_pwd.value));
|
||||||
}
|
}
|
||||||
@ -671,7 +761,7 @@ test "OSC: pointer cursor" {
|
|||||||
const input = "22;pointer";
|
const input = "22;pointer";
|
||||||
for (input) |ch| p.next(ch);
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
const cmd = p.end().?;
|
const cmd = p.end(null).?;
|
||||||
try testing.expect(cmd == .mouse_shape);
|
try testing.expect(cmd == .mouse_shape);
|
||||||
try testing.expect(std.mem.eql(u8, "pointer", cmd.mouse_shape.value));
|
try testing.expect(std.mem.eql(u8, "pointer", cmd.mouse_shape.value));
|
||||||
}
|
}
|
||||||
@ -684,7 +774,7 @@ test "OSC: report pwd empty" {
|
|||||||
const input = "7;";
|
const input = "7;";
|
||||||
for (input) |ch| p.next(ch);
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
try testing.expect(p.end() == null);
|
try testing.expect(p.end(null) == null);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "OSC: longer than buffer" {
|
test "OSC: longer than buffer" {
|
||||||
@ -695,5 +785,35 @@ test "OSC: longer than buffer" {
|
|||||||
const input = "a" ** (Parser.MAX_BUF + 2);
|
const input = "a" ** (Parser.MAX_BUF + 2);
|
||||||
for (input) |ch| p.next(ch);
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
try testing.expect(p.end() == null);
|
try testing.expect(p.end(null) == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "OSC: report default foreground color" {
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
var p: Parser = .{};
|
||||||
|
|
||||||
|
const input = "10;?";
|
||||||
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "OSC: report default background color" {
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
var p: Parser = .{};
|
||||||
|
|
||||||
|
const input = "11;?";
|
||||||
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
|
@ -861,6 +861,12 @@ pub fn Stream(comptime Handler: type) type {
|
|||||||
} else log.warn("unimplemented OSC callback: {}", .{cmd});
|
} else log.warn("unimplemented OSC callback: {}", .{cmd});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.report_default_color => |v| {
|
||||||
|
if (@hasDecl(T, "reportDefaultColor")) {
|
||||||
|
try self.handler.reportDefaultColor(v.kind, v.terminator);
|
||||||
|
} else log.warn("unimplemented OSC callback: {}", .{cmd});
|
||||||
|
},
|
||||||
|
|
||||||
else => if (@hasDecl(T, "oscUnimplemented"))
|
else => if (@hasDecl(T, "oscUnimplemented"))
|
||||||
try self.handler.oscUnimplemented(cmd)
|
try self.handler.oscUnimplemented(cmd)
|
||||||
else
|
else
|
||||||
|
@ -68,6 +68,15 @@ grid_size: renderer.GridSize,
|
|||||||
default_cursor_style: terminal.Cursor.Style,
|
default_cursor_style: terminal.Cursor.Style,
|
||||||
default_cursor_blink: bool,
|
default_cursor_blink: bool,
|
||||||
|
|
||||||
|
/// Default foreground color for OSC 10 reporting.
|
||||||
|
default_foreground_color: terminal.color.RGB,
|
||||||
|
|
||||||
|
/// Default background color for OSC 11 reporting.
|
||||||
|
default_background_color: terminal.color.RGB,
|
||||||
|
|
||||||
|
/// The OSC 10/11 reply style.
|
||||||
|
osc_color_report_format: configpkg.Config.OSCColorReportFormat,
|
||||||
|
|
||||||
/// The data associated with the currently running thread.
|
/// The data associated with the currently running thread.
|
||||||
data: ?*EventData,
|
data: ?*EventData,
|
||||||
|
|
||||||
@ -79,6 +88,9 @@ pub const DerivedConfig = struct {
|
|||||||
image_storage_limit: usize,
|
image_storage_limit: usize,
|
||||||
cursor_style: terminal.Cursor.Style,
|
cursor_style: terminal.Cursor.Style,
|
||||||
cursor_blink: bool,
|
cursor_blink: bool,
|
||||||
|
foreground: configpkg.Config.Color,
|
||||||
|
background: configpkg.Config.Color,
|
||||||
|
osc_color_report_format: configpkg.Config.OSCColorReportFormat,
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
alloc_gpa: Allocator,
|
alloc_gpa: Allocator,
|
||||||
@ -91,6 +103,9 @@ pub const DerivedConfig = struct {
|
|||||||
.image_storage_limit = config.@"image-storage-limit",
|
.image_storage_limit = config.@"image-storage-limit",
|
||||||
.cursor_style = config.@"cursor-style",
|
.cursor_style = config.@"cursor-style",
|
||||||
.cursor_blink = config.@"cursor-style-blink",
|
.cursor_blink = config.@"cursor-style-blink",
|
||||||
|
.foreground = config.foreground,
|
||||||
|
.background = config.background,
|
||||||
|
.osc_color_report_format = config.@"osc-color-report-format",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,6 +155,9 @@ pub fn init(alloc: Allocator, opts: termio.Options) !Exec {
|
|||||||
.grid_size = opts.grid_size,
|
.grid_size = opts.grid_size,
|
||||||
.default_cursor_style = opts.config.cursor_style,
|
.default_cursor_style = opts.config.cursor_style,
|
||||||
.default_cursor_blink = opts.config.cursor_blink,
|
.default_cursor_blink = opts.config.cursor_blink,
|
||||||
|
.default_foreground_color = config.foreground.toTerminalRGB(),
|
||||||
|
.default_background_color = config.background.toTerminalRGB(),
|
||||||
|
.osc_color_report_format = config.osc_color_report_format,
|
||||||
.data = null,
|
.data = null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -204,6 +222,9 @@ pub fn threadEnter(self: *Exec, thread: *termio.Thread) !ThreadData {
|
|||||||
.grid_size = &self.grid_size,
|
.grid_size = &self.grid_size,
|
||||||
.default_cursor_style = self.default_cursor_style,
|
.default_cursor_style = self.default_cursor_style,
|
||||||
.default_cursor_blink = self.default_cursor_blink,
|
.default_cursor_blink = self.default_cursor_blink,
|
||||||
|
.default_foreground_color = self.default_foreground_color,
|
||||||
|
.default_background_color = self.default_background_color,
|
||||||
|
.osc_color_report_format = self.osc_color_report_format,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -1141,6 +1162,9 @@ const StreamHandler = struct {
|
|||||||
default_cursor: bool = true,
|
default_cursor: bool = true,
|
||||||
default_cursor_style: terminal.Cursor.Style,
|
default_cursor_style: terminal.Cursor.Style,
|
||||||
default_cursor_blink: bool,
|
default_cursor_blink: bool,
|
||||||
|
default_foreground_color: terminal.color.RGB,
|
||||||
|
default_background_color: terminal.color.RGB,
|
||||||
|
osc_color_report_format: configpkg.Config.OSCColorReportFormat,
|
||||||
|
|
||||||
pub fn deinit(self: *StreamHandler) void {
|
pub fn deinit(self: *StreamHandler) void {
|
||||||
self.apc.deinit();
|
self.apc.deinit();
|
||||||
@ -1779,4 +1803,50 @@ const StreamHandler = struct {
|
|||||||
self.ev.seen_title = false;
|
self.ev.seen_title = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Implements OSC 10 and OSC 11, which reports default foreground and
|
||||||
|
/// background color respectively.
|
||||||
|
pub fn reportDefaultColor(
|
||||||
|
self: *StreamHandler,
|
||||||
|
kind: terminal.osc.Command.DefaultColorKind,
|
||||||
|
terminator: terminal.osc.Terminator,
|
||||||
|
) !void {
|
||||||
|
if (self.osc_color_report_format == .none) return;
|
||||||
|
|
||||||
|
const color = switch (kind) {
|
||||||
|
.foreground => self.default_foreground_color,
|
||||||
|
.background => self.default_background_color,
|
||||||
|
};
|
||||||
|
|
||||||
|
var msg: termio.Message = .{ .write_small = .{} };
|
||||||
|
const resp = switch (self.osc_color_report_format) {
|
||||||
|
.@"16-bit" => try std.fmt.bufPrint(
|
||||||
|
&msg.write_small.data,
|
||||||
|
"\x1B]{s};rgb:{x:0>4}/{x:0>4}/{x:0>4}{s}",
|
||||||
|
.{
|
||||||
|
kind.code(),
|
||||||
|
@as(u16, color.r) * 257,
|
||||||
|
@as(u16, color.g) * 257,
|
||||||
|
@as(u16, color.b) * 257,
|
||||||
|
terminator.string(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
.@"8-bit" => try std.fmt.bufPrint(
|
||||||
|
&msg.write_small.data,
|
||||||
|
"\x1B]{s};rgb:{x:0>2}/{x:0>2}/{x:0>2}{s}",
|
||||||
|
.{
|
||||||
|
kind.code(),
|
||||||
|
@as(u16, color.r),
|
||||||
|
@as(u16, color.g),
|
||||||
|
@as(u16, color.b),
|
||||||
|
terminator.string(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
.none => unreachable, // early return above
|
||||||
|
};
|
||||||
|
msg.write_small.len = @intCast(resp.len);
|
||||||
|
self.messageWriter(msg);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user