Add custom layout creation.
This commit is contained in:
+49
-1
@@ -37,6 +37,10 @@ watch: c_uint = 0,
|
||||
/// True once the child process has exited and the PTY hung up.
|
||||
exited: bool = false,
|
||||
|
||||
/// A script to run once the shell is ready, from the layout this pane came
|
||||
/// from. Held rather than written at spawn time — see `flushStartupCommand`.
|
||||
startup_command: ?[]u8 = null,
|
||||
|
||||
/// Called after terminal state changes, so the owner can queue a redraw.
|
||||
on_damage: *const fn (ctx: ?*anyopaque) void,
|
||||
|
||||
@@ -55,10 +59,21 @@ pub const Callbacks = struct {
|
||||
ctx: ?*anyopaque,
|
||||
};
|
||||
|
||||
/// What a layout can ask for beyond a plain shell. Both are empty for a
|
||||
/// terminal opened the ordinary way.
|
||||
pub const Options = struct {
|
||||
/// Directory the shell starts in.
|
||||
cwd: []const u8 = "",
|
||||
|
||||
/// Script fed to the shell once it is up.
|
||||
command: []const u8 = "",
|
||||
};
|
||||
|
||||
pub fn create(
|
||||
alloc: std.mem.Allocator,
|
||||
cols: u16,
|
||||
rows: u16,
|
||||
opts: Options,
|
||||
cbs: Callbacks,
|
||||
) !*Session {
|
||||
const self = try alloc.create(Session);
|
||||
@@ -103,12 +118,22 @@ pub fn create(
|
||||
}, 0);
|
||||
defer alloc.free(argv0);
|
||||
|
||||
self.pty = try .create(alloc, shell, &.{argv0}, .{
|
||||
const cwd_z: ?[:0]const u8 = if (opts.cwd.len > 0)
|
||||
try alloc.dupeZ(u8, opts.cwd)
|
||||
else
|
||||
null;
|
||||
defer if (cwd_z) |z| alloc.free(z);
|
||||
|
||||
self.pty = try .create(alloc, shell, &.{argv0}, cwd_z, .{
|
||||
.ws_row = rows,
|
||||
.ws_col = cols,
|
||||
});
|
||||
errdefer self.pty.deinit();
|
||||
|
||||
if (opts.command.len > 0) {
|
||||
self.startup_command = try alloc.dupe(u8, opts.command);
|
||||
}
|
||||
|
||||
self.watch = glibunix.fdAdd(
|
||||
self.pty.master,
|
||||
.{ .in = true, .hup = true, .err = true },
|
||||
@@ -120,6 +145,7 @@ pub fn create(
|
||||
}
|
||||
|
||||
pub fn destroy(self: *Session) void {
|
||||
if (self.startup_command) |cmd| self.alloc.free(cmd);
|
||||
if (self.watch != 0) _ = glib.Source.remove(self.watch);
|
||||
self.pty.deinit();
|
||||
self.stream.deinit();
|
||||
@@ -161,6 +187,7 @@ fn onReadable(
|
||||
var buf: [read_buf_size]u8 = undefined;
|
||||
if (self.pty.read(&buf)) |n| {
|
||||
self.stream.nextSlice(buf[0..n]);
|
||||
self.flushStartupCommand();
|
||||
self.on_damage(self.ctx);
|
||||
return 1;
|
||||
}
|
||||
@@ -173,6 +200,27 @@ fn onReadable(
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Type a layout's script into the shell, once, as soon as the shell has
|
||||
/// shown that it is alive.
|
||||
///
|
||||
/// The wait matters. Writing at spawn time puts the script into the tty input
|
||||
/// buffer before the shell has started, and shells that set up line editing
|
||||
/// (zsh's ZLE, bash's readline) can discard whatever was buffered while they
|
||||
/// were initializing, so the command silently vanishes. The shell's first
|
||||
/// output — its prompt — is proof that it has finished starting and is reading
|
||||
/// input, which is exactly the moment this becomes safe.
|
||||
///
|
||||
/// It is typed rather than executed for us, so the shell is still there
|
||||
/// afterwards with the script sitting in its history.
|
||||
fn flushStartupCommand(self: *Session) void {
|
||||
const command = self.startup_command orelse return;
|
||||
self.startup_command = null;
|
||||
defer self.alloc.free(command);
|
||||
|
||||
self.pty.writeAll(command);
|
||||
self.pty.writeAll("\n");
|
||||
}
|
||||
|
||||
/// Effect callback: the terminal wants to send bytes back to the child.
|
||||
fn effectWritePty(handler: *vt.TerminalStream.Handler, data: [:0]const u8) void {
|
||||
const self = fromHandler(handler);
|
||||
|
||||
Reference in New Issue
Block a user