NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
[defs registerDefaults:UPDATER_DEFAULTS];
- SUUpdater *updater = [SUUpdater updaterForBundle:
- [NSBundle bundleForClass:[self class]]];
- [updater setDelegate:self];
+ // If it's not time to run the updater, then bail immediately.
+ // I'm not sure why this is necessary, but Sparkle seems to be
+ // checking too often.
+ //
+ if (! [self timeToCheck])
+ [[NSApplication sharedApplication] terminate:self];
- [self awaitScreenSaverTermination:updater];
+ // If the screen saver is not running, then launch the updater now.
+ // Otherwise, wait until the screen saver deactivates, and then do
+ // it. This is because if the updater tries to pop up a dialog box
+ // while the screen saver is active, everything goes to hell and it
+ // never shows up. You'd expect the dialog to just map below the
+ // screen saver window, but no.
+
+ if (! [self screenSaverActive]) {
+ [self runUpdater];
+ } else {
+ // Run the updater when the "screensaver.didstop" notification arrives.
+ [[NSDistributedNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(saverStoppedNotification:)
+ name:@"com.apple.screensaver.didstop"
+ object:nil];
+
+ // But I'm not sure I trust that, so also poll every couple minutes.
+ timer = [NSTimer scheduledTimerWithTimeInterval: 60 * 2
+ target:self
+ selector:@selector(pollSaverTermination:)
+ userInfo:nil
+ repeats:YES];
+ }
}
-// Delegate method that lets us append extra info to the system-info URL.
-//
-- (NSArray *) feedParametersForUpdater:(SUUpdater *)updater
- sendingSystemProfile:(BOOL)sending
+- (BOOL) timeToCheck
{
- // Get the name of the saver that invoked us, and include that in the
- // system info.
- NSString *saver = [[[NSProcessInfo
- processInfo]environment]objectForKey:
- @"XSCREENSAVER_CLASSPATH"];
- if (! saver) return nil;
- NSString *head = @"org.jwz.xscreensaver.";
- if ([saver hasPrefix:head])
- saver = [saver substringFromIndex:[head length]];
-
- return @[ @{ @"key": @"saver",
- @"value": saver,
- @"displayKey": @"Current Saver",
- @"displayValue": saver
- }
- ];
+ NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
+ NSTimeInterval interval = [defs doubleForKey:@SUScheduledCheckIntervalKey];
+ NSDate *last = [defs objectForKey:@SULastCheckTimeKey];
+ if (!interval || !last)
+ return YES;
+ NSTimeInterval since = [[NSDate date] timeIntervalSinceDate:last];
+ return (since > interval);
}
// Whether ScreenSaverEngine is currently running, meaning screen is blanked.
+// There's no easy way to determine this other than scanning the process table.
//
- (BOOL) screenSaverActive
{
}
-// If the screen saver is not running, then launch the updater.
-// Otherwise, wait a while and try again. This is because if the
-// updater tries to pop up a dialog box while the screen saver is
-// active, everything goes to hell and it never shows up.
-//
-- (void) awaitScreenSaverTermination:(SUUpdater *)updater
+- (void) saverStoppedNotification:(NSNotification *)note
{
- if ([self screenSaverActive]) {
- static float delay = 1;
- [NSTimer scheduledTimerWithTimeInterval: delay
- target:self
- selector:@selector(awaitScreenSaverTerminationTimer:)
- userInfo:updater
- repeats:NO];
- // slightly exponential back-off
- delay *= 1.3;
- if (delay > 120)
- delay = 120;
- } else {
- // Launch the updater thread.
- [updater checkForUpdatesInBackground];
-
- // Now we need to wait for the Sparkle thread to finish before we can
- // exit, so just poll waiting for it.
- //
- [NSTimer scheduledTimerWithTimeInterval:1
- target:self
- selector:@selector(exitWhenDone:)
- userInfo:updater
- repeats:YES];
+ [self runUpdater];
+}
+
+
+- (void) pollSaverTermination:(NSTimer *)t
+{
+ if (! [self screenSaverActive])
+ [self runUpdater];
+}
+
+
+- (void) runUpdater
+{
+ if (timer) {
+ [timer invalidate];
+ timer = nil;
}
+
+ SUUpdater *updater = [SUUpdater updaterForBundle:
+ [NSBundle bundleForClass:[self class]]];
+ [updater setDelegate:self];
+
+ // Launch the updater thread.
+ [updater checkForUpdatesInBackground];
+
+ // Now we need to wait for the Sparkle thread to finish before we can
+ // exit, so just poll waiting for it.
+ //
+ [NSTimer scheduledTimerWithTimeInterval:1
+ target:self
+ selector:@selector(pollUpdaterTermination:)
+ userInfo:updater
+ repeats:YES];
}
-- (void) awaitScreenSaverTerminationTimer:(NSTimer *)timer
+// Delegate method that lets us append extra info to the system-info URL.
+//
+- (NSArray *) feedParametersForUpdater:(SUUpdater *)updater
+ sendingSystemProfile:(BOOL)sending
{
- [self awaitScreenSaverTermination:[timer userInfo]];
+ // Get the name of the saver that invoked us, and include that in the
+ // system info.
+ NSString *saver = [[[NSProcessInfo processInfo] environment]
+ objectForKey:@"XSCREENSAVER_CLASSPATH"];
+ if (! saver) return @[];
+ NSString *head = @"org.jwz.xscreensaver.";
+ if ([saver hasPrefix:head])
+ saver = [saver substringFromIndex:[head length]];
+
+ return @[ @{ @"key": @"saver",
+ @"value": saver,
+ @"displayKey": @"Current Saver",
+ @"displayValue": saver
+ }
+ ];
}
-- (void) exitWhenDone:(NSTimer *)timer
+- (void) pollUpdaterTermination:(NSTimer *)t
{
- SUUpdater *updater = [timer userInfo];
+ SUUpdater *updater = [t userInfo];
if (![updater updateInProgress])
[[NSApplication sharedApplication] terminate:self];
}