| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 |
- {
- Double Commander
- -------------------------------------------------------------------------
- Virtual terminal emulator escape codes
- Alexander Koblov, 2021-2022
- Based on ComPort Library
- https://sourceforge.net/projects/comport
- Author:
- Dejan Crnila, 1998 - 2002
- Maintainers:
- Lars B. Dybdahl, 2003
- License:
- Public Domain
- }
- unit VTEmuEsc;
- {$mode delphi}
- interface
- uses
- Classes, LCLType;
- type
- // terminal character result
- TEscapeResult = (erChar, erCode, erNothing);
- // terminal escape codes
- TEscapeCode = (ecUnknown, ecNotCompleted, ecCursorUp, ecCursorDown, ecCursorLeft,
- ecCursorRight, ecCursorHome, ecCursorEnd, ecCursorMove, ecCursorMoveX, ecCursorMoveY,
- ecReverseLineFeed, ecAppCursorLeft, ecAppCursorRight, ecAppCursorUp, ecAppCursorDown,
- ecAppCursorHome, ecAppCursorEnd, ecCursorNextLine, ecCursorPrevLine, ecInsertKey,
- ecDeleteKey, ecPageUpKey, ecPageDownKey,
- ecMouseDown, ecMouseUp, ecEraseLineLeft, ecEraseLineRight, ecEraseScreenRight, ecEraseScreenLeft,
- ecEraseLine, ecEraseScreen, ecEraseChar, ecDeleteChar, ecSetTab, ecClearTab, ecClearAllTabs,
- ecIdentify, ecIdentResponse, ecQueryDevice, ecReportDeviceOK,
- ecReportDeviceFailure, ecQueryCursorPos, ecReportCursorPos,
- ecAttributes, ecSetMode, ecResetMode, ecReset, ecCharSet, ecSoftReset,
- ecSaveCaretAndAttr, ecRestoreCaretAndAttr, ecSaveCaret, ecRestoreCaret,
- ecTest, ecFuncKey, ecSetTextParams, ecScrollRegion, ecDeleteLine,
- ecInsertLine, ecKeypadApp, ecKeypadNum, ecScrollUp, ecScrollDown);
- // terminal escape codes processor
- TEscapeCodes = class
- private
- FCharacter: TUTF8Char;
- FCode: TEscapeCode;
- FData: string;
- FParams: TStrings;
- public
- constructor Create;
- destructor Destroy; override;
- function ProcessChar(const Ch: TUTF8Char): TEscapeResult; virtual; abstract;
- function EscCodeToStr(Code: TEscapeCode; AParams: TStrings): string; virtual; abstract;
- function GetParam(Num: Integer; AParams: TStrings): Integer;
- property Data: string read FData;
- property Code: TEscapeCode read FCode;
- property Character: TUTF8Char read FCharacter;
- property Params: TStrings read FParams;
- end;
- // VT52 escape codes
- TEscapeCodesVT52 = class(TEscapeCodes)
- private
- FInSequence: Boolean;
- function DetectCode(Str: string): TEscapeCode;
- public
- function ProcessChar(const Ch: TUTF8Char): TEscapeResult; override;
- function EscCodeToStr(Code: TEscapeCode; AParams: TStrings): string; override;
- end;
- // ANSI/VT100 escape codes
- TEscapeCodesVT100 = class(TEscapeCodes)
- private
- FInSequence: Boolean;
- FInExtSequence: Boolean;
- FInOscSequence: Boolean;
- function DetectCode(Str: string): TEscapeCode;
- function DetectExtCode(Str: string): TEscapeCode;
- function DetectOscCode(Str: string): TEscapeCode;
- public
- function ProcessChar(const Ch: TUTF8Char): TEscapeResult; override;
- function EscCodeToStr(Code: TEscapeCode; AParams: TStrings): string; override;
- end;
- implementation
- uses
- SysUtils;
- (*****************************************
- * TEscapeCodes class *
- *****************************************)
- constructor TEscapeCodes.Create;
- begin
- inherited Create;
- FParams := TStringList.Create;
- end;
- destructor TEscapeCodes.Destroy;
- begin
- FParams.Free;
- inherited Destroy;
- end;
- function TEscapeCodes.GetParam(Num: Integer; AParams: TStrings): Integer;
- begin
- if (AParams = nil) or (AParams.Count < Num) then
- Result := 1
- else
- try
- Result := StrToInt(AParams[Num - 1]);
- except
- Result := 1;
- end;
- end;
- (*****************************************
- * TEscapeCodesVT52 class *
- *****************************************)
- // process character
- function TEscapeCodesVT52.ProcessChar(const Ch: TUTF8Char): TEscapeResult;
- var
- TempCode: TEscapeCode;
- begin
- Result := erNothing;
- if not FInSequence then
- begin
- if Ch = #27 then
- begin
- FData := '';
- FInSequence := True;
- end
- else begin
- FCharacter := Ch;
- Result := erChar;
- end;
- end else
- begin
- FData := FData + Ch;
- TempCode := DetectCode(FData);
- if TempCode <> ecNotCompleted then
- begin
- FCode := TempCode;
- FInSequence := False;
- Result := erCode;
- end;
- end;
- end;
- // escape code to string
- function TEscapeCodesVT52.EscCodeToStr(Code: TEscapeCode; AParams: TStrings): string;
- begin
- case Code of
- ecCursorUp: Result := #27'A';
- ecCursorDown: Result := #27'B';
- ecCursorRight: Result := #27'C';
- ecCursorLeft: Result := #27'D';
- ecCursorHome: Result := #27'H';
- ecReverseLineFeed: Result := #27'I';
- ecEraseScreenRight: Result := #27'J';
- ecEraseLineRight: Result := #27'K';
- ecIdentify: Result := #27'Z';
- ecIdentResponse: Result := #27'/Z';
- ecCursorMove: Result := #27'Y' +
- Chr(GetParam(1, AParams) + 31) + Chr(GetParam(2, AParams) + 31);
- else
- Result := '';
- end;
- end;
- // get escape code from string
- function TEscapeCodesVT52.DetectCode(Str: string): TEscapeCode;
- begin
- Result := ecUnknown;
- case Str[1] of
- 'A': Result := ecCursorUp;
- 'B': Result := ecCursorDown;
- 'C': Result := ecCursorRight;
- 'D': Result := ecCursorLeft;
- 'H': Result := ecCursorMove;
- 'I': Result := ecReverseLineFeed;
- 'J': Result := ecEraseScreenRight;
- 'K': Result := ecEraseLineRight;
- 'Z': Result := ecIdentify;
- '/': begin
- if Length(Str) = 1 then
- Result := ecNotCompleted
- else
- if (Length(Str) = 2) and (Str = '/Z') then
- Result := ecIdentResponse;
- end;
- 'Y': begin
- if Length(Str) < 3 then
- Result := ecNotCompleted
- else
- begin
- Result := ecCursorMove;
- FParams.Add(IntToStr(Ord(Str[3]) - 31));
- FParams.Add(IntToStr(Ord(Str[2]) - 31));
- end;
- end;
- end;
- end;
- (*****************************************
- * TEscapeCodesVT100class *
- *****************************************)
- // process character
- function TEscapeCodesVT100.ProcessChar(const Ch: TUTF8Char): TEscapeResult;
- var
- TempCode: TEscapeCode;
- begin
- Result := erNothing;
- if not FInSequence then
- begin
- if Ch = #27 then
- begin
- FData := '';
- FInSequence := True;
- end
- else begin
- FCharacter := Ch;
- Result := erChar;
- end;
- end else
- begin
- FData := FData + Ch;
- TempCode := ecNotCompleted;
- if FInExtSequence then
- TempCode := DetectExtCode(FData)
- else if FInOscSequence then
- TempCode := DetectOscCode(FData)
- else
- // character [ after ESC defines extended escape code
- if FData[1] = '[' then
- FInExtSequence := True
- else if FData[1] = ']' then
- FInOscSequence := True
- else
- TempCode := DetectCode(FData);
- if TempCode <> ecNotCompleted then
- begin
- FCode := TempCode;
- FInSequence := False;
- FInExtSequence := False;
- FInOscSequence := False;
- Result := erCode;
- end;
- end;
- end;
- // escape code to string conversion
- function TEscapeCodesVT100.EscCodeToStr(Code: TEscapeCode;
- AParams: TStrings): string;
- var
- AKey: Integer;
- begin
- case Code of
- ecIdentify: Result := #27'[c';
- ecIdentResponse: Result := Format(#27'[?1;%dc', [GetParam(1, AParams)]);
- ecQueryCursorPos: Result := #27'[6n';
- ecReportCursorPos: Result := Format(#27'[%d;%dR', [GetParam(1, AParams), GetParam(2, AParams)]);
- ecQueryDevice: Result := #27'[5n';
- ecReportDeviceOK: Result := #27'[0n';
- ecReportDeviceFailure: Result := #27'[3n';
- ecCursorUp: Result := #27'[A';
- ecCursorDown: Result := #27'[B';
- ecCursorRight: Result := #27'[C';
- ecAppCursorLeft: Result := #27'OD';
- ecAppCursorUp: Result := #27'OA';
- ecAppCursorDown: Result := #27'OB';
- ecAppCursorRight: Result := #27'OC';
- ecAppCursorHome: Result := #27'OH';
- ecAppCursorEnd: Result := #27'OF';
- ecCursorLeft: Result := #27'[D';
- ecCursorHome: Result := #27'[H';
- ecCursorEnd: Result := #27'[F';
- ecCursorMove: Result := Format(#27'[%d;%df', [GetParam(1, AParams), GetParam(2, AParams)]);
- ecEraseScreenRight: Result := #27'[J';
- ecEraseLineRight: Result := #27'[K';
- ecEraseScreen: Result := #27'[2J';
- ecEraseLine: Result := #27'[2K';
- ecSetTab: Result := #27'H';
- ecClearTab: Result := #27'[g';
- ecClearAllTabs: Result := #27'[3g';
- ecAttributes: Result := #27'[m'; // popravi
- ecSetMode: Result := #27'[h';
- ecResetMode: Result := #27'[l';
- ecReset: Result := #27'c';
- ecSaveCaret: Result := #27'[s';
- ecRestoreCaret: Result := #27'[u';
- ecSaveCaretAndAttr: Result := #27'7';
- ecRestoreCaretAndAttr: Result := #27'8';
- ecTest: Result := #27'#8';
- ecFuncKey:
- begin
- AKey:= GetParam(1, AParams);
- case AKey of
- 0: Result := #27'OP';
- 1: Result := #27'OQ';
- 2: Result := #27'OR';
- 3: Result := #27'OS';
- 4: Result := #27'[15~';
- 5: Result := #27'[17~';
- 6: Result := #27'[18~';
- 7: Result := #27'[19~';
- 8: Result := #27'[20~';
- 9: Result := #27'[21~';
- 10: Result := #27'[23~';
- 11: Result := #27'[24~';
- end;
- end;
- ecInsertKey: Result := #27'[2~';
- ecDeleteKey: Result := #27'[3~';
- ecPageUpKey: Result := #27'[5~';
- ecPageDownKey: Result := #27'[6~';
- ecMouseDown:
- Result := Format(#27'[<%d;%d;%dM', [GetParam(1, AParams), GetParam(2, AParams), GetParam(3, AParams)]);
- ecMouseUp:
- Result := Format(#27'[<%d;%d;%dm', [GetParam(1, AParams), GetParam(2, AParams), GetParam(3, AParams)]);
- else
- Result := '';
- end;
- end;
- // get vt100 escape code from string
- function TEscapeCodesVT100.DetectCode(Str: string): TEscapeCode;
- begin
- if Length(Str) = 1 then
- case Str[1] of
- 'H': Result := ecSetTab;
- 'c': Result := ecReset;
- 'M': Result := ecReverseLineFeed;
- '7': Result := ecSaveCaretAndAttr;
- '8': Result := ecRestoreCaretAndAttr;
- '=': Result := ecKeypadApp;
- '>': Result := ecKeypadNum;
- '#': Result := ecNotCompleted;
- 'O': Result := ecNotCompleted;
- '(': Result := ecNotCompleted;
- else
- Result := ecUnknown;
- end
- else
- begin
- Result := ecUnknown;
- if Str = '#8' then
- Result := ecTest
- else if Str[1] = 'O' then
- begin
- case Str[2] of
- 'A': Result := ecAppCursorUp;
- 'B': Result := ecAppCursorDown;
- 'C': Result := ecAppCursorRight;
- 'D': Result := ecAppCursorLeft;
- 'H': Result := ecAppCursorHome;
- 'F': Result := ecAppCursorEnd;
- end;
- end
- else if Str[1] = '(' then
- begin
- FParams.Clear;
- FParams.Add(Str[2]);
- Result:= ecCharSet;
- end;
- end;
- end;
- // get extended vt100 escape code from string
- function TEscapeCodesVT100.DetectExtCode(Str: string): TEscapeCode;
- var
- LastCh: Char;
- TempParams: TStrings;
- procedure ParseParams(Str: string);
- var
- I: Integer;
- TempStr: string;
- begin
- I := 1;
- TempStr := '';
- while I <= Length(Str) do
- begin
- if (Str[I] = ';') and (TempStr <> '') then
- begin
- TempParams.Add(TempStr);
- TempStr := '';
- end
- else
- TempStr := TempStr + Str[I];
- Inc(I);
- end;
- if (TempStr <> '') then
- TempParams.Add(TempStr);
- end;
- function CodeEraseScreen: TEscapeCode;
- var
- Str: string;
- begin
- if TempParams.Count = 0 then
- Result := ecEraseScreenRight
- else
- begin
- Str := TempParams[0];
- case Str[1] of
- '0': Result := ecEraseScreenRight;
- '1': Result := ecEraseScreenLeft;
- '2': Result := ecEraseScreen;
- else
- Result := ecUnknown;
- end;
- end;
- TempParams.Clear;
- end;
- function CodeEraseLine: TEscapeCode;
- var
- Str: string;
- begin
- if TempParams.Count = 0 then
- Result := ecEraseLineRight
- else
- begin
- Str := TempParams[0];
- case Str[1] of
- '0': Result := ecEraseLineRight;
- '1': Result := ecEraseLineLeft;
- '2': Result := ecEraseLine;
- else
- Result := ecUnknown;
- end;
- end;
- TempParams.Clear;
- end;
- function CodeTab: TEscapeCode;
- var
- Str: string;
- begin
- if TempParams.Count = 0 then
- Result := ecClearTab
- else
- begin
- Str := TempParams[0];
- case Str[1] of
- '0': Result := ecClearTab;
- '3': Result := ecClearAllTabs;
- else
- Result := ecUnknown;
- end;
- end;
- TempParams.Clear;
- end;
- function CodeDevice: TEscapeCode;
- var
- Str: string;
- begin
- if TempParams.Count = 0 then
- Result := ecUnknown
- else
- begin
- Str := TempParams[0];
- case Str[1] of
- '5': Result := ecQueryDevice;
- '0': Result := ecReportDeviceOK;
- '3': Result := ecReportDeviceFailure;
- '6': Result := ecQueryCursorPos;
- else
- Result := ecUnknown;
- end;
- end;
- TempParams.Clear;
- end;
- function CodeIdentify: TEscapeCode;
- begin
- if (TempParams.Count = 0) or
- ((TempParams.Count = 1) and (TempParams[0] = '0'))
- then
- Result := ecIdentify
- else
- if (TempParams.Count = 2) and (TempParams[1] = '?1') then
- Result := ecIdentResponse
- else
- Result := ecUnknown;
- end;
- begin
- Result := ecNotCompleted;
- LastCh := Str[Length(Str)];
- {$IFDEF Unicode} if not CharInSet(LastCh,['A'..'Z', 'a'..'z']) then Exit;
- {$ELSE} if not (LastCh in ['A'..'Z', 'a'..'z']) then Exit; {$ENDIF}
- TempParams := TStringList.Create;
- try
- ParseParams(Copy(Str, 2, Length(Str) - 2));
- case LastCh of
- 'A': Result := ecCursorUp;
- 'B': Result := ecCursorDown;
- 'C': Result := ecCursorRight;
- 'D': Result := ecCursorLeft;
- 'E': Result := ecCursorNextLine;
- 'F': Result := ecCursorPrevLine;
- 'H': Result := ecCursorMove;
- 'f': Result := ecCursorMove;
- 'd': Result := ecCursorMoveY;
- 'G': Result := ecCursorMoveX;
- 'J': Result := CodeEraseScreen;
- 'K': Result := CodeEraseLine;
- 'X': Result := ecEraseChar;
- 'P': Result := ecDeleteChar;
- 'g': Result := CodeTab;
- 'm': Result := ecAttributes;
- 'h': Result := ecSetMode;
- 'l': Result := ecResetMode;
- 's': Result := ecSaveCaret;
- 'u': Result := ecRestoreCaret;
- 'n': Result := CodeDevice;
- 'c': Result := CodeIdentify;
- 'R': Result := ecReportCursorPos;
- 'r': Result := ecScrollRegion;
- 'S': Result := ecScrollUp;
- 'T': Result := ecScrollDown;
- 'L': Result := ecInsertLine;
- 'M': Result := ecDeleteLine;
- 'p': Result := ecSoftReset;
- else
- Result := ecUnknown;
- end;
- FParams.Assign(TempParams);
- finally
- TempParams.Free;
- end;
- end;
- function TEscapeCodesVT100.DetectOscCode(Str: string): TEscapeCode;
- var
- LastCh: Char;
- begin
- Result := ecNotCompleted;
- LastCh := Str[Length(Str)];
- if (LastCh = #7) then
- begin
- Result:= ecSetTextParams;
- end;
- end;
- end.
|