This is Thinker.m in view mode; [Download] [Up]
#import "Thinker.h"
#import "BackWindow.h"
#import "BackView.h"
#import "SpaceView.h"
#import "BezierViewPart.h"
#import "MySlider.h"
#import "Password.h"
#import "psfuncts.h"
#import "Localization.h"
#import <appkit/Application.h>
#import <appkit/Matrix.h>
#import <appkit/Button.h>
#import <appkit/Cell.h>
#import <appkit/publicWraps.h>
#import <dpsclient/wraps.h>
#import <dpsclient/dpsclient.h>
#import <appkit/defaults.h>
#import <appkit/graphics.h>
#import <dpsclient/dpsNeXT.h>
#import <sys/resource.h>
#import <sys/file.h>
#import <mach.h>
#import <cthreads.h>
#import <c.h>
#import <libc.h>
#import <appkit/OpenPanel.h>
#import <appkit/NXImage.h>
// convert vertical blank time to milliseconds
#define VB2MS(x) (((x * 1000)/(68)) + 1000)
//#define SHOWITERATIONSPERSEC
#ifdef SHOWITERATIONSPERSEC
unsigned iterations;
BStimeval then, now, targetTime;
#endif
@implementation Thinker
- appDidInit:sender
{
globalTier = -1;
doingSaver = NO;
backZone = NXCreateZone(vm_page_size, vm_page_size, YES);
NXSetRect(&windowRect, 475, 300, 500, 450);
[NXApp getScreens:&screens count:&screenCount];
[self getViewType];
[self createViewLists];
[self setVirtualViewIndexAndIncrement:NO];
[self getWindowType];
[self getScreenSaverSetting];
[self getScreenLockerSetting];
[self getBackgroundColor];
[self getPrioritySetting];
[self getImageFile];
#ifdef SHOWITERATIONSPERSEC
then = currentTimeInMs();
targetTime = then + 10000;
#endif
return self;
}
- appDidHide:sender
{
if (windowType != BACKWINDOW) [self removeTimer];
return self;
}
- appDidUnhide:sender
{
if (windowType != NOWINDOW) [self createTimer];
return self;
}
// Pretty much a dummy function to invoke the step method.
void timedEntryFunction (DPSTimedEntry timedEntry, double timeNow, void *theObject)
{ [(id)theObject doDistributorLoop];
}
- createTimer
{
if (!timerValid)
{
timerValid = YES;
timer = DPSAddTimedEntry(0.03, &timedEntryFunction, self, NX_BASETHRESHOLD);
}
return self;
}
- removeTimer
{
if (timerValid) DPSRemoveTimedEntry (timer);
timerValid = NO;
return self;
}
- doDistributorLoop
{
NXEvent dummyEvent;
keepLooping = YES;
[spaceView lockFocus];
if ([spaceView respondsTo:@selector(didLockFocus)]) [spaceView didLockFocus];
do {
[spaceView oneStep];
NXPing (); // Synchronize postscript for smoother animation
[spaceWindow flushWindow];
[spaceView oneStep];
NXPing (); // Synchronize postscript for smoother animation
[spaceWindow flushWindow];
#ifdef SHOWITERATIONSPERSEC
iterations++;
if ((now = currentTimeInMs()) > targetTime)
{
printf("BackSpace: %5.1f its/sec\n",
(double)iterations*1000.0/(double)(now - then));
iterations = 0;
targetTime = now + 10000;
then = now;
}
#endif
} while (timerValid && keepLooping &&
([NXApp peekNextEvent:NX_ALLEVENTS into:&dummyEvent
waitFor:0 threshold:NX_BASETHRESHOLD] == NULL));
[spaceView unlockFocus];
return self;
}
- useNormalWindow
{
int myBacking;
spaceView = [self normalView];
myBacking = [self backingTypeForView:spaceView];
if (!normalWindow)
{
// window must be created as buffered or flushing will never work
// ie the Kit won't believe its buffered if it was created retained
normalWindow = [[Window allocFromZone:backZone]
initContent:&windowRect style:NX_RESIZEBARSTYLE
backing:NX_BUFFERED
buttonMask:NX_RESIZEBUTTONMASK | NX_CLOSEBUTTONMASK
defer:NO];
[spaceView setAutosizing:NX_WIDTHSIZABLE | NX_HEIGHTSIZABLE];
[[normalWindow setContentView:spaceView] free];
[self setWindowTitle];
[normalWindow useOptimizedDrawing:YES];
[normalWindow setDynamicDepthLimit:YES]; //want window depth to match device!
[normalWindow setOneShot:YES];
[normalWindow setDelegate:self];
[normalWindow setBackgroundGray:NX_BLACK];
}
spaceWindow = normalWindow;
if ([spaceView respondsTo:@selector(setImage:)])
[spaceView setImage: image];
if ([spaceView respondsTo:@selector(newWindow)]) [spaceView newWindow];
[spaceWindow display];
[spaceWindow makeKeyAndOrderFront:self];
// need to do this so flushing always works!
// must do it late because kit does lazy window creation ie the PostScript
// window might not exist until you actually draw to it
if (myBacking == NX_RETAINED)
convertToRetained([spaceWindow windowNum]);
else convertToBuffered([spaceWindow windowNum]);
return self;
}
- (int) backingTypeForView:aView
{
if ([aView respondsTo:@selector(useBufferedWindow)]
&& [aView useBufferedWindow])
return NX_BUFFERED;
return NX_RETAINED;
}
- useBackWindow:(int)tier
{
NXRect r={{0, 0}};
int myBacking;
[NXApp getScreenSize:&(r.size)];
spaceView = [self bigView];
myBacking = [self backingTypeForView:spaceView];
[self createBigWindowIfNecessaryForView:spaceView];
if (myBacking == NX_RETAINED)
{ spaceWindow = bigUnbufferedWindow;
tweakWindow([spaceWindow windowNum], tier);
}
else
{ spaceWindow = bigBufferedWindow;
}
[spaceWindow setContentView:spaceView];
if ([spaceView respondsTo:@selector(setImage:)])
[spaceView setImage: image];
[spaceWindow placeWindowAndDisplay:&r];
[spaceWindow orderFront:self];
if (myBacking == NX_BUFFERED) tweakWindow([spaceWindow windowNum], tier);
if ([spaceView respondsTo:@selector(newWindow)]) [spaceView newWindow];
[spaceWindow display];
return self;
}
- createBigWindowIfNecessaryForView:aView
{
NXRect r={{0, 0}};
int myBacking = [self backingTypeForView:aView];
[NXApp getScreenSize:&(r.size)];
if ((myBacking == NX_RETAINED) && !bigUnbufferedWindow)
{
bigUnbufferedWindow = [[BackWindow allocFromZone:backZone]
initContent:&r style:NX_TOKENSTYLE
backing:NX_NONRETAINED buttonMask:0 defer:NO];
[[bigUnbufferedWindow setContentView:aView] free];
[bigUnbufferedWindow useOptimizedDrawing:YES];
[bigUnbufferedWindow removeFromEventMask:(NX_LMOUSEDOWNMASK | NX_LMOUSEUPMASK
| NX_MOUSEMOVEDMASK | NX_LMOUSEDRAGGEDMASK
| NX_MOUSEENTEREDMASK | NX_MOUSEEXITEDMASK
| NX_KEYDOWNMASK | NX_KEYUPMASK
| NX_CURSORUPDATEMASK)];
[bigUnbufferedWindow setBackgroundGray:NX_BLACK];
}
if ((myBacking == NX_BUFFERED) && !bigBufferedWindow)
{
bigBufferedWindow = [[BackWindow allocFromZone:backZone]
initContent:&r style:NX_TOKENSTYLE
backing:NX_BUFFERED buttonMask:0 defer:NO];
[[bigBufferedWindow setContentView:aView] free];
[bigBufferedWindow useOptimizedDrawing:YES];
[bigBufferedWindow removeFromEventMask:(NX_LMOUSEDOWNMASK | NX_LMOUSEUPMASK
| NX_MOUSEMOVEDMASK | NX_LMOUSEDRAGGEDMASK
| NX_MOUSEENTEREDMASK | NX_MOUSEEXITEDMASK
| NX_KEYDOWNMASK | NX_KEYUPMASK
| NX_CURSORUPDATEMASK)];
[bigBufferedWindow setDynamicDepthLimit:YES]; //want window depth to match device!
[bigBufferedWindow setOneShot:YES];
[bigBufferedWindow setBackgroundGray:NX_BLACK];
}
return self;
}
- changeWindowType:sender
{
[self changeWindowTypeAndRemember:YES];
return self;
}
- changeWindowTypeAndRemember:(BOOL)rem
{
char str[50];
int newWindowType;
newWindowType = [windMatrix selectedRow];
if (newWindowType == windowType) return self;
windowType = newWindowType;
if (rem)
{
sprintf(str,"%1d", windowType);
NXWriteDefault([NXApp appName], "windowType", str);
}
[spaceWindow orderOut:self];
switch (windowType)
{
case NOWINDOW:
[self removeTimer];
break;
case NORMALWINDOW:
[self useNormalWindow];
[self createTimer];
break;
case BACKWINDOW:
[self useBackWindow: globalTier];
[self createTimer];
break;
}
return self;
}
- getWindowType
{
int tWindowType;
const char *ptr;
int val;
ptr = NXGetDefaultValue([NXApp appName], "windowType");
if (ptr)
{
sscanf(ptr,"%d",&val);
if (val >= 0 && val <= 2) tWindowType = val;
else tWindowType = NORMALWINDOW;
}
else tWindowType = NORMALWINDOW;
[windMatrix selectCellAt:tWindowType :0];
[self changeWindowTypeAndRemember:NO];
return self;
}
- getScreenSaverSetting
{
const char *ptr;
if((evs = open("/dev/evs0",O_RDWR)) < 0)
{ perror("/dev/evs0");
exit(10);
}
[self getDimBrightness:&oldDimBrightness];
//in case the old dim brightness is somehow invalid, I reset it
if (oldDimBrightness > 15)
{
oldDimBrightness = 15;
[self _setDimBrightness:&oldDimBrightness];
}
[screenSaver setState:0];
ptr = NXGetDefaultValue([NXApp appName], "screenSaver");
if (!ptr || !strcmp(ptr,"Off")) [self setScreenSaver:NO andRemember:NO];
else [self setScreenSaver:YES andRemember:NO];
return self;
}
- changeScreenSaverSetting:sender
{
[self setScreenSaver:([screenSaver state])andRemember:YES];
return self;
}
- setScreenSaver:(BOOL)val andRemember:(BOOL)rem
{
[screenSaver setState:val];
screenSaverVal = val;
if (val)
{
// turn it on...
// I don't like turning on bright autodim here - It should be
// in the doScreenSaver: method, but setting the dim value
// there doesn't work if the screen is dim!
[self screenSaverMode];
// we never really know what time it is, but we get an idea
// by watching the dim time progress
[self getDimTime :&dimTime];
[self getDimInterval :&realDimInterval];
// printf("BackSpace Interval: %d\n", realDimInterval/68);
[self perform:@selector(maybeDoScreenSaver:)
with:self
afterDelay:VB2MS(realDimInterval)
cancelPrevious:YES];
if (rem) NXWriteDefault([NXApp appName], "screenSaver", "On");
}
else
{
// turn it off...
[self normalMode];
if (rem) NXRemoveDefault([NXApp appName], "screenSaver");
}
return self;
}
- calcDimTime
{
int newTime, newInterval, maxInterval;
[self getDimTime :&newTime];
[self getDimInterval :&maxInterval];
newInterval = newTime - dimTime;
if (newInterval > maxInterval) newInterval = maxInterval;
if (doingSaver) newInterval = realDimInterval;
// printf("newTime:%d dimTime: %d\n",newTime, dimTime);
[self perform:@selector(maybeDoScreenSaver:)
with:self
afterDelay:VB2MS(newInterval)
cancelPrevious:YES];
// printf("BackSpace:waiting %d seconds\n",((VB2MS(newInterval))/1000));
dimTime = newTime;
return self;
}
- maybeDoScreenSaver:sender
{
NXEvent anEvent;
int autoDimmed;
// in case timed entry fires but user has killed screen saver
if (!screenSaverVal) return self;
[self getDimStatus :&autoDimmed];
if (doingSaver || !autoDimmed)
{
[self calcDimTime];
return self;
}
// The perform:afterDelay: method starts a timed entry to
// invoke maybeDoScreenSaver, so we are in a timed entry
// right now. If we just jumped into doScreenSaver:, we
// would interrupt the doDistributorLoop method while
// it's still focused on the spaceView. By posting an
// event, we force that loop to bail out so we can jump
// into the screen saver cleanly.
keepLooping = NO; // There was a bug related to this at one point.
// I don't think it's necessary anymore.
anEvent.type = NX_APPDEFINED;
anEvent.ctxt = [NXApp context];
DPSPostEvent(&anEvent,0);
return self;
}
- applicationDefined:(NXEvent *)theEvent
{
[self doScreenSaverAndResetTimer];
return self;
}
// This method invokes the screen saver. After the screen
// saver terminates, it resets the timer to fire the screen
// saver again
- doScreenSaverAndResetTimer
{
int dimInterval;
[self doScreenSaver:self];
// reset to fire again
[self getDimTime :&dimTime];
[self getDimInterval :&dimInterval];
[self perform:@selector(maybeDoScreenSaver:)
with:self
afterDelay:VB2MS(dimInterval)
cancelPrevious:YES];
return self;
}
- showFakeScreenSaver:sender
{
// dim intervals in vblanks
int oldDimInterval, tempDimInterval = 11;
[self screenSaverMode];
[self getDimInterval :&oldDimInterval];
[self setDimInterval :&tempDimInterval];
// now I wait until the screen "should" be dim. I don't really
// like this solution, but at least it's relatively harmless.
// You mustn't loop until the screen dims, because it might never
// dim. Also note that the dim interval must be long enough that
// the bright screen will be caught in doScreenSaver: before the
// screen goes dim again.
usleep((13*1000000)/68); //sleep till dim (yuck!)
// This is kind of a drag; we are running the screen saver with
// a very short dim interval. This means if somebody recompiles
// backspace (changing the executable) while somebody else is
// running the app as a fake screen saver (ie demo), the app will
// die leaving the dim interval horribly short. I don't know a good
// workaround, unfortunately.
[self doScreenSaver:self];
[self setDimInterval :&oldDimInterval]; //restore dim interval
if (!screenSaverVal) [self normalMode];
// reset to fire again
[self getDimTime :&dimTime];
[self perform:@selector(maybeDoScreenSaver:)
with:self
afterDelay:VB2MS(oldDimInterval)
cancelPrevious:YES];
return self;
}
//don't call next 2 methods, they are hacks only for doScreenSaver with screen locking
static int dimIntervalToRestore;
- forceScreenToDimHack
{
int tempDimInterval = 11;
[self getDimInterval :&dimIntervalToRestore];
[self setDimInterval :&tempDimInterval];
usleep((13*1000000)/68); //sleep till dim (yuck!)
return self;
}
- forceScreenToUnDimhack
{
dimIntervalToRestore = MAX(dimIntervalToRestore,680);
[self setDimInterval :&dimIntervalToRestore]; //restore dim interval
return self;
}
- doScreenSaver:sender
{
int autoDimmed;
int oldWindowType;
BOOL mouseOK, oldTimerValid;
// BOOL isHidden;
NXRect trackingRect;
NXPoint mouseLoc;
BOOL passwordOK;
BOOL mustRestoreOldDimInterval = NO;
BOOL stoleActivation = NO;
int oldActiveApp = 0;
// must be sure we don't enter on timed entry after faking saver
doingSaver = YES;
#ifdef VANNA
// using tokenstyle windows (the only kind that don't hide)
// triggers a weird activation bug that makes some windows
// unresponsive to events until you hide and unhide the app.
// The following code should be necessary, but isn't, and
// should cure the bug, but doesn't...
isHidden = [NXApp isHidden];
if (isHidden)
{
[NXApp unhideWithoutActivation:self];
}
#endif
if ([password isLocked])
{
oldActiveApp = [NXApp activateSelf:YES];
stoleActivation = YES;
}
[self setVirtualViewIndexAndIncrement:YES];
//save old window state
oldWindowType = [windMatrix selectedRow];
globalTier = SAVERTIER;
[self blackOutAllScreens];
//background window on screen
[windMatrix selectCellAt:BACKWINDOW :0];
[self changeWindowTypeAndRemember:NO];
//nuke timer so timed entry doesn't fire
oldTimerValid = timerValid;
[self removeTimer];
//set background window tier to SAVERTIER
if ([self backingTypeForView:spaceView] == NX_BUFFERED)
{
// make sure the one shot buffer really exists
[spaceWindow display];
setWindowLevel([spaceWindow windowNum],SAVERTIER);
}
else
{
setWindowLevel([spaceWindow windowNum],SAVERTIER);
[spaceView fillBoundsWithBlack];
[spaceView display];
}
do {
//obscure cursor
PShidecursor();
[spaceView lockFocus];
if ([spaceView respondsTo:@selector(didLockFocus)])
[spaceView didLockFocus];
[spaceWindow getMouseLocation:&mouseLoc];
trackingRect.origin.x = mouseLoc.x - 100;
trackingRect.origin.y = mouseLoc.y - 100;
trackingRect.size.width = trackingRect.size.height = 200;
do {
[spaceView oneStep];
NXPing (); // Synchronize postscript for smoother animation
[spaceWindow flushWindow];
// note: window and view coordinates the same!
// so I don't have to convert to view coord system
[spaceWindow getMouseLocation:&mouseLoc];
mouseOK = [spaceView mouse:&mouseLoc inRect:&trackingRect];
[spaceView oneStep];
NXPing (); // Synchronize postscript for smoother animation
[spaceWindow flushWindow];
[self getDimStatus :&autoDimmed]; //get dim status
} while (autoDimmed && mouseOK);
[spaceView unlockFocus];
//restore cursor
PSshowcursor();
passwordOK = [password checkPassword:
LocalString("Screen is locked. Enter password to unlock:",0,0)
randomPos:YES checkLock:YES withView:spaceView];
if (!passwordOK && !mustRestoreOldDimInterval)
{
[self forceScreenToDimHack];
mustRestoreOldDimInterval = YES;
}
} while (!passwordOK);
if (mustRestoreOldDimInterval) [self forceScreenToUnDimhack];
//background window tier to -1
setWindowLevel([spaceWindow windowNum],-1);
globalTier = -1;
if (([self backingTypeForView:spaceView] != NX_BUFFERED) &&
oldWindowType == BACKWINDOW)
// this justs fixes a display bug for really lazy nonretained windows
{
[spaceView fillBoundsWithBlack];
[spaceView display];
}
if (oldTimerValid) [self createTimer];
[self unBlackOutAllScreens];
//restore old window state
[windMatrix selectCellAt:oldWindowType :0];
[self changeWindowTypeAndRemember:NO];
if (stoleActivation)
{
if (oldActiveApp) [NXApp activate:oldActiveApp];
else [NXApp deactivateSelf];
}
#ifdef VANNA
if (isHidden)
{
[NXApp hide:self];
}
#endif
doingSaver = NO;
return self;
}
- appWillTerminate:sender
{
[self normalMode];
return self;
}
- getPrioritySetting
{
const char *ptr;
int val;
[mySlider setMinValue: 0];
[mySlider setMaxValue: 10];
ptr = NXGetDefaultValue([NXApp appName], "priority");
if (ptr)
{
sscanf(ptr,"%d",&val);
if (val >= 0 && val <= 10) priority = val;
else priority = 4;
}
else priority = 4;
[[mySlider cell] setIntValue:priority];
[[priorityLevel cell] setIntValue:priority];
// use mach call rather than unix - mach lets me increase priority!
// setpriority(PRIO_PROCESS, 0, priority);
cthread_priority(cthread_self(), priority, FALSE);
return self;
}
- changeSliderValue:sender
{
priority = [[mySlider cell] intValue];
[[priorityLevel cell] setIntValue:priority];
return self;
}
- saveSliderValue
{
char str[50];
// setpriority(PRIO_PROCESS, 0, priority);
cthread_priority(cthread_self(), priority, FALSE);
sprintf(str,"%d", priority);
NXWriteDefault([NXApp appName], "priority", str);
return self;
}
- windowWillResize:sender toSize:(NXSize *)frameSize
{
if (frameSize->width < 100) frameSize->width = 100;
if (frameSize->height < 100) frameSize->height = 100;
return self;
}
- windowWillClose:sender
{
[windMatrix selectCellAt:NOWINDOW :0];
[self perform:@selector(changeWindowType:) with:self
afterDelay:1 cancelPrevious:YES];
return nil;
}
BStimeval currentTimeInMs()
{
struct timeval curTime;
gettimeofday (&curTime, NULL);
return (curTime.tv_sec) * 1000 + curTime.tv_usec / 1000;
}
//
// Additional methods to handle a common image object for views.
// Lennart Lovstrad, Rank Xerox EuroPARC, August 1991.
//
- setImageFromFile: (const char *) filename
{
[image free];
image = [[NXImage alloc] initFromFile: filename];
if (image == nil)
{
NXRunAlertPanel([NXApp appName], LocalString("Could not open %s",0,0),
NULL, NULL, NULL, filename);
image = nil;
//return nil; //can't return, image is invalid
}
return [self commonImageInit];
}
- setImageFromName: (const char *) name
{
[image free];
image = [[NXImage alloc] initFromSection: name];
return [self commonImageInit];
}
- commonImageInit
{
[imageView setImage: image];
[imageView display];
if ([spaceView respondsTo:@selector(setImage:)])
[spaceView setImage: image];
if ([self backingTypeForView:spaceView] != NX_BUFFERED)
{
[spaceView fillBoundsWithBlack];
[spaceView display];
}
return self;
}
- getImageFile
{
const char *filename;
filename = NXGetDefaultValue([NXApp appName], "imageFile");
if (filename)
[self setImageFromFile: filename];
else [self setImageFromName: "defaultImage"];
return self;
}
- setImageFileFrom: sender
{
id openPanel = [OpenPanel new];
const char *fileTypes[] = {"tiff", "eps", NULL};
if ([openPanel runModalForTypes: fileTypes])
{
[self setImageFromFile: [openPanel filename]];
NXWriteDefault([NXApp appName], "imageFile", [openPanel filename]);
}
[spaceView display]; //don't know why this is necessary...
return self;
}
// This should return a float between 0 and 1
float frandom()
{
float val = (random() & 0x7fffffff);
val /= 0x7fffffff;
return val;
}
float randBetween(float a, float b)
{
float val, scale, t;
if (a > b)
{ t = a; a = b; b = t;
}
scale = (b-a);
val = scale * frandom();
return (a + val);
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.