Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: parse ConEmu OSC9;1 #4327

Merged
merged 4 commits into from
Jan 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/inspector/termio.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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}),
),
Expand Down
99 changes: 98 additions & 1 deletion src/terminal/osc.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -789,6 +796,9 @@ pub const Parser = struct {
},

.osc_9 => switch (c) {
'1' => {
self.state = .conemu_sleep;
},
'2' => {
self.state = .conemu_message_box;
},
Expand All @@ -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 };
Expand All @@ -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;
Expand Down Expand Up @@ -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", .{});
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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);
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should test invalid codes as well, perhaps parameters that contain something other than digits.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed in c98d207

test "OSC: show desktop notification" {
const testing = std.testing;

Expand Down
2 changes: 1 addition & 1 deletion src/terminal/stream.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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});
},
}
Expand Down