MODULE editor; (* based on the kilo editor building course*) (* Step 28 *) (******************** INCLUDES ******************) IMPORT FIO, IO, SYSTEM, termios, NumberIO; IMPORT CharClass, libc, ASCII; IMPORT errno; IMPORT VT100; FROM Strings IMPORT Concat, String1, Length, Delete; IMPORT SplitV1; IMPORT Delay; (******************** DATA ******************) VAR c : CHAR; p : POINTER TO CHAR; result : INTEGER; number : CARDINAL; editorConfig : RECORD screenrows : CARDINAL; screencols : CARDINAL; theTermios : termios.TERMIOS; raw : termios.TERMIOS; END; (* testing *) test : BOOLEAN; clearScreenStr : ARRAY[0..6] OF CHAR; cursorHomeStr : ARRAY[0..2] OF CHAR; cursorposStr : ARRAY[0..9] OF CHAR; requestCursorPosStr : ARRAY[0..15] OF CHAR; adresse : SYSTEM.ADDRESS; X,Y : CARDINAL; (******************** Utilities *****************) PROCEDURE WriteString( str : ARRAY OF CHAR); (* IO procedures : direct to libc in unbuffered mode *) VAR i : CARDINAL; BEGIN FOR i := 1 TO Length(str) DO IO.Write(str[i]) END; END WriteString; (******************** TERMINAL ******************) PROCEDURE CtrlKey(c: CHAR) : CARDINAL; (* return a value < ORD(" ") for the control keys *) VAR n : BITSET; BEGIN n := BITSET(ORD(c)); n := n * BITSET(1FH); RETURN CARDINAL(n) END CtrlKey; PROCEDURE die (s : ARRAY OF CHAR); BEGIN libc.perror(s); WriteString(clearScreenStr); HALT END die; PROCEDURE disablerawMode() : INTEGER; VAR TCSAFLUSH : INTEGER; result : INTEGER; BEGIN IO.BufferedMode(0,TRUE); IO.BufferedMode(1,TRUE); TCSAFLUSH := termios.tcsflush (); IF termios.tcsetattr(FIO.StdIn, TCSAFLUSH, editorConfig.theTermios) = -1 THEN die("tcsetattr") END; RETURN 1 END disablerawMode; PROCEDURE enablerawMode; VAR TCSAFLUSH : INTEGER; result : INTEGER; bresult : BOOLEAN; BEGIN TCSAFLUSH := termios.tcsflush (); libc.atexit(disablerawMode); bresult := termios.SetFlag(editorConfig.raw,termios.cs8, TRUE); IF termios.tcsetattr(FIO.StdIn, TCSAFLUSH, editorConfig.raw) = -1 THEN die("tcsetattr") END; IO.UnBufferedMode(0,TRUE); IO.UnBufferedMode(1,TRUE); END enablerawMode; PROCEDURE editorReadKey() : CHAR; VAR c : CHAR; BEGIN IO.Read(c); RETURN c END editorReadKey; (******************** output ******************) PROCEDURE editorDrawRows; VAR y : CARDINAL; BEGIN FOR y := 1 TO 24 DO IO.Write("~"); IO.Write(CHR(13)); IO.Write(CHR(10)); END; END editorDrawRows; PROCEDURE editorRefreshScreen; VAR i : CARDINAL; BEGIN (* modif *) (* WriteString(clearScreenStr); WriteString(cursorHomeStr); *) FOR i := 0 TO 3 DO IO.Write(clearScreenStr[i]) END; FOR i := 0 TO 3 DO IO.Write(cursorHomeStr[i]) END; editorDrawRows; (* WriteString(cursorHomeStr); *) FOR i := 0 TO 3 DO IO.Write(cursorHomeStr[i]) END; END editorRefreshScreen; PROCEDURE Card2Str ( x : CARDINAL; VAR str : ARRAY OF CHAR); BEGIN IF x < 10 THEN NumberIO.CardToStr(x,1,str); ELSIF x < 100 THEN NumberIO.CardToStr(x,2,str); ELSE NumberIO.CardToStr(x,3,str); END; END Card2Str; PROCEDURE requestCursorPosition(VAR x,y : CARDINAL); (* ESC[6n request cursor position (reports as ESC[#;#R) *) VAR str : ARRAY [0..15] OF CHAR; theStructure : SplitV1.Structure; c : CHAR; cc : String1; i : CARDINAL; BEGIN str := ""; SplitV1.InitStructure(theStructure); (* sending the request *) (* WriteString(requestCursorPosStr); *) Concat(str,ASCII.esc,str); Concat(str,"[6n", str); FOR i := 0 TO Length(str) DO IO.Write(str[i]) END; (* quering the answer *) str := ""; REPEAT IO.Read(c); cc[0] := c; Concat(str,cc,str); UNTIL c = "R"; (* extracting coodinates from the answer *) Delete(str,0,2); Delete(str,Length(str) -1 ,1); SplitV1.SplitStr(str, ";", theStructure); NumberIO.StrToCard(theStructure[0].element,x); NumberIO.StrToCard(theStructure[1].element,y); END requestCursorPosition; PROCEDURE cursorpos(x,y : CARDINAL); VAR tmpString : ARRAY[0..15] OF CHAR; tmpString1: ARRAY[0..15] OF CHAR; tmpString2: String1; i : CARDINAL; BEGIN cursorposStr := ""; Concat(cursorposStr, ASCII.esc,cursorposStr); Concat(cursorposStr, "[",cursorposStr); tmpString := ""; Concat(tmpString,cursorposStr, tmpString); tmpString1 := ""; Card2Str(y,tmpString1); Concat(tmpString,tmpString1, tmpString); tmpString2[0] := ";"; Concat(tmpString, tmpString2,tmpString); tmpString1 := ""; Card2Str(x,tmpString1); Concat(tmpString,tmpString1, tmpString); tmpString2[0] := "H"; Concat(tmpString, tmpString2,tmpString); FOR i := 0 TO Length(tmpString) DO IO.Write(tmpString[i]) END; END cursorpos; PROCEDURE getWindowSize(VAR y : CARDINAL; x: CARDINAL); BEGIN cursorpos(999,999); requestCursorPosition(x,y); END getWindowSize; (******************** input ******************) PROCEDURE editorProcessKeypress; VAR c : CHAR; b : BOOLEAN; BEGIN c := editorReadKey(); CASE ORD(c) OF 17 : HALT; END END editorProcessKeypress; (******************** INIT ******************) PROCEDURE InitScreenEscapes; VAR temp : ARRAY[0..10] OF CHAR; BEGIN cursorHomeStr := ""; Concat(cursorHomeStr,ASCII.esc,cursorHomeStr); Concat(cursorHomeStr,"[H",cursorHomeStr); clearScreenStr := ""; Concat(clearScreenStr,ASCII.esc,clearScreenStr); Concat(clearScreenStr,"[2J",clearScreenStr); cursorposStr := ""; Concat(cursorposStr,ASCII.esc,cursorposStr); Concat(cursorposStr,"[",cursorposStr); requestCursorPosStr := ""; Concat(requestCursorPosStr,ASCII.esc,requestCursorPosStr); Concat(requestCursorPosStr,"[6n",requestCursorPosStr); END InitScreenEscapes; PROCEDURE InitEditor; BEGIN getWindowSize(editorConfig.screenrows, editorConfig.screencols) END InitEditor; BEGIN InitScreenEscapes; (* termios state saved *) editorConfig.theTermios := termios.InitTermios(); (* a copy of it *) editorConfig.raw := termios.InitTermios(); IF termios.tcgetattr(FIO.StdIn, editorConfig.theTermios) = -1 THEN die("tcgetattr") END; IF termios.tcgetattr(FIO.StdIn, editorConfig.raw) = -1 THEN die("tcgetattr") END; (* end termios *) (* enablerawMode; InitEditor; LOOP editorRefreshScreen; editorProcessKeypress; editorRefreshScreen; END; *) cursorpos(30,30); requestCursorPosition(X,Y); result := disablerawMode(); NumberIO.WriteCard(X, 5); NumberIO.WriteCard(Y, 5); (* cleaning ...*) adresse := termios.KillTermios(editorConfig.raw); END editor.