-
Notifications
You must be signed in to change notification settings - Fork 41
Expand file tree
/
Copy pathDYFileWatcher.m
More file actions
108 lines (97 loc) · 3.43 KB
/
DYFileWatcher.m
File metadata and controls
108 lines (97 loc) · 3.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//
// DYFileWatcher.m
// Phoenix Slides
//
// Created by 祥 on 12/6/23.
//
#import "DYFileWatcher.h"
#import "DYCarbonGoodies.h"
#import "CreeveyController.h"
@interface DYFileWatcher ()
@property (nonatomic, copy) NSString *path;
@property (nonatomic) NSURL *fileRef;
- (instancetype)init NS_UNAVAILABLE;
- (void)gotEventPaths:(NSArray *)eventPaths flags:(const FSEventStreamEventFlags *)eventFlags count:(size_t)n;
@end
static void fseventCallback(ConstFSEventStreamRef streamRef, void *info, size_t n, void *p, const FSEventStreamEventFlags flags[], const FSEventStreamEventId eventIds[])
{
[(__bridge DYFileWatcher *)info gotEventPaths:(__bridge NSArray *)(p) flags:flags count:n];
// NB: the CFArrayRef of event paths gets released after this returns
}
@implementation DYFileWatcher
{
FSEventStreamRef stream;
id <DYFileWatcherDelegate> __weak _delegate;
CreeveyController * __weak appDelegate;
}
- (instancetype)initWithDelegate:(id <DYFileWatcherDelegate>)d {
if (self = [super init]) {
_delegate = d;
}
return self;
}
- (void)dealloc {
[self stop];
}
- (void)watchDirectory:(NSString *)s {
if (stream)
[self stop];
if ([s.stringByDeletingLastPathComponent isEqualToString:@"/"])
return; // just refuse to watch top level directories for now
stream = FSEventStreamCreate(NULL, &fseventCallback, &(FSEventStreamContext){0,(__bridge void *)self,NULL,NULL,NULL}, (__bridge CFArrayRef)@[s], kFSEventStreamEventIdSinceNow, 2.0,
kFSEventStreamCreateFlagFileEvents
|kFSEventStreamCreateFlagUseCFTypes
|kFSEventStreamCreateFlagIgnoreSelf
|kFSEventStreamCreateFlagMarkSelf
|kFSEventStreamCreateFlagWatchRoot
);
FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
if (!FSEventStreamStart(stream)) {
FSEventStreamInvalidate(stream);
stream = NULL;
}
self.path = s;
self.fileRef = [NSURL fileURLWithPath:s isDirectory:YES].fileReferenceURL;
appDelegate = (CreeveyController *)NSApp.delegate;
}
- (void)gotEventPaths:(NSArray *)eventPaths flags:(const FSEventStreamEventFlags *)eventFlags count:(size_t)n {
NSMutableSet *files = [[NSMutableSet alloc] init];
NSMutableSet *deleted = [[NSMutableSet alloc] init];
BOOL rootChanged = NO;
for (size_t i=0; i<n; ++i) {
NSString *s = eventPaths[i];
FSEventStreamEventFlags f = eventFlags[i];
if (f & (kFSEventStreamEventFlagItemCreated|kFSEventStreamEventFlagItemModified|kFSEventStreamEventFlagItemRemoved|kFSEventStreamEventFlagItemRenamed|kFSEventStreamEventFlagItemInodeMetaMod)) {
if (f & kFSEventStreamEventFlagItemIsDir) continue;
if (_wantsSubfolders ? [s hasPrefix:_path] : [s.stringByDeletingLastPathComponent isEqualToString:_path]) {
NSURL *url = [NSURL fileURLWithPath:s isDirectory:NO];
if (![appDelegate shouldShowFile:url]) continue;
if (0 == access(s.fileSystemRepresentation, R_OK))
[files addObject:s];
else
[deleted addObject:s];
}
} else if (f & kFSEventStreamEventFlagRootChanged) {
rootChanged = YES;
}
}
if (files.count || deleted.count)
[_delegate watcherFiles:files.allObjects deleted:deleted.allObjects];
if (rootChanged) {
[_delegate watcherRootChanged:_fileRef];
if (_fileRef.path != nil) {
dispatch_async(dispatch_get_main_queue(), ^{
[self watchDirectory:_fileRef.path];
});
}
}
}
- (void)stop {
if (stream) {
FSEventStreamStop(stream);
FSEventStreamInvalidate(stream);
FSEventStreamRelease(stream);
stream = NULL;
}
}
@end