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: BOOLFALSE;
in, out, editedIn: IO.STREAMNIL;
terminalIcon: Icons.IconFlavor;
inputIcon: Icons.IconFlavor;
del: CHAR = LOOPHOLE[127, CHAR];
delRope: ROPE = Rope.FromChar[del];
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
--Terminals own locking procedures
moduleEntered: BOOLFALSE;
--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: ROPENIL] =
BEGIN
InternalWriteRope[text];
IF terminalParent.iconic THEN {
terminalParent.column ← right;
ViewerOps.OpenIcon[terminalParent];
};
InputFocus.PushInputFocus[terminalParent];
ViewerTools.SetSelection[terminalParent];
END;
RequestRope: PUBLIC PROC [text: ROPENIL] 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 => {done�ort; IO.Reset[editedIn]; CONTINUE};
END;
PrepareRequest[text];
r ← IO.GetLineRope[editedIn];
InputFocus.PopInputFocus[];
IF Rope.Find[r, delRope]>=0 THEN done�ort;
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: ROPENIL] 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 => {done�ort; IO.Reset[in]; CONTINUE};
END;
PrepareRequest[text];
ch ← IO.GetChar[in]; -- returns immediately, no CR to eat
InputFocus.PopInputFocus[];
IF ch=del THEN done�ort;
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: ROPENIL] 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 => {done�ort; 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 done�ort;
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: ROPENIL, choice: LIST OF ROPE, text: ROPENIL, 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: ROPENIL, timeOut: NAT ← 0, onTimeOut: BOOLFALSE] 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: ROPENIL, timeOut: NAT ← 0] RETURNS [ROPE] =
BEGIN
IF timeOut=0 THEN RETURN [RequestRope[text]];
RETURN [MyRequestRopeWithTimeout[text, timeOut]];
END;
MyRequestRopeWithTimeout: PROC [text: ROPENIL, timeOut: NAT] RETURNS [ROPENIL] =
BEGIN
ENABLE UNWIND => UnLock[];
result: Rope.ROPENIL;
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 => {d�ort; 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: ROPENIL] =
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: ROPENIL, v1, v2, v3, v4, v5: IO.Value] =
BEGIN
WriteRope[IO.PutFR[format, v1, v2, v3, v4, v5]];
END;
WriteF1: PUBLIC PROC [format: ROPENIL, 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.