diff --git a/src/inspector/termio.zig b/src/inspector/termio.zig index 190902bb3c..1b499f9dd0 100644 --- a/src/inspector/termio.zig +++ b/src/inspector/termio.zig @@ -279,7 +279,7 @@ pub const VTEvent = struct { ), else => switch (Value) { - u8 => try md.put( + u8, u16 => try md.put( key, try std.fmt.allocPrintZ(alloc, "{}", .{value}), ), diff --git a/src/terminal/osc.zig b/src/terminal/osc.zig index dc56e10fd4..e9ab5e1e39 100644 --- a/src/terminal/osc.zig +++ b/src/terminal/osc.zig @@ -67,7 +67,7 @@ pub const Command = union(enum) { /// End of current command. /// - /// The exit-code need not be specified if if there are no options, + /// The exit-code need not be specified if there are no options, /// or if the command was cancelled (no OSC "133;C"), such as by typing /// an interrupt/cancel character (typically ctrl-C) during line-editing. /// Otherwise, it must be an integer code, where 0 means the command @@ -158,6 +158,11 @@ pub const Command = union(enum) { /// End a hyperlink (OSC 8) hyperlink_end: void, + /// Sleep (OSC 9;1) + sleep: struct { + duration_ms: u16, + }, + /// Show GUI message Box (OSC 9;2) show_message_box: []const u8, @@ -362,6 +367,8 @@ pub const Parser = struct { osc_9, // ConEmu specific substates + conemu_sleep, + conemu_sleep_value, conemu_message_box, conemu_tab, conemu_tab_txt, @@ -789,6 +796,9 @@ pub const Parser = struct { }, .osc_9 => switch (c) { + '1' => { + self.state = .conemu_sleep; + }, '2' => { self.state = .conemu_message_box; }, @@ -806,6 +816,16 @@ pub const Parser = struct { else => self.showDesktopNotification(), }, + .conemu_sleep => switch (c) { + ';' => { + self.command = .{ .sleep = .{ .duration_ms = 100 } }; + self.buf_start = self.buf_idx; + self.complete = true; + self.state = .conemu_sleep_value; + }, + else => self.state = .invalid, + }, + .conemu_message_box => switch (c) { ';' => { self.command = .{ .show_message_box = undefined }; @@ -817,6 +837,10 @@ pub const Parser = struct { else => self.state = .invalid, }, + .conemu_sleep_value => switch (c) { + else => self.complete = true, + }, + .conemu_tab => switch (c) { ';' => { self.state = .conemu_tab_txt; @@ -1193,6 +1217,22 @@ pub const Parser = struct { self.temp_state.str.* = self.buf[self.buf_start..self.buf_idx]; } + fn endConEmuSleepValue(self: *Parser) void { + switch (self.command) { + .sleep => |*v| v.duration_ms = value: { + const str = self.buf[self.buf_start..self.buf_idx]; + if (str.len == 0) break :value 100; + + if (std.fmt.parseUnsigned(u16, str, 10)) |num| { + break :value @min(num, 10_000); + } else |_| { + break :value 100; + } + }, + else => {}, + } + } + fn endKittyColorProtocolOption(self: *Parser, kind: enum { key_only, key_and_value }, final: bool) void { if (self.temp_state.key.len == 0) { log.warn("zero length key in kitty color protocol", .{}); @@ -1271,6 +1311,7 @@ pub const Parser = struct { .semantic_option_value => self.endSemanticOptionValue(), .hyperlink_uri => self.endHyperlink(), .string => self.endString(), + .conemu_sleep_value => self.endConEmuSleepValue(), .allocable_string => self.endAllocableString(), .kitty_color_protocol_key => self.endKittyColorProtocolOption(.key_only, true), .kitty_color_protocol_value => self.endKittyColorProtocolOption(.key_and_value, true), @@ -1680,6 +1721,62 @@ test "OSC: set palette color" { try testing.expectEqualStrings(cmd.set_color.value, "rgb:aa/bb/cc"); } +test "OSC: conemu sleep" { + const testing = std.testing; + + var p: Parser = .{}; + + const input = "9;1;420"; + for (input) |ch| p.next(ch); + + const cmd = p.end('\x1b').?; + + try testing.expect(cmd == .sleep); + try testing.expectEqual(420, cmd.sleep.duration_ms); +} + +test "OSC: conemu sleep with no value default to 100ms" { + const testing = std.testing; + + var p: Parser = .{}; + + const input = "9;1;"; + for (input) |ch| p.next(ch); + + const cmd = p.end('\x1b').?; + + try testing.expect(cmd == .sleep); + try testing.expectEqual(100, cmd.sleep.duration_ms); +} + +test "OSC: conemu sleep cannot exceed 10000ms" { + const testing = std.testing; + + var p: Parser = .{}; + + const input = "9;1;12345"; + for (input) |ch| p.next(ch); + + const cmd = p.end('\x1b').?; + + try testing.expect(cmd == .sleep); + try testing.expectEqual(10000, cmd.sleep.duration_ms); +} + +test "OSC: conemu sleep invalid input" { + const testing = std.testing; + + var p: Parser = .{}; + + const input = "9;1;foo"; + for (input) |ch| p.next(ch); + + const cmd = p.end('\x1b').?; + + try testing.expect(cmd == .sleep); + try testing.expectEqual(100, cmd.sleep.duration_ms); +} + test "OSC: show desktop notification" { const testing = std.testing; diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index b83f2dc1ee..f75d86c0a4 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -1605,7 +1605,7 @@ pub fn Stream(comptime Handler: type) type { } else log.warn("unimplemented OSC callback: {}", .{cmd}); }, - .progress, .show_message_box, .change_conemu_tab_title => { + .progress, .sleep, .show_message_box, .change_conemu_tab_title => { log.warn("unimplemented OSC callback: {}", .{cmd}); }, }