Merge 61b9f5ed10 into 4df593bd24
commit
68ec478dc4
|
|
@ -41,6 +41,66 @@ pub const Operation = enum {
|
|||
osc_119,
|
||||
};
|
||||
|
||||
/// The color precision to use when formatting OSC color responses.
|
||||
pub const ReportFormat = enum {
|
||||
@"16-bit",
|
||||
@"8-bit",
|
||||
};
|
||||
|
||||
/// Encode an OSC color query response for a palette or dynamic color.
|
||||
pub fn formatReport(
|
||||
writer: anytype,
|
||||
format: ReportFormat,
|
||||
kind: Target,
|
||||
color: RGB,
|
||||
) !void {
|
||||
switch (format) {
|
||||
.@"16-bit" => switch (kind) {
|
||||
.palette => |i| try writer.print(
|
||||
"\x1b]4;{d};rgb:{x:0>4}/{x:0>4}/{x:0>4}",
|
||||
.{
|
||||
i,
|
||||
@as(u16, color.r) * 257,
|
||||
@as(u16, color.g) * 257,
|
||||
@as(u16, color.b) * 257,
|
||||
},
|
||||
),
|
||||
.dynamic => |dynamic| try writer.print(
|
||||
"\x1b]{d};rgb:{x:0>4}/{x:0>4}/{x:0>4}",
|
||||
.{
|
||||
@intFromEnum(dynamic),
|
||||
@as(u16, color.r) * 257,
|
||||
@as(u16, color.g) * 257,
|
||||
@as(u16, color.b) * 257,
|
||||
},
|
||||
),
|
||||
.special => unreachable,
|
||||
},
|
||||
|
||||
.@"8-bit" => switch (kind) {
|
||||
.palette => |i| try writer.print(
|
||||
"\x1b]4;{d};rgb:{x:0>2}/{x:0>2}/{x:0>2}",
|
||||
.{
|
||||
i,
|
||||
@as(u16, color.r),
|
||||
@as(u16, color.g),
|
||||
@as(u16, color.b),
|
||||
},
|
||||
),
|
||||
.dynamic => |dynamic| try writer.print(
|
||||
"\x1b]{d};rgb:{x:0>2}/{x:0>2}/{x:0>2}",
|
||||
.{
|
||||
@intFromEnum(dynamic),
|
||||
@as(u16, color.r),
|
||||
@as(u16, color.g),
|
||||
@as(u16, color.b),
|
||||
},
|
||||
),
|
||||
.special => unreachable,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse OSCs 4, 5, 10-19, 104, 110-119
|
||||
pub fn parse(parser: *Parser, terminator_ch: ?u8) ?*Command {
|
||||
const alloc = parser.alloc orelse {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ const Action = stream.Action;
|
|||
const Screen = @import("Screen.zig");
|
||||
const modes = @import("modes.zig");
|
||||
const osc_color = @import("osc/parsers/color.zig");
|
||||
const osc = @import("osc.zig");
|
||||
const kitty_color = @import("kitty/color.zig");
|
||||
const size_report = @import("size_report.zig");
|
||||
const Terminal = @import("Terminal.zig");
|
||||
|
|
@ -233,7 +234,7 @@ pub const Handler = struct {
|
|||
.end_hyperlink => self.terminal.screens.active.endHyperlink(),
|
||||
.semantic_prompt => try self.terminal.semanticPrompt(value),
|
||||
.mouse_shape => self.terminal.mouse_shape = value,
|
||||
.color_operation => try self.colorOperation(value.op, &value.requests),
|
||||
.color_operation => try self.colorOperation(value.op, &value.requests, value.terminator),
|
||||
.kitty_color_report => try self.kittyColorOperation(value),
|
||||
|
||||
// APC
|
||||
|
|
@ -552,10 +553,19 @@ pub const Handler = struct {
|
|||
self: *Handler,
|
||||
op: osc_color.Operation,
|
||||
requests: *const osc_color.List,
|
||||
terminator: osc.Terminator,
|
||||
) !void {
|
||||
_ = op;
|
||||
if (requests.count() == 0) return;
|
||||
|
||||
var stack = std.heap.stackFallback(256, self.terminal.gpa());
|
||||
const alloc = stack.get();
|
||||
|
||||
var aw: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer aw.deinit();
|
||||
|
||||
var wrote_response = false;
|
||||
|
||||
var it = requests.constIterator(0);
|
||||
while (it.next()) |req| {
|
||||
switch (req.*) {
|
||||
|
|
@ -613,11 +623,51 @@ pub const Handler = struct {
|
|||
mask.* = .initEmpty();
|
||||
},
|
||||
|
||||
.query,
|
||||
.query => |kind| report: {
|
||||
if (self.effects.write_pty == null) break :report;
|
||||
if (!try self.colorQuery(kind, &aw.writer)) break :report;
|
||||
try aw.writer.writeAll(terminator.string());
|
||||
wrote_response = true;
|
||||
},
|
||||
|
||||
.reset_special,
|
||||
=> {},
|
||||
}
|
||||
}
|
||||
|
||||
if (wrote_response) {
|
||||
const resp = aw.toOwnedSliceSentinel(0) catch return;
|
||||
defer alloc.free(resp);
|
||||
self.writePty(resp);
|
||||
}
|
||||
}
|
||||
|
||||
fn colorQuery(
|
||||
self: *Handler,
|
||||
kind: osc_color.Target,
|
||||
writer: *std.Io.Writer,
|
||||
) !bool {
|
||||
const color = switch (kind) {
|
||||
.palette => |i| self.terminal.colors.palette.current[i],
|
||||
.dynamic => |dynamic| switch (dynamic) {
|
||||
.foreground => self.terminal.colors.foreground.get() orelse return false,
|
||||
.background => self.terminal.colors.background.get() orelse return false,
|
||||
.cursor => self.terminal.colors.cursor.get() orelse
|
||||
self.terminal.colors.foreground.get() orelse return false,
|
||||
.pointer_foreground,
|
||||
.pointer_background,
|
||||
.tektronix_foreground,
|
||||
.tektronix_background,
|
||||
.highlight_background,
|
||||
.tektronix_cursor,
|
||||
.highlight_foreground,
|
||||
=> return false,
|
||||
},
|
||||
.special => return false,
|
||||
};
|
||||
|
||||
try osc_color.formatReport(writer, .@"16-bit", kind, color);
|
||||
return true;
|
||||
}
|
||||
|
||||
fn kittyColorOperation(
|
||||
|
|
@ -1022,6 +1072,37 @@ test "OSC 11 set and reset background color" {
|
|||
try testing.expect(t.colors.background.get() == null);
|
||||
}
|
||||
|
||||
test "OSC 10/11 queries write color reports" {
|
||||
var t: Terminal = try .init(testing.allocator, .{ .cols = 10, .rows = 10 });
|
||||
defer t.deinit(testing.allocator);
|
||||
|
||||
t.colors.foreground.default = .{ .r = 0x12, .g = 0x34, .b = 0x56 };
|
||||
t.colors.background.default = .{ .r = 0xaa, .g = 0xbb, .b = 0xcc };
|
||||
|
||||
const S = struct {
|
||||
var buf: [128]u8 = undefined;
|
||||
var written: []const u8 = "";
|
||||
|
||||
fn writePty(_: *Handler, data: [:0]const u8) void {
|
||||
@memcpy(buf[0..data.len], data);
|
||||
written = buf[0..data.len];
|
||||
}
|
||||
};
|
||||
S.written = "";
|
||||
|
||||
var handler: Handler = .init(&t);
|
||||
handler.effects.write_pty = &S.writePty;
|
||||
|
||||
var s: Stream = .initAlloc(testing.allocator, handler);
|
||||
defer s.deinit();
|
||||
|
||||
s.nextSlice("\x1b]10;?\x1b\\");
|
||||
try testing.expectEqualStrings("\x1b]10;rgb:1212/3434/5656\x1b\\", S.written);
|
||||
|
||||
s.nextSlice("\x1b]11;?\x07");
|
||||
try testing.expectEqualStrings("\x1b]11;rgb:aaaa/bbbb/cccc\x07", S.written);
|
||||
}
|
||||
|
||||
test "OSC 12 set and reset cursor color" {
|
||||
var t: Terminal = try .init(testing.allocator, .{ .cols = 10, .rows = 10 });
|
||||
defer t.deinit(testing.allocator);
|
||||
|
|
|
|||
|
|
@ -1370,53 +1370,12 @@ pub const StreamHandler = struct {
|
|||
},
|
||||
};
|
||||
|
||||
switch (self.osc_color_report_format) {
|
||||
.@"16-bit" => switch (kind) {
|
||||
.palette => |i| try writer.print(
|
||||
"\x1b]4;{d};rgb:{x:0>4}/{x:0>4}/{x:0>4}",
|
||||
.{
|
||||
i,
|
||||
@as(u16, color.r) * 257,
|
||||
@as(u16, color.g) * 257,
|
||||
@as(u16, color.b) * 257,
|
||||
},
|
||||
),
|
||||
.dynamic => |dynamic| try writer.print(
|
||||
"\x1b]{d};rgb:{x:0>4}/{x:0>4}/{x:0>4}",
|
||||
.{
|
||||
@intFromEnum(dynamic),
|
||||
@as(u16, color.r) * 257,
|
||||
@as(u16, color.g) * 257,
|
||||
@as(u16, color.b) * 257,
|
||||
},
|
||||
),
|
||||
.special => unreachable,
|
||||
},
|
||||
|
||||
.@"8-bit" => switch (kind) {
|
||||
.palette => |i| try writer.print(
|
||||
"\x1b]4;{d};rgb:{x:0>2}/{x:0>2}/{x:0>2}",
|
||||
.{
|
||||
i,
|
||||
@as(u16, color.r),
|
||||
@as(u16, color.g),
|
||||
@as(u16, color.b),
|
||||
},
|
||||
),
|
||||
.dynamic => |dynamic| try writer.print(
|
||||
"\x1b]{d};rgb:{x:0>2}/{x:0>2}/{x:0>2}",
|
||||
.{
|
||||
@intFromEnum(dynamic),
|
||||
@as(u16, color.r),
|
||||
@as(u16, color.g),
|
||||
@as(u16, color.b),
|
||||
},
|
||||
),
|
||||
.special => unreachable,
|
||||
},
|
||||
|
||||
const report_format: terminal.osc.color.ReportFormat = switch (self.osc_color_report_format) {
|
||||
.@"16-bit" => .@"16-bit",
|
||||
.@"8-bit" => .@"8-bit",
|
||||
.none => unreachable,
|
||||
}
|
||||
};
|
||||
try terminal.osc.color.formatReport(writer, report_format, kind, color);
|
||||
|
||||
try writer.writeAll(terminator.string());
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue