MODULE editor; (* based on the kilo editor building course*) (* Step 39 *) (*** IMPORTS ***) IMPORT IO, termios, FIO, libc, CharClass, NumberIO, ASCII ; IMPORT STextIO, Strings, SYSTEM, SplitV1, Storage, MemUtils; (*** data ***) TYPE abufType = RECORD b : SYSTEM.ADDRESS; len : CARDINAL; END; abufTypePtr = POINTER TO abufType; CONST (* escape sequences length *) clearScreenStrLen = 4; cursorHomeStrLen = 3; tildeStrLen = 3; requestCursorPosStrLen = 4; cursorOnStrLen = 6; cursorOffStrLen = 6; VAR editorConfig : RECORD screenrows : CARDINAL; screencols : CARDINAL; posX : CARDINAL; posY : CARDINAL; theTermios : termios.TERMIOS; END; c : CHAR; TCSAFLUSH : INTEGER; theStructure: SplitV1.Structure; i : CARDINAL; (* escape sequences *) clearScreenStr : ARRAY [0..3] OF CHAR; cursorHomeStr : ARRAY [0..2] OF CHAR; tildeStr : ARRAY [0..2] OF CHAR; requestCursorPosStr : ARRAY [0..3] OF CHAR; cursorposStr : ARRAY [0..15] OF CHAR; cursorOnStr : ARRAY [0..5] OF CHAR; cursorOffStr : ARRAY [0..5] OF CHAR; tmpString : ARRAY [0..15] OF CHAR; tmpString1 : ARRAY [0..15] OF CHAR; tmpString2 : Strings.String1; cc : Strings.String1; (******************** Utilities *****************) 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; (******************** Buffer ******************) PROCEDURE abufInit( VAR ptr : abufTypePtr); BEGIN Storage.ALLOCATE(ptr,SYSTEM.TSIZE(abufType) ); ptr^.b := NIL; ptr^.len := 0; END abufInit; PROCEDURE abAppend (VAR ptr : abufTypePtr; theSourceAdress: SYSTEM.ADDRESS; theSourceLen : CARDINAL ); BEGIN IF ptr <> NIL THEN IF ptr^.b = NIL THEN Storage.ALLOCATE(ptr^.b,theSourceLen); ELSE Storage.REALLOCATE( ptr^.b,ptr^.len + theSourceLen); END; MemUtils.MemCopy(theSourceAdress,theSourceLen, SYSTEM.ADDADR(ptr^.b,ptr^.len)); ptr^.len := ptr^.len + theSourceLen; END; END abAppend; PROCEDURE abFree (VAR ptr : abufTypePtr); BEGIN Storage.DEALLOCATE(ptr^.b, ptr^.len); Storage.DEALLOCATE(ptr,SYSTEM.TSIZE(abufType)); END abFree; (*** Terminal ***) PROCEDURE CtrlKey(c: CHAR) : CARDINAL; 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.write(FIO.StdOut,SYSTEM.ADR(cursorHomeStr), cursorHomeStrLen); libc.write(FIO.StdOut,SYSTEM.ADR(clearScreenStr), clearScreenStrLen); libc.perror(s); FOR i := 0 TO Strings.Length(s) DO IO.Write(s[i]) END; HALT END die; PROCEDURE disablerawMode() : INTEGER; VAR result : INTEGER; BEGIN IO.BufferedMode(0,TRUE); IO.BufferedMode(1,TRUE); result := termios.tcsetattr(FIO.StdIn, TCSAFLUSH, editorConfig.theTermios); IF result = -1 THEN editorConfig.theTermios := termios.KillTermios(editorConfig.theTermios); die("tcsetattr") ELSE RETURN result END; END disablerawMode; PROCEDURE enablerawMode; BEGIN libc.atexit(disablerawMode); editorConfig.theTermios := termios.InitTermios(); IF termios.tcgetattr(FIO.StdIn, editorConfig.theTermios) = -1 THEN die("tcgetattr") 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; PROCEDURE editorProcessKeypress; VAR c : CHAR; BEGIN c := editorReadKey(); CASE ORD(c) OF 17 : libc.write(FIO.StdOut,SYSTEM.ADR(cursorHomeStr), cursorHomeStrLen); libc.write(FIO.StdOut,SYSTEM.ADR(clearScreenStr), clearScreenStrLen); HALT; END END editorProcessKeypress; (*** Output ***) PROCEDURE editorDrawRows(VAR ptr : abufTypePtr); CONST tilde = "~"; VAR y : CARDINAL; BEGIN (* display of the page *) FOR y := 1 TO editorConfig.screenrows -1 DO (* for all lines except the last one *) abAppend(ptr,SYSTEM.ADR(tildeStr), tildeStrLen); END; abAppend(ptr,SYSTEM.ADR(tilde), 1); END editorDrawRows; PROCEDURE editorRefreshScreen; VAR ptr : abufTypePtr; BEGIN abufInit(ptr); abAppend(ptr,SYSTEM.ADR(cursorOffStr), cursorOffStrLen); abAppend(ptr,SYSTEM.ADR(cursorHomeStr), cursorHomeStrLen); editorDrawRows(ptr); abAppend(ptr,SYSTEM.ADR(cursorHomeStr), cursorHomeStrLen); abAppend(ptr,SYSTEM.ADR(cursorOnStr), cursorOnStrLen); libc.write(FIO.StdOut,ptr^.b,ptr^.len); abFree(ptr); END editorRefreshScreen; PROCEDURE requestCursorPosition(VAR x,y : CARDINAL); VAR c : CHAR; str : ARRAY [0..15] OF CHAR; BEGIN str := ""; SplitV1.InitStructure(theStructure); libc.write(FIO.StdOut,SYSTEM.ADR(requestCursorPosStr),requestCursorPosStrLen); (* getting answer *) str := ""; REPEAT IO.Read(c); cc[0] := c; Strings.Concat(str,cc,str); UNTIL c = "R"; Strings.Delete(str,0,2); Strings.Delete(str,Strings.Length(str) -1 ,1); SplitV1.SplitStr(str, ";", theStructure); NumberIO.StrToCard(theStructure[0].element,y); NumberIO.StrToCard(theStructure[1].element,x); END requestCursorPosition; PROCEDURE cursorpos(x,y : CARDINAL); BEGIN tmpString1 := ""; Card2Str(y,tmpString1); Strings.Concat(cursorposStr,tmpString1, cursorposStr); tmpString2[0] := ";"; Strings.Concat(cursorposStr,tmpString2, cursorposStr); tmpString1 := ""; Card2Str(x,tmpString1); Strings.Concat(cursorposStr,tmpString1, cursorposStr); tmpString2[0] := "H"; Strings.Concat(cursorposStr,tmpString2, cursorposStr); libc.write(FIO.StdOut,SYSTEM.ADR(cursorposStr),Strings.Length(cursorposStr)); END cursorpos; PROCEDURE getWindowSize(VAR y : CARDINAL; VAR x: CARDINAL); BEGIN cursorpos(999,999); requestCursorPosition(x,y); END getWindowSize; (*** Input ***) (*** Init ***) PROCEDURE InitScreenEscapes; VAR temp : ARRAY[0..10] OF CHAR; BEGIN cursorHomeStr := ""; Strings.Concat(cursorHomeStr,ASCII.esc,cursorHomeStr); Strings.Concat(cursorHomeStr,"[H",cursorHomeStr); clearScreenStr := ""; Strings.Concat(clearScreenStr,ASCII.esc,clearScreenStr); Strings.Concat(clearScreenStr,"[2J",clearScreenStr); tildeStr[0] := "~"; tildeStr[1] := CHR(10); tildeStr[2] := CHR(13); cursorposStr := ""; Strings.Concat(cursorposStr,ASCII.esc,cursorposStr); Strings.Concat(cursorposStr,"[",cursorposStr); requestCursorPosStr := ""; Strings.Concat(requestCursorPosStr,ASCII.esc,requestCursorPosStr); Strings.Concat(requestCursorPosStr,"[6n",requestCursorPosStr); cursorOnStr := ""; Strings.Concat(cursorOnStr,ASCII.esc,cursorOnStr); Strings.Concat(cursorOnStr,"[?25h",cursorOnStr); cursorOffStr := ""; Strings.Concat(cursorOffStr,ASCII.esc,cursorOffStr); Strings.Concat(cursorOffStr,"[?25l",cursorOffStr); END InitScreenEscapes; PROCEDURE InitEditor; BEGIN TCSAFLUSH := termios.tcsflush (); InitScreenEscapes; editorConfig.posX := 1; editorConfig.posY := 1; getWindowSize(editorConfig.screenrows, editorConfig.screencols); editorConfig.posX := 1; editorConfig.posY := 1; libc.write(FIO.StdOut,SYSTEM.ADR(clearScreenStr),4); (* NumberIO.WriteCard(editorConfig.screenrows, 5); NumberIO.WriteCard(editorConfig.screencols, 5); *) END InitEditor; BEGIN enablerawMode; InitEditor; editorRefreshScreen; LOOP editorProcessKeypress; editorRefreshScreen; END; END editor.