mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-23 20:26:09 +03:00
termio/exec: process APC callbacks
This commit is contained in:
@ -66,9 +66,15 @@ pub fn Stream(comptime Handler: type) type {
|
|||||||
.dcs_hook => |dcs| log.warn("unhandled DCS hook: {}", .{dcs}),
|
.dcs_hook => |dcs| log.warn("unhandled DCS hook: {}", .{dcs}),
|
||||||
.dcs_put => |code| log.warn("unhandled DCS put: {x}", .{code}),
|
.dcs_put => |code| log.warn("unhandled DCS put: {x}", .{code}),
|
||||||
.dcs_unhook => log.warn("unhandled DCS unhook", .{}),
|
.dcs_unhook => log.warn("unhandled DCS unhook", .{}),
|
||||||
.apc_start => log.warn("unhandled APC start", .{}),
|
.apc_start => if (@hasDecl(T, "apcStart")) {
|
||||||
.apc_put => |code| log.warn("unhandled APC put: {x}", .{code}),
|
try self.handler.apcStart();
|
||||||
.apc_end => log.warn("unhandled APC end", .{}),
|
} else log.warn("unimplemented APC start", .{}),
|
||||||
|
.apc_put => |code| if (@hasDecl(T, "apcPut")) {
|
||||||
|
try self.handler.apcPut(code);
|
||||||
|
} else log.warn("unimplemented APC put: {x}", .{code}),
|
||||||
|
.apc_end => if (@hasDecl(T, "apcEnd")) {
|
||||||
|
try self.handler.apcEnd();
|
||||||
|
} else log.warn("unimplemented APC end", .{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,10 +47,6 @@ subprocess: Subprocess,
|
|||||||
/// just stores internal state about a grid.
|
/// just stores internal state about a grid.
|
||||||
terminal: terminal.Terminal,
|
terminal: terminal.Terminal,
|
||||||
|
|
||||||
/// The stream parser. This parses the stream of escape codes and so on
|
|
||||||
/// from the child process and calls callbacks in the stream handler.
|
|
||||||
terminal_stream: terminal.Stream(StreamHandler),
|
|
||||||
|
|
||||||
/// The shared render state
|
/// The shared render state
|
||||||
renderer_state: *renderer.State,
|
renderer_state: *renderer.State,
|
||||||
|
|
||||||
@ -114,7 +110,6 @@ pub fn init(alloc: Allocator, opts: termio.Options) !Exec {
|
|||||||
return Exec{
|
return Exec{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.terminal = term,
|
.terminal = term,
|
||||||
.terminal_stream = undefined,
|
|
||||||
.subprocess = subprocess,
|
.subprocess = subprocess,
|
||||||
.renderer_state = opts.renderer_state,
|
.renderer_state = opts.renderer_state,
|
||||||
.renderer_wakeup = opts.renderer_wakeup,
|
.renderer_wakeup = opts.renderer_wakeup,
|
||||||
@ -445,6 +440,9 @@ const EventData = struct {
|
|||||||
|
|
||||||
// Stop our process watcher
|
// Stop our process watcher
|
||||||
self.process.deinit();
|
self.process.deinit();
|
||||||
|
|
||||||
|
// Clear any StreamHandler state
|
||||||
|
self.terminal_stream.handler.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This queues a render operation with the renderer thread. The render
|
/// This queues a render operation with the renderer thread. The render
|
||||||
@ -1050,11 +1048,25 @@ const StreamHandler = struct {
|
|||||||
grid_size: *renderer.GridSize,
|
grid_size: *renderer.GridSize,
|
||||||
terminal: *terminal.Terminal,
|
terminal: *terminal.Terminal,
|
||||||
|
|
||||||
|
/// The APC command data. This is always heap-allocated and freed on
|
||||||
|
/// apcEnd because APC commands are so very rare.
|
||||||
|
apc_data: std.ArrayListUnmanaged(u8) = .{},
|
||||||
|
apc_state: enum {
|
||||||
|
inactive,
|
||||||
|
ignore,
|
||||||
|
verify,
|
||||||
|
collect,
|
||||||
|
} = .inactive,
|
||||||
|
|
||||||
/// This is set to true when a message was written to the writer
|
/// This is set to true when a message was written to the writer
|
||||||
/// mailbox. This can be used by callers to determine if they need
|
/// mailbox. This can be used by callers to determine if they need
|
||||||
/// to wake up the writer.
|
/// to wake up the writer.
|
||||||
writer_messaged: bool = false,
|
writer_messaged: bool = false,
|
||||||
|
|
||||||
|
pub fn deinit(self: *StreamHandler) void {
|
||||||
|
self.apc_data.deinit(self.alloc);
|
||||||
|
}
|
||||||
|
|
||||||
inline fn queueRender(self: *StreamHandler) !void {
|
inline fn queueRender(self: *StreamHandler) !void {
|
||||||
try self.ev.queueRender();
|
try self.ev.queueRender();
|
||||||
}
|
}
|
||||||
@ -1064,6 +1076,44 @@ const StreamHandler = struct {
|
|||||||
self.writer_messaged = true;
|
self.writer_messaged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn apcStart(self: *StreamHandler) !void {
|
||||||
|
assert(self.apc_data.items.len == 0);
|
||||||
|
self.apc_state = .verify;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apcPut(self: *StreamHandler, byte: u8) !void {
|
||||||
|
switch (self.apc_state) {
|
||||||
|
.inactive => unreachable,
|
||||||
|
|
||||||
|
// We're ignoring this APC command, likely because we don't
|
||||||
|
// recognize it so there is no need to store the data in memory.
|
||||||
|
.ignore => return,
|
||||||
|
|
||||||
|
// Verify it is a command we expect
|
||||||
|
.verify => {
|
||||||
|
switch (byte) {
|
||||||
|
'G' => {},
|
||||||
|
else => {
|
||||||
|
log.warn("unrecognized APC command, first byte: {x}", .{byte});
|
||||||
|
self.apc_state = .ignore;
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
self.apc_state = .collect;
|
||||||
|
},
|
||||||
|
|
||||||
|
.collect => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.apc_data.append(self.alloc, byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apcEnd(self: *StreamHandler) !void {
|
||||||
|
self.apc_state = .inactive;
|
||||||
|
self.apc_data.clearAndFree(self.alloc);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn print(self: *StreamHandler, ch: u21) !void {
|
pub fn print(self: *StreamHandler, ch: u21) !void {
|
||||||
try self.terminal.print(ch);
|
try self.terminal.print(ch);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user