From 93ac8e00f880e50007d6cf049df28e44b7de889c Mon Sep 17 00:00:00 2001 From: Tim Culverhouse Date: Sun, 26 May 2024 06:52:06 -0500 Subject: [PATCH] vaxis: add support for color scheme updates --- README.md | 2 +- src/Cell.zig | 5 +++++ src/Parser.zig | 48 +++++++++++++++++++++++++++++++++++++++++++++++- src/Tty.zig | 13 +++++++++++++ src/Vaxis.zig | 16 +++++++++++++++- src/ctlseqs.zig | 7 ++++++- src/event.zig | 2 ++ 7 files changed, 89 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3316894..25b0574 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Vaxis uses zig `0.12.0`. | System Notifications (OSC 777) | ✅ | ✅ | ❌ | | Synchronized Output (DEC 2026) | ✅ | ✅ | ✅ | | Unicode Core (DEC 2027) | ✅ | ✅ | ❌ | -| Color Mode Updates (DEC 2031) | ✅ | planned | ❌ | +| Color Mode Updates (DEC 2031) | ✅ | ✅ | ❌ | | Images (full/space) | ✅ | planned | ✅ | | Images (half block) | ✅ | planned | ✅ | | Images (quadrant) | ✅ | planned | ✅ | diff --git a/src/Cell.zig b/src/Cell.zig index c4364a2..15d5484 100644 --- a/src/Cell.zig +++ b/src/Cell.zig @@ -118,6 +118,11 @@ pub const Color = union(enum) { value: [3]u8, }; + pub const Scheme = enum { + dark, + light, + }; + pub fn eql(a: Color, b: Color) bool { switch (a) { .default => return b == .default, diff --git a/src/Parser.zig b/src/Parser.zig index d10ec9e..7cbcc37 100644 --- a/src/Parser.zig +++ b/src/Parser.zig @@ -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: { if (seq.private_indicator) |priv| { // 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 }, } }, - 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 => { log.warn("unhandled DECRPM: CSI {s}", .{input[start + 1 .. i + 1]}); return .{ .event = null, .n = i + 1 }; diff --git a/src/Tty.zig b/src/Tty.zig index 643e5b4..8dd83fd 100644 --- a/src/Tty.zig +++ b/src/Tty.zig @@ -32,6 +32,7 @@ state: struct { bracketed_paste: bool = false, mouse: bool = false, pixel_mouse: bool = false, + color_scheme_updates: bool = false, cursor: struct { row: usize = 0, col: usize = 0, @@ -67,6 +68,9 @@ pub fn deinit(self: *Tty) void { if (self.state.alt_screen) { _ = self.write(ctlseqs.rmcup) catch {}; } + if (self.state.color_scheme_updates) { + _ = self.write(ctlseqs.color_scheme_reset) catch {}; + } // always show the cursor on exit _ = self.write(ctlseqs.show_cursor) catch {}; self.flush() catch {}; @@ -224,6 +228,11 @@ pub fn run( loop.postEvent(.{ .color_report = report }); } }, + .color_scheme => |scheme| { + if (@hasField(Event, "color_scheme")) { + loop.postEvent(.{ .color_scheme = scheme }); + } + }, .cap_kitty_keyboard => { log.info("kitty keyboard capability detected", .{}); loop.vaxis.caps.kitty_keyboard = true; @@ -247,6 +256,10 @@ pub fn run( log.info("pixel mouse capability detected", .{}); 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 => { std.Thread.Futex.wake(&loop.vaxis.query_futex, 10); }, diff --git a/src/Vaxis.zig b/src/Vaxis.zig index 3f7ed0b..136cbd9 100644 --- a/src/Vaxis.zig +++ b/src/Vaxis.zig @@ -32,6 +32,7 @@ pub const Capabilities = struct { rgb: bool = false, unicode: gwidth.Method = .wcwidth, sgr_pixels: bool = false, + color_scheme_updates: bool = false, }; 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_sgr_pixels); _ = 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 // that // _ = try tty.write(ctlseqs.xtversion); @@ -872,3 +873,16 @@ pub fn queryColor(self: Vaxis, kind: Cell.Color.Kind) !void { } 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; +} diff --git a/src/ctlseqs.zig b/src/ctlseqs.zig index b0af893..33acfd1 100644 --- a/src/ctlseqs.zig +++ b/src/ctlseqs.zig @@ -7,7 +7,7 @@ pub const decrqm_focus = "\x1b[?1004$p"; pub const decrqm_sgr_pixels = "\x1b[?1016$p"; pub const decrqm_sync = "\x1b[?2026$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 kitty_graphics_query = "\x1b_Gi=1,a=q\x1b\\"; 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_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 pub const csi_u_push = "\x1b[>{d}u"; pub const csi_u_pop = "\x1b[