From http://www.jwz.org/xscreensaver/xscreensaver-5.27.tar.gz
[xscreensaver] / OSX / PrefsReader.m
index cf1698ece73a964928f56bc7510617fa2f89bf6b..97c227d6669485aa9290cf696221be7815c12357 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;
 
     [[NSUserDefaultsController alloc] initWithDefaults:globalDefaults
                                       initialValues:defsdict];
 # else  // USE_IPHONE
-  userDefaultsController   = userDefaults;
-  globalDefaultsController = userDefaults;
+  userDefaultsController   = [userDefaults retain];
+  globalDefaultsController = [userDefaults retain];
 # endif // USE_IPHONE
 
   NSDictionary *optsdict = [NSMutableDictionary dictionaryWithCapacity:20];
 
 # 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];
 }