mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 17:26:09 +03:00
Merge pull request #1321 from mitchellh/wait
Add `wait-after-command` configuration to keep terminal window open after command exits
This commit is contained in:
@ -258,7 +258,9 @@ pub const Surface = struct {
|
|||||||
/// The working directory to load into.
|
/// The working directory to load into.
|
||||||
working_directory: [*:0]const u8 = "",
|
working_directory: [*:0]const u8 = "",
|
||||||
|
|
||||||
/// The command to run in the new surface.
|
/// The command to run in the new surface. If this is set then
|
||||||
|
/// the "wait-after-command" option is also automatically set to true,
|
||||||
|
/// since this is used for scripting.
|
||||||
command: [*:0]const u8 = "",
|
command: [*:0]const u8 = "",
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -334,10 +336,10 @@ pub const Surface = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we have a command from the options then we set it.
|
// If we have a command from the options then we set it.
|
||||||
const cm = std.mem.sliceTo(opts.command, 0);
|
const cmd = std.mem.sliceTo(opts.command, 0);
|
||||||
if (cm.len > 0) {
|
if (cmd.len > 0) {
|
||||||
// TODO: Maybe add some validation to this, like the working directory has?
|
config.command = cmd;
|
||||||
config.command = cm;
|
config.@"wait-after-command" = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize our surface right away. We're given a view that is
|
// Initialize our surface right away. We're given a view that is
|
||||||
|
@ -400,6 +400,14 @@ palette: Palette = .{},
|
|||||||
/// flag. For example: `ghostty -e fish --with --custom --args`.
|
/// flag. For example: `ghostty -e fish --with --custom --args`.
|
||||||
command: ?[]const u8 = null,
|
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
|
/// 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
|
/// exit to be abnormal. This is used to show an error message when the
|
||||||
/// process exits too quickly.
|
/// process exits too quickly.
|
||||||
|
@ -86,6 +86,7 @@ pub const DerivedConfig = struct {
|
|||||||
term: []const u8,
|
term: []const u8,
|
||||||
grapheme_width_method: configpkg.Config.GraphemeWidthMethod,
|
grapheme_width_method: configpkg.Config.GraphemeWidthMethod,
|
||||||
abnormal_runtime_threshold_ms: u32,
|
abnormal_runtime_threshold_ms: u32,
|
||||||
|
wait_after_command: bool,
|
||||||
enquiry_response: []const u8,
|
enquiry_response: []const u8,
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
@ -108,6 +109,7 @@ pub const DerivedConfig = struct {
|
|||||||
.term = try alloc.dupe(u8, config.term),
|
.term = try alloc.dupe(u8, config.term),
|
||||||
.grapheme_width_method = config.@"grapheme-width-method",
|
.grapheme_width_method = config.@"grapheme-width-method",
|
||||||
.abnormal_runtime_threshold_ms = config.@"abnormal-command-exit-runtime",
|
.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"),
|
.enquiry_response = try alloc.dupe(u8, config.@"enquiry-response"),
|
||||||
|
|
||||||
// This has to be last so that we copy AFTER the arena allocations
|
// 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,
|
.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);
|
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
|
// Update our stream handler. The stream handler uses the same
|
||||||
// renderer mutex so this is safe to do despite being executed
|
// renderer mutex so this is safe to do despite being executed
|
||||||
// from another thread.
|
// 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.
|
// Update the configuration that we know about.
|
||||||
//
|
//
|
||||||
@ -722,6 +729,10 @@ const EventData = struct {
|
|||||||
/// when the process exits too quickly.
|
/// when the process exits too quickly.
|
||||||
abnormal_runtime_threshold_ms: u32,
|
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 {
|
pub fn deinit(self: *EventData, alloc: Allocator) void {
|
||||||
// Clear our write pools. We know we aren't ever going to do
|
// 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
|
// any more IO since we stop our data stream below so we can just
|
||||||
@ -803,6 +814,26 @@ fn processExit(
|
|||||||
return .disarm;
|
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
|
// Notify our surface we want to close
|
||||||
_ = ev.surface_mailbox.push(.{
|
_ = ev.surface_mailbox.push(.{
|
||||||
.child_exited = {},
|
.child_exited = {},
|
||||||
|
Reference in New Issue
Block a user