this repo has no description
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

vaxis: add support for color reports (OSC 4, 10, 11, 12)

Add support for querying colors (index, foreground, background, and
cursor)

+132
+56
src/Cell.zig
··· 105 105 index: u8, 106 106 rgb: [3]u8, 107 107 108 + pub const Kind = union(enum) { 109 + fg, 110 + bg, 111 + cursor, 112 + index: u8, 113 + }; 114 + 115 + /// Returned when querying a color from the terminal 116 + pub const Report = struct { 117 + kind: Kind, 118 + value: [3]u8, 119 + }; 120 + 108 121 pub fn eql(a: Color, b: Color) bool { 109 122 switch (a) { 110 123 .default => return b == .default, ··· 135 148 @truncate(b_bits), 136 149 }; 137 150 return .{ .rgb = rgb }; 151 + } 152 + 153 + /// parse an XParseColor-style rgb specification into an rgb Color. The spec 154 + /// is of the form: rgb:rrrr/gggg/bbbb. Generally, the high two bits will always 155 + /// be the same as the low two bits. 156 + pub fn rgbFromSpec(spec: []const u8) !Color { 157 + var iter = std.mem.splitScalar(u8, spec, ':'); 158 + const prefix = iter.next() orelse return error.InvalidColorSpec; 159 + if (!std.mem.eql(u8, "rgb", prefix)) return error.InvalidColorSpec; 160 + 161 + const spec_str = iter.next() orelse return error.InvalidColorSpec; 162 + 163 + var spec_iter = std.mem.splitScalar(u8, spec_str, '/'); 164 + 165 + const r_raw = spec_iter.next() orelse return error.InvalidColorSpec; 166 + if (r_raw.len != 4) return error.InvalidColorSpec; 167 + 168 + const g_raw = spec_iter.next() orelse return error.InvalidColorSpec; 169 + if (g_raw.len != 4) return error.InvalidColorSpec; 170 + 171 + const b_raw = spec_iter.next() orelse return error.InvalidColorSpec; 172 + if (b_raw.len != 4) return error.InvalidColorSpec; 173 + 174 + const r = try std.fmt.parseUnsigned(u8, r_raw[2..], 16); 175 + const g = try std.fmt.parseUnsigned(u8, g_raw[2..], 16); 176 + const b = try std.fmt.parseUnsigned(u8, b_raw[2..], 16); 177 + 178 + return .{ 179 + .rgb = [_]u8{ r, g, b }, 180 + }; 181 + } 182 + 183 + test "rgbFromSpec" { 184 + const spec = "rgb:aaaa/bbbb/cccc"; 185 + const actual = try rgbFromSpec(spec); 186 + switch (actual) { 187 + .rgb => |rgb| { 188 + try std.testing.expectEqual(0xAA, rgb[0]); 189 + try std.testing.expectEqual(0xBB, rgb[1]); 190 + try std.testing.expectEqual(0xCC, rgb[2]); 191 + }, 192 + else => try std.testing.expect(false), 193 + } 138 194 } 139 195 };
+45
src/Parser.zig
··· 1 1 const std = @import("std"); 2 2 const testing = std.testing; 3 + const Color = @import("Cell.zig").Color; 3 4 const Event = @import("event.zig").Event; 4 5 const Key = @import("Key.zig"); 5 6 const Mouse = @import("Mouse.zig"); ··· 588 589 seq.param_buf_idx = 0; 589 590 seq.param_idx += 1; 590 591 switch (p) { 592 + 4, 593 + 10, 594 + 11, 595 + 12, 596 + => { 597 + i += 1; 598 + const index: ?u8 = if (p == 4) blk: { 599 + const index_start = i; 600 + const end: usize = while (i < n) : (i += 1) { 601 + if (input[i] == ';') { 602 + i += 1; 603 + break i - 1; 604 + } 605 + } else unreachable; // invalid input 606 + break :blk try std.fmt.parseUnsigned(u8, input[index_start..end], 10); 607 + } else null; 608 + const spec_start = i; 609 + const end: usize = while (i < n) : (i += 1) { 610 + if (input[i] == 0x1B) { 611 + // advance one more for the backslash 612 + i += 1; 613 + break i - 1; 614 + } 615 + } else return .{ 616 + .event = null, 617 + .n = i, 618 + }; 619 + const color = try Color.rgbFromSpec(input[spec_start..end]); 620 + 621 + const event: Color.Report = .{ 622 + .kind = switch (p) { 623 + 4 => .{ .index = index.? }, 624 + 10 => .fg, 625 + 11 => .bg, 626 + 12 => .cursor, 627 + else => unreachable, 628 + }, 629 + .value = color.rgb, 630 + }; 631 + return .{ 632 + .event = .{ .color_report = event }, 633 + .n = i, 634 + }; 635 + }, 591 636 52 => { 592 637 var payload: ?std.ArrayList(u8) = if (paste_allocator) |allocator| 593 638 std.ArrayList(u8).init(allocator)
+5
src/Tty.zig
··· 219 219 paste_allocator.?.free(text); 220 220 } 221 221 }, 222 + .color_report => |report| { 223 + if (@hasField(Event, "color_report")) { 224 + loop.postEvent(.{ .color_report = report }); 225 + } 226 + }, 222 227 .cap_kitty_keyboard => { 223 228 log.info("kitty keyboard capability detected", .{}); 224 229 loop.vaxis.caps.kitty_keyboard = true;
+14
src/Vaxis.zig
··· 858 858 ); 859 859 try tty.flush(); 860 860 } 861 + 862 + /// Request a color report from the terminal. Note: not all terminals support 863 + /// reporting colors. It is always safe to try, but you may not receive a 864 + /// response. 865 + pub fn queryColor(self: Vaxis, kind: Cell.Color.Kind) !void { 866 + var tty = self.tty orelse return; 867 + switch (kind) { 868 + .fg => _ = try tty.write(ctlseqs.osc10_query), 869 + .bg => _ = try tty.write(ctlseqs.osc11_query), 870 + .cursor => _ = try tty.write(ctlseqs.osc12_query), 871 + .index => |idx| try tty.buffered_writer.writer().print(ctlseqs.osc4_query, .{idx}), 872 + } 873 + try tty.flush(); 874 + }
+10
src/ctlseqs.zig
··· 113 113 pub const kitty_graphics_clear = "\x1b_Ga=d\x1b\\"; 114 114 pub const kitty_graphics_preamble = "\x1b_Ga=p,i={d}"; 115 115 pub const kitty_graphics_closing = ",C=1\x1b\\"; 116 + 117 + // Color control sequences 118 + pub const osc4_query = "\x1b]4;{d};?\x1b\\"; // color index {d} 119 + pub const osc4_reset = "\x1b]104\x1b\\"; // this resets _all_ color indexes 120 + pub const osc10_query = "\x1b]10;?\x1b\\"; // fg 121 + pub const osc10_reset = "\x1b]110\x1b\\"; // reset fg to terminal default 122 + pub const osc11_query = "\x1b]11;?\x1b\\"; // bg 123 + pub const osc11_reset = "\x1b]111\x1b\\"; // reset bg to terminal default 124 + pub const osc12_query = "\x1b]12;?\x1b\\"; // cursor color 125 + pub const osc12_reset = "\x1b]112\x1b\\"; // reset cursor to terminal default
+2
src/event.zig
··· 1 1 pub const Key = @import("Key.zig"); 2 2 pub const Mouse = @import("Mouse.zig"); 3 + pub const Color = @import("Cell.zig").Color; 3 4 4 5 /// The events that Vaxis emits internally 5 6 pub const Event = union(enum) { ··· 11 12 paste_start, // bracketed paste start 12 13 paste_end, // bracketed paste end 13 14 paste: []const u8, // osc 52 paste, caller must free 15 + color_report: Color.Report, // osc 4, 10, 11, 12 response 14 16 15 17 // these are delivered as discovered terminal capabilities 16 18 cap_kitty_keyboard,