termio/exec: use message to writer thread so we can output failed cmd

This commit is contained in:
Mitchell Hashimoto
2023-12-30 17:51:34 -08:00
parent aaded1f311
commit f3aaa884c6
3 changed files with 63 additions and 34 deletions

View File

@ -544,6 +544,56 @@ pub fn jumpToPrompt(self: *Exec, delta: isize) !void {
} }
} }
/// Called when the child process exited abnormally but before
/// the surface is notified.
pub fn childExitedAbnormally(self: *Exec) !void {
// Build up our command for the error message
const command = try std.mem.join(
self.alloc,
" ",
self.subprocess.args,
);
defer self.alloc.free(command);
// Build our error message. Do this outside of the renderer lock.
var msg = std.ArrayList(u8).init(self.alloc);
defer msg.deinit();
var writer = msg.writer();
try writer.print(
\\Ghostty failed to launch the requested command.
\\Please check your "command" configuration.
\\
\\Command: {s}
\\
\\Press any key to close this window.
, .{command});
// Modify the terminal to show our error message. This
// requires grabbing the renderer state lock.
self.renderer_state.mutex.lock();
defer self.renderer_state.mutex.unlock();
const t = self.renderer_state.terminal;
// Reset the terminal completely.
// NOTE: The error output is in the terminal at this point. In the
// future, we can make an even better error message by scrolling,
// writing at the bottom, etc.
t.fullReset(self.alloc);
// Write our message out.
const view = try std.unicode.Utf8View.init(msg.items);
var it = view.iterator();
while (it.nextCodepoint()) |cp| {
if (cp == '\n') {
t.carriageReturn();
try t.linefeed();
continue;
}
try t.print(cp);
}
}
pub inline fn queueWrite(self: *Exec, data: []const u8, linefeed: bool) !void { pub inline fn queueWrite(self: *Exec, data: []const u8, linefeed: bool) !void {
const ev = self.data.?; const ev = self.data.?;
@ -757,40 +807,12 @@ fn processExit(
if (runtime > abnormal_runtime_threshold_ms) break :runtime; if (runtime > abnormal_runtime_threshold_ms) break :runtime;
log.warn("abnormal process exit detected, showing error message", .{}); log.warn("abnormal process exit detected, showing error message", .{});
// Build our error message. Do this outside of the renderer lock. // Notify our main writer thread which has access to more
const alloc = ev.terminal_stream.handler.alloc; // information so it can show a better error message.
var msg = std.ArrayList(u8).init(alloc); _ = ev.writer_mailbox.push(.{
defer msg.deinit(); .child_exited_abnormally = {},
var writer = msg.writer(); }, .{ .forever = {} });
writer.writeAll( ev.writer_wakeup.notify() catch break :runtime;
\\ Ghostty failed to launch the requested command.
\\ Please check your "command" configuration.
\\
\\ Press any key to close this window.
) catch break :runtime;
// Modify the terminal to show our error message. This
// requires grabbing the renderer state lock.
ev.renderer_state.mutex.lock();
defer ev.renderer_state.mutex.unlock();
const t = ev.renderer_state.terminal;
// Reset the terminal completely.
t.fullReset(alloc);
// Write our message out.
const view = std.unicode.Utf8View.init(msg.items) catch
break :runtime;
var it = view.iterator();
while (it.nextCodepoint()) |cp| {
if (cp == '\n') {
t.carriageReturn();
t.linefeed() catch break :runtime;
continue;
}
t.print(cp) catch break :runtime;
}
return .disarm; return .disarm;
} }

View File

@ -186,6 +186,7 @@ fn drainMailbox(self: *Thread) !void {
.jump_to_prompt => |v| try self.impl.jumpToPrompt(v), .jump_to_prompt => |v| try self.impl.jumpToPrompt(v),
.start_synchronized_output => self.startSynchronizedOutput(), .start_synchronized_output => self.startSynchronizedOutput(),
.linefeed_mode => |v| self.flags.linefeed_mode = v, .linefeed_mode => |v| self.flags.linefeed_mode = v,
.child_exited_abnormally => try self.impl.childExitedAbnormally(),
.write_small => |v| try self.impl.queueWrite(v.data[0..v.len], self.flags.linefeed_mode), .write_small => |v| try self.impl.queueWrite(v.data[0..v.len], self.flags.linefeed_mode),
.write_stable => |v| try self.impl.queueWrite(v, self.flags.linefeed_mode), .write_stable => |v| try self.impl.queueWrite(v, self.flags.linefeed_mode),
.write_alloc => |v| { .write_alloc => |v| {

View File

@ -62,6 +62,12 @@ pub const Message = union(enum) {
/// Enable or disable linefeed mode (mode 20). /// Enable or disable linefeed mode (mode 20).
linefeed_mode: bool, linefeed_mode: bool,
/// The child exited abnormally. The termio state is marked
/// as process exited but the surface hasn't been notified to
/// close because termio can use this to update the terminal
/// with an error message.
child_exited_abnormally: void,
/// Write where the data fits in the union. /// Write where the data fits in the union.
write_small: WriteReq.Small, write_small: WriteReq.Small,