TerminalIOImpl.mesa
Copyright © 1983, 1985 by Xerox Corporation. All rights reserved.
By Ch. Jacobi August 3, 1983 9:54 am
Last edited by Ch. Jacobi October 7, 1985 10:20:30 am PDT
DIRECTORY
Commander USING [CommandProc, Register],
EditedStream,
Icons USING [IconFlavor, NewIconFromFile],
InputFocus USING [PopInputFocus, PushInputFocus],
IO,
PopUpMenu,
Process,
Rope,
RuntimeError USING [UNCAUGHT],
TerminalIO,
TerminalIOExtras,
ViewerClasses USING [Viewer],
ViewerIO USING [CreateViewerStreams, GetViewerFromStream],
ViewerOps USING [PaintViewer, OpenIcon],
ViewerTools USING [SetSelection];
TerminalIOImpl:
CEDAR
MONITOR
IMPORTS Commander, EditedStream, Icons, InputFocus, IO, PopUpMenu, Process, Rope, RuntimeError, ViewerIO, ViewerOps, ViewerTools
EXPORTS TerminalIO, TerminalIOExtras =
BEGIN
ROPE: TYPE = Rope.ROPE;
UserAbort: PUBLIC SIGNAL = CODE;
TimedOut: PUBLIC SIGNAL = CODE;
terminalParent: ViewerClasses.Viewer←NIL;
messageUsed: BOOL ← FALSE;
in, out, editedIn: IO.STREAM ← NIL;
terminalIcon: Icons.IconFlavor;
inputIcon: Icons.IconFlavor;
del: CHAR = LOOPHOLE[127, CHAR];
delRope: ROPE = Rope.FromChar[del];
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
--Terminals own locking procedures
moduleEntered:
BOOL ←
FALSE;
--is set if process releases the monitorlock but waits still for input
next: CONDITION; -- the next module might enter the "moduleEntered" region
MyLock:
ENTRY PROC [] =
BEGIN
WHILE moduleEntered
DO
WAIT next;
ENDLOOP;
moduleEntered ← TRUE;
END;
ReaderLock:
PROC [] =
BEGIN
terminalParent.icon ← inputIcon;
terminalParent.name ← "Terminal [Input Expected]";
ViewerOps.PaintViewer[terminalParent, caption];
END;
MyUnLock:
ENTRY PROC [] =
BEGIN
moduleEntered ← FALSE;
BROADCAST next;
END;
ReaderUnLock:
PROC [] =
BEGIN
terminalParent.icon ← terminalIcon;
terminalParent.name ← "Terminal";
ViewerOps.PaintViewer[terminalParent, caption];
--conveniant to be here since in all places where UnLock is called
terminalParent.inhibitDestroy ← FALSE;
END;
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
--Lock, UnLock mechanism
LockList: TYPE = LIST OF PROC;
lockList: LockList←NIL;
unLockList: LockList←NIL;
AddLock:
PUBLIC
ENTRY
PROC [lock, unLock:
PROC] =
BEGIN
ENABLE UNWIND => NULL;
unLockList ← CONS[unLock, unLockList];
IF lockList=NIL THEN lockList ← LIST[lock]
ELSE
FOR l: LockList ← lockList, l.rest
DO
IF l.rest=NIL THEN {l.rest ← LIST[lock]; EXIT}
ENDLOOP;
END;
Lock:
PROC =
BEGIN
FOR l: LockList ← lockList, l.rest
WHILE l#
NIL
DO
l.first[! RuntimeError.UNCAUGHT => CONTINUE]
ENDLOOP
END;
UnLock:
PROC =
BEGIN
FOR l: LockList ← unLockList, l.rest
WHILE l#
NIL
DO
l.first[! RuntimeError.UNCAUGHT => CONTINUE]
ENDLOOP
END;
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
--creation of viewer
InternalCreateTerminal:
PROC [] =
BEGIN
[in: in, out: out] ← ViewerIO.CreateViewerStreams[name: "Terminal", viewer: NIL, editedStream: FALSE];
[editedIn] ← EditedStream.Create[in: in, echoTo: out];
terminalParent ← ViewerIO.GetViewerFromStream[out];
IF terminalParent=NIL THEN ERROR;
IF terminalParent.iconic
THEN {
terminalParent.column ← right;
ViewerOps.OpenIcon[terminalParent];
};
terminalParent.icon ← terminalIcon;
END;
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
--input procedures
Done: TYPE = {ok, repeat, intError, abort, timeout, no};
PrepareRequest:
PROC [text:
ROPE ←
NIL] =
BEGIN
InternalWriteRope[text];
IF terminalParent.iconic
THEN {
terminalParent.column ← right;
ViewerOps.OpenIcon[terminalParent];
};
InputFocus.PushInputFocus[terminalParent];
ViewerTools.SetSelection[terminalParent];
END;
RequestRope:
PUBLIC
PROC [text:
ROPE ←
NIL]
RETURNS [
ROPE] =
BEGIN
ENABLE UNWIND => UnLock[];
done: Done;
r: ROPE;
DoIt:
PROC [] =
BEGIN
ENABLE
BEGIN
IO.Error => IF ec=StreamClosed THEN {done←repeat; IO.Reset[editedIn]; CONTINUE};
EditedStream.Rubout => {doneort; IO.Reset[editedIn]; CONTINUE};
END;
PrepareRequest[text];
r ← IO.GetLineRope[editedIn];
InputFocus.PopInputFocus[];
IF Rope.Find[r, delRope]>=0 THEN doneort;
END;
--RequestRope
Lock[];
DO
done ← ok;
DoIt[];
IF done=ok THEN {UnLock[]; RETURN [r]}
ELSE IF done=repeat THEN {InternalWriteRope["XXX\n"]}
ELSE {InternalWriteRope[" ..user abort\n"]; SIGNAL UserAbort}
ENDLOOP;
END;
RequestChar:
PUBLIC
PROC [text:
ROPE ←
NIL]
RETURNS [
CHAR] =
BEGIN
ENABLE UNWIND => UnLock[];
done: Done;
ch: CHAR;
DoIt:
PROC [] =
BEGIN
ENABLE
BEGIN
IO.Error => IF ec=StreamClosed THEN {done←repeat; CONTINUE};
EditedStream.Rubout => {doneort; IO.Reset[in]; CONTINUE};
END;
PrepareRequest[text];
ch ← IO.GetChar[in]; -- returns immediately, no CR to eat
InputFocus.PopInputFocus[];
IF ch=del THEN doneort;
END;
--RequestChar
Lock[];
DO
done ← ok;
DoIt[];
IF done=ok THEN {UnLock[]; RETURN[ch]}
ELSE IF done=repeat THEN {InternalWriteRope["XXX\n"]}
ELSE {
InternalWriteRope[" ..user abort\n"];
SIGNAL UserAbort
};
ENDLOOP;
END;
RequestInt:
PUBLIC
PROC [text:
ROPE ←
NIL]
RETURNS [
INT] =
BEGIN
ENABLE UNWIND => UnLock[];
done: Done;
i: INT;
DoIt:
PROC [] =
BEGIN
ENABLE
BEGIN
IO.Error =>
IF ec=StreamClosed THEN {done←repeat; CONTINUE}
ELSE IF ec=Overflow THEN {done←intError; CONTINUE}
ELSE IF ec=SyntaxError THEN {done←intError; CONTINUE};
EditedStream.Rubout => {doneort; IO.Reset[editedIn]; CONTINUE};
END;
ch: CHAR;
PrepareRequest[text];
i ← IO.GetInt[editedIn];
WHILE
IO.CharsAvail[editedIn]>0
DO
[ch] ← IO.GetChar[editedIn];
IF ch=del THEN doneort;
IF ch#15C AND done#abort THEN done←intError
ENDLOOP;
InputFocus.PopInputFocus[];
END;
--RequestInt
Lock[];
DO
done ← ok;
DoIt[];
IF done=ok THEN {UnLock[]; RETURN[i]}
ELSE IF done=repeat THEN {InternalWriteRope["XXX\n"]}
ELSE IF done=intError THEN {InternalWriteRope[" ?? (integer), please repeat: \n"]}
ELSE {InternalWriteRope[" ..user abort\n"]; SIGNAL UserAbort}
ENDLOOP;
END;
ChoiceError: ERROR = CODE;
RequestSelection:
PUBLIC
PROC [label:
ROPE ←
NIL, choice:
LIST
OF
ROPE, text:
ROPE ←
NIL, default:
NAT ← 0, timeOut:
NAT ← 0]
RETURNS [select:
INT ← 0] =
BEGIN
ENABLE UNWIND => UnLock[];
IF (choice=NIL) OR (choice.first=NIL) THEN RETURN WITH ERROR ChoiceError;
Lock[];
IF text#NIL THEN InternalWriteRope[text];
IO.Flush[out ! RuntimeError.
UNCAUGHT =>
CONTINUE];
Process.Pause[Process.MsecToTicks[5]]; --SILLY FLUSH DOESN'T WORK
select ← PopUpMenu.RequestSelection[label, choice, default, timeOut];
UnLock[];
END;
Confirm:
PUBLIC
PROC [choice:
ROPE, label:
ROPE ←
NIL, timeOut:
NAT ← 0, onTimeOut:
BOOL ←
FALSE]
RETURNS [
BOOL] =
BEGIN
RETURN [
SELECT RequestSelection[label: label, choice:
LIST[choice], timeOut: timeOut]
FROM
1 => TRUE,
-1 => onTimeOut,
ENDCASE => FALSE
]
END;
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
--timed out input
xProcess: PROCESS;
xDoneCondition: CONDITION;
xDone: Done ← no;
xResult: Rope.ROPE;
RequestRopeWithTimeout:
PUBLIC
PROC [text:
ROPE ←
NIL, timeOut:
NAT ← 0]
RETURNS [
ROPE] =
BEGIN
IF timeOut=0 THEN RETURN [RequestRope[text]];
RETURN [MyRequestRopeWithTimeout[text, timeOut]];
END;
MyRequestRopeWithTimeout:
PROC [text:
ROPE ←
NIL, timeOut:
NAT]
RETURNS [
ROPE←
NIL] =
BEGIN
ENABLE UNWIND => UnLock[];
result: Rope.ROPE←NIL;
done: Done;
Lock[];
xDone ← no;
TRUSTED { Process.SetTimeout[@xDoneCondition, Process.SecondsToTicks[timeOut]] };
StartRopeGetter[text];
TRUSTED {JOIN xProcess};
done ← xDone;
result ← xResult;
InputFocus.PopInputFocus[];
UnLock[];
IF xDone=abort THEN ERROR UserAbort;
IF xDone#ok THEN ERROR TimedOut;
IF Rope.Find[result, delRope]>=0 THEN ERROR UserAbort;
RETURN [result]
END;
StartRopeGetter:
ENTRY
PROC [text:
ROPE←NIL] =
BEGIN ENABLE UNWIND => NULL;
xProcess ← FORK ForkedTryToGetRope[text];
WAIT xDoneCondition;
IF xDone#ok AND xDone#abort THEN TRUSTED {Process.Abort[xProcess]};
END;
SetDone:
ENTRY
PROC [done: Done] =
BEGIN ENABLE UNWIND => NULL;
Process.CheckForAbort[];
xDone ← done;
BROADCAST xDoneCondition;
END;
ForkedTryToGetRope:
PROC [text:
ROPE←NIL] =
BEGIN
ReallyTryToGetRope:
PROC [text:
ROPE] =
BEGIN
d: Done;
DoIt:
PROC [] =
BEGIN
ENABLE {
UNWIND => IO.Reset[editedIn];
IO.Error => IF ec=StreamClosed THEN {d←repeat; IO.Reset[editedIn]; CONTINUE};
EditedStream.Rubout => {dort; IO.Reset[editedIn]; CONTINUE};
};
InternalWriteRope[text];
IF terminalParent.iconic
THEN {
terminalParent.column ← right;
ViewerOps.OpenIcon[terminalParent];
};
xResult ← IO.GetLineRope[editedIn];
d ← ok;
END; --DoIt
--ReallyTryToGetRope
DO
d ← no;
DoIt[];
IF d=ok OR d=abort THEN {SetDone[d]; RETURN};
IF d=repeat THEN InternalWriteRope["XXX\n"]
ENDLOOP;
END; --ReallyTryToGetRope
--ForkedTryToGetRope
ReallyTryToGetRope[text ! ABORTED => GOTO NeverMind]
EXITS NeverMind => NULL;
END;
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
--output procedures
InternalWriteRope:
PROC [text:
ROPE] =
BEGIN
IF out=NIL THEN InternalCreateTerminal[];
IO.PutRope[out, text !
IO.Error => {
IF ec=StreamClosed
THEN {
InternalCreateTerminal[];
IO.PutRope[out, text];
CONTINUE
}
}];
END;
WriteRope:
PUBLIC
PROC [text:
ROPE] =
BEGIN ENABLE UNWIND => MyUnLock[];
MyLock[];
InternalWriteRope[text];
MyUnLock[]
END;
WriteRopes:
PUBLIC
PROC [t1, t2, t3:
ROPE←
NIL] =
BEGIN
ENABLE UNWIND => NULL;
IF t1#NIL THEN WriteRope[t1];
IF t2#NIL THEN WriteRope[t2];
IF t3#NIL THEN WriteRope[t3];
END;
WriteLn:
PUBLIC
PROC [] =
BEGIN
WriteRope["\n"];
END;
WriteChar:
PUBLIC
PROC [ch:
CHAR] =
BEGIN
WriteRope[Rope.FromChar[ch]];
END;
WriteInt:
PUBLIC
PROC [i:
INT] =
BEGIN
InternalWriteRope[IO.PutFR1[format: " %g", value: IO.int[i]]];
END;
Write:
PUBLIC
PROC [v1, v2, v3:
IO.Value] =
BEGIN
WriteRope[IO.PutR[v1, v2, v3]];
END;
Write1:
PUBLIC
PROC [value:
IO.Value] =
BEGIN
WriteRope[IO.PutR1[value]];
END;
WriteF:
PUBLIC
PROC [format:
ROPE ←
NIL, v1, v2, v3, v4, v5:
IO.Value] =
BEGIN
WriteRope[IO.PutFR[format, v1, v2, v3, v4, v5]];
END;
WriteF1:
PUBLIC
PROC [format:
ROPE ←
NIL, value:
IO.Value] =
BEGIN
WriteRope[IO.PutFR[format, value]];
END;
TOS:
PUBLIC
PROC []
RETURNS [stream:
IO.
STREAM] =
--an output stream which writes its output into the terminal
BEGIN
RETURN[IO.CreateStream[streamProcs: TerminalIOStreamProcs, streamData: NIL]];
END;
TerminalIOStreamProcs:
REF
IO.StreamProcs ←
IO.CreateStreamProcs[
variety: $output,
class: $TerminalIO,
putChar: TerminalIOStreamPutChar,
putBlock: TerminalIOStreamPutBlock,
unsafePutBlock: TerminalIOStreamUnsafePutBlock,
flush: TerminalIOStreamFlush,
eraseChar: TerminalIOStreamEraseChar,
reset: TerminalIOStreamFlush,
close: TerminalIOStreamClose
];
TerminalIOStreamPutChar:
ENTRY
PROC [self:
IO.
STREAM, char:
CHAR] = {
IF out=NIL THEN InternalCreateTerminal[];
IO.PutChar[out, char !
IO.Error => {
IF ec=StreamClosed
THEN {
InternalCreateTerminal[];
IO.PutChar[out, char];
CONTINUE
}
}];
};
TerminalIOStreamPutBlock:
ENTRY
PROC [self:
IO.
STREAM, block:
REF
READONLY
TEXT, startIndex:
NAT, count:
NAT] = {
IF out=NIL THEN InternalCreateTerminal[];
IO.PutBlock[out, block, startIndex, count !
IO.Error => {
IF ec=StreamClosed
THEN {
InternalCreateTerminal[];
IO.PutBlock[out, block, startIndex, count];
CONTINUE
}
}];
};
TerminalIOStreamUnsafePutBlock:
ENTRY
PROC [self:
IO.
STREAM, block:
IO.UnsafeBlock] = {
IF out=NIL THEN InternalCreateTerminal[];
IO.UnsafePutBlock[out, block !
IO.Error => {
IF ec=StreamClosed
THEN {
InternalCreateTerminal[];
IO.UnsafePutBlock[out, block];
CONTINUE
}
}];
};
TerminalIOStreamFlush:
PROC [self:
IO.
STREAM] = {
};
TerminalIOStreamEraseChar:
PROC [self:
IO.
STREAM, char:
CHAR] = {
};
TerminalIOStreamClose:
PROC [self:
IO.
STREAM, abort:
BOOL] = {
};
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
CreateTerminal: Commander.CommandProc =
BEGIN ENABLE UNWIND => MyUnLock[];
MyLock[];
InternalCreateTerminal[];
MyUnLock[];
END;
Init:
PROC [] =
BEGIN
terminalIcon ← Icons.NewIconFromFile["TerminalIO.icons", 0 ! RuntimeError.UNCAUGHT => CONTINUE];
inputIcon ← Icons.NewIconFromFile["TerminalIO.icons", 1 ! RuntimeError.UNCAUGHT => CONTINUE];
AddLock[MyLock, MyUnLock];
AddLock[ReaderLock, ReaderUnLock];
Commander.Register[key: "Terminal", proc: CreateTerminal, doc: "Create terminal viewer if it does not yet exist"];
END;
Init[];
END.