TerminalImpl.mesa
last edited by: Levin on: December 15, 1983 4:31 pm
Last Edited by: Birrell, November 16, 1983 11:24 am
Last Edited by: Wyatt, December 21, 1983 1:19 pm
implement color display
Last Edited by: Russ Atkinson, December 21, 1983 5:17 pm
fix problems with ShowColorCursor & HideColorCursor
DIRECTORY
Basics USING [bitsPerWord, LongMult],
ColorDisplayFace USING [
baseA, baseB, bplA, bplB, Connect, Disconnect, displayType, GetBlueMap, GetColor, GetGreenMap, GetRedMap, globalStateSize, HasMode, height, Initialize, InitializeCleanup, Mode, PagesForMode, pixelsPerInch, SetBlueMap, SetColor, SetGreenMap, SetRedMap, Show, TurnOff, TurnOn, width],
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, BBTableSpace, BitAddress, BitBltTablePtr],
PrincOpsUtils USING [AlignedBBTable, AllocateNakedCondition, BITBLT],
Process USING [
Detach, EnableAborts, GetPriority, MsecToTicks, Pause, Priority, priorityForeground, SetPriority, Ticks],
Terminal USING [
BitmapState, BWBackground, BWBorder, BWCursorBitmap, ChannelsVisible, ChannelValue, Color, ColorCursorBitmap, ColorCursorBitmapState, ColorCursorPresentation, ColorMode, Object, Position, SetBWCursorPosition, SetMousePosition, SwapAction, SwapNotifier, Virtual],
TerminalExtras USING [],
VM USING [
AddressForPageNumber, Allocate, CantAllocate, Free, Interval, lowCore, nullInterval, Pin, Unpin],
VMSideDoor USING [AssignSpecialRealMemory, ReleaseSpecialRealMemory];
TerminalImpl: CEDAR MONITOR LOCKS terminalLock
IMPORTS
Basics, ColorDisplayFace, DisplayFace, KeyboardFace, MouseFace, PrincOpsUtils, Process, Terminal, VM, VMSideDoor
EXPORTS Terminal, TerminalExtras
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],
black and white cursor
bwCursor: Terminal.Position ← [0, 0],
bwPattern: Terminal.BWCursorBitmap ← ALL[0],
black and white frame buffer
bwBitmapState: Terminal.BitmapState ← none,
bwBackground: Terminal.BWBackground ← white,
bwBorderOdd: Terminal.BWBorder ← 0,
bwBorderEven: Terminal.BWBorder ← 0,
bwBitmap: VM.Interval ← NULL,
color cursor
colorCursor: Terminal.Position ← [0, 0],
colorPattern: Terminal.ColorCursorBitmap ← ALL[0],
colorCursorPresentation: Terminal.ColorCursorPresentation ← onesAreBlack,
colorCursorState: Terminal.ColorCursorBitmapState ← visible,
colorCursorDisplayed: BOOLFALSE,
colorFrameLocked: BOOLFALSE,
xmin, ymin, xmax, ymax: NAT ← 0,
colorCursorSourceA, colorCursorSourceB: LONG POINTERNIL,
colorCursorBackupA, colorCursorBackupB: LONG POINTERNIL,
colorCursorVM: VM.Interval ← NULL,
color frame buffer
colorBitmapState: Terminal.BitmapState ← none,
colorVisibility: Terminal.ChannelsVisible ← none,
colorVM: VM.Interval ← NULL,
notifiers: Notifier ← NIL
];
Notifier: TYPE = REF NotifierItem;
NotifierItem: TYPE = RECORD [
next, prev: Notifier,
proc: Terminal.SwapNotifier,
clientData: REF ANY
];
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;
watcherDisableCount: NAT ← 0;
terminalChanging: BOOLFALSE;
'terminalChanging' synchronizes 'Select' with long-running operations that can't afford to hold the monitor lock throughout (including 'Select' itself).
terminalStable: CONDITION ← [timeout: 0];
bwRetrace: LONG POINTER TO CONDITION;
meFirst: BOOLTRUE;
verticalRetrace: CONDITION ← [timeout: 0];
colorTerminal: Virtual ← NIL;
For now, the color display can be attached to only one virtual terminal.
This should be fixed, but it requires some reworking of ColorDisplayFace.
Exports to Terminal
CantDoIt: PUBLIC ERROR = CODE;
Create: PUBLIC PROC RETURNS [vt: Virtual] = {
AddToList: ENTRY PROC [vt: Virtual] = INLINE {
ENABLE UNWIND => NULL;
terminals ← CONS[vt, terminals];
};
AddToList[vt ← NewVirtual[]];
};
Select: PUBLIC PROC [vt: Virtual ← NIL] = {
old: Virtual;
new: Virtual ← vt;
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, n.clientData];
IF (n ← IF action = coming THEN n.next ELSE n.prev) = head THEN EXIT;
ENDLOOP;
};
FlipTerminal: ENTRY PROC [old, new: Virtual] = INLINE {
ENABLE UNWIND => NULL;
UnloadTerminal[old];
current ← new;
LoadTerminal[new];
};
FindNextTerminal: ENTRY PROC [old: Virtual] RETURNS [new: Virtual] = INLINE {
ENABLE UNWIND => NULL;
FOR t: TerminalList ← terminals, t.rest UNTIL t = NIL DO
IF t.first = old THEN RETURN[(IF t.rest = NIL THEN terminals ELSE t.rest).first];
REPEAT
FINISHED => ERROR;
ENDLOOP;
};
AcquireAndMarkTerminalUnstable[];
old ← current;
IF new = NIL THEN new ← FindNextTerminal[old];
IF old ~= new THEN {
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]};
};
MarkTerminalStable[];
};
Current: PUBLIC ENTRY PROC RETURNS [vt: Virtual] = {
ENABLE UNWIND => NULL;
WHILE terminalChanging DO WAIT terminalStable; ENDLOOP;
vt ← current;
};
RegisterNotifier: PUBLIC ENTRY PROC [
vt: Virtual, notifier: Terminal.SwapNotifier, clientData: REF ANYNIL] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl = NARROW[vt.impl];
n: Notifier = NEW[NotifierItem ← [next: NIL, prev: NIL, proc: notifier, clientData: clientData]];
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, clientData: REF ANYNIL] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl = NARROW[vt.impl];
head: Notifier = impl.notifiers;
n: Notifier ← impl.notifiers;
IF head ~= NIL THEN
DO
IF n.proc = notifier AND n.clientData = clientData 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;
};
DisableKeyboardWatcher: PUBLIC ENTRY PROC = {
ENABLE UNWIND => NULL;
watcherDisableCount ← watcherDisableCount.SUCC;
};
EnableKeyboardWatcher: PUBLIC ENTRY PROC = {
ENABLE UNWIND => NULL;
IF watcherDisableCount > 0 THEN watcherDisableCount ← watcherDisableCount.PRED;
};
Mouse
SetMousePosition: PUBLIC ENTRY PROC [vt: Virtual, position: Terminal.Position] = {
ENABLE UNWIND => NULL;
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] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl = NARROW[vt.impl];
RETURN[impl.bwPattern]
};
SetBWCursorPattern: PUBLIC ENTRY PROC [vt: Virtual, pattern: Terminal.BWCursorBitmap] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl = NARROW[vt.impl];
impl.bwPattern ← pattern;
IF current = vt THEN TRUSTED {DisplayFace.SetCursorPattern[LOOPHOLE[LONG[@pattern]]]};
};
Black-and-White Display
GetBWBitmapState: PUBLIC ENTRY PROC [vt: Virtual] RETURNS [Terminal.BitmapState] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl = NARROW[vt.impl];
RETURN [impl.bwBitmapState]
};
SetBWBitmapState: PUBLIC PROC [vt: Virtual, new: Terminal.BitmapState]
RETURNS [old: Terminal.BitmapState] = {
impl: VirtualImpl = NARROW[vt.impl];
SetBWBitmapStateInner: ENTRY PROC = {
ENABLE UNWIND => NULL;
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[impl];
none => {
IF current = vt THEN {TurnOff[impl]; DisplayFace.Disconnect[]};
TRUSTED {VM.Free[impl.bwBitmap]};
};
ENDCASE;
impl.bwBitmapState ← new;
};
Acquire/Release replaced by ENTRY on inner proc to prevent deadlock in notifier stuff. This is probably not a good fix, since it may keep the monitor locked for a long time while allocating the bitmap
--AcquireAndMarkTerminalUnstable[];
SetBWBitmapStateInner[];
--MarkTerminalStable[];
};
GetBWBackground: PUBLIC ENTRY PROC [vt: Virtual] RETURNS [Terminal.BWBackground] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl = NARROW[vt.impl];
RETURN [impl.bwBackground]
};
SetBWBackground: PUBLIC ENTRY PROC [vt: Virtual, new: Terminal.BWBackground]
RETURNS [old: Terminal.BWBackground] = {
ENABLE UNWIND => NULL;
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: Terminal.BWBorder] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl = NARROW[vt.impl];
RETURN [impl.bwBorderOdd, impl.bwBorderEven]
};
SetBWBorder: PUBLIC ENTRY PROC [vt: Virtual, oddPairs, evenPairs: Terminal.BWBorder] = {
ENABLE UNWIND => NULL;
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] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl = NARROW[vt.impl];
bbt ← DisplayFace.GetBitBltTable[];
TRUSTED{
bbt.dst ← bbt.src ← [
word:
IF impl.bwBitmapState = none THEN NIL
ELSE VM.AddressForPageNumber[impl.bwBitmap.page],
bit: 0
];
};
bbt.width ← vt.bwWidth;
bbt.height ← vt.bwHeight;
};
WaitForBWVerticalRetrace: PUBLIC ENTRY PROC [vt: Virtual] = {
ENABLE UNWIND => NULL;
WaitUntilSelected[vt];
InternalWaitForBWVerticalRetrace[];
};
BlinkBWDisplay: PUBLIC PROC [vt: Virtual] = {
background: Terminal.BWBackground;
AcquireAndMarkTerminalUnstable[];
IF vt = current THEN {
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];
};
MarkTerminalStable[];
};
Color Display
nullColorFaceMode: ColorDisplayFace.Mode ~
[full: FALSE, useA: FALSE, useB: FALSE, lgBitsPerPixelA: 0, lgBitsPerPixelB: 0];
ColorFaceMode: PROC[mode: Terminal.ColorMode]
RETURNS[fmode: ColorDisplayFace.Mode ← nullColorFaceMode] = {
illegal: BOOLFALSE;
IF mode.full THEN fmode.full ← TRUE
ELSE {
IF (fmode.useA ← mode.bitsPerPixelChannelA>0) THEN
SELECT mode.bitsPerPixelChannelA FROM
1 => fmode.lgBitsPerPixelA ← 0;
2 => fmode.lgBitsPerPixelA ← 1;
4 => fmode.lgBitsPerPixelA ← 2;
8 => fmode.lgBitsPerPixelA ← 3;
ENDCASE => illegal ← TRUE;
IF (fmode.useB ← mode.bitsPerPixelChannelB>0) THEN
SELECT mode.bitsPerPixelChannelB FROM
1 => fmode.lgBitsPerPixelB ← 0;
2 => fmode.lgBitsPerPixelB ← 1;
4 => fmode.lgBitsPerPixelB ← 2;
8 => fmode.lgBitsPerPixelB ← 3;
ENDCASE => illegal ← TRUE;
};
IF illegal THEN fmode ← nullColorFaceMode;
};
LegalColorFaceMode: PROC[fmode: ColorDisplayFace.Mode] RETURNS[BOOL] = {
IF fmode=nullColorFaceMode THEN RETURN[FALSE]
ELSE TRUSTED { RETURN[ColorDisplayFace.HasMode[fmode]] };
};
GetColorBitmapState: PUBLIC ENTRY PROC [vt: Virtual] RETURNS [Terminal.BitmapState] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl = NARROW[vt.impl];
RETURN [impl.colorBitmapState]
};
SetColorBitmapState: PUBLIC PROC [vt: Virtual,
newState: Terminal.BitmapState,
newMode: Terminal.ColorMode, newVisibility: Terminal.ChannelsVisible
]
RETURNS [
oldState: Terminal.BitmapState,
oldMode: Terminal.ColorMode, oldVisibility: Terminal.ChannelsVisible
] = {
impl: VirtualImpl = NARROW[vt.impl];
SetColorBitmapStateInner: ENTRY PROC = {
ENABLE UNWIND => NULL;
Connect: INTERNAL PROC[fmode: ColorDisplayFace.Mode] = TRUSTED {
impl.colorVM ← AllocateColorVM[fmode];
ColorDisplayFace.Connect[mode: fmode,
firstPage: impl.colorVM.page, nPages: impl.colorVM.count];
vt.colorWidth ← ColorDisplayFace.width;
vt.colorHeight ← ColorDisplayFace.height;
vt.colorBitmapA ← ColorDisplayFace.baseA;
vt.colorBitmapB ← ColorDisplayFace.baseB;
IF (ColorDisplayFace.bplA MOD Basics.bitsPerWord)=0 THEN
vt.colorWordsPerLineA ← ColorDisplayFace.bplA/Basics.bitsPerWord
ELSE ERROR;
IF (ColorDisplayFace.bplB MOD Basics.bitsPerWord)=0 THEN
vt.colorWordsPerLineB ← ColorDisplayFace.bplB/Basics.bitsPerWord
ELSE ERROR;
impl.colorCursorVM ← VM.Allocate[3];
VM.Pin[impl.colorCursorVM];
impl.colorCursorSourceA ← VM.AddressForPageNumber[impl.colorCursorVM.page]; --page 1
impl.colorCursorBackupA ← impl.colorCursorSourceA+256; --page 2
impl.colorCursorSourceB ← impl.colorCursorBackupA+256; --first half of page 3
impl.colorCursorBackupB ← impl.colorCursorSourceB+128; --second half of page 3
ExpandColorCursorPattern[vt, impl];
};
Disconnect: INTERNAL PROC = TRUSTED {
vt.colorBitmapA ← vt.colorBitmapB ← NIL;
vt.colorWordsPerLineA ← vt.colorWordsPerLineB ← 0;
ColorDisplayFace.Disconnect[];
VM.Free[impl.colorVM];
impl.colorVM ← VM.nullInterval;
impl.colorBitmapState ← none;
impl.colorCursorSourceA ← impl.colorCursorBackupA ← NIL;
impl.colorCursorSourceB ← impl.colorCursorBackupB ← NIL;
VM.Free[impl.colorCursorVM];
impl.colorCursorVM ← VM.nullInterval;
};
TurnOn: INTERNAL PROC = {
IF current=vt THEN {
VM.Pin[impl.colorVM];
TRUSTED{ColorDisplayFace.TurnOn[]};
};
};
TurnOff: INTERNAL PROC = {
IF current=vt THEN {
TRUSTED{ColorDisplayFace.TurnOff[]};
VM.Unpin[impl.colorVM];
};
};
oldMode ← vt.colorMode;
oldState ← impl.colorBitmapState;
oldVisibility ← impl.colorVisibility;
IF newMode#oldMode THEN {
fmode: ColorDisplayFace.Mode ~ ColorFaceMode[newMode];
IF LegalColorFaceMode[fmode] THEN vt.colorMode ← newMode ELSE ERROR CantDoIt;
IF oldState#none THEN {
IF oldState=displayed THEN TurnOff[];
Disconnect[];
};
IF newState#none THEN {
Connect[fmode];
IF newState=displayed THEN TurnOn[];
};
}
ELSE IF newState#oldState THEN {
IF oldState=displayed THEN TurnOff[];
IF newState=none THEN Disconnect[];
IF oldState=none THEN Connect[ColorFaceMode[vt.colorMode]];
IF newState=displayed THEN TurnOn[];
};
impl.colorBitmapState ← newState;
IF newVisibility#oldVisibility THEN {
impl.colorVisibility ← newVisibility;
IF vt=current THEN SELECT newVisibility FROM
none => TRUSTED{ColorDisplayFace.Show[a: FALSE, b: FALSE]};
aOnly => TRUSTED{ColorDisplayFace.Show[a: TRUE, b: FALSE]};
bOnly => TRUSTED{ColorDisplayFace.Show[a: FALSE, b: TRUE]};
all => TRUSTED{ColorDisplayFace.Show[a: TRUE, b: TRUE]};
ENDCASE => ERROR;
};
};
Acquire/Release replaced by ENTRY on inner proc to prevent deadlock in notifier stuff. This is probably not a good fix, since it may keep the monitor locked for a long time while allocating the bitmap
--AcquireAndMarkTerminalUnstable[];
SetColorBitmapStateInner[];
--MarkTerminalStable[];
};
LegalColorMode: PUBLIC PROC[vt: Virtual, mode: Terminal.ColorMode] RETURNS[BOOL] = {
IF vt.hasColorDisplay THEN RETURN[LegalColorFaceMode[ColorFaceMode[mode]]]
ELSE RETURN[FALSE];
};
GetVisibility: PUBLIC ENTRY PROC [vt: Virtual] RETURNS [Terminal.ChannelsVisible] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl = NARROW[vt.impl];
RETURN [impl.colorVisibility];
};
Colormaps
Color: TYPE = Terminal.Color;
GetRedMap: PUBLIC ENTRY PROC [vt: Virtual, in: Color] RETURNS [Color] = {
impl: VirtualImpl = NARROW[vt.impl];
IF vt.hasColorDisplay AND impl.colorBitmapState#none AND vt.colorMode.full THEN
TRUSTED{RETURN[ColorDisplayFace.GetRedMap[in]]}
ELSE RETURN[0];
};
GetGreenMap: PUBLIC ENTRY PROC [vt: Virtual, in: Color] RETURNS [Color] = {
impl: VirtualImpl = NARROW[vt.impl];
IF vt.hasColorDisplay AND impl.colorBitmapState#none AND vt.colorMode.full THEN
TRUSTED{RETURN[ColorDisplayFace.GetGreenMap[in]]}
ELSE RETURN[0];
};
GetBlueMap: PUBLIC ENTRY PROC [vt: Virtual, in: Color] RETURNS [Color] = {
impl: VirtualImpl = NARROW[vt.impl];
IF vt.hasColorDisplay AND impl.colorBitmapState#none AND vt.colorMode.full THEN
TRUSTED{RETURN[ColorDisplayFace.GetBlueMap[in]]}
ELSE RETURN[0];
};
SetRedMap: PUBLIC ENTRY PROC [vt: Virtual, in: Color, out: Color] = {
impl: VirtualImpl = NARROW[vt.impl];
IF vt.hasColorDisplay AND impl.colorBitmapState#none AND vt.colorMode.full THEN
TRUSTED{ColorDisplayFace.SetRedMap[in: in, out: out]};
};
SetGreenMap: PUBLIC ENTRY PROC [vt: Virtual, in: Color, out: Color] = {
impl: VirtualImpl = NARROW[vt.impl];
IF vt.hasColorDisplay AND impl.colorBitmapState#none AND vt.colorMode.full THEN
TRUSTED{ColorDisplayFace.SetGreenMap[in: in, out: out]};
};
SetBlueMap: PUBLIC ENTRY PROC [vt: Virtual, in: Color, out: Color] = {
impl: VirtualImpl = NARROW[vt.impl];
IF vt.hasColorDisplay AND impl.colorBitmapState#none AND vt.colorMode.full THEN
TRUSTED{ColorDisplayFace.SetBlueMap[in: in, out: out]};
};
GetColor: PUBLIC ENTRY PROC [vt: Virtual,
aChannelValue, bChannelValue: Terminal.ChannelValue ← 0]
RETURNS [red, green, blue: Color] = {
impl: VirtualImpl = NARROW[vt.impl];
IF vt.hasColorDisplay AND impl.colorBitmapState#none AND NOT vt.colorMode.full THEN
TRUSTED{[r: red, g: green, b: blue] ← ColorDisplayFace.GetColor[
pixelA: aChannelValue, pixelB: bChannelValue]}
ELSE red ← green ← blue ← 0;
};
SetColor: PUBLIC ENTRY PROC [vt: Virtual,
aChannelValue, bChannelValue: Terminal.ChannelValue ← 0,
red, green, blue: Color] = {
impl: VirtualImpl = NARROW[vt.impl];
IF vt.hasColorDisplay AND impl.colorBitmapState#none AND NOT vt.colorMode.full THEN
TRUSTED{ColorDisplayFace.SetColor[pixelA: aChannelValue, pixelB: bChannelValue,
r: red, g: green, b: blue]};
};
Color Cursor
GetColorCursorPosition returns the position of the virtual cursor associated with the color display on the specified virtual terminal. This position changes only as a result of calls on SetColorCursorPosition.
SetColorCursorPosition sets the cursor position of the specified virtual terminal to the indicated value. This position will be reflected on the screen when the virtual terminal is next selected (or, if it is presently selected, immediately). It is the responsibility of the client to clip the position, if desired, to ensure that the cursor remains on the visible area of the display.
GetColorCursorPosition: PUBLIC ENTRY PROC [vt: Virtual] RETURNS [Terminal.Position] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl = NARROW[vt.impl];
RETURN[impl.colorCursor];
};
SetColorCursorPosition: PUBLIC ENTRY PROC [vt: Virtual, position: Terminal.Position] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl = NARROW[vt.impl];
IF NOT vt.hasColorDisplay THEN RETURN;
IF impl.colorCursor=position THEN RETURN; -- no change
IF impl.colorCursorDisplayed THEN HideColorCursor[vt, impl];
impl.colorCursor ← position;
IF impl.colorCursorState=$visible AND NOT InhibitColorCursor[impl] THEN
ShowColorCursor[vt, impl];
};
GetColorCursorPattern returns the bitmap of the virtual cursor associated with the color display on the specified virtual terminal. This bitmap changes only as a result of calls on SetColorCursorPattern and/or SetColorCursorPresentation.
SetColorCursorPattern sets the cursor bitmap of the specified virtual terminal to the indicated value. This bitmap will be reflected on the screen when the virtual terminal is next selected (or, if it is presently selected, immediately).
GetColorCursorPattern: PUBLIC ENTRY PROC [vt: Virtual]
RETURNS [pattern: Terminal.ColorCursorBitmap] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl = NARROW[vt.impl];
RETURN[impl.colorPattern];
};
SetColorCursorPattern: PUBLIC ENTRY PROC [vt: Virtual,
pattern: Terminal.ColorCursorBitmap] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl ~ NARROW[vt.impl];
displayed: BOOL ~ impl.colorCursorDisplayed;
IF NOT vt.hasColorDisplay THEN RETURN;
IF displayed THEN HideColorCursor[vt, impl];
impl.colorPattern ← pattern;
IF impl.colorBitmapState#none THEN ExpandColorCursorPattern[vt, impl];
IF displayed THEN ShowColorCursor[vt, impl];
};
These procedures control the manner in which the cursor bitmap is presented on the color display. "onesAreWhite" means a one-bit in the ColorCursorBitmap is mapped to a color value of all ones (conventionally white) and a zero-bit is mapped to a color value of all zeros (conventionally black). "onesAreBlack" reverses this interpretation.
GetColorCursorPresentation: PUBLIC ENTRY PROC [vt: Virtual]
RETURNS [Terminal.ColorCursorPresentation] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl = NARROW[vt.impl];
RETURN[impl.colorCursorPresentation];
};
SetColorCursorPresentation: PUBLIC ENTRY PROC [vt: Virtual,
new: Terminal.ColorCursorPresentation]
RETURNS [old: Terminal.ColorCursorPresentation] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl ~ NARROW[vt.impl];
displayed: BOOL ~ impl.colorCursorDisplayed;
old ← impl.colorCursorPresentation;
IF NOT vt.hasColorDisplay THEN RETURN;
IF new=old THEN RETURN; -- no change
IF displayed THEN HideColorCursor[vt, impl];
impl.colorCursorPresentation ← new;
IF displayed THEN ShowColorCursor[vt, impl];
};
These procedures control the visibility of the cursor bitmap on the color display. When the state is "visible", the cursor bitmap (supplied by SetColorCursorPattern and influenced by SetColorCursorPresentation) is displayed at the position determined by SetColorCursorPosition. When the state is "invisible", no cursor appears on the color display. In the "visible" state, the cursor is merged into the color display bitmap by software, which caches the contents of the display bitmap that lie "under" the cursor. If the cursor moves (as a result of SetColorCursorPosition), the display bitmap is restored to the cached value. The procedures used to modify the display bitmap do not check to see if this cache should be invalidated. Therefore, if the client wants to change the display bitmap under the cursor, it must first flush the cache by setting the cursor state to "invisible".
The effect of these procedures can only be observed when vt is selected and GetColorBitmapState[vt] = displayed.
GetColorCursorState: PUBLIC ENTRY PROC [vt: Virtual]
RETURNS [Terminal.ColorCursorBitmapState] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl = NARROW[vt.impl];
RETURN[impl.colorCursorState];
};
SetColorCursorState: PUBLIC ENTRY PROC [vt: Virtual, new: Terminal.ColorCursorBitmapState]
RETURNS [old: Terminal.ColorCursorBitmapState] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl = NARROW[vt.impl];
old ← impl.colorCursorState;
IF NOT vt.hasColorDisplay THEN RETURN;
IF new=old THEN RETURN;
IF impl.colorBitmapState#none THEN SELECT new FROM
$invisible => IF impl.colorCursorDisplayed THEN HideColorCursor[vt, impl];
$visible => IF NOT InhibitColorCursor[impl] THEN ShowColorCursor[vt, impl];
ENDCASE;
impl.colorCursorState ← new;
};
LockColorFrame: PUBLIC ENTRY PROC[vt: Virtual,
xmin, ymin: NAT ← 0, xmax, ymax: NATNAT.LAST] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl = NARROW[vt.impl];
impl.xmin ← xmin; impl.ymin ← ymin;
impl.xmax ← xmax; impl.ymax ← ymax;
impl.colorFrameLocked ← TRUE;
IF impl.colorCursorDisplayed AND InhibitColorCursor[impl] THEN HideColorCursor[vt, impl];
};
UnlockColorFrame: PUBLIC ENTRY PROC[vt: Virtual] = {
ENABLE UNWIND => NULL;
impl: VirtualImpl = NARROW[vt.impl];
impl.colorFrameLocked ← FALSE;
IF impl.colorBitmapState#none AND impl.colorCursorState=$visible AND NOT impl.colorCursorDisplayed THEN ShowColorCursor[vt, impl];
};
InhibitColorCursor: INTERNAL PROC[impl: VirtualImpl] RETURNS[BOOL] = INLINE {
RETURN[impl.colorFrameLocked AND ColorCursorIntersectsFrame[impl]];
};
ColorCursorIntersectsFrame: INTERNAL PROC[impl: VirtualImpl] RETURNS[BOOL] = {
x: INTEGER ~ impl.colorCursor.x; y: INTEGER ~ impl.colorCursor.y;
w: INTEGER ~ 16; h: INTEGER ~ 16;
RETURN[x<impl.xmax AND y<impl.ymax AND (x+w)>impl.xmin AND (y+h)>impl.ymin];
};
colorCursorWplSrcA: NAT ~ 16;
colorCursorWplSrcB: NAT ~ 8;
ExpandColorCursorPattern: INTERNAL PROC[vt: Virtual, impl: VirtualImpl] = {
ExpandBitmap: PROC[buffer: LONG POINTER, wpl: CARDINAL, bpp: NAT] = TRUSTED {
AddOne: PROC[address: PrincOps.BitAddress] RETURNS[PrincOps.BitAddress] = TRUSTED {
IF address.bit<15 THEN address.bit ← address.bit+1
ELSE {address.bit ← 0; address.word ← address.word+1};
RETURN[address];
};
ppw: NAT ~ 16/bpp;
bbSpace: PrincOps.BBTableSpace;
bb: PrincOps.BitBltTablePtr ~ PrincOpsUtils.AlignedBBTable[@bbSpace];
bb^ ← [dst: [word: NIL, bit: 0], dstBpl: 0, src: [word: NIL, bit: 0], srcDesc: [srcBpl[0]],
width: 0, height: 0, flags: [disjoint: TRUE, gray: FALSE, srcFunc: null, dstFunc: null]];
bb.src.word ← @impl.colorPattern;
bb.srcDesc.srcBpl ← 16;
bb.dst.word ← buffer;
bb.dstBpl ← wpl*16;
bb.width ← 1;
bb.height ← 16;
FOR i: NAT IN [0..16) DO
FOR j: NAT IN [0..bpp) DO
PrincOpsUtils.BITBLT[bb]; --write one column
bb.dst ← AddOne[bb.dst];
ENDLOOP;
bb.src ← AddOne[bb.src];
ENDLOOP;
};
IF impl.colorBitmapState=none THEN RETURN;
IF vt.colorMode.full THEN {
ExpandBitmap[impl.colorCursorSourceA, colorCursorWplSrcA, 16];
ExpandBitmap[impl.colorCursorSourceB, colorCursorWplSrcB, 8];
}
ELSE IF vt.colorMode.bitsPerPixelChannelA>0 THEN {
ExpandBitmap[impl.colorCursorSourceA, colorCursorWplSrcA,
vt.colorMode.bitsPerPixelChannelA];
};
};
HideColorCursor: INTERNAL PROC[vt: Virtual, impl: VirtualImpl] = TRUSTED {
xMax: INTEGER ~ vt.colorWidth;
yMax: INTEGER ~ vt.colorHeight;
x: INTEGER ~ impl.colorCursor.x;
y: INTEGER ~ impl.colorCursor.y;
w: INTEGER ~ 16;
h: INTEGER ~ 16;
IF (x+w)>0 AND (y+h)>0 AND x<xMax AND y<yMax
AND (vt.colorMode.full OR vt.colorMode.bitsPerPixelChannelA>0) THEN {
bpp: NAT ~ IF vt.colorMode.full THEN 16 ELSE vt.colorMode.bitsPerPixelChannelA;
ppw: NAT ~ 16/bpp;
bltX, bltY, bltW, bltH: INTEGER;
xoff, yoff: INTEGER ← 0;
bbSpace: PrincOps.BBTableSpace;
bb: PrincOps.BitBltTablePtr ~ PrincOpsUtils.AlignedBBTable[@bbSpace];
bb^ ← [dst: [word: NIL, bit: 0], dstBpl: 0, src: [word: NIL, bit: 0], srcDesc: [srcBpl[0]],
width: 0, height: 0, flags: [disjoint: TRUE, gray: FALSE, srcFunc: null, dstFunc: null]];
bltX ← x; bltY ← y;
bltW ← w; bltH ← h;
IF x<0 THEN {xoff ← ABS[x]; bltW ← w-xoff; bltX ← 0};
IF y<0 THEN {yoff ← ABS[y]; bltH ← h-yoff; bltY ← 0};
IF x>(xMax-w) THEN bltW ← xMax-x;
IF y>(yMax-h) THEN bltH ← yMax-y;
bb.dst.word ← vt.colorBitmapA + Basics.LongMult[bltY,vt.colorWordsPerLineA] + bltX/ppw;
bb.dst.bit ← (bltX MOD ppw)*bpp;
bb.dstBpl ← vt.colorWordsPerLineA*16;
bb.src.word ← impl.colorCursorBackupA;
bb.src.bit ← 0;
bb.srcDesc.srcBpl ← colorCursorWplSrcA*16;
bb.width ← bltW*bpp;
bb.height ← bltH;
PrincOpsUtils.BITBLT[bb]; -- restore the bits under the cursor
IF vt.colorMode.full THEN {
bb.dst.word ← vt.colorBitmapB + Basics.LongMult[bltY,vt.colorWordsPerLineB] + bltX/2;
bb.dst.bit ← (bltX MOD 2)*8;
bb.dstBpl ← vt.colorWordsPerLineB*16;
bb.src.word ← impl.colorCursorBackupB;
bb.src.bit ← 0;
bb.srcDesc.srcBpl ← colorCursorWplSrcB*16;
bb.width ← bltW*8;
bb.height ← bltH;
PrincOpsUtils.BITBLT[bb]; -- restore the rest of the bits under the cursor
};
};
impl.colorCursorDisplayed ← FALSE;
};
ShowColorCursor: INTERNAL PROC[vt: Virtual, impl: VirtualImpl] = TRUSTED {
xMax: INTEGER ~ vt.colorWidth;
yMax: INTEGER ~ vt.colorHeight;
x: INTEGER ~ impl.colorCursor.x;
y: INTEGER ~ impl.colorCursor.y;
w: INTEGER ~ 16;
h: INTEGER ~ 16;
xoff, yoff: INTEGER ← 0;
IF (x+w)>0 AND (y+h)>0 AND x<xMax AND y<yMax
AND (vt.colorMode.full OR vt.colorMode.bitsPerPixelChannelA>0) THEN {
bpp: NAT ~ IF vt.colorMode.full THEN 16 ELSE vt.colorMode.bitsPerPixelChannelA;
ppw: NAT ~ 16/bpp;
bltX, bltY, bltW, bltH: INTEGER;
xoff, yoff: INTEGER ← 0;
bbSpace: PrincOps.BBTableSpace;
bb: PrincOps.BitBltTablePtr ~ PrincOpsUtils.AlignedBBTable[@bbSpace];
bb^ ← [dst: [word: NIL, bit: 0], dstBpl: 0, src: [word: NIL, bit: 0], srcDesc: [srcBpl[0]],
width: 0, height: 0, flags: [disjoint: TRUE, gray: FALSE, srcFunc: null, dstFunc: null]];
bltX ← x; bltY ← y;
bltW ← w; bltH ← h;
IF x<0 THEN {xoff ← ABS[x]; bltW ← w-xoff; bltX ← 0};
IF y<0 THEN {yoff ← ABS[y]; bltH ← h-yoff; bltY ← 0};
IF x>(xMax-w) THEN bltW ← xMax-x;
IF y>(yMax-h) THEN bltH ← yMax-y;
bb.dst ← [word: impl.colorCursorBackupA, bit: 0];
bb.dstBpl ← colorCursorWplSrcA*16;
bb.src.word ← vt.colorBitmapA + Basics.LongMult[bltY,vt.colorWordsPerLineA] + bltX/ppw;
bb.src.bit ← (bltX MOD ppw)*bpp;
bb.srcDesc.srcBpl ← vt.colorWordsPerLineA*16;
bb.width ← bltW*bpp;
bb.height ← bltH;
PrincOpsUtils.BITBLT[bb]; -- save the bits under the cursor
IF vt.colorMode.full THEN {
bb.src.word ← vt.colorBitmapB + Basics.LongMult[bltY,vt.colorWordsPerLineB] + bltX/2;
bb.src.bit ← (bltX MOD 2)*8;
bb.srcDesc.srcBpl ← vt.colorWordsPerLineB*16;
bb.dst ← [word: impl.colorCursorBackupB, bit: 0];
bb.dstBpl ← colorCursorWplSrcB*16;
bb.width ← bltW*8;
bb.height ← bltH;
PrincOpsUtils.BITBLT[bb]; -- save the rest of the bits under the cursor
};
SELECT impl.colorCursorPresentation FROM
$onesAreBlack => { bb.flags.srcFunc ← complement; bb.flags.dstFunc ← and };
$onesAreWhite => { bb.flags.srcFunc ← null; bb.flags.dstFunc ← or };
ENDCASE => ERROR;
bb.dst.word ← vt.colorBitmapA + Basics.LongMult[bltY,vt.colorWordsPerLineA] + bltX/ppw;
bb.dst.bit ← (bltX MOD ppw)*bpp;
bb.dstBpl ← vt.colorWordsPerLineA*16;
bb.src.word ← impl.colorCursorSourceA + yoff*colorCursorWplSrcA + xoff/ppw;
bb.src.bit ← (xoff MOD ppw)*bpp;
bb.srcDesc.srcBpl ← colorCursorWplSrcA*16;
bb.width ← bltW*bpp;
bb.height ← bltH;
PrincOpsUtils.BITBLT[bb]; -- Put the cursor in
IF vt.colorMode.full THEN {
bb.dst.word ← vt.colorBitmapB + Basics.LongMult[bltY,vt.colorWordsPerLineB] + bltX/2;
bb.dst.bit ← (bltX MOD 2)*8;
bb.dstBpl ← vt.colorWordsPerLineB*16;
bb.src.word ← impl.colorCursorSourceB + yoff*colorCursorWplSrcB + xoff/2;
bb.src.bit ← (xoff MOD 2)*8;
bb.srcDesc.srcBpl ← colorCursorWplSrcB*16;
bb.width ← bltW*8;
bb.height ← bltH;
PrincOpsUtils.BITBLT[bb]; -- Put the rest of the cursor in
};
};
impl.colorCursorDisplayed ← TRUE;
};
Internal Procedures
WaitUntilSelected: INTERNAL PROC [vt: Virtual] = {
UNTIL current = vt DO WAIT terminalStable; ENDLOOP;
};
AcquireAndMarkTerminalUnstable: ENTRY PROC = {
ENABLE UNWIND => NULL;
WHILE terminalChanging DO WAIT terminalStable; ENDLOOP;
terminalChanging ← TRUE;
};
AcquireAndMarkSpecificTerminalUnstable: ENTRY PROC [vt: Virtual] = {
ENABLE UNWIND => NULL;
WHILE terminalChanging OR vt ~= current DO WAIT terminalStable; ENDLOOP;
terminalChanging ← TRUE;
};
MarkTerminalStable: ENTRY PROC = {
ENABLE UNWIND => NULL;
terminalChanging ← FALSE;
BROADCAST terminalStable;
};
InternalWaitForBWVerticalRetrace: INTERNAL PROC = {
ENABLE UNWIND => NULL;
IF meFirst THEN {
meFirst ← FALSE;
TRUSTED {WAIT bwRetrace^};
BROADCAST verticalRetrace;
meFirst ← TRUE;
}
ELSE WAIT verticalRetrace;
};
TurnOff: INTERNAL PROC[impl: VirtualImpl] = {
DisplayFace.TurnOff[];
InternalWaitForBWVerticalRetrace[];
InternalWaitForBWVerticalRetrace[];
IF DisplayFace.hasBuffer THEN VMSideDoor.ReleaseSpecialRealMemory[impl.bwBitmap]
ELSE VM.Unpin[impl.bwBitmap];
};
TurnOffColor: INTERNAL PROC[impl: VirtualImpl] = {
TRUSTED{ColorDisplayFace.TurnOff[]};
VM.Unpin[impl.colorVM];
};
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.bwCursor];
DisplayFace.SetBackground[IF vtImpl.bwBackground = white THEN white ELSE black];
DisplayFace.SetBorderPattern[oddPairs: vtImpl.bwBorderOdd, evenPairs: vtImpl.bwBorderEven];
DisplayFace.SetCursorPattern[@vtImpl.bwPattern];
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;
IF vtImpl.colorBitmapState=displayed THEN {
VM.Pin[vtImpl.colorVM];
ColorDisplayFace.TurnOn[];
};
};
UnloadTerminal: INTERNAL 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.bwCursor ← LOOPHOLE[DisplayFace.cursorPosition^];
vt.bwCursor ← @vtImpl.bwCursor;
SELECT vtImpl.bwBitmapState FROM
displayed => {
TurnOff[vtImpl];
DisplayFace.Disconnect[];
};
allocated => {
IF DisplayFace.hasBuffer THEN VMSideDoor.ReleaseSpecialRealMemory[vtImpl.bwBitmap];
DisplayFace.Disconnect[];
};
none => NULL;
ENDCASE;
IF vtImpl.colorBitmapState=displayed THEN {
ColorDisplayFace.TurnOff[];
VM.Unpin[vtImpl.colorVM];
};
};
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.bwCursor],
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]
]];
};
};
AttachColorDisplay: PUBLIC ENTRY PROC[vt: Virtual] = { -- export to TerminalExtras
ENABLE UNWIND => NULL;
IF colorTerminal=NIL THEN colorTerminal ← vt
ELSE IF colorTerminal=vt THEN NULL
ELSE ERROR CantDoIt;
IF ColorDisplayFace.displayType=none THEN RETURN;
vt.colorPixelsPerInch ← ColorDisplayFace.pixelsPerInch;
vt.colorDisplayType ← (SELECT ColorDisplayFace.displayType FROM
ramtek525 => ramtek525, hitachi3619 => hitachi3619,
ENDCASE => ramtek525); -- should be 'other'
vt.hasColorDisplay ← TRUE;
};
AllocateBWBitmap: PROC RETURNS [interval: VM.Interval] = {
interval ←
VM.Allocate[count: DisplayFace.pagesForBitmap, in64K: TRUE
! VM.CantAllocate => GO TO cant];
EXITS
cant => ERROR CantDoIt;
};
AllocateColorVM: PROC[fmode: ColorDisplayFace.Mode] RETURNS [interval: VM.Interval] = {
pages: INT;
TRUSTED { pages ← ColorDisplayFace.PagesForMode[fmode] };
interval ← VM.Allocate[count: pages ! VM.CantAllocate => GO TO cant];
EXITS
cant => ERROR CantDoIt;
};
KeyboardWatcher: PROC = TRUSTED {
WatcherEnabled: ENTRY PROC RETURNS [BOOL] = TRUSTED INLINE {
ENABLE UNWIND => NULL;
RETURN[watcherDisableCount = 0]
};
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 WatcherEnabled[] 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;
};
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[];
IF ColorDisplayFace.displayType#none THEN {
colorHeadGlobalP: LONG POINTER = VM.lowCore.NEW[WordSequence[ColorDisplayFace.globalStateSize]];
ColorDisplayFace.Initialize[colorHeadGlobalP];
ColorDisplayFace.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.EnableAborts[@terminalStable];
Process.Detach[FORK KeyboardWatcher[]];
};
Initialize[];
END.