diff options
Diffstat (limited to 'src/gui')
| -rw-r--r-- | src/gui/gui.zig | 46 |
1 files changed, 45 insertions, 1 deletions
diff --git a/src/gui/gui.zig b/src/gui/gui.zig index 1f7812e4..0cc5f520 100644 --- a/src/gui/gui.zig +++ b/src/gui/gui.zig @@ -29,6 +29,7 @@ const selection_pipe = @import("../selection_pipe.zig"); const is_emscripten = builtin.os.tag == .emscripten; const temp_file = if (is_emscripten) struct {} else @import("../temp_file.zig"); const shell_bin = if (is_emscripten) struct {} else @import("../shell_bin.zig"); +const nested = if (is_emscripten) struct {} else @import("../nested.zig"); pub const c = @cImport({ @cDefine("SDL_DISABLE_OLD_NAMES", "1"); @@ -623,6 +624,9 @@ const Msg = union(enum) { pipe: selection_pipe.Response, /// something happened in a watched directory (see watchThread) files_changed, + /// a pardes launched inside this one sent us a builtin command line (see + /// lookThread); gpa-owned, like `output` bytes + command: []u8, }; /// The files on open panes, watched through ONE inotify instance. Same shape @@ -682,6 +686,7 @@ const Queue = struct { var response = response_value; response.deinit(q.gpa); }, + .command => |line| q.gpa.free(line), .eof, .files_changed => {}, } return; @@ -695,6 +700,7 @@ const Queue = struct { var response = response_value; response.deinit(q.gpa); }, + .command => |line| q.gpa.free(line), .eof, .files_changed => {}, } return; @@ -727,6 +733,7 @@ const Queue = struct { var response = response_value; response.deinit(q.gpa); }, + .command => |line| q.gpa.free(line), .eof, .files_changed => {}, }; q.items.deinit(q.gpa); @@ -807,6 +814,21 @@ fn watchThread(fd: c_int, q: *Queue) void { } } +/// Block on the nested-instance socket and hand the loop each command line a +/// pardes started inside this one sends. Detached like the pty readers and the +/// watcher — but NOT ended the way they are: close(2) does not release a +/// thread parked in accept4 on linux, so this one simply dies with the +/// process. The window that leaves is one connection accepted between the last +/// drain and process exit pushing into a queue nobody empties again; Queue +/// frees a push made after close(), and the process is on its way out anyway. +fn lookThread(gpa: std.mem.Allocator, fd: c_int, q: *Queue) void { + var buf: [nested.max_line]u8 = undefined; + while (nested.acceptLine(fd, &buf)) |line| { + const owned = gpa.dupe(u8, line) catch continue; + q.push(.{ .command = owned }); + } +} + /// Hand the core every watched pane whose bytes moved on disk. The wake says /// only THAT something happened, so this re-reads the lot; the hash comparison /// is what keeps our own Save — and any write that lands on identical content @@ -1310,6 +1332,19 @@ fn runNative(init: std.process.Init, opts_in: pardes.Options) !void { inotify_fd = -1; }; var watches: [pardes.MAX_PANES]?Watch = @splat(null); + // The socket a pardes launched inside this one connects to (nested.zig). + // --nested opted out of the whole mechanism, including being an outer + // instance; so does any failure to bind, and then children simply open + // their own session. + var sock_buf: [108]u8 = undefined; + const sock_path: ?[:0]const u8 = if (opts.nested) null else nested.socketPath(&sock_buf, libc.getpid()); + const sock_fd: c_int = if (sock_path) |sp| nested.listenAt(sp) else -1; + // only on the fd, so a bind that FAILED cannot unlink a path this process + // never created + defer if (sock_fd >= 0) { + _ = libc.close(sock_fd); + _ = libc.unlink(sock_path.?); + }; // initial spawns BEFORE any worker thread exists: forkpty from a // multithreaded process can wedge the child before exec (see tty.zig). @@ -1319,6 +1354,9 @@ fn runNative(init: std.process.Init, opts_in: pardes.Options) !void { // already exists and an unwatched inotify instance just parks in read(2) — // one thread for the process, however many panes come and go. if (inotify_fd >= 0) if (std.Thread.spawn(.{}, watchThread, .{ inotify_fd, &queue })) |th| th.detach() else |_| {}; + // ...and the nested-instance listener, detached like every other blocking + // worker here + if (sock_fd >= 0) if (std.Thread.spawn(.{}, lookThread, .{ gpa, sock_fd, &queue })) |th| th.detach() else |_| {}; _ = c.SDL_StartTextInput(window); @@ -1375,6 +1413,10 @@ fn runNative(init: std.process.Init, opts_in: pardes.Options) !void { break; }; }, + .command => |line| { + core.update(.{ .command = line }); + gpa.free(line); + }, // Coalesced on purpose: a burst of writes (a formatter, a build, a // `git checkout`) collapses into ONE pass below, so it cannot queue // a reload — or an undo entry — per write. @@ -1862,7 +1904,9 @@ fn runGrid(init: std.process.Init, opts_in: pardes.Options) !void { break; }; }, - .files_changed => {}, // unreachable: no watcher thread in this mode + // both unreachable here: this mode starts neither thread + .files_changed => {}, + .command => |line| gpa.free(line), }; msgs.deinit(gpa); drainEffects(core, &ptys, &gens, io, gpa, &queue, &pipe_tasks, null, -1, &watches, true); |
