1 /* xscreensaver, Copyright (c) 2006 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
12 /* This implements the substrate of the xscreensaver preferences code:
13 It does this by writing defaults to, and reading values from, the
14 NSUserDefaultsController (and ScreenSaverDefaults/NSUserDefaults)
15 and thereby reading the preferences that may have been edited by
16 the UI (XScreenSaverConfigSheet).
19 #import <ScreenSaver/ScreenSaverDefaults.h>
20 #import "PrefsReader.h"
21 #import "screenhackI.h"
23 @implementation PrefsReader
25 /* Converts an array of "key:value" strings to an NSDictionary.
27 - (NSDictionary *) defaultsToDict: (const char * const *) defs
29 NSDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:20];
31 char *line = strdup (*defs);
34 while (*key == '.' || *key == '*' || *key == ' ' || *key == '\t')
37 while (*val && *val != ':')
39 if (*val != ':') abort();
41 while (*val == ' ' || *val == '\t')
45 while (L > 0 && (val[L-1] == ' ' || val[L-1] == '\t'))
48 // When storing into preferences, look at the default string and
49 // decide whether it's a boolean, int, float, or string, and store
50 // an object of the appropriate type in the prefs.
52 NSString *nskey = [NSString stringWithCString:key
53 encoding:NSUTF8StringEncoding];
58 if (!strcasecmp (val, "true") || !strcasecmp (val, "yes"))
59 nsval = [NSNumber numberWithBool:YES];
60 else if (!strcasecmp (val, "false") || !strcasecmp (val, "no"))
61 nsval = [NSNumber numberWithBool:NO];
62 else if (1 == sscanf (val, " %d %c", &dd, &cc))
63 nsval = [NSNumber numberWithInt:dd];
64 else if (1 == sscanf (val, " %lf %c", &ff, &cc))
65 nsval = [NSNumber numberWithDouble:ff];
67 nsval = [NSString stringWithCString:val encoding:NSUTF8StringEncoding];
69 // NSLog (@"default: \"%@\" = \"%@\" [%@]", nskey, nsval, [nsval class]);
70 [dict setValue:nsval forKey:nskey];
78 /* Initialize the Cocoa preferences database:
79 - sets the default preferences values from the 'defaults' array;
80 - binds 'self' to each preference as an observer;
81 - ensures that nothing is mentioned in 'options' and not in 'defaults';
82 - ensures that nothing is mentioned in 'defaults' and not in 'options'.
84 - (void) registerXrmKeys: (const XrmOptionDescRec *) opts
85 defaults: (const char * const *) defs
87 // Store the contents of 'defaults' into the real preferences database.
88 NSDictionary *defsdict = [self defaultsToDict:defs];
89 [userDefaults registerDefaults:defsdict];
91 userDefaultsController =
92 [[NSUserDefaultsController alloc] initWithDefaults:userDefaults
93 initialValues:defsdict];
95 NSDictionary *optsdict = [NSMutableDictionary dictionaryWithCapacity:20];
97 while (opts[0].option) {
98 //const char *option = opts->option;
99 const char *resource = opts->specifier;
101 while (*resource == '.' || *resource == '*')
103 NSString *nsresource = [NSString stringWithCString:resource
104 encoding:NSUTF8StringEncoding];
106 // make sure there's no resource mentioned in options and not defaults.
107 if (![defsdict objectForKey:nsresource]) {
108 if (! (!strcmp(resource, "font") || // don't warn about these
109 !strcmp(resource, "textLiteral") ||
110 !strcmp(resource, "textFile") ||
111 !strcmp(resource, "textURL") ||
112 !strcmp(resource, "imageDirectory")))
113 NSLog (@"warning: \"%s\" is in options but not defaults", resource);
115 [optsdict setValue:nsresource forKey:nsresource];
120 // make sure there's no resource mentioned in defaults and not options.
121 NSEnumerator *enumerator = [defsdict keyEnumerator];
123 while ((key = [enumerator nextObject])) {
125 if (! [optsdict objectForKey:key])
126 if (! ([key isEqualToString:@"foreground"] || // don't warn about these
127 [key isEqualToString:@"background"] ||
128 [key isEqualToString:@"Background"] ||
129 [key isEqualToString:@"geometry"] ||
130 [key isEqualToString:@"font"] ||
131 [key isEqualToString:@"dontClearRoot"] ||
134 [key isEqualToString:@"fpsSolid"] ||
135 [key isEqualToString:@"fpsTop"] ||
136 [key isEqualToString:@"titleFont"] ||
138 // analogtv.c settings
139 [key isEqualToString:@"TVBrightness"] ||
140 [key isEqualToString:@"TVColor"] ||
141 [key isEqualToString:@"TVContrast"] ||
142 [key isEqualToString:@"TVTint"]
144 NSLog (@"warning: \"%@\" is in defaults but not options", key);
150 - (NSUserDefaultsController *) userDefaultsController
152 NSAssert(userDefaultsController, @"userDefaultsController uninitialized");
153 return userDefaultsController;
157 - (NSObject *) getObjectResource: (const char *) name
160 NSString *key = [NSString stringWithCString:name
161 encoding:NSUTF8StringEncoding];
162 NSObject *obj = [userDefaults objectForKey:key];
166 // If key is "foo.bar.baz", check "foo.bar.baz", "bar.baz", and "baz".
168 const char *dot = strchr (name, '.');
177 - (char *) getStringResource: (const char *) name
179 NSObject *o = [self getObjectResource:name];
180 //NSLog(@"%s = %@",name,o);
182 if (! (!strcmp(name, "eraseMode") || // erase.c
183 // xlockmore.c reads all of these whether used or not...
184 !strcmp(name, "right3d") ||
185 !strcmp(name, "left3d") ||
186 !strcmp(name, "both3d") ||
187 !strcmp(name, "none3d") ||
188 !strcmp(name, "font") ||
189 !strcmp(name, "labelFont") || // grabclient.c
190 !strcmp(name, "titleFont") ||
191 !strcmp(name, "background")
193 NSLog(@"warning: no preference \"%s\" [string]", name);
197 if (! [o isKindOfClass:[NSString class]]) {
198 NSAssert2(0, @"%s = \"%@\" but should have been an NSString", name, o);
202 if (! [o isKindOfClass:[NSString class]]) {
203 NSLog(@"asked for %s as a string, but it is a %@", name, [o class]);
204 o = [(NSNumber *) o stringValue];
208 NSString *os = (NSString *) o;
209 const char *result = [os cStringUsingEncoding:NSUTF8StringEncoding];
210 return strdup (result);
214 - (double) getFloatResource: (const char *) name
216 NSObject *o = [self getObjectResource:name];
218 // xlockmore.c reads all of these whether used or not...
219 if (! (!strcmp(name, "cycles") ||
220 !strcmp(name, "size") ||
221 !strcmp(name, "use3d") ||
222 !strcmp(name, "delta3d") ||
223 !strcmp(name, "wireframe") ||
224 !strcmp(name, "showFPS") ||
225 !strcmp(name, "fpsSolid") ||
226 !strcmp(name, "fpsTop") ||
227 !strcmp(name, "mono") ||
228 !strcmp(name, "count") ||
229 !strcmp(name, "ncolors") ||
230 !strcmp(name, "eraseSeconds") // erase.c
232 NSLog(@"warning: no preference \"%s\" [float]", name);
235 if ([o isKindOfClass:[NSString class]]) {
236 return [(NSString *) o doubleValue];
237 } else if ([o isKindOfClass:[NSNumber class]]) {
238 return [(NSNumber *) o doubleValue];
240 NSAssert2(0, @"%s = \"%@\" but should have been an NSNumber", name, o);
246 - (int) getIntegerResource: (const char *) name
248 return (int) [self getFloatResource:name];
252 - (BOOL) getBooleanResource: (const char *) name
254 int n = [self getIntegerResource:name];
255 if (n == 0) return NO;
256 else if (n == 1) return YES;
258 NSAssert2(0, @"%s = %d but should have been 0 or 1", name, n);
264 - (id) initWithName: (NSString *) name
265 xrmKeys: (const XrmOptionDescRec *) opts
266 defaults: (const char * const *) defs
269 if (!self) return nil;
271 userDefaults = [ScreenSaverDefaults defaultsForModuleWithName:name];
273 [self registerXrmKeys:opts defaults:defs];
279 [userDefaultsController release];