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.