jens@20: // jens@20: // MYDirectoryWatcher.m jens@20: // Murky jens@20: // jens@20: // Copyright 2008 Jens Alfke. All rights reserved. jens@20: // jens@20: jens@20: #import "MYDirectoryWatcher.h" jens@27: #import "Test.h" jens@27: #import "Logging.h" jens@20: #import jens@20: jens@20: jens@20: static void directoryWatcherCallback(ConstFSEventStreamRef streamRef, jens@20: void *clientCallBackInfo, jens@20: size_t numEvents, jens@20: void *eventPaths, jens@20: const FSEventStreamEventFlags eventFlags[], jens@20: const FSEventStreamEventId eventIds[]); jens@20: jens@20: @interface MYDirectoryEvent () jens@20: - (id) _initWithWatcher: (MYDirectoryWatcher*)itsWatcher jens@20: path: (NSString*)itsPath jens@20: flags: (FSEventStreamEventFlags)itsFlags jens@20: eventID: (FSEventStreamEventId)itsEventID; jens@20: @end jens@20: jens@20: jens@20: @implementation MYDirectoryWatcher jens@20: jens@20: jens@20: - (id) initWithDirectory: (NSString*)path target: (id)target action: (SEL)action jens@20: { jens@27: Assert(path!=nil); jens@20: self = [super init]; jens@20: if (self != nil) { jens@20: _path = path.copy; jens@32: _standardizedPath = [[_path stringByStandardizingPath] copy]; jens@20: _target = target; jens@20: _action = action; jens@20: _latency = 5.0; jens@20: _lastEventID = kFSEventStreamEventIdSinceNow; jens@20: } jens@20: return self; jens@20: } jens@20: jens@20: - (void) dealloc jens@20: { jens@20: [self stop]; jens@20: [_path release]; jens@32: [_standardizedPath release]; jens@20: [super dealloc]; jens@20: } jens@20: jens@20: - (void) finalize jens@20: { jens@20: [self stop]; jens@20: [super finalize]; jens@20: } jens@20: jens@20: jens@20: @synthesize path=_path, latency=_latency, lastEventID=_lastEventID; jens@20: jens@31: - (NSString*) standardizedPath { jens@31: return _standardizedPath; jens@31: } jens@31: jens@20: jens@20: - (BOOL) start jens@20: { jens@20: if( ! _stream ) { jens@20: FSEventStreamContext context = {0,self,NULL,NULL,NULL}; jens@20: _stream = FSEventStreamCreate(NULL, jens@20: &directoryWatcherCallback, &context, jens@20: (CFArrayRef)[NSArray arrayWithObject: _path], jens@20: _lastEventID, jens@20: _latency, jens@20: kFSEventStreamCreateFlagUseCFTypes); jens@20: if( ! _stream ) jens@20: return NO; jens@20: FSEventStreamScheduleWithRunLoop(_stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); jens@20: if( ! FSEventStreamStart(_stream) ) { jens@20: [self stop]; jens@20: return NO; jens@20: } jens@20: _historyDone = (_lastEventID == kFSEventStreamEventIdSinceNow); jens@27: LogTo(MYDirectoryWatcher, @"Started on %@ (latency=%g, lastEvent=%llu)",_path,_latency,_lastEventID); jens@20: } jens@20: return YES; jens@20: } jens@20: jens@20: - (void) pause jens@20: { jens@20: if( _stream ) { jens@20: FSEventStreamStop(_stream); jens@20: FSEventStreamInvalidate(_stream); jens@20: FSEventStreamRelease(_stream); jens@20: _stream = NULL; jens@27: LogTo(MYDirectoryWatcher, @"Stopped on %@ (lastEvent=%llu)",_path,_lastEventID); jens@20: } jens@20: } jens@20: jens@20: - (void) stop jens@20: { jens@20: [self pause]; jens@20: _lastEventID = kFSEventStreamEventIdSinceNow; // so events from now till next start will be dropped jens@20: [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector(start) object: nil]; jens@20: } jens@20: jens@20: - (void) stopTemporarily jens@20: { jens@20: if( _stream ) { jens@20: [self stop]; jens@20: [self performSelector: @selector(start) withObject: nil afterDelay: 0.0]; jens@20: } jens@20: } jens@20: jens@20: jens@20: - (void) _notifyEvents: (size_t)numEvents jens@20: paths: (NSArray*)paths jens@20: flags: (const FSEventStreamEventFlags[])eventFlags jens@20: eventIDs: (const FSEventStreamEventId[])eventIDs jens@20: { jens@20: for (size_t i=0; i