1 /* xscreensaver, Copyright (c) 2006-2008 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 is just a test harness, really -- it makes a window with an
13 XScreenSaverGLView in it and a Preferences button, because I don't want
14 to have to debug these programs by installing them as screen savers
18 #import "SaverTester.h"
19 #import "XScreenSaverGLView.h"
21 @implementation SaverTester
23 - (ScreenSaverView *) makeSaverView: (NSString *) module
25 NSString *dir = [[[NSBundle mainBundle] bundlePath]
26 stringByDeletingLastPathComponent];
27 NSString *name = [module stringByAppendingPathExtension:@"saver"];
28 NSString *path = [dir stringByAppendingPathComponent:name];
29 NSBundle *bundleToLoad = [NSBundle bundleWithPath:path];
30 Class new_class = [bundleToLoad principalClass];
31 NSAssert1 (new_class, @"unable to load \"%@\"", path);
34 rect.origin.x = rect.origin.y = 0;
35 rect.size.width = 320;
36 rect.size.height = 240;
38 id instance = [[new_class alloc] initWithFrame:rect isPreview:YES];
39 NSAssert1 (instance, @"unable to instantiate %@", new_class);
40 return (ScreenSaverView *) instance;
44 static ScreenSaverView *
45 find_saverView_child (NSView *v)
47 NSArray *kids = [v subviews];
48 int nkids = [kids count];
50 for (i = 0; i < nkids; i++) {
51 NSObject *kid = [kids objectAtIndex:i];
52 if ([kid isKindOfClass:[ScreenSaverView class]]) {
53 return (ScreenSaverView *) kid;
55 ScreenSaverView *sv = find_saverView_child ((NSView *) kid);
63 static ScreenSaverView *
64 find_saverView (NSView *v)
67 NSView *p = [v superview];
71 return find_saverView_child (v);
75 - (void) openPreferences: (NSObject *) button
77 ScreenSaverView *sv = find_saverView ((NSView *) button);
78 NSAssert (sv, @"no saver view");
79 NSWindow *prefs = [sv configureSheet];
81 [NSApp beginSheet:prefs
82 modalForWindow:[sv window]
84 didEndSelector:@selector(preferencesClosed:returnCode:contextInfo:)
86 int code = [NSApp runModalForWindow:prefs];
88 /* Restart the animation if the "OK" button was hit, but not if "Cancel".
89 We have to restart *both* animations, because the xlockmore-style
90 ones will blow up if one re-inits but the other doesn't.
92 if (code != NSCancelButton) {
98 - (void) preferencesClosed: (NSWindow *) sheet
99 returnCode: (int) returnCode
100 contextInfo: (void *) contextInfo
102 [NSApp stopModalWithCode:returnCode];
106 - (void)selectedSaverDidChange:(NSDictionary *)change
108 NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
109 NSString *module = [prefs stringForKey:@"selectedSaverName"];
112 if (! module) return;
114 for (i = 0; i < [windows count]; i++) {
115 NSView *cv = [[windows objectAtIndex:i] contentView];
116 ScreenSaverView *old_view = find_saverView (cv);
117 NSView *sup = [old_view superview];
119 [old_view stopAnimation];
120 [old_view removeFromSuperview];
122 ScreenSaverView *new_view = [self makeSaverView:module];
123 [new_view setFrame: [old_view frame]];
124 [sup addSubview: new_view];
125 [new_view setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
126 [new_view startAnimation];
129 NSUserDefaultsController *ctl =
130 [NSUserDefaultsController sharedUserDefaultsController];
135 - (NSArray *) listSaverBundleNames
137 NSString *dir = [[[NSBundle mainBundle] bundlePath]
138 stringByDeletingLastPathComponent];
139 NSString *longest = 0;
140 NSArray *matches = 0;
141 NSArray *exts = [NSArray arrayWithObjects:@"saver", nil];
142 unsigned int n = [dir completePathIntoString: &longest
144 matchesIntoArray: &matches
147 NSLog (@"no .saver bundles found in \"%@/\"!", dir);
152 NSMutableArray *result = [NSMutableArray arrayWithCapacity: n+1];
153 for (i = 0; i < n; i++)
154 [result addObject: [[[matches objectAtIndex: i] lastPathComponent]
155 stringByDeletingPathExtension]];
160 - (NSPopUpButton *) makeMenu
163 rect.origin.x = rect.origin.y = 0;
164 rect.size.width = 10;
165 rect.size.height = 10;
166 NSPopUpButton *popup = [[NSPopUpButton alloc] initWithFrame:rect
170 for (i = 0; i < [saverNames count]; i++) {
171 NSString *name = [saverNames objectAtIndex:i];
172 [popup addItemWithTitle:name];
173 [[popup itemWithTitle:name] setRepresentedObject:name];
175 NSRect r = [popup frame];
176 if (r.size.width > max_width) max_width = r.size.width;
179 // Bind the menu to preferences, and trigger a callback when an item
182 NSString *key = @"values.selectedSaverName";
183 NSUserDefaultsController *prefs =
184 [NSUserDefaultsController sharedUserDefaultsController];
185 [prefs addObserver:self
188 context:@selector(selectedSaverDidChange:)];
189 [popup bind:@"selectedObject"
193 [prefs setAppliesImmediately:YES];
195 NSRect r = [popup frame];
196 r.size.width = max_width;
202 /* This is called when the "selectedSaverName" pref changes, e.g.,
203 when a menu selection is made.
205 - (void)observeValueForKeyPath:(NSString *)keyPath
207 change:(NSDictionary *)change
208 context:(void *)context
210 SEL dispatchSelector = (SEL)context;
211 if (dispatchSelector != NULL) {
212 [self performSelector:dispatchSelector withObject:change];
214 [super observeValueForKeyPath:keyPath
223 - (NSWindow *) makeWindow
226 static int count = 0;
228 // make a "Preferences" button
230 rect.origin.x = rect.origin.y = 0;
231 rect.size.width = rect.size.height = 10;
232 NSButton *pb = [[NSButton alloc] initWithFrame:rect];
233 [pb setTitle:@"Preferences"];
234 [pb setBezelStyle:NSRoundedBezelStyle];
238 NSRect sv_rect = rect;
239 sv_rect.size.width = 320;
240 sv_rect.size.height = 240;
241 ScreenSaverView *sv = [[ScreenSaverView alloc] // dummy placeholder
242 initWithFrame:sv_rect
245 rect.origin.x = ([sv frame].size.width -
246 [pb frame].size.width) / 2;
247 [pb setFrameOrigin:rect.origin];
252 [pb setAction:@selector(openPreferences:)];
254 // Make a saver selection menu
256 NSPopUpButton *menu = [self makeMenu];
259 [menu setFrameOrigin:rect.origin];
261 // make a box to wrap the saverView
265 rect.origin.y = [pb frame].origin.y + [pb frame].size.height;
266 NSBox *gbox = [[NSBox alloc] initWithFrame:rect];
267 rect.size.width = rect.size.height = 10;
268 [gbox setContentViewMargins:rect.size];
269 [gbox setTitlePosition:NSNoTitle];
270 [gbox addSubview:sv];
273 // make a box to wrap the other two boxes
275 rect.origin.x = rect.origin.y = 0;
276 rect.size.width = [gbox frame].size.width;
277 rect.size.height = [gbox frame].size.height + [gbox frame].origin.y;
278 NSBox *pbox = [[NSBox alloc] initWithFrame:rect];
279 [pbox setTitlePosition:NSNoTitle];
280 [pbox setBorderType:NSNoBorder];
281 [pbox addSubview:gbox];
282 [pbox addSubview:menu];
283 [pbox addSubview:pb];
286 // and make a window to hold that.
288 NSScreen *screen = [NSScreen mainScreen];
290 rect.origin.x = ([screen frame].size.width - rect.size.width) / 2;
291 rect.origin.y = ([screen frame].size.height - rect.size.height) / 2;
293 rect.origin.x += rect.size.width * (count ? 0.55 : -0.55);
295 NSWindow *window = [[NSWindow alloc]
296 initWithContentRect:rect
297 styleMask:(NSTitledWindowMask |
298 NSClosableWindowMask |
299 NSMiniaturizableWindowMask |
300 NSResizableWindowMask)
301 backing:NSBackingStoreBuffered
304 [window setTitle:@"XScreenSaver"];
305 [window setMinSize:[window frameRectForContentRect:rect].size];
307 [[window contentView] addSubview:pbox];
309 [sv setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
310 [pb setAutoresizingMask:NSViewMinXMargin|NSViewMaxXMargin];
311 [menu setAutoresizingMask:NSViewMinXMargin|NSViewMaxXMargin];
312 [gbox setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
313 [pbox setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
316 sprintf (buf, "SaverDebugWindow%d", count);
317 [window setFrameAutosaveName:
318 [NSString stringWithCString:buf encoding:NSUTF8StringEncoding]];
319 [window setFrameUsingName:[window frameAutosaveName]];
321 [window makeKeyAndOrderFront:window];
323 [sv startAnimation]; // this is the dummy saver
331 - (void)applicationDidFinishLaunching: (NSNotification *) notif
333 saverNames = [[self listSaverBundleNames] retain];
336 NSMutableArray *w = [[NSMutableArray arrayWithCapacity: n+1] retain];
338 for (i = 0; i < n; i++)
339 [w addObject: [self makeWindow]];
341 [self selectedSaverDidChange:nil];
345 /* When the window closes, exit (even if prefs still open.)
347 - (BOOL) applicationShouldTerminateAfterLastWindowClosed: (NSApplication *) n