]> granicus.if.org Git - transmission/commitdiff
#5223 Use VDKQueue for watching for torrent files
authorMitchell Livingston <livings124@transmissionbt.com>
Sat, 19 Jan 2013 05:03:00 +0000 (05:03 +0000)
committerMitchell Livingston <livings124@transmissionbt.com>
Sat, 19 Jan 2013 05:03:00 +0000 (05:03 +0000)
18 files changed:
AUTHORS
Transmission.xcodeproj/project.pbxproj
macosx/Controller.h
macosx/Controller.m
macosx/Credits.rtf
macosx/PrefsController.m
macosx/UKKQueue/Readme.txt [deleted file]
macosx/UKKQueue/UKFNSubscribeFileWatcher.h [deleted file]
macosx/UKKQueue/UKFNSubscribeFileWatcher.m [deleted file]
macosx/UKKQueue/UKFileWatcher.h [deleted file]
macosx/UKKQueue/UKFileWatcher.m [deleted file]
macosx/UKKQueue/UKKQueue.h [deleted file]
macosx/UKKQueue/UKKQueue.m [deleted file]
macosx/UKKQueue/UKMainThreadProxy.h [deleted file]
macosx/UKKQueue/UKMainThreadProxy.m [deleted file]
macosx/VDKQueue/README.md [new file with mode: 0755]
macosx/VDKQueue/VDKQueue.h [new file with mode: 0755]
macosx/VDKQueue/VDKQueue.m [new file with mode: 0755]

diff --git a/AUTHORS b/AUTHORS
index d0343ac5a5de6c4da7d212e1c12a7d833c7d6b54..6570ec03e7322db979f98b533425a57f001ea1ef 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -52,5 +52,5 @@ Third-Party Resources:
   Thomas Bernard for MiniUPnP and libnatpmp. <http://miniupnp.tuxfamily.org/>
   The Growl Development Team for Growl. <http://growl.info/>
   Andy Matuschak for Sparkle. <http://sparkle.andymatuschak.org/>
-  Christian Hammond for SexyIconEntry. <http://www.chipx86.com/wiki/Libsexy> 
-  M. Uli Kusterer for UKKQueue.
+  Christian Hammond for SexyIconEntry. <http://www.chipx86.com/wiki/Libsexy>
+  Bryan D K Jones for VDKQueue. <https://github.com/bdkjones/VDKQueue>
index 2e0c149f08d114629a03c43c4df9bf77f392cbc8..7d65846c5c19eb39894f35764ca86f453d1b95f3 100644 (file)
                A29DF8BE0DB2545F00D04E5A /* verify.h in Headers */ = {isa = PBXBuildFile; fileRef = A2D22A110D65EED100007D5F /* verify.h */; };
                A29E653613F1603100048D71 /* evutil_rand.c in Sources */ = {isa = PBXBuildFile; fileRef = A29E653513F1603100048D71 /* evutil_rand.c */; };
                A2A1CB7A0BF29D5500AE959F /* PeerProgressIndicatorCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A2A1CB780BF29D5500AE959F /* PeerProgressIndicatorCell.m */; };
-               A2A3065C0AAD24A80049E2AC /* UKFileWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = A2A306540AAD24A80049E2AC /* UKFileWatcher.m */; };
-               A2A3065E0AAD24A80049E2AC /* UKFNSubscribeFileWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = A2A306560AAD24A80049E2AC /* UKFNSubscribeFileWatcher.m */; };
-               A2A306600AAD24A80049E2AC /* UKKQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = A2A306580AAD24A80049E2AC /* UKKQueue.m */; };
-               A2A306620AAD24A80049E2AC /* UKMainThreadProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = A2A3065A0AAD24A80049E2AC /* UKMainThreadProxy.m */; };
                A2A4E9210DE0F7E9000CE197 /* web.h in Headers */ = {isa = PBXBuildFile; fileRef = A29EBE530DC01FC9006CEE80 /* web.h */; };
                A2A4E9220DE0F7EB000CE197 /* web.c in Sources */ = {isa = PBXBuildFile; fileRef = A29EBE520DC01FC9006CEE80 /* web.c */; };
                A2A4EA0E0DE106EB000CE197 /* ConvertUTF.c in Sources */ = {isa = PBXBuildFile; fileRef = A2A4EA0A0DE106E8000CE197 /* ConvertUTF.c */; };
                A2AAB65F0DE0CF6200E04DDA /* rpcimpl.c in Sources */ = {isa = PBXBuildFile; fileRef = A2AAB65B0DE0CF6200E04DDA /* rpcimpl.c */; };
                A2AAB6650DE0D08B00E04DDA /* blocklist.h in Headers */ = {isa = PBXBuildFile; fileRef = A2D307930D9EC4860051FD27 /* blocklist.h */; };
                A2AB76EA15D8130B009EFC95 /* libcurl.4.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A2290D2D1442B23200B95A09 /* libcurl.4.dylib */; };
+               A2AB883E16A399A6008FAD50 /* VDKQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = A2AB883C16A399A6008FAD50 /* VDKQueue.m */; };
                A2AF1C390A3D0F6200F1575D /* FileOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = A2AF1C370A3D0F6200F1575D /* FileOutlineView.m */; };
                A2B3FB460E5901E700FF78FB /* cli.c in Sources */ = {isa = PBXBuildFile; fileRef = A2B3FB450E5901E700FF78FB /* cli.c */; };
                A2B3FB4C0E59023000FF78FB /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
                A2A1C81D142EC032008C17BF /* nl */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = nl; path = macosx/nl.lproj/GlobalOptionsPopover.xib; sourceTree = "<group>"; };
                A2A1CB770BF29D5500AE959F /* PeerProgressIndicatorCell.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = PeerProgressIndicatorCell.h; path = macosx/PeerProgressIndicatorCell.h; sourceTree = "<group>"; };
                A2A1CB780BF29D5500AE959F /* PeerProgressIndicatorCell.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = PeerProgressIndicatorCell.m; path = macosx/PeerProgressIndicatorCell.m; sourceTree = "<group>"; };
-               A2A306530AAD24A80049E2AC /* UKFileWatcher.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = UKFileWatcher.h; path = macosx/UKKQueue/UKFileWatcher.h; sourceTree = "<group>"; };
-               A2A306540AAD24A80049E2AC /* UKFileWatcher.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = UKFileWatcher.m; path = macosx/UKKQueue/UKFileWatcher.m; sourceTree = "<group>"; };
-               A2A306550AAD24A80049E2AC /* UKFNSubscribeFileWatcher.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = UKFNSubscribeFileWatcher.h; path = macosx/UKKQueue/UKFNSubscribeFileWatcher.h; sourceTree = "<group>"; };
-               A2A306560AAD24A80049E2AC /* UKFNSubscribeFileWatcher.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = UKFNSubscribeFileWatcher.m; path = macosx/UKKQueue/UKFNSubscribeFileWatcher.m; sourceTree = "<group>"; };
-               A2A306570AAD24A80049E2AC /* UKKQueue.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = UKKQueue.h; path = macosx/UKKQueue/UKKQueue.h; sourceTree = "<group>"; };
-               A2A306580AAD24A80049E2AC /* UKKQueue.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = UKKQueue.m; path = macosx/UKKQueue/UKKQueue.m; sourceTree = "<group>"; };
-               A2A306590AAD24A80049E2AC /* UKMainThreadProxy.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = UKMainThreadProxy.h; path = macosx/UKKQueue/UKMainThreadProxy.h; sourceTree = "<group>"; };
-               A2A3065A0AAD24A80049E2AC /* UKMainThreadProxy.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = UKMainThreadProxy.m; path = macosx/UKKQueue/UKMainThreadProxy.m; sourceTree = "<group>"; };
                A2A4EA0A0DE106E8000CE197 /* ConvertUTF.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ConvertUTF.c; path = libtransmission/ConvertUTF.c; sourceTree = "<group>"; };
                A2A4EA0B0DE106E8000CE197 /* ConvertUTF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ConvertUTF.h; path = libtransmission/ConvertUTF.h; sourceTree = "<group>"; };
                A2A632190CD9751700E3DA60 /* BadgeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BadgeView.h; path = macosx/BadgeView.h; sourceTree = "<group>"; };
                A2AAB6590DE0CF6200E04DDA /* rpcimpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rpcimpl.h; path = libtransmission/rpcimpl.h; sourceTree = "<group>"; };
                A2AAB65A0DE0CF6200E04DDA /* rpc-server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "rpc-server.h"; path = "libtransmission/rpc-server.h"; sourceTree = "<group>"; };
                A2AAB65B0DE0CF6200E04DDA /* rpcimpl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = rpcimpl.c; path = libtransmission/rpcimpl.c; sourceTree = "<group>"; };
+               A2AB883B16A399A6008FAD50 /* VDKQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VDKQueue.h; sourceTree = "<group>"; };
+               A2AB883C16A399A6008FAD50 /* VDKQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VDKQueue.m; sourceTree = "<group>"; };
                A2AC81D60D987E2D00A2878C /* nl */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = nl; path = macosx/nl.lproj/AddWindow.xib; sourceTree = "<group>"; };
                A2AF1C360A3D0F6200F1575D /* FileOutlineView.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = FileOutlineView.h; path = macosx/FileOutlineView.h; sourceTree = "<group>"; };
                A2AF1C370A3D0F6200F1575D /* FileOutlineView.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = FileOutlineView.m; path = macosx/FileOutlineView.m; sourceTree = "<group>"; };
                                A26AF2220D2DA42800FF7140 /* File Outline View */,
                                E1B6FBFD0C0D72430015FE4D /* Prefs Window */,
                                E1B6FC000C0D72A00015FE4D /* Overlay Window */,
-                               E1B6FBEB0C0D70AC0015FE4D /* UKKQueue */,
                                E138A9750C04D88F00C5426C /* ProgressGradients.h */,
                                E138A9760C04D88F00C5426C /* ProgressGradients.m */,
                                A2E57AB91310822C00A7DAB1 /* StatusBarController.h */,
                                A222E9860E6B21D9009FB003 /* BlocklistDownloaderViewController.m */,
                                A222EA790E6C32C4009FB003 /* BlocklistScheduler.h */,
                                A222EA7A0E6C32C4009FB003 /* BlocklistScheduler.m */,
+                               A2AB883916A399A6008FAD50 /* VDKQueue */,
                        );
                        name = Sources;
                        sourceTree = "<group>";
                        name = "File Outline View";
                        sourceTree = "<group>";
                };
+               A2AB883916A399A6008FAD50 /* VDKQueue */ = {
+                       isa = PBXGroup;
+                       children = (
+                               A2AB883B16A399A6008FAD50 /* VDKQueue.h */,
+                               A2AB883C16A399A6008FAD50 /* VDKQueue.m */,
+                       );
+                       name = VDKQueue;
+                       path = macosx/VDKQueue;
+                       sourceTree = "<group>";
+               };
                A2E384BF130DFA49001F501B /* libutp */ = {
                        isa = PBXGroup;
                        children = (
                        name = daemon;
                        sourceTree = "<group>";
                };
-               E1B6FBEB0C0D70AC0015FE4D /* UKKQueue */ = {
-                       isa = PBXGroup;
-                       children = (
-                               A2A306570AAD24A80049E2AC /* UKKQueue.h */,
-                               A2A306580AAD24A80049E2AC /* UKKQueue.m */,
-                               A2A306530AAD24A80049E2AC /* UKFileWatcher.h */,
-                               A2A306540AAD24A80049E2AC /* UKFileWatcher.m */,
-                               A2A306550AAD24A80049E2AC /* UKFNSubscribeFileWatcher.h */,
-                               A2A306560AAD24A80049E2AC /* UKFNSubscribeFileWatcher.m */,
-                               A2A306590AAD24A80049E2AC /* UKMainThreadProxy.h */,
-                               A2A3065A0AAD24A80049E2AC /* UKMainThreadProxy.m */,
-                       );
-                       name = UKKQueue;
-                       sourceTree = "<group>";
-               };
                E1B6FBF80C0D719B0015FE4D /* Info Window */ = {
                        isa = PBXGroup;
                        children = (
                                A2AF1C390A3D0F6200F1575D /* FileOutlineView.m in Sources */,
                                A2710E770A86796000CE4F7D /* PrefsWindow.m in Sources */,
                                A256588D0A9A695400E8A03B /* MessageWindowController.m in Sources */,
-                               A2A3065C0AAD24A80049E2AC /* UKFileWatcher.m in Sources */,
-                               A2A3065E0AAD24A80049E2AC /* UKFNSubscribeFileWatcher.m in Sources */,
-                               A2A306600AAD24A80049E2AC /* UKKQueue.m in Sources */,
-                               A2A306620AAD24A80049E2AC /* UKMainThreadProxy.m in Sources */,
                                A29C8B370ACC6EB3000ED9F9 /* PortChecker.m in Sources */,
                                A2AA579D0ADFCAB400CA59F6 /* PiecesView.m in Sources */,
                                A25E74650AF5097C006F11AE /* ExpandedPathToPathTransformer.m in Sources */,
                                A22BAE281388040500FB022F /* NSMutableArrayAdditions.m in Sources */,
                                A2966E8713DAF74C007B52DF /* GlobalOptionsPopoverViewController.m in Sources */,
                                A234EA541453563B000F3E97 /* NSImageAdditions.m in Sources */,
+                               A2AB883E16A399A6008FAD50 /* VDKQueue.m in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
index f08050c6ee0f0285c34f91b45fc70f91637548a6..aeb0ea9afce0bc9757c04a972cd563c0a90150e7 100644 (file)
@@ -26,6 +26,7 @@
 #import <transmission.h>
 #import <Quartz/Quartz.h>
 #import <Growl/Growl.h>
+#import "VDKQueue.h"
 
 @class AddMagnetWindowController;
 @class AddWindowController;
@@ -49,7 +50,7 @@ typedef enum
     ADD_CREATED
 } addType;
 
-@interface Controller : NSObject <GrowlApplicationBridgeDelegate, NSURLDownloadDelegate, NSUserNotificationCenterDelegate, NSPopoverDelegate, NSSoundDelegate, NSToolbarDelegate, NSWindowDelegate, QLPreviewPanelDataSource, QLPreviewPanelDelegate>
+@interface Controller : NSObject <GrowlApplicationBridgeDelegate, NSURLDownloadDelegate, NSUserNotificationCenterDelegate, NSPopoverDelegate, NSSoundDelegate, NSToolbarDelegate, NSWindowDelegate, QLPreviewPanelDataSource, QLPreviewPanelDelegate, VDKQueueDelegate>
 {
     tr_session                      * fLib;
     
@@ -233,6 +234,8 @@ typedef enum
 
 - (void) sleepCallback: (natural_t) messageType argument: (void *) messageArgument;
 
+@property (retain, readonly) VDKQueue * fileWatcherQueue;
+
 - (void) torrentTableViewSelectionDidChange: (NSNotification *) notification;
 
 - (void) toggleSmallView: (id) sender;
index f1566c6708dfbfd2d305a4a0dea06311eb4dea76..6e3c33c49c77206bcaf295217562b6cf49c5bbeb 100644 (file)
@@ -60,7 +60,7 @@
 #import "utils.h"
 #import "variant.h"
 
-#import "UKKQueue.h"
+#import "VDKQueue.h"
 #import <Sparkle/Sparkle.h>
 
 #define TOOLBAR_CREATE                  @"Toolbar Create"
@@ -160,6 +160,7 @@ static void sleepCallback(void * controller, io_service_t y, natural_t messageTy
 #warning remove ivars in header when 64-bit only (or it compiles in 32-bit mode)
 @synthesize prefsController = fPrefsController;
 @synthesize messageWindowController = fMessageController;
+@synthesize fileWatcherQueue = fFileWatcherQueue;
 
 + (void) initialize
 {
@@ -371,6 +372,10 @@ static void sleepCallback(void * controller, io_service_t y, natural_t messageTy
         
         fInfoController = [[InfoWindowController alloc] init];
         
+        //needs to be done before init-ing the prefs controller
+        fFileWatcherQueue = [[[VDKQueue alloc] init] autorelease];
+        [fFileWatcherQueue setDelegate: self];
+        
         fPrefsController = [[PrefsController alloc] initWithHandle: fLib];
         
         fQuitting = NO;
@@ -385,8 +390,6 @@ static void sleepCallback(void * controller, io_service_t y, natural_t messageTy
         
         [GrowlApplicationBridge setGrowlDelegate: self];
         
-        [[UKKQueue sharedFileWatcher] setDelegate: self];
-        
         [[SUUpdater sharedUpdater] setDelegate: self];
         fQuitRequested = NO;
         
@@ -777,6 +780,8 @@ static void sleepCallback(void * controller, io_service_t y, natural_t messageTy
     
     [fConfigDirectory release];
     
+    [fFileWatcherQueue release];
+    
     //complete cleanup
     tr_sessionClose(fLib);
 }
@@ -2929,27 +2934,26 @@ static void sleepCallback(void * controller, io_service_t y, natural_t messageTy
     fSoundPlaying = NO;
 }
 
-- (void) watcher: (id<UKFileWatcher>) watcher receivedNotification: (NSString *) notification forPath: (NSString *) path
+-(void) VDKQueue: (VDKQueue *) queue receivedNotification: (NSString*) notification forPath: (NSString*) fpath
 {
-    if ([notification isEqualToString: UKFileWatcherWriteNotification])
+    NSParameterAssert([notification isEqualToString:VDKQueueWriteNotification]);NSLog(@"%@",notification);
+    
+    if (![fDefaults boolForKey: @"AutoImport"] || ![fDefaults stringForKey: @"AutoImportDirectory"])
+        return;
+    
+    if (fAutoImportTimer)
     {
-        if (![fDefaults boolForKey: @"AutoImport"] || ![fDefaults stringForKey: @"AutoImportDirectory"])
-            return;
-        
-        if (fAutoImportTimer)
-        {
-            if ([fAutoImportTimer isValid])
-                [fAutoImportTimer invalidate];
-            [fAutoImportTimer release];
-            fAutoImportTimer = nil;
-        }
-        
-        //check again in 10 seconds in case torrent file wasn't complete
-        fAutoImportTimer = [[NSTimer scheduledTimerWithTimeInterval: 10.0 target: self 
-            selector: @selector(checkAutoImportDirectory) userInfo: nil repeats: NO] retain];
-        
-        [self checkAutoImportDirectory];
+        if ([fAutoImportTimer isValid])
+            [fAutoImportTimer invalidate];
+        [fAutoImportTimer release];
+        fAutoImportTimer = nil;
     }
+    
+    //check again in 10 seconds in case torrent file wasn't complete
+    fAutoImportTimer = [[NSTimer scheduledTimerWithTimeInterval: 10.0 target: self 
+        selector: @selector(checkAutoImportDirectory) userInfo: nil repeats: NO] retain];
+    
+    [self checkAutoImportDirectory];
 }
 
 - (void) changeAutoImport
index 7c278a49925631475631ee1270482f2e1c4757c4..f62ece1083864d2824e02c4c7ca0e2fb01d87922 100644 (file)
@@ -1,5 +1,5 @@
-{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf470
-{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
 {\colortbl;\red255\green255\blue255;\red127\green127\blue127;}
 \vieww14160\viewh15100\viewkind0
 \pard\tx440\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li100\slleading40\sb40\qc
@@ -145,4 +145,4 @@ Lead Developers
        Thomas Bernard for use of MiniUPnP and libnatpmp. <{\field{\*\fldinst{HYPERLINK "http://miniupnp.tuxfamily.org/"}}{\fldrslt http://miniupnp.tuxfamily.org/}}>\
        The Growl Development Team for use of Growl. <{\field{\*\fldinst{HYPERLINK "http://growl.info/"}}{\fldrslt http://growl.info/}}>\
        Andy Matuschak for use of Sparkle. <{\field{\*\fldinst{HYPERLINK "http://sparkle.andymatuschak.org/"}}{\fldrslt http://sparkle.andymatuschak.org/}}>\
-       M. Uli Kusterer for use of UKKQueue.}
\ No newline at end of file
+       Bryan D K Jones for use of VDKQueue. <{\field{\*\fldinst{HYPERLINK "https://github.com/bdkjones/VDKQueue"}}{\fldrslt https://github.com/bdkjones/VDKQueue}}>}
\ No newline at end of file
index 52a29e36069282e3f5b01f5a3fe0121382190822..47fad063607778bf789c14204b1123f112409b45 100644 (file)
@@ -30,7 +30,7 @@
 #import "BonjourController.h"
 #import "NSApplicationAdditions.h"
 #import "NSStringAdditions.h"
-#import "UKKQueue.h"
+#import "VDKQueue.h"
 
 #import "transmission.h"
 #import "utils.h"
         //set auto import
         NSString * autoPath;
         if ([fDefaults boolForKey: @"AutoImport"] && (autoPath = [fDefaults stringForKey: @"AutoImportDirectory"]))
-            [[UKKQueue sharedFileWatcher] addPath: [autoPath stringByExpandingTildeInPath]];
+            [[(Controller *)[NSApp delegate] fileWatcherQueue] addPath: [autoPath stringByExpandingTildeInPath] notifyingAbout: VDKQueueNotifyAboutWrite];
         
         //set blocklist scheduler
         [[BlocklistScheduler scheduler] updateSchedule];
     NSString * path;
     if ((path = [fDefaults stringForKey: @"AutoImportDirectory"]))
     {
-        path = [path stringByExpandingTildeInPath];
+        VDKQueue * watcherQueue = [(Controller *)[NSApp delegate] fileWatcherQueue];
         if ([fDefaults boolForKey: @"AutoImport"])
-            [[UKKQueue sharedFileWatcher] addPath: path];
+        {
+            path = [path stringByExpandingTildeInPath];
+            [watcherQueue addPath: path notifyingAbout: VDKQueueNotifyAboutWrite];
+        }
         else
-            [[UKKQueue sharedFileWatcher] removePathFromQueue: path];
+            [watcherQueue removeAllPaths];
         
         [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoImportSettingChange" object: self];
     }
     [panel setCanCreateDirectories: YES];
 
     [panel beginSheetModalForWindow: [self window] completionHandler: ^(NSInteger result) {
-        NSString * path = [fDefaults stringForKey: @"AutoImportDirectory"];
         if (result == NSFileHandlingPanelOKButton)
         {
-            UKKQueue * sharedQueue = [UKKQueue sharedFileWatcher];
-            if (path)
-                [sharedQueue removePathFromQueue: [path stringByExpandingTildeInPath]];
+            VDKQueue * watcherQueue = [(Controller *)[NSApp delegate] fileWatcherQueue];
+            [watcherQueue removeAllPaths];
             
-            path = [[[panel URLs] objectAtIndex: 0] path];
+            NSString * path = [[[panel URLs] objectAtIndex: 0] path];
             [fDefaults setObject: path forKey: @"AutoImportDirectory"];
-            [sharedQueue addPath: [path stringByExpandingTildeInPath]];
+            [watcherQueue addPath: [path stringByExpandingTildeInPath] notifyingAbout: VDKQueueNotifyAboutWrite];
             
             [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoImportSettingChange" object: self];
         }
-        else if (!path)
-            [fDefaults setBool: NO forKey: @"AutoImport"];
+        else
+        {
+            NSString * path = [fDefaults stringForKey: @"AutoImportDirectory"];
+            if (!path)
+                [fDefaults setBool: NO forKey: @"AutoImport"];
+        }
         
         [fImportFolderPopUp selectItemAtIndex: 0];
     }];
diff --git a/macosx/UKKQueue/Readme.txt b/macosx/UKKQueue/Readme.txt
deleted file mode 100644 (file)
index 582e3da..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-UKKQUEUE
---------
-
-A wrapper class around the kqueue file change notification mechanism.
-
-Simply create a UKKQueue (or use the singleton), add a few paths to it and listen to the change notifications via NSWorkspace's notification center.
-
-LICENSE:
-
-(c) 2003-06 by M. Uli Kusterer. You may redistribute, modify, use in
-commercial products free of charge, however distributing modified copies
-requires that you clearly mark them as having been modified by you, while
-maintaining the original markings and copyrights. I don't like getting bug
-reports about code I wasn't involved in.
-
-I'd also appreciate if you gave credit in your app's about screen or a similar
-place. A simple "Thanks to M. Uli Kusterer" is quite sufficient.
-Also, I rarely turn down any postcards, gifts, complementary copies of
-applications etc.
-
-
-REVISION HISTORY:
-0.1 - Initial release.
-0.2 - Now calls delegate on main thread using UKMainThreadProxy, and checks retain count to make sure the object is released even when the thread is still holding on to it. Equivalent to SVN revision 79.
-0.3 - Now adopts UKFileWatcher protocol to allow swapping out a kqueue for another scheme easily. Uses O_EVONLY instead of O_RDONLY to open the file without preventing it from being deleted or its drive ejected.
-0.4 - Now includes UKFNSubscribeFileWatcher, and closes the kqueue file descriptor in a separate thread (thanks to Dominic Yu for the suggestion!) so you don't have to wait for close() to time out.
-0.5 - Turns off all deprecated features. Changes the notifications to make it possible to subscribe to them more selectively. Changes notification constants to be safer for apps that expose KQueue to their plugins. FNSubscribeFileWatcher now also sends notifications (sorry, "write" only).
-
-
-CONTACT:
-Get the newest version at http://www.zathras.de
-E-Mail me at witness (at) zathras (dot) de or witness (dot) of (dot) teachtext (at) gmx (dot) net
diff --git a/macosx/UKKQueue/UKFNSubscribeFileWatcher.h b/macosx/UKKQueue/UKFNSubscribeFileWatcher.h
deleted file mode 100644 (file)
index 259cb83..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/* =============================================================================
-       FILE:           UKFNSubscribeFileWatcher.m
-       PROJECT:        Filie
-    
-    COPYRIGHT:  (c) 2005 M. Uli Kusterer, all rights reserved.
-    
-       AUTHORS:        M. Uli Kusterer - UK
-    
-    LICENSES:   MIT License
-
-       REVISIONS:
-               2006-03-13      UK      Commented, added singleton.
-               2005-03-02      UK      Created.
-   ========================================================================== */
-
-// -----------------------------------------------------------------------------
-//  Headers:
-// -----------------------------------------------------------------------------
-
-#import <Cocoa/Cocoa.h>
-#import "UKFileWatcher.h"
-#import <Carbon/Carbon.h>
-
-/*
-       NOTE: FNSubscribe has a built-in delay: If your application is in the
-       background while the changes happen, all notifications will be queued up
-       and sent to your app at once the moment it is brought to front again. If
-       your app really needs to do live updates in the background, use a KQueue
-       instead.
-*/
-
-// -----------------------------------------------------------------------------
-//  Class declaration:
-// -----------------------------------------------------------------------------
-
-@interface UKFNSubscribeFileWatcher : NSObject <UKFileWatcher>
-{
-    id                      delegate;           // Delegate must respond to UKFileWatcherDelegate protocol.
-    NSMutableDictionary*    subscriptions;      // List of FNSubscription pointers in NSValues, with the pathnames as their keys.
-}
-
-+(id) sharedFileWatcher;
-
-// UKFileWatcher defines the methods: addPath: removePath: and delegate accessors.
-
-// Private:
--(void) sendDelegateMessage: (FNMessage)message forSubscription: (FNSubscriptionRef)subscription;
-
-@end
diff --git a/macosx/UKKQueue/UKFNSubscribeFileWatcher.m b/macosx/UKKQueue/UKFNSubscribeFileWatcher.m
deleted file mode 100644 (file)
index d642a3a..0000000
+++ /dev/null
@@ -1,201 +0,0 @@
-/* =============================================================================
-       FILE:           UKFNSubscribeFileWatcher.m
-       PROJECT:        Filie
-    
-    COPYRIGHT:  (c) 2005 M. Uli Kusterer, all rights reserved.
-    
-       AUTHORS:        M. Uli Kusterer - UK
-    
-    LICENSES:   MIT License
-
-       REVISIONS:
-               2006-03-13      UK      Commented, added singleton, added notifications.
-               2005-03-02      UK      Created.
-   ========================================================================== */
-
-// -----------------------------------------------------------------------------
-//  Headers:
-// -----------------------------------------------------------------------------
-
-#import "UKFNSubscribeFileWatcher.h"
-#import <Carbon/Carbon.h>
-
-
-// -----------------------------------------------------------------------------
-//  Prototypes:
-// -----------------------------------------------------------------------------
-
-void    UKFileSubscriptionProc(FNMessage message, OptionBits flags, void *refcon, FNSubscriptionRef subscription);
-
-
-@implementation UKFNSubscribeFileWatcher
-
-// -----------------------------------------------------------------------------
-//  sharedFileWatcher:
-//             Singleton accessor.
-// -----------------------------------------------------------------------------
-
-+(id) sharedFileWatcher
-{
-       static UKFNSubscribeFileWatcher* sSharedFileWatcher = nil;
-       
-       if( !sSharedFileWatcher )
-               sSharedFileWatcher = [[UKFNSubscribeFileWatcher alloc] init];   // This is a singleton, and thus an intentional "leak".
-    
-    return sSharedFileWatcher;
-}
-
-
-// -----------------------------------------------------------------------------
-//  * CONSTRUCTOR:
-// -----------------------------------------------------------------------------
-
--(id)   init
-{
-    self = [super init];
-    if( !self ) 
-        return nil;
-    
-    subscriptions = [[NSMutableDictionary alloc] init];
-    
-    return self;
-}
-
-
-// -----------------------------------------------------------------------------
-//  * DESTRUCTOR:
-// -----------------------------------------------------------------------------
-
--(void) dealloc
-{
-    NSEnumerator*   enny = [subscriptions objectEnumerator];
-    NSValue*        subValue = nil;
-    
-    while( (subValue = [enny nextObject]) )
-    {
-        FNSubscriptionRef   subscription = [subValue pointerValue];
-        FNUnsubscribe( subscription );
-    }
-    
-    [subscriptions release];
-    [super dealloc];
-}
-
-
-// -----------------------------------------------------------------------------
-//  addPath:
-//             Start watching the object at the specified path. This only sends write
-//             notifications for all changes, as FNSubscribe doesn't tell what actually
-//             changed about our folder.
-// -----------------------------------------------------------------------------
-
--(void) addPath: (NSString*)path
-{
-    OSStatus                    err = noErr;
-    static FNSubscriptionUPP    subscriptionUPP = NULL;
-    FNSubscriptionRef           subscription = NULL;
-    
-    if( !subscriptionUPP )
-        subscriptionUPP = NewFNSubscriptionUPP( UKFileSubscriptionProc );
-    
-    err = FNSubscribeByPath( (UInt8*) [path fileSystemRepresentation], subscriptionUPP, (void*)self,
-                                kNilOptions, &subscription );
-    if( err != noErr )
-    {
-        NSLog( @"UKFNSubscribeFileWatcher addPath: %@ failed due to error ID=%d.", path, err );
-        return;
-    }
-    
-    [subscriptions setObject: [NSValue valueWithPointer: subscription] forKey: path];
-}
-
-
-// -----------------------------------------------------------------------------
-//  removePath:
-//             Stop watching the object at the specified path.
-// -----------------------------------------------------------------------------
-
--(void) removePath: (NSString*)path
-{
-    NSValue*            subValue = nil;
-    @synchronized( self )
-    {
-        subValue = [[[subscriptions objectForKey: path] retain] autorelease];
-        [subscriptions removeObjectForKey: path];
-    }
-    
-       if( subValue )
-       {
-               FNSubscriptionRef   subscription = [subValue pointerValue];
-               
-               FNUnsubscribe( subscription );
-       }
-}
-
-
-// -----------------------------------------------------------------------------
-//  sendDelegateMessage:forSubscription:
-//             Bottleneck for change notifications. This is called by our callback
-//             function to actually inform the delegate and send out notifications.
-//
-//             This *only* sends out write notifications, as FNSubscribe doesn't tell
-//             what changed about our folder.
-// -----------------------------------------------------------------------------
-
--(void) sendDelegateMessage: (FNMessage)message forSubscription: (FNSubscriptionRef)subscription
-{
-    NSValue*                    subValue = [NSValue valueWithPointer: subscription];
-    NSString*                   path = [[subscriptions allKeysForObject: subValue] objectAtIndex: 0];
-    
-       [[[NSWorkspace sharedWorkspace] notificationCenter] postNotificationName: UKFileWatcherWriteNotification
-                                                                                                                       object: self
-                                                                                                                       userInfo: [NSDictionary dictionaryWithObjectsAndKeys: path, @"path", nil]];
-       
-    [delegate watcher: self receivedNotification: UKFileWatcherWriteNotification forPath: path];
-    //NSLog( @"UKFNSubscribeFileWatcher noticed change to %@", path ); // DEBUG ONLY!
-}
-
-
-
-// -----------------------------------------------------------------------------
-//  delegate:
-//             Accessor for file watcher delegate.
-// -----------------------------------------------------------------------------
-
--(id)   delegate
-{
-    return delegate;
-}
-
-
-// -----------------------------------------------------------------------------
-//  setDelegate:
-//             Mutator for file watcher delegate.
-// -----------------------------------------------------------------------------
-
--(void) setDelegate: (id)newDelegate
-{
-    delegate = newDelegate;
-}
-
-
-@end
-
-
-// -----------------------------------------------------------------------------
-//  UKFileSubscriptionProc:
-//             Callback function we hand to Carbon so it can tell us when something
-//             changed about our watched folders. We set the refcon to a pointer to
-//             our object. This simply extracts the object and hands the info off to
-//             sendDelegateMessage:forSubscription: which does the actual work.
-// -----------------------------------------------------------------------------
-
-void    UKFileSubscriptionProc( FNMessage message, OptionBits flags, void *refcon, FNSubscriptionRef subscription )
-{
-    UKFNSubscribeFileWatcher*   obj = (UKFNSubscribeFileWatcher*) refcon;
-    
-    if( message == kFNDirectoryModifiedMessage )    // No others exist as of 10.4
-        [obj sendDelegateMessage: message forSubscription: subscription];
-    else
-        NSLog( @"UKFileSubscriptionProc: Unknown message %d", message );
-}
\ No newline at end of file
diff --git a/macosx/UKKQueue/UKFileWatcher.h b/macosx/UKKQueue/UKFileWatcher.h
deleted file mode 100644 (file)
index c2c4ba2..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/* =============================================================================
-       FILE:           UKFileWatcher.h
-       PROJECT:        Filie
-    
-    COPYRIGHT:  (c) 2005 M. Uli Kusterer, all rights reserved.
-    
-       AUTHORS:        M. Uli Kusterer - UK
-    
-    LICENSES:   MIT License
-
-       REVISIONS:
-               2006-03-13      UK      Moved notification constants to .m file.
-               2005-02-25      UK      Created.
-   ========================================================================== */
-
-/*
-    This is a protocol that file change notification classes should adopt.
-    That way, no matter whether you use Carbon's FNNotify/FNSubscribe, BSD's
-    kqueue or whatever, the object being notified can react to change
-    notifications the same way, and you can easily swap one out for the other
-    to cater to different OS versions, target volumes etc.
-*/
-
-// -----------------------------------------------------------------------------
-//  Protocol:
-// -----------------------------------------------------------------------------
-
-@protocol UKFileWatcher
-
-// +(id) sharedFileWatcher;                    // Singleton accessor. Not officially part of the protocol, but use this name if you provide a singleton.
-
--(void) addPath: (NSString*)path;
--(void) removePath: (NSString*)path;
-
--(id)   delegate;
--(void) setDelegate: (id)newDelegate;
-
-@end
-
-// -----------------------------------------------------------------------------
-//  Methods delegates need to provide:
-// -----------------------------------------------------------------------------
-
-@interface NSObject (UKFileWatcherDelegate)
-
--(void) watcher: (id<UKFileWatcher>)kq receivedNotification: (NSString*)nm forPath: (NSString*)fpath;
-
-@end
-
-
-// Notifications this sends:
-/*  object                     = the file watcher object
-       userInfo.path   = file path watched
-       These notifications are sent via the NSWorkspace notification center */
-extern NSString* UKFileWatcherRenameNotification;
-extern NSString* UKFileWatcherWriteNotification;
-extern NSString* UKFileWatcherDeleteNotification;
-extern NSString* UKFileWatcherAttributeChangeNotification;
-extern NSString* UKFileWatcherSizeIncreaseNotification;
-extern NSString* UKFileWatcherLinkCountChangeNotification;
-extern NSString* UKFileWatcherAccessRevocationNotification;
-
diff --git a/macosx/UKKQueue/UKFileWatcher.m b/macosx/UKKQueue/UKFileWatcher.m
deleted file mode 100644 (file)
index 952cf95..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/* =============================================================================
-       FILE:           UKKQueue.m
-       PROJECT:        Filie
-    
-    COPYRIGHT:  (c) 2005-06 M. Uli Kusterer, all rights reserved.
-    
-       AUTHORS:        M. Uli Kusterer - UK
-    
-    LICENSES:   MIT License
-
-       REVISIONS:
-               2006-03-13      UK      Created, moved notification constants here as exportable
-                                               symbols.
-   ========================================================================== */
-
-// -----------------------------------------------------------------------------
-//  Headers:
-// -----------------------------------------------------------------------------
-
-#import <Cocoa/Cocoa.h>
-#import "UKFileWatcher.h"
-
-
-// -----------------------------------------------------------------------------
-//  Constants:
-// -----------------------------------------------------------------------------
-
-// Do not rely on the actual contents of these constants. They will eventually
-//     be changed to be more generic and less KQueue-specific.
-
-NSString* UKFileWatcherRenameNotification                              = @"UKKQueueFileRenamedNotification";
-NSString* UKFileWatcherWriteNotification                               = @"UKKQueueFileWrittenToNotification";
-NSString* UKFileWatcherDeleteNotification                              = @"UKKQueueFileDeletedNotification";
-NSString* UKFileWatcherAttributeChangeNotification             = @"UKKQueueFileAttributesChangedNotification";
-NSString* UKFileWatcherSizeIncreaseNotification                        = @"UKKQueueFileSizeIncreasedNotification";
-NSString* UKFileWatcherLinkCountChangeNotification             = @"UKKQueueFileLinkCountChangedNotification";
-NSString* UKFileWatcherAccessRevocationNotification            = @"UKKQueueFileAccessRevocationNotification";
-
diff --git a/macosx/UKKQueue/UKKQueue.h b/macosx/UKKQueue/UKKQueue.h
deleted file mode 100644 (file)
index facd513..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-/* =============================================================================
-       FILE:           UKKQueue.h
-       PROJECT:        Filie
-    
-    COPYRIGHT:  (c) 2003 M. Uli Kusterer, all rights reserved.
-    
-       AUTHORS:        M. Uli Kusterer - UK
-    
-    LICENSES:   MIT License
-
-       REVISIONS:
-               2006-03-13      UK      Clarified license, streamlined UKFileWatcher stuff,
-                                               Changed notifications to be useful and turned off by
-                                               default some deprecated stuff.
-               2003-12-21      UK      Created.
-   ========================================================================== */
-
-// -----------------------------------------------------------------------------
-//  Headers:
-// -----------------------------------------------------------------------------
-
-#import <Foundation/Foundation.h>
-#include <sys/types.h>
-#include <sys/event.h>
-#import "UKFileWatcher.h"
-
-
-// -----------------------------------------------------------------------------
-//  Constants:
-// -----------------------------------------------------------------------------
-
-// Backwards compatibility constants. Don't rely on code commented out with these constants, because it may be deleted in a future version.
-#ifndef UKKQUEUE_BACKWARDS_COMPATIBLE
-#define UKKQUEUE_BACKWARDS_COMPATIBLE 0                        // 1 to send old-style kqueue:receivedNotification:forFile: messages to objects that accept them.
-#endif
-
-#ifndef UKKQUEUE_SEND_STUPID_NOTIFICATIONS
-#define UKKQUEUE_SEND_STUPID_NOTIFICATIONS 0   // 1 to send old-style notifications that have the path as the object and no userInfo dictionary.
-#endif
-
-#ifndef UKKQUEUE_OLD_SINGLETON_ACCESSOR_NAME
-#define UKKQUEUE_OLD_SINGLETON_ACCESSOR_NAME 0 // 1 to allow use of sharedQueue instead of sharedFileWatcher.
-#endif
-
-#ifndef UKKQUEUE_OLD_NOTIFICATION_NAMES
-#define UKKQUEUE_OLD_NOTIFICATION_NAMES 0              // 1 to allow use of old KQueue-style notification names instead of the new more generic ones in UKFileWatcher.
-#endif
-
-// Flags for notifyingAbout:
-#define UKKQueueNotifyAboutRename                                      NOTE_RENAME             // Item was renamed.
-#define UKKQueueNotifyAboutWrite                                       NOTE_WRITE              // Item contents changed (also folder contents changed).
-#define UKKQueueNotifyAboutDelete                                      NOTE_DELETE             // item was removed.
-#define UKKQueueNotifyAboutAttributeChange                     NOTE_ATTRIB             // Item attributes changed.
-#define UKKQueueNotifyAboutSizeIncrease                                NOTE_EXTEND             // Item size increased.
-#define UKKQueueNotifyAboutLinkCountChanged                    NOTE_LINK               // Item's link count changed.
-#define UKKQueueNotifyAboutAccessRevocation                    NOTE_REVOKE             // Access to item was revoked.
-
-// Notifications this sends:
-//  (see UKFileWatcher)
-// Old names: *deprecated*
-#if UKKQUEUE_OLD_NOTIFICATION_NAMES
-#define UKKQueueFileRenamedNotification                                UKFileWatcherRenameNotification
-#define UKKQueueFileWrittenToNotification                      UKFileWatcherWriteNotification
-#define UKKQueueFileDeletedNotification                                UKFileWatcherDeleteNotification
-#define UKKQueueFileAttributesChangedNotification   UKFileWatcherAttributeChangeNotification
-#define UKKQueueFileSizeIncreasedNotification          UKFileWatcherSizeIncreaseNotification
-#define UKKQueueFileLinkCountChangedNotification       UKFileWatcherLinkCountChangeNotification
-#define UKKQueueFileAccessRevocationNotification       UKFileWatcherAccessRevocationNotification
-#endif
-
-
-// -----------------------------------------------------------------------------
-//  UKKQueue:
-// -----------------------------------------------------------------------------
-
-@interface UKKQueue : NSObject <UKFileWatcher>
-{
-       int                             queueFD;                        // The actual queue ID (Unix file descriptor).
-       NSMutableArray* watchedPaths;           // List of NSStrings containing the paths we're watching.
-       NSMutableArray* watchedFDs;                     // List of NSNumbers containing the file descriptors we're watching.
-       id                              delegate;                       // Gets messages about changes instead of notification center, if specified.
-       id                              delegateProxy;          // Proxy object to which we send messages so they reach delegate on the main thread.
-       BOOL                    alwaysNotify;           // Send notifications even if we have a delegate? Defaults to NO.
-       BOOL                    keepThreadRunning;      // Termination criterion of our thread.
-}
-
-+(id)  sharedFileWatcher;      // Returns a singleton, a shared kqueue object Handy if you're subscribing to the notifications. Use this, or just create separate objects using alloc/init. Whatever floats your boat.
-
--(int)  queueFD;               // I know you unix geeks want this...
-
-// High-level file watching: (use UKFileWatcher protocol methods instead, where possible!)
--(void) addPathToQueue: (NSString*)path;
--(void) addPathToQueue: (NSString*)path notifyingAbout: (u_int)fflags;
--(void) removePathFromQueue: (NSString*)path;
-
--(id)  delegate;
--(void)        setDelegate: (id)newDelegate;
-
--(BOOL)        alwaysNotify;
--(void)        setAlwaysNotify: (BOOL)n;
-
-#if UKKQUEUE_OLD_SINGLETON_ACCESSOR_NAME
-+(UKKQueue*)    sharedQueue;
-#endif
-
-// private:
--(void)                watcherThread: (id)sender;
--(void)                postNotification: (NSString*)nm forFile: (NSString*)fp; // Message-posting bottleneck.
-
-@end
-
-
-// -----------------------------------------------------------------------------
-//  Methods delegates need to provide:
-//      * DEPRECATED * use UKFileWatcher delegate methods instead!
-// -----------------------------------------------------------------------------
-
-@interface NSObject (UKKQueueDelegate)
-
--(void) kqueue: (UKKQueue*)kq receivedNotification: (NSString*)nm forFile: (NSString*)fpath;
-
-@end
diff --git a/macosx/UKKQueue/UKKQueue.m b/macosx/UKKQueue/UKKQueue.m
deleted file mode 100644 (file)
index be6604e..0000000
+++ /dev/null
@@ -1,480 +0,0 @@
-/* =============================================================================
-       FILE:           UKKQueue.m
-       PROJECT:        Filie
-    
-    COPYRIGHT:  (c) 2003 M. Uli Kusterer, all rights reserved.
-    
-       AUTHORS:        M. Uli Kusterer - UK
-    
-    LICENSES:   MIT License
-
-       REVISIONS:
-               2006-03-13      UK      Clarified license, streamlined UKFileWatcher stuff,
-                                               Changed notifications to be useful and turned off by
-                                               default some deprecated stuff.
-        2004-12-28  UK  Several threading fixes.
-               2003-12-21      UK      Created.
-   ========================================================================== */
-
-// -----------------------------------------------------------------------------
-//  Headers:
-// -----------------------------------------------------------------------------
-
-#import "UKKQueue.h"
-#import "UKMainThreadProxy.h"
-#import <unistd.h>
-#import <fcntl.h>
-
-
-// -----------------------------------------------------------------------------
-//  Macros:
-// -----------------------------------------------------------------------------
-
-// @synchronized isn't available prior to 10.3, so we use a typedef so
-//  this class is thread-safe on Panther but still compiles on older OSs.
-
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3
-#define AT_SYNCHRONIZED(n)      @synchronized(n)
-#else
-#define AT_SYNCHRONIZED(n)
-#endif
-
-
-// -----------------------------------------------------------------------------
-//  Globals:
-// -----------------------------------------------------------------------------
-
-static UKKQueue * gUKKQueueSharedQueueSingleton = nil;
-
-
-@implementation UKKQueue
-
-// Deprecated:
-#if UKKQUEUE_OLD_SINGLETON_ACCESSOR_NAME
-+(UKKQueue*) sharedQueue
-{
-       return [self sharedFileWatcher];
-}
-#endif
-
-// -----------------------------------------------------------------------------
-//  sharedQueue:
-//             Returns a singleton queue object. In many apps (especially those that
-//      subscribe to the notifications) there will only be one kqueue instance,
-//      and in that case you can use this.
-//
-//      For all other cases, feel free to create additional instances to use
-//      independently.
-//
-//     REVISIONS:
-//             2006-03-13      UK      Renamed from sharedQueue.
-//      2005-07-02  UK  Created.
-// -----------------------------------------------------------------------------
-
-+(id) sharedFileWatcher
-{
-    AT_SYNCHRONIZED( self )
-    {
-        if( !gUKKQueueSharedQueueSingleton )
-            gUKKQueueSharedQueueSingleton = [[UKKQueue alloc] init];   // This is a singleton, and thus an intentional "leak".
-    }
-    
-    return gUKKQueueSharedQueueSingleton;
-}
-
-
-// -----------------------------------------------------------------------------
-//     * CONSTRUCTOR:
-//             Creates a new KQueue and starts that thread we use for our
-//             notifications.
-//
-//     REVISIONS:
-//      2004-11-12  UK  Doesn't pass self as parameter to watcherThread anymore,
-//                      because detachNewThreadSelector retains target and args,
-//                      which would cause us to never be released.
-//             2004-03-13      UK      Documented.
-// -----------------------------------------------------------------------------
-
--(id)   init
-{
-       self = [super init];
-       if( self )
-       {
-               queueFD = kqueue();
-               if( queueFD == -1 )
-               {
-                       [self release];
-                       return nil;
-               }
-               
-               watchedPaths = [[NSMutableArray alloc] init];
-               watchedFDs = [[NSMutableArray alloc] init];
-               
-               // Start new thread that fetches and processes our events:
-               keepThreadRunning = YES;
-               [NSThread detachNewThreadSelector:@selector(watcherThread:) toTarget:self withObject:nil];
-       }
-       
-       return self;
-}
-
-
-// -----------------------------------------------------------------------------
-//     release:
-//             Since NSThread retains its target, we need this method to terminate the
-//      thread when we reach a retain-count of two. The thread is terminated by
-//      setting keepThreadRunning to NO.
-//
-//     REVISIONS:
-//             2004-11-12      UK      Created.
-// -----------------------------------------------------------------------------
-
--(oneway void) release
-{
-    AT_SYNCHRONIZED(self)
-    {
-        //NSLog(@"%@ (%d)", self, [self retainCount]);
-        if( [self retainCount] == 2 && keepThreadRunning )
-            keepThreadRunning = NO;
-    }
-    
-    [super release];
-}
-    
-// -----------------------------------------------------------------------------
-//     * DESTRUCTOR:
-//             Releases the kqueue again.
-//
-//     REVISIONS:
-//             2004-03-13      UK      Documented.
-// -----------------------------------------------------------------------------
-
--(void) dealloc
-{
-       delegate = nil;
-       [delegateProxy release];
-       
-       if( keepThreadRunning )
-               keepThreadRunning = NO;
-       
-       // Close all our file descriptors so the files can be deleted:
-       NSEnumerator*   enny = [watchedFDs objectEnumerator];
-       NSNumber*               fdNum;
-       while( (fdNum = [enny nextObject]) )
-       {
-       if( close( [fdNum intValue] ) == -1 )
-            NSLog(@"dealloc: Couldn't close file descriptor (%d)", errno);
-    }
-       
-       [watchedPaths release];
-       watchedPaths = nil;
-       [watchedFDs release];
-       watchedFDs = nil;
-       
-       [super dealloc];
-    
-    //NSLog(@"kqueue released.");
-}
-
-
-// -----------------------------------------------------------------------------
-//     queueFD:
-//             Returns a Unix file descriptor for the KQueue this uses. The descriptor
-//             is owned by this object. Do not close it!
-//
-//     REVISIONS:
-//             2004-03-13      UK      Documented.
-// -----------------------------------------------------------------------------
-
--(int)  queueFD
-{
-       return queueFD;
-}
-
-
-// -----------------------------------------------------------------------------
-//     addPathToQueue:
-//             Tell this queue to listen for all interesting notifications sent for
-//             the object at the specified path. If you want more control, use the
-//             addPathToQueue:notifyingAbout: variant instead.
-//
-//     REVISIONS:
-//             2004-03-13      UK      Documented.
-// -----------------------------------------------------------------------------
-
--(void) addPathToQueue: (NSString*)path
-{
-       [self addPath: path];
-}
-
-
--(void) addPath: (NSString*)path
-{
-       [self addPathToQueue: path notifyingAbout: UKKQueueNotifyAboutRename
-                                                                                               | UKKQueueNotifyAboutWrite
-                                                                                               | UKKQueueNotifyAboutDelete
-                                                                                               | UKKQueueNotifyAboutAttributeChange];
-}
-
-
-// -----------------------------------------------------------------------------
-//     addPathToQueue:notfyingAbout:
-//             Tell this queue to listen for the specified notifications sent for
-//             the object at the specified path.
-//
-//     REVISIONS:
-//      2005-06-29  UK  Files are now opened using O_EVTONLY instead of O_RDONLY
-//                      which allows ejecting or deleting watched files/folders.
-//                      Thanks to Phil Hargett for finding this flag in the docs.
-//             2004-03-13      UK      Documented.
-// -----------------------------------------------------------------------------
-
--(void) addPathToQueue: (NSString*)path notifyingAbout: (u_int)fflags
-{
-       struct timespec         nullts = { 0, 0 };
-       struct kevent           ev;
-       int                                     fd = open( [path fileSystemRepresentation], O_EVTONLY, 0 );
-       
-    if( fd >= 0 )
-    {
-        EV_SET( &ev, fd, EVFILT_VNODE, 
-                               EV_ADD | EV_ENABLE | EV_CLEAR,
-                               fflags, 0, (void*)path );
-               
-        AT_SYNCHRONIZED( self )
-        {
-            [watchedPaths addObject: path];
-            [watchedFDs addObject: [NSNumber numberWithInt: fd]];
-            kevent( queueFD, &ev, 1, NULL, 0, &nullts );
-        }
-    }
-}
-
-
--(void) removePath: (NSString*)path
-{
-    [self removePathFromQueue: path];
-}
-
-
-// -----------------------------------------------------------------------------
-//     removePathFromQueue:
-//             Stop listening for changes to the specified path. This removes all
-//             notifications. Use this to balance both addPathToQueue:notfyingAbout:
-//             as well as addPathToQueue:.
-//
-//     REVISIONS:
-//             2004-03-13      UK      Documented.
-// -----------------------------------------------------------------------------
-
--(void) removePathFromQueue: (NSString*)path
-{
-    NSUInteger  index = 0;
-    int         fd = -1;
-    
-    AT_SYNCHRONIZED( self )
-    {
-        index = [watchedPaths indexOfObject: path];
-        
-        if( index == NSNotFound )
-            return;
-        
-        fd = [[watchedFDs objectAtIndex: index] intValue];
-        
-        [watchedFDs removeObjectAtIndex: index];
-        [watchedPaths removeObjectAtIndex: index];
-    }
-       
-       if( close( fd ) == -1 )
-        NSLog(@"removePathFromQueue: Couldn't close file descriptor (%d)", errno);
-}
-
-
-// -----------------------------------------------------------------------------
-//     removeAllPathsFromQueue:
-//             Stop listening for changes to all paths. This removes all
-//             notifications.
-//
-//  REVISIONS:
-//      2004-12-28  UK  Added as suggested by bbum.
-// -----------------------------------------------------------------------------
-
--(void) removeAllPathsFromQueue;
-{
-    AT_SYNCHRONIZED( self )
-    {
-        NSEnumerator *  fdEnumerator = [watchedFDs objectEnumerator];
-        NSNumber     *  anFD;
-        
-        while( (anFD = [fdEnumerator nextObject]) != nil )
-            close( [anFD intValue] );
-
-        [watchedFDs removeAllObjects];
-        [watchedPaths removeAllObjects];
-    }
-}
-
-
-// -----------------------------------------------------------------------------
-//     watcherThread:
-//             This method is called by our NSThread to loop and poll for any file
-//             changes that our kqueue wants to tell us about. This sends separate
-//             notifications for the different kinds of changes that can happen.
-//             All messages are sent via the postNotification:forFile: main bottleneck.
-//
-//             This also calls sharedWorkspace's noteFileSystemChanged.
-//
-//      To terminate this method (and its thread), set keepThreadRunning to NO.
-//
-//     REVISIONS:
-//             2005-08-27      UK      Changed to use keepThreadRunning instead of kqueueFD
-//                                             being -1 as termination criterion, and to close the
-//                                             queue in this thread so the main thread isn't blocked.
-//             2004-11-12      UK      Fixed docs to include termination criterion, added
-//                      timeout to make sure the bugger gets disposed.
-//             2004-03-13      UK      Documented.
-// -----------------------------------------------------------------------------
-
--(void)                watcherThread: (id)sender
-{
-       int                                     n;
-    struct kevent              ev;
-    struct timespec     timeout = { 5, 0 }; // 5 seconds timeout.
-       int                                     theFD = queueFD;        // So we don't have to risk accessing iVars when the thread is terminated.
-    
-    while( keepThreadRunning )
-    {
-               NSAutoreleasePool*  pool = [[NSAutoreleasePool alloc] init];
-               
-               NS_DURING
-                       n = kevent( queueFD, NULL, 0, &ev, 1, &timeout );
-                       if( n > 0 )
-                       {
-                               if( ev.filter == EVFILT_VNODE )
-                               {
-                                       if( ev.fflags )
-                                       {
-                                               NSString*               fpath = [[(NSString *)ev.udata retain] autorelease];    // In case one of the notified folks removes the path.
-                                               //NSLog(@"UKKQueue: Detected file change: %@", fpath);
-                                               [[NSWorkspace sharedWorkspace] noteFileSystemChanged: fpath];
-                                               
-                                               //NSLog(@"ev.flags = %u",ev.fflags);    // DEBUG ONLY!
-                                               
-                                               if( (ev.fflags & NOTE_RENAME) == NOTE_RENAME )
-                                                       [self postNotification: UKFileWatcherRenameNotification forFile: fpath];
-                                               if( (ev.fflags & NOTE_WRITE) == NOTE_WRITE )
-                                                       [self postNotification: UKFileWatcherWriteNotification forFile: fpath];
-                                               if( (ev.fflags & NOTE_DELETE) == NOTE_DELETE )
-                                                       [self postNotification: UKFileWatcherDeleteNotification forFile: fpath];
-                                               if( (ev.fflags & NOTE_ATTRIB) == NOTE_ATTRIB )
-                                                       [self postNotification: UKFileWatcherAttributeChangeNotification forFile: fpath];
-                                               if( (ev.fflags & NOTE_EXTEND) == NOTE_EXTEND )
-                                                       [self postNotification: UKFileWatcherSizeIncreaseNotification forFile: fpath];
-                                               if( (ev.fflags & NOTE_LINK) == NOTE_LINK )
-                                                       [self postNotification: UKFileWatcherLinkCountChangeNotification forFile: fpath];
-                                               if( (ev.fflags & NOTE_REVOKE) == NOTE_REVOKE )
-                                                       [self postNotification: UKFileWatcherAccessRevocationNotification forFile: fpath];
-                                       }
-                               }
-                       }
-               NS_HANDLER
-                       NSLog(@"Error in UKKQueue watcherThread: %@",localException);
-               NS_ENDHANDLER
-               
-               [pool release];
-    }
-    
-       // Close our kqueue's file descriptor:
-       if( close( theFD ) == -1 )
-               NSLog(@"release: Couldn't close main kqueue (%d)", errno);
-       
-    //NSLog(@"exiting kqueue watcher thread.");
-}
-
-
-// -----------------------------------------------------------------------------
-//     postNotification:forFile:
-//             This is the main bottleneck for posting notifications. If you don't want
-//             the notifications to go through NSWorkspace, override this method and
-//             send them elsewhere.
-//
-//     REVISIONS:
-//      2004-02-27  UK  Changed this to send new notification, and the old one
-//                      only to objects that respond to it. The old category on
-//                      NSObject could cause problems with the proxy itself.
-//             2004-10-31      UK      Helloween fun: Make this use a mainThreadProxy and
-//                                             allow sending the notification even if we have a
-//                                             delegate.
-//             2004-03-13      UK      Documented.
-// -----------------------------------------------------------------------------
-
--(void) postNotification: (NSString*)nm forFile: (NSString*)fp
-{
-       if( delegateProxy )
-    {
-        #if UKKQUEUE_BACKWARDS_COMPATIBLE
-        if( ![delegateProxy respondsToSelector: @selector(watcher:receivedNotification:forPath:)] )
-            [delegateProxy kqueue: self receivedNotification: nm forFile: fp];
-        else
-        #endif
-            [delegateProxy watcher: self receivedNotification: nm forPath: fp];
-    }
-       
-       if( !delegateProxy || alwaysNotify )
-       {
-               #if UKKQUEUE_SEND_STUPID_NOTIFICATIONS
-               [[[NSWorkspace sharedWorkspace] notificationCenter] postNotificationName: nm object: fp];
-               #else
-               [[[NSWorkspace sharedWorkspace] notificationCenter] postNotificationName: nm object: self
-                                                                                                                               userInfo: [NSDictionary dictionaryWithObjectsAndKeys: fp, @"path", nil]];
-               #endif
-       }
-}
-
--(id)  delegate
-{
-    return delegate;
-}
-
--(void)        setDelegate: (id)newDelegate
-{
-       id      oldProxy = delegateProxy;
-       delegate = newDelegate;
-       delegateProxy = [delegate copyMainThreadProxy];
-       [oldProxy release];
-}
-
-// -----------------------------------------------------------------------------
-//     Flag to send a notification even if we have a delegate:
-// -----------------------------------------------------------------------------
-
--(BOOL)        alwaysNotify
-{
-       return alwaysNotify;
-}
-
-
--(void)        setAlwaysNotify: (BOOL)n
-{
-       alwaysNotify = n;
-}
-
-
-// -----------------------------------------------------------------------------
-//     description:
-//             This method can be used to help in debugging. It provides the value
-//      used by NSLog & co. when you request to print this object using the
-//      %@ format specifier.
-//
-//     REVISIONS:
-//             2004-11-12      UK      Created.
-// -----------------------------------------------------------------------------
-
--(NSString*)   description
-{
-       return [NSString stringWithFormat: @"%@ { watchedPaths = %@, alwaysNotify = %@ }", NSStringFromClass([self class]), watchedPaths, (alwaysNotify? @"YES" : @"NO") ];
-}
-
-@end
-
-
diff --git a/macosx/UKKQueue/UKMainThreadProxy.h b/macosx/UKKQueue/UKMainThreadProxy.h
deleted file mode 100644 (file)
index 9fd6ff4..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/* =============================================================================
-       FILE:           UKMainThreadProxy.h
-       PROJECT:        UKMainThreadProxy
-    
-    PURPOSE:    Send a message to object theObject to [theObject mainThreadProxy]
-                instead and the message will be received on the main thread by
-                theObject.
-
-    COPYRIGHT:  (c) 2004 M. Uli Kusterer, all rights reserved.
-    
-       AUTHORS:        M. Uli Kusterer - UK
-    
-    LICENSES:   MIT License
-
-       REVISIONS:
-               2006-03-13      UK      Clarified license.
-               2004-10-14      UK      Created.
-   ========================================================================== */
-
-// -----------------------------------------------------------------------------
-//  Headers:
-// -----------------------------------------------------------------------------
-
-#import <Cocoa/Cocoa.h>
-
-
-// -----------------------------------------------------------------------------
-//  Categories:
-// -----------------------------------------------------------------------------
-
-@interface NSObject (UKMainThreadProxy)
-
--(id)  mainThreadProxy;                // You can't init or release this object.
--(id)  copyMainThreadProxy;    // Gives you a retained version.
-
-@end
-
-
-// -----------------------------------------------------------------------------
-//  Classes:
-// -----------------------------------------------------------------------------
-
-/*
-       This object is created as a proxy in a second thread for an existing object.
-       All messages you send to this object will automatically be sent to the other
-       object on the main thread, except NSObject methods like retain/release etc.
-*/
-
-@interface UKMainThreadProxy : NSObject
-{
-       IBOutlet id             target;
-}
-
--(id)  initWithTarget: (id)targ;
-
-@end
diff --git a/macosx/UKKQueue/UKMainThreadProxy.m b/macosx/UKKQueue/UKMainThreadProxy.m
deleted file mode 100644 (file)
index c351798..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/* =============================================================================
-       FILE:           UKMainThreadProxy.h
-       PROJECT:        UKMainThreadProxy
-    
-    PURPOSE:    Send a message to object theObject to [theObject mainThreadProxy]
-                instead and the message will be received on the main thread by
-                theObject.
-
-    COPYRIGHT:  (c) 2004 M. Uli Kusterer, all rights reserved.
-    
-       AUTHORS:        M. Uli Kusterer - UK
-    
-    LICENSES:   MIT Licenseâ
-
-       REVISIONS:
-               2006-03-13      UK      Clarified license.
-               2004-10-14      UK      Created.
-   ========================================================================== */
-
-// -----------------------------------------------------------------------------
-//  Headers:
-// -----------------------------------------------------------------------------
-
-#import "UKMainThreadProxy.h"
-
-
-@implementation UKMainThreadProxy
-
--(id)  initWithTarget: (id)targ
-{
-       self = [super init];
-       if( self )
-               target = targ;
-       
-       return self;
-}
-
-
-// -----------------------------------------------------------------------------
-//     Introspection overrides:
-// -----------------------------------------------------------------------------
-
--(BOOL)        respondsToSelector: (SEL)itemAction
-{
-       BOOL    does = [super respondsToSelector: itemAction];
-       
-       return( does || [target respondsToSelector: itemAction] );
-}
-
-
--(id)  performSelector: (SEL)itemAction
-{
-       BOOL    does = [super respondsToSelector: itemAction];
-       if( does )
-               return [super performSelector: itemAction];
-       
-       if( ![target respondsToSelector: itemAction] )
-               [self doesNotRecognizeSelector: itemAction];
-       
-       [target retain];
-       [target performSelectorOnMainThread: itemAction withObject: nil waitUntilDone: YES];
-       [target release];
-       
-       return nil;
-}
-
-
--(id)  performSelector: (SEL)itemAction withObject: (id)obj
-{
-       BOOL    does = [super respondsToSelector: itemAction];
-       if( does )
-               return [super performSelector: itemAction withObject: obj];
-       
-       if( ![target respondsToSelector: itemAction] )
-               [self doesNotRecognizeSelector: itemAction];
-       
-       [target retain];
-       [obj retain];
-       [target performSelectorOnMainThread: itemAction withObject: obj waitUntilDone: YES];
-       [obj release];
-       [target release];
-       
-       return nil;
-}
-
-
-// -----------------------------------------------------------------------------
-//     Forwarding unknown methods to the target:
-// -----------------------------------------------------------------------------
-
--(NSMethodSignature*)  methodSignatureForSelector: (SEL)itemAction
-{
-       NSMethodSignature*      sig = [super methodSignatureForSelector: itemAction];
-
-       if( sig )
-               return sig;
-       
-       return [target methodSignatureForSelector: itemAction];
-}
-
--(void)        forwardInvocation: (NSInvocation*)invocation
-{
-    SEL itemAction = [invocation selector];
-
-    if( [target respondsToSelector: itemAction] )
-       {
-               [invocation retainArguments];
-               [target retain];
-               [invocation performSelectorOnMainThread: @selector(invokeWithTarget:) withObject: target waitUntilDone: YES];
-               [target release];
-       }
-       else
-        [self doesNotRecognizeSelector: itemAction];
-}
-
-
-// -----------------------------------------------------------------------------
-//     Safety net:
-// -----------------------------------------------------------------------------
-
--(id)  mainThreadProxy     // Just in case someone accidentally sends this message to a main thread proxy.
-{
-       return self;
-}
-
--(id)  copyMainThreadProxy     // Just in case someone accidentally sends this message to a main thread proxy.
-{
-       return [self retain];
-}
-
-@end
-
-
-// -----------------------------------------------------------------------------
-//     Shorthand notation for getting a main thread proxy:
-// -----------------------------------------------------------------------------
-
-@implementation NSObject (UKMainThreadProxy)
-
--(id)  mainThreadProxy
-{
-       return [[[UKMainThreadProxy alloc] initWithTarget: self] autorelease];
-}
-
--(id)  copyMainThreadProxy
-{
-       return [[UKMainThreadProxy alloc] initWithTarget: self];
-}
-
-@end
-
diff --git a/macosx/VDKQueue/README.md b/macosx/VDKQueue/README.md
new file mode 100755 (executable)
index 0000000..bc556a4
--- /dev/null
@@ -0,0 +1,69 @@
+VDKQueue
+=======
+
+A modern, faster, better version of UKKQueue.
+
+<http://incident57.com/codekit>
+
+
+about
+-----
+
+VDKQueue is an Objective-C wrapper around kernel queues (kQueues).
+It allows you to watch a file or folder for changes and be notified when they occur.
+
+VDKQueue is a modern, streamlined and much faster version of UKKQueue, which was originally written in 2003 by Uli Kusterer.
+Objective-C has come a long way in the past nine years and UKKQueue was long in the tooth. VDKQueue is better in several ways:
+
+       -- The number of method calls is vastly reduced.
+       -- Grand Central Dispatch is used in place of Uli's "threadProxy" notifications (much faster)
+       -- Memory footprint is roughly halved, since VDKQueue creates less overhead
+       -- Fewer locks are taken, especially in loops (faster)
+       -- The code is *much* cleaner and simpler!
+       -- There is only one .h and one .m file to include.
+       
+VDKQueue also fixes long-standing bugs in UKKQueue. For example: OS X limits the number of open file descriptors each process
+may have to about 3,000. If UKKQueue fails to open a new file descriptor because it has hit this limit, it will crash. VDKQueue will not.
+       
+       
+       
+performance
+-----------
+
+Adding 1,945 file paths to a UKKQueue instance took, on average, 80ms. 
+Adding those same files to a VDKQueue instance took, on average, 65ms.
+
+VDKQueue processes and pushes out notifications about file changes roughly 50-70% faster than UKKQueue.
+
+All tests conducted on a 2008 MacBook Pro 2.5Ghz with 4GB of RAM running OS 10.7.3 using Xcode and Instruments (time profiler).
+
+       
+
+
+requirements
+------------
+
+VDKQueue requires Mac OS X 10.6+ because it uses Grand Central Dispatch.
+
+VDKQueue does not support garbage collection. If you use garbage collection, you are lazy. Shape up.
+
+VDKQueue does not currently use ARC, although it should be straightforward to convert if you wish. (Don't be the guy that can't manually manage memory, though.)
+
+
+
+
+license
+-------
+
+Created by Bryan D K Jones on 28 March 2012
+Copyright 2013 Bryan D K Jones
+
+Based heavily on UKKQueue, which was created and copyrighted by Uli Kusterer on 21 Dec 2003.
+
+This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+          
+3. This notice may not be removed or altered from any source distribution.
diff --git a/macosx/VDKQueue/VDKQueue.h b/macosx/VDKQueue/VDKQueue.h
new file mode 100755 (executable)
index 0000000..9856a34
--- /dev/null
@@ -0,0 +1,150 @@
+//     VDKQueue.h
+//     Created by Bryan D K Jones on 28 March 2012
+//     Copyright 2013 Bryan D K Jones
+//
+//  Based heavily on UKKQueue, which was created and copyrighted by Uli Kusterer on 21 Dec 2003.
+//
+//     This software is provided 'as-is', without any express or implied
+//     warranty. In no event will the authors be held liable for any damages
+//     arising from the use of this software.
+//     Permission is granted to anyone to use this software for any purpose,
+//     including commercial applications, and to alter it and redistribute it
+//     freely, subject to the following restrictions:
+//        1. The origin of this software must not be misrepresented; you must not
+//        claim that you wrote the original software. If you use this software
+//        in a product, an acknowledgment in the product documentation would be
+//        appreciated but is not required.
+//        2. Altered source versions must be plainly marked as such, and must not be
+//        misrepresented as being the original software.
+//        3. This notice may not be removed or altered from any source
+//        distribution.
+
+//
+//  BASED ON UKKQUEUE:
+//
+//      This is an updated, modernized and streamlined version of the excellent UKKQueue class, which was authored by Uli Kusterer.
+//      UKKQueue was written back in 2003 and there have been many, many improvements to Objective-C since then. VDKQueue uses the 
+//      core of Uli's original class, but makes it faster and more efficient. Method calls are reduced. Grand Central Dispatch is used in place
+//      of Uli's "threadProxy" objects. The memory footprint is roughly halved, as I don't create the overhead that UKKQueue does.
+//
+//      VDKQueue is also simplified. The option to use it as a singleton is removed. You simply alloc/init an instance and add paths you want to
+//      watch. Your objects can be alerted to changes either by notifications or by a delegate method (or both). See below. 
+//
+//      It also fixes several bugs. For one, it won't crash if it can't create a file descriptor to a file you ask it to watch. (By default, an OS X process can only
+//      have about 3,000 file descriptors open at once. If you hit that limit, UKKQueue will crash. VDKQueue will not.)
+//
+
+//
+//  DEPENDENCIES: 
+//      
+//      VDKQueue requires OS 10.6+ because it relies on Grand Central Dispatch.
+//
+
+//
+//  IMPORTANT NOTE ABOUT ATOMIC OPERATIONS
+//
+//      There are two ways of saving a file on OS X: Atomic and Non-Atomic. In a non-atomic operation, a file is saved by directly overwriting it with new data.
+//      In an Atomic save, a temporary file is first written to a different location on disk. When that completes successfully, the original file is deleted and the
+//      temporary one is renamed and moved into place where the original file existed.
+//
+//      This matters a great deal. If you tell VDKQueue to watch file X, then you save file X ATOMICALLY, you'll receive a notification about that event. HOWEVER, you will
+//      NOT receive any additional notifications for file X from then on. This is because the atomic operation has essentially created a new file that replaced the one you
+//      told VDKQueue to watch. (This is not an issue for non-atomic operations.)
+//
+//      To handle this, any time you receive a change notification from VDKQueue, you should call -removePath: followed by -addPath: on the file's path, even if the path
+//      has not changed. This will ensure that if the event that triggered the notification was an atomic operation, VDKQueue will start watching the "new" file that took
+//      the place of the old one.
+//
+//      Other frameworks out there try to work around this issue by immediately attempting to re-open the file descriptor to the path. This is not bulletproof and may fail;
+//      it all depends on the timing of disk I/O. Bottom line: you could not rely on it and might miss future changes to the file path you're supposedly watching. That's why
+//      VDKQueue does not take this approach, but favors the "manual" method of "stop-watching-then-rewatch". 
+//
+
+
+
+#import <Foundation/Foundation.h>
+#include <sys/types.h>
+#include <sys/event.h>
+
+
+//
+//  Logical OR these values into the u_int that you pass in the -addPath:notifyingAbout: method
+//  to specify the types of notifications you're interested in. Pass the default value to receive all of them.
+//
+#define VDKQueueNotifyAboutRename                                      NOTE_RENAME             // Item was renamed.
+#define VDKQueueNotifyAboutWrite                                       NOTE_WRITE              // Item contents changed (also folder contents changed).
+#define VDKQueueNotifyAboutDelete                                      NOTE_DELETE             // item was removed.
+#define VDKQueueNotifyAboutAttributeChange                     NOTE_ATTRIB             // Item attributes changed.
+#define VDKQueueNotifyAboutSizeIncrease                                NOTE_EXTEND             // Item size increased.
+#define VDKQueueNotifyAboutLinkCountChanged                    NOTE_LINK               // Item's link count changed.
+#define VDKQueueNotifyAboutAccessRevocation                    NOTE_REVOKE             // Access to item was revoked.
+
+#define VDKQueueNotifyDefault                                          (VDKQueueNotifyAboutRename | VDKQueueNotifyAboutWrite \
+                                                    | VDKQueueNotifyAboutDelete | VDKQueueNotifyAboutAttributeChange \
+                                                    | VDKQueueNotifyAboutSizeIncrease | VDKQueueNotifyAboutLinkCountChanged \
+                                                    | VDKQueueNotifyAboutAccessRevocation)
+
+//
+//  Notifications that this class sends to the NSWORKSPACE notification center.
+//      Object          =   the instance of VDKQueue that was watching for changes
+//      userInfo.path   =   the file path where the change was observed
+//
+extern NSString * VDKQueueRenameNotification;
+extern NSString * VDKQueueWriteNotification;
+extern NSString * VDKQueueDeleteNotification;
+extern NSString * VDKQueueAttributeChangeNotification;
+extern NSString * VDKQueueSizeIncreaseNotification;
+extern NSString * VDKQueueLinkCountChangeNotification;
+extern NSString * VDKQueueAccessRevocationNotification;
+
+
+
+//
+//  Or, instead of subscribing to notifications, you can specify a delegate and implement this method to respond to kQueue events.
+//  Note the required statement! For speed, this class does not check to make sure the delegate implements this method. (When I say "required" I mean it!)
+//
+@class VDKQueue;
+@protocol VDKQueueDelegate <NSObject>
+@required
+
+-(void) VDKQueue:(VDKQueue *)queue receivedNotification:(NSString*)noteName forPath:(NSString*)fpath;
+
+@end
+
+
+
+
+
+@interface VDKQueue : NSObject
+{
+    id<VDKQueueDelegate>    _delegate;
+    BOOL                    _alwaysPostNotifications;               // By default, notifications are posted only if there is no delegate set. Set this value to YES to have notes posted even when there is a delegate.
+    
+@private
+    int                                                _coreQueueFD;                           // The actual kqueue ID (Unix file descriptor).
+       NSMutableDictionary    *_watchedPathEntries;                    // List of VDKQueuePathEntries. Keys are NSStrings of the path that each VDKQueuePathEntry is for.
+    BOOL                    _keepWatcherThreadRunning;              // Set to NO to cancel the thread that watches _coreQueueFD for kQueue events
+}
+
+
+//
+//  Note: there is no need to ask whether a path is already being watched. Just add it or remove it and this class
+//        will take action only if appropriate. (Add only if we're not already watching it, remove only if we are.)
+//  
+//  Warning: You must pass full, root-relative paths. Do not pass tilde-abbreviated paths or file URLs. 
+//
+- (void) addPath:(NSString *)aPath;
+- (void) addPath:(NSString *)aPath notifyingAbout:(u_int)flags;     // See note above for values to pass in "flags"
+
+- (void) removePath:(NSString *)aPath;
+- (void) removeAllPaths;
+
+
+- (NSUInteger) numberOfWatchedPaths;                                //  Returns the number of paths that this VDKQueue instance is actively watching.
+
+
+
+@property (assign) id<VDKQueueDelegate> delegate;
+@property (assign) BOOL alwaysPostNotifications;
+
+@end
\ No newline at end of file
diff --git a/macosx/VDKQueue/VDKQueue.m b/macosx/VDKQueue/VDKQueue.m
new file mode 100755 (executable)
index 0000000..f1bc83b
--- /dev/null
@@ -0,0 +1,441 @@
+//     VDKQueue.m
+//     Created by Bryan D K Jones on 28 March 2012
+//     Copyright 2013 Bryan D K Jones
+//
+//  Based heavily on UKKQueue, which was created and copyrighted by Uli Kusterer on 21 Dec 2003.
+//
+//     This software is provided 'as-is', without any express or implied
+//     warranty. In no event will the authors be held liable for any damages
+//     arising from the use of this software.
+//     Permission is granted to anyone to use this software for any purpose,
+//     including commercial applications, and to alter it and redistribute it
+//     freely, subject to the following restrictions:
+//        1. The origin of this software must not be misrepresented; you must not
+//        claim that you wrote the original software. If you use this software
+//        in a product, an acknowledgment in the product documentation would be
+//        appreciated but is not required.
+//        2. Altered source versions must be plainly marked as such, and must not be
+//        misrepresented as being the original software.
+//        3. This notice may not be removed or altered from any source
+//        distribution.
+
+#import "VDKQueue.h"
+#import <unistd.h>
+#import <fcntl.h>
+#include <sys/stat.h>
+
+
+
+NSString * VDKQueueRenameNotification = @"VDKQueueFileRenamedNotification";
+NSString * VDKQueueWriteNotification = @"VDKQueueFileWrittenToNotification";
+NSString * VDKQueueDeleteNotification = @"VDKQueueFileDeletedNotification";
+NSString * VDKQueueAttributeChangeNotification = @"VDKQueueFileAttributesChangedNotification";
+NSString * VDKQueueSizeIncreaseNotification = @"VDKQueueFileSizeIncreasedNotification";
+NSString * VDKQueueLinkCountChangeNotification = @"VDKQueueLinkCountChangedNotification";
+NSString * VDKQueueAccessRevocationNotification = @"VDKQueueAccessWasRevokedNotification";
+
+
+
+#pragma mark -
+#pragma mark VDKQueuePathEntry
+#pragma mark -
+#pragma ------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+//  This is a simple model class used to hold info about each path we watch.
+@interface VDKQueuePathEntry : NSObject
+{
+       NSString*               _path;
+       int                             _watchedFD;
+       u_int                   _subscriptionFlags;
+}
+
+- (id) initWithPath:(NSString*)inPath andSubscriptionFlags:(u_int)flags;
+
+@property (atomic, copy) NSString *path;
+@property (atomic, assign) int watchedFD;
+@property (atomic, assign) u_int subscriptionFlags;
+
+@end
+
+@implementation VDKQueuePathEntry
+@synthesize path = _path, watchedFD = _watchedFD, subscriptionFlags = _subscriptionFlags;
+
+
+- (id) initWithPath:(NSString*)inPath andSubscriptionFlags:(u_int)flags;
+{
+    self = [super init];
+       if (self)
+       {
+               _path = [inPath copy];
+               _watchedFD = open([_path fileSystemRepresentation], O_EVTONLY, 0);
+               if (_watchedFD < 0)
+               {
+                       [self autorelease];
+                       return nil;
+               }
+               _subscriptionFlags = flags;
+       }
+       return self;
+}
+
+-(void)        dealloc
+{
+       [_path release];
+       _path = nil;
+    
+       if (_watchedFD >= 0) close(_watchedFD);
+       _watchedFD = -1;
+       
+       [super dealloc];
+}
+
+@end
+
+
+
+
+
+
+
+
+
+
+
+#pragma mark -
+#pragma mark VDKQueue
+#pragma mark -
+#pragma ------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+@interface VDKQueue ()
+- (void) watcherThread:(id)sender;
+@end
+
+
+
+@implementation VDKQueue
+@synthesize delegate = _delegate, alwaysPostNotifications = _alwaysPostNotifications;
+
+
+
+#pragma mark -
+#pragma mark INIT/DEALLOC
+
+- (id) init
+{
+       self = [super init];
+       if (self)
+       {
+               _coreQueueFD = kqueue();
+               if (_coreQueueFD == -1)
+               {
+                       [self autorelease];
+                       return nil;
+               }
+               
+        _alwaysPostNotifications = NO;
+               _watchedPathEntries = [[NSMutableDictionary alloc] init];
+       }
+       return self;
+}
+
+
+- (void) dealloc
+{
+    // Shut down the thread that's scanning for kQueue events
+    _keepWatcherThreadRunning = NO;
+    
+    // Do this to close all the open file descriptors for files we're watching
+    [self removeAllPaths];
+    
+    [_watchedPathEntries release];
+    _watchedPathEntries = nil;
+    
+    [super dealloc];
+}
+
+
+
+
+
+#pragma mark -
+#pragma mark PRIVATE METHODS
+
+- (VDKQueuePathEntry *)        addPathToQueue:(NSString *)path notifyingAbout:(u_int)flags
+{
+       @synchronized(self)
+       {
+        // Are we already watching this path?
+               VDKQueuePathEntry *pathEntry = [_watchedPathEntries objectForKey:path];
+               
+        if (pathEntry)
+               {
+            // All flags already set?
+                       if(([pathEntry subscriptionFlags] & flags) == flags) 
+            {
+                               return [[pathEntry retain] autorelease]; 
+            }
+                       
+                       flags |= [pathEntry subscriptionFlags];
+               }
+               
+               struct timespec         nullts = { 0, 0 };
+               struct kevent           ev;
+               
+               if (!pathEntry)
+        {
+            pathEntry = [[[VDKQueuePathEntry alloc] initWithPath:path andSubscriptionFlags:flags] autorelease];
+        }
+        
+               if (pathEntry)
+               {
+                       EV_SET(&ev, [pathEntry watchedFD], EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, flags, 0, pathEntry);
+                       
+                       [pathEntry setSubscriptionFlags:flags];
+            
+            [_watchedPathEntries setObject:pathEntry forKey:path];
+            kevent(_coreQueueFD, &ev, 1, NULL, 0, &nullts);
+            
+                       // Start the thread that fetches and processes our events if it's not already running.
+                       if(!_keepWatcherThreadRunning)
+                       {
+                               _keepWatcherThreadRunning = YES;
+                               [NSThread detachNewThreadSelector:@selector(watcherThread:) toTarget:self withObject:nil];
+                       }
+        }
+        
+        return [[pathEntry retain] autorelease];
+    }
+    
+    return nil;
+}
+
+
+//
+//  WARNING: This thread has no active autorelease pool, so if you make changes, you must manually manage 
+//           memory without relying on autorelease. Otherwise, you will leak!
+//
+- (void) watcherThread:(id)sender
+{
+    int                                        n;
+    struct kevent              ev;
+    struct timespec     timeout = { 1, 0 };     // 1 second timeout. Should be longer, but we need this thread to exit when a kqueue is dealloced, so 1 second timeout is quite a while to wait.
+       int                                     theFD = _coreQueueFD;   // So we don't have to risk accessing iVars when the thread is terminated.
+    
+    NSMutableArray      *notesToPost = [[NSMutableArray alloc] initWithCapacity:5];
+    
+#if DEBUG_LOG_THREAD_LIFETIME
+       NSLog(@"watcherThread started.");
+#endif
+       
+    while(_keepWatcherThreadRunning)
+    {
+        @try 
+        {
+            n = kevent(theFD, NULL, 0, &ev, 1, &timeout);
+            if (n > 0)
+            {
+                //NSLog( @"KEVENT returned %d", n );
+                if (ev.filter == EVFILT_VNODE)
+                {
+                    //NSLog( @"KEVENT filter is EVFILT_VNODE" );
+                    if (ev.fflags)
+                    {
+                        //NSLog( @"KEVENT flags are set" );
+                        
+                        //
+                        //  Note: VDKQueue gets tested by thousands of CodeKit users who each watch several thousand files at once.
+                        //        I was receiving about 3 EXC_BAD_ACCESS (SIGSEGV) crash reports a month that listed the 'path' objc_msgSend
+                        //        as the culprit. That suggests the KEVENT is being sent back to us with a udata value that is NOT what we assigned
+                        //        to the queue, though I don't know why and I don't know why it's intermittent. Regardless, I've added an extra
+                        //        check here to try to eliminate this (infrequent) problem. In theory, a KEVENT that does not have a VDKQueuePathEntry
+                        //        object attached as the udata parameter is not an event we registered for, so we should not be "missing" any events. In theory.
+                        //
+                        id pe = ev.udata;
+                        if (pe && [pe respondsToSelector:@selector(path)])
+                        {
+                            NSString *fpath = [((VDKQueuePathEntry *)pe).path retain];         // Need to retain so it does not disappear while the block at the bottom is waiting to run on the main thread. Released in that block.
+                            if (!fpath) continue;
+                            
+                            [[NSWorkspace sharedWorkspace] noteFileSystemChanged:fpath];
+                            
+                            // Clear any old notifications
+                            [notesToPost removeAllObjects];
+                            
+                            // Figure out which notifications we need to issue
+                            if ((ev.fflags & NOTE_RENAME) == NOTE_RENAME)
+                            {
+                                [notesToPost addObject:VDKQueueRenameNotification];
+                            }
+                            if ((ev.fflags & NOTE_WRITE) == NOTE_WRITE)
+                            {
+                                [notesToPost addObject:VDKQueueWriteNotification];
+                            }
+                            if ((ev.fflags & NOTE_DELETE) == NOTE_DELETE)
+                            {
+                                [notesToPost addObject:VDKQueueDeleteNotification];
+                            }
+                            if ((ev.fflags & NOTE_ATTRIB) == NOTE_ATTRIB)
+                            {
+                                [notesToPost addObject:VDKQueueAttributeChangeNotification];
+                            }
+                            if ((ev.fflags & NOTE_EXTEND) == NOTE_EXTEND)
+                            {
+                                [notesToPost addObject:VDKQueueSizeIncreaseNotification];
+                            }
+                            if ((ev.fflags & NOTE_LINK) == NOTE_LINK)
+                            {
+                                [notesToPost addObject:VDKQueueLinkCountChangeNotification];
+                            }
+                            if ((ev.fflags & NOTE_REVOKE) == NOTE_REVOKE)
+                            {
+                                [notesToPost addObject:VDKQueueAccessRevocationNotification];
+                            }
+                            
+                            
+                            NSArray *notes = [[NSArray alloc] initWithArray:notesToPost];   // notesToPost will be changed in the next loop iteration, which will likely occur before the block below runs.
+                            
+                            
+                            // Post the notifications (or call the delegate method) on the main thread.
+                            dispatch_async(dispatch_get_main_queue(),
+                                           ^{
+                                               for (NSString *note in notes)
+                                               {
+                                                   [_delegate VDKQueue:self receivedNotification:note forPath:fpath];
+                                                   
+                                                   if (!_delegate || _alwaysPostNotifications)
+                                                   {
+                                                       NSDictionary *userInfoDict = [[NSDictionary alloc] initWithObjectsAndKeys:fpath, @"path", nil];
+                                                       [[[NSWorkspace sharedWorkspace] notificationCenter] postNotificationName:note object:self userInfo:userInfoDict];
+                                                       [userInfoDict release];
+                                                   }
+                                               }
+                                               
+                                               [fpath release];
+                                               [notes release];
+                                           });
+                        }
+                    }
+                }
+            }
+        }
+        
+        @catch (NSException *localException) 
+        {
+            NSLog(@"Error in VDKQueue watcherThread: %@", localException);
+        }
+    }
+    
+       // Close our kqueue's file descriptor
+       if(close(theFD) == -1) {
+       NSLog(@"VDKQueue watcherThread: Couldn't close main kqueue (%d)", errno); 
+    }
+    
+    [notesToPost release];
+    
+#if DEBUG_LOG_THREAD_LIFETIME
+       NSLog(@"watcherThread finished.");
+#endif
+
+}
+
+
+
+
+
+
+#pragma mark -
+#pragma mark PUBLIC METHODS
+#pragma -----------------------------------------------------------------------------------------------------------------------------------------------------
+
+
+- (void) addPath:(NSString *)aPath
+{
+    if (!aPath) return;
+    [aPath retain];
+    
+    @synchronized(self)
+    {
+        VDKQueuePathEntry *entry = [_watchedPathEntries objectForKey:aPath];
+        
+        // Only add this path if we don't already have it.
+        if (!entry)
+        {
+            entry = [self addPathToQueue:aPath notifyingAbout:VDKQueueNotifyDefault];
+            if (!entry) {
+                NSLog(@"VDKQueue tried to add the path %@ to watchedPathEntries, but the VDKQueuePathEntry was nil. \nIt's possible that the host process has hit its max open file descriptors limit.", aPath);
+            }
+        }
+    }
+    
+    [aPath release];
+}
+
+
+- (void) addPath:(NSString *)aPath notifyingAbout:(u_int)flags
+{
+    if (!aPath) return;
+    [aPath retain];
+    
+    @synchronized(self)
+    {
+        VDKQueuePathEntry *entry = [_watchedPathEntries objectForKey:aPath];
+        
+        // Only add this path if we don't already have it.
+        if (!entry)
+        {
+            entry = [self addPathToQueue:aPath notifyingAbout:flags];
+            if (!entry) {
+                NSLog(@"VDKQueue tried to add the path %@ to watchedPathEntries, but the VDKQueuePathEntry was nil. \nIt's possible that the host process has hit its max open file descriptors limit.", aPath);
+            }
+        }
+    }
+    
+    [aPath release];
+}
+
+
+- (void) removePath:(NSString *)aPath
+{
+    if (!aPath) return;
+    [aPath retain];
+    
+    @synchronized(self)
+       {
+               VDKQueuePathEntry *entry = [_watchedPathEntries objectForKey:aPath];
+        
+        // Remove it only if we're watching it.
+        if (entry) {
+            [_watchedPathEntries removeObjectForKey:aPath];
+        }
+       }
+    
+    [aPath release];
+}
+
+
+- (void) removeAllPaths
+{
+    @synchronized(self)
+    {
+        [_watchedPathEntries removeAllObjects];
+    }
+}
+
+
+- (NSUInteger) numberOfWatchedPaths
+{
+    NSUInteger count;
+    
+    @synchronized(self)
+    {
+        count = [_watchedPathEntries count];
+    }
+    
+    return count;
+}
+
+
+
+
+@end
+