summaryrefslogtreecommitdiff
path: root/src/gui/gui.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/gui.zig')
-rw-r--r--src/gui/gui.zig46
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);