summaryrefslogtreecommitdiff
path: root/src/tty/tty.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/tty/tty.zig')
-rw-r--r--src/tty/tty.zig35
1 files changed, 21 insertions, 14 deletions
diff --git a/src/tty/tty.zig b/src/tty/tty.zig
index f8f7150b..e8a36035 100644
--- a/src/tty/tty.zig
+++ b/src/tty/tty.zig
@@ -11,7 +11,6 @@ const std = @import("std");
const builtin = @import("builtin");
const posix = std.posix;
const libc = std.c;
-const linux = std.os.linux;
const vaxis = @import("vaxis");
const pardes = @import("../pardes.zig");
const tracy = @import("../tracy.zig");
@@ -343,8 +342,8 @@ test "Kitty PDF fragments never declare pixels beyond their clipped bottom" {
try std.testing.expect(kittyPlacement(height, 80, 16, 640, 256) == null);
}
-test "Linux host watch closes initial race and reloads rename-over PDF while idle" {
- if (comptime builtin.os.tag != .linux or !pardes.pdf_enabled) return;
+test "host watch closes initial race and reloads rename-over PDF while idle" {
+ if (comptime !file_watch.supported or !pardes.pdf_enabled) return;
const io = std.testing.io;
const gpa = std.testing.allocator;
var tmp = std.testing.tmpDir(.{});
@@ -361,8 +360,8 @@ test "Linux host watch closes initial race and reloads rename-over PDF while idl
var path_buf: [256]u8 = undefined;
const path = try std.fmt.bufPrint(&path_buf, ".zig-cache/tmp/{s}/live.pdf", .{tmp.sub_path});
- const fd = libc.inotify_init1(linux.IN.CLOEXEC | linux.IN.NONBLOCK);
- if (fd < 0) return error.InotifyInitFailed;
+ const fd = file_watch.init(true);
+ if (fd < 0) return error.WatchInitFailed;
defer _ = libc.close(fd);
var watches: file_watch.Table = @splat(null);
defer for (0..pardes.MAX_PANES) |id| file_watch.watchPane(
@@ -394,9 +393,7 @@ test "Linux host watch closes initial race and reloads rename-over PDF while idl
try std.testing.expectEqual(@as(usize, 1), core.panes[0].?.pdf.?.page_count);
try tmp.dir.rename("live-replacement.pdf", tmp.dir, "live.pdf", io);
- var events: [4096]u8 = undefined;
- const event_bytes = libc.read(fd, &events, events.len);
- try std.testing.expect(event_bytes > 0);
+ try std.testing.expect(file_watch.drain(fd));
// This is the same pass the watcher thread schedules; no key, mouse, or
// synthetic core file_changed event participates in the transaction.
@@ -699,11 +696,12 @@ fn localSession(
.kitty = kitty_handles,
.frame = &frame_arena,
.paste_buf = paste_buf,
- // One inotify instance for every watched pane, opened here — before
- // any thread exists — so the pre-loop effect drain below can already
- // mark the file a positional path argument opened. -1 off linux:
+ // One watcher for every watched pane, opened here — before any thread
+ // exists — so the pre-loop effect drain below can already mark the file
+ // a positional path argument opened. `false`: this host parks a thread
+ // in it rather than polling it. -1 where there is no watcher to make:
// watchPane goes quiet and the core simply never gets a file_changed.
- .inotify_fd = if (builtin.os.tag == .linux) libc.inotify_init1(linux.IN.CLOEXEC) else -1,
+ .inotify_fd = file_watch.init(false),
.fs = fs,
};
// The protocol client's reader threads narrate server state through this
@@ -746,8 +744,10 @@ fn localSession(
sh.lsp_task = null;
}
sh.pipe_tasks.cancelAll(io);
- // same contract as the pty readers: cancel unblocks the watcher's
- // read, and only then is the fd safe to close
+ // same contract as the pty readers: the watcher has to be off the
+ // descriptor before it is closed. `stop` is what releases a kqueue wait
+ // (macos); `cancel` is what interrupts the blocking read (linux).
+ file_watch.stop(sh.inotify_fd);
if (sh.watch_task) |*t| {
t.cancel(io) catch {};
sh.watch_task = null;
@@ -1679,6 +1679,13 @@ fn pipeWorker(
/// per open file pane; filter by basename here if it ever shows up in a
/// profile.
fn watchFiles(io: std.Io, fd: c_int, loop: *Loop) anyerror!void {
+ // A kqueue cannot be read, so there is no std.Io.File to wrap and no
+ // `cancel` to interrupt: this arm parks in kevent(2) and the teardown's
+ // `file_watch.stop` is what releases it. See file_watch.wait.
+ if (comptime builtin.os.tag != .linux) {
+ while (file_watch.wait(fd)) loop.postEvent(.files_changed) catch break;
+ return;
+ }
const file: std.Io.File = .{ .handle = fd, .flags = .{ .nonblocking = false } };
var read_buf: [4096]u8 = undefined;
var reader = file.readerStreaming(io, &read_buf);