Syntax10.Scn.Fnt Syntax10i.Scn.Fnt Syntax10b.Scn.Fnt MODULE Terminals; (*Copyright (c) Robert Griesemer and Wolfgang Weck, 1992-95 /gri, ww, mf 30.9.93*) IMPORT Texts; CONST (* geometry *) Height*=24; MaxWidth*=132; (* attributes *) none*=0; bold*=1; underline*=2; blinking*=4; reverse*=8; (* notifier id's *) update*=1; moveCursor*=2; scroll*=3; (* control characters *) ENQ=5X; BS=08X; HT=09X; LF=0AX; VT=0BX; FF=0CX; CR=0DX; CAN=18X; SUB=1AX; ESC=1BX; DEL=7FX; NSC=91X; BRK=0ACX; (* flags *) ansi*=0; applic*=1; lineFeed*=2; insert*=3; relative*=4; cursorKeys*=5; autowrap*=6; TYPE Char*=RECORD ch*: CHAR; attr*: SHORTINT END; Line*=POINTER TO RECORD len*: INTEGER; ch*: ARRAY MaxWidth+1 OF Char END; Location*=RECORD line*, col*: INTEGER END; Terminal*=POINTER TO TerminalDesc; Sender*=PROCEDURE (T: Terminal; ch: CHAR); Breaker*=PROCEDURE (T: Terminal); Notifier*=PROCEDURE (T: Terminal; op, fromLine, fromCol, toLine, toCol: INTEGER; oldCursor: Location); TerminalDesc*=RECORD (* text *) attr*: SHORTINT; width*: INTEGER; top, bottom: INTEGER; cursor*: Location; wrapBefore: BOOLEAN; notify*: Notifier; line*: ARRAY Height+1 OF Line; (* state machine *) flags*: SET; send: Sender; break: Breaker; state, parPos, strPos: INTEGER; parBuf: ARRAY 32 OF CHAR; strBuf: ARRAY 256 OF CHAR; tabs: ARRAY MaxWidth+1 OF BOOLEAN; answerback*: ARRAY 32 OF CHAR; oldAttr: SHORTINT; oldCursor: Location; oldRelative: BOOLEAN; cache*: Texts.Writer; text*: Texts.Text; pin*: LONGINT END; (* text operations *) PROCEDURE MoveLines (t: Terminal; top, bot, dH: INTEGER); VAR i, j, d, s, from, to: INTEGER; line: Line; buf: ARRAY Height OF Line; BEGIN IF (dH # 0) & (top <= bot) THEN IF top < 1 THEN top:=1 END; IF bot > Height THEN bot:=Height END; IF dH < 0 THEN d:=1; from:=top-dH; to:=top; s:=bot ELSE d:=-1; from:=bot-dH; to:=bot; s:=top END; i:=to; j:=0; REPEAT line:=t.line[i]; line.len:=0; buf[j]:=line; i:=i+d; INC(j) UNTIL i=from; s:=s+d; WHILE from # s DO t.line[to]:=t.line[from]; to:=to+d; from:=from+d END; j:=0; s:=ABS(dH); REPEAT t.line[to]:=buf[j]; to:=to+d; INC(j) UNTIL j=s; t.notify(t, scroll, top, dH, bot, 0, t.cursor) END END MoveLines; PROCEDURE Erase (t: Terminal; fromLine, fromCol, toLine, toCol: INTEGER); (* [from, to] *) VAR line: Line; i, j, len: INTEGER; char: Char; BEGIN char.ch:=" "; char.attr:=none; line:=t.line[fromLine]; len:=line.len; IF fromLine=toLine THEN IF fromCol <= len THEN IF toCol > len THEN line.len:=fromCol-1 ELSE j:=fromCol; WHILE j <= toCol DO line.ch[j]:=char; INC(j) END END END ELSE IF fromCol <= len THEN line.len:=fromCol-1 END; j:=fromLine+1; WHILE j < toLine DO t.line[j].len:=0; INC(j) END; line:=t.line[toLine]; len:=line.len; IF toCol >= len THEN line.len:=0 ELSE i:=1; WHILE i <= toCol DO line.ch[i].ch:=" "; INC(i) END END END; t.notify(t, update, fromLine, fromCol, toLine, toCol, t.cursor) END Erase; PROCEDURE DeleteChars (t: Terminal; n: INTEGER); (* including cursor character *) VAR c, j, len: INTEGER; line: Line; cursor: Location; BEGIN cursor:=t.cursor; c:=cursor.col; line:=t.line[cursor.line]; j:=c+n; len:=line.len; IF j <= len THEN REPEAT line.ch[c]:=line.ch[j]; INC(c); INC(j) UNTIL j > len; line.len:=c-1; t.notify(t, update, cursor.line, cursor.col, cursor.line, len, cursor) END END DeleteChars; PROCEDURE DeleteLines (t: Terminal; n: INTEGER); (* including cursor line *) VAR top, bot: INTEGER; BEGIN top:=t.cursor.line; bot:=t.bottom; IF bot-top+1 < n THEN n:=bot-top+1 END; MoveLines(t, t.cursor.line, t.bottom, -n) END DeleteLines; PROCEDURE InsertChars (t: Terminal; n: INTEGER); (* move including cursor character, cursor not moved *) VAR i, j, len: INTEGER; line: Line; char: Char; cursor: Location; BEGIN cursor:=t.cursor; line:=t.line[cursor.line]; len:=line.len; j:=len+n; IF j >= t.width THEN j:=t.width END; i:=j-n; IF i < cursor.col THEN IF len >= cursor.col THEN line.len:=cursor.col-1 END ELSE line.len:=j; REPEAT line.ch[j]:=line.ch[i]; DEC(i); DEC(j) UNTIL i < cursor.col; char.ch:=" "; char.attr:=none; WHILE j >= cursor.col DO line.ch[j]:=char; DEC(j) END END; t.notify(t, update, cursor.line, cursor.col, cursor.line, line.len, cursor) END InsertChars; PROCEDURE InsertLines (t: Terminal; n: INTEGER); (* move including cursor line, cursor not moved *) VAR top, bot: INTEGER; BEGIN top:=t.cursor.line; bot:=t.bottom; IF bot-top+1 < n THEN n:=bot-top+1 END; MoveLines(t, top, bot, n) END InsertLines; PROCEDURE Scroll (t: Terminal; up: BOOLEAN); (* scroll one line within margins *) BEGIN IF up THEN MoveLines(t, t.top, t.bottom, -1) ELSE MoveLines(t, t.top, t.bottom, 1) END END Scroll; PROCEDURE SetCursor (t: Terminal; line, col: INTEGER; relative: BOOLEAN); (* (1, 1) means upper, left corner *) VAR newLoc: Location; BEGIN IF relative THEN IF line < t.top THEN line:=t.top ELSIF line > t.bottom THEN line:=t.bottom END ELSE IF line < 1 THEN line:=1 ELSIF line > Height THEN line:=Height END END; IF col < 1 THEN col:=1 ELSIF col > t.width THEN col:=t.width END; newLoc.line:=line; newLoc.col:=col; t.notify(t, moveCursor, t.cursor.line, t.cursor.col, line, col, newLoc); t.cursor:=newLoc; t.wrapBefore:=FALSE END SetCursor; PROCEDURE SetMargins (t: Terminal; top, bottom: INTEGER); (* [top, bottom] *) BEGIN t.top:=top; t.bottom:=bottom END SetMargins; PROCEDURE SetWidth (t: Terminal; width: INTEGER); VAR i: INTEGER; BEGIN t.width:=width; i:=1; WHILE i <= Height DO IF t.line[i].len > width THEN t.line[i].len:=width END; INC(i) END; IF t.cursor.col > width THEN t.cursor.col:=width END; t.notify(t, update, 1, 1, Height, t.width, t.cursor) END SetWidth; PROCEDURE SetAttribute (t: Terminal; attr: SHORTINT); BEGIN IF attr=none THEN t.attr:=attr ELSE t.attr:=t.attr+attr END END SetAttribute; PROCEDURE PutChar(t: Terminal; VAR cursor: Location; VAR wrapBefore: BOOLEAN; ch: Char; VAR dH: INTEGER); VAR len: INTEGER; line: Line; char: Char; BEGIN IF wrapBefore THEN IF cursor.col=t.width THEN INC(cursor.line); cursor.col:=1; IF cursor.line > t.bottom THEN Scroll(t, TRUE); DEC(cursor.line); DEC(dH) END ELSE INC(cursor.col) END END; line:=t.line[cursor.line]; len:=line.len; IF (ch.ch=" ") & (ch.attr=none) & (cursor.col=len) THEN DEC(line.len) ELSIF (ch.ch # " ") OR (ch.attr # none) OR (cursor.col < len) THEN IF len <= cursor.col THEN line.len:=cursor.col; char.ch:=" "; char.attr:=none; INC(len); WHILE len < cursor.col DO line.ch[len]:=char; INC(len) END END; line.ch[cursor.col]:=ch END; IF cursor.col=t.width THEN wrapBefore:=autowrap IN t.flags ELSE INC(cursor.col); wrapBefore:=FALSE END END PutChar; PROCEDURE WriteString (t: Terminal; s: ARRAY OF CHAR; n: INTEGER); (* writes n characters at curs. pos. *) VAR i, dH: INTEGER; cursor, oldCur: Location; char: Char; BEGIN cursor:=t.cursor; oldCur:=cursor; char.attr:=t.attr; dH:=0; i:=0; WHILE i < n DO char.ch:=s[i]; PutChar(t, cursor, t.wrapBefore, char, dH); INC(i) END; t.cursor:=cursor; IF oldCur.line+dH < 1 THEN t.notify(t, update, 1, oldCur.col, cursor.line, cursor.col, oldCur) ELSE t.notify(t, update, oldCur.line+dH, oldCur.col, cursor.line, cursor.col, oldCur) END END WriteString; PROCEDURE EFill (t: Terminal); VAR i, j, w: INTEGER; line: Line; char: Char; BEGIN i:=1; w:=t.width; char.ch:="E"; char.attr:=none; WHILE i <= Height DO j:=1; line:=t.line[i]; line.len:=w; WHILE j <= w DO line.ch[j]:=char; INC(j) END; INC(i) END; t.notify(t, update, 1, 1, Height, t.width, t.cursor) END EFill; (* sequence interpretation *) PROCEDURE DelLast (T: Terminal); BEGIN IF T.cache.buf.len > 0 THEN Texts.Append(T.text, T.cache.buf) END; IF T.text.len > 0 THEN Texts.Delete(T.text, T.text.len-1, T.text.len) END END DelLast; PROCEDURE Reset* (T: Terminal); VAR i: INTEGER; BEGIN T.flags:={ansi, autowrap}; T.state:=0; T.strPos:=0; i:=1; WHILE i <= MaxWidth DO T.tabs[i]:=i MOD 8=1; INC(i) END; T.answerback:="*** Hello World ***"; T.oldAttr:=none; T.oldCursor.line:=1; T.oldCursor.col:=1; T.oldRelative:=FALSE; Erase(T, 1, 1, Height, T.width); SetMargins(T, 1, Height); SetCursor(T, 1, 1, FALSE) END Reset; PROCEDURE SendStr (T: Terminal; s: ARRAY OF CHAR); VAR i: INTEGER; BEGIN i:=0; WHILE s[i] # 0X DO T.send(T, s[i]); INC(i) END END SendStr; PROCEDURE SendInt (T: Terminal; x: INTEGER); VAR i: INTEGER; d: ARRAY 3 OF CHAR; BEGIN i:=0; REPEAT d[i]:=CHR(x MOD 10+ORD("0")); x:=x DIV 10; INC(i) UNTIL x=0; WHILE i > 0 DO DEC(i); T.send(T, d[i]) END END SendInt; PROCEDURE Update (T: Terminal); BEGIN IF T.strPos > 0 THEN WriteString(T, T.strBuf, T.strPos); T.strPos:=0 END END Update; PROCEDURE Flush* (T: Terminal); BEGIN Update(T); IF (T.text # NIL) & (T.cache.buf.len > 0) THEN Texts.Append(T.text, T.cache.buf) END END Flush; PROCEDURE Write (T: Terminal; ch: CHAR); BEGIN IF T.strPos >= LEN(T.strBuf) THEN Update(T) END; T.strBuf[T.strPos]:=ch; INC(T.strPos) END Write; PROCEDURE ESCSequence (T: Terminal; last: CHAR); VAR ch: CHAR; BEGIN ch:=T.parBuf[0]; IF ansi IN T.flags THEN IF ("7" <= last) & (last <= "c") THEN CASE last OF | "7": T.oldAttr:=T.attr; T.oldCursor:=T.cursor; T.oldRelative:=relative IN T.flags | "8": IF ch="#" THEN EFill(T) ELSE SetAttribute(T, T.oldAttr); SetCursor(T, T.oldCursor.line, T.oldCursor.col, FALSE); IF T.oldRelative THEN INCL(T.flags, relative) ELSE EXCL(T.flags, relative) END END | "=": INCL(T.flags, applic) | ">": EXCL(T.flags, applic) | "D": IF T.cursor.line=T.bottom THEN Scroll(T, TRUE) ELSE SetCursor(T, T.cursor.line+1, T.cursor.col, FALSE) END | "E": IF T.cursor.line=T.bottom THEN Scroll(T, TRUE); SetCursor(T, T.cursor.line, 1, FALSE) ELSE SetCursor(T, T.cursor.line+1, 1, FALSE) END | "H": T.tabs[T.cursor.col]:=TRUE | "M": IF T.cursor.line=T.top THEN Scroll(T, FALSE) ELSE SetCursor(T, T.cursor.line-1, T.cursor.col, relative IN T.flags) END | "Z": T.send(T, ESC); SendStr(T, "[?1;2c") (* VT100 *) | "c": Reset(T) | "9" .. "<", "?" .. "C", "F", "G", "I" .. "L", "N" .. "Y", "[" .. "b": (* ignore *) END END ELSE (* VT52 mode *) IF ("<" <= last) & (last <= "Z") THEN CASE last OF | "<": INCL(T.flags, ansi) | "=": INCL(T.flags, applic) | ">": EXCL(T.flags, applic) | "A": SetCursor(T, T.cursor.line-1, T.cursor.col, FALSE) | "B": SetCursor(T, T.cursor.line+1, T.cursor.col, FALSE) | "C": SetCursor(T, T.cursor.line, T.cursor.col+1, FALSE) | "D": SetCursor(T, T.cursor.line, T.cursor.col-1, FALSE) | "H": SetCursor(T, 1, 1, FALSE) | "I": IF T.cursor.line=1 THEN Scroll(T, FALSE) ELSE SetCursor(T, T.cursor.line-1, T.cursor.col, FALSE) END | "J": Erase(T, T.cursor.line, T.cursor.col, Height, T.width) | "K": Erase(T, T.cursor.line, T.cursor.col, T.cursor.line, T.width) | "Z": T.send(T, ESC); SendStr(T, "/Z") | "?", "@", "E" .. "G", "L" .. "Y", "[", "\": (* ignore *) END END END END ESCSequence; PROCEDURE CSISequence (T: Terminal; last: CHAR); VAR ch: CHAR; pos, p1, p2: INTEGER; PROCEDURE Next; BEGIN ch:=T.parBuf[pos]; INC(pos) END Next; PROCEDURE Par (zeroVal: INTEGER): INTEGER; VAR x: INTEGER; BEGIN x:=0; IF ("0" <= ch) & (ch <= "9") THEN x:=ORD(ch)-ORD("0"); Next; WHILE ("0" <= ch) & (ch <= "9") & (x <= (MAX(INTEGER)-9) DIV 10) DO x:=10*x+ORD(ch)-ORD("0"); Next END; WHILE ("0" <= ch) & (ch <= "9") DO Next END; IF ch=";" THEN Next END ELSIF ch=";" THEN Next END; IF x=0 THEN x:=zeroVal END; RETURN x END Par; BEGIN T.parBuf[T.parPos]:=0FFX; pos:=0; Next; IF ( "@" <= last) & (last <= "y") THEN CASE last OF | "@": InsertChars(T, Par(1)) | "A": SetCursor(T, T.cursor.line-Par(1), T.cursor.col, relative IN T.flags) | "B": SetCursor(T, T.cursor.line+Par(1), T.cursor.col, relative IN T.flags) | "C": SetCursor(T, T.cursor.line, T.cursor.col+Par(1), relative IN T.flags) | "D": SetCursor(T, T.cursor.line, T.cursor.col-Par(1), relative IN T.flags) | "H", "f": p1:=Par(1); p2:=Par(1); IF relative IN T.flags THEN SetCursor(T, T.top+p1-1, p2, TRUE) ELSE SetCursor(T, p1, p2, FALSE) END | "J": p1:=Par(0); IF p1=0 THEN Erase(T, T.cursor.line, T.cursor.col, Height, T.width) ELSIF p1=1 THEN Erase(T, 1, 1, T.cursor.line, T.cursor.col) ELSIF p1=2 THEN Erase(T, 1, 1, Height, T.width) END | "K": p1:=Par(0); IF p1=0 THEN Erase(T, T.cursor.line, T.cursor.col, T.cursor.line, T.width) ELSIF p1=1 THEN Erase(T, T.cursor.line, 1, T.cursor.line, T.cursor.col) ELSIF p1=2 THEN Erase(T, T.cursor.line, 1, T.cursor.line, T.width) END | "L": InsertLines(T, Par(1)) | "M": DeleteLines(T, Par(1)) | "P": DeleteChars(T, Par(1)) | "c": IF Par(0)=0 THEN T.send(T, ESC); SendStr(T, "[?1;2c") (* VT100 *) END | "g": REPEAT p1:=Par(0); IF p1=0 THEN T.tabs[T.cursor.col]:=FALSE ELSIF p1=3 THEN p1:=1; WHILE p1 <= MaxWidth DO T.tabs[p1]:=FALSE; INC(p1) END END UNTIL pos > T.parPos | "h": IF ch="?" THEN Next; REPEAT p1:=Par(0); IF p1=1 THEN INCL(T.flags, cursorKeys) ELSIF p1=3 THEN Erase(T, 1, 1, Height, T.width); SetWidth(T, MaxWidth); SetMargins(T, 1, Height); SetCursor(T, 1, 1, relative IN T.flags) ELSIF p1=6 THEN INCL(T.flags, relative); SetCursor(T, 1, 1, TRUE) ELSIF p1=7 THEN INCL(T.flags, autowrap) END UNTIL pos > T.parPos ELSE REPEAT p1:=Par(0); IF p1=4 THEN INCL(T.flags, insert) ELSIF p1=20 THEN INCL(T.flags, lineFeed) END UNTIL pos > T.parPos END | "l": IF ch="?" THEN Next; REPEAT p1:=Par(0); IF p1=1 THEN EXCL(T.flags, cursorKeys) ELSIF p1=2 THEN EXCL(T.flags, ansi) ELSIF p1=3 THEN Erase(T, 1, 1, Height, T.width); SetWidth(T, 80); SetMargins(T, 1, Height); SetCursor(T, 1, 1, relative IN T.flags) ELSIF p1=6 THEN EXCL(T.flags, relative); SetCursor(T, 1, 1, FALSE) ELSIF p1=7 THEN EXCL(T.flags, autowrap) END UNTIL pos > T.parPos ELSE REPEAT p1:=Par(0); IF p1=4 THEN EXCL(T.flags, insert) ELSIF p1=20 THEN EXCL(T.flags, lineFeed) END UNTIL pos > T.parPos END | "m": REPEAT p1:=Par(0); IF p1=0 THEN SetAttribute(T, none) ELSIF p1=1 THEN SetAttribute(T, bold) ELSIF p1=4 THEN SetAttribute(T, underline) ELSIF p1=5 THEN SetAttribute(T, blinking) ELSIF p1=7 THEN SetAttribute(T, reverse) END UNTIL pos > T.parPos | "n": IF ch="?" THEN Next; IF Par(0)=15 THEN (* printer status report *) T.send(T, ESC); SendStr(T, "[?13n") END ELSE p1:=Par(0); IF p1=5 THEN (* terminal status report *) T.send(T, ESC); SendStr(T, "[0n") ELSIF p1=6 THEN (* cursor position report *) T.send(T, ESC); T.send(T, "["); IF relative IN T.flags THEN SendInt(T, T.cursor.line-T.top+1) ELSE SendInt(T, T.cursor.line) END; T.send(T, ";"); SendInt(T, T.cursor.col); T.send(T, "R") END END | "r": p1:=Par(0); p2:=Par(0); IF (p1=0) & (p2=0) THEN p1:=1; p2:=Height END; SetMargins(T, p1, p2); SetCursor(T, 1, 1, TRUE) |"y": p1:=Par(0); IF p1=2 THEN p1:=Par(0); IF p1=1 THEN Reset(T) ELSE T.send(T, ESC); SendStr(T, "[0n") END END | "E" .. "G", "I", "N", "O", "Q" .. "b", "d", "e", "i" .. "k", "o" .. "q", "s" .. "x": (* ignore *) END END END CSISequence; PROCEDURE Receive* (T: Terminal; ch: CHAR); VAR p: INTEGER; BEGIN ch:=CHR(ORD(ch) MOD 128); IF ch < " " THEN (* interpret control characters immediately *) CASE ch OF | ENQ: SendStr(T, T.answerback) | BS: Update(T); SetCursor(T, T.cursor.line, T.cursor.col-1, FALSE); IF T.text # NIL THEN DelLast(T) END | HT: Update(T); p:=T.cursor.col+1; WHILE (p <= T.width) & ~T.tabs[p] DO INC(p) END; SetCursor(T, T.cursor.line, p, FALSE); IF T.text # NIL THEN Texts.Write(T.cache, HT) END | LF, VT, FF: Update(T); IF T.cursor.line=T.bottom THEN Scroll(T, TRUE); IF lineFeed IN T.flags THEN SetCursor(T, T.cursor.line, 1, FALSE) END ELSIF lineFeed IN T.flags THEN SetCursor(T, T.cursor.line+1, 1, FALSE) ELSE SetCursor(T, T.cursor.line+1, T.cursor.col, FALSE) END; IF T.text # NIL THEN Texts.Write(T.cache, CR) END | CR: Update(T); SetCursor(T, T.cursor.line, 1, FALSE) | CAN, SUB: (* cancel *) T.state:=0 | ESC: T.state:=1 | 0X .. 4X, 6X, 7X, 0EX .. 17X, 19X, 1CX .. 1FX: (* ignore *) END ELSE (* drive state machine *) CASE T.state OF | 0: (* normal characters *) IF ch=DEL THEN IF T.cursor.col > 1 THEN Update(T); SetCursor(T, T.cursor.line, T.cursor.col-1, FALSE); DeleteChars(T, 1) END; IF T.text # NIL THEN DelLast(T) END ELSE IF insert IN T.flags THEN Update(T); InsertChars(T, 1) END; Write(T, ch); IF T.text # NIL THEN Texts.Write(T.cache, ch) END END | 1: (* sequence introduction *) IF ch="[" THEN T.state:=3; T.parPos:=0 ELSIF ~(ansi IN T.flags) & (ch="Y") THEN T.state:=4; T.parPos:=0 ELSIF (" " <= ch) & (ch <= "/") THEN T.state:=2; T.parBuf[0]:=ch; T.parPos:=1 ELSIF ("0" <= ch) & (ch <= "~") THEN T.state:=2; T.parPos:=0; Update(T); ESCSequence(T, ch); T.state:=0 ELSE (* error *) T.state:=0 END | 2: (* ESC sequence *) IF ("0" <= ch) & (ch <= "~") THEN Update(T); ESCSequence(T, ch); T.state:=0 ELSIF T.parPos < LEN(T.parBuf) THEN T.parBuf[T.parPos]:=ch; INC(T.parPos) ELSE (* error *) T.state:=0 END | 3: (* CSI sequence *) IF ("@" <= ch) & (ch <= "~") THEN Update(T); CSISequence(T, ch); T.state:=0 ELSIF T.parPos < LEN(T.parBuf) THEN T.parBuf[T.parPos]:=ch; INC(T.parPos) ELSE (* error *) T.state:=0 END | 4: (* VT52 ESC Y sequence *) IF T.parPos=0 THEN T.parBuf[0]:=ch; T.parPos:=1 ELSE Update(T); SetCursor(T, ORD(T.parBuf[0])-31, ORD(ch)-31, FALSE); T.state:=0 END END END END Receive; PROCEDURE Send* (T: Terminal; ch: CHAR); BEGIN IF T.text # NIL THEN T.pin:=T.text.len END; IF ch <= DEL THEN (* normal ASCII *) T.send(T, ch); IF (ch=CR) & (lineFeed IN T.flags) THEN T.send(T, LF) END ELSIF (ch=BRK) OR (ch=0ADX) THEN T.break(T) ELSIF (ch=0AEX) OR (ch=0AFX) THEN SendStr(T, T.answerback) ELSIF (0C1X <= ch) & (ch <= 0C4X) THEN (* cursor keys *) T.send(T, ESC); IF ansi IN T.flags THEN IF cursorKeys IN T.flags THEN T.send(T, "O") ELSE T.send(T, "[") END END; T.send(T, CHR(ORD(ch)-128)) ELSIF ch=80X THEN T.send(T, "A") ELSIF ch=81X THEN T.send(T, "O") ELSIF ch=82X THEN T.send(T, "U") ELSIF ch=83X THEN T.send(T, "a") ELSIF ch=84X THEN T.send(T, "o") ELSIF ch=85X THEN T.send(T, "u") ELSIF ch=9FX THEN T.send(T, " ") END END Send; PROCEDURE SendString* (T: Terminal; VAR s: ARRAY OF CHAR); VAR i: INTEGER; BEGIN i:=0; WHILE s[i] # 0X DO Send(T, s[i]); INC(i) END END SendString; PROCEDURE SendText* (T: Terminal; text: Texts.Text; beg, end: LONGINT); VAR R: Texts.Reader; ch: CHAR; BEGIN Texts.OpenReader(R, text, beg); WHILE Texts.Pos(R) < end DO Texts.Read(R, ch); Send(T, ch) END END SendText; PROCEDURE Open* (T: Terminal; text: Texts.Text; send: Sender; break: Breaker; notify: Notifier); VAR l: Line; i: INTEGER; BEGIN T.width:=80; T.top:=1; T.bottom:=Height; T.cursor.line:=1; T.cursor.col:=1; T.attr:=none; T.wrapBefore:=FALSE; T.notify:=notify; i:=Height; REPEAT NEW(l); T.line[i]:=l ; DEC(i) UNTIL i=0; Reset(T); T.text:=text; Texts.OpenWriter(T.cache); T.pin:=0; T.send:=send; T.break:=break END Open; END Terminals.