Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 305 lines (238 sloc) 8.487 kb
720b0e7b »
2010-03-04 Added licensing information to the source files.
1 //
2 // UKFSEventsWatcher.m
3 //
4 //
5 // Created by Peter Baumgartner on 9.6.2008.
6 // Copyright 2008 Peter Baumgartner.
7 //
8 // This software is provided 'as-is', without any express or implied
9 // warranty. In no event will the authors be held liable for any damages
10 // arising from the use of this software.
11 //
12 // Permission is granted to anyone to use this software for any purpose,
13 // including commercial applications, and to alter it and redistribute it
14 // freely, subject to the following restrictions:
15 //
16 // 1. The origin of this software must not be misrepresented; you must not
17 // claim that you wrote the original software. If you use this software
18 // in a product, an acknowledgment in the product documentation would be
19 // appreciated but is not required.
20 //
21 // 2. Altered source versions must be plainly marked as such, and must not be
22 // misrepresented as being the original software.
23 //
24 // 3. This notice may not be removed or altered from any source
25 // distribution.
26 //
de62e9e5 » uli
2009-07-04 Added FSEvents file watcher and cleaned-up version of UKKQueue.
27
28 // -----------------------------------------------------------------------------
29 // Headers:
30 // -----------------------------------------------------------------------------
31
32 #import "UKFSEventsWatcher.h"
33 #import <CoreServices/CoreServices.h>
34
35
36 // -----------------------------------------------------------------------------
37 // FSEventCallback
38 // Private callback that is called by the FSEvents framework
39 // -----------------------------------------------------------------------------
40
41 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4
42
43 static void FSEventCallback(ConstFSEventStreamRef inStreamRef,
44 void* inClientCallBackInfo,
45 size_t inNumEvents,
46 void* inEventPaths,
47 const FSEventStreamEventFlags inEventFlags[],
48 const FSEventStreamEventId inEventIds[])
49 {
50 UKFSEventsWatcher* watcher = (UKFSEventsWatcher*)inClientCallBackInfo;
51
52 if (watcher != nil && [watcher delegate] != nil)
53 {
54 id delegate = [watcher delegate];
55
56 if ([delegate respondsToSelector:@selector(watcher:receivedNotification:forPath:)])
57 {
58 NSEnumerator* paths = [(NSArray*)inEventPaths objectEnumerator];
59 NSString* path;
60
61 while (path = [paths nextObject])
62 {
63 [delegate watcher:watcher receivedNotification:UKFileWatcherWriteNotification forPath:path];
64
65 [[[NSWorkspace sharedWorkspace] notificationCenter]
66 postNotificationName: UKFileWatcherWriteNotification
67 object:watcher
68 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:path,@"path",nil]];
69 }
70 }
71 }
72 }
73
74
75 @implementation UKFSEventsWatcher
76
77 // -----------------------------------------------------------------------------
78 // sharedFileWatcher:
79 // Singleton accessor.
80 // -----------------------------------------------------------------------------
81
82 +(id) sharedFileWatcher
83 {
84 static UKFSEventsWatcher* sSharedFileWatcher = nil;
85 static NSString* sSharedFileWatcherMutex = @"UKFSEventsWatcher";
86
87 @synchronized(sSharedFileWatcherMutex)
88 {
89 if (sSharedFileWatcher == nil)
90 {
91 sSharedFileWatcher = [[UKFSEventsWatcher alloc] init]; // This is a singleton, and thus an intentional "leak".
92 }
93 }
94
95 return sSharedFileWatcher;
96 }
97
98
99 // -----------------------------------------------------------------------------
100 // * CONSTRUCTOR:
101 // -----------------------------------------------------------------------------
102
103 -(id) init
104 {
105 if (self = [super init])
106 {
107 latency = 1.0;
108 flags = kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagWatchRoot;
109 eventStreams = [[NSMutableDictionary alloc] init];
110 }
111
112 return self;
113 }
114
115
116 // -----------------------------------------------------------------------------
117 // * DESTRUCTOR:
118 // -----------------------------------------------------------------------------
119
120 -(void) dealloc
121 {
122 [self removeAllPaths];
123 [eventStreams release];
124 [super dealloc];
125 }
126
127 -(void) finalize
128 {
129 [self removeAllPaths];
130 [super finalize];
131 }
132
133
134 // -----------------------------------------------------------------------------
135 // setLatency:
136 // Time that must pass before events are being sent.
137 // -----------------------------------------------------------------------------
138
139 - (void) setLatency:(CFTimeInterval)inLatency
140 {
141 latency = inLatency;
142 }
143
144
145 // -----------------------------------------------------------------------------
146 // latency
147 // Time that must pass before events are being sent.
148 // -----------------------------------------------------------------------------
149
150 - (CFTimeInterval) latency
151 {
152 return latency;
153 }
154
155
156 // -----------------------------------------------------------------------------
157 // setFSEventStreamCreateFlags:
158 // See FSEvents.h for meaning of these flags.
159 // -----------------------------------------------------------------------------
160
161 - (void) setFSEventStreamCreateFlags:(FSEventStreamCreateFlags)inFlags
162 {
163 flags = inFlags;
164 }
165
166
167 // -----------------------------------------------------------------------------
168 // fsEventStreamCreateFlags
169 // See FSEvents.h for meaning of these flags.
170 // -----------------------------------------------------------------------------
171
172 - (FSEventStreamCreateFlags) fsEventStreamCreateFlags
173 {
174 return flags;
175 }
176
177
178 // -----------------------------------------------------------------------------
179 // setDelegate:
180 // Mutator for file watcher delegate.
181 // -----------------------------------------------------------------------------
182
183 -(void) setDelegate: (id)newDelegate
184 {
185 delegate = newDelegate;
186 }
187
188
189 // -----------------------------------------------------------------------------
190 // delegate:
191 // Accessor for file watcher delegate.
192 // -----------------------------------------------------------------------------
193
194 -(id) delegate
195 {
196 return delegate;
197 }
198
199
200 // -----------------------------------------------------------------------------
201 // parentFolderForFilePath:
202 // We need to supply a folder to FSEvents, so if we were passed a path
203 // to a file, then convert it to the parent folder path...
204 // -----------------------------------------------------------------------------
205
206 - (NSString*) pathToParentFolderOfFile:(NSString*)inPath
207 {
208 BOOL directory;
209 BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:inPath isDirectory:&directory];
210 BOOL package = [[NSWorkspace sharedWorkspace] isFilePackageAtPath:inPath];
211
212 if (exists && directory==NO && package==NO)
213 {
214 inPath = [inPath stringByDeletingLastPathComponent];
215 }
216
217 return inPath;
218 }
219
220
221 // -----------------------------------------------------------------------------
222 // addPath:
223 // Start watching the folder at the specified path.
224 // -----------------------------------------------------------------------------
225
226 -(void) addPath: (NSString*)path
227 {
228 path = [self pathToParentFolderOfFile:path];
229 NSArray* paths = [NSArray arrayWithObject:path];
230
231 FSEventStreamContext context;
232 context.version = 0;
233 context.info = (void*) self;
234 context.retain = NULL;
235 context.release = NULL;
236 context.copyDescription = NULL;
237
238 FSEventStreamRef stream = FSEventStreamCreate(NULL,&FSEventCallback,&context,(CFArrayRef)paths,kFSEventStreamEventIdSinceNow,latency,flags);
239
240 if (stream)
241 {
242 FSEventStreamScheduleWithRunLoop(stream,CFRunLoopGetMain(),kCFRunLoopCommonModes);
243 FSEventStreamStart(stream);
244
245 @synchronized (self)
246 {
247 [eventStreams setObject:[NSValue valueWithPointer:stream] forKey:path];
248 }
249 }
250 else
251 {
252 NSLog( @"UKFSEventsWatcher addPath:%@ failed",path);
253 }
254 }
255
256
257 // -----------------------------------------------------------------------------
258 // removePath:
259 // Stop watching the folder at the specified path.
260 // -----------------------------------------------------------------------------
261
262 -(void) removePath: (NSString*)path
263 {
264 NSValue* value = nil;
265
266 @synchronized (self)
267 {
268 value = [[[eventStreams objectForKey:path] retain] autorelease];
269 [eventStreams removeObjectForKey:path];
270 }
271
272 if (value)
273 {
274 FSEventStreamRef stream = [value pointerValue];
275
276 if (stream)
277 {
278 FSEventStreamStop(stream);
279 FSEventStreamInvalidate(stream);
280 FSEventStreamRelease(stream);
281 }
282 }
283 }
284
285
286 // -----------------------------------------------------------------------------
287 // removeAllPaths:
288 // Stop watching all known folders.
289 // -----------------------------------------------------------------------------
290
291 -(void) removeAllPaths
292 {
293 NSEnumerator* paths = [[eventStreams allKeys] objectEnumerator];
294 NSString* path;
295
296 while (path = [paths nextObject])
297 {
298 [self removePath:path];
299 }
300 }
301
302
303 @end
304
305 #endif
306
Something went wrong with that request. Please try again.