|
|
Sample Programs from NeXSTEP 3.3
/* Thinker.m, the brains in the color server app */
#import "Thinker.h"
#import "SpotView.h"
#import "ClientRecord.h"
#import "FullCopyList.h"
#import <libc.h>
#import <appkit/Panel.h>
#import <machkit/NXNetNameServer.h>
#import <machkit/senderIsInvalid.h>
#import <mach/mach.h>
#import <mach/mach_error.h>
#import <objc/hashtable.h>
#import <servers/netname.h>
#define REGISTERED_NAME "samsColorServer"
#define streq !strcmp
@implementation Thinker
//////////////////////////////////////////////////////
// Shared server and client code
//////////////////////////////////////////////////////
- appDidInit:sender
{
float r,g,b;
NXColor theColor;
spotList = [[FullCopyList alloc] init];
// return a proxy for the server
// this looks for a color server on any host. In the real world,
// you would probably specify a host.
// this returns a local proxy to the remote "root" object
server = [NXConnection connectToName:REGISTERED_NAME onHost:"*"];
if (server)
{
// Become a client, already a server running
isServer = NO;
myConnection = [server connectionForProxy];
[myConnection registerForInvalidationNotification:self];
[server setProtocolForProxy:@protocol(spotServerMethods)];
[win setTitle:"Spot Client"];
}
else
{
// Become the spot server, since no server is running
// There is a window of vulnerability here; if 2 apps
// launch nearly simultaneously, both will not find servers,
// one will become the real server but the other will also
// think it's the server (but will never get clients).
// I should deal with this...
isServer = YES;
myConnection = [NXConnection registerRoot:self withName:REGISTERED_NAME];
// printf("server: root connection is 0x%x\n", myConnection);
[win setTitle:"Spot Server"];
clientList = [[List alloc] init];
server = self; //so this can act as its own pseudo client...
}
[myConnection runFromAppKit];
[mySpotView lateInit];
[server addClient:self r:&r g:&g b:&b];
theColor = NXConvertRGBToColor(r,g,b);
[[win setBackgroundColor:theColor] display];
return self;
}
- appWillTerminate:sender
{
if (isServer)
{
int i, n = [clientList count];
for (i = 0; i < n; i++)
{
id theClient = [[clientList objectAt:i] client];
if (theClient != self) [theClient serverTerminated];
}
}
else
{
[server clientTerminated:self];
}
return self;
}
- server
{ return server;
}
- (BOOL)isServer
{ return isServer;
}
// this is sent by a connection
- senderIsInvalid:sender
{
if (isServer)
{
int i, n = [clientList count];
for (i = 0; i < n; i++)
{
id theClientRecord = [clientList objectAt:i];
if ([theClientRecord connection] == sender)
{
[self nukeClient: theClientRecord];
break;
}
}
}
else
{
[self serverTerminated];
}
// now free the connection since it's no longer useful
// this also frees the proxies that the connection maintained
[sender free];
return nil;
}
//////////////////////////////////////////////////////
// Server code
//////////////////////////////////////////////////////
// the server needs to keep a local database of clients so it can
// message them all and associate port deaths to dead clients
- (void) addClient:remoteClient
r:(out float *)r
g:(out float *)g
b:(out float *)b
{
id connToClient, aSpot, client;
if (remoteClient != self)
{
[remoteClient setProtocolForProxy:@protocol(spotClientMethods)];
connToClient = [remoteClient connectionForProxy];
[connToClient registerForInvalidationNotification:self];
}
else connToClient = nil;
aSpot = [[[Spot alloc] init] addReference];
client = [[ClientRecord alloc]
initClient:remoteClient connection:connToClient spot:aSpot];
// the client list is used internally by the server, while the
// spot list contains some duplicate information, but must
// be exported.
[clientList addObject:client];
[spotList addObject:aSpot];
NXConvertColorToRGB([aSpot color], r, g, b);
[self sendSpotListToClients];
return;
}
// only called in server by server
- nukeClient: theClientRecord
{
id theSpot = [theClientRecord spot];
[clientList removeObject:theClientRecord];
[spotList removeObject:theSpot];
[theSpot invalidate];
[NXApp delayedFree:theSpot]; // so we don't kill in modal loop
[theClientRecord free];
[self sendSpotListToClients];
return self;
}
- (void) clientTerminated:(in id)sender
{
int i, n = [clientList count];
id theClient;
for (i = 0; i < n; i++)
{
id theClientRecord = [clientList objectAt:i];
theClient = [theClientRecord client];
if (theClient == sender)
{
[self nukeClient: theClientRecord];
return;
}
}
return;
}
- (oneway void) sendSpotListToClients
{
int i, n = [clientList count];
id theClient;
for (i = 0; i < n; i++)
{
theClient = [[clientList objectAt:i] client];
if (theClient != self)
{
[theClient useSpotList:spotList];
}
else [mySpotView display];
}
return;
}
// the point is passed in view coordinates; it is interpreted in the
// server's view's coordinate system. It returns a spot object if
// possible, which creates a proxy to that object on the client side
- getSpotForPoint:(NXPoint) pnt spotLocation:(out NXPoint *)loc
{
int n = [spotList count];
int i;
for (i = n-1; i >= 0; i--)
{
id aSpot = [spotList objectAt:i];
NXPoint spnt = [aSpot location];
if (pnt.x > spnt.x && pnt.x < spnt.x + 30 &&
pnt.y > spnt.y && pnt.y < spnt.y + 30)
{
if ([aSpot doLock])
{
*loc = spnt;
return aSpot;
}
return nil;
}
}
return nil;
}
- (oneway void) spotDidChange
{
[server sendSpotListToClients];
return;
}
//////////////////////////////////////////////////////
// Client code
//////////////////////////////////////////////////////
// return the clients own copy of the spotList
- spotList
{
return spotList;
}
// don't ever send this message to the server!
- (oneway void) useSpotList: (bycopy in id) newSpotList
{
[spotList freeObjects];
[spotList free];
spotList = newSpotList;
[mySpotView display];
return;
}
// the dying server calls this to nuke the clients
- (oneway void) serverTerminated
{
NXRunAlertPanel (NULL, "Server terminated. Helpless client now "
"terminating as well.", "Oot!", NULL, NULL);
[NXApp terminate:self];
}
@end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.