This is SpaceSaver.m in view mode; [Download] [Up]
// ScreenSaver.m // // This class is the brains behind the SpaceSaver login bundle. // // This recast of BackSpace into a ScreenSaver bundle for use when no one // is logged in was done by Christopher_Lane@Med.Stanford.EDU, December 1993. // // The original BackSpace application was done by Sam Streeper of NeXT, // with contributions from Bill Bumgarner, Lennart Lovstrand, Bruce Blumberg, // shou-h@nexus.or.jp and others. Many of the code comments are also Sam's. // // Undocumented 3.3 SpaceSaver.loginbundle details obtained via /bin/otool. // // You may freely copy, distribute, and reuse the code in this example. // NeXT disclaims any warranty of any kind, expressed or implied, as // to its fitness for any particular use. #import "SpaceSaver.h" #import "SpaceView.h" #import "psfuncts.h" #import <sys/dir.h> #import <appkit/appkit.h> #ifdef KLUDGE static NXDefaultsVector MovieShowDefaults = { // keep uninitialized MovieShowView from failing {"Jump", "NO"}, {"SlideFrames", "NO"}, {"SlidePauses", "YES"}, {NULL} }; static NXDefaultsVector MultiViewDefaults = { // keep uninitialized MulitView from failing {"MultiLayout", "0"}, {"MultiViews", "Space"}, {NULL} }; #endif static NXDefaultsVector BackSpaceDefaults = { {"ViewDirectory", "/LocalLibrary/BackSpaceViews"}, {"viewType", "All"}, {"priority", "4"}, {NULL} }; static char *compiledViewNames[] = { "Space" }, *applicationName, *launchDir; #define COMVIEWCOUNT (sizeof(compiledViewNames) / sizeof(*compiledViewNames)) typedef enum {MINIMUMPRIORITY = 0, BACKSPACEPRIORITY = 4, DEFAULTPRIORITY = 16, MAXIMUMPRIORITY = 31} PRIORITY; #define streq(s, t) (strcmp(s, t) == 0) #define MSECPERSEC (1000) #define USECPERMSEC (1000) static id _BSThinker, appDelegate; id BSThinker() { return _BSThinker; } static int eventMask, basePriority; BOOL doesDidLockFocus; #define BACKSPACENAME "BackSpace" #define DEFAULTIMAGENAME "defaultImage" #define BINARYEXTENSION "BackO" #define BUNDLEEXTENSION "BackModule" @implementation SpaceSaver + alloc { static id instance; // probably not a good idea to have more than one of these... if (instance == nil) instance = [super alloc]; return (self = instance); } - didStartScreenSaver { if (screenSaverVal) return self; if (!doingSaver) [self createScreenSaver]; [self setScreenSaver:YES]; #ifndef DEBUG PShidecursor(); [self blackOutAllScreens]; #endif [self setVirtualViewIndex]; [[spaceView fillBoundsWithBlack] display]; [[spaceWindow display] makeKeyAndOrderFront:self]; #ifndef DEBUG (void) [spaceWindow addToEventMask:NX_MOUSEMOVEDMASK]; #endif eventMask = [spaceWindow eventMask]; NXPing(); if (priority != basePriority) cthread_priority(cthread_self(), priority, FALSE); if ([spaceView respondsTo:@selector(enteredScreenSaverMode)]) [spaceView enteredScreenSaverMode]; return self; } - didStopScreenSaver { if (!screenSaverVal) return self; [self setScreenSaver:NO]; if ([spaceView respondsTo:@selector(willExitScreenSaverMode)]) [spaceView willExitScreenSaverMode]; if (basePriority != priority) cthread_priority(cthread_self(), basePriority, FALSE); [spaceWindow orderOut:self]; (void) [spaceWindow setEventMask:eventMask]; // loaded view may have changed event mask on us. #ifndef DEBUG (void) [spaceWindow removeFromEventMask:NX_MOUSEMOVEDMASK]; [self unBlackOutAllScreens]; PSshowcursor(); #endif return self; } - oneStep { // NXEvent dummyEvent; if (!screenSaverVal) return nil; // timed entry misfire [spaceView lockFocus]; { if (doesDidLockFocus) [spaceView didLockFocus]; // do { [spaceView oneStep]; [spaceWindow flushWindow]; NXPing(); // Synchronize postscript for smoother animation // } while (screenSaverVal && ([NXApp peekNextEvent:NX_ALLEVENTS into:&dummyEvent waitFor:0 threshold:NX_BASETHRESHOLD] == NULL)); } [spaceView unlockFocus]; return self; } - createScreenSaver { const char *string; char buffer[MAXPATHLEN]; NXRegisterDefaults(BACKSPACENAME, BackSpaceDefaults); #ifdef KLUDGE (void) sprintf(buffer, "%s/defaultImage.tiff", [[NXBundle bundleForClass:[self class]] directory]); (void) NXSetDefault(BACKSPACENAME, "imageFile", buffer); [self borrowDefaults:BACKSPACENAME]; // if module does NXGetDefaultValue([NXApp appName], ... NXRegisterDefaults(applicationName, MultiViewDefaults); #endif launchDir = NXCopyStringBuffer([[NXBundle mainBundle] directory]); [NXApp getScreens:&screens count:&screenCount]; [self getViewType]; [self getPrioritySetting]; [self getImageFile]; if ((string = NXGetDefaultValue(BACKSPACENAME, "viewType")) != NULL) realViewIndex = [moduleList indexOfName:string]; doingSaver = YES; return self; } - init // do the easy stuff, leave the rest for first time we 'start' { _BSThinker = self; screenSaverVal = doingSaver = NO; backZone = NXCreateZone(vm_page_size, vm_page_size, YES); moduleList = [[ModuleList alloc] init]; basePriority = priority = DEFAULTPRIORITY; realViewIndex = virtualViewIndex = -1; currentInspector = commonImageInspector = nullInspector = nil; appDelegate = [NXApp delegate]; applicationName = (char *) [NXApp appName]; #ifndef DEBUG srandom(time(NULL)); #endif #ifdef KLUDGE (void) fmod(2.0, 3.0); // force 'fmod' to not inline and let SpewView load #endif return self; } - free { [self didStopScreenSaver]; return [super free]; } int readstr(FILE *stream, char *s) // wish I could use scanf but %s won't read unanticipated "xxx yyy" { int c, flag = FALSE; while ((c = getc(stream)) != EOF) { if (c == '"') flag = !flag; else if ((c == ' ' || c == '\n') && !flag) break; *s++ = c; } *s = '\0'; return c; } - borrowDefaults:(const char *) realOwner { // NeXT should have defined (NXDefaultsVector *) NXReadDefaults(const char *owner) FILE *pipe; char string[MAXPATHLEN]; (void) sprintf(string, "dread -o %s", realOwner); if ((pipe = popen(string, "r")) != NULL) { char owner[MAXNAMLEN], name[MAXNAMLEN], value[MAXPATHLEN]; while (readstr(pipe, owner) != EOF && readstr(pipe, name) != EOF && readstr(pipe, value) != EOF) { if (streq(owner, realOwner) && NXGetDefaultValue(applicationName, name) == NULL) { (void) NXSetDefault(applicationName, name, value); // loginwindow & BackSpace overlap! } #ifdef DEBUG else (void) fprintf(stderr, "%s %s %s\n", owner, name, value); #endif } (void) pclose(pipe); } else return nil; return self; } - (NXZone *) backZone { return backZone; } - (ModuleList *) moduleList { return moduleList; } - installSpaceViewIntoWindow { NXRect cvrect; unsigned int i, count; id subviews, contentView = [spaceWindow contentView]; [contentView getBounds:&cvrect]; subviews = [contentView subviews]; // remove old subviews, this is overkill really... for (i = 0, count = [subviews count]; i < count; i++) [[subviews objectAt:i] removeFromSuperview]; (void) [contentView addSubview:spaceView]; [contentView setAutoresizeSubviews:YES]; // don't really need to resize but some views break if you don't! [spaceView setAutosizing:NX_WIDTHSIZABLE | NX_HEIGHTSIZABLE]; [spaceView sizeTo:cvrect.size.width :cvrect.size.height]; return self; } - (int) backingTypeForView:aView { if ([aView respondsTo:@selector(useBufferedWindow)] && [aView useBufferedWindow]) return NX_BUFFERED; return NX_RETAINED; } - createBigWindowIfNecessaryForBacking:(int) backing { id window = nil; #ifndef DEBUG const NXScreen *mainScreen = [NXApp mainScreen]; NXRect r = mainScreen->screenBounds; #else NXRect r = {{0, 0}, {640, 480}}; #endif if ((backing == NX_RETAINED) && !bigUnbufferedWindow) { window = bigUnbufferedWindow = [[Window allocFromZone:backZone] initContent:&r style:NX_TOKENSTYLE backing:NX_NONRETAINED buttonMask:0 defer:NO]; } if ((backing == NX_BUFFERED) && !bigBufferedWindow) { window = bigBufferedWindow = [[Window allocFromZone:backZone] initContent:&r style:NX_TOKENSTYLE backing:NX_BUFFERED buttonMask:0 defer:NO]; [[bigBufferedWindow setOneShot:YES] setDynamicDepthLimit:YES]; // want window depth to match device! } if (window != nil) { [[window useOptimizedDrawing:YES] setBackgroundGray:NX_BLACK]; tweakWindow([window windowNum], SAVERTIER); } return self; } - setScreenSaver:(BOOL) val { screenSaverVal = val; // I don't handle any app* messages but some modules assume I do, if (screenSaverVal) [NXApp setDelegate:self]; else [NXApp setDelegate:appDelegate]; return self; } - getPrioritySetting { struct thread_basic_info info; int value, count = THREAD_BASIC_INFO_COUNT; const char *string = NXGetDefaultValue(BACKSPACENAME, "priority"); if(thread_info(cthread_thread(cthread_self()), THREAD_BASIC_INFO, (thread_info_t) &info, &count) == KERN_SUCCESS) { basePriority = info.base_priority; } if (sscanf(string, "%d", &value) == 1 && value >= MINIMUMPRIORITY && value <= MAXIMUMPRIORITY) priority = value; else priority = BACKSPACEPRIORITY; return self; } BStimeval currentTimeInMs() { struct timeval curTime; gettimeofday(&curTime, NULL); return ((curTime.tv_sec * MSECPERSEC) + (curTime.tv_usec / USECPERMSEC)); } // Additional methods to handle a common image object for views. // Lennart Lovstrad, Rank Xerox EuroPARC, August 1991. - setImageFromName:(const char *) name { if (image != nil) [image free]; image = [[NXImage allocFromZone:backZone] initFromSection:name]; return [self commonImageInit]; } - setImageFromFile:(const char *) filename { if (image != nil) [image free]; image = [[NXImage allocFromZone:backZone] initFromFile:filename]; return [self commonImageInit]; } - commonImageInit { if ([spaceView respondsTo:@selector(setImage:)]) [spaceView setImage:image]; if ([self backingTypeForView:spaceView] != NX_BUFFERED) [[spaceView fillBoundsWithBlack] display]; return self; } - getImageFile { const char *filename; if ((filename = NXGetDefaultValue(BACKSPACENAME, "imageFile")) != NULL) [self setImageFromFile:filename]; else [self setImageFromName:DEFAULTIMAGENAME]; return self; } float frandom() // This should return a float between 0 and 1 { return (((float) (random() & MAXLONG)) / (float) MAXLONG); } float randBetween(float low, float high) { float temp = low; if (low > high) { low = high; high = temp; } return (((high - low) * frandom()) + low); } // float randBetween(float a, float b) { return (MIN(a, b) + (fabs(a - b) * frandom())); } - getViewType // must invoke this before creating window or setting up the views { unsigned int i; [self loadViewsFrom:NXGetDefaultValue(BACKSPACENAME, "ViewDirectory")]; for (i = 0; i < COMVIEWCOUNT; i++) { [moduleList addObject:[[ModuleInfo alloc] initWithView:nil name:compiledViewNames[i] path:NULL]]; } [moduleList sort]; return self; } // this method is the actual view setting mechanism, // guaranteed to get called to set the view - setVirtualViewIndex { if (realViewIndex != -1) virtualViewIndex = realViewIndex; else { #ifndef DEBUG virtualViewIndex = random() % [moduleList count]; #else if (++virtualViewIndex >= [moduleList count]) virtualViewIndex = 0; #endif } while ([self selectScreenSaverViews] == nil || ([spaceView respondsTo:@selector(isBoringScreenSaver)] && [spaceView isBoringScreenSaver])) { if (++virtualViewIndex >= [moduleList count]) virtualViewIndex = 0; } return self; } - selectScreenSaverViews { int myBacking; if ((spaceView = [self backView]) == nil) return nil; myBacking = [self backingTypeForView:spaceView]; [self createBigWindowIfNecessaryForBacking:myBacking]; spaceWindow = (myBacking == NX_BUFFERED) ? bigBufferedWindow : bigUnbufferedWindow; #ifdef DEBUG (void) fprintf(stderr, "Window type = NX_%sBUFFERED\n", (myBacking == NX_BUFFERED) ? "" : "UN"); #endif [self installSpaceViewIntoWindow]; #ifdef KLUDGE if ([spaceView respondsTo:@selector(inspector:)]) currentInspector = [spaceView inspector:self]; else currentInspector = [self nullInspector]; // don't use inspectors, but some modules use to initialize -- sigh #endif if ([spaceView respondsTo:@selector(setImage:)]) [spaceView setImage:image]; if ([spaceView respondsTo:@selector(newWindow)]) [spaceView newWindow]; doesDidLockFocus = [spaceView respondsTo:@selector(didLockFocus)]; return self; } - backView { id theView; ModuleInfo *info = [moduleList objectAt:virtualViewIndex]; if ((theView = [info view]) == nil) { char class[MAXNAMLEN]; #ifdef DEBUG NXRect aFrame = {{0, 0}, {640, 480}}; #else const NXScreen *mainScreen = [NXApp mainScreen]; NXRect aFrame = mainScreen->screenBounds; #endif (void) sprintf(class, "%sView", [info viewName]); // before I loaded all classes at launch time; now classes are loaded only as // needed. This idea and some of the code here is from bill bumgarner, thanx! if ([info path]) { // we have path but no instance, must load class char path[MAXPATHLEN]; char *filenames[] = {path, NULL}; // order dependency long result; struct mach_header *header; NXStream *stream = NULL; #ifdef DEBUG char *address; int length, maximum; #endif do { #ifdef DEBUG stream = NXOpenMemory(NULL, 0, NX_WRITEONLY); #endif (void) sprintf(path, "%s/%s.%s", [info path], class, BINARYEXTENSION); result = objc_loadModules(filenames, stream, NULL, &header, NULL); #ifdef DEBUG NXFlush(stream); NXGetMemoryBuffer(stream, &address, &length, &maximum); (void) fprintf(stderr, address); NXCloseMemory(stream, NX_FREEBUFFER); #endif // objc_loadModules succeeds with a warning if the architecture of the // object file is wrong, so we better check if we really got a class if (result == 0 && objc_getClass(class) == nil) result = -1; } while (result != 0 && [info useNextPath] != nil); [info discardAltPaths]; #ifdef DEBUG (void) fprintf(stderr, "Dynamic load of class: %s -- %s!\n", class, (result == 0) ? "succeeded" : "failed"); #endif if (result != 0) return nil; // Ugh, failed -- return failure. else [info setHeader:header]; } theView = [objc_getClass(class) allocFromZone:backZone]; // at this point we must have a valid name for a loaded class #ifdef KLUDGE if (streq(class, "MovieShowView")) { // don't prompt user for move name char buffer[MAXPATHLEN]; NXRegisterDefaults("MovieShow", MovieShowDefaults); (void) sprintf(buffer, "%s/defaultImage.anim", [[NXBundle bundleForClass:[self class]] directory]); (void) NXSetDefault("MovieShow", "Movie", buffer); } #endif [info setView:[theView initFrame:&aFrame]]; } return theView; } // Dynamically load all object files found in the specified directory // if we find a module in several places, we save the additional paths // in case they point to modules for different architectures - loadViewsFrom: (const char *) dirname { DIR *dir; int index; struct direct *de; BOOL validName, filePackage; char *iptr, name[MAXNAMLEN], path[MAXPATHLEN]; if ((dir = opendir(dirname)) == NULL) return nil; while ((de = readdir(dir)) != NULL) { if (de->d_name[0] == '.') continue; // Ignore '.'-files (not really necessary, I guess) filePackage = validName = NO; if (de->d_namlen > (strlen(BINARYEXTENSION) + 1) && streq(&de->d_name[de->d_namlen - (strlen(BINARYEXTENSION) + 1)], "." BINARYEXTENSION)) validName = YES; else if (de->d_namlen > (strlen(BUNDLEEXTENSION) + 1) && streq(&de->d_name[de->d_namlen - (strlen(BUNDLEEXTENSION) + 1)], "." BUNDLEEXTENSION)) validName = filePackage = YES; if (!validName) continue; if (filePackage) (void) sprintf(path, "%s/%s", dirname, de->d_name); else (void) strcpy(path, dirname); if ((iptr = rindex(strcpy(name, de->d_name), 'V')) != NULL) *iptr = '\0'; // Smash out the 'V' in "FooView.BackO" if ((index = [moduleList indexOfName:name]) != -1) { [[moduleList objectAt:index] appendPath:path]; continue; } // I used to load the class at this time; this got horribly inefficient. // I now wait until I'm about to instantiate a view before doing this (thanx bbum!) [moduleList addObject:[[ModuleInfo alloc] initWithView:NULL name:name path:path]]; } closedir(dir); return self; } - (const char *) appDirectory { return launchDir; } - (const char *) moduleDirectory:(const char *) name { int index = [moduleList indexOfName:name]; if (index == -1) return NULL; return [[moduleList objectAt:index] path]; } - (struct mach_header *) headerForModule:(const char *) name { int index = [moduleList indexOfName:name]; if (index == -1) return NULL; return [[moduleList objectAt:index] header]; } // In the multi-headed case, I gotta throw a black window over all // the screens so they don't burn in while I do animation on one. // You'd want to black out all screen in every case if you switched // animations on the fly to prevent the screen from possibly being // unlocked for a moment. // Hmm, I don't know why I didn't just put a single big non retained // window over all screens instead... - blackOutAllScreens { id window; NXRect *rect; unsigned int i; if (screenCount <= 1) return self; if (screenList == nil) screenList = [[List alloc] initCount:screenCount]; for (i = 0; i < screenCount; i++) { rect = &screens[i].screenBounds; window = [[Window allocFromZone:backZone] initContent:rect style:NX_TOKENSTYLE backing:NX_NONRETAINED buttonMask:0 defer:NO]; [screenList addObject:window]; [window setBackgroundGray:NX_BLACK]; (void) [window addToEventMask:NX_MOUSEMOVEDMASK]; tweakWindow([window windowNum], SAVERTIER - 1); [[window placeWindowAndDisplay:rect] orderFront:self]; } return self; } - unBlackOutAllScreens { if (screenCount <= 1) return self; [[screenList makeObjectsPerform:@selector(orderOut:) with:self] freeObjects]; return self; } - nullInspector { return nullInspector; } - commonImageInspector { return commonImageInspector; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.