1 /* xscreensaver, Copyright (c) 2013 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
11 * XScreenSaverUpdater.app -- downloads and installs XScreenSaver updates
12 * via Sparkle.framework.
16 * NOTE: This does not work with Sparkle 1.5b6 -- it requires the "HEAD"
17 * version 4-Dec-2013 or later.
21 #import "Sparkle/SUUpdater.h"
23 @implementation XScreenSaverUpdater : NSObject
25 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
27 NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
28 [defs registerDefaults:UPDATER_DEFAULTS];
30 // If it's not time to run the updater, then bail immediately.
31 // I'm not sure why this is necessary, but Sparkle seems to be
32 // checking too often.
34 if (! [self timeToCheck])
35 [[NSApplication sharedApplication] terminate:self];
37 // If the screen saver is not running, then launch the updater now.
38 // Otherwise, wait until the screen saver deactivates, and then do
39 // it. This is because if the updater tries to pop up a dialog box
40 // while the screen saver is active, everything goes to hell and it
41 // never shows up. You'd expect the dialog to just map below the
42 // screen saver window, but no.
44 if (! [self screenSaverActive]) {
47 // Run the updater when the "screensaver.didstop" notification arrives.
48 [[NSDistributedNotificationCenter defaultCenter]
50 selector:@selector(saverStoppedNotification:)
51 name:@"com.apple.screensaver.didstop"
54 // But I'm not sure I trust that, so also poll every couple minutes.
55 timer = [NSTimer scheduledTimerWithTimeInterval: 60 * 2
57 selector:@selector(pollSaverTermination:)
66 NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
67 NSTimeInterval interval = [defs doubleForKey:@SUScheduledCheckIntervalKey];
68 NSDate *last = [defs objectForKey:@SULastCheckTimeKey];
69 if (!interval || !last)
71 NSTimeInterval since = [[NSDate date] timeIntervalSinceDate:last];
72 return (since > interval);
76 // Whether ScreenSaverEngine is currently running, meaning screen is blanked.
77 // There's no easy way to determine this other than scanning the process table.
79 - (BOOL) screenSaverActive
82 NSString *target = @"/ScreenSaverEngine.app";
83 ProcessSerialNumber psn = { kNoProcess, kNoProcess };
84 while (GetNextProcess(&psn) == noErr) {
85 CFDictionaryRef cfdict =
86 ProcessInformationCopyDictionary (&psn,
87 kProcessDictionaryIncludeAllInformationMask);
89 NSDictionary *dict = (NSDictionary *) cfdict;
90 NSString *path = [dict objectForKey:@"BundlePath"];
91 if (path && [path hasSuffix:target])
102 - (void) saverStoppedNotification:(NSNotification *)note
108 - (void) pollSaverTermination:(NSTimer *)t
110 if (! [self screenSaverActive])
122 SUUpdater *updater = [SUUpdater updaterForBundle:
123 [NSBundle bundleForClass:[self class]]];
124 [updater setDelegate:self];
126 // Launch the updater thread.
127 [updater checkForUpdatesInBackground];
129 // Now we need to wait for the Sparkle thread to finish before we can
130 // exit, so just poll waiting for it.
132 [NSTimer scheduledTimerWithTimeInterval:1
134 selector:@selector(pollUpdaterTermination:)
140 // Delegate method that lets us append extra info to the system-info URL.
142 - (NSArray *) feedParametersForUpdater:(SUUpdater *)updater
143 sendingSystemProfile:(BOOL)sending
145 // Get the name of the saver that invoked us, and include that in the
147 NSString *saver = [[[NSProcessInfo processInfo] environment]
148 objectForKey:@"XSCREENSAVER_CLASSPATH"];
149 if (! saver) return @[];
150 NSString *head = @"org.jwz.xscreensaver.";
151 if ([saver hasPrefix:head])
152 saver = [saver substringFromIndex:[head length]];
154 return @[ @{ @"key": @"saver",
156 @"displayKey": @"Current Saver",
157 @"displayValue": saver
163 - (void) pollUpdaterTermination:(NSTimer *)t
165 SUUpdater *updater = [t userInfo];
166 if (![updater updateInProgress])
167 [[NSApplication sharedApplication] terminate:self];
171 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app