Hugo Machet: 1 [WIP] Implements inotify to watch files 2 files changed, 106 insertions(+), 5 deletions(-)
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~leon_plickat/nfm/patches/33111/mbox | git am -3Learn more about email & git
--- src/DirMap.zig | 66 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/nfm.zig | 45 +++++++++++++++++++++++++++++++--- 2 files changed, 106 insertions(+), 5 deletions(-) diff --git a/src/DirMap.zig b/src/DirMap.zig index 3552f0d0b855..8f581292831f 100644 --- a/src/DirMap.zig +++ b/src/DirMap.zig @@ -26,6 +26,8 @@ const os = std.os; const unicode = std.unicode; const sort = std.sort; +const context = &@import("nfm.zig").context; + const Self = @This(); pub const VisibilityConfig = struct { @@ -121,14 +123,36 @@ const Dir = struct { last_selected_id: usize, last_scroll_offset: usize, + inotify_wd: i32, + fn init(self: *Dir, map: *Self, alloc: mem.Allocator, dir: fs.Dir, name: []const u8) !void { self.files = std.ArrayList(File).init(alloc); self.name = name; self.last_selected_id = math.maxInt(usize); self.last_scroll_offset = 0; - // Get Directories. - // TODO should be moved to helper function for more flexibility + self.inotify_wd = try os.inotify_add_watch( + context.inotify_fd, + name, + os.linux.IN.CREATE | os.linux.IN.DELETE | os.linux.IN.DELETE_SELF | + os.linux.IN.MODIFY | os.linux.IN.MOVE_SELF | os.linux.IN.MOVE, + ); + + try self.getEntries(map, dir); + } + + fn refresh(self: *Dir, map: *Self) !void { + self.files.clearRetainingCapacity(); + var dir = try fs.cwd().openDir(self.name, .{ .iterate = true }); + defer dir.close(); + + try self.getEntries(map, dir); + try self.files.resize(self.files.items.len); + } + + fn getEntries(self: *Dir, map: *Self, dir: fs.Dir) !void { + const alloc = map.arena.allocator(); + var it = dir.iterate(); while (try it.next()) |entry| { const file = try self.files.addOne(); @@ -174,9 +198,47 @@ pub fn new(alloc: mem.Allocator, visconf: VisibilityConfig) !Self { } pub fn deinit(self: *Self) void { + var it = self.dirs.valueIterator(); + while (it.next()) |dir| { + os.inotify_rm_watch(context.inotify_fd, dir.*.inotify_wd); + } + self.arena.deinit(); } +pub fn handleInotifyEvent(self: *Self, visconf: VisibilityConfig) !void { + var buf: [4096]u8 align(@alignOf(os.linux.inotify_event)) = undefined; + const read = try os.read(context.inotify_fd, &buf); + + if (read == 0) return; + + var offset: usize = 0; + while (offset < read) { + const ev = @ptrCast( + *os.linux.inotify_event, + @alignCast(@alignOf(os.linux.inotify_event), &buf), + ); + + var it = self.dirs.valueIterator(); + while (it.next()) |dir| { + if (dir.*.inotify_wd == ev.wd) { + try dir.*.refresh(self); + try self.updateVisibleFiles(visconf); + self.setCursor(self.cursor); + break; + } + } else { + return error.UnknownInotifyWatchDescriptor; + } + + offset += @sizeOf(std.os.linux.inotify_event) + ev.len; + } + + // TODO: Avoid clearing the term? + context.ui.title_dirty = true; + context.ui.list_dirty = true; +} + /// Add or find a dir for a given relative path pub fn setCwdByRelativePath(self: *Self, _relpath: []const u8, visconf: VisibilityConfig) !void { if (_relpath.len == 1 and _relpath[0] == '.') return; diff --git a/src/nfm.zig b/src/nfm.zig index ca4ee4d07714..9b9606587e93 100644 --- a/src/nfm.zig +++ b/src/nfm.zig @@ -44,12 +44,14 @@ const util = @import("util.zig"); pub const Context = struct { pub const PollIndex = enum(u32) { ui = 1, + dirmap = 2, }; // TODO multiple view buffers? epoll_fd: i32 = undefined, - fds: [1]os.pollfd = undefined, + fds: [2]os.pollfd = undefined, + inotify_fd: os.fd_t = undefined, config: Config = .{}, ui: UserInterface = undefined, @@ -367,6 +369,32 @@ fn dumpUsage() !void { fn nfm() !void { if (builtin.os.tag == .linux) { context.epoll_fd = try os.epoll_create1(0); + context.inotify_fd = try os.inotify_init1(0); + } + defer { + if (builtin.os.tag == .linux) { + os.close(context.inotify_fd); + } + } + + // TODO: Move this somewhere else? + if (builtin.os.tag == .linux) { + var inotify_ev: os.linux.epoll_event = .{ + .events = os.linux.EPOLL.IN, + .data = .{ .@"u32" = @enumToInt(Context.PollIndex.dirmap) }, + }; + try os.epoll_ctl( + context.epoll_fd, + os.linux.EPOLL.CTL_ADD, + context.inotify_fd, + &inotify_ev, + ); + } else { + context.fds[@enumToInt(Context.PollIndex.dirmap)] = .{ + .fd = context.inotify_fd, + .events = os.POLL.IN, + .revents = 0, + }; } defer context.view.deinit(); @@ -394,9 +422,11 @@ fn nfm() !void { var input_buf: [32]u8 = undefined; while (context.loop) { + // TODO: number of events? + var epoll_ev: [8]os.linux.epoll_event = undefined; + var epoll_ev_count: usize = 0; if (builtin.os.tag == .linux) { - var ev: [4]os.linux.epoll_event = undefined; - _ = os.linux.epoll_wait(context.epoll_fd, &ev, 4, -1); + epoll_ev_count = os.epoll_wait(context.epoll_fd, &epoll_ev, -1); } else { _ = try os.poll(&context.fds, -1); } @@ -417,6 +447,15 @@ fn nfm() !void { } } + if (builtin.os.tag == .linux) { + for (epoll_ev[0..epoll_ev_count]) |ev| { + switch (@intToEnum(Context.PollIndex, ev.data.@"u32")) { + .ui => {}, + .dirmap => try context.dirmap.handleInotifyEvent(context.config.visconf), + } + } + } + if (context.ui.title_dirty or context.ui.list_dirty) { try context.ui.term.updateContent(); } -- 2.36.1
builds.sr.ht <builds@sr.ht>nfm/patches: FAILED in 46s [[WIP] Implements inotify to watch files][0] from [Hugo Machet][1] [0]: https://lists.sr.ht/~leon_plickat/nfm/patches/33111 [1]: mailto:mail@hmachet.com ✓ #782734 SUCCESS nfm/patches/alpine.yml https://builds.sr.ht/~leon_plickat/job/782734 ✗ #782735 FAILED nfm/patches/freebsd.yml https://builds.sr.ht/~leon_plickat/job/782735