-- ClipperImpl.mesa
-- Last changed by Doug Wyatt, September 25, 1980 3:58 PM

DIRECTORY
Clipper,
Area USING [Handle, Free],
Patch USING [Handle, New, NewPipe, Free],
Pipe USING [Handle, Procs, Object, Sink, Put, Ref, Free],
Memory USING [NewZone];

ClipperImpl: PROGRAM
IMPORTS Memory,Area,Patch,Pipe
EXPORTS Clipper SHARES Clipper,Pipe = {
OPEN Clipper;

ClipperError: PUBLIC SIGNAL = CODE;

zone: UNCOUNTED ZONE = Memory.NewZone["ClipperImpl"];

Data: PUBLIC TYPE = RECORD [
level: CARDINAL, -- current Push depth
clevel: CARDINAL, -- level above which clipper is enabled/disabled
state: State, -- current clipper state
c: CDataRef
];
DataRef: TYPE = LONG POINTER TO Data;

CData: PUBLIC TYPE = RECORD [
plist: NodeRef, -- list of patches
ppipe: Pipe.Handle, -- pipe into patch chain
tpipe: Pipe.Handle, -- top pipe handed out to clients
ipipe,opipe: Pipe.Handle, -- current in,out pipes
state: State, -- state after last clipping operation
inuse: BOOLEAN, -- TRUE if a client is holding tpipe
refs: CARDINAL
];
CDataRef: TYPE = LONG POINTER TO CData;

Node: TYPE = RECORD [
next: NodeRef,
patch: Patch.Handle
];
NodeRef: TYPE = LONG POINTER TO Node;

cProcs: LONG POINTER TO READONLY Procs = zone.NEW[Procs = [
NewPipe: CNewPipe,
Push: CPush,
Pop: CPop,
Test: CTest,
NewRegion: CNewRegion,
Copy: CCopy,
Free: CFree
]];

iProcs: LONG POINTER TO READONLY Procs = zone.NEW[Procs = [
NewPipe: INewPipe,
Push: IPush,
Pop: IPop,
Test: CTest,
NewRegion: CNewRegion,
Copy: CCopy,
Free: CFree
]];

oProcs: LONG POINTER TO READONLY Procs = zone.NEW[Procs = [
NewPipe: ONewPipe,
Push: IPush,
Pop: IPop,
Test: CTest,
NewRegion: CNewRegion,
Copy: CCopy,
Free: CFree
]];

NewCData: PROC RETURNS[CDataRef] = {
c: CDataRef = zone.NEW[CData ← [
plist: NIL, ppipe: NIL, tpipe: NIL, ipipe: NIL, opipe: NIL,
state: [FALSE,FALSE], inuse: FALSE, refs: 1
]];
RETURN[c];
};

FreePList: PROC[c: CDataRef] = {
IF c.plist#NIL THEN {
list: NodeRef←c.plist; c.plist←NIL;
zone.FREE[@c.tpipe]; Pipe.Free[@c.ppipe];
UNTIL list=NIL DO
p: NodeRef←list; list←p.next;
Patch.Free[@p.patch]; zone.FREE[@p];
ENDLOOP;
};
};

FreeCData: PROC[cPtr: LONG POINTER TO CDataRef] = {
c: CDataRef←cPtr↑; cPtr↑←NIL;
IF c.inuse THEN ERROR ClipperError;
IF (c.refs←c.refs-1)=0 THEN {
FreePList[c]; zone.FREE[@c];
};
};

NewClipper: PUBLIC PROC RETURNS[Handle] = {
d: DataRef = zone.NEW[Data ← [
level: 1, clevel: 0, state: [FALSE,FALSE], c: NewCData[]
]];
RETURN[zone.NEW[Object ← [procs: oProcs, data: d]]];
};

ipipeProcs: LONG POINTER TO READONLY Pipe.Procs = zone.NEW[Pipe.Procs = [
Put: IPPut, Free: PFree]];
opipeProcs: LONG POINTER TO READONLY Pipe.Procs = zone.NEW[Pipe.Procs = [
Put: OPPut, Free: PFree]];
NewIPipe: PROC[c: CDataRef] RETURNS[Pipe.Handle] = INLINE {
RETURN[zone.NEW[Pipe.Object ← [procs: ipipeProcs, data: LOOPHOLE[c]]]];
};
NewOPipe: PROC[c: CDataRef] RETURNS[Pipe.Handle] = INLINE {
RETURN[zone.NEW[Pipe.Object ← [procs: opipeProcs, data: LOOPHOLE[c]]]];
};
IPPut: PROC[self: Pipe.Handle, area: Area.Handle] = {
c: CDataRef=LOOPHOLE[self.data];
c.state.in←TRUE; Pipe.Put[c.ipipe,area];
};
OPPut: PROC[self: Pipe.Handle, area: Area.Handle] = {
c: CDataRef=LOOPHOLE[self.data];
c.state.out←TRUE; Pipe.Put[c.opipe,area];
};
PFree: PROC[self: Pipe.Handle] = {
zone.FREE[@self];
};

tpipeProcs: LONG POINTER TO READONLY Pipe.Procs = zone.NEW[Pipe.Procs = [
Put: TPPut, Free: TPFree]];
NewTPipe: PROC[c: CDataRef] RETURNS[Pipe.Handle] = INLINE {
RETURN[zone.NEW[Pipe.Object ← [
procs: tpipeProcs, data: LOOPHOLE[c], refs: 0
]]];
};
TPPut: PROC[self: Pipe.Handle, area: Area.Handle] = {
c: CDataRef=LOOPHOLE[self.data];
-- could do some culling here
Pipe.Put[c.ppipe,area];
};
TPFree: PROC[self: Pipe.Handle] = {
c: CDataRef=LOOPHOLE[self.data];
Pipe.Free[@c.ipipe]; Pipe.Free[@c.opipe]; c.inuse←FALSE;
};

MakePipe: PROC[c: CDataRef] = {
ip: Pipe.Handle←NewIPipe[c];
op: Pipe.Handle←NewOPipe[c];
c.ppipe←Pipe.Ref[op];
FOR p: NodeRef←c.plist,p.next UNTIL p=NIL DO
c.ppipe←Patch.NewPipe[p.patch,Pipe.Ref[ip],c.ppipe];
ENDLOOP;
Pipe.Free[@ip]; Pipe.Free[@op];
c.tpipe←NewTPipe[c];
};

CNewPipe: PROC[self: Handle, ipipe,opipe: Pipe.Handle]
RETURNS[Pipe.Handle] = {
d: DataRef=self.data;
c: CDataRef=d.c;
IF c.inuse THEN ERROR ClipperError;
c.ipipe←ipipe; c.opipe←opipe;
c.state←[FALSE,FALSE]; c.inuse←TRUE;
RETURN[Pipe.Ref[c.tpipe]];
};

INewPipe: PROC[self: Handle, ipipe,opipe: Pipe.Handle]
RETURNS[Pipe.Handle] = {
Pipe.Free[@opipe]; RETURN[ipipe]
};

ONewPipe: PROC[self: Handle, ipipe,opipe: Pipe.Handle]
RETURNS[Pipe.Handle] = {
Pipe.Free[@ipipe]; RETURN[opipe]
};

CPush: PROC[self: Handle, area: Area.Handle] = {
d: DataRef=self.data;
c: CDataRef=d.c;
pipe: Pipe.Handle←CNewPipe[self,Pipe.Sink[],Pipe.Sink[]];
Pipe.Put[pipe,area]; Pipe.Free[@pipe];
d.state←c.state; d.level←d.level+1;
IF d.state=[TRUE,TRUE] THEN d.clevel←d.level
ELSE self.procs←(IF d.state.in THEN iProcs ELSE oProcs);
};

CPop: PROC[self: Handle] = {
d: DataRef=self.data;
IF d.level>1 THEN d.level←d.clevel←d.level-1;
};

IPush: PROC[self: Handle, area: Area.Handle] = {
d: DataRef=self.data;
Area.Free[@area]; d.level←d.level+1;
};

IPop: PROC[self: Handle] = {
d: DataRef=self.data;
IF d.level>1 THEN {
d.level←d.level-1;
IF d.level=d.clevel THEN { d.state←[TRUE,TRUE]; self.procs←cProcs };
};
};

CTest: PROC[self: Handle] RETURNS[State] = {
d: DataRef=self.data; RETURN[d.state]
};

regionProcs: LONG POINTER TO READONLY Pipe.Procs = zone.NEW[Pipe.Procs = [
Put: RPut, Free: RFree]];

CNewRegion: PROC[self: Handle] RETURNS[Pipe.Handle] = {
d: DataRef=self.data;
self.procs←oProcs; FreeCData[@d.c];
d↑←[level: 1, clevel: 0, state: [FALSE,FALSE], c: NewCData[]];
d.c.inuse←TRUE;
RETURN[zone.NEW[Pipe.Object ← [procs: regionProcs, data: LOOPHOLE[self]]]];
};

RPut: PROC[self: Pipe.Handle, area: Area.Handle] = {
clip: Handle=LOOPHOLE[self.data];
d: DataRef=clip.data;
c: CDataRef=d.c;
p: NodeRef=zone.NEW[Node ← [next: NIL, patch: Patch.New[area]]];
p.next←c.plist; c.plist←p;
};

RFree: PROC[self: Pipe.Handle] = {
clip: Handle=LOOPHOLE[self.data];
d: DataRef=clip.data;
c: CDataRef=d.c;
IF c.plist#NIL THEN {
MakePipe[c];
d.state←[TRUE,TRUE]; d.clevel←d.level;
clip.procs←cProcs;
};
c.inuse←FALSE;
zone.FREE[@self];
};

CCopy: PROC[self: Handle] RETURNS[Handle] = {
d: DataRef=self.data;
dd: DataRef=zone.NEW[Data←d↑];
c: CDataRef=dd.c; c.refs←c.refs+1;
RETURN[zone.NEW[Object ← [procs: self.procs, data: dd]]];
};

CFree: PROC[self: Handle] = {
d: DataRef←self.data;
FreeCData[@d.c]; zone.FREE[@d]; zone.FREE[@self];
};

}.