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 SUUpdater *updater = [SUUpdater updaterForBundle:
31 [NSBundle bundleForClass:[self class]]];
32 [updater setDelegate:self];
34 [self awaitScreenSaverTermination:updater];
38 // Delegate method that lets us append extra info to the system-info URL.
40 - (NSArray *) feedParametersForUpdater:(SUUpdater *)updater
41 sendingSystemProfile:(BOOL)sending
43 // Get the name of the saver that invoked us, and include that in the
45 NSString *saver = [[[NSProcessInfo
46 processInfo]environment]objectForKey:
47 @"XSCREENSAVER_CLASSPATH"];
48 if (! saver) return nil;
49 NSString *head = @"org.jwz.xscreensaver.";
50 if ([saver hasPrefix:head])
51 saver = [saver substringFromIndex:[head length]];
53 return @[ @{ @"key": @"saver",
55 @"displayKey": @"Current Saver",
56 @"displayValue": saver
62 // Whether ScreenSaverEngine is currently running, meaning screen is blanked.
64 - (BOOL) screenSaverActive
67 NSString *target = @"/ScreenSaverEngine.app";
68 ProcessSerialNumber psn = { kNoProcess, kNoProcess };
69 while (GetNextProcess(&psn) == noErr) {
70 CFDictionaryRef cfdict =
71 ProcessInformationCopyDictionary (&psn,
72 kProcessDictionaryIncludeAllInformationMask);
74 NSDictionary *dict = (NSDictionary *) cfdict;
75 NSString *path = [dict objectForKey:@"BundlePath"];
76 if (path && [path hasSuffix:target])
87 // If the screen saver is not running, then launch the updater.
88 // Otherwise, wait a while and try again. This is because if the
89 // updater tries to pop up a dialog box while the screen saver is
90 // active, everything goes to hell and it never shows up.
92 - (void) awaitScreenSaverTermination:(SUUpdater *)updater
94 if ([self screenSaverActive]) {
95 static float delay = 1;
96 [NSTimer scheduledTimerWithTimeInterval: delay
98 selector:@selector(awaitScreenSaverTerminationTimer:)
101 // slightly exponential back-off
106 // Launch the updater thread.
107 [updater checkForUpdatesInBackground];
109 // Now we need to wait for the Sparkle thread to finish before we can
110 // exit, so just poll waiting for it.
112 [NSTimer scheduledTimerWithTimeInterval:1
114 selector:@selector(exitWhenDone:)
121 - (void) awaitScreenSaverTerminationTimer:(NSTimer *)timer
123 [self awaitScreenSaverTermination:[timer userInfo]];
127 - (void) exitWhenDone:(NSTimer *)timer
129 SUUpdater *updater = [timer userInfo];
130 if (![updater updateInProgress])
131 [[NSApplication sharedApplication] terminate:self];
135 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app