Add custom layout creation.
This commit is contained in:
1 parent
a7a9429d09
commit
3026bdcd4c
14 files changed
+1964
-20
No files matched your search
+38
-1
@@ -27,6 +27,8 @@ const c = struct {
|
||||
extern "c" fn ptsname_r(fd: c_int, buf: [*]u8, buflen: usize) c_int;
|
||||
extern "c" fn setsid() pid_t;
|
||||
extern "c" fn fork() pid_t;
|
||||
extern "c" fn chdir(path: [*:0]const u8) c_int;
|
||||
extern "c" fn readlink(path: [*:0]const u8, buf: [*]u8, bufsiz: usize) isize;
|
||||
extern "c" fn close(fd: fd_t) c_int;
|
||||
extern "c" fn dup2(old: fd_t, new: fd_t) c_int;
|
||||
extern "c" fn open(path: [*:0]const u8, flags: c_int, ...) c_int;
|
||||
@@ -86,10 +88,15 @@ pub const Error = error{
|
||||
/// started by running e.g. /usr/bin/zsh with an argv[0] of "-zsh"; folding
|
||||
/// them together would pass "-zsh" as an ordinary argument instead, which
|
||||
/// shells either misparse or reject outright.
|
||||
///
|
||||
/// `dir` is the directory the child starts in. A null, or a directory that
|
||||
/// cannot be entered, leaves it wherever the app was started — a layout
|
||||
/// naming a path that no longer exists should still give you a usable shell.
|
||||
pub fn create(
|
||||
alloc: std.mem.Allocator,
|
||||
path: [:0]const u8,
|
||||
argv: []const [:0]const u8,
|
||||
dir: ?[:0]const u8,
|
||||
size: Winsize,
|
||||
) !Pty {
|
||||
const master = c.posix_openpt(O_RDWR | O_NOCTTY);
|
||||
@@ -120,7 +127,7 @@ pub fn create(
|
||||
const pid = c.fork();
|
||||
if (pid < 0) return Error.ForkFailed;
|
||||
if (pid == 0) {
|
||||
childExec(master, slave_path_z, path, argv_z, envp_z);
|
||||
childExec(master, slave_path_z, path, argv_z, envp_z, dir);
|
||||
// childExec only returns on failure, and a forked child has no
|
||||
// sensible way to report that back to us.
|
||||
c._exit(127);
|
||||
@@ -136,9 +143,17 @@ fn childExec(
|
||||
path: [:0]const u8,
|
||||
argv: [:null]const ?[*:0]const u8,
|
||||
envp: [:null]const ?[*:0]const u8,
|
||||
dir: ?[:0]const u8,
|
||||
) void {
|
||||
_ = c.close(master);
|
||||
|
||||
// Deliberately unchecked: a layout pointing at a directory that has since
|
||||
// been moved or deleted should still open a shell, in the directory the
|
||||
// app was started from, rather than a pane that dies on arrival.
|
||||
if (dir) |d| if (d.len > 0) {
|
||||
_ = c.chdir(d.ptr);
|
||||
};
|
||||
|
||||
// A new session detaches us from the parent's controlling terminal so
|
||||
// that we can claim the slave as our own below.
|
||||
if (c.setsid() < 0) return;
|
||||
@@ -211,6 +226,28 @@ fn freeEnv(alloc: std.mem.Allocator, envp: [:null]?[*:0]const u8) void {
|
||||
alloc.free(envp);
|
||||
}
|
||||
|
||||
/// The child's current working directory, copied into `buf`, or null if it
|
||||
/// can't be read.
|
||||
///
|
||||
/// This is the shell's directory as of right now, not the one it started in,
|
||||
/// which is what makes "save this tab as a layout" record where you actually
|
||||
/// are rather than where you began. Read straight out of procfs: there is no
|
||||
/// portable API for it, and this app is Linux-only regardless.
|
||||
pub fn cwd(self: Pty, buf: []u8) ?[]const u8 {
|
||||
var link_buf: [64]u8 = undefined;
|
||||
const link = std.fmt.bufPrintZ(&link_buf, "/proc/{d}/cwd", .{self.pid}) catch return null;
|
||||
|
||||
const n = c.readlink(link.ptr, buf.ptr, buf.len);
|
||||
if (n <= 0) return null;
|
||||
|
||||
const len: usize = @intCast(n);
|
||||
// readlink truncates rather than failing, and a truncated path is worse
|
||||
// than none: it would silently point somewhere else.
|
||||
if (len >= buf.len) return null;
|
||||
|
||||
return buf[0..len];
|
||||
}
|
||||
|
||||
/// Tell the child its window changed size. This both updates the kernel's
|
||||
/// idea of the terminal size and delivers SIGWINCH to the foreground group.
|
||||
pub fn setSize(self: Pty, size: Winsize) void {
|
||||
|
||||
Reference in new issue
Block a user