| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192 |
- {
- Double Commander
- -------------------------------------------------------------------------
- Virtual terminal emulator control
- Alexander Koblov, 2021-2022
- Based on ComPort Library
- https://sourceforge.net/projects/comport
- Author:
- Dejan Crnila, 1998 - 2002
- Maintainers:
- Lars B. Dybdahl, 2003
- Brian Gochnauer, 2010
- License:
- Public Domain
- }
- unit VTEmuCtl;
- {$mode delphi}
- {$pointermath on}
- interface
- uses
- LCLType, Classes, Controls, StdCtrls, ExtCtrls, Forms, Messages, Graphics,
- VTEmuEsc, LCLIntf, Types, LazUtf8, LMessages;
- type
- TOnRxBuf = procedure(Sender: TObject; const Buffer; Count: Integer) of object;
- { TCustomPtyDevice }
- TCustomPtyDevice = class(TComponent)
- protected
- FOnRxBuf: TOnRxBuf;
- FConnected: Boolean;
- protected
- procedure SetConnected(AValue: Boolean); virtual; abstract;
- public
- function WriteStr(const Str: string): Integer; virtual; abstract;
- function SetCurrentDir(const Path: String): Boolean; virtual; abstract;
- function SetScreenSize(aCols, aRows: Integer): Boolean; virtual; abstract;
- property OnRxBuf: TOnRxBuf read FOnRxBuf write FOnRxBuf;
- property Connected: Boolean read FConnected write SetConnected default False;
- end;
- TCustomComTerminal = class; // forward declaration
- // terminal character
- PComTermChar = ^TComTermChar;
- TComTermChar = record
- Ch: TUTF8Char;
- FrontColor: TColor;
- BackColor: TColor;
- Underline: Boolean;
- Bold: Boolean;
- end;
- // buffer which holds terminal screen data
- TComTermBuffer = class
- private
- FBuffer: PByte;
- FTabs: Pointer;
- FTopLeft: TPoint;
- FCaretPos: TPoint;
- FScrollRange: TRect;
- FOwner: TCustomComTerminal;
- strict private
- FRows: Integer;
- FColumns: Integer;
- public
- constructor Create(AOwner: TCustomComTerminal);
- destructor Destroy; override;
- procedure Init(ARows, AColumns: Integer);
- procedure SetChar(Column, Row: Integer; TermChar: TComTermChar);
- function GetChar(Column, Row: Integer): TComTermChar;
- procedure SetTab(Column: Integer; Put: Boolean);
- function GetTab(Column: Integer): Boolean;
- function NextTab(Column: Integer): Integer;
- procedure ClearAllTabs;
- procedure ScrollDown;
- procedure ScrollUp;
- procedure EraseScreenLeft(Column, Row: Integer);
- procedure EraseScreenRight(Column, Row: Integer);
- procedure EraseLineLeft(Column, Row: Integer);
- procedure EraseLineRight(Column, Row: Integer);
- procedure EraseChar(Column, Row, Count: Integer);
- procedure DeleteChar(Column, Row, Count: Integer);
- procedure DeleteLine(Row, Count: Integer);
- procedure InsertLine(Row, Count: Integer);
- function GetLineLength(Line: Integer): Integer;
- function GetLastLine: Integer;
- property Rows: Integer read FRows;
- property Columns: Integer read FColumns;
- end;
- // terminal types
- TTermEmulation = (teVT100orANSI, teVT52, teNone);
- TTermCaret = (tcBlock, tcUnderline, tcNone);
- TAdvanceCaret = (acChar, acReturn, acLineFeed, acReverseLineFeed,
- acTab, acBackspace, acPage);
- TArrowKeys = (akTerminal, akWindows);
- TTermAttributes = record
- FrontColor: TColor;
- BackColor: TColor;
- Invert: Boolean;
- Bold: Boolean;
- Underline: Boolean;
- end;
- TTermMode = record
- Keys: TArrowKeys;
- CharSet: Boolean;
- MouseMode: Boolean;
- MouseTrack: Boolean;
- end;
- TEscapeEvent = procedure(Sender: TObject; var EscapeCodes: TEscapeCodes) of object;
- TUnhandledEvent = procedure(Sender: TObject; Code: TEscapeCode; Data: string) of object;
- TUnhandledModeEvent = procedure(Sender: TObject; const Data: string; OnOff: Boolean) of object;
- TStrRecvEvent = procedure(Sender: TObject; var Str: string) of object;
- TChScreenEvent = procedure(Sender: TObject; Ch: TUTF8Char) of object;
- // communication terminal control
- { TCustomComTerminal }
- TCustomComTerminal = class(TCustomControl)
- private
- FPtyDevice: TCustomPtyDevice;
- FScrollBars: TScrollStyle;
- FArrowKeys: TArrowKeys;
- FWantTab: Boolean;
- FColumns: Integer;
- FRows: Integer;
- FVisibleRows: Integer;
- FLocalEcho: Boolean;
- FSendLF: Boolean;
- FAppendLF: Boolean;
- FForce7Bit: Boolean;
- FWrapLines: Boolean;
- FSmoothScroll: Boolean;
- FAutoFollow : Boolean;
- FFontHeight: Integer;
- FFontWidth: Integer;
- FPartChar: TUTF8Char;
- FEmulation: TTermEmulation;
- FCaret: TTermCaret;
- FCaretPos: TPoint;
- FSaveCaret: TPoint;
- FCaretCreated: Boolean;
- FTopLeft: TPoint;
- FCaretHeight: Integer;
- FSaveAttr: TTermAttributes;
- FBuffer: TComTermBuffer;
- FMainBuffer: TComTermBuffer;
- FAlternateBuffer: TComTermBuffer;
- FParams: TStrings;
- FEscapeCodes: TEscapeCodes;
- FTermAttr: TTermAttributes;
- FTermMode: TTermMode;
- FOnChar: TChScreenEvent;
- FOnGetEscapeCodes: TEscapeEvent;
- FOnUnhandledCode: TUnhandledEvent;
- FOnUnhandledMode: TUnhandledModeEvent;
- FOnStrRecieved: TStrRecvEvent;
- procedure AdvanceCaret(Kind: TAdvanceCaret);
- function CalculateMetrics: Boolean;
- procedure CreateEscapeCodes;
- procedure CreateTerminalCaret;
- procedure DrawChar(AColumn, ARow: Integer; Ch: TComTermChar);
- function GetCharAttr: TComTermChar;
- function GetConnected: Boolean;
- procedure HideCaret;
- procedure InitCaret;
- procedure InvalidatePortion(ARect: TRect);
- procedure ModifyScrollBar(ScrollBar, ScrollCode, Pos: Integer);
- procedure SetColumns(const Value: Integer);
- procedure SetPtyDevice(const Value: TCustomPtyDevice);
- procedure SetConnected(const Value: Boolean);
- procedure SetEmulation(const Value: TTermEmulation);
- procedure SetRows(const Value: Integer);
- procedure SetScrollBars(const Value: TScrollStyle);
- procedure SetCaret(const Value: TTermCaret);
- procedure SetAttributes(AParams: TStrings);
- procedure SetMode(AParams: TStrings; OnOff: Boolean);
- procedure ShowCaret;
- procedure StringReceived(Str: string);
- procedure PaintTerminal(Rect: TRect);
- procedure PaintDesign;
- procedure PutChar(Ch: TUTF8Char);
- function PutEscapeCode(ACode: TEscapeCode; AParams: TStrings): Boolean;
- procedure RestoreAttr;
- procedure RestoreCaretPos;
- procedure RxBuf(Sender: TObject; const Buffer; Count: Integer);
- procedure SaveAttr;
- procedure SaveCaretPos;
- procedure SendChar(Ch: TUTF8Char);
- procedure SendCode(Code: TEscapeCode; AParams: TStrings);
- procedure SendCodeNoEcho(Code: TEscapeCode; AParams: TStrings);
- procedure MouseEvent(Code: TEscapeCode; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
- procedure PerformTest(ACh: Char);
- procedure UpdateScrollPos;
- procedure UpdateScrollRange;
- procedure WrapLine(AWidth: Integer);
- protected
- procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
- procedure CMColorChanged(var Message: TMessage); message CM_COLORCHANGED;
- procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
- procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL;
- procedure WMKillFocus(var Message: TWMSetFocus); message WM_KILLFOCUS;
- procedure WMLButtonDown(var Message: TLMLButtonDown); message WM_LBUTTONDOWN;
- procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
- procedure WMSize(var Msg: TWMSize); message WM_SIZE;
- procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
- function CanAutoSize(var NewWidth, NewHeight: Integer): Boolean; override;
- procedure CreateParams(var Params: TCreateParams); override;
- procedure KeyDown(var Key: Word; Shift: TShiftState); override;
- procedure KeyPress(var Key: Char); override;
- procedure UTF8KeyPress(var UTF8Key: TUTF8Char); override;
- procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
- procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
- procedure CreateWnd; override;
- procedure Notification(AComponent: TComponent; Operation: TOperation); override;
- procedure Paint; override;
- procedure DoChar(Ch: TUTF8Char); dynamic;
- procedure DoGetEscapeCodes(var EscapeCodes: TEscapeCodes); dynamic;
- procedure DoStrRecieved(var Str: string); dynamic;
- procedure DoUnhandledCode(Code: TEscapeCode; Data: string); dynamic;
- procedure DoUnhandledMode(const Data: string; OnOff: Boolean); dynamic;
- function DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint): Boolean; override;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure ClearScreen;
- procedure MoveCaret(AColumn, ARow: Integer);
- procedure Write(const Buffer:string; Size: Integer);
- procedure WriteStr(const Str: string);
- procedure WriteEscCode(ACode: TEscapeCode; AParams: TStrings);
- procedure LoadFromStream(Stream: TStream);
- procedure SaveToStream(Stream: TStream);
- procedure SelectFont;
- property AppendLF: Boolean read FAppendLF write FAppendLF default False;
- property AutoFollow : Boolean read FAutoFollow write FAutoFollow default True;
- property ArrowKeys: TArrowKeys read FArrowKeys write FArrowKeys default akTerminal;
- property Caret: TTermCaret read FCaret write SetCaret default tcBlock;
- property Connected: Boolean read GetConnected write SetConnected stored False;
- property PtyDevice: TCustomPtyDevice read FPtyDevice write SetPtyDevice;
- property Columns: Integer read FColumns write SetColumns default 80;
- property Emulation: TTermEmulation read FEmulation write SetEmulation;
- property EscapeCodes: TEscapeCodes read FEscapeCodes;
- property Force7Bit: Boolean read FForce7Bit write FForce7Bit default False;
- property LocalEcho: Boolean read FLocalEcho write FLocalEcho default False;
- property SendLF: Boolean read FSendLF write FSendLF default False;
- property ScrollBars: TScrollStyle read FScrollBars write SetScrollBars;
- property SmoothScroll: Boolean read FSmoothScroll write FSmoothScroll default False;
- property Rows: Integer read FRows write SetRows default 24;
- property WantTab: Boolean read FWantTab write FWantTab default False;
- property WrapLines: Boolean read FWrapLines write FWrapLines default False;
- property OnChar: TChScreenEvent read FOnChar write FOnChar;
- property OnGetEscapeCodes: TEscapeEvent
- read FOnGetEscapeCodes write FOnGetEscapeCodes;
- property OnStrRecieved: TStrRecvEvent
- read FOnStrRecieved write FOnStrRecieved;
- property OnUnhandledMode: TUnhandledModeEvent
- read FOnUnhandledMode write FOnUnhandledMode;
- property OnUnhandledCode: TUnhandledEvent
- read FOnUnhandledCode write FOnUnhandledCode;
- end;
- // publish properties
- TVirtualTerminal = class(TCustomComTerminal)
- published
- property Align;
- property AppendLF;
- property ArrowKeys;
- property BorderStyle;
- property Color;
- property Columns;
- property PtyDevice;
- property Connected;
- property DragCursor;
- property DragMode;
- property Emulation;
- property Enabled;
- property Font;
- property Force7Bit;
- property Hint;
- property LocalEcho;
- property ParentColor;
- property ParentShowHint;
- property PopupMenu;
- property Rows;
- property ScrollBars;
- property SendLF;
- property ShowHint;
- property SmoothScroll;
- property TabOrder;
- property TabStop default True;
- property Caret;
- property Visible;
- property WantTab;
- property WrapLines;
- property Anchors;
- property AutoSize;
- property Constraints;
- property DragKind;
- property OnChar;
- property OnClick;
- property OnDblClick;
- property OnDragDrop;
- property OnDragOver;
- property OnEndDrag;
- property OnEnter;
- property OnExit;
- property OnGetEscapeCodes;
- property OnKeyDown;
- property OnKeyPress;
- property OnKeyUp;
- property OnMouseDown;
- property OnMouseMove;
- property OnMouseUp;
- property OnStartDrag;
- property OnStrRecieved;
- property OnUnhandledCode;
- property OnConstrainedResize;
- property OnDockDrop;
- property OnEndDock;
- property OnMouseWheel;
- property OnMouseWheelDown;
- property OnMouseWheelUp;
- property OnResize;
- property OnStartDock;
- property OnUnDock;
- property OnContextPopup;
- end;
- implementation
- uses
- SysUtils, Dialogs, Math, VTColorTable, VTWideCharWidth;
- const
- TMPF_FIXED_PITCH = $01;
- (*****************************************
- * TComTermBuffer class *
- *****************************************)
- // create class
- constructor TComTermBuffer.Create(AOwner: TCustomComTerminal);
- begin
- inherited Create;
- FOwner := AOwner;
- FTopLeft := Classes.Point(1, 1);
- FCaretPos := Classes.Point(1, 1);
- end;
- // destroy class
- destructor TComTermBuffer.Destroy;
- begin
- if FBuffer <> nil then
- begin
- FreeMem(FBuffer);
- FreeMem(FTabs);
- end;
- inherited Destroy;
- end;
- // put char in buffer
- procedure TComTermBuffer.SetChar(Column, Row: Integer; TermChar: TComTermChar);
- var
- Address: Integer;
- begin
- if (Row > FRows) or (Column > FColumns) then
- Exit;
- Address := (Row - 1) * FColumns + (Column - 1);
- PComTermChar(FBuffer + (Address * SizeOf(TComTermChar)))^:= TermChar;
- end;
- // get char from buffer
- function TComTermBuffer.GetChar(Column, Row: Integer): TComTermChar;
- var
- Address: Integer;
- begin
- if (Row > FRows) or (Column > FColumns) then
- Exit(Default(TComTermChar));
- Address := (Row - 1) * FColumns + (Column - 1);
- Result:= PComTermChar(FBuffer + (Address * SizeOf(TComTermChar)))^;
- end;
- // scroll down up line
- procedure TComTermBuffer.ScrollDown;
- begin
- DeleteLine(FScrollRange.Top, 1);
- end;
- // scroll up one line
- procedure TComTermBuffer.ScrollUp;
- begin
- InsertLine(FScrollRange.Top, 1)
- end;
- procedure TComTermBuffer.EraseLineLeft(Column, Row: Integer);
- var
- Index: Integer;
- B: PComTermChar;
- begin
- if (Row > FRows) or (Column > FColumns) then Exit;
- // in memory
- B:= PComTermChar(FBuffer) + ((Row - 1) * FColumns);
- for Index:= 0 to Column - 1 do
- begin
- B[Index].Ch:= #32;
- B[Index].BackColor:= FOwner.FTermAttr.BackColor;
- B[Index].FrontColor:= FOwner.FTermAttr.FrontColor;
- end;
- // on screen
- if FOwner.DoubleBuffered then
- FOwner.Invalidate
- else
- FOwner.InvalidatePortion(Classes.Rect(1, Row, Column, Row));
- end;
- // erase line
- procedure TComTermBuffer.EraseLineRight(Column, Row: Integer);
- var
- Index: Integer;
- Count: Integer;
- B: PComTermChar;
- begin
- if (Row > FRows) or (Column > FColumns) then Exit;
- // in memory
- Count:= (FColumns - Column + 1);
- B:= PComTermChar(FBuffer) + ((Row - 1) * FColumns + (Column - 1));
- for Index:= 0 to Count - 1 do
- begin
- B[Index].Ch:= #32;
- B[Index].BackColor:= FOwner.FTermAttr.BackColor;
- B[Index].FrontColor:= FOwner.FTermAttr.FrontColor;
- end;
- // on screen
- if FOwner.DoubleBuffered then
- FOwner.Invalidate
- else
- FOwner.InvalidatePortion(Classes.Rect(Column, Row, FColumns, Row));
- end;
- procedure TComTermBuffer.EraseChar(Column, Row, Count: Integer);
- var
- Index: Integer;
- B: PComTermChar;
- begin
- if (Row > FRows) or (Column > FColumns) then Exit;
- if (Column + Count > FColumns) then Count:= FColumns - Column;
- // in memory
- B:= PComTermChar(FBuffer) + ((Row - 1) * FColumns + (Column - 1));
- for Index:= 0 to Count - 1 do
- begin
- B[Index].Ch:= #32;
- B[Index].BackColor:= FOwner.FTermAttr.BackColor;
- B[Index].FrontColor:= FOwner.FTermAttr.FrontColor;
- end;
- // on screen
- if FOwner.DoubleBuffered then
- FOwner.Invalidate
- else
- FOwner.InvalidatePortion(Classes.Rect(Column, Row, FColumns, Row));
- end;
- procedure TComTermBuffer.DeleteChar(Column, Row, Count: Integer);
- var
- Index: Integer;
- DstAddr: PComTermChar;
- SrcAddr: PComTermChar;
- begin
- if (Row > FRows) or (Column > FColumns) then Exit;
- if (Column + Count > FColumns) then Count:= FColumns - Column;
- // in memory
- DstAddr:= PComTermChar(FBuffer) + ((Row - 1) * FColumns + (Column - 1));
- SrcAddr:= PComTermChar(FBuffer) + ((Row - 1) * FColumns + (Column - 1) + Count);
- // Move characters
- Count:= (FColumns - (Column + Count));
- Move(SrcAddr^, DstAddr^, Count * SizeOf(TComTermChar));
- // Erase moved
- for Index:= 0 to Count - 1 do
- begin
- SrcAddr[Index].Ch:= #32;
- SrcAddr[Index].BackColor:= FOwner.FTermAttr.BackColor;
- SrcAddr[Index].FrontColor:= FOwner.FTermAttr.FrontColor;
- end;
- // on screen
- if FOwner.DoubleBuffered then
- FOwner.Invalidate
- else
- FOwner.InvalidatePortion(Classes.Rect(Column, Row, FColumns, Row));
- end;
- procedure TComTermBuffer.DeleteLine(Row, Count: Integer);
- var
- Index: Integer;
- B: PComTermChar;
- DstAddr: Pointer;
- SrcAddr: Pointer;
- BytesToMove: Integer;
- Top, Bottom: Integer;
- begin
- Top:= FScrollRange.Top;
- Bottom:= FScrollRange.Bottom;
- if (Row < Top) or (Row > Bottom) then Exit;
- if (Row - 1) + Count > Bottom then Count:= Bottom - Row + 1;
- if Row < Bottom then
- begin
- DstAddr := (FBuffer + (Row - 1) * FColumns * SizeOf(TComTermChar));
- SrcAddr := (FBuffer + (Row + Count - 1) * FColumns * SizeOf(TComTermChar));
- BytesToMove := (Bottom - Row - Count + 1) * FColumns * SizeOf(TComTermChar);
- // scroll in buffer
- Move(SrcAddr^, DstAddr^, BytesToMove);
- end;
- B:= PComTermChar(FBuffer) + ((Bottom - Count) * FColumns);
- for Index:= 0 to Count * FColumns - 1 do
- begin
- B[Index].Ch:= #32;
- B[Index].BackColor:= FOwner.FTermAttr.BackColor;
- B[Index].FrontColor:= FOwner.FTermAttr.FrontColor;
- end;
- // on screen
- if FOwner.DoubleBuffered then
- FOwner.Invalidate
- else
- FOwner.InvalidatePortion(Classes.Rect(1, Row, FColumns, Bottom));
- end;
- procedure TComTermBuffer.InsertLine(Row, Count: Integer);
- var
- Index: Integer;
- B: PComTermChar;
- DstAddr: Pointer;
- SrcAddr: Pointer;
- BytesToMove: Integer;
- Top, Bottom: Integer;
- begin
- Top:= FScrollRange.Top;
- Bottom:= FScrollRange.Bottom;
- if (Row < Top) or (Row > Bottom) then Exit;
- if (Row - 1) + Count > Bottom then Count:= Bottom - Row + 1;
- if Row < Bottom then
- begin
- SrcAddr := (FBuffer + (Row - 1) * FColumns * SizeOf(TComTermChar));
- DstAddr := (FBuffer + (Row + Count - 1) * FColumns * SizeOf(TComTermChar));
- BytesToMove := (Bottom - Row - Count + 1) * FColumns * SizeOf(TComTermChar);
- // scroll in buffer
- Move(SrcAddr^, DstAddr^, BytesToMove);
- end;
- B:= PComTermChar(FBuffer) + ((Row - 1) * FColumns);
- for Index:= 0 to Count * FColumns - 1 do
- begin
- B[Index].Ch:= #32;
- B[Index].BackColor:= FOwner.FTermAttr.BackColor;
- B[Index].FrontColor:= FOwner.FTermAttr.FrontColor;
- end;
- // on screen
- if FOwner.DoubleBuffered then
- FOwner.Invalidate
- else
- FOwner.InvalidatePortion(Classes.Rect(1, Row, FColumns, Bottom));
- end;
- // erase screen
- procedure TComTermBuffer.EraseScreenLeft(Column, Row: Integer);
- var
- Index: Integer;
- Count: Integer;
- B: PComTermChar;
- begin
- if (Row > FRows) or (Column > FColumns) then Exit;
- // in memory
- B:= PComTermChar(FBuffer);
- Count:= (Row * FColumns + Column);
- for Index:= 0 to Count - 1 do
- begin
- B[Index].Ch:= #32;
- B[Index].BackColor:= FOwner.FTermAttr.BackColor;
- B[Index].FrontColor:= FOwner.FTermAttr.FrontColor;
- end;
- // on screen
- if FOwner.DoubleBuffered then
- FOwner.Invalidate
- else
- FOwner.InvalidatePortion(Classes.Rect(1, 1, FColumns, Row))
- end;
- // erase screen
- procedure TComTermBuffer.EraseScreenRight(Column, Row: Integer);
- var
- Index: Integer;
- Count: Integer;
- B: PComTermChar;
- begin
- if (Row > FRows) or (Column > FColumns) then Exit;
- // in memory
- B:= PComTermChar(FBuffer) + ((Row - 1) * FColumns + (Column - 1));
- Count:= ((FRows - Row) * FColumns + (FColumns - Column) + 1);
- for Index:= 0 to Count - 1 do
- begin
- B[Index].Ch:= #32;
- B[Index].BackColor:= FOwner.FTermAttr.BackColor;
- B[Index].FrontColor:= FOwner.FTermAttr.FrontColor;
- end;
- // on screen
- if FOwner.DoubleBuffered then
- FOwner.Invalidate
- else
- FOwner.InvalidatePortion(Classes.Rect(1, Row, FColumns, FRows))
- end;
- // init buffer
- procedure TComTermBuffer.Init(ARows, AColumns: Integer);
- var
- I: Integer;
- begin
- if ARows > 0 then
- FRows:= ARows;
- if AColumns > 0 then
- FColumns:= AColumns;
- if FBuffer <> nil then
- begin
- FreeMem(FBuffer);
- FreeMem(FTabs);
- end;
- GetMem(FBuffer, FColumns * FRows * SizeOf(TComTermChar));
- FillChar(FBuffer^, FColumns * FRows * SizeOf(TComTermChar), 0);
- GetMem(FTabs, FColumns * SizeOf(Boolean));
- FillChar(FTabs^, FColumns * SizeOf(Boolean), 0);
- I := 1;
- while (I <= FColumns) do
- begin
- SetTab(I, True);
- Inc(I, 8);
- end;
- FScrollRange.Top:= 1;
- FScrollRange.Bottom:= FRows;
- end;
- // get tab at Column
- function TComTermBuffer.GetTab(Column: Integer): Boolean;
- begin
- Result := Boolean((FTabs + (Column - 1) * SizeOf(Boolean))^);
- end;
- // set tab at column
- procedure TComTermBuffer.SetTab(Column: Integer; Put: Boolean);
- begin
- Boolean((FTabs + (Column - 1) * SizeOf(Boolean))^) := Put;
- end;
- // find nexts tab position
- function TComTermBuffer.NextTab(Column: Integer): Integer;
- var
- I: Integer;
- begin
- I := Column;
- while (I <= FColumns) do
- if GetTab(I) then
- Break
- else
- Inc(I);
- if I > FColumns then
- Result := 0
- else
- Result := I;
- end;
- // clear all tabs
- procedure TComTermBuffer.ClearAllTabs;
- begin
- FillChar(FTabs^, FColumns * SizeOf(Boolean), 0);
- end;
- function TComTermBuffer.GetLineLength(Line: Integer): Integer;
- var
- I: Integer;
- begin
- Result := 0;
- for I := 1 to FColumns do
- if GetChar(I, Line).Ch <> #0 then
- Result := I;
- end;
- function TComTermBuffer.GetLastLine: Integer;
- var
- J: Integer;
- begin
- Result := 0;
- for J := 1 to FRows do
- if GetLineLength(J) > 0 then
- Result := J;
- end;
- (*****************************************
- * TComCustomTerminal control *
- *****************************************)
- // create control
- constructor TCustomComTerminal.Create(AOwner: TComponent);
- begin
- FScrollBars := ssVertical;
- inherited Create(AOwner);
- Parent:= TWinControl(AOwner);
- BorderStyle := bsSingle;
- Color := clBlack;
- DoubleBuffered := True;
- TabStop := True;
- Font.Name := 'Consolas';
- Font.Color:= clWhite;
- FEmulation := teVT100orANSI;
- FColumns := 80;
- FRows := 100;
- FVisibleRows:= 25;
- FWrapLines := True;
- FAutoFollow := True;
- FCaretPos := Classes.Point(1, 1);
- FTopLeft := Classes.Point(1, 1);
- FMainBuffer := TComTermBuffer.Create(Self);
- FAlternateBuffer := TComTermBuffer.Create(Self);
- FTermAttr.FrontColor := Font.Color;
- FTermAttr.BackColor := Color;
- FBuffer:= FMainBuffer;
- FParams:= TStringList.Create;
- CreateEscapeCodes;
- if not (csDesigning in ComponentState) then
- begin
- FMainBuffer.Init(FRows, FColumns);
- FAlternateBuffer.Init(FVisibleRows, FColumns);
- end;
- SetBounds(Left, Top, 400, 250);
- end;
- // destroy control
- destructor TCustomComTerminal.Destroy;
- begin
- PtyDevice := nil;
- FMainBuffer.Free;
- FAlternateBuffer.Free;
- FEscapeCodes.Free;
- FParams.Free;
- inherited Destroy;
- end;
- // clear terminal screen
- procedure TCustomComTerminal.ClearScreen;
- begin
- FBuffer.Init(0, 0);
- MoveCaret(1, 1);
- Invalidate;
- end;
- // move caret
- procedure TCustomComTerminal.MoveCaret(AColumn, ARow: Integer);
- begin
- if AColumn > FBuffer.Columns then
- begin
- if FWrapLines then
- FCaretPos.X := FBuffer.Columns + 1
- else
- FCaretPos.X := FBuffer.Columns
- end
- else
- if AColumn < 1 then
- FCaretPos.X := 1
- else
- FCaretPos.X := AColumn;
- if ARow > FBuffer.Rows then
- FCaretPos.Y := FBuffer.Rows
- else
- if ARow < 1 then
- FCaretPos.Y := 1
- else
- FCaretPos.Y := ARow;
- if FCaretCreated then
- SetCaretPos((FCaretPos.X - FTopLeft.X) * FFontWidth,
- (FCaretPos.Y - FTopLeft.Y) * FFontHeight + FFontHeight - FCaretHeight);
- end;
- // write data to screen
- procedure TCustomComTerminal.Write(const Buffer:string; Size: Integer);
- var
- I: Integer;
- L: Integer;
- Ch: TUTF8Char;
- Res: TEscapeResult;
- begin
- HideCaret;
- try
- // show it on screen
- I:= 1;
- while I <= Size do
- begin
- L:= UTF8CodepointSizeFast(@Buffer[I]);
- Ch:= Copy(Buffer, I, L);
- // got partial character
- if (I + L - 1 > Size) then
- begin
- FPartChar:= Ch;
- Break;
- end;
- if (FEscapeCodes <> nil) then
- begin
- Res := FEscapeCodes.ProcessChar(Ch);
- if Res = erChar then
- PutChar(FEscapeCodes.Character);
- if Res = erCode then
- begin
- if not PutEscapeCode(FEscapeCodes.Code, FEscapeCodes.Params) then
- DoUnhandledCode(FEscapeCodes.Code, FEscapeCodes.Data);
- FEscapeCodes.Params.Clear;
- end;
- end
- else begin
- PutChar(Ch);
- end;
- I+= L;
- end;
- finally
- ShowCaret;
- end;
- end;
- // write string on screen, but not to port
- procedure TCustomComTerminal.WriteStr(const Str: string);
- begin
- Write(Str, Length(Str));
- end;
- // write escape code on screen
- procedure TCustomComTerminal.WriteEscCode(ACode: TEscapeCode;
- AParams: TStrings);
- begin
- if FEscapeCodes <> nil then
- PutEscapeCode(ACode, AParams);
- end;
- // load screen buffer from file
- procedure TCustomComTerminal.LoadFromStream(Stream: TStream);
- var
- ABuffer: TBytes;
- begin
- HideCaret;
- ABuffer:= Default(TBytes);
- SetLength(ABuffer, Stream.Size);
- Stream.ReadBuffer(ABuffer[0], Length(ABuffer));
- RxBuf(Self, ABuffer[0], Length(ABuffer));
- ShowCaret;
- end;
- // save screen buffer to file
- procedure TCustomComTerminal.SaveToStream(Stream: TStream);
- var
- I, J: Integer;
- Ch: TUTF8Char;
- EndLine: string;
- LastChar, LastLine: Integer;
- begin
- EndLine := #13#10;
- LastLine := FBuffer.GetLastLine;
- for J := 1 to LastLine do
- begin
- LastChar := FBuffer.GetLineLength(J);
- if LastChar > 0 then
- begin
- for I := 1 to LastChar do
- begin
- Ch := FBuffer.GetChar(I, J).Ch;
- // replace null characters with blanks
- if Ch = #0 then
- Ch := #32;
- Stream.Write(Ch, Length(Ch));
- end;
- end;
- // new line
- if J <> LastLine then
- Stream.Write(EndLine[1], Length(EndLine));
- end;
- end;
- // select terminal font
- procedure TCustomComTerminal.SelectFont;
- begin
- with TFontDialog.Create(Application) do
- begin
- Options := Options + [fdFixedPitchOnly];
- Font := Self.Font;
- if Execute then
- Self.Font := Font;
- Free;
- end;
- end;
- // process font change
- procedure TCustomComTerminal.CMFontChanged(var Message: TMessage);
- begin
- inherited;
- FTermAttr.FrontColor := Font.Color;
- if not CalculateMetrics then
- ;//Font.Name := ComTerminalFont.Name;
- if fsUnderline in Font.Style then
- Font.Style := Font.Style - [fsUnderline];
- AdjustSize;
- UpdateScrollRange;
- end;
- procedure TCustomComTerminal.CMColorChanged(var Message: TMessage);
- begin
- inherited;
- FTermAttr.BackColor := Color;
- end;
- procedure TCustomComTerminal.WMGetDlgCode(var Message: TWMGetDlgCode);
- begin
- // request arrow keys and WM_CHAR message to be handled by the control
- Message.Result := DLGC_WANTARROWS or DLGC_WANTCHARS;
- // tab key
- if FWantTab then
- Message.Result := Message.Result or DLGC_WANTTAB;
- end;
- // lost focus
- procedure TCustomComTerminal.WMKillFocus(var Message: TWMSetFocus);
- begin
- // destroy caret because it could be requested by some other control
- DestroyCaret(Handle);
- FCaretCreated := False;
- inherited;
- end;
- // gained focus
- procedure TCustomComTerminal.WMSetFocus(var Message: TWMSetFocus);
- begin
- inherited;
- // control activated, create caret
- InitCaret;
- end;
- // left button pressed
- procedure TCustomComTerminal.WMLButtonDown(var Message: TLMLButtonDown);
- begin
- // set focus when left button down
- if CanFocus and TabStop then
- SetFocus;
- inherited;
- end;
- // size changed
- procedure TCustomComTerminal.WMSize(var Msg: TWMSize);
- var
- ARows, AColumns: Integer;
- begin
- inherited WMSize(Msg);
- if (ClientWidth = 0) or (ClientHeight = 0) then
- Exit;
- ARows:= Max(2, ClientHeight div FFontHeight);
- AColumns:= Max(2, ClientWidth div FFontWidth);
- if (ARows <> FVisibleRows) or (AColumns <> FColumns) then
- begin
- FColumns := AColumns;
- FVisibleRows := ARows;
- FRows := Max(FRows, FVisibleRows);
- AdjustSize;
- if not ((csLoading in ComponentState) or (csDesigning in ComponentState)) then
- begin
- FMainBuffer.Init(FRows, FColumns);
- FAlternateBuffer.Init(FVisibleRows, FColumns);
- if Assigned(FPtyDevice) then
- FPtyDevice.SetScreenSize(FColumns, FVisibleRows);
- Invalidate;
- end;
- UpdateScrollRange;
- if (FCaretPos.Y = FBuffer.Rows) or
- ((FCaretPos.Y - FTopLeft.Y) >= FVisibleRows) then
- begin
- ARows:= FCaretPos.Y - FVisibleRows;
- ModifyScrollBar(SB_Vert, SB_THUMBPOSITION, ARows);
- end;
- end;
- end;
- // vertical scroll
- procedure TCustomComTerminal.WMHScroll(var Message: TWMHScroll);
- begin
- ModifyScrollBar(SB_HORZ, Message.ScrollCode, Message.Pos);
- end;
- // horizontal scroll
- procedure TCustomComTerminal.WMVScroll(var Message: TWMVScroll);
- begin
- ModifyScrollBar(SB_VERT, Message.ScrollCode, Message.Pos);
- end;
- // set size to fit whole terminal screen
- function TCustomComTerminal.CanAutoSize(var NewWidth,
- NewHeight: Integer): Boolean;
- var
- Border: Integer;
- begin
- Result := True;
- if Align in [alNone, alLeft, alRight] then
- begin
- NewWidth := FFontWidth * FColumns;
- if BorderStyle = bsSingle then
- begin
- Border := SM_CXBORDER;
- NewWidth := NewWidth + 2 * GetSystemMetrics(BORDER);
- end;
- end;
- if Align in [alNone, alTop, alBottom] then
- begin
- NewHeight := FFontHeight * FRows;
- if BorderStyle = bsSingle then
- begin
- Border := SM_CYBORDER;
- NewHeight := NewHeight + 2 * GetSystemMetrics(Border);
- end;
- end;
- end;
- // set control parameters
- procedure TCustomComTerminal.CreateParams(var Params: TCreateParams);
- const
- BorderStyles: array[TBorderStyle] of DWORD = (0, WS_BORDER);
- begin
- inherited CreateParams(Params);
- with Params do
- begin
- Style := Style or BorderStyles[BorderStyle];
- if NewStyleControls and (BorderStyle = bsSingle) then
- begin
- Style := Style and not WS_BORDER;
- ExStyle := ExStyle or WS_EX_CLIENTEDGE;
- end;
- if FScrollBars in [ssVertical, ssBoth] then
- Style := Style or WS_VSCROLL;
- if FScrollBars in [ssHorizontal, ssBoth] then
- Style := Style or WS_HSCROLL;
- end;
- ControlStyle := ControlStyle + [csOpaque];
- end;
- // key down
- procedure TCustomComTerminal.KeyDown(var Key: Word; Shift: TShiftState);
- var
- Code: TEscapeCode;
- begin
- inherited KeyDown(Key, Shift);
- if (Key in [VK_TAB, VK_ESCAPE]) then
- begin
- SendChar(Chr(Key));
- Key:= 0;
- Exit;
- end;
- if (Key = VK_BACK) then
- begin
- SendChar(#$7f);
- Key:= 0;
- Exit;
- end;
- if Key in [VK_F1..VK_F12] then
- begin
- Code := ecFuncKey;
- FParams.Text:= IntToStr(Key - VK_F1);
- SendCode(Code, FParams);
- Exit;
- end;
- case Key of
- VK_INSERT: Code := ecInsertKey;
- VK_DELETE: Code := ecDeleteKey;
- VK_PRIOR: Code := ecPageUpKey;
- VK_NEXT: Code := ecPageDownKey;
- else
- Code := ecUnknown;
- end;
- if (Code <> ecUnknown) then
- begin
- SendCode(Code, nil);
- Exit;
- end;
- case Key of
- VK_UP: Code := ecCursorUp;
- VK_DOWN: Code := ecCursorDown;
- VK_LEFT: Code := ecCursorLeft;
- VK_RIGHT: Code := ecCursorRight;
- VK_HOME: Code := ecCursorHome;
- VK_END: Code := ecCursorEnd;
- else
- Code := ecUnknown;
- end;
- if FTermMode.Keys = akTerminal then
- begin
- if Code <> ecUnknown then
- if FArrowKeys = akTerminal then
- SendCode(Code, nil)
- else
- PutEscapeCode(Code, nil);
- end
- else
- case Code of
- ecCursorUp: SendCode(ecAppCursorUp, nil);
- ecCursorDown: SendCode(ecAppCursorDown, nil);
- ecCursorLeft: SendCode(ecAppCursorLeft, nil);
- ecCursorRight: SendCode(ecAppCursorRight, nil);
- ecCursorHome: SendCode(ecAppCursorHome, nil);
- ecCursorEnd: SendCode(ecAppCursorEnd, nil);
- end;
- {$IFDEF LCLGTK2}
- if Key in [VK_UP, VK_DOWN] then
- begin
- Key:= 0;
- end;
- {$ENDIF}
- end;
- // key pressed
- procedure TCustomComTerminal.KeyPress(var Key: Char);
- begin
- inherited KeyPress(Key);
- // SendChar(Key);
- end;
- procedure TCustomComTerminal.UTF8KeyPress(var UTF8Key: TUTF8Char);
- begin
- inherited UTF8KeyPress(UTF8Key);
- SendChar(UTF8Key);
- end;
- procedure TCustomComTerminal.MouseDown(Button: TMouseButton;
- Shift: TShiftState; X, Y: Integer);
- begin
- inherited MouseDown(Button, Shift, X, Y);
- MouseEvent(ecMouseDown, Button, Shift, X, Y);
- end;
- procedure TCustomComTerminal.MouseUp(Button: TMouseButton; Shift: TShiftState;
- X, Y: Integer);
- begin
- inherited MouseUp(Button, Shift, X, Y);
- MouseEvent(ecMouseUp, Button, Shift, X, Y);
- end;
- procedure TCustomComTerminal.CreateWnd;
- begin
- inherited CreateWnd;
- if FScrollBars in [ssVertical, ssBoth] then
- ShowScrollBar(Handle, SB_VERT, True);
- if FScrollBars in [ssHorizontal, ssBoth] then
- ShowScrollBar(Handle, SB_HORZ, True);
- end;
- procedure TCustomComTerminal.Notification(AComponent: TComponent;
- Operation: TOperation);
- begin
- inherited Notification(AComponent, Operation);
- if (AComponent = FPtyDevice) and (Operation = opRemove) then
- PtyDevice := nil;
- end;
- // paint characters
- procedure TCustomComTerminal.PaintTerminal(Rect: TRect);
- var
- I, J, X, Y: Integer;
- Ch: TComTermChar;
- begin
- HideCaret;
- if (Rect.Bottom + FTopLeft.Y - 1) > FBuffer.Rows then
- Dec(Rect.Bottom);
- if (Rect.Right + FTopLeft.X - 1) > FBuffer.Columns then
- Dec(Rect.Right);
- for J := Rect.Top to Rect.Bottom do
- begin
- Y := J + FTopLeft.Y - 1;
- for I := Rect.Left to Rect.Right do
- begin
- X := I + FTopLeft.X - 1;
- Ch := FBuffer.GetChar(X, Y);
- if Ch.Ch <> Chr(0) then
- DrawChar(I, J, Ch);
- end;
- end;
- ShowCaret;
- end;
- procedure TCustomComTerminal.PaintDesign;
- begin
- Canvas.TextOut(0, 0, 'Virtual Terminal Emulator');
- end;
- procedure TCustomComTerminal.Paint;
- var
- ARect: TRect;
- begin
- Canvas.Font := Font;
- Canvas.Brush.Color := Color;
- if csDesigning in ComponentState then
- PaintDesign
- else
- begin
- MoveCaret(FCaretPos.X, FCaretPos.Y);
- // don't paint whole screen, but only the invalidated portion
- ARect.Left := Canvas.ClipRect.Left div FFontWidth + 1;
- ARect.Right := Min(Canvas.ClipRect.Right div FFontWidth + 1, FBuffer.Columns);
- ARect.Top := Canvas.ClipRect.Top div FFontHeight + 1;
- ARect.Bottom := Min(Canvas.ClipRect.Bottom div FFontHeight + 1, FBuffer.Rows);
- PaintTerminal(ARect);
- end;
- end;
- // creates caret
- procedure TCustomComTerminal.CreateTerminalCaret;
- begin
- FCaretHeight := 0;
- if FCaret = tcBlock then
- FCaretHeight := FFontHeight
- else
- if FCaret = tcUnderline then
- FCaretHeight := FFontHeight div 8;
- if FCaretHeight > 0 then
- begin
- CreateCaret(Handle, 0, FFontWidth, FCaretHeight);
- FCaretCreated := True;
- end;
- end;
- // string received from com port
- procedure TCustomComTerminal.StringReceived(Str: string);
- begin
- DoStrRecieved(Str);
- WriteStr(Str);
- end;
- // draw one character on screen, but do not put it in buffer
- procedure TCustomComTerminal.DrawChar(AColumn, ARow: Integer;
- Ch: TComTermChar);
- var
- OldBackColor, OldFrontColor: Integer;
- begin
- OldBackColor := Canvas.Brush.Color;
- OldFrontColor := Canvas.Font.Color;
- Canvas.Brush.Color := Ch.BackColor;
- Canvas.Font.Color := Ch.FrontColor;
- if Ch.Bold then
- Canvas.Font.Style := Canvas.Font.Style + [fsBold]
- else begin
- Canvas.Font.Style := Canvas.Font.Style - [fsBold];
- end;
- if Ch.Underline then
- Canvas.Font.Style := Canvas.Font.Style + [fsUnderline]
- else begin
- Canvas.Font.Style := Canvas.Font.Style - [fsUnderline];
- end;
- Canvas.TextOut((AColumn - 1) * FFontWidth, (ARow - 1) * FFontHeight, Ch.Ch);
- Canvas.Brush.Color := OldBackColor;
- Canvas.Font.Color := OldFrontColor;
- end;
- procedure TCustomComTerminal.WrapLine(AWidth: Integer);
- begin
- if FCaretPos.X + AWidth > FBuffer.Columns + 1 then
- begin
- if FCaretPos.Y = FBuffer.Rows then
- begin
- FBuffer.ScrollDown;
- MoveCaret(1, FCaretPos.Y);
- end
- else begin
- MoveCaret(1, FCaretPos.Y + 1)
- end;
- end;
- end;
- // move caret after new char is put on screen
- procedure TCustomComTerminal.AdvanceCaret(Kind: TAdvanceCaret);
- var
- I: Integer;
- begin
- case Kind of
- acChar:
- begin
- if (FCaretPos.X < FColumns) or FWrapLines then
- MoveCaret(FCaretPos.X + 1, FCaretPos.Y);
- end;
- acReturn: MoveCaret(1, FCaretPos.Y);
- acLineFeed:
- begin
- if FCaretPos.Y = FBuffer.FScrollRange.Bottom then
- FBuffer.ScrollDown
- else
- MoveCaret(FCaretPos.X, FCaretPos.Y + 1);
- end;
- acReverseLineFeed:
- begin
- if FCaretPos.Y = FBuffer.FScrollRange.Top then
- FBuffer.ScrollUp
- else
- MoveCaret(FCaretPos.X, FCaretPos.Y - 1);
- end;
- acBackSpace: MoveCaret(FCaretPos.X - 1, FCaretPos.Y);
- acTab:
- begin
- I := FBuffer.NextTab(FCaretPos.X + 1);
- if I > 0 then
- MoveCaret(I, FCaretPos.Y);
- end;
- acPage:
- ClearScreen;
- end;
- if FAutoFollow then
- begin
- if (FCaretPos.Y - FTopLeft.Y) > FVisibleRows then
- begin
- I:= FCaretPos.Y - FVisibleRows + 1;
- ModifyScrollBar(SB_Vert, SB_THUMBPOSITION, I);
- end;
- end;
- end;
- // set character attributes
- procedure TCustomComTerminal.SetAttributes(AParams: TStrings);
- var
- I, Value: Integer;
- procedure AllOff;
- begin
- FTermAttr.FrontColor := Font.Color;
- FTermAttr.BackColor := Color;
- FTermAttr.Invert := False;
- FTermAttr.Bold := False;
- FTermAttr.Underline := False;
- end;
- function GetExtendedColor(var Index: Integer): TColor;
- var
- RGB: Integer;
- R, G, B: Byte;
- AParam: Integer;
- begin
- AParam:= FEscapeCodes.GetParam(Index + 1, AParams);
- // Color from RGB value
- if AParam = 2 then
- begin
- R:= FEscapeCodes.GetParam(Index + 2, AParams);
- G:= FEscapeCodes.GetParam(Index + 3, AParams);
- B:= FEscapeCodes.GetParam(Index + 4, AParams);
- Result:= RGBToColor(R, G, B);
- Inc(Index, 4);
- end
- // Color from 256 color palette
- else if (AParam = 5) then
- begin
- RGB:= FEscapeCodes.GetParam(Index + 2, AParams);
- if (RGB >= 0) and (RGB < 256) then
- begin
- Result:= Color256Table[RGB];
- end;
- Inc(Index, 2);
- end;
- end;
- begin
- I:= 1;
- if AParams.Count = 0 then
- AllOff;
- while I <= AParams.Count do
- begin
- Value := FEscapeCodes.GetParam(I, AParams);
- case Value of
- 0: AllOff;
- 1: FTermAttr.Bold := True;
- 4: FTermAttr.Underline := True;
- 7: FTermAttr.Invert := True;
- 22: FTermAttr.Bold := False;
- 24: FTermAttr.Underline := False;
- 27: FTermAttr.Invert := False;
- // Extended foreground color
- 38: FTermAttr.FrontColor := GetExtendedColor(I);
- // Default foreground color
- 39: FTermAttr.FrontColor := Font.Color;
- // Extended background color
- 48: FTermAttr.BackColor := GetExtendedColor(I);
- // Default background color
- 49: FTermAttr.BackColor := Color;
- // NEW foreground colors
- else if (Value in [30..37]) then
- FTermAttr.FrontColor := Color256Table[Value - 30]
- // NEW background colors
- else if (Value in [40..47]) then
- FTermAttr.BackColor := Color256Table[Value - 40]
- // BRIGHT foreground colors
- else if (Value in [90..97]) then
- FTermAttr.FrontColor := Color256Table[Value - 90 + 8]
- // BRIGHT background colors
- else if (Value in [100..107]) then
- FTermAttr.BackColor := Color256Table[Value - 100 + 8]
- else begin
- DoUnhandledCode(ecAttributes, IntToStr(Value));
- end;
- end;
- Inc(I);
- end;
- end;
- procedure TCustomComTerminal.SetMode(AParams: TStrings; OnOff: Boolean);
- var
- Str: string;
- begin
- if AParams.Count = 0 then
- Exit;
- Str := AParams[0];
- if Str = '?1' then
- begin
- if OnOff then
- FTermMode.Keys := akWindows
- else
- FTermMode.Keys := akTerminal;
- end
- else if Str = '?7' then
- FWrapLines := OnOff
- else if Str = '?3' then
- begin
- if OnOff then
- Columns := 132
- else
- Columns := 80;
- end
- else if Str = '?1002' then
- FTermMode.MouseTrack:= OnOff
- else if Str = '?1006' then
- FTermMode.MouseMode:= OnOff
- else if Str = '?1049' then
- begin
- FBuffer.FTopLeft:= FTopLeft;
- FBuffer.FCaretPos:= FCaretPos;
- if OnOff then
- FBuffer := FAlternateBuffer
- else begin
- FBuffer := FMainBuffer;
- end;
- FTopLeft:= FBuffer.FTopLeft;
- FCaretPos:= FBuffer.FCaretPos;
- UpdateScrollRange;
- Invalidate;
- end
- else begin
- DoUnhandledMode(Str, OnOff);
- end;
- end;
- // invalidate portion of screen
- procedure TCustomComTerminal.InvalidatePortion(ARect: TRect);
- var
- Rect: TRect;
- begin
- Rect.Left := Max((ARect.Left - FTopLeft.X) * FFontWidth, 0);
- Rect.Right := Max((ARect.Right - FTopLeft.X + 1) * FFontWidth, 0);
- Rect.Top := Max((ARect.Top - FTopLeft.Y) * FFontHeight, 0);
- Rect.Bottom := Max((ARect.Bottom - FTopLeft.Y + 1) * FFontHeight, 0);
- InvalidateRect(Handle, @Rect, True);
- end;
- // modify scroll bar
- procedure TCustomComTerminal.ModifyScrollBar(ScrollBar, ScrollCode,
- Pos: Integer);
- var
- CellSize, OldPos, APos, Dx, Dy: Integer;
- begin
- if (ScrollCode = SB_ENDSCROLL) or
- ((ScrollCode = SB_THUMBTRACK) and not FSmoothScroll)
- then
- Exit;
- if ScrollBar = SB_HORZ then
- CellSize := FFontWidth
- else
- CellSize := FFontHeight;
- APos := GetScrollPos(Handle, ScrollBar);
- OldPos := APos;
- case ScrollCode of
- SB_LINEUP: Dec(APos);
- SB_LINEDOWN: Inc(APos);
- SB_PAGEUP: Dec(APos, ClientHeight div CellSize);
- SB_PAGEDOWN: Inc(APos, ClientHeight div CellSize);
- SB_THUMBPOSITION,
- SB_THUMBTRACK: APos := Pos;
- end;
- SetScrollPos(Handle, ScrollBar, APos, True);
- APos := GetScrollPos(Handle, ScrollBar);
- if ScrollBar = SB_HORZ then
- begin
- FTopLeft.X := APos + 1;
- Dx := (OldPos - APos) * FFontWidth;
- Dy := 0;
- end else
- begin
- FTopLeft.Y := APos + 1;
- Dx := 0;
- Dy := (OldPos - APos) * FFontHeight;
- end;
- if DoubleBuffered then
- Invalidate
- else
- ScrollWindowEx(Handle, Dx, Dy, nil, nil, 0, nil, SW_ERASE or SW_INVALIDATE);
- end;
- // calculate scroll position
- procedure TCustomComTerminal.UpdateScrollPos;
- begin
- if FScrollBars in [ssBoth, ssHorizontal] then
- begin
- SetScrollPos(Handle, SB_HORZ, FTopLeft.X - 1, True);
- end;
- if FScrollBars in [ssBoth, ssVertical] then
- begin
- SetScrollPos(Handle, SB_VERT, FTopLeft.Y - 1, True);
- end;
- end;
- // calculate scroll range
- procedure TCustomComTerminal.UpdateScrollRange;
- var
- OldScrollBars: TScrollStyle;
- AHeight, AWidth: Integer;
- // is scroll bar visible?
- function ScrollBarVisible(Code: Word): Boolean;
- var
- Min, Max: Integer;
- begin
- Result := False;
- if (ScrollBars = ssBoth) or
- ((Code = SB_HORZ) and (ScrollBars = ssHorizontal)) or
- ((Code = SB_VERT) and (ScrollBars = ssVertical)) then
- begin
- GetScrollRange(Handle, Code, Min, Max);
- Result := Min <> Max;
- end;
- end;
- procedure SetRange(Code, Max: Integer);
- var
- Info: TScrollInfo;
- begin
- Info:= Default(TScrollInfo);
- Info.fMask := SIF_RANGE or SIF_PAGE;
- Info.nMax := Max;
- Info.nPage := 1;
- SetScrollInfo(Handle, Code, Info, False);
- end;
- // set horizontal range
- procedure SetHorzRange;
- var
- Max: Integer;
- AColumns: Integer;
- begin
- if OldScrollBars in [ssBoth, ssHorizontal] then
- begin
- AColumns := AWidth div FFontWidth;
- if AColumns >= FBuffer.Columns then
- SetRange(SB_HORZ, 1) // screen is wide enough, hide scroll bar
- else
- begin
- Max := FBuffer.Columns - (AColumns - 1);
- SetRange(SB_HORZ, Max);
- end;
- end;
- end;
- // set vertical range
- procedure SetVertRange;
- var
- Max, ARows: Integer;
- begin
- if OldScrollBars in [ssBoth, ssVertical] then
- begin
- ARows := AHeight div FFontHeight;
- if ARows >= FBuffer.Rows then
- SetRange(SB_VERT, 1) // screen is high enough, hide scroll bar
- else
- begin
- Max := FBuffer.Rows - (ARows - 1);
- SetRange(SB_VERT, Max);
- end;
- end;
- end;
- begin
- if (FScrollBars = ssNone) or (FBuffer = nil) then
- Exit;
- AHeight := ClientHeight;
- AWidth := ClientWidth;
- if ScrollBarVisible(SB_HORZ) then
- Inc(AHeight, GetSystemMetrics(SM_CYHSCROLL));
- if ScrollBarVisible(SB_VERT) then
- Inc(AWidth, GetSystemMetrics(SM_CXVSCROLL));
- // Temporarily mark us as not having scroll bars to avoid recursion
- OldScrollBars := FScrollBars;
- FScrollBars := ssNone;
- try
- SetHorzRange;
- AHeight := ClientHeight;
- SetVertRange;
- if AWidth <> ClientWidth then
- begin
- AWidth := ClientWidth;
- SetHorzRange;
- end;
- finally
- FScrollBars := OldScrollBars;
- end;
- // range changed, update scroll bar position
- UpdateScrollPos;
- end;
- // hide caret
- procedure TCustomComTerminal.HideCaret;
- begin
- if FCaretCreated then
- LCLIntf.HideCaret(Handle);
- end;
- // show caret
- procedure TCustomComTerminal.ShowCaret;
- begin
- if FCaretCreated then
- LCLIntf.ShowCaret(Handle);
- end;
- // send character to com port
- procedure TCustomComTerminal.SendChar(Ch: TUTF8Char);
- begin
- if (FPtyDevice <> nil) and (FPtyDevice.Connected) then
- begin
- FPtyDevice.WriteStr(Ch);
- if FLocalEcho then
- begin
- // local echo is on, show character on screen
- HideCaret;
- PutChar(Ch);
- ShowCaret;
- end;
- // send line feeds after carriage return
- if (Ch = Chr(13)) and FSendLF then
- SendChar(Chr(10));
- end;
- end;
- // send escape code
- procedure TCustomComTerminal.SendCode(Code: TEscapeCode; AParams: TStrings);
- begin
- if (FPtyDevice <> nil) and (FPtyDevice.Connected) and (FEscapeCodes <> nil) then
- begin
- FPtyDevice.WriteStr(FEscapeCodes.EscCodeToStr(Code, AParams));
- if FLocalEcho then
- begin
- // local echo is on, show character on screen
- HideCaret;
- PutEscapeCode(Code, AParams);
- ShowCaret;
- end;
- end;
- end;
- // send escape code to port
- procedure TCustomComTerminal.SendCodeNoEcho(Code: TEscapeCode; AParams: TStrings);
- begin
- if (FPtyDevice <> nil) and (FPtyDevice.Connected) and (FEscapeCodes <> nil) then
- FPtyDevice.WriteStr(FEscapeCodes.EscCodeToStr(Code, AParams));
- end;
- procedure TCustomComTerminal.MouseEvent(Code: TEscapeCode;
- Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
- var
- AButton: Integer;
- begin
- if (FTermMode.MouseMode and FTermMode.MouseTrack) then
- begin
- case Button of
- mbLeft: AButton:= 0;
- mbRight: AButton:= 2;
- mbMiddle: AButton:= 1;
- else AButton:= Ord(Button);
- end;
- FParams.Text:= IntToStr(AButton);
- FParams.Add(IntToStr(X div FFontWidth + 1));
- FParams.Add(IntToStr(Y div FFontHeight + 1));
- SendCodeNoEcho(Code, FParams);
- end;
- end;
- // process escape code on screen
- function TCustomComTerminal.PutEscapeCode(ACode: TEscapeCode; AParams: TStrings): Boolean;
- begin
- Result := True;
- with FEscapeCodes do
- case ACode of
- ecCursorUp: MoveCaret(FCaretPos.X, FCaretPos.Y - GetParam(1, AParams));
- ecCursorDown: MoveCaret(FCaretPos.X, FCaretPos.Y + GetParam(1, AParams));
- ecCursorRight: MoveCaret(FCaretPos.X + GetParam(1, AParams), FCaretPos.Y);
- ecCursorLeft: MoveCaret(FCaretPos.X - GetParam(1, AParams), FCaretPos.Y);
- ecCursorNextLine: MoveCaret(1, FCaretPos.Y + GetParam(1, AParams));
- ecCursorPrevLine: MoveCaret(1, FCaretPos.Y - GetParam(1, AParams));
- ecCursorMove: MoveCaret(GetParam(2, AParams), GetParam(1, AParams));
- ecCursorMoveX: MoveCaret(GetParam(1, AParams), FCaretPos.Y);
- ecCursorMoveY: MoveCaret(FCaretPos.X, GetParam(1, AParams));
- ecReverseLineFeed: AdvanceCaret(acReverseLineFeed);
- ecEraseLineLeft: FBuffer.EraseLineLeft(FCaretPos.X, FCaretPos.Y);
- ecEraseLineRight: FBuffer.EraseLineRight(FCaretPos.X, FCaretPos.Y);
- ecEraseLine:
- begin
- FBuffer.EraseLineRight(1, FCaretPos.Y);
- MoveCaret(1, FCaretPos.Y)
- end;
- ecEraseScreenLeft: FBuffer.EraseScreenLeft(FCaretPos.X, FCaretPos.Y);
- ecEraseScreenRight: FBuffer.EraseScreenRight(FCaretPos.X, FCaretPos.Y);
- ecEraseScreen:
- begin
- FBuffer.EraseScreenRight(1, 1);
- MoveCaret(1, 1)
- end;
- ecEraseChar: FBuffer.EraseChar(FCaretPos.X, FCaretPos.Y, GetParam(1, AParams));
- ecDeleteChar: FBuffer.DeleteChar(FCaretPos.X, FCaretPos.Y, GetParam(1, AParams));
- ecIdentify:
- begin
- AParams.Clear;
- AParams.Add('2');
- SendCodeNoEcho(ecIdentResponse, AParams);
- end;
- ecSetTab: FBuffer.SetTab(FCaretPos.X, True);
- ecClearTab: FBuffer.SetTab(FCaretPos.X, False);
- ecClearAllTabs: FBuffer.ClearAllTabs;
- ecAttributes: SetAttributes(AParams);
- ecSetMode: SetMode(AParams, True);
- ecResetMode: SetMode(AParams, False);
- ecReset:
- begin
- AParams.Clear;
- AParams.Add('0');
- SetAttributes(AParams);
- end;
- ecSaveCaret: SaveCaretPos;
- ecRestoreCaret: RestoreCaretPos;
- ecSaveCaretAndAttr: begin SaveCaretPos; SaveAttr; end;
- ecRestoreCaretAndAttr: begin RestoreCaretPos; RestoreAttr; end;
- ecQueryCursorPos:
- begin
- AParams.Clear;
- AParams.Add(IntToStr(FCaretPos.Y));
- AParams.Add(IntToStr(FCaretPos.X));
- SendCodeNoEcho(ecReportCursorPos, AParams);
- end;
- ecQueryDevice: SendCodeNoEcho(ecReportDeviceOK, nil);
- ecTest: PerformTest('E');
- ecScrollRegion:
- begin
- FBuffer.FScrollRange.Top:= GetParam(1, AParams);
- FBuffer.FScrollRange.Bottom:= GetParam(2, AParams);
- end;
- ecScrollDown,
- ecInsertLine: FBuffer.InsertLine(FCaretPos.Y, GetParam(1, AParams));
- ecScrollUp,
- ecDeleteLine: FBuffer.DeleteLine(FCaretPos.Y, GetParam(1, AParams));
- ecSoftReset:
- begin
- FTermMode.CharSet:= False;
- FBuffer.FScrollRange.Top:= 1;
- FBuffer.FScrollRange.Bottom:= FBuffer.Rows;
- end;
- ecCharSet:
- begin
- // Designate Character Set
- if (AParams.Count > 0) and (Length(AParams[0]) > 0) then
- FTermMode.CharSet:= (AParams[0] = '0');
- end
- else
- Result := False;
- end;
- end;
- // calculate font height and width
- function TCustomComTerminal.CalculateMetrics: Boolean;
- var
- Metrics: TTextMetric;
- begin
- GetTextMetrics(Canvas.Handle, Metrics);
- FFontHeight := Metrics.tmHeight;
- FFontWidth := Metrics.tmAveCharWidth;
- // allow only fixed pitch fonts
- Result := (Metrics.tmPitchAndFamily and TMPF_FIXED_PITCH) = 0;
- end;
- // visual character is appears on screen
- procedure TCustomComTerminal.DoChar(Ch: TUTF8Char);
- begin
- if Assigned(FOnChar) then
- FOnChar(Self, Ch);
- end;
- // get custom escape codes processor
- procedure TCustomComTerminal.DoGetEscapeCodes(
- var EscapeCodes: TEscapeCodes);
- begin
- if Assigned(FOnGetEscapeCodes) then
- FOnGetEscapeCodes(Self, EscapeCodes);
- end;
- // string recieved
- procedure TCustomComTerminal.DoStrRecieved(var Str: string);
- begin
- if Assigned(FOnStrRecieved) then
- FOnStrRecieved(Self, Str);
- end;
- // let application handle unhandled escape code
- procedure TCustomComTerminal.DoUnhandledCode(Code: TEscapeCode;
- Data: string);
- begin
- if Assigned(FOnUnhandledCode) then
- FOnUnhandledCode(Self, Code, Data);
- end;
- procedure TCustomComTerminal.DoUnhandledMode(const Data: string; OnOff: Boolean);
- begin
- if Assigned(FOnUnhandledMode) then
- FOnUnhandledMode(Self, Data, OnOff);
- end;
- function TCustomComTerminal.DoMouseWheel(Shift: TShiftState;
- WheelDelta: Integer; MousePos: TPoint): Boolean;
- var
- APos: Integer;
- begin
- Result:= True;
- APos:= GetScrollPos(Handle, SB_VERT);
- if WheelDelta < 0 then
- APos:= APos + Mouse.WheelScrollLines
- else begin
- APos:= APos - Mouse.WheelScrollLines;
- end;
- ModifyScrollBar(SB_VERT, SB_THUMBPOSITION, APos);
- end;
- // create escape codes processor
- procedure TCustomComTerminal.CreateEscapeCodes;
- begin
- if csDesigning in ComponentState then
- Exit;
- case FEmulation of
- teVT52: FEscapeCodes := TEscapeCodesVT52.Create;
- teVT100orANSI: FEscapeCodes := TEscapeCodesVT100.Create;
- else
- begin
- FEscapeCodes := nil;
- DoGetEscapeCodes(FEscapeCodes);
- end;
- end;
- end;
- // perform screen test
- procedure TCustomComTerminal.PerformTest(ACh: Char);
- var
- I, J: Integer;
- TermCh: TComTermChar;
- begin
- with TermCh do
- begin
- Ch := ACh;
- FrontColor := Font.Color;
- BackColor := Color;
- Underline := False;
- end;
- for I := 1 to FBuffer.Columns do
- for J := 1 to FBuffer.Rows do
- FBuffer.SetChar(I, J, TermCh);
- Invalidate;
- end;
- // get current character properties
- function TCustomComTerminal.GetCharAttr: TComTermChar;
- begin
- if FTermAttr.Invert then
- // Result.FrontColor := Color
- Result.FrontColor := FTermAttr.BackColor
- else
- // Result.BackColor := Font.Color;
- Result.FrontColor := FTermAttr.FrontColor;
- if FTermAttr.Invert then
- // Result.BackColor := Font.Color
- Result.BackColor := FTermAttr.FrontColor
- else
- // Result.FrontColor := Color
- Result.BackColor := FTermAttr.BackColor;
- // NEW end changes
- Result.Bold := FTermAttr.Bold;
- Result.Underline := FTermAttr.Underline;
- Result.Ch := #0;
- end;
- // put one character on screen
- procedure TCustomComTerminal.PutChar(Ch: TUTF8Char);
- var
- AWidth: Integer;
- TermCh: TComTermChar;
- begin
- case Ch[1] of
- #8: AdvanceCaret(acBackspace);
- #9: AdvanceCaret(acTab);
- #10: AdvanceCaret(acLineFeed);
- #12: AdvanceCaret(acPage);
- #13: AdvanceCaret(acReturn);
- #32..#255:
- begin
- TermCh := GetCharAttr;
- if not FTermMode.CharSet then
- TermCh.Ch := Ch
- else begin
- case Ch[1] of
- 'j': TermCh.Ch := '┘';
- 'k': TermCh.Ch := '┐';
- 'l': TermCh.Ch := '┌';
- 'm': TermCh.Ch := '└';
- 'n': TermCh.Ch := '┼';
- 'q': TermCh.Ch := '─';
- 't': TermCh.Ch := '├';
- 'u': TermCh.Ch := '┤';
- 'v': TermCh.Ch := '┴';
- 'w': TermCh.Ch := '┬';
- 'x': TermCh.Ch := '│';
- else TermCh.Ch := Ch;
- end;
- end;
- AWidth:= UTF8Width(Ch);
- if AWidth <= 0 then Exit;
- if FWrapLines then WrapLine(AWidth);
- FBuffer.SetChar(FCaretPos.X, FCaretPos.Y, TermCh);
- DrawChar(FCaretPos.X - FTopLeft.X + 1, FCaretPos.Y - FTopLeft.Y + 1, TermCh);
- AdvanceCaret(acChar);
- Dec(AWidth);
- while (AWidth > 0) do
- begin
- TermCh.Ch := #0;
- FBuffer.SetChar(FCaretPos.X, FCaretPos.Y, TermCh);
- AdvanceCaret(acChar);
- Dec(AWidth);
- end;
- end;
- end;
- DoChar(Ch);
- end;
- // init caret
- procedure TCustomComTerminal.InitCaret;
- begin
- CreateTerminalCaret;
- MoveCaret(FCaretPos.X, FCaretPos.Y);
- ShowCaret;
- end;
- // restore caret position
- procedure TCustomComTerminal.RestoreCaretPos;
- begin
- MoveCaret(FSaveCaret.X, FSaveCaret.Y);
- end;
- // save caret position
- procedure TCustomComTerminal.SaveCaretPos;
- begin
- FSaveCaret := FCaretPos;
- end;
- // restore attributes
- procedure TCustomComTerminal.RestoreAttr;
- begin
- FTermAttr := FSaveAttr;
- end;
- // save attributes
- procedure TCustomComTerminal.SaveAttr;
- begin
- FSaveAttr := FTermAttr;
- end;
- procedure TCustomComTerminal.RxBuf(Sender: TObject; const Buffer; Count: Integer);
- var
- L: Integer;
- Str: String;
- // append line feeds to carriage return
- procedure AppendLineFeeds;
- var
- I: Integer;
- begin
- I := 1;
- while I <= Length(Str) do
- begin
- if Str[I] = Chr(13) then
- Str := Copy(Str, 1, I) + Chr(10) + Copy(Str, I + 1, Length(Str) - I);
- Inc(I);
- end;
- end;
- // convert to 7 bit data
- procedure Force7BitData;
- var
- I: Integer;
- begin
- for I := 1 to Length(Str) do
- Str[I] := Char(Byte(Str[I]) and $0FFFFFFF);
- end;
- begin
- if (Length(FPartChar) = 0) then
- begin
- SetLength(Str, Count);
- Move(Buffer, Str[1], Count);
- end
- else begin
- L:= Length(FPartChar);
- SetLength(Str, Count + L);
- Move(FPartChar[1], Str[1], L);
- Move(Buffer, Str[L + 1], Count);
- FPartChar:= EmptyStr;
- end;
- if FForce7Bit then
- begin
- Force7BitData;
- end;
- if FAppendLF then
- begin
- AppendLineFeeds;
- end;
- StringReceived(Str);
- end;
- function TCustomComTerminal.GetConnected: Boolean;
- begin
- Result := False;
- if FPtyDevice <> nil then
- Result := FPtyDevice.Connected;
- end;
- procedure TCustomComTerminal.SetConnected(const Value: Boolean);
- begin
- if FPtyDevice <> nil then
- FPtyDevice.Connected := Value;
- end;
- procedure TCustomComTerminal.SetScrollBars(const Value: TScrollStyle);
- begin
- if FScrollBars <> Value then
- begin
- FScrollBars := Value;
- RecreateWnd(Self);
- end;
- end;
- procedure TCustomComTerminal.SetColumns(const Value: Integer);
- begin
- if Value <> FColumns then
- begin
- FColumns := Min(Max(2, Value), 256);
- AdjustSize;
- if not ((csLoading in ComponentState) or (csDesigning in ComponentState)) then
- begin
- FMainBuffer.Init(0, FColumns);
- FAlternateBuffer.Init(0, FColumns);
- if Assigned(FPtyDevice) then
- FPtyDevice.SetScreenSize(FColumns, FVisibleRows);
- Invalidate;
- end;
- UpdateScrollRange;
- end;
- end;
- procedure TCustomComTerminal.SetRows(const Value: Integer);
- var
- ARows: Integer;
- begin
- ARows := Max(Value, FVisibleRows);
- if ARows <> FRows then
- begin
- FRows := ARows;
- if not ((csLoading in ComponentState) or (csDesigning in ComponentState)) then
- begin
- FMainBuffer.Init(FRows, 0);
- end;
- UpdateScrollRange;
- end;
- end;
- procedure TCustomComTerminal.SetEmulation(const Value: TTermEmulation);
- begin
- if FEmulation <> Value then
- begin
- FEmulation := Value;
- if not (csLoading in ComponentState) then
- begin
- FEscapeCodes.Free;
- CreateEscapeCodes;
- end;
- end;
- end;
- procedure TCustomComTerminal.SetCaret(const Value: TTermCaret);
- begin
- if Value <> FCaret then
- begin
- FCaret := Value;
- if Focused then
- begin
- DestroyCaret(Handle);
- InitCaret;
- end;
- end;
- end;
- procedure TCustomComTerminal.SetPtyDevice(const Value: TCustomPtyDevice);
- begin
- if Value <> FPtyDevice then
- begin
- if FPtyDevice <> nil then
- begin
- FPtyDevice.OnRxBuf:= nil;
- end;
- FPtyDevice := Value;
- if FPtyDevice <> nil then
- begin
- FPtyDevice.OnRxBuf:= RxBuf;
- FPtyDevice.FreeNotification(Self);
- FPtyDevice.SetScreenSize(FColumns, FVisibleRows);
- end;
- end;
- end;
- end.
|