GLImpl.mesa - implements the graphics list interface
last changed by Mik Lamming, January 4, 1983 11:05 am
Last Edited by: Stone, March 18, 1983 2:15 pm
DIRECTORY
CGPath  USING [Copy],
GL,
Graphics  USING [ClipArea, ClipBox, Concat, Context, ContextRep, DrawArea, DrawBox, DrawImage, DrawRope, DrawStroke, DrawTo, GraphicsProcs, NewContext, nullMark, Save, Restore, SetColor, SetCP, SetFat, SetDefaultFont, SetPaintMode, Translate],
GraphicsBasic USING [Box, Color, FontRef, ImageRef, Mark, PaintMode, Path, StrokeEnds, Texture, YMode],
GraphicsOps USING [Disable, DrawTexturedBox, MoveDeviceRectangle, SetYMode],
Rope   USING [Concat, FromChar, ROPE];
GLImpl: CEDAR MONITOR
IMPORTS CGPath, Graphics, GraphicsOps, Rope
EXPORTS GL = {
Types
This is not a CedarGraphics context! It is a special structure created by the graphics list package. It holds the graphics list while it is being built and a pointer to a disabled screen context. The latter is used to hold CedarGraphics variables such as CP, which may be needed by the client while it is computing the list. Note that pointers to both the head and tail of the list are maintained.
Context: TYPE = REF ContextRec;
ContextRec: TYPE = RECORD [
context: Graphics.Context ← NIL,
head, tail: List ← NIL
];
This defines the range of nodes that comprise a graphics list. Note that there is one for each CedarGraphics function that modifies the graphics context.
CommandType: TYPE = {LockNode, SetCP, DrawTo, DrawStroke, DrawArea, DrawBox, DrawImage, Translate, Concat, SetColor, SetPaintMode, SetFat, SetDefaultFont, DrawChars, ClipArea, ClipBox, Save, Restore, DrawBits, SetYMode, DrawTexturedBox, Disable, MoveDeviceRectangle };
Graphics lists comprise a singly linked sequence of the following types of node. Every list has a LockNode at its head which contains the condition variables for ensuring sequential access.
List: PUBLIC TYPE = REF ListRec;
ListRec: PUBLIC TYPE = RECORD [
next: List,    -- pointer to the next mode in the graphics list
command: SELECT commandType: CommandType FROM
LockNode  => [locked:BOOLEAN, notInUse:CONDITION],
SetCP   => [x,y: REAL, rel: BOOLEAN],
DrawTo   => [x,y: REAL, rel: BOOLEAN], 
DrawStroke => [path: GraphicsBasic.Path, width: REAL, closed: BOOLEAN,
      ends: GraphicsBasic.StrokeEnds],
DrawArea => [path: GraphicsBasic.Path, parityFill: BOOLEAN],
DrawBox  => [box: GraphicsBasic.Box],
DrawImage => [image: GraphicsBasic.ImageRef, raw: BOOLEAN],
Translate  => [tx,ty: REAL, round: BOOLEAN],
Concat  => [m11,m12,m21,m22: REAL],
SetColor  => [color: GraphicsBasic.Color],
SetPaintMode => [mode: GraphicsBasic.PaintMode],
SetFat   => [fat: BOOLEAN],
SetDefaultFont => [font: GraphicsBasic.FontRef],
DrawChars => [font: GraphicsBasic.FontRef, rope: Rope.ROPE], --N.B., this doesn't match call args
ClipArea  => [path: GraphicsBasic.Path, parityFill: BOOLEAN, exclude: BOOLEAN],
ClipBox  => [box: GraphicsBasic.Box, exclude: BOOLEAN],
Save   => [mark: CARDINAL],
Restore  => [mark: CARDINAL],
DrawBits  => [base: LONG POINTER, raster: CARDINAL, bitsPerPixel: [0..16),
      x, y, w, h: CARDINAL, xorigin, yorigin: INTEGER],
SetYMode  => [mode: GraphicsBasic.YMode],
DrawTexturedBox => [box: GraphicsBasic.Box, texture: GraphicsBasic.Texture],
Disable  => [],
MoveDeviceRectangle => [width, height, fromX, fromY, toX, toY: NAT]
ENDCASE
];
Globals
This vector of procedure calls gets plugged into the regular CedarGraphics context structure which is returned to the client by the procedure GL.Start. These preocedures effectively ambush calls to CedarGraphics and construct list nodes.
glProcs: REF Graphics.GraphicsProcs ← NEW[Graphics.GraphicsProcs ← [
GetCP: AmbushGetCP,
SetCP: AmbushSetCP,
DrawTo: AmbushDrawTo,
DrawStroke: AmbushDrawStroke,
DrawArea: AmbushDrawArea,
DrawBox:AmbushDrawBox,
DrawImage: AmbushDrawImage,
Translate: AmbushTranslate,
Concat: AmbushConcat,
WorldToUser: AmbushWorldToUser,
UserToWorld: AmbushUserToWorld,
SetColor: AmbushSetColor,
GetColor: AmbushGetColor,
SetPaintMode: AmbushSetPaintMode,
SetFat: AmbushSetFat,
GetDefaultFont: AmbushGetDefaultFont,
SetDefaultFont: AmbushSetDefaultFont,
DrawChars: AmbushDrawChars,
ClipArea: AmbushClipArea,
ClipBox: AmbushClipBox,
IsPointVisible: AmbushIsPointVisible,
IsRectangular: AmbushIsRectangular,
GetBounds: AmbushGetBounds,
Visible: AmbushVisible,
Save: AmbushSave,
Restore: AmbushRestore,
DrawBits: AmbushDrawBits,
UserToDevice: AmbushUserToDevice,
DeviceToUser: AmbushDeviceToUser,
GetYMode: AmbushGetYMode,
SetYMode: AmbushSetYMode,
DrawTexturedBox: AmbushDrawTexturedBox,
Disable: AmbushDisable,
MoveDeviceRectangle: AmbushMoveDeviceRectangle
]];
Client Interface Procedures
Start: PUBLIC PROC[oldList: List ← NIL] RETURNS[dc:Graphics.Context] = {
Creates an ambushing context (dc). dc is a record which looks like a CedarGraphics context. The procs portion points to the GraphicsList ambushing procedures. The data portion points to a record containing the graphics list under construction and a pointer to a regular but disabled CedarGraphics context. The graphics list is monitored!
context: Context;
tail: List;
newDc: Graphics.Context ← Graphics.NewContext[]; -- get a new screen context
IF oldList = NIL THEN oldList ← NEW[LockNode ListRec]; -- client wants new list
Lock[oldList]; -- prevent other reads or writes
GraphicsOps.Disable[newDc]; -- only want its variables not its output
FOR tail ← oldList, tail.next WHILE tail.next # NIL DO --find the tail for appends
ENDLOOP;
Make a special structure to hold the list and the dummy context for interpretation by ambushing routines.
context ← NEW[ContextRec ← [context: newDc, head: oldList, tail: tail]];
Now fake a new CG context, plug the special structure into the data field and pass it back to the client
dc ← NEW[Graphics.ContextRep ← [procs:glProcs, data:context]];
};
End: PUBLIC PROC[dc: Graphics.Context] RETURNS [list: List] = {
Extracts the current list from the context and returns it to the user.
IF dc.procs # glProcs THEN
RETURN[NIL];
list ← NARROW[dc.data, Context].head;
UnLock[list]; -- free it up for the next client
};

Draw: PUBLIC PROC[context: Graphics.Context, list: List] = TRUSTED {
Display a graphics list. But only if it is not being updated
head:List ← list;
Lock[head]; -- prevent other reads or writes
WHILE list # NIL DO -- execute each node on the list
WITH node:list SELECT FROM
SetCP =>
Graphics.SetCP[context, node.x, node.y, node.rel];
DrawTo =>
Graphics.DrawTo[context, node.x, node.y, node.rel];
DrawStroke =>
Graphics.DrawStroke[context, node.path, node.width, node.closed, node.ends];
DrawArea =>
Graphics.DrawArea[context, node.path, node.parityFill];
DrawBox =>
Graphics.DrawBox[context, node.box];
DrawImage =>
Graphics.DrawImage[context, node.image, node.raw];
Translate =>
Graphics.Translate[context, node.tx, node.ty, node.round];
Concat =>
Graphics.Concat[context, node.m11, node.m12, node.m21, node.m22];
SetColor =>
Graphics.SetColor[context, node.color];
SetPaintMode =>
[] ← Graphics.SetPaintMode[context, node.mode];
SetFat =>
[] ← Graphics.SetFat[context, node.fat];
SetDefaultFont =>
Graphics.SetDefaultFont[context, node.font];
DrawChars =>
Graphics.DrawRope[self: context, rope: node.rope, font: node.font];
ClipArea =>
Graphics.ClipArea[context, node.path, node.parityFill, node.exclude];
ClipBox =>
Graphics.ClipBox[context, node.box, node.exclude];
Save =>
[] ← Graphics.Save[context];
Restore =>
Graphics.Restore[context];
DrawBits =>
Graphics.DrawBits[context, node.base, node.raster, node.bitsPerPixel, node.x, node.y, node.w, node.h, node.xorigin, node.yorigin];
SetYMode =>
GraphicsOps.SetYMode[context, node.mode];
DrawTexturedBox =>
GraphicsOps.DrawTexturedBox[context, node.box, node.texture];
Disable =>
GraphicsOps.Disable[context];
MoveDeviceRectangle =>
GraphicsOps.MoveDeviceRectangle[context, node.width, node.height, node.fromX, node.fromY, node.toX, node.toY];
ENDCASE;
list ← list.next;
ENDLOOP;
UnLock[head]; -- free it up for the next client
};
Internal Monitor Procedures
Lock: ENTRY PROC[list: List] = TRUSTED {
Mark a graphics list as 'In Use' and set its monitor lock
WITH node:list SELECT FROM
LockNode => {
WHILE node.locked DO
WAIT node.notInUse;
ENDLOOP;
node.locked ← TRUE;
};
ENDCASE => ERROR;
};
UnLock: ENTRY PROC[list: List] = TRUSTED {
Mark a graphics list as 'not in use' and clear its monitor lock
WITH node:list SELECT FROM
LockNode => {
node.locked ← FALSE;
NOTIFY node.notInUse;
};
ENDCASE => ERROR;
};
Ambushing Procedures
AmbushGetCP: PUBLIC PROC[self: Graphics.Context, rounded: BOOLEAN] RETURNS[x,y: REAL] = {
glContext: Context ← NARROW[self.data];
RETURN glContext.context.procs.GetCP[glContext.context, rounded];
};
AmbushSetCP: PUBLIC PROCEDURE[self: Graphics.Context, x,y: REAL, rel: BOOLEANFALSE] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.SetCP[glContext.context, x, y, rel];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: SetCP [x: x, y: y, rel: rel]]];
};
AmbushDrawTo: PUBLIC PROCEDURE[self: Graphics.Context, x,y: REAL, rel: BOOLEANFALSE] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.DrawTo[glContext.context, x, y, rel];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: DrawTo [x: x, y: y, rel: rel]]];
};
AmbushDrawStroke: PUBLIC PROCEDURE[self: Graphics.Context, path: GraphicsBasic.Path, width: REAL, closed: BOOLEAN, ends: GraphicsBasic.StrokeEnds] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.DrawStroke[glContext.context, path, width, closed, ends];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command:DrawStroke [path: CGPath.Copy[path], width: width, closed: closed, ends: ends]]];
};
AmbushDrawArea: PUBLIC PROCEDURE[self: Graphics.Context, path: GraphicsBasic.Path, parityFill: BOOLEANFALSE] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.DrawArea[glContext.context, path, parityFill];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command:DrawArea [path: CGPath.Copy[path], parityFill: parityFill]]];
};
AmbushDrawBox: PUBLIC PROCEDURE[self: Graphics.Context, box: GraphicsBasic.Box] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.DrawBox[glContext.context, box];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: DrawBox [box: box]]];
};
AmbushDrawImage: PUBLIC PROCEDURE[self: Graphics.Context, image: GraphicsBasic.ImageRef, raw: BOOLEAN] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.DrawImage[glContext.context, image, raw];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: DrawImage [image: image, raw: raw]]];
};
AmbushTranslate: PUBLIC PROC[self: Graphics.Context, tx,ty: REAL, round: BOOLEANFALSE] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.Translate[glContext.context, tx, ty, round];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: Translate [tx: tx, ty: ty, round: round]]];
};
AmbushConcat: PUBLIC PROCEDURE[self: Graphics.Context, m11,m12,m21,m22: REAL] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.Concat[glContext.context, m11, m12, m21, m22];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: Concat [m11: m11, m12: m12, m21: m21, m22: m22]]];
};
AmbushWorldToUser: PUBLIC PROC[self: Graphics.Context, wx,wy: REAL] RETURNS[x,y: REAL] = {
glContext: Context ← NARROW[self.data];
RETURN glContext.context.procs.WorldToUser[glContext.context, wx, wy];
};
AmbushUserToWorld: PUBLIC PROC[self: Graphics.Context, x,y: REAL] RETURNS[wx,wy: REAL] = {
glContext: Context ← NARROW[self.data];
RETURN glContext.context.procs.UserToWorld[glContext.context, x, y];
};
AmbushSetColor: PUBLIC PROCEDURE[self: Graphics.Context, color: GraphicsBasic.Color] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.SetColor[glContext.context, color];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: SetColor [color: color]]];
};
AmbushGetColor: PUBLIC PROC[self: Graphics.Context] RETURNS[GraphicsBasic.Color] = {
glContext: Context ← NARROW[self.data];
RETURN glContext.context.procs.GetColor[glContext.context];
};
AmbushSetPaintMode: PUBLIC PROC[self: Graphics.Context, mode: GraphicsBasic.PaintMode] RETURNS[GraphicsBasic.PaintMode] = {
glContext: Context ← NARROW[self.data];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: SetPaintMode [mode: mode]]];
RETURN glContext.context.procs.SetPaintMode[glContext.context, mode];
};
AmbushSetFat: PUBLIC PROCEDURE[self: Graphics.Context, fat: BOOLEAN] RETURNS[BOOLEAN] = {
glContext: Context ← NARROW[self.data];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: SetFat [fat: fat]]];
RETURN glContext.context.procs.SetFat[glContext.context, fat];
};
AmbushGetDefaultFont: PUBLIC PROC[self: Graphics.Context] RETURNS[GraphicsBasic.FontRef] = {
glContext: Context ← NARROW[self.data];
RETURN glContext.context.procs.GetDefaultFont[glContext.context];
};

AmbushSetDefaultFont: PUBLIC PROC[self: Graphics.Context, font: GraphicsBasic.FontRef] = {
glContext: Context ← NARROW[self.data];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: SetDefaultFont [font: font]]];
glContext.context.procs.SetDefaultFont[glContext.context, font];
};
AmbushDrawChars: PUBLIC PROC[self: Graphics.Context, font: GraphicsBasic.FontRef, map: PROC[SAFE PROC[CHAR] RETURNS[BOOL]]] = {
glContext: Context ← NARROW[self.data];
r: Rope.ROPE;
MakeRope: SAFE PROC[c: CHAR] RETURNS[BOOL] = CHECKED {
r ← Rope.Concat[r, Rope.FromChar[c]];
RETURN[FALSE]};
map[MakeRope];
glContext.context.procs.DrawChars[glContext.context, font, map];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: DrawChars [font: font, rope: r]]];
};
AmbushClipArea: PUBLIC PROCEDURE[self: Graphics.Context, path: GraphicsBasic.Path, parityFill: BOOLEAN, exclude: BOOLEAN] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.ClipArea[glContext.context, path, parityFill, exclude];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: ClipArea [path: CGPath.Copy[path], parityFill: parityFill, exclude: exclude]]];
};
AmbushClipBox: PUBLIC PROCEDURE[self: Graphics.Context, box: GraphicsBasic.Box,
exclude: BOOLEANFALSE] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.ClipBox[glContext.context, box, exclude];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: ClipBox [box: box, exclude: exclude]]];
};
AmbushIsPointVisible: PUBLIC PROC[self: Graphics.Context, x,y: REAL] RETURNS[BOOLEAN] = {
glContext: Context ← NARROW[self.data];
RETURN[glContext.context.procs.IsPointVisible[glContext.context, x, y]];
};
AmbushIsRectangular: PUBLIC PROC[self: Graphics.Context] RETURNS[BOOLEAN] = {
glContext: Context ← NARROW[self.data];
RETURN[glContext.context.procs.IsRectangular[glContext.context]];
};
AmbushGetBounds: PUBLIC PROC[self: Graphics.Context] RETURNS[GraphicsBasic.Box] = {
glContext: Context ← NARROW[self.data];
RETURN[glContext.context.procs.GetBounds[glContext.context]];
};
AmbushVisible: PUBLIC PROC[self: Graphics.Context] RETURNS[BOOLEAN] = {
glContext: Context ← NARROW[self.data];
RETURN[glContext.context.procs.Visible[glContext.context]];
};
AmbushSave: PUBLIC PROCEDURE[self: Graphics.Context] RETURNS[GraphicsBasic.Mark] = {
glContext: Context ← NARROW[self.data];
mark: GraphicsBasic.Mark ← glContext.context.procs.Save[glContext.context];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: Save [mark: 0]]];
RETURN[mark];
};
AmbushRestore: PUBLIC PROCEDURE[self: Graphics.Context, mark: GraphicsBasic.Mark ← Graphics.nullMark] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.Restore[glContext.context, mark];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: Restore [mark: 0]]];
};
AmbushDrawBits: PUBLIC UNSAFE PROC[self: Graphics.Context,
base: LONG POINTER, raster: CARDINAL, bitsPerPixel: [0..16),
x, y, w, h: CARDINAL, xorigin, yorigin: INTEGER] = TRUSTED {
glContext: Context ← NARROW[self.data];
glContext.context.procs.DrawBits[glContext.context, base, raster, bitsPerPixel, x, y, w, h, xorigin, yorigin];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: DrawBits [base: base, raster: raster, bitsPerPixel: bitsPerPixel, x: x, y: y, w: w, h: h, xorigin: xorigin, yorigin: yorigin]]];
};

AmbushUserToDevice: PUBLIC PROC[self: Graphics.Context, x, y: REAL, rel: BOOLEAN] RETURNS[tx, ty: REAL] = {
glContext: Context ← NARROW[self.data];
RETURN glContext.context.procs.UserToDevice[glContext.context, x, y, rel];
};
AmbushDeviceToUser: PUBLIC PROC[self: Graphics.Context, tx, ty: REAL, rel: BOOLEAN] RETURNS[x, y: REAL] = {
glContext: Context ← NARROW[self.data];
RETURN glContext.context.procs.DeviceToUser[glContext.context, tx, ty, rel];
};
AmbushGetYMode: PUBLIC PROC[self: Graphics.Context] RETURNS[GraphicsBasic.YMode] = {
glContext: Context ← NARROW[self.data];
RETURN[glContext.context.procs.GetYMode[glContext.context]];
};
AmbushSetYMode: PUBLIC PROC[self: Graphics.Context, mode: GraphicsBasic.YMode] = {
glContext: Context ← NARROW[self.data];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: SetYMode [mode: mode]]];
glContext.context.procs.SetYMode[glContext.context, mode];
};
AmbushDrawTexturedBox: PUBLIC PROC[self: Graphics.Context, box: GraphicsBasic.Box, texture: GraphicsBasic.Texture] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.DrawTexturedBox[glContext.context, box, texture];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: DrawTexturedBox [box: box, texture: texture]]];
};
AmbushDisable: PUBLIC PROC[self: Graphics.Context] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.Disable[glContext.context];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: Disable []]];
};
AmbushMoveDeviceRectangle: PUBLIC PROC[self: Graphics.Context, width, height, fromX, fromY, toX, toY: NAT] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.MoveDeviceRectangle[glContext.context, width, height, fromX, fromY, toX, toY];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: MoveDeviceRectangle[width: width, height: height, fromX: fromX, fromY: fromY, toX: toX, toY: toY]]];
};
}.