ftp.nice.ch/pub/next/tools/screen/BackSpace.1.02.N.bs.tar.gz#/BackSpace/backspace/Thinker.m

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.