config: introduce wait-after-command

This commit is contained in:
Mitchell Hashimoto
2024-01-17 08:40:21 -08:00
parent 18dc09c0c3
commit 33bc424d7e
2 changed files with 40 additions and 1 deletions

View File

@ -400,6 +400,14 @@ palette: Palette = .{},
/// flag. For example: `ghostty -e fish --with --custom --args`.
command: ?[]const u8 = null,
/// If true, keep the terminal open after the command exits. Normally,
/// the terminal window closes when the running command (such as a shell)
/// exits. With this true, the terminal window will stay open until any
/// keypress is received.
///
/// This is primarily useful for scripts or debugging.
@"wait-after-command": bool = false,
/// The number of milliseconds of runtime below which we consider a process
/// exit to be abnormal. This is used to show an error message when the
/// process exits too quickly.

View File

@ -86,6 +86,7 @@ pub const DerivedConfig = struct {
term: []const u8,
grapheme_width_method: configpkg.Config.GraphemeWidthMethod,
abnormal_runtime_threshold_ms: u32,
wait_after_command: bool,
enquiry_response: []const u8,
pub fn init(
@ -108,6 +109,7 @@ pub const DerivedConfig = struct {
.term = try alloc.dupe(u8, config.term),
.grapheme_width_method = config.@"grapheme-width-method",
.abnormal_runtime_threshold_ms = config.@"abnormal-command-exit-runtime",
.wait_after_command = config.@"wait-after-command",
.enquiry_response = try alloc.dupe(u8, config.@"enquiry-response"),
// This has to be last so that we copy AFTER the arena allocations
@ -261,6 +263,7 @@ pub fn threadEnter(self: *Exec, thread: *termio.Thread) !ThreadData {
},
},
.abnormal_runtime_threshold_ms = self.config.abnormal_runtime_threshold_ms,
.wait_after_command = self.config.wait_after_command,
};
errdefer ev_data_ptr.deinit(self.alloc);
@ -358,7 +361,11 @@ pub fn changeConfig(self: *Exec, config: *DerivedConfig) !void {
// Update our stream handler. The stream handler uses the same
// renderer mutex so this is safe to do despite being executed
// from another thread.
if (self.data) |data| data.terminal_stream.handler.changeConfig(&self.config);
if (self.data) |data| {
data.abnormal_runtime_threshold_ms = config.abnormal_runtime_threshold_ms;
data.wait_after_command = config.wait_after_command;
data.terminal_stream.handler.changeConfig(&self.config);
}
// Update the configuration that we know about.
//
@ -722,6 +729,10 @@ const EventData = struct {
/// when the process exits too quickly.
abnormal_runtime_threshold_ms: u32,
/// If true, do not immediately send a child exited message to the
/// surface to close the surface when the command exits.
wait_after_command: bool,
pub fn deinit(self: *EventData, alloc: Allocator) void {
// Clear our write pools. We know we aren't ever going to do
// any more IO since we stop our data stream below so we can just
@ -803,6 +814,26 @@ fn processExit(
return .disarm;
}
// If we're purposely waiting then we just return since the process
// exited flag is set to true. This allows the terminal window to remain
// open.
if (ev.wait_after_command) {
// We output a message so that the user knows whats going on and
// doesn't think their terminal just froze.
terminal: {
ev.renderer_state.mutex.lock();
defer ev.renderer_state.mutex.unlock();
const t = ev.renderer_state.terminal;
t.carriageReturn();
t.linefeed() catch break :terminal;
t.printString("Process exited. Press any key to close the terminal.") catch
break :terminal;
t.modes.set(.cursor_visible, false);
}
return .disarm;
}
// Notify our surface we want to close
_ = ev.surface_mailbox.push(.{
.child_exited = {},