diff --git a/.gitignore b/.gitignore index a11a18d..ca7120f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ build *.xcodeproj/*.pbxuser *.xcodeproj/*.mode1v* +*.xcodeproj/project.xcworkspace/xcuserdata/*.xcuserdatad +*.xcodeproj/xcuserdata/*.xcuserdatad src/pngcrush-generated.c +.DS_Store + diff --git a/.gitmodules b/.gitmodules index 09acdbe..5ca60d1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "pngcrush"] path = pngcrush url = git://pmt.git.sourceforge.net/gitroot/pmt/pmt +[submodule "SCEvents"] + path = SCEvents + url = git://github.com/mz2/SCEvents.git diff --git a/SCEvents b/SCEvents new file mode 160000 index 0000000..ce3efb6 --- /dev/null +++ b/SCEvents @@ -0,0 +1 @@ +Subproject commit ce3efb68f12a9d367a19732b85ca02c9cb4d9423 diff --git a/scrup.xcodeproj/project.pbxproj b/scrup.xcodeproj/project.pbxproj index 3d53ce5..c77a1d4 100644 --- a/scrup.xcodeproj/project.pbxproj +++ b/scrup.xcodeproj/project.pbxproj @@ -22,6 +22,10 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 14F5B9CA15AD484400368031 /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = 14F5B9C215AD484400368031 /* .gitignore */; }; + 14F5B9CB15AD484400368031 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 14F5B9C315AD484400368031 /* README.md */; }; + 14F5B9CC15AD484400368031 /* SCEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F5B9C615AD484400368031 /* SCEvent.m */; }; + 14F5B9CD15AD484400368031 /* SCEvents.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F5B9C915AD484400368031 /* SCEvents.m */; }; 3A1504AE10618DDF00263983 /* DPAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A1504AD10618DDF00263983 /* DPAppDelegate.m */; }; 3A199BA810BAF1E300E1F99C /* status-item-paused.png in Resources */ = {isa = PBXBuildFile; fileRef = 3A199BA710BAF1E300E1F99C /* status-item-paused.png */; }; 3A199BC910BAF3FF00E1F99C /* status-item-selected-paused.png in Resources */ = {isa = PBXBuildFile; fileRef = 3A199BC810BAF3FF00E1F99C /* status-item-selected-paused.png */; }; @@ -152,6 +156,14 @@ /* Begin PBXFileReference section */ 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; + 14F5B9C215AD484400368031 /* .gitignore */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = .gitignore; sourceTree = ""; }; + 14F5B9C315AD484400368031 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.md; sourceTree = ""; }; + 14F5B9C415AD484400368031 /* SCConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCConstants.h; sourceTree = ""; }; + 14F5B9C515AD484400368031 /* SCEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCEvent.h; sourceTree = ""; }; + 14F5B9C615AD484400368031 /* SCEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCEvent.m; sourceTree = ""; }; + 14F5B9C715AD484400368031 /* SCEventListenerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCEventListenerProtocol.h; sourceTree = ""; }; + 14F5B9C815AD484400368031 /* SCEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCEvents.h; sourceTree = ""; }; + 14F5B9C915AD484400368031 /* SCEvents.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCEvents.m; sourceTree = ""; }; 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = src/main.m; sourceTree = ""; }; 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; @@ -310,6 +322,21 @@ name = "Other Frameworks"; sourceTree = ""; }; + 14F5B9C115AD484400368031 /* SCEvents */ = { + isa = PBXGroup; + children = ( + 14F5B9C215AD484400368031 /* .gitignore */, + 14F5B9C315AD484400368031 /* README.md */, + 14F5B9C415AD484400368031 /* SCConstants.h */, + 14F5B9C515AD484400368031 /* SCEvent.h */, + 14F5B9C615AD484400368031 /* SCEvent.m */, + 14F5B9C715AD484400368031 /* SCEventListenerProtocol.h */, + 14F5B9C815AD484400368031 /* SCEvents.h */, + 14F5B9C915AD484400368031 /* SCEvents.m */, + ); + path = SCEvents; + sourceTree = ""; + }; 19C28FACFE9D520D11CA2CBB /* Products */ = { isa = PBXGroup; children = ( @@ -419,6 +446,7 @@ 3A7AD03710669AF800AF4940 /* 3rd party */ = { isa = PBXGroup; children = ( + 14F5B9C115AD484400368031 /* SCEvents */, 3AE55FA610BC9566006A7F94 /* pngcrush */, 3A1C74FC10BB0F4500BC6030 /* PFMoveApplication.h */, 3A1C74FD10BB0F4500BC6030 /* PFMoveApplication.m */, @@ -608,6 +636,8 @@ 3ADC9E7410FFF35F00EDEE39 /* BottomTB_line.png in Resources */, 3ADC9E7510FFF35F00EDEE39 /* BottomTB_rectangle.png in Resources */, 3ADC9EB310FFFE2D00EDEE39 /* BottomTB_inspector.png in Resources */, + 14F5B9CA15AD484400368031 /* .gitignore in Resources */, + 14F5B9CB15AD484400368031 /* README.md in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -701,6 +731,8 @@ 3A90685910FE9D29009B6B2F /* DPPreprocessingWindowController.m in Sources */, 3ADC9B3210FF537F00EDEE39 /* DPCheckerView.m in Sources */, 3ADC9F1B1100AC9300EDEE39 /* DPAttachedWindow.m in Sources */, + 14F5B9CC15AD484400368031 /* SCEvent.m in Sources */, + 14F5B9CD15AD484400368031 /* SCEvents.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -794,7 +826,7 @@ INSTALL_PATH = /usr/local/lib; PREBINDING = NO; PRODUCT_NAME = pngcrush; - SDKROOT = macosx10.6; + SDKROOT = macosx10.7; VALID_ARCHS = "i386 ppc ppc64 ppc7400 ppc970 x86_64"; }; name = Debug; @@ -822,7 +854,7 @@ INSTALL_PATH = /usr/local/lib; PREBINDING = NO; PRODUCT_NAME = pngcrush; - SDKROOT = macosx10.6; + SDKROOT = macosx10.7; VALID_ARCHS = "i386 ppc ppc64 ppc7400 ppc970 x86_64"; ZERO_LINK = NO; }; @@ -852,8 +884,9 @@ GCC_WARN_UNUSED_VALUE = YES; INFOPLIST_FILE = resources/Info.plist; INSTALL_PATH = "$(HOME)/Applications"; + MACOSX_DEPLOYMENT_TARGET = 10.6; PRODUCT_NAME = Scrup; - SDKROOT = macosx10.6; + SDKROOT = macosx10.7; }; name = Debug; }; @@ -880,8 +913,9 @@ GCC_WARN_UNUSED_VALUE = YES; INFOPLIST_FILE = resources/Info.plist; INSTALL_PATH = "$(HOME)/Applications"; + MACOSX_DEPLOYMENT_TARGET = 10.6; PRODUCT_NAME = Scrup; - SDKROOT = macosx10.6; + SDKROOT = macosx10.7; }; name = Release; }; diff --git a/scrup.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/scrup.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..19f6470 --- /dev/null +++ b/scrup.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/src/DPAppDelegate.h b/src/DPAppDelegate.h index 253d6d1..621fdab 100644 --- a/src/DPAppDelegate.h +++ b/src/DPAppDelegate.h @@ -1,9 +1,10 @@ #import "Sparkle/SUUpdater.h" +#import "SCEvents.h" #import "HTTPPOSTOperation.h" #import "DPAttachedWindow.h" #import "DPPreprocessingWindowController.h" -@interface DPAppDelegate : NSObject { +@interface DPAppDelegate : NSObject { NSUserDefaults *defaults; NSStatusItem *statusItem; IBOutlet NSWindow *mainWindow; @@ -56,6 +57,8 @@ IBOutlet NSView *preprocessingUIView; IBOutlet DPPreprocessingWindowController *preprocessingWindowController; NSMutableArray *preprocessingUIBlockQueue; + + SCEvents *eventManager; } // UI bindings diff --git a/src/DPAppDelegate.m b/src/DPAppDelegate.m index 6d55f54..ef88a1c 100644 --- a/src/DPAppDelegate.m +++ b/src/DPAppDelegate.m @@ -62,6 +62,9 @@ - (id)init { preprocessingWindow = nil; preprocessingWindowController = nil; preprocessingUIBlockQueue = [NSMutableArray array]; + eventManager = [[SCEvents alloc] init]; + [eventManager setDelegate:self]; + [eventManager setNotificationLatency:1]; // set boolean properties from user defaults or give them default values #define SETDEFBOOL(_member_, _defval_) \ @@ -105,7 +108,12 @@ - (id)init { && [[NSFileManager defaultManager] fileExistsAtPath:s] ) { - screenshotLocation = s; + // Trim / suffix to be sure we can ignore safely subdirectories in FSEvent + if([s hasSuffix:@"/"] && [s length] > 2) { + screenshotLocation = [s substringToIndex:[s length] - 1]; + } else { + screenshotLocation = s; + } [log info:@"using com.apple.screencapture location => \"%@\"", screenshotLocation]; } // type @@ -294,17 +302,14 @@ - (void)dequeueDisplayOfPreprocessingUI { #if DEBUG -(void)debugPerpetualStateCheck { ASLLogger *tlog; - NSDistributedNotificationCenter *dnc; tlog = [ASLLogger loggerForModule:@"state"]; if (g_debug) { tlog.connection.level = ASLLoggerLevelNone; [tlog addFileHandle:[NSFileHandle fileHandleWithStandardError]]; } - dnc = [NSDistributedNotificationCenter defaultCenter]; while (1) { - [tlog debug:@"DNC: %s", [dnc suspended] ? "suspended" : "active"]; [NSThread sleepForTimeInterval:10]; } } @@ -1011,34 +1016,44 @@ - (IBAction)orderFrontSettingsWindow:(id)sender { [mainWindow makeKeyAndOrderFront:sender]; } --(void)onDirectoryNotification:(NSNotification *)n { - id obj = [n object]; - [log debug:@"received directory notification => %@ ([object class] => %@)", n, obj ? [obj class] : nil]; - // WARNING: Possible problem: "FNObject 469-101" is a string we have found by trial-and-error - // and is far from official or even documented, thus might differ in future OS versions etc. - // But since there are a _lot_ of directory notifications received, we need this op. - if (obj && [obj isKindOfClass:[NSString class]]) { - [self checkForScreenshotsAtPath:screenshotLocation]; - } +- (void)pathWatcher:(SCEvents *)pathWatcher eventOccurred:(SCEvent *)event { + // check flags for kFSEventStreamEventFlagItemCreated (0x100) + int flags = [event eventFlags]; + if (!(flags & 0x100)) + return; + + #if DEBUG + [log debug:@"Received SCEvents directory notification"]; + #endif + NSString *eventPath = [event eventPath]; + if([eventPath hasSuffix:@"/"] && [eventPath length] > 2) + eventPath = [eventPath substringToIndex:[eventPath length] - 1]; + if([eventPath isEqualToString:screenshotLocation]) { + [self checkForScreenshotsAtPath:screenshotLocation]; + } + #if DEBUG + else { + [log debug:@"Event in subfolder, ignoring"]; + } + #endif } - (void)startObservingDesktop { if (isObservingDesktop) return; - [log info:@"starting observation of com.apple.carbon.core.DirectoryNotification"]; - NSDistributedNotificationCenter *dnc = [NSDistributedNotificationCenter defaultCenter]; - // We need to use NSNotificationSuspensionBehaviorDeliverImmediately here because we're - // experiencing a weird suspension bug causing DNC to be suspended seemingly stochastic. - [dnc addObserver:self selector:@selector(onDirectoryNotification:) name:@"com.apple.carbon.core.DirectoryNotification" object:nil suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately]; - isObservingDesktop = YES; + [log info:@"Starting observation of desktop using SCEvents"]; + NSMutableArray *pathsToWatch = [NSMutableArray arrayWithObject:screenshotLocation]; + isObservingDesktop = [eventManager startWatchingPaths:pathsToWatch]; + if(!isObservingDesktop) + [log error:@"Error while starting desktop observer"]; } - (void)stopObservingDesktop { if (!isObservingDesktop) return; - [log info:@"stopping observation of com.apple.carbon.core.DirectoryNotification"]; - NSDistributedNotificationCenter *dnc = [NSDistributedNotificationCenter defaultCenter]; - [dnc removeObserver:self name:@"com.apple.carbon.core.DirectoryNotification" object:nil]; + [log info:@"Stopping observation of desktop using SCEvents"]; + if(![eventManager stopWatchingPaths]) + [log error:@"Error while stopping desktop observer"]; isObservingDesktop = NO; }