Added some new utilities, taken from Murky.
2 // MYDirectoryWatcher.m
5 // Copyright 2008 Jens Alfke. All rights reserved.
8 #import "MYDirectoryWatcher.h"
9 #import <CoreServices/CoreServices.h>
12 static void directoryWatcherCallback(ConstFSEventStreamRef streamRef,
13 void *clientCallBackInfo,
16 const FSEventStreamEventFlags eventFlags[],
17 const FSEventStreamEventId eventIds[]);
19 @interface MYDirectoryEvent ()
20 - (id) _initWithWatcher: (MYDirectoryWatcher*)itsWatcher
21 path: (NSString*)itsPath
22 flags: (FSEventStreamEventFlags)itsFlags
23 eventID: (FSEventStreamEventId)itsEventID;
27 @implementation MYDirectoryWatcher
30 - (id) initWithDirectory: (NSString*)path target: (id)target action: (SEL)action
32 NSParameterAssert(path);
39 _lastEventID = kFSEventStreamEventIdSinceNow;
58 @synthesize path=_path, latency=_latency, lastEventID=_lastEventID;
64 FSEventStreamContext context = {0,self,NULL,NULL,NULL};
65 _stream = FSEventStreamCreate(NULL,
66 &directoryWatcherCallback, &context,
67 (CFArrayRef)[NSArray arrayWithObject: _path],
70 kFSEventStreamCreateFlagUseCFTypes);
73 FSEventStreamScheduleWithRunLoop(_stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
74 if( ! FSEventStreamStart(_stream) ) {
78 _historyDone = (_lastEventID == kFSEventStreamEventIdSinceNow);
79 Log(@"MYDirectoryWatcher: Started on %@ (latency=%g, lastEvent=%llu)",_path,_latency,_lastEventID);
87 FSEventStreamStop(_stream);
88 FSEventStreamInvalidate(_stream);
89 FSEventStreamRelease(_stream);
91 Log(@"MYDirectoryWatcher: Stopped on %@ (lastEvent=%llu)",_path,_lastEventID);
98 _lastEventID = kFSEventStreamEventIdSinceNow; // so events from now till next start will be dropped
99 [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector(start) object: nil];
102 - (void) stopTemporarily
106 [self performSelector: @selector(start) withObject: nil afterDelay: 0.0];
111 - (void) _notifyEvents: (size_t)numEvents
112 paths: (NSArray*)paths
113 flags: (const FSEventStreamEventFlags[])eventFlags
114 eventIDs: (const FSEventStreamEventId[])eventIDs
116 for (size_t i=0; i<numEvents; i++) {
117 NSString *path = [paths objectAtIndex: i];
118 FSEventStreamEventFlags flags = eventFlags[i];
119 FSEventStreamEventId eventID = eventIDs[i];
120 if( flags & (kFSEventStreamEventFlagMount | kFSEventStreamEventFlagUnmount) ) {
121 if( flags & kFSEventStreamEventFlagMount )
122 Log(@"MYDirectoryWatcher: Volume mounted: %@",path);
124 Log(@"MYDirectoryWatcher: Volume unmounted: %@",path);
125 } else if( flags & kFSEventStreamEventFlagHistoryDone ) {
126 Log(@"MYDirectoryWatcher: Event #%llu History done",eventID);
129 Log(@"MYDirectoryWatcher: Event #%llu flags=%02x path=%@",eventID,flags,path);
131 flags |= kFSEventStreamEventFlagHistoryDone;
133 MYDirectoryEvent *event = [[MYDirectoryEvent alloc] _initWithWatcher: self
137 [_target performSelector: _action withObject: event];
140 _lastEventID = eventIDs[i];
145 static void directoryWatcherCallback(ConstFSEventStreamRef streamRef,
149 const FSEventStreamEventFlags eventFlags[],
150 const FSEventStreamEventId eventIDs[])
152 [(MYDirectoryWatcher*)watcher _notifyEvents: numEvents
153 paths: (NSArray*)eventPaths
165 @implementation MYDirectoryEvent
167 - (id) _initWithWatcher: (MYDirectoryWatcher*)itsWatcher
168 path: (NSString*)itsPath
169 flags: (FSEventStreamEventFlags)itsFlags
170 eventID: (FSEventStreamEventId)itsEventID
174 watcher = itsWatcher;
177 eventID = itsEventID;
188 @synthesize watcher,path,flags,eventID;
190 - (NSString*) relativePath
192 NSString *base = watcher.path;
193 if( ! [path hasPrefix: base] )
195 int length = base.length;
196 while( length < path.length && [path characterAtIndex: length]=='/' )
198 return [path substringFromIndex: length];
201 - (BOOL) mustScanSubdirectories {return (flags & kFSEventStreamEventFlagMustScanSubDirs) != 0;}
202 - (BOOL) eventsWereDropped {return (flags & (kFSEventStreamEventFlagUserDropped|kFSEventStreamEventFlagKernelDropped)) != 0;}
203 - (BOOL) isHistorical {return (flags & kFSEventStreamEventFlagHistoryDone)==0;}
204 - (BOOL) rootChanged {return (flags & kFSEventStreamEventFlagRootChanged)!=0;}