From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / OSX / PrefsReader.m
index cf1698ece73a964928f56bc7510617fa2f89bf6b..4044280294aeb2fb07158b1ff2829aab25fce3f5 100644 (file)
@@ -26,6 +26,7 @@
 
 #ifndef USE_IPHONE
 
+#include <objc/runtime.h>
 
 /* GlobalDefaults is an NSUserDefaults implementation that writes into
    the preferences key we provide, instead of whatever the default would
 @end
 
 @implementation GlobalDefaults
-- (id) initWithDomain:(NSString *)_domain
-{
+- (id) initWithDomain:(NSString *)_domain module:(NSString *)_module
+{
+  // Key-Value Observing tries to create an Objective-C class named
+  // NSKVONotifying_GlobalDefaults when the configuration page is shown. But if
+  // this is the second XScreenSaver .saver running in the same process, class
+  // creation fails because that class name was already used by the first
+  // .saver, and it refers to the GlobalDefaults from the other .saver.
+
+  // This gives the class a unique name, sidestepping the above issue.
+
+  // It really just needs to be unique for this .saver and this instance.
+  // Using the pointer to the .saver's mach_header and the full path to the
+  // .saver would be preferable, but this should be good enough.
+  char class_name[128];
+  sprintf(class_name, "GlobalDefaults_%s_%p_%u",
+          strrchr(_module.UTF8String, '.') + 1, self, random());
+  Class c = objc_allocateClassPair([GlobalDefaults class], class_name, 0);
+  if (!c)
+    return nil;
+  objc_registerClassPair(c);
+
   self = [super init];
+  object_setClass(self, c);
   domain = [_domain retain];
   return self;
 }
 
 - (void) dealloc
 {
+  Class c = object_getClass(self);
+
   [domain release];
   [defaults release];
   [super dealloc];
+
+  objc_disposeClassPair(c);
 }
 
 - (void)registerDefaults:(NSDictionary *)dict
     while (*val == ' ' || *val == '\t')
       val++;
 
-    int L = strlen(val);
+    unsigned long L = strlen(val);
     while (L > 0 && (val[L-1] == ' ' || val[L-1] == '\t'))
       val[--L] = 0;
 
                                       initialValues:defsdict];
   globalDefaultsController = 
     [[NSUserDefaultsController alloc] initWithDefaults:globalDefaults
-                                      initialValues:defsdict];
+                                      initialValues:UPDATER_DEFAULTS];
 # else  // USE_IPHONE
-  userDefaultsController   = userDefaults;
-  globalDefaultsController = userDefaults;
+  userDefaultsController   = [userDefaults retain];
+  globalDefaultsController = [userDefaults retain];
 # endif // USE_IPHONE
 
   NSDictionary *optsdict = [NSMutableDictionary dictionaryWithCapacity:20];
 
 - (NSObject *) getObjectResource: (const char *) name
 {
-  // First look in userDefaults, then in globalDefaults.
-  for (int globalp = 0; globalp <= 1; globalp++) {
-    const char *name2 = name;
-    while (1) {
-      NSString *key = [self makeCKey:name2];
-      NSObject *obj = [(globalp ? globalDefaults : userDefaults)
-                        objectForKey:key];
-      if (obj)
-        return obj;
-
-      // If key is "foo.bar.baz", check "foo.bar.baz", "bar.baz", and "baz".
-      //
-      const char *dot = strchr (name2, '.');
-      if (dot && dot[1])
-        name2 = dot + 1;
-      else
-        break;
-    }
+  // Only look in globalDefaults for updater preferences.
+
+  static NSDictionary *updaterDefaults;
+  if (!updaterDefaults) {
+    updaterDefaults = UPDATER_DEFAULTS;
+    [updaterDefaults retain];
+  }
+  
+  NSUserDefaults *defaults =
+    [updaterDefaults objectForKey:[NSString stringWithUTF8String:name]] ?
+    globalDefaults :
+    userDefaults;
+  
+  const char *name2 = name;
+  while (1) {
+    NSString *key = [self makeCKey:name2];
+    NSObject *obj = [defaults objectForKey:key];
+    if (obj)
+      return obj;
+
+    // If key is "foo.bar.baz", check "foo.bar.baz", "bar.baz", and "baz".
+    //
+    const char *dot = strchr (name2, '.');
+    if (dot && dot[1])
+      name2 = dot + 1;
+    else
+      break;
   }
   return NULL;
 }
 
 # ifndef USE_IPHONE
   userDefaults = [ScreenSaverDefaults defaultsForModuleWithName:name];
-  globalDefaults = [[[GlobalDefaults alloc] initWithDomain:@UPDATER_DOMAIN]
-                     retain];
+  globalDefaults = [[GlobalDefaults alloc] initWithDomain:@UPDATER_DOMAIN
+                                                   module:name];
 # else  // USE_IPHONE
   userDefaults = [NSUserDefaults standardUserDefaults];
-  globalDefaults = userDefaults;
+  globalDefaults = [userDefaults retain];
 # endif // USE_IPHONE
 
   // Convert "org.jwz.xscreensaver.NAME" to just "NAME".
   [saver_name release];
   [userDefaultsController release];
   [globalDefaultsController release];
+  [globalDefaults release];
   [super dealloc];
 }