TerminalImpl.mesa
last edited by: Levin on: June 30, 1983 3:52 pm
DIRECTORY
DisplayFace USING [
Connect, cursorPosition, Disconnect, GetBitBltTable, globalStateSize, hasBorder, hasBuffer, height, Initialize, InitializeCleanup, pagesForBitmap, pixelsPerInch, SetBackground, SetBorderPattern, SetCursorPattern, TurnOff, TurnOn, width],
KeyboardFace USING [keyboard],
Keys USING [KeyBits],
MouseFace USING [position, SetPosition],
PrincOps USING [BBTable, BYTE],
PrincOpsUtils USING [AllocateNakedCondition],
Process USING [
Detach, GetPriority, MsecToTicks, Pause, Priority, priorityForeground, SetPriority, Ticks],
Terminal USING [
BitmapState, BWBackground, BWCursorBitmap, Position, SetBWCursorPosition, SetMousePosition, SwapAction, SwapNotifier, Object, Virtual],
VM USING [Allocate, CantAllocate, Free, Interval, lowCore, Pin, Unpin],
VMSideDoor USING [AssignSpecialRealMemory, ReleaseSpecialRealMemory];
TerminalImpl: CEDAR MONITOR LOCKS terminalLock
IMPORTS
DisplayFace, KeyboardFace, MouseFace, PrincOpsUtils, Process, Terminal, VM, VMSideDoor
EXPORTS Terminal
SHARES Terminal =
BEGIN
Types
Virtual: TYPE = Terminal.Virtual;
TerminalList: TYPE = LIST OF Virtual;
VirtualImpl: TYPE = REF VirtualImplObject;
VirtualImplObject: TYPE = RECORD [
keyboard: Keys.KeyBits ← ALL[up],
mouse: Terminal.Position ← [0, 0],
cursor: Terminal.Position ← [0, 0],
pattern: Terminal.BWCursorBitmap ← ALL[0],
bwBitmapState: Terminal.BitmapState ← none,
bwBackground: Terminal.BWBackground ← white,
bwBorderOdd: PrincOps.BYTE ← 0,
bwBorderEven: PrincOps.BYTE ← 0,
bwBitmap: VM.Interval ← NULL,
bwSavedBitMap: VM.Interval ← NULL,
notifiers: Notifier ← NIL
];
Notifier: TYPE = REF NotifierItem;
NotifierItem: TYPE = RECORD [
next, prev: Notifier,
proc: Terminal.SwapNotifier
];
priorityDuringSelect: Process.Priority = Process.priorityForeground;
blinkTime: Process.Ticks = Process.MsecToTicks[100];
Global variables protected by the monitor lock
terminalLock: PUBLIC MONITORLOCK;
terminals: TerminalList;
current: Virtual ← NIL;
lockDepth: NAT ← 0;
terminalChanging: BOOLFALSE;
terminalStable: CONDITION ← [timeout: 0];
bwRetrace: LONG POINTER TO CONDITION;
meFirst: BOOLTRUE;
verticalRetrace: CONDITION ← [timeout: 0];
Exports to Terminal
CantDoIt: PUBLIC ERROR = CODE;
Create: PUBLIC PROC RETURNS [vt: Virtual] = {
AddToList: ENTRY PROC [vt: Virtual] = INLINE {terminals ← CONS[vt, terminals]};
AddToList[vt ← NewVirtual[]];
};
Select: PUBLIC PROC [vt: Virtual ← NIL, lock: BOOLFALSE] RETURNS [worked: BOOL] = {
old: Virtual ← current;
new: Virtual ← vt;
AcquisitionOutcome: TYPE = {cantSelect, correctTerminal, swapNeeded};
AcquireForSelect: ENTRY PROC RETURNS [AcquisitionOutcome] = INLINE {
WHILE terminalChanging DO WAIT terminalStable; ENDLOOP;
IF new = NIL THEN
FOR t: TerminalList ← terminals, terminals.rest UNTIL t = NIL DO
IF t.first = current THEN {
new ← (IF t.rest = NIL THEN terminals ELSE t.rest).first;
EXIT
};
REPEAT
FINISHED => ERROR;
ENDLOOP;
IF lockDepth > 0 AND old ~= new THEN RETURN[cantSelect];
IF lock THEN lockDepth ← lockDepth + 1;
RETURN[IF (terminalChanging ← old ~= new) THEN swapNeeded ELSE correctTerminal]
};
NotifyNewTerminal: ENTRY PROC = INLINE {
terminalChanging ← FALSE;
BROADCAST terminalStable;
};
DoNotifiers: PROC [vt: Virtual, action: Terminal.SwapAction] = {
impl: VirtualImpl = NARROW[vt.impl];
head: Notifier = impl.notifiers;
n: Notifier ← impl.notifiers;
IF head = NIL THEN RETURN;
DO
n.proc[vt, action];
IF (n ← IF action = coming THEN n.next ELSE n.prev) = head THEN EXIT;
ENDLOOP;
};
FlipTerminal: ENTRY PROC [old, new: Virtual] = INLINE {
UnloadTerminal[old];
current ← new;
LoadTerminal[new];
};
SELECT AcquireForSelect[] FROM
cantSelect => RETURN[FALSE];
correctTerminal => NULL;
swapNeeded => {
priority: Process.Priority = Process.GetPriority[];
IF priority < priorityDuringSelect THEN
TRUSTED {Process.SetPriority[priorityDuringSelect]};
DoNotifiers[old, going];
DoNotifiers[new, coming];
FlipTerminal[old: old, new: new];
DoNotifiers[old, gone];
DoNotifiers[new, here];
IF priority < priorityDuringSelect THEN
TRUSTED {Process.SetPriority[priority]};
NotifyNewTerminal[];
};
ENDCASE;
RETURN[TRUE]
};
Current: PUBLIC ENTRY PROC RETURNS [vt: Virtual, lockCount: NAT] = {
WaitUntilStable[];
RETURN [vt: current, lockCount: lockDepth]
};
PermitSwaps: PUBLIC ENTRY PROC [vt: Virtual] = {
IF current ~= vt OR lockDepth = 0 THEN RETURN;
lockDepth ← lockDepth - 1;
};
PreventSwaps: PUBLIC ENTRY PROC [vt: Virtual] = {
WaitUntilSelected[vt];
lockDepth ← lockDepth + 1;
};
RegisterNotifier: PUBLIC ENTRY PROC [vt: Virtual, notifier: Terminal.SwapNotifier] = {
impl: VirtualImpl = NARROW[vt.impl];
n: Notifier = NEW[NotifierItem ← [next: NIL, prev: NIL, proc: notifier]];
head: Notifier = impl.notifiers;
IF head = NIL THEN n.next ← n.prev ← n
ELSE {n.next ← head; n.prev ← head.prev; head.prev.next ← n; head.prev ← n};
impl.notifiers ← n;
};
UnregisterNotifier: PUBLIC ENTRY PROC [vt: Virtual, notifier: Terminal.SwapNotifier] = {
impl: VirtualImpl = NARROW[vt.impl];
head: Notifier = impl.notifiers;
n: Notifier ← impl.notifiers;
IF head ~= NIL THEN
DO
IF n.proc = notifier THEN {
n.prev.next ← n.next;
n.next.prev ← n.prev;
IF n = head THEN impl.notifiers ← IF n.next = n THEN NIL ELSE n.next;
EXIT
};
IF (n ← n.next) = head THEN EXIT;
ENDLOOP;
};
KeyboardWatcher: PROC = TRUSTED {
KeysRecord: TYPE = RECORD [
SELECT OVERLAID * FROM
bits => [bits: Keys.KeyBits],
words => [words: ARRAY [0..SIZE[Keys.KeyBits]) OF WORD],
ENDCASE
];
keyboard: LONG POINTER TO KeysRecord = LOOPHOLE[KeyboardFace.keyboard];
waitTime: Process.Ticks = Process.MsecToTicks[300];
lastTime: KeysRecord ← keyboard^;
In the following, [bits[bits: ALL[up]]] doesn't quite work, since the number of bits in a Keys.KeyBits isn't evenly divisible by bitsPerWord, causing the excess bits in the last word not to be initialized. This leads to trouble in the word-oriented comparisons in the main loop.
magicCombination: KeysRecord ← [words[words: ALL[177777B]]];
magicCombination.bits[Ctrl] ← magicCombination.bits[LeftShift] ←
magicCombination.bits[RightShift] ← down;
Process.SetPriority[Process.priorityForeground];
DO
Process.Pause[waitTime];
IF lockDepth = 0 THEN {
thisTime: KeysRecord = keyboard^;
changed: BOOLFALSE;
select: BOOLTRUE;
FOR word: NAT IN [0..SIZE[KeysRecord]) DO
thisWord: WORD = thisTime.words[word];
IF thisWord ~= lastTime.words[word] THEN changed ← TRUE;
IF thisWord ~= magicCombination.words[word] THEN select ← FALSE;
ENDLOOP;
IF changed THEN {
lastTime ← thisTime;
IF select THEN [] ← Select[NIL];
};
};
ENDLOOP;
};
Mouse
SetMousePosition: PUBLIC ENTRY PROC [vt: Virtual, position: Terminal.Position] = {
impl: VirtualImpl = NARROW[vt.impl];
IF current = vt THEN MouseFace.SetPosition[LOOPHOLE[position]]
ELSE impl.mouse ← position;
};
Black-and-White Cursor
GetBWCursorPattern: PUBLIC ENTRY PROC [vt: Virtual]
RETURNS [pattern: Terminal.BWCursorBitmap] = {
impl: VirtualImpl = NARROW[vt.impl];
RETURN[impl.pattern]
};
SetBWCursorPattern: PUBLIC ENTRY PROC [vt: Virtual, pattern: Terminal.BWCursorBitmap] = {
impl: VirtualImpl = NARROW[vt.impl];
impl.pattern ← pattern;
IF current = vt THEN TRUSTED {DisplayFace.SetCursorPattern[LOOPHOLE[LONG[@pattern]]]};
};
Black-and-White Display
GetBWBitmapState: PUBLIC ENTRY PROC [vt: Virtual] RETURNS [Terminal.BitmapState] = {
impl: VirtualImpl = NARROW[vt.impl];
RETURN [impl.bwBitmapState]
};
SetBWBitmapState: PUBLIC ENTRY PROC [vt: Virtual, new: Terminal.BitmapState]
RETURNS [old: Terminal.BitmapState] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl = NARROW[vt.impl];
TurnOff: INTERNAL PROC = {
DisplayFace.TurnOff[];
lockDepth ← lockDepth + 1;
WaitForBWVerticalRetraceInternal[vt];
WaitForBWVerticalRetraceInternal[vt];
lockDepth ← lockDepth - 1;
IF DisplayFace.hasBuffer THEN VMSideDoor.ReleaseSpecialRealMemory[impl.bwBitmap]
ELSE VM.Unpin[impl.bwBitmap];
};
IF (old ← impl.bwBitmapState) = new THEN RETURN;
IF old = none THEN {
impl.bwBitmap ← AllocateBWBitmap[];
IF current = vt THEN DisplayFace.Connect[impl.bwBitmap.page];
IF (impl.bwBitmapState ← new) = allocated THEN RETURN;
};
SELECT new FROM
displayed =>
IF current = vt THEN {
IF DisplayFace.hasBuffer THEN VMSideDoor.AssignSpecialRealMemory[impl.bwBitmap]
ELSE VM.Pin[impl.bwBitmap];
DisplayFace.TurnOn[];
};
allocated => IF current = vt THEN TurnOff[];
none => {
IF current = vt THEN {TurnOff[]; DisplayFace.Disconnect[]};
TRUSTED {VM.Free[impl.bwBitmap]};
};
ENDCASE;
impl.bwBitmapState ← new;
};
GetBWBackground: PUBLIC ENTRY PROC [vt: Virtual] RETURNS [Terminal.BWBackground] = {
impl: VirtualImpl = NARROW[vt.impl];
RETURN [impl.bwBackground]
};
SetBWBackground: PUBLIC ENTRY PROC [vt: Virtual, new: Terminal.BWBackground]
RETURNS [old: Terminal.BWBackground] = {
impl: VirtualImpl = NARROW[vt.impl];
IF (old ← impl.bwBackground) = new THEN RETURN;
impl.bwBackground ← new;
IF vt = current THEN
DisplayFace.SetBackground[IF new = white THEN white ELSE black];
};
GetBWBorder: PUBLIC ENTRY PROC [vt: Virtual]
RETURNS [oddPairs, evenPairs: PrincOps.BYTE] = {
impl: VirtualImpl = NARROW[vt.impl];
RETURN [impl.bwBorderOdd, impl.bwBorderEven]
};
SetBWBorder: PUBLIC ENTRY PROC [vt: Virtual, oddPairs, evenPairs: PrincOps.BYTE] = {
impl: VirtualImpl = NARROW[vt.impl];
impl.bwBorderOdd ← oddPairs;
impl.bwBorderEven ← evenPairs;
IF DisplayFace.hasBorder AND vt = current THEN
DisplayFace.SetBorderPattern[oddPairs: oddPairs, evenPairs: evenPairs];
};
GetBitBltTable: PUBLIC ENTRY UNSAFE PROC [vt: Virtual]
RETURNS [bbt: PrincOps.BBTable] = {
RETURN[DisplayFace.GetBitBltTable[]]
};
WaitForBWVerticalRetrace: PUBLIC ENTRY PROC [vt: Virtual] = {
WaitUntilSelected[vt];
WaitForBWVerticalRetraceInternal[vt];
};
BlinkBWDisplay: PUBLIC PROC [vt: Virtual] = {
background: Terminal.BWBackground;
PreventSwapsCurrent[];
background ← NARROW[vt.impl, VirtualImpl].bwBackground;
DisplayFace.SetBackground[IF background = white THEN black ELSE white];
Process.Pause[blinkTime];
DisplayFace.SetBackground[IF background = white THEN white ELSE black];
PermitSwapsCurrent[];
};
Internal Procedures
WaitUntilStable: INTERNAL PROC = {
WHILE terminalChanging DO WAIT terminalStable; ENDLOOP;
};
WaitUntilSelected: INTERNAL PROC [vt: Virtual] = {
UNTIL current = vt DO WAIT terminalStable; ENDLOOP;
};
PreventSwapsCurrent: ENTRY PROC = INLINE {
WaitUntilStable[];
lockDepth ← lockDepth + 1;
};
PermitSwapsCurrent: ENTRY PROC = INLINE {
lockDepth ← lockDepth - 1;
};
WaitForBWVerticalRetraceInternal: INTERNAL PROC [vt: Virtual] = {
IF meFirst THEN {
meFirst ← FALSE;
TRUSTED {WAIT bwRetrace^};
BROADCAST verticalRetrace;
meFirst ← TRUE;
}
ELSE WAIT verticalRetrace;
};
LoadTerminal: PROC [vt: Virtual] = TRUSTED {
vtImpl: VirtualImpl = NARROW[vt.impl];
vt.keyboard ← LOOPHOLE[KeyboardFace.keyboard];
vt.mouse ← LOOPHOLE[MouseFace.position];
MouseFace.SetPosition[LOOPHOLE[vtImpl.mouse]];
vt.bwCursor ← LOOPHOLE[DisplayFace.cursorPosition];
vt.bwCursor^ ← LOOPHOLE[vtImpl.cursor];
DisplayFace.SetBackground[IF vtImpl.bwBackground = white THEN white ELSE black];
DisplayFace.SetBorderPattern[oddPairs: vtImpl.bwBorderOdd, evenPairs: vtImpl.bwBorderEven];
DisplayFace.SetCursorPattern[@vtImpl.pattern];
SELECT vtImpl.bwBitmapState FROM
displayed => {
DisplayFace.Connect[vtImpl.bwBitmap.page];
IF DisplayFace.hasBuffer THEN VMSideDoor.AssignSpecialRealMemory[vtImpl.bwBitmap]
ELSE VM.Pin[vtImpl.bwBitmap];
DisplayFace.TurnOn[];
};
allocated => {
DisplayFace.Connect[vtImpl.bwBitmap.page];
IF DisplayFace.hasBuffer THEN VMSideDoor.AssignSpecialRealMemory[vtImpl.bwBitmap];
};
none => NULL;
ENDCASE;
};
UnloadTerminal: PROC [vt: Virtual] = TRUSTED {
vtImpl: VirtualImpl = NARROW[vt.impl];
vtImpl.keyboard ← LOOPHOLE[KeyboardFace.keyboard^];
vt.keyboard ← @vtImpl.keyboard;
vtImpl.mouse ← LOOPHOLE[MouseFace.position^];
vt.mouse ← @vtImpl.mouse;
vtImpl.cursor ← LOOPHOLE[DisplayFace.cursorPosition^];
vt.bwCursor ← @vtImpl.cursor;
SELECT vtImpl.bwBitmapState FROM
displayed => {
DisplayFace.TurnOff[];
IF DisplayFace.hasBuffer THEN VMSideDoor.ReleaseSpecialRealMemory[vtImpl.bwBitmap]
ELSE VM.Unpin[vtImpl.bwBitmap];
DisplayFace.Disconnect[];
};
allocated => {
IF DisplayFace.hasBuffer THEN VMSideDoor.ReleaseSpecialRealMemory[vtImpl.bwBitmap];
DisplayFace.Disconnect[];
};
none => NULL;
ENDCASE;
};
NewVirtual: PROC RETURNS [vt: Virtual] = {
vtImpl: VirtualImpl = NEW[VirtualImplObject ← []];
TRUSTED {
vt ← NEW[Terminal.Object ← [
keyboard: LOOPHOLE[@vtImpl.keyboard],
mouse: LOOPHOLE[@vtImpl.mouse],
hasBlackAndWhiteDisplay: TRUE,
bwWidth: DisplayFace.width,
bwHeight: DisplayFace.height,
bwHasBorder: DisplayFace.hasBorder,
bwPixelsPerInch: DisplayFace.pixelsPerInch,
bwCursor: LOOPHOLE[@vtImpl.cursor],
hasColorDisplay: FALSE,
colorWidth: 0,
colorHeight: 0,
colorPixelsPerInch: 0,
colorDisplayType: NULL,
colorMode: [FALSE, 0, 0],
colorBitmapA: NIL,
colorBitmapB: NIL,
colorWordsPerLineA: 0,
colorWordsPerLineB: 0,
impl: LOOPHOLE[vtImpl]
]];
};
};
AllocateBWBitmap: PROC RETURNS [interval: VM.Interval] = {
interval ←
VM.Allocate[count: DisplayFace.pagesForBitmap, in64K: TRUE
! VM.CantAllocate => GO TO cant];
EXITS
cant => ERROR CantDoIt;
};
Initialization
Initialize: PROC = TRUSTED {
WordSequence: TYPE = RECORD [SEQUENCE COMPUTED CARDINAL OF WORD];
headGlobalP: LONG POINTER = VM.lowCore.NEW[WordSequence[DisplayFace.globalStateSize]];
mask: WORD;
vt: Virtual = NewVirtual[];
initialPosition: Terminal.Position;
[bwRetrace, mask] ← PrincOpsUtils.AllocateNakedCondition[];
DisplayFace.Initialize[headGlobalP, mask];
DisplayFace.InitializeCleanup[];
initialPosition ← [x: vt.bwWidth/2, y: vt.bwHeight/2];
Terminal.SetMousePosition[vt, initialPosition];
Terminal.SetBWCursorPosition[vt, initialPosition];
LoadTerminal[current ← vt];
terminals ← CONS[current, NIL];
Process.Detach[FORK KeyboardWatcher[]];
};
Initialize[];
END.