flow/src/watcher/MacosWatcher.zig

108 lines
2.8 KiB
Zig

const MacosWatcher = @This();
const std = @import("std");
const Debouncer = @import("Debouncer.zig");
const c = @cImport({
@cInclude("CoreServices/CoreServices.h");
});
const log = std.log.scoped(.watcher);
gpa: std.mem.Allocator,
debouncer: *Debouncer,
dir_paths: []const []const u8,
pub fn init(
gpa: std.mem.Allocator,
debouncer: *Debouncer,
dir_paths: []const []const u8,
) MacosWatcher {
return .{
.gpa = gpa,
.debouncer = debouncer,
.dir_paths = dir_paths,
};
}
pub fn start(watcher: *MacosWatcher) !void {
const t = try std.Thread.spawn(.{}, MacosWatcher.listen, .{watcher});
t.detach();
}
pub fn listen(watcher: *MacosWatcher) error{ OutOfMemory, FSEventStreamError }!void {
const macos_paths = try watcher.gpa.alloc(
c.CFStringRef,
watcher.dir_paths.len,
);
defer watcher.gpa.free(macos_paths);
for (watcher.dir_paths, macos_paths) |str, *ref| {
ref.* = c.CFStringCreateWithCString(
null,
str.ptr,
c.kCFStringEncodingUTF8,
);
}
const paths_to_watch: c.CFArrayRef = c.CFArrayCreate(
null,
@ptrCast(macos_paths.ptr),
@intCast(macos_paths.len),
null,
);
var stream_context: c.FSEventStreamContext = .{ .info = watcher };
const stream: c.FSEventStreamRef = c.FSEventStreamCreate(
null,
&macosCallback,
&stream_context,
paths_to_watch,
c.kFSEventStreamEventIdSinceNow,
0.05,
c.kFSEventStreamCreateFlagFileEvents,
);
c.FSEventStreamScheduleWithRunLoop(
stream,
c.CFRunLoopGetCurrent(),
c.kCFRunLoopDefaultMode,
);
if (c.FSEventStreamStart(stream) == 0)
return error.FSEventStreamError;
c.CFRunLoopRun();
c.FSEventStreamStop(stream);
c.FSEventStreamInvalidate(stream);
c.FSEventStreamRelease(stream);
c.CFRelease(paths_to_watch);
}
pub fn macosCallback(
streamRef: c.ConstFSEventStreamRef,
clientCallBackInfo: ?*anyopaque,
numEvents: usize,
eventPaths: ?*anyopaque,
eventFlags: ?[*]const c.FSEventStreamEventFlags,
eventIds: ?[*]const c.FSEventStreamEventId,
) callconv(.c) void {
_ = eventIds;
_ = eventFlags;
_ = streamRef;
const watcher: *MacosWatcher = @ptrCast(@alignCast(clientCallBackInfo));
const paths: [*][*:0]u8 = @ptrCast(@alignCast(eventPaths));
for (paths[0..numEvents]) |p| {
const path = std.mem.span(p);
log.debug("Changed: {s}\n", .{path});
// const basename = std.fs.path.basename(path);
// var base_path = path[0 .. path.len - basename.len];
// if (std.mem.endsWith(u8, base_path, "/"))
// base_path = base_path[0 .. base_path.len - 1];
watcher.debouncer.newEvent();
}
}