terminal: all DCS events can produce a command

This commit is contained in:
Mitchell Hashimoto
2024-07-11 18:53:42 -07:00
parent 01e1538ad3
commit f375bf009c
2 changed files with 40 additions and 27 deletions

View File

@ -21,43 +21,54 @@ pub const Handler = struct {
self.discard(); self.discard();
} }
pub fn hook(self: *Handler, alloc: Allocator, dcs: DCS) void { pub fn hook(self: *Handler, alloc: Allocator, dcs: DCS) ?Command {
assert(self.state == .inactive); assert(self.state == .inactive);
self.state = if (tryHook(alloc, dcs)) |state_| state: {
if (state_) |state| break :state state else { // Initialize our state to ignore in case of error
log.info("unknown DCS hook: {}", .{dcs}); self.state = .{ .ignore = {} };
break :state .{ .ignore = {} };
} // Try to parse the hook.
} else |err| state: { const hk_ = tryHook(alloc, dcs) catch |err| {
log.info( log.info("error initializing DCS hook, will ignore hook err={}", .{err});
"error initializing DCS hook, will ignore hook err={}", return null;
.{err},
);
break :state .{ .ignore = {} };
}; };
const hk = hk_ orelse {
log.info("unknown DCS hook: {}", .{dcs});
return null;
};
self.state = hk.state;
return hk.command;
} }
fn tryHook(alloc: Allocator, dcs: DCS) !?State { const Hook = struct {
state: State,
command: ?Command = null,
};
fn tryHook(alloc: Allocator, dcs: DCS) !?Hook {
return switch (dcs.intermediates.len) { return switch (dcs.intermediates.len) {
1 => switch (dcs.intermediates[0]) { 1 => switch (dcs.intermediates[0]) {
'+' => switch (dcs.final) { '+' => switch (dcs.final) {
// XTGETTCAP // XTGETTCAP
// https://github.com/mitchellh/ghostty/issues/517 // https://github.com/mitchellh/ghostty/issues/517
'q' => .{ 'q' => .{
.state = .{
.xtgettcap = try std.ArrayList(u8).initCapacity( .xtgettcap = try std.ArrayList(u8).initCapacity(
alloc, alloc,
128, // Arbitrary choice 128, // Arbitrary choice
), ),
}, },
},
else => null, else => null,
}, },
'$' => switch (dcs.final) { '$' => switch (dcs.final) {
// DECRQSS // DECRQSS
'q' => .{ 'q' => .{ .state = .{
.decrqss = .{}, .decrqss = .{},
}, } },
else => null, else => null,
}, },
@ -222,7 +233,7 @@ test "unknown DCS command" {
var h: Handler = .{}; var h: Handler = .{};
defer h.deinit(); defer h.deinit();
h.hook(alloc, .{ .final = 'A' }); try testing.expect(h.hook(alloc, .{ .final = 'A' }) == null);
try testing.expect(h.state == .ignore); try testing.expect(h.state == .ignore);
try testing.expect(h.unhook() == null); try testing.expect(h.unhook() == null);
try testing.expect(h.state == .inactive); try testing.expect(h.state == .inactive);
@ -234,7 +245,7 @@ test "XTGETTCAP command" {
var h: Handler = .{}; var h: Handler = .{};
defer h.deinit(); defer h.deinit();
h.hook(alloc, .{ .intermediates = "+", .final = 'q' }); try testing.expect(h.hook(alloc, .{ .intermediates = "+", .final = 'q' }) == null);
for ("536D756C78") |byte| _ = h.put(byte); for ("536D756C78") |byte| _ = h.put(byte);
var cmd = h.unhook().?; var cmd = h.unhook().?;
defer cmd.deinit(); defer cmd.deinit();
@ -249,7 +260,7 @@ test "XTGETTCAP command multiple keys" {
var h: Handler = .{}; var h: Handler = .{};
defer h.deinit(); defer h.deinit();
h.hook(alloc, .{ .intermediates = "+", .final = 'q' }); try testing.expect(h.hook(alloc, .{ .intermediates = "+", .final = 'q' }) == null);
for ("536D756C78;536D756C78") |byte| _ = h.put(byte); for ("536D756C78;536D756C78") |byte| _ = h.put(byte);
var cmd = h.unhook().?; var cmd = h.unhook().?;
defer cmd.deinit(); defer cmd.deinit();
@ -265,7 +276,7 @@ test "XTGETTCAP command invalid data" {
var h: Handler = .{}; var h: Handler = .{};
defer h.deinit(); defer h.deinit();
h.hook(alloc, .{ .intermediates = "+", .final = 'q' }); try testing.expect(h.hook(alloc, .{ .intermediates = "+", .final = 'q' }) == null);
for ("who;536D756C78") |byte| _ = h.put(byte); for ("who;536D756C78") |byte| _ = h.put(byte);
var cmd = h.unhook().?; var cmd = h.unhook().?;
defer cmd.deinit(); defer cmd.deinit();
@ -281,7 +292,7 @@ test "DECRQSS command" {
var h: Handler = .{}; var h: Handler = .{};
defer h.deinit(); defer h.deinit();
h.hook(alloc, .{ .intermediates = "$", .final = 'q' }); try testing.expect(h.hook(alloc, .{ .intermediates = "$", .final = 'q' }) == null);
_ = h.put('m'); _ = h.put('m');
var cmd = h.unhook().?; var cmd = h.unhook().?;
defer cmd.deinit(); defer cmd.deinit();
@ -295,7 +306,7 @@ test "DECRQSS invalid command" {
var h: Handler = .{}; var h: Handler = .{};
defer h.deinit(); defer h.deinit();
h.hook(alloc, .{ .intermediates = "$", .final = 'q' }); try testing.expect(h.hook(alloc, .{ .intermediates = "$", .final = 'q' }) == null);
_ = h.put('z'); _ = h.put('z');
var cmd = h.unhook().?; var cmd = h.unhook().?;
defer cmd.deinit(); defer cmd.deinit();
@ -304,7 +315,7 @@ test "DECRQSS invalid command" {
h.discard(); h.discard();
h.hook(alloc, .{ .intermediates = "$", .final = 'q' }); try testing.expect(h.hook(alloc, .{ .intermediates = "$", .final = 'q' }) == null);
_ = h.put('"'); _ = h.put('"');
_ = h.put(' '); _ = h.put(' ');
_ = h.put('q'); _ = h.put('q');

View File

@ -1860,7 +1860,9 @@ const StreamHandler = struct {
} }
pub fn dcsHook(self: *StreamHandler, dcs: terminal.DCS) !void { pub fn dcsHook(self: *StreamHandler, dcs: terminal.DCS) !void {
self.dcs.hook(self.alloc, dcs); var cmd = self.dcs.hook(self.alloc, dcs) orelse return;
defer cmd.deinit();
try self.dcsCommand(&cmd);
} }
pub fn dcsPut(self: *StreamHandler, byte: u8) !void { pub fn dcsPut(self: *StreamHandler, byte: u8) !void {