This is FireflyView.m in view mode; [Download] [Up]
/* file: FireflyView.m history: 1993-06-06, andrew abernathy · created 1993-07-04, andrew abernathy · added configurability */ #import <appkit/appkit.h> #import "FireflyView.h" #define DEFAULTS_OWNER "BackSpace.Fireflies" // this is the owner name to use for the defaults database // this macro returns a random integer between min & max #define randomIn(min,max) ((random () % (max - min)) + min) // convert an NXColor to an ASCII representation static char * ColorToASCII (NXColor color, char * buf) { float red, green, blue; NXConvertColorToRGB (color, &red, &green, &blue); sprintf (buf, "%f:%f:%f", red, green, blue); return buf; } // convert an ASCII representation to an NXColor static NXColor ASCIIToColor (const char * buf) { float red, green, blue; sscanf (buf, "%f:%f:%f", &red, &green, &blue); return NXConvertRGBToColor (red, green, blue); } // register our defaults static void RegisterDefaults (void) { static NXDefaultsVector FireflyDefaults = { { "FireflyColor", "1.0:1.0:1.0" }, // fireflies default to white { "FireflyCount", "60" }, // 60 fireflies initially { "BackgroundColor", "0:0:0" }, // background starts at black { "BoringModule", "YES" }, // yeah, we're a boring module { NULL, NULL } }; NXRegisterDefaults (DEFAULTS_OWNER, FireflyDefaults); // register the defaults } @implementation FireflyView:View /* Here we do class initialization. All we do right now is set up our defaults. */ + initialize { RegisterDefaults (); return self; } /* Here we're notified that our inspector has been installed. We use this for most of the initialization as we don't know of a better place; because of the way BackSpace does things, we're sure this will always get called before the screen saver module actually kicks in. Besides, we can't make our inspector reflect our configuration data before the inspector is loaded. If we already have some fireflies, everything must already be set up, so we don't worry about it. Our first group of fireflies will just be wasted - never drawn. However, this is better than checking to make sure we don't release or draw non-existant fireflies every time we get a oneStep message. */ - inspectorInstalled { if (!fireflies) // no fireflies, better do setup { fireflyColor = ASCIIToColor (NXGetDefaultValue (DEFAULTS_OWNER, "FireflyColor")); [fireflyColorWell setColor:fireflyColor]; // update the firefly color well on the inspector fireflyCount = atoi (NXGetDefaultValue (DEFAULTS_OWNER, "FireflyCount")); [fireflyCountField setIntValue:fireflyCount]; // update the count field on the inspector fireflyCountChanged = FALSE; // we don't have to worry about this since we set the count manually backgroundColor = ASCIIToColor (NXGetDefaultValue (DEFAULTS_OWNER, "BackgroundColor")); [backgroundColorWell setColor:backgroundColor]; // update the background color well on the inspector backgroundColorChanged = TRUE; // so that we read the color and clear the frame in "oneStep" isBoring = strcmp (NXGetDefaultValue (DEFAULTS_OWNER, "BoringModule"), "YES") == 0; [isBoringSwitch setState:(isBoring ? 1 : 0)]; // update the boring module switch on the inspector fireflies = [self generateFireflies:fireflyCount]; // create some fireflies } [fireflyCountField selectText:self]; // select the firefly count so that we can quickly type to change it return self; } /* Here we make a single "move": create new fireflies, draw them, then erase the old fireflies. Erasing in the end provides the potential for erasing new fireflies, if they happen to fall in the same position as an old firefly, but this is minor, and it avoids the flicker caused by: bunch of fireflies, black, bunch of fireflies, black, etc. Erasing old fireflies individually is much slower than just blacking everything out, but if we blacked everything out, we would get flicker. Best would be to draw a single firefly, then erase the equivalent "old" firefly, but this is much slower due to the constant color changes. Maybe I should be doing this off screen, but I don't know that much yet. */ - oneStep { int firefly; // firefly counter NXPoint * oldFireflies; // the old fireflies int oldFireflyCount; // number of old fireflies if (backgroundColorChanged) // background color changed; redraw the background { backgroundColor = [backgroundColorWell color]; // get new color NXSetColor (backgroundColor); // set the color to redraw the background in PSrectfill (0, 0, NX_WIDTH (&frame), NX_HEIGHT (&frame)); } oldFireflies = fireflies; // save the old fireflies oldFireflyCount = fireflyCount; // save the old fireflies count if (fireflyCountChanged) // new number of fireflies { fireflyCount = [fireflyCountField intValue]; // get new count fireflyCountChanged = FALSE; // reset firefly count changed flag } fireflies = [self generateFireflies:fireflyCount]; // make new fireflies NXSetColor (fireflyColor); // set the color to draw the fireflies in for (firefly = 0; firefly < fireflyCount; firefly++) // draw the fireflies { PSrectfill (fireflies[firefly].x, fireflies[firefly].y, 1.0, 1.0); } if (!backgroundColorChanged) // background didn't change, so we need to "black out" old fireflies { NXSetColor (backgroundColor); // set the color to erase the old fireflies with for (firefly = 0; firefly < oldFireflyCount; firefly++) // erase the old fireflies { PSrectfill (oldFireflies[firefly].x, oldFireflies[firefly].y, 1.0, 1.0); } } else // background DID change... { backgroundColorChanged = FALSE; // reset background changed flag } if (oldFireflies) // got old fireflies? { free (oldFireflies); // delete the old fireflies } return self; } /* We're going to get a oneStep message in a second; don't bother to draw everything, but say that we have a new background color to ensure that we clear our drawing area. */ - drawSelf:(const NXRect *)rects :(int)rectCount { backgroundColorChanged = TRUE; return self; } /* Most of our initialization is actually done in the "inspectorInstalled" method - we assume that our inspector is always pulled up once (manually or by BackSpace) before we begin executing. */ - initFrame:(const NXRect *)frameRect { [super initFrame:frameRect]; [self allocateGState]; // For faster lock/unlockFocus [self setClipping:NO]; // even faster... srandom (time (0)); // seed random number generator return self; } /* Find our inspector panel and return it. */ - inspector:sender { char buf[MAXPATHLEN]; if (!sharedInspectorPanel) // inspector not loaded yet? do so now. { sprintf (&buf[0], "%s/%s", [(BSThinker ()) moduleDirectory:"Firefly"], "FireflyView.nib"); [NXApp loadNibFile:buf owner:self withNames:NO]; } return sharedInspectorPanel; } /* Create some fireflies, position them, and return them for life (albeit brief) in the great outdoors. */ - (NXPoint *)generateFireflies:(int)numFireflies { int firefly; NXPoint * newFireflies; newFireflies = (NXPoint *) malloc (sizeof (NXPoint) * numFireflies); for (firefly = 0; firefly < fireflyCount; firefly++) { newFireflies[firefly].x = (NXCoord) randomIn (0, (int) NX_WIDTH (&frame)); newFireflies[firefly].y = (NXCoord) randomIn (0, (int) NX_HEIGHT (&frame)); } return newFireflies; } /* If we say we're boring, BackSpace won't call on us if it's in the "All" mode. */ - (BOOL)isBoringScreenSaver { return isBoring; } /* Movement in the background color well - let's see if the color actually changed. If so, we note that it changed and write the new color to the defaults database. We don't actually set the new color; that's done in the "oneStep" method, or colors get confused. */ - setBackgroundColor:sender { char buf[256]; NXColor tempColor; tempColor = [backgroundColorWell color]; if (!NXEqualColor (tempColor, backgroundColor)) { backgroundColorChanged = TRUE; NXWriteDefault (DEFAULTS_OWNER, "BackgroundColor", ColorToASCII (tempColor, &buf[0])); } return self; } /* Movement in the firefly color well - lets see if the color actually changed. If so, we set the new color and write the new color to the defaults database. We go ahead and set the new firefly color here as nothing gets confused when we do. */ - setFireflyColor:sender { char buf[256]; NXColor tempColor; tempColor = [sender color]; if (!NXEqualColor (tempColor, fireflyColor)) { fireflyColor = tempColor; NXWriteDefault (DEFAULTS_OWNER, "FireflyColor", ColorToASCII (fireflyColor, &buf[0])); } return self; } /* Movement in the firefly count field. If the new count is negative, reset the field, as negative numbers are invalid. Otherwise, if the new count is actually different from the old count, we note that fact and write the new value to the defaults database. We don't actually set the new count here or we risk not erasing old fireflies or erasing fireflies that didn't exist, tromping through memory we didn't allocate and happily grabbing non-existant (and often very strange) new fireflies. Kind of a bad thing, overall. */ - setFireflyCount:sender { char buf[256]; int tempCount; tempCount = [sender intValue]; if (tempCount < 0) // don't allow negative number of fireflies { [sender setIntValue:fireflyCount]; } else if (fireflyCount != tempCount) // the count really did change? { // we don't set the count here - that'll be done in "oneStep" fireflyCountChanged = TRUE; sprintf (&buf[0], "%d", tempCount); NXWriteDefault (DEFAULTS_OWNER, "FireflyCount", &buf[0]); [fireflyCountField setIntValue:tempCount]; // update the count field in case they entered a number larger than MAXINT } [fireflyCountField selectText:self]; return self; } /* Movement with the boring module switch; check the switch and set the corresponding flag and write the value to the defaults database. */ - setIsBoring:sender { switch ([sender state]) { case 0: // not set isBoring = FALSE; NXWriteDefault (DEFAULTS_OWNER, "BoringModule", "NO"); break; case 1: // IS set isBoring = TRUE; NXWriteDefault (DEFAULTS_OWNER, "BoringModule", "YES"); break; default: // error! should always be 0 or 1, so we ignore this case. shouldn't ever occur, anyway. break; } return self; } /* Open the readme file for the user's reading pleasure. */ - openReadme:sender { char buf[MAXPATHLEN]; sprintf (&buf[0], "open %s/%s", [(BSThinker ()) moduleDirectory:"Firefly"], "Info.rtf"); system (&buf[0]); return self; } /* Reset the configuration settings to their defaults. A side benefit of the way I did this is that it cleans out the defaults database of values for this module, so someone removing the module from their system can easily remove all of the associated defaults. Here we remove all of the defaults, re-register the defaults, and re-read the defaults. We have to re-register our defaults or the act of looking one up causes a crash. (Guess how I learned about that...) */ - resetSettings:sender { const char * tempStr; // remove defaults NXRemoveDefault (DEFAULTS_OWNER, "FireflyColor"); NXRemoveDefault (DEFAULTS_OWNER, "FireflyCount"); NXRemoveDefault (DEFAULTS_OWNER, "BackgroundColor"); NXRemoveDefault (DEFAULTS_OWNER, "BoringModule"); // re-register defaults RegisterDefaults (); // re-read defaults and update inspector tempStr = NXGetDefaultValue (DEFAULTS_OWNER, "FireflyColor"); if (tempStr) // got a valid string { fireflyColor = ASCIIToColor (tempStr); [fireflyColorWell setColor:fireflyColor]; } else { NXRunAlertPanel (0, "Could not determine firefly color", 0, 0, 0); } tempStr = NXGetDefaultValue (DEFAULTS_OWNER, "FireflyCount"); if (tempStr) { [fireflyCountField setIntValue:atoi (tempStr)]; fireflyCountChanged = TRUE; // make sure that we read this in "oneStep" } else { NXRunAlertPanel (0, "Could not determine number of fireflies", 0, 0, 0); } tempStr = NXGetDefaultValue (DEFAULTS_OWNER, "BackgroundColor"); if (tempStr) { [backgroundColorWell setColor:ASCIIToColor (tempStr)]; backgroundColorChanged = TRUE; // so that we read the color and clear the frame in "oneStep" } else { NXRunAlertPanel (0, "Could not determine background color", 0, 0, 0); } tempStr = NXGetDefaultValue (DEFAULTS_OWNER, "BoringModule"); if (tempStr) { isBoring = strcmp (tempStr, "YES") == 0; [isBoringSwitch setState:(isBoring ? 1 : 0)]; } else { NXRunAlertPanel (0, "Could not determine whether or not module is boring", 0, 0, 0); } [fireflyCountField selectText:self]; // select the firefly count so that we can quickly type to change it return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.