From http://www.jwz.org/xscreensaver/xscreensaver-5.27.tar.gz
[xscreensaver] / OSX / Updater.m
index 2e3ccbe1a2acf1b69b53e0bc65a915b36eea8851..1bf29fa2e841d99fb9662125ae83b1c520978671 100644 (file)
   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];
 }