vaxis: add support for color scheme updates

This commit is contained in:
Tim Culverhouse 2024-05-26 06:52:06 -05:00
parent 0fa082033e
commit 93ac8e00f8
7 changed files with 89 additions and 4 deletions

View file

@ -30,7 +30,7 @@ Vaxis uses zig `0.12.0`.
| System Notifications (OSC 777) | ✅ | ✅ | ❌ | | System Notifications (OSC 777) | ✅ | ✅ | ❌ |
| Synchronized Output (DEC 2026) | ✅ | ✅ | ✅ | | Synchronized Output (DEC 2026) | ✅ | ✅ | ✅ |
| Unicode Core (DEC 2027) | ✅ | ✅ | ❌ | | Unicode Core (DEC 2027) | ✅ | ✅ | ❌ |
| Color Mode Updates (DEC 2031) | ✅ | planned | ❌ | | Color Mode Updates (DEC 2031) | ✅ | | ❌ |
| Images (full/space) | ✅ | planned | ✅ | | Images (full/space) | ✅ | planned | ✅ |
| Images (half block) | ✅ | planned | ✅ | | Images (half block) | ✅ | planned | ✅ |
| Images (quadrant) | ✅ | planned | ✅ | | Images (quadrant) | ✅ | planned | ✅ |

View file

@ -118,6 +118,11 @@ pub const Color = union(enum) {
value: [3]u8, value: [3]u8,
}; };
pub const Scheme = enum {
dark,
light,
};
pub fn eql(a: Color, b: Color) bool { pub fn eql(a: Color, b: Color) bool {
switch (a) { switch (a) {
.default => return b == .default, .default => return b == .default,

View file

@ -341,6 +341,47 @@ pub fn parse(self: *Parser, input: []const u8, paste_allocator: ?std.mem.Allocat
}, },
} }
}, },
'n' => {
switch (seq.params[0]) {
5 => {
// "Ok" response
return .{
.event = null,
.n = i + 1,
};
},
997 => {
switch (seq.params[1]) {
1 => {
return .{
.event = .{ .color_scheme = .dark },
.n = i + 1,
};
},
2 => {
return .{
.event = .{ .color_scheme = .dark },
.n = i + 1,
};
},
else => {
log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]});
return .{
.event = null,
.n = i + 1,
};
},
}
},
else => {
log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]});
return .{
.event = null,
.n = i + 1,
};
},
}
},
'u' => blk: { 'u' => blk: {
if (seq.private_indicator) |priv| { if (seq.private_indicator) |priv| {
// response to our kitty query // response to our kitty query
@ -417,7 +458,12 @@ pub fn parse(self: *Parser, input: []const u8, paste_allocator: ?std.mem.Allocat
else => return .{ .event = .cap_unicode, .n = i + 1 }, else => return .{ .event = .cap_unicode, .n = i + 1 },
} }
}, },
2031 => {}, 2031 => {
switch (seq.params[1]) {
0, 4 => return .{ .event = null, .n = i + 1 },
else => return .{ .event = .cap_color_scheme_updates, .n = i + 1 },
}
},
else => { else => {
log.warn("unhandled DECRPM: CSI {s}", .{input[start + 1 .. i + 1]}); log.warn("unhandled DECRPM: CSI {s}", .{input[start + 1 .. i + 1]});
return .{ .event = null, .n = i + 1 }; return .{ .event = null, .n = i + 1 };

View file

@ -32,6 +32,7 @@ state: struct {
bracketed_paste: bool = false, bracketed_paste: bool = false,
mouse: bool = false, mouse: bool = false,
pixel_mouse: bool = false, pixel_mouse: bool = false,
color_scheme_updates: bool = false,
cursor: struct { cursor: struct {
row: usize = 0, row: usize = 0,
col: usize = 0, col: usize = 0,
@ -67,6 +68,9 @@ pub fn deinit(self: *Tty) void {
if (self.state.alt_screen) { if (self.state.alt_screen) {
_ = self.write(ctlseqs.rmcup) catch {}; _ = self.write(ctlseqs.rmcup) catch {};
} }
if (self.state.color_scheme_updates) {
_ = self.write(ctlseqs.color_scheme_reset) catch {};
}
// always show the cursor on exit // always show the cursor on exit
_ = self.write(ctlseqs.show_cursor) catch {}; _ = self.write(ctlseqs.show_cursor) catch {};
self.flush() catch {}; self.flush() catch {};
@ -224,6 +228,11 @@ pub fn run(
loop.postEvent(.{ .color_report = report }); loop.postEvent(.{ .color_report = report });
} }
}, },
.color_scheme => |scheme| {
if (@hasField(Event, "color_scheme")) {
loop.postEvent(.{ .color_scheme = scheme });
}
},
.cap_kitty_keyboard => { .cap_kitty_keyboard => {
log.info("kitty keyboard capability detected", .{}); log.info("kitty keyboard capability detected", .{});
loop.vaxis.caps.kitty_keyboard = true; loop.vaxis.caps.kitty_keyboard = true;
@ -247,6 +256,10 @@ pub fn run(
log.info("pixel mouse capability detected", .{}); log.info("pixel mouse capability detected", .{});
loop.vaxis.caps.sgr_pixels = true; loop.vaxis.caps.sgr_pixels = true;
}, },
.cap_color_scheme_updates => {
log.info("color_scheme_updates capability detected", .{});
loop.vaxis.caps.color_scheme_updates = true;
},
.cap_da1 => { .cap_da1 => {
std.Thread.Futex.wake(&loop.vaxis.query_futex, 10); std.Thread.Futex.wake(&loop.vaxis.query_futex, 10);
}, },

View file

@ -32,6 +32,7 @@ pub const Capabilities = struct {
rgb: bool = false, rgb: bool = false,
unicode: gwidth.Method = .wcwidth, unicode: gwidth.Method = .wcwidth,
sgr_pixels: bool = false, sgr_pixels: bool = false,
color_scheme_updates: bool = false,
}; };
pub const Options = struct { pub const Options = struct {
@ -202,7 +203,7 @@ pub fn queryTerminalSend(self: *Vaxis) !void {
// _ = try tty.write(ctlseqs.decrqm_sync); // _ = try tty.write(ctlseqs.decrqm_sync);
_ = try tty.write(ctlseqs.decrqm_sgr_pixels); _ = try tty.write(ctlseqs.decrqm_sgr_pixels);
_ = try tty.write(ctlseqs.decrqm_unicode); _ = try tty.write(ctlseqs.decrqm_unicode);
_ = try tty.write(ctlseqs.decrqm_color_theme); _ = try tty.write(ctlseqs.decrqm_color_scheme);
// TODO: XTVERSION has a DCS response. uncomment when we can parse // TODO: XTVERSION has a DCS response. uncomment when we can parse
// that // that
// _ = try tty.write(ctlseqs.xtversion); // _ = try tty.write(ctlseqs.xtversion);
@ -872,3 +873,16 @@ pub fn queryColor(self: Vaxis, kind: Cell.Color.Kind) !void {
} }
try tty.flush(); try tty.flush();
} }
/// Subscribe to color theme updates. A `color_scheme: Color.Scheme` tag must
/// exist on your Event type to receive the response. This is a queried
/// capability. Support can be detected by checking the value of
/// vaxis.caps.color_scheme_updates. The initial scheme will be reported when
/// subscribing.
pub fn subscribeToColorSchemeUpdates(self: Vaxis) !void {
var tty = self.tty orelse return;
_ = try tty.write(ctlseqs.color_scheme_request);
_ = try tty.write(ctlseqs.color_scheme_set);
try tty.flush();
tty.state.color_scheme_updates = true;
}

View file

@ -7,7 +7,7 @@ pub const decrqm_focus = "\x1b[?1004$p";
pub const decrqm_sgr_pixels = "\x1b[?1016$p"; pub const decrqm_sgr_pixels = "\x1b[?1016$p";
pub const decrqm_sync = "\x1b[?2026$p"; pub const decrqm_sync = "\x1b[?2026$p";
pub const decrqm_unicode = "\x1b[?2027$p"; pub const decrqm_unicode = "\x1b[?2027$p";
pub const decrqm_color_theme = "\x1b[?2031$p"; pub const decrqm_color_scheme = "\x1b[?2031$p";
pub const csi_u_query = "\x1b[?u"; pub const csi_u_query = "\x1b[?u";
pub const kitty_graphics_query = "\x1b_Gi=1,a=q\x1b\\"; pub const kitty_graphics_query = "\x1b_Gi=1,a=q\x1b\\";
pub const sixel_geometry_query = "\x1b[?2;1;0S"; pub const sixel_geometry_query = "\x1b[?2;1;0S";
@ -29,6 +29,11 @@ pub const unicode_reset = "\x1b[?2027l";
pub const bp_set = "\x1b[?2004h"; pub const bp_set = "\x1b[?2004h";
pub const bp_reset = "\x1b[?2004l"; pub const bp_reset = "\x1b[?2004l";
// color scheme updates
pub const color_scheme_request = "\x1b[?996n";
pub const color_scheme_set = "\x1b[?2031h";
pub const color_scheme_reset = "\x1b[?2031l";
// Key encoding // Key encoding
pub const csi_u_push = "\x1b[>{d}u"; pub const csi_u_push = "\x1b[>{d}u";
pub const csi_u_pop = "\x1b[<u"; pub const csi_u_pop = "\x1b[<u";

View file

@ -13,6 +13,7 @@ pub const Event = union(enum) {
paste_end, // bracketed paste end paste_end, // bracketed paste end
paste: []const u8, // osc 52 paste, caller must free paste: []const u8, // osc 52 paste, caller must free
color_report: Color.Report, // osc 4, 10, 11, 12 response color_report: Color.Report, // osc 4, 10, 11, 12 response
color_scheme: Color.Scheme,
// these are delivered as discovered terminal capabilities // these are delivered as discovered terminal capabilities
cap_kitty_keyboard, cap_kitty_keyboard,
@ -21,4 +22,5 @@ pub const Event = union(enum) {
cap_sgr_pixels, cap_sgr_pixels,
cap_unicode, cap_unicode,
cap_da1, cap_da1,
cap_color_scheme_updates,
}; };