Initial import
This commit is contained in:
parent
d4c175e419
commit
47040b9885
|
@ -0,0 +1,492 @@
|
||||||
|
{$I M_OPS.PAS}
|
||||||
|
|
||||||
|
Unit m_MenuBox;
|
||||||
|
|
||||||
|
Interface
|
||||||
|
|
||||||
|
Uses
|
||||||
|
m_Types,
|
||||||
|
m_Input,
|
||||||
|
m_Output;
|
||||||
|
|
||||||
|
Type
|
||||||
|
TMenuBox = Class
|
||||||
|
Console : TOutput;
|
||||||
|
Image : TConsoleImageRec;
|
||||||
|
HideImage : ^TConsoleImageRec;
|
||||||
|
FrameType : Byte;
|
||||||
|
BoxAttr : Byte;
|
||||||
|
Box3D : Boolean;
|
||||||
|
BoxAttr2 : Byte;
|
||||||
|
BoxAttr3 : Byte;
|
||||||
|
BoxAttr4 : Byte;
|
||||||
|
Shadow : Boolean;
|
||||||
|
ShadowAttr : Byte;
|
||||||
|
HeadAttr : Byte;
|
||||||
|
HeadType : Byte;
|
||||||
|
Header : String;
|
||||||
|
WasOpened : Boolean;
|
||||||
|
|
||||||
|
Constructor Create (Var Screen: TOutput);
|
||||||
|
Destructor Destroy; Override;
|
||||||
|
Procedure Open (X1, Y1, X2, Y2: Byte);
|
||||||
|
Procedure Close;
|
||||||
|
Procedure Hide;
|
||||||
|
Procedure Show;
|
||||||
|
End;
|
||||||
|
|
||||||
|
TMenuListStatusProc = Procedure (Num: Word; Str: String);
|
||||||
|
|
||||||
|
TMenuListBoxRec = Record
|
||||||
|
Name : String;
|
||||||
|
Tagged : Byte; { 0 = false, 1 = true, 2 = never }
|
||||||
|
End;
|
||||||
|
|
||||||
|
TMenuList = Class
|
||||||
|
Screen : TOutput;
|
||||||
|
List : Array[1..65535] of ^TMenuListBoxRec;
|
||||||
|
Box : TMenuBox;
|
||||||
|
InKey : TInput;
|
||||||
|
HiAttr : Byte;
|
||||||
|
LoAttr : Byte;
|
||||||
|
PosBar : Boolean;
|
||||||
|
Format : Byte;
|
||||||
|
LoChars : String;
|
||||||
|
HiChars : String;
|
||||||
|
ExitCode : Char;
|
||||||
|
Picked : Integer;
|
||||||
|
TopPage : Integer;
|
||||||
|
NoWindow : Boolean;
|
||||||
|
ListMax : Integer;
|
||||||
|
AllowTag : Boolean;
|
||||||
|
TagChar : Char;
|
||||||
|
TagKey : Char;
|
||||||
|
TagPos : Byte;
|
||||||
|
TagAttr : Byte;
|
||||||
|
Marked : Word;
|
||||||
|
StatusProc : TMenuListStatusProc;
|
||||||
|
Width : Integer;
|
||||||
|
Length : Integer;
|
||||||
|
X1 : Byte;
|
||||||
|
Y1 : Byte;
|
||||||
|
NoInput : Boolean;
|
||||||
|
|
||||||
|
Constructor Create (Var S: TOutput);
|
||||||
|
Destructor Destroy; Override;
|
||||||
|
Procedure Open (BX1, BY1, BX2, BY2: Byte);
|
||||||
|
Procedure Close;
|
||||||
|
Procedure Add (Str: String; B: Byte);
|
||||||
|
Procedure Get (Num: Word; Var Str: String; Var B: Boolean);
|
||||||
|
Procedure SetStatusProc (P: TMenuListStatusProc);
|
||||||
|
Procedure Clear;
|
||||||
|
Procedure Delete (RecPos : Word);
|
||||||
|
{ Procedure Focus (Num: Word);}
|
||||||
|
Procedure Update;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Implementation
|
||||||
|
|
||||||
|
Uses
|
||||||
|
m_Strings;
|
||||||
|
|
||||||
|
Constructor TMenuBox.Create (Var Screen: TOutput);
|
||||||
|
Begin
|
||||||
|
Inherited Create;
|
||||||
|
|
||||||
|
Console := Screen;
|
||||||
|
Shadow := True;
|
||||||
|
ShadowAttr := 0;
|
||||||
|
Header := '';
|
||||||
|
FrameType := 6;
|
||||||
|
Box3D := True;
|
||||||
|
BoxAttr := 15 + 7 * 16;
|
||||||
|
BoxAttr2 := 8 + 7 * 16;
|
||||||
|
BoxAttr3 := 15 + 7 * 16;
|
||||||
|
BoxAttr4 := 8 + 7 * 16;
|
||||||
|
HeadAttr := 15 + 1 * 16;
|
||||||
|
HeadType := 0;
|
||||||
|
HideImage := NIL;
|
||||||
|
WasOpened := False;
|
||||||
|
|
||||||
|
FillChar(Image, SizeOf(TConsoleImageRec), 0);
|
||||||
|
|
||||||
|
Console.BufFlush;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Destructor TMenuBox.Destroy;
|
||||||
|
Begin
|
||||||
|
Inherited Destroy;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuBox.Open (X1, Y1, X2, Y2: Byte);
|
||||||
|
Const
|
||||||
|
BF : Array[1..8] of String[8] =
|
||||||
|
('ÚÄ¿³³ÀÄÙ',
|
||||||
|
'ÉÍ»ººÈͼ',
|
||||||
|
'ÖÄ·ººÓĽ',
|
||||||
|
'Õ͸³³Ô;',
|
||||||
|
'ÛßÛÛÛÛÜÛ',
|
||||||
|
'ÛßÜÛÛßÜÛ',
|
||||||
|
' ',
|
||||||
|
'.-.||`-''');
|
||||||
|
Var
|
||||||
|
A : Integer;
|
||||||
|
B : Integer;
|
||||||
|
Ch : Char;
|
||||||
|
Begin
|
||||||
|
If Not WasOpened Then
|
||||||
|
If Shadow Then
|
||||||
|
Console.GetScreenImage(X1, Y1, X2 + 2{3}, Y2 + 1, Image)
|
||||||
|
Else
|
||||||
|
Console.GetScreenImage(X1, Y1, X2, Y2, Image);
|
||||||
|
|
||||||
|
WasOpened := True;
|
||||||
|
|
||||||
|
B := X2 - X1 - 1;
|
||||||
|
|
||||||
|
If Not Box3D Then Begin
|
||||||
|
BoxAttr2 := BoxAttr;
|
||||||
|
BoxAttr3 := BoxAttr;
|
||||||
|
BoxAttr4 := BoxAttr;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Console.WriteXY (X1, Y1, BoxAttr, BF[FrameType][1] + strRep(BF[FrameType][2], B));
|
||||||
|
Console.WriteXY (X2, Y1, BoxAttr4, BF[FrameType][3]);
|
||||||
|
|
||||||
|
For A := Y1 + 1 To Y2 - 1 Do Begin
|
||||||
|
Console.WriteXY (X1, A, BoxAttr, BF[FrameType][4] + strRep(' ', B));
|
||||||
|
Console.WriteXY (X2, A, BoxAttr2, BF[FrameType][5]);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Console.WriteXY (X1, Y2, BoxAttr3, BF[FrameType][6]);
|
||||||
|
Console.WriteXY (X1+1, Y2, BoxAttr2, strRep(BF[FrameType][7], B) + BF[FrameType][8]);
|
||||||
|
|
||||||
|
If Header <> '' Then
|
||||||
|
Case HeadType of
|
||||||
|
0 : Console.WriteXY (X1 + 1 + (B - Length(Header)) DIV 2, Y1, HeadAttr, Header);
|
||||||
|
1 : Console.WriteXY (X1 + 1, Y1, HeadAttr, Header);
|
||||||
|
2 : Console.WriteXY (X2 - Length(Header), Y1, HeadAttr, Header);
|
||||||
|
End;
|
||||||
|
|
||||||
|
If Shadow Then Begin
|
||||||
|
For A := Y1 + 1 to Y2 + 1 Do
|
||||||
|
For B := X2 to X2 + 1 Do Begin
|
||||||
|
Ch := Console.ReadCharXY(B, A);
|
||||||
|
Console.WriteXY (B + 1, A, ShadowAttr, Ch);
|
||||||
|
End;
|
||||||
|
|
||||||
|
A := Y2 + 1;
|
||||||
|
For B := (X1 + 2) To (X2 + 2) Do Begin
|
||||||
|
Ch := Console.ReadCharXY(B, A);
|
||||||
|
Console.WriteXY (B, A, ShadowAttr, Ch);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuBox.Close;
|
||||||
|
Begin
|
||||||
|
If WasOpened Then Console.PutScreenImage(Image);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuBox.Hide;
|
||||||
|
Begin
|
||||||
|
If Assigned(HideImage) Then FreeMem(HideImage, SizeOf(TConsoleImageRec));
|
||||||
|
|
||||||
|
GetMem (HideImage, SizeOf(TConsoleImageRec));
|
||||||
|
|
||||||
|
Console.GetScreenImage (Image.X1, Image.Y1, Image.X2, Image.Y2, HideImage^);
|
||||||
|
Console.PutScreenImage (Image);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuBox.Show;
|
||||||
|
Begin
|
||||||
|
If Assigned (HideImage) Then Begin
|
||||||
|
Console.PutScreenImage(HideImage^);
|
||||||
|
FreeMem (HideImage, SizeOf(TConsoleImageRec));
|
||||||
|
HideImage := NIL;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Constructor TMenuList.Create (Var S: TOutput);
|
||||||
|
Begin
|
||||||
|
Inherited Create;
|
||||||
|
|
||||||
|
Screen := S;
|
||||||
|
Box := TMenuBox.Create(S);
|
||||||
|
InKey := TInput.Create;
|
||||||
|
ListMax := 0;
|
||||||
|
HiAttr := 15 + 1 * 16;
|
||||||
|
LoAttr := 1 + 7 * 16;
|
||||||
|
PosBar := True;
|
||||||
|
Format := 0;
|
||||||
|
LoChars := #13#27;
|
||||||
|
HiChars := '';
|
||||||
|
NoWindow := False;
|
||||||
|
AllowTag := False;
|
||||||
|
TagChar := '*';
|
||||||
|
TagKey := #32;
|
||||||
|
TagPos := 0;
|
||||||
|
TagAttr := 15 + 7 * 16;
|
||||||
|
Marked := 0;
|
||||||
|
Picked := 1;
|
||||||
|
NoInput := False;
|
||||||
|
StatusProc := NIL;
|
||||||
|
|
||||||
|
Screen.BufFlush;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuList.Clear;
|
||||||
|
Var
|
||||||
|
A : Word;
|
||||||
|
Begin
|
||||||
|
For A := 1 to ListMax Do Dispose(List[A]);
|
||||||
|
ListMax := 0;
|
||||||
|
Marked := 0;
|
||||||
|
End;
|
||||||
|
|
||||||
|
(*
|
||||||
|
Procedure TMenuList.Focus (Num: Word);
|
||||||
|
Var
|
||||||
|
NewPicked : Word;
|
||||||
|
NewTopPage : Word;
|
||||||
|
Count : Word;
|
||||||
|
Begin
|
||||||
|
If Num > ListMax Then Exit;
|
||||||
|
|
||||||
|
Picked := 1;
|
||||||
|
ListMax :=
|
||||||
|
|
||||||
|
For Count := 1 to ListMax Do
|
||||||
|
|
||||||
|
If Picked < ListMax Then Inc (Picked);
|
||||||
|
If Picked > TopPage + Length - 1 Then Inc (TopPage);
|
||||||
|
End;
|
||||||
|
*)
|
||||||
|
|
||||||
|
Procedure TMenuList.Delete (RecPos : Word);
|
||||||
|
Var
|
||||||
|
Count : Word;
|
||||||
|
Begin
|
||||||
|
If List[RecPos] <> NIL Then Begin
|
||||||
|
Dispose (List[RecPos]);
|
||||||
|
|
||||||
|
For Count := RecPos To ListMax - 1 Do
|
||||||
|
List[Count] := List[Count + 1];
|
||||||
|
|
||||||
|
Dec (ListMax);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Destructor TMenuList.Destroy;
|
||||||
|
Begin
|
||||||
|
Box.Free;
|
||||||
|
InKey.Free;
|
||||||
|
|
||||||
|
Clear;
|
||||||
|
|
||||||
|
Inherited Destroy;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuList.Update;
|
||||||
|
Var
|
||||||
|
A : LongInt;
|
||||||
|
S : String;
|
||||||
|
B : Integer;
|
||||||
|
C : Integer;
|
||||||
|
Begin
|
||||||
|
For A := 0 to Length - 1 Do Begin
|
||||||
|
C := TopPage + A;
|
||||||
|
If C <= ListMax Then Begin
|
||||||
|
S := ' ' + List[C]^.Name + ' ';
|
||||||
|
Case Format of
|
||||||
|
0 : S := strPadR (S, Width, ' ');
|
||||||
|
1 : S := strPadL (S, Width, ' ');
|
||||||
|
2 : S := strPadC (S, Width, ' ');
|
||||||
|
End;
|
||||||
|
End Else
|
||||||
|
S := strRep(' ', Width);
|
||||||
|
|
||||||
|
If C = Picked Then B := HiAttr Else B := LoAttr;
|
||||||
|
|
||||||
|
Screen.WriteXY (X1 + 1, Y1 + 1 + A, B, S);
|
||||||
|
If PosBar Then
|
||||||
|
Screen.WriteXY (X1 + Width + 1, Y1 + 1 + A, Box.BoxAttr2, '°');
|
||||||
|
|
||||||
|
If AllowTag Then
|
||||||
|
If (C <= ListMax) and (List[C]^.Tagged = 1) Then
|
||||||
|
Screen.WriteXY (TagPos, Y1 + 1 + A, TagAttr, TagChar)
|
||||||
|
Else
|
||||||
|
Screen.WriteXY (TagPos, Y1 + 1 + A, TagAttr, ' ');
|
||||||
|
End;
|
||||||
|
|
||||||
|
If PosBar Then
|
||||||
|
If (ListMax > 0) and (Length > 0) Then Begin
|
||||||
|
A := (Picked * Length) DIV ListMax;
|
||||||
|
If Picked >= ListMax Then A := Pred(Length);
|
||||||
|
If (A < 0) or (Picked = 1) Then A := 0;
|
||||||
|
Screen.WriteXY (X1 + Width + 1, Y1 + 1 + A, Box.BoxAttr2, '²');
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuList.Open (BX1, BY1, BX2, BY2 : Byte);
|
||||||
|
Var
|
||||||
|
Ch : Char;
|
||||||
|
A : Word;
|
||||||
|
sPos : Word;
|
||||||
|
ePos : Word;
|
||||||
|
First : Boolean;
|
||||||
|
Begin
|
||||||
|
If Not NoWindow Then
|
||||||
|
Box.Open (BX1, BY1, BX2, BY2);
|
||||||
|
|
||||||
|
X1 := BX1;
|
||||||
|
Y1 := BY1;
|
||||||
|
|
||||||
|
If (Picked < TopPage) or (Picked < 1) or (Picked > ListMax) or (TopPage < 1) or (TopPage > ListMax) Then Begin
|
||||||
|
Picked := 1;
|
||||||
|
TopPage := 1;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Width := BX2 - X1 - 1;
|
||||||
|
Length := BY2 - Y1 - 1;
|
||||||
|
TagPos := X1 + 1;
|
||||||
|
|
||||||
|
If NoInput Then Exit;
|
||||||
|
|
||||||
|
Repeat
|
||||||
|
Update;
|
||||||
|
|
||||||
|
If Assigned(StatusProc) Then
|
||||||
|
If ListMax > 0 Then
|
||||||
|
StatusProc(Picked, List[Picked]^.Name)
|
||||||
|
Else
|
||||||
|
StatusProc(Picked, '');
|
||||||
|
|
||||||
|
Ch := InKey.ReadKey;
|
||||||
|
Case Ch of
|
||||||
|
#00 : Begin
|
||||||
|
Ch := InKey.ReadKey;
|
||||||
|
Case Ch of
|
||||||
|
#71 : Begin { home }
|
||||||
|
Picked := 1;
|
||||||
|
TopPage := 1;
|
||||||
|
End;
|
||||||
|
#72 : Begin { up arrow }
|
||||||
|
If Picked > 1 Then Dec (Picked);
|
||||||
|
If Picked < TopPage Then Dec (TopPage);
|
||||||
|
End;
|
||||||
|
#73 : Begin { page up }
|
||||||
|
If Picked - Length > 1 Then Dec (Picked, Length) Else Picked := 1;
|
||||||
|
If TopPage - Length < 1 Then TopPage := 1 Else Dec(TopPage, Length);
|
||||||
|
End;
|
||||||
|
#79 : Begin { end }
|
||||||
|
If ListMax > Length Then TopPage := ListMax - Length + 1;
|
||||||
|
Picked := ListMax;
|
||||||
|
End;
|
||||||
|
#80 : Begin { down arrow }
|
||||||
|
If Picked < ListMax Then Inc (Picked);
|
||||||
|
If Picked > TopPage + Length - 1 Then Inc (TopPage);
|
||||||
|
End;
|
||||||
|
#81 : If ListMax > 0 Then Begin { page down }
|
||||||
|
If ListMax > Length Then Begin
|
||||||
|
If Picked + Length > ListMax Then
|
||||||
|
Picked := ListMax
|
||||||
|
Else
|
||||||
|
Inc (Picked, Length);
|
||||||
|
Inc (TopPage, Length);
|
||||||
|
If TopPage + Length > ListMax Then TopPage := ListMax - Length + 1;
|
||||||
|
End Else Begin
|
||||||
|
Picked := ListMax;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
Else
|
||||||
|
If Pos(Ch, HiChars) > 0 Then Begin
|
||||||
|
ExitCode := Ch;
|
||||||
|
Exit;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
Else
|
||||||
|
If AllowTag and (Ch = TagKey) and (List[Picked]^.Tagged <> 2) Then Begin
|
||||||
|
If (List[Picked]^.Tagged = 1) Then Begin
|
||||||
|
Dec (List[Picked]^.Tagged);
|
||||||
|
Dec (Marked);
|
||||||
|
End Else Begin
|
||||||
|
List[Picked]^.Tagged := 1;
|
||||||
|
Inc (Marked);
|
||||||
|
End;
|
||||||
|
If Picked < ListMax Then Inc (Picked);
|
||||||
|
If Picked > TopPage + Length - 1 Then Inc (TopPage);
|
||||||
|
End Else
|
||||||
|
If Pos(Ch, LoChars) > 0 Then Begin
|
||||||
|
ExitCode := Ch;
|
||||||
|
Exit;
|
||||||
|
End Else Begin
|
||||||
|
Ch := UpCase(Ch);
|
||||||
|
First := True;
|
||||||
|
sPos := Picked + 1;
|
||||||
|
ePos := ListMax;
|
||||||
|
|
||||||
|
If sPos > ListMax Then sPos := 1;
|
||||||
|
|
||||||
|
A := sPos;
|
||||||
|
|
||||||
|
While (A <= ePos) Do Begin
|
||||||
|
If UpCase(List[A]^.Name[1]) = Ch Then Begin
|
||||||
|
While A <> Picked Do Begin
|
||||||
|
If Picked < A Then Begin
|
||||||
|
If Picked < ListMax Then Inc (Picked);
|
||||||
|
If Picked > TopPage + Length - 1 Then Inc (TopPage);
|
||||||
|
End Else
|
||||||
|
If Picked > A Then Begin
|
||||||
|
If Picked > 1 Then Dec (Picked);
|
||||||
|
If Picked < TopPage Then Dec (TopPage);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
If (A = ListMax) and First Then Begin
|
||||||
|
A := 0;
|
||||||
|
sPos := 1;
|
||||||
|
ePos := Picked - 1;
|
||||||
|
First := False;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Inc (A);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
Until False;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuList.Close;
|
||||||
|
Begin
|
||||||
|
If Not NoWindow Then Box.Close;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuList.Add (Str : String; B : Byte);
|
||||||
|
Begin
|
||||||
|
Inc (ListMax);
|
||||||
|
New (List[ListMax]);
|
||||||
|
List[ListMax]^.Name := Str;
|
||||||
|
List[ListMax]^.Tagged := B;
|
||||||
|
|
||||||
|
If B = 1 Then Inc(Marked);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuList.Get (Num : Word; Var Str : String; Var B : Boolean);
|
||||||
|
Begin
|
||||||
|
Str := '';
|
||||||
|
B := False;
|
||||||
|
If Num <= ListMax Then Begin
|
||||||
|
Str := List[Num]^.Name;
|
||||||
|
B := List[Num]^.Tagged = 1;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuList.SetStatusProc (P : TMenuListStatusProc);
|
||||||
|
Begin
|
||||||
|
StatusProc := P;
|
||||||
|
End;
|
||||||
|
|
||||||
|
End.
|
|
@ -0,0 +1,693 @@
|
||||||
|
{$I M_OPS.PAS}
|
||||||
|
|
||||||
|
Unit m_MenuForm;
|
||||||
|
|
||||||
|
Interface
|
||||||
|
|
||||||
|
Uses
|
||||||
|
m_Types,
|
||||||
|
m_MenuInput,
|
||||||
|
m_Output;
|
||||||
|
|
||||||
|
Const
|
||||||
|
FormMaxItems = 50;
|
||||||
|
|
||||||
|
Const
|
||||||
|
YesNoStr : Array[False..True] of String[03] = ('No', 'Yes');
|
||||||
|
|
||||||
|
Type
|
||||||
|
FormItemType = (
|
||||||
|
ItemNone,
|
||||||
|
ItemString,
|
||||||
|
ItemBoolean,
|
||||||
|
ItemByte,
|
||||||
|
ItemWord,
|
||||||
|
ItemLong,
|
||||||
|
ItemToggle,
|
||||||
|
ItemPath,
|
||||||
|
ItemChar,
|
||||||
|
ItemAttr,
|
||||||
|
ItemFlags,
|
||||||
|
ItemDate,
|
||||||
|
ItemPass,
|
||||||
|
ItemPipe,
|
||||||
|
ItemCaps,
|
||||||
|
ItemBits
|
||||||
|
);
|
||||||
|
|
||||||
|
FormItemPTR = ^FormItemRec;
|
||||||
|
FormItemRec = Record
|
||||||
|
HotKey : Char;
|
||||||
|
Desc : String[60];
|
||||||
|
Help : String[120];
|
||||||
|
DescX : Byte;
|
||||||
|
DescY : Byte;
|
||||||
|
DescSize : Byte;
|
||||||
|
FieldX : Byte;
|
||||||
|
FieldY : Byte;
|
||||||
|
FieldSize : Byte;
|
||||||
|
ItemType : FormItemType;
|
||||||
|
MaxSize : Byte;
|
||||||
|
MinNum : LongInt;
|
||||||
|
MaxNum : LongInt;
|
||||||
|
S : ^String;
|
||||||
|
O : ^Boolean;
|
||||||
|
B : ^Byte;
|
||||||
|
W : ^Word;
|
||||||
|
L : ^LongInt;
|
||||||
|
C : ^Char;
|
||||||
|
F : ^TMenuFormFlagsRec;
|
||||||
|
Toggle : String[68];
|
||||||
|
End;
|
||||||
|
|
||||||
|
TMenuFormHelpProc = Procedure; // tested
|
||||||
|
TMenuFormDrawProc = Procedure (Hi: Boolean); // not functional
|
||||||
|
TMenuFormDataProc = Procedure; // not functional
|
||||||
|
|
||||||
|
TMenuForm = Class
|
||||||
|
Private
|
||||||
|
Function GetColorAttr (C: Byte) : Byte;
|
||||||
|
Function DrawAccessFlags (Var Flags: TMenuFormFlagsRec) : String;
|
||||||
|
Procedure EditAccessFlags (Var Flags: TMenuFormFlagsRec);
|
||||||
|
Procedure AddBasic (HK: Char; D: String; X, Y, FX, FY, DS, FS, MS: Byte; I: FormItemType; P: Pointer; H: String);
|
||||||
|
Procedure BarON;
|
||||||
|
Procedure BarOFF (RecPos: Word);
|
||||||
|
Procedure FieldWrite (RecPos : Word);
|
||||||
|
Procedure EditOption;
|
||||||
|
Public
|
||||||
|
Screen : TOutput;
|
||||||
|
Input : TMenuInput;
|
||||||
|
HelpProc : TMenuFormHelpProc;
|
||||||
|
DrawProc : TMenuFormDrawProc;
|
||||||
|
DataProc : TMenuFormDataProc;
|
||||||
|
ItemData : Array[1..FormMaxItems] of FormItemPTR;
|
||||||
|
Items : Word;
|
||||||
|
ItemPos : Word;
|
||||||
|
Changed : Boolean;
|
||||||
|
ExitOnFirst : Boolean;
|
||||||
|
ExitOnLast : Boolean;
|
||||||
|
WasHiExit : Boolean;
|
||||||
|
WasFirstExit: Boolean;
|
||||||
|
WasLastExit : Boolean;
|
||||||
|
LoExitChars : String[30];
|
||||||
|
HiExitChars : String[30];
|
||||||
|
HelpX : Byte;
|
||||||
|
HelpY : Byte;
|
||||||
|
HelpSize : Byte;
|
||||||
|
HelpColor : Byte;
|
||||||
|
cLo : Byte;
|
||||||
|
cHi : Byte;
|
||||||
|
cData : Byte;
|
||||||
|
cLoKey : Byte;
|
||||||
|
cHiKey : Byte;
|
||||||
|
cField1 : Byte;
|
||||||
|
cField2 : Byte;
|
||||||
|
|
||||||
|
Constructor Create (Var Con: TOutput);
|
||||||
|
Destructor Destroy; Override;
|
||||||
|
|
||||||
|
Procedure Clear;
|
||||||
|
Procedure AddNone (HK: Char; D: String; X, Y, DS: Byte; H: String);
|
||||||
|
Procedure AddStr (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
|
||||||
|
Procedure AddPipe (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
|
||||||
|
Procedure AddPath (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
|
||||||
|
Procedure AddPass (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
|
||||||
|
Procedure AddBol (HK: Char; D: String; X, Y, FX, FY, DS, FS: Byte; P: Pointer; H: String);
|
||||||
|
Procedure AddByte (HK: Char; D: String; X, Y, FX, FY, DS, FS: Byte; MN, MX: Byte; P: Pointer; H: String);
|
||||||
|
Procedure AddWord (HK: Char; D: String; X, Y, FX, FY, DS, FS: Byte; MN, MX: Word; P: Pointer; H: String);
|
||||||
|
Procedure AddLong (HK: Char; D: String; X, Y, FX, FY, DS, FS: Byte; MN, MX: LongInt; P: Pointer; H: String);
|
||||||
|
Procedure AddTog (HK: Char; D: String; X, Y, FX, FY, DS, FS, MN, MX: Byte; TG: String; P: Pointer; H: String);
|
||||||
|
Procedure AddChar (HK: Char; D: String; X, Y, FX, FY, DS, MN, MX: Byte; P: Pointer; H: String);
|
||||||
|
Procedure AddAttr (HK: Char; D: String; X, Y, FX, FY, DS: Byte; P: Pointer; H: String);
|
||||||
|
Procedure AddFlag (HK: Char; D: String; X, Y, FX, FY, DS: Byte; P: Pointer; H: String);
|
||||||
|
Procedure AddDate (HK: Char; D: String; X, Y, FX, FY, DS: Byte; P: Pointer; H: String);
|
||||||
|
Procedure AddCaps (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
|
||||||
|
Procedure AddBits (HK: Char; D: String; X, Y, FX, FY, DS: Byte; Flag: LongInt; P: Pointer; H: String);
|
||||||
|
Function Execute : Char;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Implementation
|
||||||
|
|
||||||
|
Uses
|
||||||
|
m_FileIO,
|
||||||
|
m_Strings,
|
||||||
|
m_MenuBox;
|
||||||
|
|
||||||
|
Constructor TMenuForm.Create (Var Con: TOutput);
|
||||||
|
Begin
|
||||||
|
Inherited Create;
|
||||||
|
|
||||||
|
Screen := Con;
|
||||||
|
HelpProc := NIL;
|
||||||
|
DrawProc := NIL;
|
||||||
|
DataProc := NIL;
|
||||||
|
cLo := 0 + 7 * 16;
|
||||||
|
cHi := 11 + 1 * 16;
|
||||||
|
cData := 1 + 7 * 16;
|
||||||
|
cLoKey := 15 + 7 * 16;
|
||||||
|
cHiKey := 15 + 1 * 16;
|
||||||
|
cField1 := 15 + 1 * 16;
|
||||||
|
cField2 := 7 + 1 * 16;
|
||||||
|
HelpX := 5;
|
||||||
|
HelpY := 25;
|
||||||
|
HelpColor := 15;
|
||||||
|
HelpSize := 75;
|
||||||
|
WasHiExit := False;
|
||||||
|
WasFirstExit := False;
|
||||||
|
ExitOnFirst := False;
|
||||||
|
WasLastExit := False;
|
||||||
|
ExitOnLast := False;
|
||||||
|
|
||||||
|
Input := TMenuInput.Create(Screen);
|
||||||
|
|
||||||
|
Clear;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Destructor TMenuForm.Destroy;
|
||||||
|
Begin
|
||||||
|
Clear;
|
||||||
|
|
||||||
|
Input.Free;
|
||||||
|
|
||||||
|
Inherited Destroy;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.Clear;
|
||||||
|
Var
|
||||||
|
Count : Word;
|
||||||
|
Begin
|
||||||
|
For Count := 1 to Items Do
|
||||||
|
Dispose(ItemData[Count]);
|
||||||
|
|
||||||
|
Items := 0;
|
||||||
|
ItemPos := 1;
|
||||||
|
Changed := False;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TMenuForm.DrawAccessFlags (Var Flags: TMenuFormFlagsRec) : String;
|
||||||
|
Var
|
||||||
|
S : String;
|
||||||
|
Ch : Char;
|
||||||
|
Begin
|
||||||
|
S := '';
|
||||||
|
|
||||||
|
For Ch := 'A' to 'Z' Do
|
||||||
|
If Ord(Ch) - 64 in Flags Then S := S + Ch Else S := S + '-';
|
||||||
|
|
||||||
|
DrawAccessFlags := S;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.EditAccessFlags (Var Flags: TMenuFormFlagsRec);
|
||||||
|
Var
|
||||||
|
Box : TMenuBox;
|
||||||
|
Ch : Char;
|
||||||
|
Begin
|
||||||
|
Box := TMenuBox.Create(Screen);
|
||||||
|
|
||||||
|
Box.Open (25, 11, 56, 14);
|
||||||
|
|
||||||
|
Screen.WriteXY (28, 13, 113, 'A-Z to toggle, ESC to Quit');
|
||||||
|
|
||||||
|
Repeat
|
||||||
|
Screen.WriteXY (28, 12, 112, DrawAccessFlags(Flags));
|
||||||
|
|
||||||
|
Ch := UpCase(Input.ReadKey);
|
||||||
|
|
||||||
|
Case Ch of
|
||||||
|
#00 : Input.ReadKey;
|
||||||
|
#27 : Break;
|
||||||
|
'A'..
|
||||||
|
'Z' : Begin
|
||||||
|
If Ord(Ch) - 64 in Flags Then
|
||||||
|
Flags := Flags - [Ord(Ch) - 64]
|
||||||
|
Else
|
||||||
|
Flags := Flags + [Ord(Ch) - 64];
|
||||||
|
|
||||||
|
Changed := True;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
Until False;
|
||||||
|
|
||||||
|
Box.Close;
|
||||||
|
Box.Free;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TMenuForm.GetColorAttr (C: Byte) : Byte;
|
||||||
|
Var
|
||||||
|
FG : Byte;
|
||||||
|
BG : Byte;
|
||||||
|
Box : TMenuBox;
|
||||||
|
A : Byte;
|
||||||
|
B : Byte;
|
||||||
|
Begin
|
||||||
|
FG := C AND $F;
|
||||||
|
BG := (C SHR 4) AND 7;
|
||||||
|
|
||||||
|
Box := TMenuBox.Create(Screen);
|
||||||
|
|
||||||
|
Box.Header := ' Select color ';
|
||||||
|
|
||||||
|
Box.Open (30, 7, 51, 18);
|
||||||
|
|
||||||
|
Repeat
|
||||||
|
For A := 0 to 9 Do
|
||||||
|
Screen.WriteXY (31, 8 + A, Box.BoxAttr, ' ');
|
||||||
|
|
||||||
|
For A := 0 to 7 Do
|
||||||
|
For B := 0 to 15 Do
|
||||||
|
Screen.WriteXY (33 + B, 9 + A, B + A * 16, 'þ');
|
||||||
|
|
||||||
|
Screen.WriteXY (37, 18, FG + BG * 16, ' Sample ');
|
||||||
|
|
||||||
|
Screen.WriteXYPipe (31 + FG, 8 + BG, 15, 5, 'Û|23ßßß|08Ü');
|
||||||
|
Screen.WriteXYPipe (31 + FG, 9 + BG, 15, 5, 'Û|23 |08Û');
|
||||||
|
Screen.WriteXYPipe (31 + FG, 10 + BG, 15, 5, '|23ß|08ÜÜÜ|08Û');
|
||||||
|
Screen.WriteXY (33 + FG, 9 + BG, FG + BG * 16, 'þ');
|
||||||
|
|
||||||
|
Case Input.ReadKey of
|
||||||
|
#00 : Case Input.ReadKey of
|
||||||
|
#72 : If BG > 0 Then Dec(BG);
|
||||||
|
#75 : If FG > 0 Then Dec(FG);
|
||||||
|
#77 : If FG < 15 Then Inc(FG);
|
||||||
|
#80 : If BG < 7 Then Inc(BG);
|
||||||
|
End;
|
||||||
|
#13 : Begin
|
||||||
|
GetColorAttr := FG + BG * 16;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
#27 : Begin
|
||||||
|
GetColorAttr := C;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
Until False;
|
||||||
|
|
||||||
|
Box.Close;
|
||||||
|
Box.Free;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.AddBasic (HK: Char; D: String; X, Y, FX, FY, DS, FS, MS: Byte; I: FormItemType; P: Pointer; H: String);
|
||||||
|
Begin
|
||||||
|
Inc (Items);
|
||||||
|
|
||||||
|
New (ItemData[Items]);
|
||||||
|
|
||||||
|
With ItemData[Items]^ Do Begin
|
||||||
|
HotKey := HK;
|
||||||
|
Desc := D;
|
||||||
|
DescX := X;
|
||||||
|
DescY := Y;
|
||||||
|
DescSize := DS;
|
||||||
|
Help := H;
|
||||||
|
ItemType := I;
|
||||||
|
FieldSize := FS;
|
||||||
|
MaxSize := MS;
|
||||||
|
FieldX := FX;
|
||||||
|
FieldY := FY;
|
||||||
|
|
||||||
|
Case ItemType of
|
||||||
|
ItemCaps,
|
||||||
|
ItemPipe,
|
||||||
|
ItemPass,
|
||||||
|
ItemDate,
|
||||||
|
ItemPath,
|
||||||
|
ItemString : S := P;
|
||||||
|
ItemBoolean : O := P;
|
||||||
|
ItemAttr,
|
||||||
|
ItemToggle,
|
||||||
|
ItemByte : B := P;
|
||||||
|
ItemWord : W := P;
|
||||||
|
ItemBits,
|
||||||
|
ItemLong : L := P;
|
||||||
|
ItemChar : C := P;
|
||||||
|
ItemFlags : F := P;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.AddNone (HK: Char; D: String; X, Y, DS: Byte; H: String);
|
||||||
|
Begin
|
||||||
|
If Items = FormMaxItems Then Exit;
|
||||||
|
|
||||||
|
AddBasic (HK, D, X, Y, 0, 0, DS, 0, 0, ItemNone, NIL, H);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.AddChar (HK: Char; D: String; X, Y, FX, FY, DS, MN, MX: Byte; P: Pointer; H: String);
|
||||||
|
Begin
|
||||||
|
If Items = FormMaxItems Then Exit;
|
||||||
|
|
||||||
|
AddBasic (HK, D, X, Y, FX, FY, DS, 1, 1, ItemChar, P, H);
|
||||||
|
|
||||||
|
ItemData[Items]^.MinNum := MN;
|
||||||
|
ItemData[Items]^.MaxNum := MX;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.AddStr (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
|
||||||
|
Begin
|
||||||
|
If Items = FormMaxItems Then Exit;
|
||||||
|
|
||||||
|
AddBasic (HK, D, X, Y, FX, FY, DS, FS, MX, ItemString, P, H);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.AddPipe (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
|
||||||
|
Begin
|
||||||
|
If Items = FormMaxItems Then Exit;
|
||||||
|
|
||||||
|
AddBasic (HK, D, X, Y, FX, FY, DS, FS, MX, ItemPipe, P, H);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.AddCaps (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
|
||||||
|
Begin
|
||||||
|
If Items = FormMaxItems Then Exit;
|
||||||
|
|
||||||
|
AddBasic (HK, D, X, Y, FX, FY, DS, FS, MX, ItemCaps, P, H);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.AddPass (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
|
||||||
|
Begin
|
||||||
|
If Items = FormMaxItems Then Exit;
|
||||||
|
|
||||||
|
AddBasic (HK, D, X, Y, FX, FY, DS, FS, MX, ItemPass, P, H);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.AddPath (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
|
||||||
|
Begin
|
||||||
|
If Items = FormMaxItems Then Exit;
|
||||||
|
|
||||||
|
AddBasic (HK, D, X, Y, FX, FY, DS, FS, MX, ItemPath, P, H);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.AddBol (HK: Char; D: String; X, Y, FX, FY, DS, FS: Byte; P: Pointer; H: String);
|
||||||
|
Begin
|
||||||
|
If Items = FormMaxItems Then Exit;
|
||||||
|
|
||||||
|
AddBasic (HK, D, X, Y, FX, FY, DS, FS, 3, ItemBoolean, P, H);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.AddBits (HK: Char; D: String; X, Y, FX, FY, DS: Byte; Flag: LongInt; P: Pointer; H: String);
|
||||||
|
Begin
|
||||||
|
If Items = FormMaxItems Then Exit;
|
||||||
|
|
||||||
|
AddBasic (HK, D, X, Y, FX, FY, DS, 3, 3, ItemBits, P, H);
|
||||||
|
|
||||||
|
ItemData[Items]^.MaxNum := Flag;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.AddByte (HK: Char; D: String; X, Y, FX, FY, DS, FS: Byte; MN, MX: Byte; P: Pointer; H: String);
|
||||||
|
Begin
|
||||||
|
If Items = FormMaxItems Then Exit;
|
||||||
|
|
||||||
|
AddBasic (HK, D, X, Y, FX, FY, DS, FS, Length(strI2S(MX)), ItemByte, P, H);
|
||||||
|
|
||||||
|
ItemData[Items]^.MinNum := MN;
|
||||||
|
ItemData[Items]^.MaxNum := MX;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.AddWord (HK: Char; D: String; X, Y, FX, FY, DS, FS: Byte; MN, MX: Word; P: Pointer; H: String);
|
||||||
|
Begin
|
||||||
|
If Items = FormMaxItems Then Exit;
|
||||||
|
|
||||||
|
AddBasic (HK, D, X, Y, FX, FY, DS, FS, Length(strI2S(MX)), ItemWord, P, H);
|
||||||
|
|
||||||
|
ItemData[Items]^.MinNum := MN;
|
||||||
|
ItemData[Items]^.MaxNum := MX;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.AddLong (HK: Char; D: String; X, Y, FX, FY, DS, FS: Byte; MN, MX: LongInt; P: Pointer; H: String);
|
||||||
|
Begin
|
||||||
|
If Items = FormMaxItems Then Exit;
|
||||||
|
|
||||||
|
AddBasic (HK, D, X, Y, FX, FY, DS, FS, Length(strI2S(MX)), ItemLong, P, H);
|
||||||
|
|
||||||
|
ItemData[Items]^.MinNum := MN;
|
||||||
|
ItemData[Items]^.MaxNum := MX;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.AddTog (HK: Char; D: String; X, Y, FX, FY, DS, FS, MN, MX: Byte; TG: String; P: Pointer; H: String);
|
||||||
|
Begin
|
||||||
|
If Items = FormMaxItems Then Exit;
|
||||||
|
|
||||||
|
AddBasic (HK, D, X, Y, FX, FY, DS, FS, MX, ItemToggle, P, H);
|
||||||
|
|
||||||
|
ItemData[Items]^.Toggle := TG;
|
||||||
|
ItemData[Items]^.MinNum := MN;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.AddAttr (HK: Char; D: String; X, Y, FX, FY, DS: Byte; P: Pointer; H: String);
|
||||||
|
Begin
|
||||||
|
If Items = FormMaxItems Then Exit;
|
||||||
|
|
||||||
|
AddBasic (HK, D, X, Y, FX, FY, DS, 8, 8, ItemAttr, P, H);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.AddFlag (HK: Char; D: String; X, Y, FX, FY, DS: Byte; P: Pointer; H: String);
|
||||||
|
Begin
|
||||||
|
If Items = FormMaxItems Then Exit;
|
||||||
|
|
||||||
|
AddBasic (HK, D, X, Y, FX, FY, DS, 26, 26, ItemFlags, P, H);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.AddDate (HK: Char; D: String; X, Y, FX, FY, DS: Byte; P: Pointer; H: String);
|
||||||
|
Begin
|
||||||
|
If Items = FormMaxItems Then Exit;
|
||||||
|
|
||||||
|
AddBasic (HK, D, X, Y, FX, FY, DS, 8, 8, ItemDate, P, H);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.BarON;
|
||||||
|
Var
|
||||||
|
A : Byte;
|
||||||
|
Begin
|
||||||
|
If ItemPos = 0 Then Exit;
|
||||||
|
|
||||||
|
With ItemData[ItemPos]^ Do Begin
|
||||||
|
Screen.WriteXY (DescX, DescY, cHi, strPadR(Desc, DescSize, ' '));
|
||||||
|
|
||||||
|
A := Pos(HotKey, strUpper(Desc));
|
||||||
|
If A > 0 Then
|
||||||
|
Screen.WriteXY (DescX + A - 1, DescY, cHiKey, Desc[A]);
|
||||||
|
|
||||||
|
If HelpSize > 0 Then
|
||||||
|
If Assigned(HelpProc) Then
|
||||||
|
HelpProc
|
||||||
|
Else
|
||||||
|
Screen.WriteXYPipe (HelpX, HelpY, HelpColor, HelpSize, Help);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.BarOFF (RecPos: Word);
|
||||||
|
Var
|
||||||
|
A : Byte;
|
||||||
|
Begin
|
||||||
|
If RecPos = 0 Then Exit;
|
||||||
|
|
||||||
|
With ItemData[RecPos]^ Do Begin
|
||||||
|
Screen.WriteXY (DescX, DescY, cLo, strPadR(Desc, DescSize, ' '));
|
||||||
|
|
||||||
|
A := Pos(HotKey, strUpper(Desc));
|
||||||
|
If A > 0 Then
|
||||||
|
Screen.WriteXY (DescX + A - 1, DescY, cLoKey, Desc[A]);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.FieldWrite (RecPos : Word);
|
||||||
|
Begin
|
||||||
|
With ItemData[RecPos]^ Do Begin
|
||||||
|
Case ItemType of
|
||||||
|
ItemPass : Screen.WriteXY (FieldX, FieldY, cData, strPadR(strRep('*', Length(S^)), FieldSize, ' '));
|
||||||
|
ItemCaps,
|
||||||
|
ItemDate,
|
||||||
|
ItemPath,
|
||||||
|
ItemString : Screen.WriteXY (FieldX, FieldY, cData, strPadR(S^, FieldSize, ' '));
|
||||||
|
ItemBoolean : Screen.WriteXY (FieldX, FieldY, cData, strPadR(YesNoStr[O^], FieldSize, ' '));
|
||||||
|
ItemByte : Screen.WriteXY (FieldX, FieldY, cData, strPadR(strI2S(B^), FieldSize, ' '));
|
||||||
|
ItemWord : Screen.WriteXY (FieldX, FieldY, cData, strPadR(strI2S(W^), FieldSize, ' '));
|
||||||
|
ItemLong : Screen.WriteXY (FieldX, FieldY, cData, strPadR(strI2S(L^), FieldSize, ' '));
|
||||||
|
ItemToggle : Screen.WriteXY (FieldX, FieldY, cData, StrPadR(strWordGet(B^ + 1 - MinNum, Toggle, ' '), FieldSize, ' '));
|
||||||
|
ItemChar : Screen.WriteXY (FieldX, FieldY, cData, C^);
|
||||||
|
ItemAttr : Screen.WriteXY (FieldX, FieldY, B^, ' Sample ');
|
||||||
|
ItemFlags : Screen.WriteXY (FieldX, FieldY, cData, DrawAccessFlags(F^));
|
||||||
|
ItemPipe : Screen.WriteXYPipe (FieldX, FieldY, 7, FieldSize, S^);
|
||||||
|
ItemBits : Screen.WriteXY (FieldX, FieldY, cData, strPadR(YesNoStr[L^ AND MaxNum <> 0], FieldSize, ' '));
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TMenuForm.EditOption;
|
||||||
|
Var
|
||||||
|
TempStr : String;
|
||||||
|
TempByte : Byte;
|
||||||
|
TempLong : LongInt;
|
||||||
|
Begin
|
||||||
|
With ItemData[ItemPos]^ Do
|
||||||
|
Case ItemType of
|
||||||
|
ItemCaps : S^ := Input.GetStr(FieldX, FieldY, FieldSize, MaxSize, 2, S^);
|
||||||
|
ItemDate : S^ := Input.GetStr(FieldX, FieldY, FieldSize, MaxSize, 3, S^);
|
||||||
|
ItemPass,
|
||||||
|
ItemPipe,
|
||||||
|
ItemString : S^ := Input.GetStr(FieldX, FieldY, FieldSize, MaxSize, 1, S^);
|
||||||
|
ItemBoolean : Begin
|
||||||
|
O^ := Not O^;
|
||||||
|
Changed := True;
|
||||||
|
End;
|
||||||
|
ItemByte : B^ := Byte(Input.GetNum(FieldX, FieldY, FieldSize, MaxSize, MinNum, MaxNum, B^));
|
||||||
|
ItemWord : W^ := Word(Input.GetNum(FieldX, FieldY, FieldSize, MaxSize, MinNum, MaxNum, W^));
|
||||||
|
ItemLong : L^ := LongInt(Input.GetNum(FieldX, FieldY, FieldSize, MaxSize, MinNum, MaxNum, L^));
|
||||||
|
ItemToggle : Begin
|
||||||
|
If B^ < MaxSize Then Inc(B^) Else B^ := MinNum;
|
||||||
|
Changed := True;
|
||||||
|
End;
|
||||||
|
ItemPath : S^ := DirSlash(Input.GetStr(FieldX, FieldY, FieldSize, MaxSize, 1, S^));
|
||||||
|
ItemChar : Begin
|
||||||
|
TempStr := Input.GetStr(FieldX, FieldY, FieldSize, MaxSize, 1, C^);
|
||||||
|
Changed := TempStr[1] <> C^;
|
||||||
|
C^ := TempStr[1];
|
||||||
|
End;
|
||||||
|
ItemAttr : Begin
|
||||||
|
TempByte := GetColorAttr(B^);
|
||||||
|
Changed := TempByte <> B^;
|
||||||
|
B^ := TempByte;
|
||||||
|
End;
|
||||||
|
ItemFlags : EditAccessFlags(F^);
|
||||||
|
ItemBits : Begin
|
||||||
|
Changed := True;
|
||||||
|
TempLong := L^;
|
||||||
|
TempLong := TempLong XOR MaxNum;
|
||||||
|
L^ := TempLong;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
FieldWrite (ItemPos);
|
||||||
|
|
||||||
|
Changed := Changed or Input.Changed;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TMenuForm.Execute : Char;
|
||||||
|
Var
|
||||||
|
Count : Word;
|
||||||
|
Ch : Char;
|
||||||
|
NewPos : Word;
|
||||||
|
NewXPos : Word;
|
||||||
|
Begin
|
||||||
|
WasHiExit := False;
|
||||||
|
|
||||||
|
Input.Attr := cField1;
|
||||||
|
Input.FillAttr := cField2;
|
||||||
|
|
||||||
|
For Count := 1 to Items Do Begin
|
||||||
|
BarOFF(Count);
|
||||||
|
FieldWrite(Count);
|
||||||
|
End;
|
||||||
|
|
||||||
|
BarON;
|
||||||
|
|
||||||
|
Repeat
|
||||||
|
Changed := Changed OR Input.Changed;
|
||||||
|
|
||||||
|
Ch := UpCase(Input.ReadKey);
|
||||||
|
|
||||||
|
Case Ch of
|
||||||
|
#00 : Begin
|
||||||
|
Ch := Input.ReadKey;
|
||||||
|
|
||||||
|
If Pos(Ch, HiExitChars) > 0 Then Begin
|
||||||
|
WasHiExit := True;
|
||||||
|
Execute := Ch;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Case Ch of
|
||||||
|
#72 : If ItemPos > 1 Then Begin
|
||||||
|
BarOFF(ItemPos);
|
||||||
|
Dec(ItemPos);
|
||||||
|
BarON;
|
||||||
|
End Else
|
||||||
|
If ExitOnFirst Then Begin
|
||||||
|
WasFirstExit := True;
|
||||||
|
Execute := Ch;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
#75 : Begin
|
||||||
|
NewPos := 0;
|
||||||
|
NewXPos := 0;
|
||||||
|
|
||||||
|
For Count := 1 to Items Do
|
||||||
|
If (ItemData[Count]^.DescY = ItemData[ItemPos]^.DescY) and
|
||||||
|
(ItemData[Count]^.DescX < ItemData[ItemPos]^.DescX) and
|
||||||
|
(ItemData[Count]^.DescX > NewXPos) Then Begin
|
||||||
|
NewXPos := ItemData[Count]^.DescX;
|
||||||
|
NewPos := Count;
|
||||||
|
End;
|
||||||
|
|
||||||
|
If NewPos > 0 Then Begin
|
||||||
|
BarOFF(ItemPos);
|
||||||
|
ItemPos := NewPos;
|
||||||
|
BarON;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
#77 : Begin
|
||||||
|
NewPos := 0;
|
||||||
|
NewXPos := 80;
|
||||||
|
|
||||||
|
For Count := 1 to Items Do
|
||||||
|
If (ItemData[Count]^.DescY = ItemData[ItemPos]^.DescY) and
|
||||||
|
(ItemData[Count]^.DescX > ItemData[ItemPos]^.DescX) and
|
||||||
|
(ItemData[Count]^.DescX < NewXPos) Then Begin
|
||||||
|
NewXPos := ItemData[Count]^.DescX;
|
||||||
|
NewPos := Count;
|
||||||
|
End;
|
||||||
|
|
||||||
|
If NewPos > 0 Then Begin
|
||||||
|
BarOFF(ItemPos);
|
||||||
|
ItemPos := NewPos;
|
||||||
|
BarON;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
#80 : If ItemPos < Items Then Begin
|
||||||
|
BarOFF(ItemPos);
|
||||||
|
Inc(ItemPos);
|
||||||
|
BarON;
|
||||||
|
End Else
|
||||||
|
If ExitOnLast Then Begin
|
||||||
|
WasLastExit := True;
|
||||||
|
Execute := Ch;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
#13 : If ItemPos > 0 Then
|
||||||
|
If ItemData[ItemPos]^.ItemType = ItemNone Then Begin
|
||||||
|
Execute := ItemData[ItemPos]^.HotKey;
|
||||||
|
Break;
|
||||||
|
End Else
|
||||||
|
EditOption;
|
||||||
|
#27 : Begin
|
||||||
|
Execute := #27;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
Else
|
||||||
|
If Pos(Ch, LoExitChars) > 0 Then Begin
|
||||||
|
Execute := Ch;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
|
||||||
|
For Count := 1 to Items Do
|
||||||
|
If ItemData[Count]^.HotKey = Ch Then Begin
|
||||||
|
BarOFF(ItemPos);
|
||||||
|
ItemPos := Count;
|
||||||
|
BarON;
|
||||||
|
|
||||||
|
If ItemData[ItemPos]^.ItemType = ItemNone Then Begin
|
||||||
|
Execute := ItemData[ItemPos]^.HotKey;
|
||||||
|
BarOFF(ItemPos);
|
||||||
|
Exit;
|
||||||
|
End Else
|
||||||
|
EditOption;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
Until False;
|
||||||
|
|
||||||
|
BarOFF(ItemPos);
|
||||||
|
End;
|
||||||
|
|
||||||
|
End.
|
|
@ -0,0 +1,418 @@
|
||||||
|
{$I M_OPS.PAS}
|
||||||
|
|
||||||
|
Unit m_MenuInput;
|
||||||
|
|
||||||
|
Interface
|
||||||
|
|
||||||
|
Uses
|
||||||
|
m_Strings,
|
||||||
|
m_Input,
|
||||||
|
m_Output;
|
||||||
|
|
||||||
|
Type
|
||||||
|
TMenuInput = Class
|
||||||
|
Private
|
||||||
|
Console : TOutput;
|
||||||
|
Public
|
||||||
|
Key : TInput;
|
||||||
|
HiChars : String;
|
||||||
|
LoChars : String;
|
||||||
|
ExitCode : Char;
|
||||||
|
Attr : Byte;
|
||||||
|
FillChar : Char;
|
||||||
|
FillAttr : Byte;
|
||||||
|
Changed : Boolean;
|
||||||
|
|
||||||
|
Constructor Create (Var Screen: TOutput);
|
||||||
|
Destructor Destroy; Override;
|
||||||
|
Function GetStr (X, Y, Field, Len, Mode: Byte; Default: String) : String;
|
||||||
|
Function GetNum (X, Y, Field, Len: Byte; Min, Max, Default: LongInt) : LongInt;
|
||||||
|
Function GetChar (X, Y : Byte; Default: Char) : Char;
|
||||||
|
Function GetEnter (X, Y, Len: Byte; Default : String) : Boolean;
|
||||||
|
Function GetYN (X, Y : Byte; Default: Boolean) : Boolean;
|
||||||
|
|
||||||
|
Function KeyWaiting : Boolean;
|
||||||
|
Function ReadKey : Char;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Implementation
|
||||||
|
|
||||||
|
Constructor TMenuInput.Create (Var Screen: TOutput);
|
||||||
|
Begin
|
||||||
|
Inherited Create;
|
||||||
|
|
||||||
|
Console := Screen;
|
||||||
|
Key := TInput.Create;
|
||||||
|
LoChars := #13;
|
||||||
|
HiChars := '';
|
||||||
|
Attr := 15 + 1 * 16;
|
||||||
|
FillAttr := 7 + 1 * 16;
|
||||||
|
FillChar := '°';
|
||||||
|
Changed := False;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Destructor TMenuInput.Destroy;
|
||||||
|
Begin
|
||||||
|
Key.Free;
|
||||||
|
|
||||||
|
Inherited Destroy;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TMenuInput.GetYN (X, Y : Byte; Default: Boolean) : Boolean;
|
||||||
|
Var
|
||||||
|
Ch : Char;
|
||||||
|
Res : Boolean;
|
||||||
|
YS : Array[False..True] of String[3] = ('No ', 'Yes');
|
||||||
|
Begin
|
||||||
|
ExitCode := #0;
|
||||||
|
Changed := False;
|
||||||
|
|
||||||
|
Console.CursorXY (X, Y);
|
||||||
|
|
||||||
|
Res := Default;
|
||||||
|
|
||||||
|
Repeat
|
||||||
|
Console.WriteXY (X, Y, Attr, YS[Res]);
|
||||||
|
|
||||||
|
Ch := ReadKey;
|
||||||
|
Case Ch of
|
||||||
|
#00 : Begin
|
||||||
|
Ch := ReadKey;
|
||||||
|
If Pos(Ch, HiChars) > 0 Then Begin
|
||||||
|
ExitCode := Ch;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
#13,
|
||||||
|
#32 : Res := Not Res;
|
||||||
|
Else
|
||||||
|
If Pos(Ch, LoChars) > 0 Then Begin
|
||||||
|
ExitCode := Ch;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
Until False;
|
||||||
|
|
||||||
|
Changed := (Res <> Default);
|
||||||
|
GetYN := Res;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TMenuInput.GetChar (X, Y : Byte; Default: Char) : Char;
|
||||||
|
Var
|
||||||
|
Ch : Char;
|
||||||
|
Res : Char;
|
||||||
|
Begin
|
||||||
|
ExitCode := #0;
|
||||||
|
Changed := False;
|
||||||
|
Res := Default;
|
||||||
|
|
||||||
|
Console.CursorXY (X, Y);
|
||||||
|
|
||||||
|
Repeat
|
||||||
|
Console.WriteXY (X, Y, Attr, Res);
|
||||||
|
|
||||||
|
Ch := ReadKey;
|
||||||
|
|
||||||
|
Case Ch of
|
||||||
|
#00 : Begin
|
||||||
|
Ch := ReadKey;
|
||||||
|
If Pos(Ch, HiChars) > 0 Then Begin
|
||||||
|
ExitCode := Ch;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
Else
|
||||||
|
If Ch = #27 Then Res := Default;
|
||||||
|
|
||||||
|
If Pos(Ch, LoChars) > 0 Then Begin
|
||||||
|
ExitCode := Ch;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
|
||||||
|
If Ord(Ch) > 31 Then Res := Ch;
|
||||||
|
End;
|
||||||
|
Until False;
|
||||||
|
|
||||||
|
GetChar := Res;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TMenuInput.GetEnter (X, Y, Len: Byte; Default : String) : Boolean;
|
||||||
|
Var
|
||||||
|
Ch : Char;
|
||||||
|
Res : Boolean;
|
||||||
|
Begin
|
||||||
|
ExitCode := #0;
|
||||||
|
Changed := False;
|
||||||
|
|
||||||
|
Console.WriteXY (X, Y, Attr, strPadR(Default, Len, ' '));
|
||||||
|
Console.CursorXY (X, Y);
|
||||||
|
|
||||||
|
Repeat
|
||||||
|
Ch := ReadKey;
|
||||||
|
Res := Ch = #13;
|
||||||
|
Case Ch of
|
||||||
|
#00 : Begin
|
||||||
|
Ch := ReadKey;
|
||||||
|
If Pos(Ch, HiChars) > 0 Then Begin
|
||||||
|
ExitCode := Ch;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
Else
|
||||||
|
If Pos(Ch, LoChars) > 0 Then Begin
|
||||||
|
ExitCode := Ch;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
Until Res;
|
||||||
|
|
||||||
|
Changed := Res;
|
||||||
|
GetEnter := Res;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TMenuInput.GetStr (X, Y, Field, Len, Mode : Byte; Default : String) : String;
|
||||||
|
{ mode options: }
|
||||||
|
{ 0 = numbers only }
|
||||||
|
{ 1 = as typed }
|
||||||
|
{ 2 = all caps }
|
||||||
|
{ 3 = date input }
|
||||||
|
Var
|
||||||
|
Ch : Char;
|
||||||
|
Str : String;
|
||||||
|
StrPos : Integer;
|
||||||
|
Junk : Integer;
|
||||||
|
CurPos : Integer;
|
||||||
|
|
||||||
|
Procedure ReDraw;
|
||||||
|
Var
|
||||||
|
T : String;
|
||||||
|
Begin
|
||||||
|
T := Copy(Str, Junk, Field);
|
||||||
|
|
||||||
|
Console.WriteXY (X, Y, Attr, T);
|
||||||
|
Console.WriteXY (X + Length(T), Y, FillAttr, strRep(FillChar, Field - Length(T)));
|
||||||
|
Console.CursorXY (X + CurPos - 1, Console.CursorY);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure ReDrawPart;
|
||||||
|
Var
|
||||||
|
T : String;
|
||||||
|
Begin
|
||||||
|
T := Copy(Str, StrPos, Field - CurPos + 1);
|
||||||
|
|
||||||
|
Console.WriteXY (Console.CursorX, Y, Attr, T);
|
||||||
|
Console.WriteXY (Console.CursorX + Length(T), Y, FillAttr, strRep(FillChar, (Field - CurPos + 1) - Length(T)));
|
||||||
|
Console.CursorXY (X + CurPos - 1, Y);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure ScrollRight;
|
||||||
|
Begin
|
||||||
|
Inc (Junk);
|
||||||
|
If Junk > Length(Str) Then Junk := Length(Str);
|
||||||
|
If Junk > Len then Junk := Len;
|
||||||
|
CurPos := StrPos - Junk + 1;
|
||||||
|
ReDraw;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure ScrollLeft;
|
||||||
|
Begin
|
||||||
|
If Junk > 1 Then Begin
|
||||||
|
Dec (Junk);
|
||||||
|
CurPos := StrPos - Junk + 1;
|
||||||
|
ReDraw;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure Add_Char (Ch : Char);
|
||||||
|
Begin
|
||||||
|
If Length(Str) >= Len Then Exit;
|
||||||
|
|
||||||
|
If (CurPos >= Field) and (Field <> Len) Then ScrollRight;
|
||||||
|
|
||||||
|
Insert (Ch, Str, StrPos);
|
||||||
|
If StrPos < Length(Str) Then ReDrawPart;
|
||||||
|
|
||||||
|
Inc (StrPos);
|
||||||
|
Inc (CurPos);
|
||||||
|
|
||||||
|
Console.WriteXY (Console.CursorX, Console.CursorY, Attr, Ch);
|
||||||
|
Console.CursorXY (Console.CursorX + 1, Console.CursorY);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Begin
|
||||||
|
Changed := False;
|
||||||
|
Str := Default;
|
||||||
|
StrPos := Length(Str) + 1;
|
||||||
|
Junk := Length(Str) - Field + 1;
|
||||||
|
If Junk < 1 Then Junk := 1;
|
||||||
|
CurPos := StrPos - Junk + 1;
|
||||||
|
|
||||||
|
Console.CursorXY (X, Y);
|
||||||
|
Console.TextAttr := Attr;
|
||||||
|
|
||||||
|
ReDraw;
|
||||||
|
|
||||||
|
Repeat
|
||||||
|
Ch := Key.ReadKey;
|
||||||
|
Case Ch of
|
||||||
|
#00 : Begin
|
||||||
|
Ch := Key.ReadKey;
|
||||||
|
Case Ch of
|
||||||
|
#77 : If StrPos < Length(Str) + 1 Then Begin
|
||||||
|
If (CurPos = Field) and (StrPos < Length(Str)) Then ScrollRight;
|
||||||
|
Inc (CurPos);
|
||||||
|
Inc (StrPos);
|
||||||
|
Console.CursorXY (Console.CursorX + 1, Console.CursorY);
|
||||||
|
End;
|
||||||
|
#75 : If StrPos > 1 Then Begin
|
||||||
|
If CurPos = 1 Then ScrollLeft;
|
||||||
|
Dec (StrPos);
|
||||||
|
Dec (CurPos);
|
||||||
|
Console.CursorXY (Console.CursorX - 1, Console.CursorY);
|
||||||
|
End;
|
||||||
|
#71 : If StrPos > 1 Then Begin
|
||||||
|
StrPos := 1;
|
||||||
|
Junk := 1;
|
||||||
|
CurPos := 1;
|
||||||
|
ReDraw;
|
||||||
|
End;
|
||||||
|
#79 : Begin
|
||||||
|
StrPos := Length(Str) + 1;
|
||||||
|
Junk := Length(Str) - Field + 1;
|
||||||
|
If Junk < 1 Then Junk := 1;
|
||||||
|
CurPos := StrPos - Junk + 1;
|
||||||
|
ReDraw;
|
||||||
|
End;
|
||||||
|
#83 : If (StrPos <= Length(Str)) and (Length(Str) > 0) Then Begin
|
||||||
|
Delete (Str, StrPos, 1);
|
||||||
|
ReDrawPart;
|
||||||
|
End;
|
||||||
|
#115: Begin
|
||||||
|
If (StrPos > 1) and (Str[StrPos] = ' ') or (Str[StrPos - 1] = ' ') Then Begin
|
||||||
|
If CurPos = 1 Then ScrollLeft;
|
||||||
|
Dec(StrPos);
|
||||||
|
Dec(CurPos);
|
||||||
|
|
||||||
|
While (StrPos > 1) and (Str[StrPos] = ' ') Do Begin
|
||||||
|
If CurPos = 1 Then ScrollLeft;
|
||||||
|
Dec(StrPos);
|
||||||
|
Dec(CurPos);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
While (StrPos > 1) and (Str[StrPos] <> ' ') Do Begin
|
||||||
|
If CurPos = 1 Then ScrollLeft;
|
||||||
|
Dec(StrPos);
|
||||||
|
Dec(CurPos);
|
||||||
|
End;
|
||||||
|
|
||||||
|
While (StrPos > 1) and (Str[StrPos] <> ' ') Do Begin
|
||||||
|
If CurPos = 1 Then ScrollLeft;
|
||||||
|
Dec(StrPos);
|
||||||
|
Dec(CurPos);
|
||||||
|
End;
|
||||||
|
|
||||||
|
If (Str[StrPos] = ' ') and (StrPos > 1) Then Begin
|
||||||
|
Inc(StrPos);
|
||||||
|
Inc(CurPos);
|
||||||
|
End;
|
||||||
|
|
||||||
|
ReDraw;
|
||||||
|
End;
|
||||||
|
#116: Begin
|
||||||
|
While StrPos < Length(Str) + 1 Do Begin
|
||||||
|
If (CurPos = Field) and (StrPos < Length(Str)) Then ScrollRight;
|
||||||
|
Inc (CurPos);
|
||||||
|
Inc (StrPos);
|
||||||
|
|
||||||
|
If Str[StrPos] = ' ' Then Begin
|
||||||
|
If StrPos < Length(Str) + 1 Then Begin
|
||||||
|
If (CurPos = Field) and (StrPos < Length(Str)) Then ScrollRight;
|
||||||
|
Inc (CurPos);
|
||||||
|
Inc (StrPos);
|
||||||
|
End;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
Console.CursorXY (X + CurPos - 1, Y);
|
||||||
|
End;
|
||||||
|
Else
|
||||||
|
If Pos(Ch, HiChars) > 0 Then Begin
|
||||||
|
ExitCode := Ch;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
#08 : If StrPos > 1 Then Begin
|
||||||
|
Dec (StrPos);
|
||||||
|
Delete (Str, StrPos, 1);
|
||||||
|
If CurPos = 1 Then
|
||||||
|
ScrollLeft
|
||||||
|
Else Begin
|
||||||
|
Console.CursorXY (Console.CursorX - 1, Console.CursorY);
|
||||||
|
Dec (CurPos);
|
||||||
|
ReDrawPart;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
^Y : Begin
|
||||||
|
Str := '';
|
||||||
|
StrPos := 1;
|
||||||
|
Junk := 1;
|
||||||
|
CurPos := 1;
|
||||||
|
ReDraw;
|
||||||
|
End;
|
||||||
|
#32..
|
||||||
|
#254: Case Mode of
|
||||||
|
0 : If Ch in ['0'..'9', '-'] Then Add_Char(Ch);
|
||||||
|
1 : Add_Char (Ch);
|
||||||
|
2 : Add_Char (UpCase(Ch));
|
||||||
|
3 : If (Ch > '/') and (Ch < ':') Then
|
||||||
|
Case StrPos of
|
||||||
|
2,5 : Begin
|
||||||
|
Add_Char (Ch);
|
||||||
|
Add_Char ('/');
|
||||||
|
End;
|
||||||
|
3,6 : Begin
|
||||||
|
Add_Char ('/');
|
||||||
|
Add_Char (Ch);
|
||||||
|
End;
|
||||||
|
Else
|
||||||
|
Add_Char (Ch);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
Else
|
||||||
|
If Pos(Ch, LoChars) > 0 Then Begin
|
||||||
|
ExitCode := Ch;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
Until False;
|
||||||
|
|
||||||
|
Changed := (Str <> Default);
|
||||||
|
Result := Str;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TMenuInput.GetNum (X, Y, Field, Len: Byte; Min, Max, Default: LongInt) : LongInt;
|
||||||
|
Var
|
||||||
|
N : LongInt;
|
||||||
|
Begin
|
||||||
|
N := Default;
|
||||||
|
N := strS2I(Self.GetStr(X, Y, Field, Len, 0, strI2S(N)));
|
||||||
|
|
||||||
|
If N < Min Then N := Min;
|
||||||
|
If N > Max Then N := Max;
|
||||||
|
|
||||||
|
GetNum := N;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TMenuInput.KeyWaiting : Boolean;
|
||||||
|
Begin
|
||||||
|
Result := Key.KeyPressed;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TMenuInput.ReadKey : Char;
|
||||||
|
Begin
|
||||||
|
Result := Key.ReadKey;
|
||||||
|
End;
|
||||||
|
|
||||||
|
End.
|
|
@ -0,0 +1,718 @@
|
||||||
|
{$I M_OPS.PAS}
|
||||||
|
|
||||||
|
Unit m_Output_Darwin;
|
||||||
|
|
||||||
|
Interface
|
||||||
|
|
||||||
|
Uses
|
||||||
|
TermIO,
|
||||||
|
BaseUnix,
|
||||||
|
m_Types;
|
||||||
|
|
||||||
|
Const
|
||||||
|
ConIn = 0;
|
||||||
|
ConOut = 1;
|
||||||
|
ConBufSize = 4096;
|
||||||
|
|
||||||
|
Type
|
||||||
|
TOutputDarwin = Class
|
||||||
|
Private
|
||||||
|
TermInfo : TermIos;
|
||||||
|
TermInRaw : Boolean;
|
||||||
|
TermOutRaw : Boolean;
|
||||||
|
OutBuffer : Array[1..ConBufSize] of Char;
|
||||||
|
FTextAttr : Byte;
|
||||||
|
FWinTop : Byte;
|
||||||
|
FCursorX : Byte;
|
||||||
|
FCursorY : Byte;
|
||||||
|
|
||||||
|
Procedure SetTextAttr (Attr: Byte);
|
||||||
|
Public
|
||||||
|
OutBufPos : Word;
|
||||||
|
ScreenSize : Byte;
|
||||||
|
Buffer : TConsoleScreenRec;
|
||||||
|
Active : Boolean;
|
||||||
|
SavedTerm : TermIOS;
|
||||||
|
FWinBot : Byte;
|
||||||
|
|
||||||
|
Function AttrToAnsi (Attr: Byte) : String;
|
||||||
|
Procedure BufFlush;
|
||||||
|
Procedure BufAddStr (Str: String);
|
||||||
|
Procedure SaveRawSettings (Var TIo: TermIos);
|
||||||
|
Procedure RestoreRawSettings (TIo: TermIos);
|
||||||
|
Procedure SetRawMode (SetOn: Boolean);
|
||||||
|
Procedure WriteXY (X, Y, A: Byte; Text: String);
|
||||||
|
Procedure WriteXYPipe (X, Y, Attr, Pad: Integer; Text: String);
|
||||||
|
Procedure GetScreenImage (X1, Y1, X2, Y2: Byte; Var Image: TConsoleImageRec);
|
||||||
|
Procedure PutScreenImage (Image: TConsoleImageRec);
|
||||||
|
Procedure LoadScreenImage (Var DataPtr; Len, Width, X, Y: Integer);
|
||||||
|
|
||||||
|
Constructor Create (A: Boolean);
|
||||||
|
Destructor Destroy; Override;
|
||||||
|
Procedure ClearScreen; Virtual;
|
||||||
|
Procedure ScrollWindow; Virtual;
|
||||||
|
Procedure ClearEOL;
|
||||||
|
Procedure CursorXY (X, Y: Byte);
|
||||||
|
Procedure SetWindow (X1, Y1, X2, Y2: Byte; Home: Boolean);
|
||||||
|
Procedure SetScreenSize (Mode: Byte);
|
||||||
|
Procedure SetWindowTitle (Str: String);
|
||||||
|
Procedure WriteChar (Ch: Char);
|
||||||
|
Procedure WriteLine (Str: String);
|
||||||
|
Procedure WriteLineRec (YPos: Integer; Line: TConsoleLineRec);
|
||||||
|
Procedure WriteStr (Str: String);
|
||||||
|
Procedure RawWriteStr (Str: String);
|
||||||
|
Function ReadCharXY (X, Y: Byte) : Char;
|
||||||
|
Function ReadAttrXY (X, Y: Byte) : Byte;
|
||||||
|
Procedure ShowBuffer;
|
||||||
|
|
||||||
|
Property TextAttr : Byte Read FTextAttr Write SetTextAttr;
|
||||||
|
Property CursorX : Byte Read FCursorX;
|
||||||
|
Property CursorY : Byte Read FCursorY;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Implementation
|
||||||
|
|
||||||
|
Uses
|
||||||
|
m_Strings;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.WriteLineRec (YPos: Integer; Line: TConsoleLineRec);
|
||||||
|
Var
|
||||||
|
Count : LongInt;
|
||||||
|
Begin
|
||||||
|
CursorXY(1, YPos);
|
||||||
|
|
||||||
|
For Count := 1 to 80 Do
|
||||||
|
BufAddStr(AttrToAnsi(Line[Count].Attributes) + Line[Count].UnicodeChar);
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
|
||||||
|
// For Count := 1 to 80 Do Begin
|
||||||
|
// FTextAttr := Line[Count].Attributes;
|
||||||
|
// WriteChar(Line[Count].UnicodeChar);
|
||||||
|
// End;
|
||||||
|
|
||||||
|
Buffer[YPos] := Line;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Constructor TOutputDarwin.Create (A: Boolean);
|
||||||
|
Begin
|
||||||
|
Inherited Create;
|
||||||
|
|
||||||
|
// SaveRawSettings(SavedTerm);
|
||||||
|
|
||||||
|
SetRawMode(True);
|
||||||
|
|
||||||
|
Active := A;
|
||||||
|
OutBufPos := 0;
|
||||||
|
FTextAttr := 7;
|
||||||
|
FWinTop := 1;
|
||||||
|
FWinBot := 25;
|
||||||
|
ScreenSize := 25;
|
||||||
|
|
||||||
|
RawWriteStr (#27 + '(U' + #27 + '[0m');
|
||||||
|
|
||||||
|
ClearScreen;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Destructor TOutputDarwin.Destroy;
|
||||||
|
Begin
|
||||||
|
WriteLine('');
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
|
||||||
|
// RestoreRawSettings(SavedTerm);
|
||||||
|
SetRawMode(False);
|
||||||
|
|
||||||
|
Inherited Destroy;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Const
|
||||||
|
AnsiTable : String[8] = '04261537';
|
||||||
|
|
||||||
|
Function TOutputDarwin.AttrToAnsi (Attr: Byte) : String;
|
||||||
|
Var
|
||||||
|
Str : String[16];
|
||||||
|
OldFG : LongInt;
|
||||||
|
OldBG : LongInt;
|
||||||
|
FG : LongInt;
|
||||||
|
BG : LongInt;
|
||||||
|
|
||||||
|
Procedure AddSep (Ch: Char);
|
||||||
|
Begin
|
||||||
|
If Length(Str) > 0 Then
|
||||||
|
Str := Str + ';';
|
||||||
|
Str := Str + Ch;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Begin
|
||||||
|
If Attr = FTextAttr Then Begin
|
||||||
|
AttrToAnsi := '';
|
||||||
|
Exit;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Str := '';
|
||||||
|
FG := Attr and $F;
|
||||||
|
BG := Attr shr 4;
|
||||||
|
OldFG := FTextAttr and $F;
|
||||||
|
OldBG := FTextAttr shr 4;
|
||||||
|
|
||||||
|
If (OldFG <> 7) or (FG = 7) or ((OldFG > 7) and (FG < 8)) or ((OldBG > 7) and (BG < 8)) Then Begin
|
||||||
|
Str := '0';
|
||||||
|
OldFG := 7;
|
||||||
|
OldBG := 0;
|
||||||
|
End;
|
||||||
|
|
||||||
|
If (FG > 7) and (OldFG < 8) Then Begin
|
||||||
|
AddSep('1');
|
||||||
|
OldFG := OldFG or 8;
|
||||||
|
End;
|
||||||
|
|
||||||
|
If (BG and 8) <> (OldBG and 8) Then Begin
|
||||||
|
AddSep('5');
|
||||||
|
OldBG := OldBG or 8;
|
||||||
|
End;
|
||||||
|
|
||||||
|
If (FG <> OldFG) Then Begin
|
||||||
|
AddSep('3');
|
||||||
|
Str := Str + AnsiTable[(FG and 7) + 1];
|
||||||
|
End;
|
||||||
|
|
||||||
|
If (BG <> OldBG) Then Begin
|
||||||
|
AddSep('4');
|
||||||
|
Str := Str + AnsiTable[(BG and 7) + 1];
|
||||||
|
End;
|
||||||
|
|
||||||
|
FTextAttr := FG + BG * 16;
|
||||||
|
AttrToAnsi := #27 + '[' + Str + 'm';
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.BufFlush;
|
||||||
|
Begin
|
||||||
|
If OutBufPos > 0 Then Begin
|
||||||
|
If Active Then fpWrite (ConOut, OutBuffer[1], OutBufPos);
|
||||||
|
OutBufPos := 0;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.BufAddStr (Str: String);
|
||||||
|
Var
|
||||||
|
Count : LongInt;
|
||||||
|
Begin
|
||||||
|
For Count := 1 to Length(Str) Do Begin
|
||||||
|
Inc (OutBufPos);
|
||||||
|
OutBuffer[OutBufPos] := Str[Count];
|
||||||
|
If OutBufPos = ConBufSize Then BufFlush;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.SetTextAttr (Attr: Byte);
|
||||||
|
Begin
|
||||||
|
If Attr = FTextAttr Then Exit;
|
||||||
|
|
||||||
|
BufAddStr(AttrToAnsi(Attr));
|
||||||
|
|
||||||
|
FTextAttr := Attr;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.CursorXY (X, Y: Byte);
|
||||||
|
Begin
|
||||||
|
If (Y < 1) Then Y := 1 Else
|
||||||
|
// If (Y > FWinBot) Then Y := FWinBot; {changed 109a4}
|
||||||
|
If (Y > ScreenSize) Then Y := ScreenSize;
|
||||||
|
If (X < 1) Then X := 1 Else
|
||||||
|
If (X > 80) Then X := 80;
|
||||||
|
|
||||||
|
BufAddStr(#27 + '[' + strI2S(Y) + ';' + strI2S(X) + 'H');
|
||||||
|
BufFlush;
|
||||||
|
|
||||||
|
FCursorX := X;
|
||||||
|
FCursorY := Y;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.ClearScreen;
|
||||||
|
Var
|
||||||
|
Fill : TCharInfo;
|
||||||
|
Count : Byte;
|
||||||
|
Begin
|
||||||
|
BufFlush;
|
||||||
|
|
||||||
|
Fill.Attributes := FTextAttr;
|
||||||
|
Fill.UnicodeChar := ' ';
|
||||||
|
|
||||||
|
If (FWinTop = 1) and (FWinBot = {25}ScreenSize) Then Begin
|
||||||
|
BufAddStr(#27 + '[2J');
|
||||||
|
FillWord (Buffer, SizeOf(Buffer) DIV 2, Word(Fill));
|
||||||
|
End Else Begin
|
||||||
|
For Count := FWinTop to FWinBot Do Begin
|
||||||
|
BufAddStr (#27 + '[' + strI2S(Count) + ';1H' + #27 + '[K');
|
||||||
|
FillWord (Buffer[Count][1], SizeOf(TConsoleLineRec) DIV 2, Word(Fill));
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
CursorXY (1, FWinTop);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.SetScreenSize (Mode: Byte);
|
||||||
|
Begin
|
||||||
|
FWinBot := Mode;
|
||||||
|
ScreenSize := Mode;
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
RawWriteStr(#27 + '[8;' + strI2S(Mode) + ';80t');
|
||||||
|
SetWindow(1, 1, 80, Mode, False);
|
||||||
|
//need to figure this out.
|
||||||
|
|
||||||
|
//esc[8;h;w
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.SetWindow (X1, Y1, X2, Y2: Byte; Home: Boolean);
|
||||||
|
Begin
|
||||||
|
// X1 and X2 are ignored in Darwin and are only here for compatibility
|
||||||
|
// reasons.
|
||||||
|
|
||||||
|
FWinTop := Y1;
|
||||||
|
FWinBot := Y2;
|
||||||
|
|
||||||
|
BufAddStr (#27 + '[' + strI2S(Y1) + ';' + strI2S(Y2) + 'r');
|
||||||
|
BufFlush;
|
||||||
|
|
||||||
|
If Home Then CursorXY (1, Y1);
|
||||||
|
|
||||||
|
If (FCursorY > Y2) Then CursorXY (CursorX, Y2);
|
||||||
|
|
||||||
|
// If Home or (FCursorY < Y1) or (FCursorY > Y2) Then CursorXY(1, Y1);
|
||||||
|
{ this home thing is shady. compare it to win. going from 50 to 25 }
|
||||||
|
{ will screw up the buffers - this has to be more elegant. }
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.SetWindowTitle (Str: String);
|
||||||
|
Begin
|
||||||
|
RawWriteStr (#27 + ']0;' + Str + #07);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.ClearEOL;
|
||||||
|
Var
|
||||||
|
Fill : TCharInfo;
|
||||||
|
Begin
|
||||||
|
BufAddStr(#27 + '[K');
|
||||||
|
|
||||||
|
Fill.Attributes := 7;
|
||||||
|
Fill.UnicodeChar := ' ';
|
||||||
|
|
||||||
|
FillWord (Buffer[CursorY][CursorX], (80 - CursorX) * 2, Word(Fill));
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.ScrollWindow;
|
||||||
|
Begin
|
||||||
|
Move (Buffer[2][1], Buffer[1][1], SizeOf(TConsoleLineRec) * (FWinBot - 1));
|
||||||
|
FillChar(Buffer[FWinBot][1], SizeOf(TConsoleLineRec), 0);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.WriteChar (Ch: Char);
|
||||||
|
Var
|
||||||
|
A : Byte;
|
||||||
|
Begin
|
||||||
|
If Ch <> #10 Then BufAddStr(Ch);
|
||||||
|
|
||||||
|
Case Ch of
|
||||||
|
#08 : If FCursorX > 1 Then
|
||||||
|
Dec(FCursorX);
|
||||||
|
#10 : Begin
|
||||||
|
If FCursorY < FWinBot Then Begin
|
||||||
|
BufAddStr(Ch);
|
||||||
|
Inc (FCursorY)
|
||||||
|
End Else Begin
|
||||||
|
A := FTextAttr;
|
||||||
|
SetTextAttr(7);
|
||||||
|
BufAddStr(Ch);
|
||||||
|
ScrollWindow;
|
||||||
|
SetTextAttr(A);
|
||||||
|
End;
|
||||||
|
|
||||||
|
FCursorX := 1;
|
||||||
|
CursorXY(FCursorX, FCursorY);
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
End;
|
||||||
|
#13 : FCursorX := 1;
|
||||||
|
Else
|
||||||
|
Buffer[FCursorY][FCursorX].Attributes := FTextAttr;
|
||||||
|
Buffer[FCursorY][FCursorX].UnicodeChar := Ch;
|
||||||
|
|
||||||
|
If FCursorX < 80 Then
|
||||||
|
Inc (FCursorX)
|
||||||
|
Else Begin
|
||||||
|
FCursorX := 1;
|
||||||
|
|
||||||
|
If FCursorY < FWinBot Then
|
||||||
|
Inc (FCursorY)
|
||||||
|
Else
|
||||||
|
ScrollWindow;
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.WriteStr (Str: String);
|
||||||
|
Var
|
||||||
|
Count : Byte;
|
||||||
|
Begin
|
||||||
|
For Count := 1 to Length(Str) Do
|
||||||
|
WriteChar(Str[Count]);
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.WriteLine (Str: String);
|
||||||
|
Var
|
||||||
|
Count : Byte;
|
||||||
|
Begin
|
||||||
|
Str := Str + #13#10;
|
||||||
|
|
||||||
|
For Count := 1 To Length(Str) Do
|
||||||
|
WriteChar(Str[Count]);
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.RawWriteStr (Str: String);
|
||||||
|
Begin
|
||||||
|
fpWrite (ConOut, Str[1], Length(Str));
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.SaveRawSettings (Var TIo: TermIos);
|
||||||
|
Begin
|
||||||
|
With TIo Do Begin
|
||||||
|
TermInRaw :=
|
||||||
|
((c_iflag and (IGNBRK or BRKINT or PARMRK or ISTRIP or
|
||||||
|
INLCR or IGNCR or ICRNL or IXON)) = 0) and
|
||||||
|
((c_lflag and (ECHO or ECHONL or ICANON or ISIG or IEXTEN)) = 0);
|
||||||
|
TermOutRaw :=
|
||||||
|
((c_oflag and OPOST) = 0) and
|
||||||
|
((c_cflag and (CSIZE or PARENB)) = 0) and
|
||||||
|
((c_cflag and CS8) <> 0);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.RestoreRawSettings (TIo: TermIos);
|
||||||
|
Begin
|
||||||
|
With TIo Do Begin
|
||||||
|
If TermInRaw Then Begin
|
||||||
|
c_iflag := c_iflag and (not (IGNBRK or BRKINT or PARMRK or ISTRIP or
|
||||||
|
INLCR or IGNCR or ICRNL or IXON));
|
||||||
|
c_lflag := c_lflag and
|
||||||
|
(not (ECHO or ECHONL or ICANON or ISIG or IEXTEN));
|
||||||
|
End;
|
||||||
|
|
||||||
|
If TermOutRaw Then Begin
|
||||||
|
c_oflag := c_oflag and not(OPOST);
|
||||||
|
c_cflag := c_cflag and not(CSIZE or PARENB) or CS8;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.SetRawMode (SetOn: Boolean);
|
||||||
|
Var
|
||||||
|
Tio : TermIos;
|
||||||
|
Begin
|
||||||
|
If SetOn Then Begin
|
||||||
|
TCGetAttr(1, Tio);
|
||||||
|
SaveRawSettings(Tio);
|
||||||
|
TermInfo := Tio;
|
||||||
|
CFMakeRaw(Tio);
|
||||||
|
End Else Begin
|
||||||
|
RestoreRawSettings(TermInfo);
|
||||||
|
Tio := TermInfo;
|
||||||
|
End;
|
||||||
|
|
||||||
|
TCSetAttr(1, TCSANOW, Tio);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TOutputDarwin.ReadCharXY (X, Y: Byte) : Char;
|
||||||
|
Begin
|
||||||
|
ReadCharXY := Buffer[Y][X].UnicodeChar;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TOutputDarwin.ReadAttrXY (X, Y: Byte) : Byte;
|
||||||
|
Begin
|
||||||
|
ReadAttrXY := Buffer[Y][X].Attributes;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.WriteXY (X, Y, A: Byte; Text: String);
|
||||||
|
Var
|
||||||
|
OldAttr : Byte;
|
||||||
|
OldX : Byte;
|
||||||
|
OldY : Byte;
|
||||||
|
Count : Byte;
|
||||||
|
Begin
|
||||||
|
If X > 80 Then Exit;
|
||||||
|
|
||||||
|
OldAttr := FTextAttr;
|
||||||
|
OldX := FCursorX;
|
||||||
|
OldY := FCursorY;
|
||||||
|
|
||||||
|
CursorXY (X, Y);
|
||||||
|
SetTextAttr (A);
|
||||||
|
|
||||||
|
For Count := 1 to Length(Text) Do
|
||||||
|
If FCursorX <= 80 Then Begin
|
||||||
|
Buffer[FCursorY][FCursorX].Attributes := FTextAttr;
|
||||||
|
Buffer[FCursorY][FCursorX].UnicodeChar := Text[Count];
|
||||||
|
|
||||||
|
Inc (FCursorX);
|
||||||
|
|
||||||
|
BufAddStr(Text[Count]);
|
||||||
|
End Else
|
||||||
|
Break;
|
||||||
|
|
||||||
|
SetTextAttr(OldAttr);
|
||||||
|
CursorXY (OldX, OldY);
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.WriteXYPipe (X, Y, Attr, Pad: Integer; Text: String);
|
||||||
|
|
||||||
|
Procedure AddChar (Ch: Char);
|
||||||
|
Begin
|
||||||
|
If CursorX > 80 Then Exit;
|
||||||
|
|
||||||
|
Buffer[CursorY][CursorX].Attributes := FTextAttr;
|
||||||
|
Buffer[CursorY][CursorX].UnicodeChar := Ch;
|
||||||
|
|
||||||
|
BufAddStr(Ch);
|
||||||
|
|
||||||
|
Inc (FCursorX);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Var
|
||||||
|
Count : Byte;
|
||||||
|
Code : String[2];
|
||||||
|
CodeNum : Byte;
|
||||||
|
OldAttr : Byte;
|
||||||
|
OldX : Byte;
|
||||||
|
OldY : Byte;
|
||||||
|
Begin
|
||||||
|
OldAttr := FTextAttr;
|
||||||
|
OldX := FCursorX;
|
||||||
|
OldY := FCursorY;
|
||||||
|
|
||||||
|
CursorXY (X, Y);
|
||||||
|
SetTextAttr (Attr);
|
||||||
|
|
||||||
|
Count := 1;
|
||||||
|
|
||||||
|
While Count <= Length(Text) Do Begin
|
||||||
|
If Text[Count] = '|' Then Begin
|
||||||
|
Code := Copy(Text, Count + 1, 2);
|
||||||
|
CodeNum := strS2I(Code);
|
||||||
|
|
||||||
|
If (Code = '00') or ((CodeNum > 0) and (CodeNum < 24) and (Code[1] <> '&') and (Code[1] <> '$')) Then Begin
|
||||||
|
Inc (Count, 2);
|
||||||
|
If CodeNum in [00..15] Then
|
||||||
|
SetTextAttr (CodeNum + ((FTextAttr SHR 4) AND 7) * 16)
|
||||||
|
Else
|
||||||
|
SetTextAttr ((FTextAttr AND $F) + (CodeNum - 16) * 16);
|
||||||
|
End Else Begin
|
||||||
|
AddChar(Text[Count]);
|
||||||
|
Dec (Pad);
|
||||||
|
End;
|
||||||
|
End Else Begin
|
||||||
|
AddChar(Text[Count]);
|
||||||
|
Dec (Pad);
|
||||||
|
End;
|
||||||
|
|
||||||
|
If Pad = 0 Then Break;
|
||||||
|
|
||||||
|
Inc (Count);
|
||||||
|
End;
|
||||||
|
|
||||||
|
While Pad > 0 Do Begin
|
||||||
|
AddChar(' ');
|
||||||
|
Dec(Pad);
|
||||||
|
End;
|
||||||
|
|
||||||
|
SetTextAttr(OldAttr);
|
||||||
|
CursorXY (OldX, OldY);
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.GetScreenImage (X1, Y1, X2, Y2: Byte; Var Image: TConsoleImageRec);
|
||||||
|
Var
|
||||||
|
Count : Byte;
|
||||||
|
Begin
|
||||||
|
// If X2 > 80 Then X2 := 80;
|
||||||
|
// If Y2 > FWinBot Then Y2 := FWinBot;
|
||||||
|
|
||||||
|
FillChar(Image, SizeOf(Image), #0);
|
||||||
|
|
||||||
|
Image.Data := Buffer;
|
||||||
|
// For Count := Y1 to Y2 Do Begin
|
||||||
|
// Image.Data[Count] := Buffer[Count];
|
||||||
|
|
||||||
|
Image.CursorX := FCursorX;
|
||||||
|
Image.CursorY := FCursorY;
|
||||||
|
Image.CursorA := FTextAttr;
|
||||||
|
Image.X1 := X1;
|
||||||
|
Image.X2 := X2;
|
||||||
|
Image.Y1 := Y1;
|
||||||
|
Image.Y2 := Y2;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.PutScreenImage (Image: TConsoleImageRec);
|
||||||
|
Var
|
||||||
|
CountX : Byte;
|
||||||
|
CountY : Byte;
|
||||||
|
Begin
|
||||||
|
For CountY := Image.Y1 to Image.Y2 Do Begin
|
||||||
|
CursorXY (Image.X1, CountY);
|
||||||
|
|
||||||
|
For CountX := Image.X1 to Image.X2 Do Begin
|
||||||
|
SetTextAttr(Image.Data[CountY][CountX].Attributes);
|
||||||
|
If Image.Data[CountY][CountX].UnicodeChar = #0 Then BufAddStr(' ') Else BufAddStr(Image.Data[CountY][CountX].UnicodeChar);
|
||||||
|
// the above is a placeholder until we properly fill the buffers. #0 does not work in ITERM2
|
||||||
|
Buffer[CountY][CountX] := Image.Data[CountY][CountX];
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
SetTextAttr (Image.CursorA);
|
||||||
|
CursorXY (Image.CursorX, Image.CursorY);
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
End;
|
||||||
|
|
||||||
|
(*
|
||||||
|
Procedure TOutputDarwin.GetScreenImage (X1, Y1, X2, Y2: Byte; Var Image: TConsoleImageRec);
|
||||||
|
Var
|
||||||
|
Count : Byte;
|
||||||
|
Line : Byte;
|
||||||
|
Temp : TConsoleLineRec;
|
||||||
|
Begin
|
||||||
|
Line := 1;
|
||||||
|
|
||||||
|
If X2 > 80 Then X2 := 80;
|
||||||
|
If Y2 > FWinBot Then Y2 := FWinBot;
|
||||||
|
|
||||||
|
FillChar(Image, SizeOf(Image), #0);
|
||||||
|
|
||||||
|
For Count := Y1 to Y2 Do Begin
|
||||||
|
Move (Buffer[Count][X1], Image.Data[Line][1], (X2 - X1 + 1) * SizeOf(TCharInfo));
|
||||||
|
Inc (Line);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Image.CursorX := FCursorX;
|
||||||
|
Image.CursorY := FCursorY;
|
||||||
|
Image.CursorA := FTextAttr;
|
||||||
|
Image.X1 := X1;
|
||||||
|
Image.X2 := X2;
|
||||||
|
Image.Y1 := Y1;
|
||||||
|
Image.Y2 := Y2;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.PutScreenImage (Var Image: TConsoleImageRec);
|
||||||
|
Var
|
||||||
|
CountX : Byte;
|
||||||
|
CountY : Byte;
|
||||||
|
Begin
|
||||||
|
For CountY := 1 to (Image.Y2 - Image.Y1 + 1) Do Begin
|
||||||
|
CursorXY (Image.X1, CountY + Image.Y1 - 1);
|
||||||
|
|
||||||
|
Move (Image.Data[CountY][1], Buffer[CountY + Image.Y1 - 1][Image.X1], (Image.X2 - Image.X1 + 1) * SizeOf(TCharInfo));
|
||||||
|
|
||||||
|
For CountX := 1 to (Image.X2 - Image.X1 + 1) Do Begin
|
||||||
|
SetTextAttr(Image.Data[CountY][CountX].Attributes);
|
||||||
|
BufAddStr(Image.Data[CountY][CountX].UnicodeChar);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
SetTextAttr (Image.CursorA);
|
||||||
|
CursorXY (Image.CursorX, Image.CursorY);
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
End;
|
||||||
|
*)
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.LoadScreenImage (Var DataPtr; Len, Width, X, Y: Integer);
|
||||||
|
Var
|
||||||
|
Image : TConsoleImageRec;
|
||||||
|
Data : Array[1..8000] of Byte Absolute DataPtr;
|
||||||
|
PosX : Word;
|
||||||
|
PosY : Byte;
|
||||||
|
Attrib : Byte;
|
||||||
|
Count : Word;
|
||||||
|
A : Byte;
|
||||||
|
B : Byte;
|
||||||
|
C : Byte;
|
||||||
|
Begin
|
||||||
|
PosX := 1;
|
||||||
|
PosY := 1;
|
||||||
|
Attrib := 7;
|
||||||
|
Count := 1;
|
||||||
|
|
||||||
|
FillChar(Image.Data, SizeOf(Image.Data), #0);
|
||||||
|
|
||||||
|
While (Count <= Len) Do begin
|
||||||
|
Case Data[Count] of
|
||||||
|
00..
|
||||||
|
15 : Attrib := Data[Count] + ((Attrib SHR 4) and 7) * 16;
|
||||||
|
16..
|
||||||
|
23 : Attrib := (Attrib And $F) + (Data[Count] - 16) * 16;
|
||||||
|
24 : Begin
|
||||||
|
Inc (PosY);
|
||||||
|
PosX := 1;
|
||||||
|
End;
|
||||||
|
25 : Begin
|
||||||
|
Inc (Count);
|
||||||
|
|
||||||
|
For A := 0 to Data[Count] Do Begin
|
||||||
|
Image.Data[PosY][PosX].UnicodeChar := ' ';
|
||||||
|
Image.Data[PosY][PosX].Attributes := Attrib;
|
||||||
|
|
||||||
|
Inc (PosX);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
26 : Begin
|
||||||
|
A := Data[Count + 1];
|
||||||
|
B := Data[Count + 2];
|
||||||
|
|
||||||
|
Inc (Count, 2);
|
||||||
|
|
||||||
|
For C := 0 to A Do Begin
|
||||||
|
Image.Data[PosY][PosX].UnicodeChar := Char(B);
|
||||||
|
Image.Data[PosY][PosX].Attributes := Attrib;
|
||||||
|
|
||||||
|
Inc (PosX);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
27..
|
||||||
|
31 : ;
|
||||||
|
Else
|
||||||
|
Image.Data[PosY][PosX].UnicodeChar := Char(Data[Count]);
|
||||||
|
Image.Data[PosY][PosX].Attributes := Attrib;
|
||||||
|
|
||||||
|
Inc (PosX);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Inc(Count);
|
||||||
|
End;
|
||||||
|
|
||||||
|
If PosY > ScreenSize Then PosY := ScreenSize;
|
||||||
|
|
||||||
|
Image.CursorX := PosX;
|
||||||
|
Image.CursorY := PosY;
|
||||||
|
Image.CursorA := Attrib;
|
||||||
|
Image.X1 := X;
|
||||||
|
Image.X2 := Width;
|
||||||
|
Image.Y1 := Y;
|
||||||
|
Image.Y2 := PosY;
|
||||||
|
|
||||||
|
PutScreenImage(Image);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputDarwin.ShowBuffer;
|
||||||
|
Begin
|
||||||
|
End;
|
||||||
|
|
||||||
|
End.
|
|
@ -0,0 +1,719 @@
|
||||||
|
{$I M_OPS.PAS}
|
||||||
|
|
||||||
|
Unit m_Output_Linux;
|
||||||
|
|
||||||
|
Interface
|
||||||
|
|
||||||
|
Uses
|
||||||
|
TermIO,
|
||||||
|
BaseUnix,
|
||||||
|
m_Types;
|
||||||
|
|
||||||
|
Const
|
||||||
|
ConIn = 0;
|
||||||
|
ConOut = 1;
|
||||||
|
ConBufSize = 4096;
|
||||||
|
|
||||||
|
Type
|
||||||
|
TOutputLinux = Class
|
||||||
|
Private
|
||||||
|
TermInfo : TermIos;
|
||||||
|
TermInRaw : Boolean;
|
||||||
|
TermOutRaw : Boolean;
|
||||||
|
OutBuffer : Array[1..ConBufSize] of Char;
|
||||||
|
FTextAttr : Byte;
|
||||||
|
FWinTop : Byte;
|
||||||
|
FCursorX : Byte;
|
||||||
|
FCursorY : Byte;
|
||||||
|
|
||||||
|
Procedure SetTextAttr (Attr: Byte);
|
||||||
|
Public
|
||||||
|
OutBufPos : Word;
|
||||||
|
ScreenSize : Byte;
|
||||||
|
Buffer : TConsoleScreenRec;
|
||||||
|
Active : Boolean;
|
||||||
|
SavedTerm : TermIOS;
|
||||||
|
FWinBot : Byte;
|
||||||
|
|
||||||
|
Function AttrToAnsi (Attr: Byte) : String;
|
||||||
|
Procedure BufFlush;
|
||||||
|
Procedure BufAddStr (Str: String);
|
||||||
|
Procedure SaveRawSettings (Var TIo: TermIos);
|
||||||
|
Procedure RestoreRawSettings (TIo: TermIos);
|
||||||
|
Procedure SetRawMode (SetOn: Boolean);
|
||||||
|
Procedure WriteXY (X, Y, A: Byte; Text: String);
|
||||||
|
Procedure WriteXYPipe (X, Y, Attr, Pad: Integer; Text: String);
|
||||||
|
Procedure GetScreenImage (X1, Y1, X2, Y2: Byte; Var Image: TConsoleImageRec);
|
||||||
|
Procedure PutScreenImage (Image: TConsoleImageRec);
|
||||||
|
Procedure LoadScreenImage (Var DataPtr; Len, Width, X, Y: Integer);
|
||||||
|
|
||||||
|
Constructor Create (A: Boolean);
|
||||||
|
Destructor Destroy; Override;
|
||||||
|
Procedure ClearScreen; Virtual;
|
||||||
|
Procedure ScrollWindow; Virtual;
|
||||||
|
Procedure ClearEOL;
|
||||||
|
Procedure CursorXY (X, Y: Byte);
|
||||||
|
Procedure SetWindow (X1, Y1, X2, Y2: Byte; Home: Boolean);
|
||||||
|
Procedure SetScreenSize (Mode: Byte);
|
||||||
|
Procedure SetWindowTitle (Str: String);
|
||||||
|
Procedure WriteChar (Ch: Char);
|
||||||
|
Procedure WriteLine (Str: String);
|
||||||
|
Procedure WriteLineRec (YPos: Integer; Line: TConsoleLineRec);
|
||||||
|
Procedure WriteStr (Str: String);
|
||||||
|
Procedure RawWriteStr (Str: String);
|
||||||
|
Function ReadCharXY (X, Y: Byte) : Char;
|
||||||
|
Function ReadAttrXY (X, Y: Byte) : Byte;
|
||||||
|
Procedure ShowBuffer;
|
||||||
|
|
||||||
|
Property TextAttr : Byte Read FTextAttr Write SetTextAttr;
|
||||||
|
Property CursorX : Byte Read FCursorX;
|
||||||
|
Property CursorY : Byte Read FCursorY;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Implementation
|
||||||
|
|
||||||
|
Uses
|
||||||
|
m_Strings;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.WriteLineRec (YPos: Integer; Line: TConsoleLineRec);
|
||||||
|
Var
|
||||||
|
Count : LongInt;
|
||||||
|
Begin
|
||||||
|
CursorXY(1, YPos);
|
||||||
|
|
||||||
|
For Count := 1 to 80 Do
|
||||||
|
BufAddStr(AttrToAnsi(Line[Count].Attributes) + Line[Count].UnicodeChar);
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
|
||||||
|
// For Count := 1 to 80 Do Begin
|
||||||
|
// FTextAttr := Line[Count].Attributes;
|
||||||
|
// WriteChar(Line[Count].UnicodeChar);
|
||||||
|
// End;
|
||||||
|
|
||||||
|
Buffer[YPos] := Line;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Constructor TOutputLinux.Create (A: Boolean);
|
||||||
|
Begin
|
||||||
|
Inherited Create;
|
||||||
|
|
||||||
|
// SaveRawSettings(SavedTerm);
|
||||||
|
|
||||||
|
SetRawMode(True);
|
||||||
|
|
||||||
|
Active := A;
|
||||||
|
OutBufPos := 0;
|
||||||
|
FTextAttr := 7;
|
||||||
|
FWinTop := 1;
|
||||||
|
FWinBot := 25;
|
||||||
|
ScreenSize := 25;
|
||||||
|
|
||||||
|
RawWriteStr (#27 + '(U' + #27 + '[0m');
|
||||||
|
|
||||||
|
ClearScreen;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Destructor TOutputLinux.Destroy;
|
||||||
|
Begin
|
||||||
|
WriteLine('');
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
|
||||||
|
// RestoreRawSettings(SavedTerm);
|
||||||
|
SetRawMode(False);
|
||||||
|
|
||||||
|
Inherited Destroy;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Const
|
||||||
|
AnsiTable : String[8] = '04261537';
|
||||||
|
|
||||||
|
Function TOutputLinux.AttrToAnsi (Attr: Byte) : String;
|
||||||
|
Var
|
||||||
|
Str : String[16];
|
||||||
|
OldFG : LongInt;
|
||||||
|
OldBG : LongInt;
|
||||||
|
FG : LongInt;
|
||||||
|
BG : LongInt;
|
||||||
|
|
||||||
|
Procedure AddSep (Ch: Char);
|
||||||
|
Begin
|
||||||
|
If Length(Str) > 0 Then
|
||||||
|
Str := Str + ';';
|
||||||
|
Str := Str + Ch;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Begin
|
||||||
|
If Attr = FTextAttr Then Begin
|
||||||
|
AttrToAnsi := '';
|
||||||
|
Exit;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Str := '';
|
||||||
|
FG := Attr and $F;
|
||||||
|
BG := Attr shr 4;
|
||||||
|
OldFG := FTextAttr and $F;
|
||||||
|
OldBG := FTextAttr shr 4;
|
||||||
|
|
||||||
|
If (OldFG <> 7) or (FG = 7) or ((OldFG > 7) and (FG < 8)) or ((OldBG > 7) and (BG < 8)) Then Begin
|
||||||
|
Str := '0';
|
||||||
|
OldFG := 7;
|
||||||
|
OldBG := 0;
|
||||||
|
End;
|
||||||
|
|
||||||
|
If (FG > 7) and (OldFG < 8) Then Begin
|
||||||
|
AddSep('1');
|
||||||
|
OldFG := OldFG or 8;
|
||||||
|
End;
|
||||||
|
|
||||||
|
If (BG and 8) <> (OldBG and 8) Then Begin
|
||||||
|
AddSep('5');
|
||||||
|
OldBG := OldBG or 8;
|
||||||
|
End;
|
||||||
|
|
||||||
|
If (FG <> OldFG) Then Begin
|
||||||
|
AddSep('3');
|
||||||
|
Str := Str + AnsiTable[(FG and 7) + 1];
|
||||||
|
End;
|
||||||
|
|
||||||
|
If (BG <> OldBG) Then Begin
|
||||||
|
AddSep('4');
|
||||||
|
Str := Str + AnsiTable[(BG and 7) + 1];
|
||||||
|
End;
|
||||||
|
|
||||||
|
FTextAttr := FG + BG * 16;
|
||||||
|
AttrToAnsi := #27 + '[' + Str + 'm';
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.BufFlush;
|
||||||
|
Begin
|
||||||
|
If OutBufPos > 0 Then Begin
|
||||||
|
If Active Then fpWrite (ConOut, OutBuffer[1], OutBufPos);
|
||||||
|
OutBufPos := 0;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.BufAddStr (Str: String);
|
||||||
|
Var
|
||||||
|
Count : LongInt;
|
||||||
|
Begin
|
||||||
|
For Count := 1 to Length(Str) Do Begin
|
||||||
|
Inc (OutBufPos);
|
||||||
|
OutBuffer[OutBufPos] := Str[Count];
|
||||||
|
If OutBufPos = ConBufSize Then BufFlush;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.SetTextAttr (Attr: Byte);
|
||||||
|
Begin
|
||||||
|
If Attr = FTextAttr Then Exit;
|
||||||
|
|
||||||
|
BufAddStr(AttrToAnsi(Attr));
|
||||||
|
|
||||||
|
FTextAttr := Attr;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.CursorXY (X, Y: Byte);
|
||||||
|
Begin
|
||||||
|
If (Y < 1) Then Y := 1 Else
|
||||||
|
// If (Y > FWinBot) Then Y := FWinBot; {changed 109a4}
|
||||||
|
If (Y > ScreenSize) Then Y := ScreenSize;
|
||||||
|
If (X < 1) Then X := 1 Else
|
||||||
|
If (X > 80) Then X := 80;
|
||||||
|
|
||||||
|
BufAddStr(#27 + '[' + strI2S(Y) + ';' + strI2S(X) + 'H');
|
||||||
|
BufFlush;
|
||||||
|
|
||||||
|
FCursorX := X;
|
||||||
|
FCursorY := Y;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.ClearScreen;
|
||||||
|
Var
|
||||||
|
Fill : TCharInfo;
|
||||||
|
Count : Byte;
|
||||||
|
Begin
|
||||||
|
BufFlush;
|
||||||
|
|
||||||
|
Fill.Attributes := FTextAttr;
|
||||||
|
Fill.UnicodeChar := ' ';
|
||||||
|
|
||||||
|
If (FWinTop = 1) and (FWinBot = {25}ScreenSize) Then Begin
|
||||||
|
BufAddStr(#27 + '[2J');
|
||||||
|
FillWord (Buffer, SizeOf(Buffer) DIV 2, Word(Fill));
|
||||||
|
End Else Begin
|
||||||
|
For Count := FWinTop to FWinBot Do Begin
|
||||||
|
BufAddStr (#27 + '[' + strI2S(Count) + ';1H' + #27 + '[K');
|
||||||
|
FillWord (Buffer[Count][1], SizeOf(TConsoleLineRec) DIV 2, Word(Fill));
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
CursorXY (1, FWinTop);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.SetScreenSize (Mode: Byte);
|
||||||
|
Begin
|
||||||
|
FWinBot := Mode;
|
||||||
|
ScreenSize := Mode;
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
RawWriteStr(#27 + '[8;' + strI2S(Mode) + ';80t');
|
||||||
|
SetWindow(1, 1, 80, Mode, False);
|
||||||
|
//need to figure this out.
|
||||||
|
|
||||||
|
//esc[8;h;w
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.SetWindow (X1, Y1, X2, Y2: Byte; Home: Boolean);
|
||||||
|
Begin
|
||||||
|
// X1 and X2 are ignored in Linux and are only here for compatibility
|
||||||
|
// reasons.
|
||||||
|
|
||||||
|
FWinTop := Y1;
|
||||||
|
FWinBot := Y2;
|
||||||
|
|
||||||
|
BufAddStr (#27 + '[' + strI2S(Y1) + ';' + strI2S(Y2) + 'r');
|
||||||
|
BufFlush;
|
||||||
|
|
||||||
|
If Home Then CursorXY (1, Y1);
|
||||||
|
|
||||||
|
If (FCursorY > Y2) Then CursorXY (CursorX, Y2);
|
||||||
|
|
||||||
|
// If Home or (FCursorY < Y1) or (FCursorY > Y2) Then CursorXY(1, Y1);
|
||||||
|
{ this home thing is shady. compare it to win. going from 50 to 25 }
|
||||||
|
{ will screw up the buffers - this has to be more elegant. }
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.SetWindowTitle (Str: String);
|
||||||
|
Begin
|
||||||
|
RawWriteStr (#27 + ']0;' + Str + #07);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.ClearEOL;
|
||||||
|
Var
|
||||||
|
Fill : TCharInfo;
|
||||||
|
Begin
|
||||||
|
BufAddStr(#27 + '[K');
|
||||||
|
|
||||||
|
Fill.Attributes := 7;
|
||||||
|
Fill.UnicodeChar := ' ';
|
||||||
|
|
||||||
|
FillWord (Buffer[CursorY][CursorX], (80 - CursorX) * 2, Word(Fill));
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.ScrollWindow;
|
||||||
|
Begin
|
||||||
|
Move (Buffer[2][1], Buffer[1][1], SizeOf(TConsoleLineRec) * (FWinBot - 1));
|
||||||
|
FillChar(Buffer[FWinBot][1], SizeOf(TConsoleLineRec), 0);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.WriteChar (Ch: Char);
|
||||||
|
Var
|
||||||
|
A : Byte;
|
||||||
|
Begin
|
||||||
|
If Ch <> #10 Then BufAddStr(Ch);
|
||||||
|
|
||||||
|
Case Ch of
|
||||||
|
#08 : If FCursorX > 1 Then
|
||||||
|
Dec(FCursorX);
|
||||||
|
#10 : Begin
|
||||||
|
If FCursorY < FWinBot Then Begin
|
||||||
|
BufAddStr(Ch);
|
||||||
|
Inc (FCursorY)
|
||||||
|
End Else Begin
|
||||||
|
A := FTextAttr;
|
||||||
|
SetTextAttr(7);
|
||||||
|
BufAddStr(Ch);
|
||||||
|
ScrollWindow;
|
||||||
|
SetTextAttr(A);
|
||||||
|
End;
|
||||||
|
|
||||||
|
FCursorX := 1;
|
||||||
|
CursorXY(FCursorX, FCursorY);
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
End;
|
||||||
|
#13 : FCursorX := 1;
|
||||||
|
Else
|
||||||
|
Buffer[FCursorY][FCursorX].Attributes := FTextAttr;
|
||||||
|
Buffer[FCursorY][FCursorX].UnicodeChar := Ch;
|
||||||
|
|
||||||
|
If FCursorX < 80 Then
|
||||||
|
Inc (FCursorX)
|
||||||
|
Else Begin
|
||||||
|
FCursorX := 1;
|
||||||
|
|
||||||
|
If FCursorY < FWinBot Then
|
||||||
|
Inc (FCursorY)
|
||||||
|
Else
|
||||||
|
ScrollWindow;
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.WriteStr (Str: String);
|
||||||
|
Var
|
||||||
|
Count : Byte;
|
||||||
|
Begin
|
||||||
|
For Count := 1 to Length(Str) Do
|
||||||
|
WriteChar(Str[Count]);
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.WriteLine (Str: String);
|
||||||
|
Var
|
||||||
|
Count : Byte;
|
||||||
|
Begin
|
||||||
|
Str := Str + #13#10;
|
||||||
|
|
||||||
|
For Count := 1 To Length(Str) Do
|
||||||
|
WriteChar(Str[Count]);
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.RawWriteStr (Str: String);
|
||||||
|
Begin
|
||||||
|
fpWrite (ConOut, Str[1], Length(Str));
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.SaveRawSettings (Var TIo: TermIos);
|
||||||
|
Begin
|
||||||
|
With TIo Do Begin
|
||||||
|
TermInRaw :=
|
||||||
|
((c_iflag and (IGNBRK or BRKINT or PARMRK or ISTRIP or
|
||||||
|
INLCR or IGNCR or ICRNL or IXON)) = 0) and
|
||||||
|
((c_lflag and (ECHO or ECHONL or ICANON or ISIG or IEXTEN)) = 0);
|
||||||
|
TermOutRaw :=
|
||||||
|
((c_oflag and OPOST) = 0) and
|
||||||
|
((c_cflag and (CSIZE or PARENB)) = 0) and
|
||||||
|
((c_cflag and CS8) <> 0);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.RestoreRawSettings (TIo: TermIos);
|
||||||
|
Begin
|
||||||
|
With TIo Do Begin
|
||||||
|
If TermInRaw Then Begin
|
||||||
|
c_iflag := c_iflag and (not (IGNBRK or BRKINT or PARMRK or ISTRIP or
|
||||||
|
INLCR or IGNCR or ICRNL or IXON));
|
||||||
|
c_lflag := c_lflag and
|
||||||
|
(not (ECHO or ECHONL or ICANON or ISIG or IEXTEN));
|
||||||
|
End;
|
||||||
|
|
||||||
|
If TermOutRaw Then Begin
|
||||||
|
c_oflag := c_oflag and not(OPOST);
|
||||||
|
c_cflag := c_cflag and not(CSIZE or PARENB) or CS8;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.SetRawMode (SetOn: Boolean);
|
||||||
|
Var
|
||||||
|
Tio : TermIos;
|
||||||
|
Begin
|
||||||
|
If SetOn Then Begin
|
||||||
|
TCGetAttr(1, Tio);
|
||||||
|
SaveRawSettings(Tio);
|
||||||
|
TermInfo := Tio;
|
||||||
|
CFMakeRaw(Tio);
|
||||||
|
End Else Begin
|
||||||
|
RestoreRawSettings(TermInfo);
|
||||||
|
Tio := TermInfo;
|
||||||
|
End;
|
||||||
|
|
||||||
|
TCSetAttr(1, TCSANOW, Tio);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TOutputLinux.ReadCharXY (X, Y: Byte) : Char;
|
||||||
|
Begin
|
||||||
|
ReadCharXY := Buffer[Y][X].UnicodeChar;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TOutputLinux.ReadAttrXY (X, Y: Byte) : Byte;
|
||||||
|
Begin
|
||||||
|
ReadAttrXY := Buffer[Y][X].Attributes;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.WriteXY (X, Y, A: Byte; Text: String);
|
||||||
|
Var
|
||||||
|
OldAttr : Byte;
|
||||||
|
OldX : Byte;
|
||||||
|
OldY : Byte;
|
||||||
|
Count : Byte;
|
||||||
|
Begin
|
||||||
|
If X > 80 Then Exit;
|
||||||
|
|
||||||
|
OldAttr := FTextAttr;
|
||||||
|
OldX := FCursorX;
|
||||||
|
OldY := FCursorY;
|
||||||
|
|
||||||
|
CursorXY (X, Y);
|
||||||
|
SetTextAttr (A);
|
||||||
|
|
||||||
|
For Count := 1 to Length(Text) Do
|
||||||
|
If FCursorX <= 80 Then Begin
|
||||||
|
Buffer[FCursorY][FCursorX].Attributes := FTextAttr;
|
||||||
|
Buffer[FCursorY][FCursorX].UnicodeChar := Text[Count];
|
||||||
|
|
||||||
|
Inc (FCursorX);
|
||||||
|
|
||||||
|
BufAddStr(Text[Count]);
|
||||||
|
End Else
|
||||||
|
Break;
|
||||||
|
|
||||||
|
SetTextAttr(OldAttr);
|
||||||
|
CursorXY (OldX, OldY);
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.WriteXYPipe (X, Y, Attr, Pad: Integer; Text: String);
|
||||||
|
|
||||||
|
Procedure AddChar (Ch: Char);
|
||||||
|
Begin
|
||||||
|
If CursorX > 80 Then Exit;
|
||||||
|
|
||||||
|
Buffer[CursorY][CursorX].Attributes := FTextAttr;
|
||||||
|
Buffer[CursorY][CursorX].UnicodeChar := Ch;
|
||||||
|
|
||||||
|
BufAddStr(Ch);
|
||||||
|
|
||||||
|
Inc (FCursorX);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Var
|
||||||
|
Count : Byte;
|
||||||
|
Code : String[2];
|
||||||
|
CodeNum : Byte;
|
||||||
|
OldAttr : Byte;
|
||||||
|
OldX : Byte;
|
||||||
|
OldY : Byte;
|
||||||
|
Begin
|
||||||
|
OldAttr := FTextAttr;
|
||||||
|
OldX := FCursorX;
|
||||||
|
OldY := FCursorY;
|
||||||
|
|
||||||
|
CursorXY (X, Y);
|
||||||
|
SetTextAttr (Attr);
|
||||||
|
|
||||||
|
Count := 1;
|
||||||
|
|
||||||
|
While Count <= Length(Text) Do Begin
|
||||||
|
If Text[Count] = '|' Then Begin
|
||||||
|
Code := Copy(Text, Count + 1, 2);
|
||||||
|
CodeNum := strS2I(Code);
|
||||||
|
|
||||||
|
If (Code = '00') or ((CodeNum > 0) and (CodeNum < 24) and (Code[1] <> '&') and (Code[1] <> '$')) Then Begin
|
||||||
|
Inc (Count, 2);
|
||||||
|
If CodeNum in [00..15] Then
|
||||||
|
SetTextAttr (CodeNum + ((FTextAttr SHR 4) AND 7) * 16)
|
||||||
|
Else
|
||||||
|
SetTextAttr ((FTextAttr AND $F) + (CodeNum - 16) * 16);
|
||||||
|
End Else Begin
|
||||||
|
AddChar(Text[Count]);
|
||||||
|
Dec (Pad);
|
||||||
|
End;
|
||||||
|
End Else Begin
|
||||||
|
AddChar(Text[Count]);
|
||||||
|
Dec (Pad);
|
||||||
|
End;
|
||||||
|
|
||||||
|
If Pad = 0 Then Break;
|
||||||
|
|
||||||
|
Inc (Count);
|
||||||
|
End;
|
||||||
|
|
||||||
|
While Pad > 0 Do Begin
|
||||||
|
AddChar(' ');
|
||||||
|
Dec(Pad);
|
||||||
|
End;
|
||||||
|
|
||||||
|
SetTextAttr(OldAttr);
|
||||||
|
CursorXY (OldX, OldY);
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.GetScreenImage (X1, Y1, X2, Y2: Byte; Var Image: TConsoleImageRec);
|
||||||
|
//Var
|
||||||
|
// Count : Byte;
|
||||||
|
Begin
|
||||||
|
// If X2 > 80 Then X2 := 80;
|
||||||
|
// If Y2 > FWinBot Then Y2 := FWinBot;
|
||||||
|
|
||||||
|
FillChar(Image, SizeOf(Image), #0);
|
||||||
|
|
||||||
|
Image.Data := Buffer;
|
||||||
|
// For Count := Y1 to Y2 Do Begin
|
||||||
|
// Image.Data[Count] := Buffer[Count];
|
||||||
|
|
||||||
|
Image.CursorX := FCursorX;
|
||||||
|
Image.CursorY := FCursorY;
|
||||||
|
Image.CursorA := FTextAttr;
|
||||||
|
Image.X1 := X1;
|
||||||
|
Image.X2 := X2;
|
||||||
|
Image.Y1 := Y1;
|
||||||
|
Image.Y2 := Y2;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.PutScreenImage (Image: TConsoleImageRec);
|
||||||
|
Var
|
||||||
|
CountX : Byte;
|
||||||
|
CountY : Byte;
|
||||||
|
Begin
|
||||||
|
For CountY := Image.Y1 to Image.Y2 Do Begin
|
||||||
|
CursorXY (Image.X1, CountY);
|
||||||
|
|
||||||
|
// Move (Image.Data[CountY][Image.X1], Buffer[CountY + Image.Y1 - 1][Image.X1], (Image.X2 - Image.X1 + 1) * SizeOf(TCharInfo));
|
||||||
|
|
||||||
|
For CountX := Image.X1 to Image.X2 Do Begin
|
||||||
|
SetTextAttr(Image.Data[CountY][CountX].Attributes);
|
||||||
|
BufAddStr(Image.Data[CountY][CountX].UnicodeChar);
|
||||||
|
Buffer[CountY][CountX] := Image.Data[CountY][CountX];
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
SetTextAttr (Image.CursorA);
|
||||||
|
CursorXY (Image.CursorX, Image.CursorY);
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
End;
|
||||||
|
|
||||||
|
(*
|
||||||
|
Procedure TOutputLinux.GetScreenImage (X1, Y1, X2, Y2: Byte; Var Image: TConsoleImageRec);
|
||||||
|
Var
|
||||||
|
Count : Byte;
|
||||||
|
Line : Byte;
|
||||||
|
Temp : TConsoleLineRec;
|
||||||
|
Begin
|
||||||
|
Line := 1;
|
||||||
|
|
||||||
|
If X2 > 80 Then X2 := 80;
|
||||||
|
If Y2 > FWinBot Then Y2 := FWinBot;
|
||||||
|
|
||||||
|
FillChar(Image, SizeOf(Image), #0);
|
||||||
|
|
||||||
|
For Count := Y1 to Y2 Do Begin
|
||||||
|
Move (Buffer[Count][X1], Image.Data[Line][1], (X2 - X1 + 1) * SizeOf(TCharInfo));
|
||||||
|
Inc (Line);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Image.CursorX := FCursorX;
|
||||||
|
Image.CursorY := FCursorY;
|
||||||
|
Image.CursorA := FTextAttr;
|
||||||
|
Image.X1 := X1;
|
||||||
|
Image.X2 := X2;
|
||||||
|
Image.Y1 := Y1;
|
||||||
|
Image.Y2 := Y2;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.PutScreenImage (Var Image: TConsoleImageRec);
|
||||||
|
Var
|
||||||
|
CountX : Byte;
|
||||||
|
CountY : Byte;
|
||||||
|
Begin
|
||||||
|
For CountY := 1 to (Image.Y2 - Image.Y1 + 1) Do Begin
|
||||||
|
CursorXY (Image.X1, CountY + Image.Y1 - 1);
|
||||||
|
|
||||||
|
Move (Image.Data[CountY][1], Buffer[CountY + Image.Y1 - 1][Image.X1], (Image.X2 - Image.X1 + 1) * SizeOf(TCharInfo));
|
||||||
|
|
||||||
|
For CountX := 1 to (Image.X2 - Image.X1 + 1) Do Begin
|
||||||
|
SetTextAttr(Image.Data[CountY][CountX].Attributes);
|
||||||
|
BufAddStr(Image.Data[CountY][CountX].UnicodeChar);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
SetTextAttr (Image.CursorA);
|
||||||
|
CursorXY (Image.CursorX, Image.CursorY);
|
||||||
|
|
||||||
|
BufFlush;
|
||||||
|
End;
|
||||||
|
*)
|
||||||
|
|
||||||
|
Procedure TOutputLinux.LoadScreenImage (Var DataPtr; Len, Width, X, Y: Integer);
|
||||||
|
Var
|
||||||
|
Image : TConsoleImageRec;
|
||||||
|
Data : Array[1..8000] of Byte Absolute DataPtr;
|
||||||
|
PosX : Word;
|
||||||
|
PosY : Byte;
|
||||||
|
Attrib : Byte;
|
||||||
|
Count : Word;
|
||||||
|
A : Byte;
|
||||||
|
B : Byte;
|
||||||
|
C : Byte;
|
||||||
|
Begin
|
||||||
|
PosX := 1;
|
||||||
|
PosY := 1;
|
||||||
|
Attrib := 7;
|
||||||
|
Count := 1;
|
||||||
|
|
||||||
|
FillChar(Image.Data, SizeOf(Image.Data), #0);
|
||||||
|
|
||||||
|
While (Count <= Len) Do begin
|
||||||
|
Case Data[Count] of
|
||||||
|
00..
|
||||||
|
15 : Attrib := Data[Count] + ((Attrib SHR 4) and 7) * 16;
|
||||||
|
16..
|
||||||
|
23 : Attrib := (Attrib And $F) + (Data[Count] - 16) * 16;
|
||||||
|
24 : Begin
|
||||||
|
Inc (PosY);
|
||||||
|
PosX := 1;
|
||||||
|
End;
|
||||||
|
25 : Begin
|
||||||
|
Inc (Count);
|
||||||
|
|
||||||
|
For A := 0 to Data[Count] Do Begin
|
||||||
|
Image.Data[PosY][PosX].UnicodeChar := ' ';
|
||||||
|
Image.Data[PosY][PosX].Attributes := Attrib;
|
||||||
|
|
||||||
|
Inc (PosX);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
26 : Begin
|
||||||
|
A := Data[Count + 1];
|
||||||
|
B := Data[Count + 2];
|
||||||
|
|
||||||
|
Inc (Count, 2);
|
||||||
|
|
||||||
|
For C := 0 to A Do Begin
|
||||||
|
Image.Data[PosY][PosX].UnicodeChar := Char(B);
|
||||||
|
Image.Data[PosY][PosX].Attributes := Attrib;
|
||||||
|
|
||||||
|
Inc (PosX);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
27..
|
||||||
|
31 : ;
|
||||||
|
Else
|
||||||
|
Image.Data[PosY][PosX].UnicodeChar := Char(Data[Count]);
|
||||||
|
Image.Data[PosY][PosX].Attributes := Attrib;
|
||||||
|
|
||||||
|
Inc (PosX);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Inc(Count);
|
||||||
|
End;
|
||||||
|
|
||||||
|
If PosY > ScreenSize Then PosY := ScreenSize;
|
||||||
|
|
||||||
|
Image.CursorX := PosX;
|
||||||
|
Image.CursorY := PosY;
|
||||||
|
Image.CursorA := Attrib;
|
||||||
|
Image.X1 := X;
|
||||||
|
Image.X2 := Width;
|
||||||
|
Image.Y1 := Y;
|
||||||
|
Image.Y2 := PosY;
|
||||||
|
|
||||||
|
PutScreenImage(Image);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputLinux.ShowBuffer;
|
||||||
|
Begin
|
||||||
|
End;
|
||||||
|
|
||||||
|
End.
|
|
@ -0,0 +1,733 @@
|
||||||
|
{$I M_OPS.PAS}
|
||||||
|
|
||||||
|
Unit m_Output_Windows;
|
||||||
|
|
||||||
|
Interface
|
||||||
|
|
||||||
|
Uses
|
||||||
|
Windows,
|
||||||
|
m_Types;
|
||||||
|
|
||||||
|
Type
|
||||||
|
TOutputWindows = Class
|
||||||
|
Private
|
||||||
|
ConOut : THandle;
|
||||||
|
Cursor : TCoord;
|
||||||
|
Public
|
||||||
|
ScreenSize : Byte;
|
||||||
|
Active : Boolean;
|
||||||
|
TextAttr : Byte;
|
||||||
|
Buffer : TConsoleScreenRec;
|
||||||
|
LineBuf : TConsoleLineRec;
|
||||||
|
Window : TSmallRect;
|
||||||
|
|
||||||
|
Constructor Create (A: Boolean);
|
||||||
|
Destructor Destroy; Override;
|
||||||
|
Procedure ClearScreen; Virtual;
|
||||||
|
Procedure ClearScreenNoUpdate;
|
||||||
|
Procedure ScrollWindow; Virtual;
|
||||||
|
Procedure ClearEOL;
|
||||||
|
Procedure CursorXY (X, Y: Byte);
|
||||||
|
Function CursorX : Byte;
|
||||||
|
Function CursorY : Byte;
|
||||||
|
Procedure SetScreenSize (Mode: Byte);
|
||||||
|
Procedure SetWindowTitle (Title: String);
|
||||||
|
Procedure SetWindow (X1, Y1, X2, Y2: Byte; Home: Boolean);
|
||||||
|
Procedure GetScreenImage (X1, Y1, X2, Y2: Byte; Var Image: TConsoleImageRec);
|
||||||
|
Procedure PutScreenImage (Var Image: TConsoleImageRec);
|
||||||
|
Procedure LoadScreenImage (Var DataPtr; Len, Width, X, Y: Integer);
|
||||||
|
Procedure WriteXY (X, Y, A: Byte; Text: String);
|
||||||
|
Procedure WriteXYNoUpdate (X, Y, A: Byte; Text: String);
|
||||||
|
Procedure WriteXYPipe (X, Y, Attr, Pad: Integer; Text: String);
|
||||||
|
Procedure WriteLineRec (YPos: Integer; Line: TConsoleLineRec);
|
||||||
|
Function ReadCharXY (X, Y: Byte) : Char;
|
||||||
|
Function ReadAttrXY (X, Y: Byte) : Byte;
|
||||||
|
Procedure WriteChar (Ch: Char);
|
||||||
|
Procedure WriteLine (Str: String);
|
||||||
|
Procedure WriteStr (Str: String);
|
||||||
|
Procedure ShowBuffer;
|
||||||
|
Procedure BufFlush; // Linux compatibility only
|
||||||
|
|
||||||
|
// Property ScreenSize : Byte Read FScreenSize;
|
||||||
|
// Property TextAttr : Byte Read FTextAttr Write FTextAttr;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Implementation
|
||||||
|
|
||||||
|
Uses
|
||||||
|
m_Strings;
|
||||||
|
|
||||||
|
Procedure TOutputWindows.WriteLineRec (YPos: Integer; Line: TConsoleLineRec);
|
||||||
|
Var
|
||||||
|
BufSize : TCoord;
|
||||||
|
BufCoord : TCoord;
|
||||||
|
Region : TSmallRect;
|
||||||
|
Begin
|
||||||
|
BufSize.X := 80;
|
||||||
|
BufSize.Y := 1;
|
||||||
|
BufCoord.X := 0;
|
||||||
|
BufCoord.Y := 0;
|
||||||
|
Region.Left := 0;
|
||||||
|
Region.Top := YPos - 1;
|
||||||
|
Region.Right := 79;
|
||||||
|
Region.Bottom := YPos - 1;
|
||||||
|
|
||||||
|
WriteConsoleOutput(ConOut, @Line, BufSize, BufCoord, Region);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputWindows.SetWindow (X1, Y1, X2, Y2 : Byte; Home: Boolean);
|
||||||
|
Begin
|
||||||
|
If (X1 > X2) or (X2 > 80) or
|
||||||
|
(Y1 > Y2) or (Y2 > ScreenSize) Then Exit;
|
||||||
|
|
||||||
|
Window.Left := X1 - 1;
|
||||||
|
Window.Top := Y1 - 1;
|
||||||
|
Window.Right := X2 - 1;
|
||||||
|
Window.Bottom := Y2 - 1;
|
||||||
|
|
||||||
|
If Home Then CursorXY (X1, Y1) Else CursorXY (Cursor.X + 1, Cursor.Y + 1);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Constructor TOutputWindows.Create (A: Boolean);
|
||||||
|
Var
|
||||||
|
ScreenMode : TConsoleScreenBufferInfo;
|
||||||
|
CursorInfo : TConsoleCursorInfo;
|
||||||
|
Begin
|
||||||
|
Inherited Create;
|
||||||
|
|
||||||
|
Active := A;
|
||||||
|
ConOut := GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
|
||||||
|
GetConsoleScreenBufferInfo(ConOut, ScreenMode);
|
||||||
|
|
||||||
|
Case ScreenMode.dwSize.Y of
|
||||||
|
25 : ScreenSize := 25;
|
||||||
|
50 : ScreenSize := 50;
|
||||||
|
Else
|
||||||
|
SetScreenSize(25);
|
||||||
|
ScreenSize := 25;
|
||||||
|
End;
|
||||||
|
|
||||||
|
CursorInfo.bVisible := True;
|
||||||
|
CursorInfo.dwSize := 15;
|
||||||
|
|
||||||
|
SetConsoleCursorInfo(ConOut, CursorInfo);
|
||||||
|
|
||||||
|
Window.Top := 0;
|
||||||
|
Window.Left := 0;
|
||||||
|
Window.Right := 79;
|
||||||
|
Window.Bottom := ScreenSize - 1;
|
||||||
|
|
||||||
|
TextAttr := 7;
|
||||||
|
|
||||||
|
ClearScreen;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Destructor TOutputWindows.Destroy;
|
||||||
|
Begin
|
||||||
|
Inherited Destroy;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputWindows.SetScreenSize (Mode: Byte);
|
||||||
|
Var
|
||||||
|
Size : TCoord;
|
||||||
|
Begin
|
||||||
|
If (Mode = ScreenSize) Or Not (Mode in [25, 50]) Then Exit;
|
||||||
|
|
||||||
|
Size.X := 80;
|
||||||
|
Size.Y := Mode;
|
||||||
|
|
||||||
|
Window.Top := 0;
|
||||||
|
Window.Left := 0;
|
||||||
|
Window.Right := Size.X - 1;
|
||||||
|
Window.Bottom := Size.Y - 1;
|
||||||
|
|
||||||
|
SetConsoleScreenBufferSize (ConOut, Size);
|
||||||
|
SetConsoleWindowInfo (ConOut, True, Window);
|
||||||
|
SetConsoleScreenBufferSize (ConOut, Size);
|
||||||
|
|
||||||
|
ScreenSize := Mode;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputWindows.CursorXY (X, Y: Byte);
|
||||||
|
Begin
|
||||||
|
// don't move to x/y coordinate outside of window
|
||||||
|
|
||||||
|
Cursor.X := X - 1;
|
||||||
|
Cursor.Y := Y - 1;
|
||||||
|
|
||||||
|
If Cursor.X < Window.Left Then Cursor.X := Window.Left Else
|
||||||
|
If Cursor.X > Window.Right Then Cursor.X := Window.Right;
|
||||||
|
If Cursor.Y < Window.Top Then Cursor.Y := Window.Top Else
|
||||||
|
If Cursor.Y > Window.Bottom Then Cursor.Y := Window.Bottom;
|
||||||
|
|
||||||
|
If Active Then
|
||||||
|
SetConsoleCursorPosition(ConOut, Cursor);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputWindows.ClearEOL;
|
||||||
|
Var
|
||||||
|
Buf : Array[1..80] of TCharInfo;
|
||||||
|
Count : Byte;
|
||||||
|
BufSize : TCoord;
|
||||||
|
BufCoord : TCoord;
|
||||||
|
Region : TSmallRect;
|
||||||
|
Begin
|
||||||
|
Count := 0;
|
||||||
|
|
||||||
|
While Count <= Window.Right - Cursor.X Do Begin
|
||||||
|
Inc (Count);
|
||||||
|
Buf[Count].Attributes := TextAttr;
|
||||||
|
Buf[Count].UnicodeChar := ' ';
|
||||||
|
End;
|
||||||
|
|
||||||
|
Move(Buf[1], Buffer[Cursor.Y + 1][Cursor.X + 1], Count);
|
||||||
|
|
||||||
|
If Active Then Begin
|
||||||
|
BufSize.X := Count;
|
||||||
|
BufSize.Y := 1;
|
||||||
|
BufCoord.X := 0;
|
||||||
|
BufCoord.Y := 0;
|
||||||
|
Region.Left := Cursor.X;
|
||||||
|
Region.Top := Cursor.Y;
|
||||||
|
Region.Right := Cursor.X + Count - 1;
|
||||||
|
Region.Bottom := Cursor.Y;
|
||||||
|
|
||||||
|
WriteConsoleOutput(ConOut, @Buf, BufSize, BufCoord, Region);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputWindows.ClearScreenNoUpdate;
|
||||||
|
Var
|
||||||
|
Res : ULong;
|
||||||
|
Count : Byte;
|
||||||
|
Size : Byte;
|
||||||
|
Cell : TCharInfo;
|
||||||
|
Begin
|
||||||
|
Size := Window.Right - Window.Left + 1;
|
||||||
|
Cursor.X := Window.Left;
|
||||||
|
Cell.Attributes := TextAttr;
|
||||||
|
Cell.UnicodeChar := ' ';
|
||||||
|
|
||||||
|
For Count := Window.Top To Window.Bottom Do Begin
|
||||||
|
Cursor.Y := Count;
|
||||||
|
|
||||||
|
FillConsoleOutputAttribute(ConOut, Cell.Attributes, Size, Cursor, Res);
|
||||||
|
FillConsoleOutputCharacter(ConOut, ' ', Size, Cursor, Res);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputWindows.ClearScreen;
|
||||||
|
Var
|
||||||
|
Res : ULong;
|
||||||
|
Count : Byte;
|
||||||
|
Size : Byte;
|
||||||
|
Cell : TCharInfo;
|
||||||
|
Begin
|
||||||
|
Size := Window.Right - Window.Left + 1;
|
||||||
|
Cursor.X := Window.Left;
|
||||||
|
Cell.Attributes := TextAttr;
|
||||||
|
Cell.UnicodeChar := ' ';
|
||||||
|
|
||||||
|
If Active Then Begin
|
||||||
|
For Count := Window.Top To Window.Bottom Do Begin
|
||||||
|
Cursor.Y := Count;
|
||||||
|
|
||||||
|
FillConsoleOutputAttribute(ConOut, Cell.Attributes, Size, Cursor, Res);
|
||||||
|
FillConsoleOutputCharacter(ConOut, ' ', Size, Cursor, Res);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
FillChar (Buffer, SizeOf(Buffer), 0);
|
||||||
|
|
||||||
|
CursorXY (Window.Left + 1, Window.Top + 1);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputWindows.SetWindowTitle (Title: String);
|
||||||
|
Begin
|
||||||
|
Title := Title + #0;
|
||||||
|
SetConsoleTitle(@Title[1]);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputWindows.WriteXY (X, Y, A: Byte; Text: String);
|
||||||
|
Var
|
||||||
|
Buf : Array[1..80] of TCharInfo;
|
||||||
|
BufSize : TCoord;
|
||||||
|
BufCoord : TCoord;
|
||||||
|
Region : TSmallRect;
|
||||||
|
Count : Byte;
|
||||||
|
Begin
|
||||||
|
Count := 1;
|
||||||
|
|
||||||
|
While Count <= Length(Text) Do Begin
|
||||||
|
Buf[Count].Attributes := A;
|
||||||
|
Buf[Count].UnicodeChar := Text[Count];
|
||||||
|
Inc (Count);
|
||||||
|
End;
|
||||||
|
|
||||||
|
// add to screen buffer
|
||||||
|
Move (Buf[1], Buffer[Y][X], (Count - 1) * SizeOf(TCharInfo));
|
||||||
|
|
||||||
|
If Active Then Begin
|
||||||
|
BufSize.X := Count - 1;
|
||||||
|
BufSize.Y := 1;
|
||||||
|
BufCoord.X := 0;
|
||||||
|
BufCoord.Y := 0;
|
||||||
|
Region.Left := X - 1;
|
||||||
|
Region.Top := Y - 1;
|
||||||
|
Region.Right := X + Count - 1;
|
||||||
|
Region.Bottom := Y - 1;
|
||||||
|
|
||||||
|
If Region.Right > 79 Then Region.Right := 79;
|
||||||
|
|
||||||
|
WriteConsoleOutput(ConOut, @Buf, BufSize, BufCoord, Region);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputWindows.WriteXYNoUpdate (X, Y, A: Byte; Text: String);
|
||||||
|
Var
|
||||||
|
Buf : Array[1..80] of TCharInfo;
|
||||||
|
BufSize : TCoord;
|
||||||
|
BufCoord : TCoord;
|
||||||
|
Region : TSmallRect;
|
||||||
|
Count : Byte;
|
||||||
|
Begin
|
||||||
|
Count := 1;
|
||||||
|
|
||||||
|
While Count <= Length(Text) Do Begin
|
||||||
|
Buf[Count].Attributes := A;
|
||||||
|
Buf[Count].UnicodeChar := Text[Count];
|
||||||
|
Inc (Count);
|
||||||
|
End;
|
||||||
|
|
||||||
|
BufSize.X := Count - 1;
|
||||||
|
BufSize.Y := 1;
|
||||||
|
BufCoord.X := 0;
|
||||||
|
BufCoord.Y := 0;
|
||||||
|
Region.Left := X - 1;
|
||||||
|
Region.Top := Y - 1;
|
||||||
|
Region.Right := X + Count - 1;
|
||||||
|
Region.Bottom := Y - 1;
|
||||||
|
|
||||||
|
If Region.Right > 79 Then Region.Right := 79;
|
||||||
|
|
||||||
|
WriteConsoleOutput(ConOut, @Buf, BufSize, BufCoord, Region);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputWindows.WriteXYPipe (X, Y, Attr, Pad: Integer; Text: String);
|
||||||
|
Var
|
||||||
|
Buf : Array[1..80] of TCharInfo;
|
||||||
|
BufPos : Byte;
|
||||||
|
Count : Byte;
|
||||||
|
Code : String[2];
|
||||||
|
CodeNum : Byte;
|
||||||
|
BufSize : TCoord;
|
||||||
|
BufCoord : TCoord;
|
||||||
|
Region : TSmallRect;
|
||||||
|
|
||||||
|
Procedure AddChar;
|
||||||
|
Begin
|
||||||
|
Inc (BufPos);
|
||||||
|
|
||||||
|
Buf[BufPos].Attributes := Attr;
|
||||||
|
Buf[BufPos].UnicodeChar := Text[Count];
|
||||||
|
End;
|
||||||
|
|
||||||
|
Begin
|
||||||
|
FillChar(Buf, SizeOf(Buf), #0);
|
||||||
|
|
||||||
|
Count := 1;
|
||||||
|
BufPos := 0;
|
||||||
|
|
||||||
|
While Count <= Length(Text) Do Begin
|
||||||
|
If Text[Count] = '|' Then Begin
|
||||||
|
Code := Copy(Text, Count + 1, 2);
|
||||||
|
CodeNum := strS2I(Code);
|
||||||
|
|
||||||
|
If (Code = '00') or ((CodeNum > 0) and (CodeNum < 24) and (Code[1] <> '$') and (Code[1] <> '&')) Then Begin
|
||||||
|
Inc (Count, 2);
|
||||||
|
If CodeNum in [00..15] Then
|
||||||
|
Attr := CodeNum + ((Attr SHR 4) AND 7) * 16
|
||||||
|
Else
|
||||||
|
Attr := (Attr AND $F) + (CodeNum - 16) * 16;
|
||||||
|
End Else
|
||||||
|
AddChar;
|
||||||
|
End Else
|
||||||
|
AddChar;
|
||||||
|
|
||||||
|
If BufPos = Pad Then Break;
|
||||||
|
|
||||||
|
Inc (Count);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Text[1] := #32;
|
||||||
|
Count := 1;
|
||||||
|
|
||||||
|
While BufPos < Pad Do AddChar;
|
||||||
|
|
||||||
|
BufSize.X := Pad;
|
||||||
|
BufSize.Y := 1;
|
||||||
|
BufCoord.X := 0;
|
||||||
|
BufCoord.Y := 0;
|
||||||
|
Region.Left := X - 1;
|
||||||
|
Region.Top := Y - 1;
|
||||||
|
Region.Right := X + Pad;
|
||||||
|
Region.Bottom := Y - 1;
|
||||||
|
|
||||||
|
If Region.Right > 79 Then Region.Right := 79;
|
||||||
|
|
||||||
|
Move (Buf[1], Buffer[Y][X], BufSize.X * SizeOf(TCharInfo));
|
||||||
|
|
||||||
|
If Active Then
|
||||||
|
WriteConsoleOutput(ConOut, @Buf, BufSize, BufCoord, Region);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TOutputWindows.CursorX : Byte;
|
||||||
|
Begin
|
||||||
|
CursorX := Cursor.X + 1;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TOutputWindows.CursorY : Byte;
|
||||||
|
Begin
|
||||||
|
CursorY := Cursor.Y + 1;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputWindows.WriteChar (Ch: Char);
|
||||||
|
Var
|
||||||
|
BufferSize,
|
||||||
|
BufferCoord : TCoord;
|
||||||
|
WriteRegion : TSmallRect;
|
||||||
|
OneCell : TCharInfo;
|
||||||
|
Begin
|
||||||
|
Case Ch of
|
||||||
|
#08 : If Cursor.X > Window.Left Then Begin
|
||||||
|
Dec(Cursor.X);
|
||||||
|
If Active Then SetConsoleCursorPosition(ConOut, Cursor);
|
||||||
|
End;
|
||||||
|
#10 : Begin
|
||||||
|
If Cursor.Y = Window.Bottom Then
|
||||||
|
ScrollWindow
|
||||||
|
Else Begin
|
||||||
|
Inc (Cursor.Y);
|
||||||
|
Cursor.X := Window.Left;
|
||||||
|
End;
|
||||||
|
|
||||||
|
If Active Then SetConsoleCursorPosition(ConOut, Cursor);
|
||||||
|
End;
|
||||||
|
#13 : Cursor.X := Window.Left;
|
||||||
|
Else
|
||||||
|
If Active Then Begin
|
||||||
|
OneCell.UnicodeChar := Ch;
|
||||||
|
OneCell.Attributes := TextAttr;
|
||||||
|
|
||||||
|
BufferSize.X := 1;
|
||||||
|
BufferSize.Y := 1;
|
||||||
|
BufferCoord.X := 0;
|
||||||
|
BufferCoord.Y := 0;
|
||||||
|
|
||||||
|
WriteRegion.Left := Cursor.X;
|
||||||
|
WriteRegion.Top := Cursor.Y;
|
||||||
|
WriteRegion.Right := Cursor.X;
|
||||||
|
WriteRegion.Bottom := Cursor.Y;
|
||||||
|
|
||||||
|
WriteConsoleOutput (ConOut, @OneCell, BufferSize, BufferCoord, WriteRegion);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Buffer[Cursor.Y + 1][Cursor.X + 1].UnicodeChar := Ch;
|
||||||
|
Buffer[Cursor.Y + 1][Cursor.X + 1].Attributes := TextAttr;
|
||||||
|
|
||||||
|
If Cursor.X < Window.Right Then
|
||||||
|
Inc (Cursor.X)
|
||||||
|
Else Begin
|
||||||
|
If (Cursor.X = Window.Right) And (Cursor.Y = Window.Bottom - 1) Then Begin
|
||||||
|
Inc (Cursor.X);
|
||||||
|
Exit;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Cursor.X := Window.Left;
|
||||||
|
|
||||||
|
If Cursor.Y = Window.Bottom Then
|
||||||
|
ScrollWindow
|
||||||
|
Else
|
||||||
|
Inc (Cursor.Y);
|
||||||
|
End;
|
||||||
|
|
||||||
|
If Active Then SetConsoleCursorPosition(ConOut, Cursor);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputWindows.WriteLine (Str: String);
|
||||||
|
Var
|
||||||
|
Count : Byte;
|
||||||
|
Begin
|
||||||
|
Str := Str + #13#10;
|
||||||
|
|
||||||
|
For Count := 1 to Length(Str) Do WriteChar(Str[Count]);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputWindows.WriteStr (Str: String);
|
||||||
|
Var
|
||||||
|
Count : Byte;
|
||||||
|
Begin
|
||||||
|
For Count := 1 to Length(Str) Do WriteChar(Str[Count]);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputWindows.ScrollWindow;
|
||||||
|
Var
|
||||||
|
ClipRect,
|
||||||
|
ScrollRect : TSmallRect;
|
||||||
|
DestCoord : TCoord;
|
||||||
|
Fill : TCharInfo;
|
||||||
|
Begin
|
||||||
|
Fill.UnicodeChar := ' ';
|
||||||
|
// Fill.Attributes := TextAttr;
|
||||||
|
Fill.Attributes := 7;
|
||||||
|
|
||||||
|
ScrollRect.Left := Window.Left;
|
||||||
|
ScrollRect.Top := Window.Top;
|
||||||
|
ScrollRect.Right := Window.Right;
|
||||||
|
ScrollRect.Bottom := Window.Bottom;
|
||||||
|
|
||||||
|
// might not need cliprect... might be able to pass scrollrect twice
|
||||||
|
|
||||||
|
ClipRect := ScrollRect;
|
||||||
|
|
||||||
|
DestCoord.X := Window.Left;
|
||||||
|
DestCoord.Y := Window.Top - 1;
|
||||||
|
|
||||||
|
If Active Then
|
||||||
|
ScrollConsoleScreenBuffer(ConOut, ScrollRect, ClipRect, DestCoord, PCharInfo(@Fill)^);
|
||||||
|
|
||||||
|
Move (Buffer[2][1], Buffer[1][1], SizeOf(TConsoleLineRec) * 49);
|
||||||
|
FillChar(Buffer[Window.Bottom + 1][1], SizeOf(TConsoleLineRec), #0);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputWindows.GetScreenImage (X1, Y1, X2, Y2: Byte; Var Image: TConsoleImageRec);
|
||||||
|
Var
|
||||||
|
CountY : Byte;
|
||||||
|
CountX : Byte;
|
||||||
|
BufPos : Integer;
|
||||||
|
NewBuf : Array[1..SizeOf(TConsoleScreenRec) DIV 2] of Word Absolute Image.Data;
|
||||||
|
Begin
|
||||||
|
Image.X1 := X1;
|
||||||
|
Image.X2 := X2;
|
||||||
|
Image.Y1 := Y1;
|
||||||
|
Image.Y2 := Y2;
|
||||||
|
Image.CursorX := CursorX;
|
||||||
|
Image.CursorY := CursorY;
|
||||||
|
Image.CursorA := TextAttr;
|
||||||
|
|
||||||
|
BufPos := 1;
|
||||||
|
|
||||||
|
For CountY := Y1 to Y2 Do Begin
|
||||||
|
For CountX := X1 to X2 Do Begin
|
||||||
|
NewBuf[BufPos] := Word(Buffer[CountY][CountX].UnicodeChar);
|
||||||
|
NewBuf[BufPos+1] := Buffer[CountY][CountX].Attributes;
|
||||||
|
Inc (BufPos, 2);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
(*
|
||||||
|
Procedure TOutputWindows.GetScreenImage (X1, Y1, X2, Y2: Byte; Var Image: TConsoleImageRec);
|
||||||
|
Var
|
||||||
|
BufSize : TCoord;
|
||||||
|
BufCoord : TCoord;
|
||||||
|
Region : TSmallRect;
|
||||||
|
// x,y,cx,cy:byte;
|
||||||
|
Begin
|
||||||
|
BufSize.X := X2 - X1 + 1;
|
||||||
|
BufSize.Y := Y2 - Y1 + 1;
|
||||||
|
BufCoord.X := 0;
|
||||||
|
BufCoord.Y := 0;
|
||||||
|
Region.Left := X1 - 1;
|
||||||
|
Region.Top := Y1 - 1;
|
||||||
|
Region.Right := X2 - 1;
|
||||||
|
Region.Bottom := Y2 - 1;
|
||||||
|
Image.X1 := X1;
|
||||||
|
Image.X2 := X2;
|
||||||
|
Image.Y1 := Y1;
|
||||||
|
Image.Y2 := Y2;
|
||||||
|
Image.CursorX := CursorX;
|
||||||
|
Image.CursorY := CursorY;
|
||||||
|
Image.CursorA := TextAttr;
|
||||||
|
|
||||||
|
If Active Then
|
||||||
|
ReadConsoleOutput (ConOut, @Image.Data[1][1], BufSize, BufCoord, Region)
|
||||||
|
Else
|
||||||
|
Image.Data := Buffer;
|
||||||
|
End;
|
||||||
|
*)
|
||||||
|
|
||||||
|
Procedure TOutputWindows.ShowBuffer;
|
||||||
|
Var
|
||||||
|
BufSize : TCoord;
|
||||||
|
BufCoord : TCoord;
|
||||||
|
Region : TSmallRect;
|
||||||
|
Begin
|
||||||
|
BufSize.X := 80;
|
||||||
|
BufSize.Y := ScreenSize;
|
||||||
|
BufCoord.X := 0;
|
||||||
|
BufCoord.Y := 0;
|
||||||
|
Region.Left := 0;
|
||||||
|
Region.Top := 0;
|
||||||
|
Region.Right := 79;
|
||||||
|
Region.Bottom := ScreenSize - 1;
|
||||||
|
|
||||||
|
WriteConsoleOutput (ConOut, @Buffer[1][1], BufSize, BufCoord, Region);
|
||||||
|
|
||||||
|
CursorXY (Cursor.X + 1, Cursor.Y + 1);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputWindows.PutScreenImage (Var Image: TConsoleImageRec);
|
||||||
|
Var
|
||||||
|
BufSize : TCoord;
|
||||||
|
BufCoord : TCoord;
|
||||||
|
Region : TSmallRect;
|
||||||
|
|
||||||
|
CountX : Byte;
|
||||||
|
CountY : Byte;
|
||||||
|
BufPos : Integer;
|
||||||
|
TempBuf : Array[1..SizeOf(TConsoleScreenRec) DIV 4] of LongInt Absolute Image.Data;
|
||||||
|
Begin
|
||||||
|
BufSize.X := Image.X2 - Image.X1 + 1;
|
||||||
|
BufSize.Y := Image.Y2 - Image.Y1 + 1;
|
||||||
|
BufCoord.X := 0;
|
||||||
|
BufCoord.Y := 0;
|
||||||
|
Region.Left := Image.X1 - 1;
|
||||||
|
Region.Top := Image.Y1 - 1;
|
||||||
|
Region.Right := Image.X2 - 1;
|
||||||
|
Region.Bottom := Image.Y2 - 1;
|
||||||
|
|
||||||
|
WriteConsoleOutput (ConOut, @Image.Data[1][1], BufSize, BufCoord, Region);
|
||||||
|
|
||||||
|
BufPos := 1;
|
||||||
|
|
||||||
|
For CountY := Image.Y1 to Image.Y2 Do
|
||||||
|
For CountX := Image.X1 to Image.X2 Do Begin
|
||||||
|
Buffer[CountY][CountX] := TCharInfo(TempBuf[BufPos]);
|
||||||
|
Inc(BufPos);
|
||||||
|
End;
|
||||||
|
|
||||||
|
CursorXY (Image.CursorX, Image.CursorY);
|
||||||
|
|
||||||
|
TextAttr := Image.CursorA;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputWindows.LoadScreenImage (Var DataPtr; Len, Width, X, Y: Integer);
|
||||||
|
Var
|
||||||
|
Screen : TConsoleScreenRec;
|
||||||
|
Data : Array[1..8000] of Byte Absolute DataPtr;
|
||||||
|
PosX : Word;
|
||||||
|
PosY : Byte;
|
||||||
|
Attrib : Byte;
|
||||||
|
Count : Word;
|
||||||
|
A : Byte;
|
||||||
|
B : Byte;
|
||||||
|
C : Byte;
|
||||||
|
BufSize : TCoord;
|
||||||
|
BufCoord : TCoord;
|
||||||
|
Region : TSmallRect;
|
||||||
|
Begin
|
||||||
|
PosX := 1;
|
||||||
|
PosY := 1;
|
||||||
|
Attrib := 7;
|
||||||
|
Count := 1;
|
||||||
|
|
||||||
|
FillChar(Screen, SizeOf(Screen), #0);
|
||||||
|
|
||||||
|
While (Count <= Len) Do Begin
|
||||||
|
Case Data[Count] of
|
||||||
|
00..
|
||||||
|
15 : Attrib := Data[Count] + ((Attrib SHR 4) and 7) * 16;
|
||||||
|
16..
|
||||||
|
23 : Attrib := (Attrib And $F) + (Data[Count] - 16) * 16;
|
||||||
|
24 : Begin
|
||||||
|
Inc (PosY);
|
||||||
|
PosX := 1;
|
||||||
|
End;
|
||||||
|
25 : Begin
|
||||||
|
Inc (Count);
|
||||||
|
For A := 0 to Data[Count] Do Begin
|
||||||
|
Screen[PosY][PosX].UnicodeChar := ' ';
|
||||||
|
Screen[PosY][PosX].Attributes := Attrib;
|
||||||
|
|
||||||
|
Inc (PosX);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
26 : Begin
|
||||||
|
A := Data[Count + 1];
|
||||||
|
B := Data[Count + 2];
|
||||||
|
|
||||||
|
Inc (Count, 2);
|
||||||
|
|
||||||
|
For C := 0 to A Do Begin
|
||||||
|
Screen[PosY][PosX].UnicodeChar := Char(B);
|
||||||
|
Screen[PosY][PosX].Attributes := Attrib;
|
||||||
|
|
||||||
|
Inc (PosX);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
27..
|
||||||
|
31 : ;
|
||||||
|
Else
|
||||||
|
Screen[PosY][PosX].UnicodeChar := Char(Data[Count]);
|
||||||
|
Screen[PosY][PosX].Attributes := Attrib;
|
||||||
|
|
||||||
|
Inc (PosX);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Inc (Count);
|
||||||
|
End;
|
||||||
|
|
||||||
|
//If PosY > ScreenSize Then PosY := ScreenSize;
|
||||||
|
|
||||||
|
BufSize.Y := PosY - (Y - 1);
|
||||||
|
BufSize.X := Width;
|
||||||
|
BufCoord.X := 0;
|
||||||
|
BufCoord.Y := 0;
|
||||||
|
Region.Left := X - 1;
|
||||||
|
Region.Top := Y - 1;
|
||||||
|
Region.Right := Width - 1;
|
||||||
|
Region.Bottom := PosY - 1;
|
||||||
|
|
||||||
|
WriteConsoleOutput (ConOut, @Screen[1][1], BufSize, BufCoord, Region);
|
||||||
|
|
||||||
|
CursorXY(PosX, PosY);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TOutputWindows.ReadCharXY (X, Y: Byte) : Char;
|
||||||
|
//Var
|
||||||
|
// Coord : TCoord;
|
||||||
|
// WasRead : ULong;
|
||||||
|
Begin
|
||||||
|
// Coord.X := X;
|
||||||
|
// Coord.Y := Y - 1;
|
||||||
|
|
||||||
|
// should use buffer instead
|
||||||
|
|
||||||
|
// ReadConsoleOutputCharacter(ConOut, @Result, 1, Coord, WasRead);
|
||||||
|
|
||||||
|
Result := Buffer[Y][X].UnicodeChar;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TOutputWindows.ReadAttrXY (X, Y: Byte) : Byte;
|
||||||
|
//Var
|
||||||
|
// Coord : TCoord;
|
||||||
|
// WasRead : ULong;
|
||||||
|
Begin
|
||||||
|
// Coord.X := X;
|
||||||
|
// Coord.Y := Y - 1;
|
||||||
|
|
||||||
|
// should use buffer instead
|
||||||
|
|
||||||
|
Result := Buffer[Y][X].Attributes;
|
||||||
|
|
||||||
|
// ReadConsoleOutputAttribute(ConOut, @Result, 1, Coord, WasRead);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TOutputWindows.BufFlush;
|
||||||
|
Begin
|
||||||
|
End;
|
||||||
|
|
||||||
|
End.
|
|
@ -0,0 +1,570 @@
|
||||||
|
{$I M_OPS.PAS}
|
||||||
|
|
||||||
|
Unit m_Socket_Class;
|
||||||
|
|
||||||
|
Interface
|
||||||
|
|
||||||
|
Uses
|
||||||
|
{$IFDEF WIN32}
|
||||||
|
Windows,
|
||||||
|
Winsock2,
|
||||||
|
{$ENDIF}
|
||||||
|
{$IFDEF UNIX}
|
||||||
|
BaseUnix,
|
||||||
|
cNetDB,
|
||||||
|
{$ENDIF}
|
||||||
|
Sockets,
|
||||||
|
Classes,
|
||||||
|
m_DateTime,
|
||||||
|
m_Strings;
|
||||||
|
|
||||||
|
Const
|
||||||
|
TSocketBufferSize = 8 * 1024 - 1;
|
||||||
|
|
||||||
|
Type
|
||||||
|
TSocketBuffer = Array[0..TSocketBufferSize] of Char;
|
||||||
|
|
||||||
|
TSocketClass = Class
|
||||||
|
SocketStatus : TStringList;
|
||||||
|
StatusUpdated : Boolean;
|
||||||
|
FSocketHandle : LongInt;
|
||||||
|
FPort : LongInt;
|
||||||
|
FPeerName : String;
|
||||||
|
FPeerIP : String;
|
||||||
|
FHostIP : String;
|
||||||
|
FInBuf : TSocketBuffer;
|
||||||
|
FInBufPos : LongInt;
|
||||||
|
FInBufEnd : LongInt;
|
||||||
|
FOutBuf : TSocketBuffer;
|
||||||
|
FOutBufPos : LongInt;
|
||||||
|
FTelnetState : Byte;
|
||||||
|
FTelnetReply : Array[1..14] of Char;
|
||||||
|
FTelnetCmd : Char;
|
||||||
|
FTelnetSubCmd : Char;
|
||||||
|
FTelnetLen : Byte;
|
||||||
|
FTelnetEcho : Boolean;
|
||||||
|
FTelnetSubData : String;
|
||||||
|
FTelnetClient : Boolean;
|
||||||
|
FTelnetServer : Boolean;
|
||||||
|
|
||||||
|
Constructor Create;
|
||||||
|
Destructor Destroy; Override;
|
||||||
|
Procedure Disconnect;
|
||||||
|
Function DataWaiting : Boolean;
|
||||||
|
Function WriteBuf (Var Buf; Len: LongInt) : LongInt;
|
||||||
|
Procedure BufFlush;
|
||||||
|
Procedure BufWriteChar (Ch: Char);
|
||||||
|
Procedure BufWriteStr (Str: String);
|
||||||
|
Function WriteLine (Str: String) : LongInt;
|
||||||
|
Function WriteStr (Str: String) : LongInt;
|
||||||
|
Function WriteFile (Str: String) : Boolean;
|
||||||
|
Procedure ProcessBuf (Var Buf: TSocketBuffer; Var Len: LongInt);
|
||||||
|
Function ReadBuf (Var Buf; Len: LongInt) : LongInt;
|
||||||
|
Function ReadLine (Var Str: String) : LongInt;
|
||||||
|
Function SetBlocking (Block: Boolean): LongInt;
|
||||||
|
Function WaitForData (TimeOut: LongInt) : LongInt;
|
||||||
|
Function Connect (Address: String; Port: Word) : Boolean;
|
||||||
|
Function ResolveAddress (Host: String) : LongInt;
|
||||||
|
Procedure WaitInit (Port: Word);
|
||||||
|
Function WaitConnection : TSocketClass;
|
||||||
|
|
||||||
|
Procedure PurgeInputData;
|
||||||
|
Function ReadChar : Char;
|
||||||
|
Function WriteChar (Ch: Char) : LongInt;
|
||||||
|
|
||||||
|
Procedure Status (Str: String);
|
||||||
|
|
||||||
|
Property SocketHandle : LongInt READ FSocketHandle WRITE FSocketHandle;
|
||||||
|
Property PeerPort : LongInt READ FPort WRITE FPort;
|
||||||
|
Property PeerName : String READ FPeerName WRITE FPeerName;
|
||||||
|
Property PeerIP : String READ FPeerIP WRITE FPeerIP;
|
||||||
|
Property HostIP : String READ FHostIP WRITE FHostIP;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Implementation
|
||||||
|
|
||||||
|
{ TELNET NEGOTIATION CONSTANTS }
|
||||||
|
|
||||||
|
Const
|
||||||
|
Telnet_IAC = #255;
|
||||||
|
Telnet_DONT = #254;
|
||||||
|
Telnet_DO = #253;
|
||||||
|
Telnet_WONT = #252;
|
||||||
|
Telnet_WILL = #251;
|
||||||
|
Telnet_SB = #250;
|
||||||
|
Telnet_BINARY = #000;
|
||||||
|
Telnet_ECHO = #001;
|
||||||
|
Telnet_SE = #240;
|
||||||
|
Telnet_TERM = #24;
|
||||||
|
Telnet_SGA = #003;
|
||||||
|
|
||||||
|
FPSENDOPT = 0;
|
||||||
|
FPRECVOPT = 0;
|
||||||
|
|
||||||
|
Constructor TSocketClass.Create;
|
||||||
|
Begin
|
||||||
|
Inherited Create;
|
||||||
|
|
||||||
|
FSocketHandle := -1;
|
||||||
|
FPort := 0;
|
||||||
|
FPeerName := 'Unknown';
|
||||||
|
FPeerIP := FPeerName;
|
||||||
|
FInBufPos := 0;
|
||||||
|
FInBufEnd := 0;
|
||||||
|
FOutBufPos := 0;
|
||||||
|
FTelnetState := 0;
|
||||||
|
FTelnetEcho := False;
|
||||||
|
FTelnetClient := False;
|
||||||
|
FTelnetServer := False;
|
||||||
|
{ FHostIP := '127.0.0.1';}
|
||||||
|
FHostIP := '';
|
||||||
|
StatusUpdated := False;
|
||||||
|
|
||||||
|
SocketStatus := TStringList.Create;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Destructor TSocketClass.Destroy;
|
||||||
|
Begin
|
||||||
|
Disconnect;
|
||||||
|
|
||||||
|
SocketStatus.Free;
|
||||||
|
|
||||||
|
Inherited Destroy;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TSocketClass.PurgeInputData;
|
||||||
|
Var
|
||||||
|
Buf : Array[1..1024] of Char;
|
||||||
|
Begin
|
||||||
|
If FSocketHandle = -1 Then Exit;
|
||||||
|
|
||||||
|
If DataWaiting Then
|
||||||
|
Repeat
|
||||||
|
Until ReadBuf(Buf, SizeOf(Buf)) <> 1024;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TSocketClass.Disconnect;
|
||||||
|
Begin
|
||||||
|
If FSocketHandle <> -1 Then Begin
|
||||||
|
fpShutdown(FSocketHandle, 2);
|
||||||
|
CloseSocket(FSocketHandle);
|
||||||
|
|
||||||
|
FSocketHandle := -1;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TSocketClass.DataWaiting : Boolean;
|
||||||
|
Begin
|
||||||
|
Result := (FInBufPos < FInBufEnd) or (WaitForData(0) > 0);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TSocketClass.WriteBuf (Var Buf; Len: LongInt) : LongInt;
|
||||||
|
Begin
|
||||||
|
Result := fpSend(FSocketHandle, @Buf, Len, FPSENDOPT);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TSocketClass.BufFlush;
|
||||||
|
Begin
|
||||||
|
If FOutBufPos > 0 Then Begin
|
||||||
|
WriteBuf (FOutBuf, FOutBufPos);
|
||||||
|
FOutBufPos := 0;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TSocketClass.BufWriteChar (Ch: Char);
|
||||||
|
Begin
|
||||||
|
FOutBuf[FOutBufPos] := Ch;
|
||||||
|
Inc(FOutBufPos);
|
||||||
|
|
||||||
|
If FOutBufPos > TSocketBufferSize Then Begin
|
||||||
|
WriteBuf (FOutBuf, FOutBufPos - 1);
|
||||||
|
FOutBufPos := 0;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TSocketClass.BufWriteStr (Str: String);
|
||||||
|
Var
|
||||||
|
Count : LongInt;
|
||||||
|
Begin
|
||||||
|
For Count := 1 to Length(Str) Do
|
||||||
|
BufWriteChar(Str[Count]);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TSocketClass.WriteLine (Str: String) : LongInt;
|
||||||
|
Begin
|
||||||
|
Str := Str + #13#10;
|
||||||
|
Result := fpSend(FSocketHandle, @Str[1], Length(Str), FPSENDOPT);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TSocketClass.WriteChar (Ch: Char) : LongInt;
|
||||||
|
Begin
|
||||||
|
Result := fpSend(FSocketHandle, @Ch, 1, FPSENDOPT);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TSocketClass.WriteStr (Str: String) : LongInt;
|
||||||
|
Begin
|
||||||
|
Result := fpSend(FSocketHandle, @Str[1], Length(Str), FPSENDOPT);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TSocketClass.WriteFile (Str: String) : Boolean;
|
||||||
|
Var
|
||||||
|
Buf : Array[1..4096] of Char;
|
||||||
|
Size : LongInt;
|
||||||
|
F : File;
|
||||||
|
Begin
|
||||||
|
Result := False;
|
||||||
|
|
||||||
|
FileMode := 66;
|
||||||
|
|
||||||
|
Assign (F, Str);
|
||||||
|
Reset (F, 1);
|
||||||
|
|
||||||
|
If IoResult <> 0 Then Exit;
|
||||||
|
|
||||||
|
Repeat
|
||||||
|
BlockRead (F, Buf, SizeOf(Buf), Size);
|
||||||
|
|
||||||
|
If Size = 0 Then Break;
|
||||||
|
|
||||||
|
If Buf[Size] = #26 Then Dec(Size);
|
||||||
|
|
||||||
|
WriteBuf (Buf, Size);
|
||||||
|
Until Size <> SizeOf(Buf);
|
||||||
|
|
||||||
|
Result := True;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TSocketClass.ProcessBuf (Var Buf: TSocketBuffer; Var Len: LongInt);
|
||||||
|
|
||||||
|
Procedure SendCommand (YesNo, CmdType: Char);
|
||||||
|
Var
|
||||||
|
Reply : String[3];
|
||||||
|
Begin
|
||||||
|
Reply[1] := Telnet_IAC;
|
||||||
|
Reply[2] := Char(YesNo); {DO/DONT, WILL/WONT}
|
||||||
|
Reply[3] := CmdType;
|
||||||
|
|
||||||
|
fpSend (FSocketHandle, @Reply[1], 3, FPSENDOPT);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure SendData (CmdType: Char; Data: String);
|
||||||
|
Var
|
||||||
|
Reply : String;
|
||||||
|
DataLen : Byte;
|
||||||
|
Begin
|
||||||
|
DataLen := Length(Data);
|
||||||
|
Reply[1] := Telnet_IAC;
|
||||||
|
Reply[2] := Telnet_SB;
|
||||||
|
Reply[3] := CmdType;
|
||||||
|
Reply[4] := #0;
|
||||||
|
|
||||||
|
Move (Data[1], Reply[5], DataLen);
|
||||||
|
|
||||||
|
Reply[5 + DataLen] := #0;
|
||||||
|
Reply[6 + DataLen] := Telnet_IAC;
|
||||||
|
Reply[7 + DataLen] := Telnet_SE;
|
||||||
|
|
||||||
|
fpSend (FSocketHandle, @Reply[1], 7 + DataLen, FPSENDOPT);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Var
|
||||||
|
Count : LongInt;
|
||||||
|
TempPos : LongInt;
|
||||||
|
Temp : TSocketBuffer;
|
||||||
|
ReplyGood : Char;
|
||||||
|
ReplyBad : Char;
|
||||||
|
Begin
|
||||||
|
TempPos := 0;
|
||||||
|
|
||||||
|
For Count := 0 to Len - 1 Do Begin
|
||||||
|
Case FTelnetState of
|
||||||
|
1 : If Buf[Count] = Telnet_IAC Then Begin
|
||||||
|
FTelnetState := 0;
|
||||||
|
Temp[TempPos] := Telnet_IAC;
|
||||||
|
Inc (TempPos);
|
||||||
|
End Else Begin
|
||||||
|
Inc (FTelnetState);
|
||||||
|
FTelnetCmd := Buf[Count];
|
||||||
|
End;
|
||||||
|
2 : Begin
|
||||||
|
FTelnetState := 0;
|
||||||
|
|
||||||
|
Case FTelnetCmd of
|
||||||
|
Telnet_WONT : Begin
|
||||||
|
// FTelnetSubCmd := Telnet_DONT;
|
||||||
|
// SockSend(FSocketHandle, FTelnetSubCmd, 1, 0);
|
||||||
|
End;
|
||||||
|
Telnet_DONT : Begin
|
||||||
|
// FTelnetSubCmd := Telnet_WONT;
|
||||||
|
// SockSend(FSocketHandle, FTelnetSubCmd, 1, 0);
|
||||||
|
End;
|
||||||
|
Telnet_SB : Begin
|
||||||
|
FTelnetState := 3;
|
||||||
|
FTelnetSubCmd := Buf[Count];
|
||||||
|
End;
|
||||||
|
Telnet_WILL,
|
||||||
|
Telnet_DO : Begin
|
||||||
|
If FTelnetCmd = Telnet_DO Then Begin
|
||||||
|
ReplyGood := Telnet_WILL;
|
||||||
|
ReplyBad := Telnet_WONT;
|
||||||
|
End Else Begin
|
||||||
|
ReplyGood := Telnet_DO;
|
||||||
|
ReplyBad := Telnet_DONT;
|
||||||
|
End;
|
||||||
|
|
||||||
|
If FTelnetClient Then Begin
|
||||||
|
Case Buf[Count] of
|
||||||
|
Telnet_BINARY,
|
||||||
|
Telnet_ECHO,
|
||||||
|
Telnet_SGA,
|
||||||
|
Telnet_TERM : SendCommand(ReplyGood, Buf[Count])
|
||||||
|
Else
|
||||||
|
SendCommand(ReplyBad, Buf[Count]);
|
||||||
|
End;
|
||||||
|
|
||||||
|
If Buf[Count] = Telnet_Echo Then
|
||||||
|
FTelnetEcho := (FTelnetCmd = Telnet_DO);
|
||||||
|
End Else Begin
|
||||||
|
Case Buf[Count] of
|
||||||
|
Telnet_ECHO : FTelnetEcho := True;
|
||||||
|
Telnet_SGA : ;
|
||||||
|
Else
|
||||||
|
SendCommand(ReplyBad, Buf[Count]);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
3 : If Buf[Count] = Telnet_SE Then Begin
|
||||||
|
If FTelnetClient Then
|
||||||
|
Case FTelnetSubCmd of
|
||||||
|
Telnet_TERM : SendData(Telnet_TERM, 'vt100');
|
||||||
|
End;
|
||||||
|
|
||||||
|
FTelnetState := 0;
|
||||||
|
FTelnetSubData := '';
|
||||||
|
End Else
|
||||||
|
FTelnetSubData := FTelnetSubData + Buf[Count];
|
||||||
|
Else
|
||||||
|
If Buf[Count] = Telnet_IAC Then Begin
|
||||||
|
Inc (FTelnetState);
|
||||||
|
End Else Begin
|
||||||
|
Temp[TempPos] := Buf[Count];
|
||||||
|
Inc (TempPos);
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Buf := Temp;
|
||||||
|
Len := TempPos;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TSocketClass.ReadChar : Char;
|
||||||
|
Var
|
||||||
|
Ch : Char;
|
||||||
|
Begin
|
||||||
|
ReadBuf(Ch, 1);
|
||||||
|
Result := Ch;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TSocketClass.ReadBuf (Var Buf; Len: LongInt) : LongInt;
|
||||||
|
Begin
|
||||||
|
If FInBufPos = FInBufEnd Then Begin
|
||||||
|
FInBufEnd := fpRecv(FSocketHandle, @FInBuf, TSocketBufferSize, FPRECVOPT);
|
||||||
|
FInBufPos := 0;
|
||||||
|
If FInBufEnd <= 0 Then Begin
|
||||||
|
FInBufEnd := 0;
|
||||||
|
Result := -1;
|
||||||
|
Exit;
|
||||||
|
End;
|
||||||
|
|
||||||
|
If FTelnetClient or FTelnetServer Then ProcessBuf(FInBuf, FInBufEnd);
|
||||||
|
End;
|
||||||
|
|
||||||
|
If Len > FInBufEnd - FInBufPos Then Len := FInBufEnd - FInBufPos;
|
||||||
|
|
||||||
|
Move (FInBuf[FInBufPos], Buf, Len);
|
||||||
|
Inc (FInBufPos, Len);
|
||||||
|
|
||||||
|
Result := Len;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TSocketClass.ReadLine (Var Str: String) : LongInt;
|
||||||
|
Var
|
||||||
|
Ch : Char;
|
||||||
|
Res : LongInt;
|
||||||
|
Begin
|
||||||
|
Str := '';
|
||||||
|
Res := 0;
|
||||||
|
|
||||||
|
Repeat
|
||||||
|
If FInBufPos = FInBufEnd Then Res := ReadBuf(Ch, 0);
|
||||||
|
|
||||||
|
Ch := FInBuf[FInBufPos];
|
||||||
|
Inc (FInBufPos);
|
||||||
|
If (Ch <> #10) And (Ch <> #13) And (FInBufEnd > 0) Then Str := Str + Ch;
|
||||||
|
Until (Ch = #10) Or (Res < 0) Or (FInBufEnd = 0);
|
||||||
|
|
||||||
|
If Res < 0 Then Result := -1 Else Result := Length(Str);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TSocketClass.SetBlocking (Block: Boolean): LongInt;
|
||||||
|
//Var
|
||||||
|
// Data : DWord;
|
||||||
|
Begin
|
||||||
|
If FSocketHandle = -1 Then Begin
|
||||||
|
Result := FSocketHandle;
|
||||||
|
Exit;
|
||||||
|
End;
|
||||||
|
|
||||||
|
// Data := Ord(Not Block);
|
||||||
|
// Result := ioctlSocket(FSocketHandle, FIONBIO, Data);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TSocketClass.WaitForData (TimeOut: LongInt) : LongInt;
|
||||||
|
Var
|
||||||
|
T : TTimeVal;
|
||||||
|
rFDSET,
|
||||||
|
wFDSET,
|
||||||
|
eFDSET : TFDSet;
|
||||||
|
Begin
|
||||||
|
T.tv_sec := 0;
|
||||||
|
T.tv_usec := TimeOut * 1000;
|
||||||
|
|
||||||
|
{$IFDEF UNIX}
|
||||||
|
fpFD_Zero(rFDSET);
|
||||||
|
fpFD_Zero(wFDSET);
|
||||||
|
fpFD_Zero(eFDSET);
|
||||||
|
fpFD_Set(FSocketHandle, rFDSET);
|
||||||
|
Result := fpSelect(FSocketHandle + 1, @rFDSET, @wFDSET, @eFDSET, @T);
|
||||||
|
{$ELSE}
|
||||||
|
FD_Zero(rFDSET);
|
||||||
|
FD_Zero(wFDSET);
|
||||||
|
FD_Zero(eFDSET);
|
||||||
|
FD_Set(FSocketHandle, rFDSET);
|
||||||
|
Result := Select(FSocketHandle + 1, @rFDSET, @wFDSET, @eFDSET, @T);
|
||||||
|
{$ENDIF}
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TSocketClass.ResolveAddress (Host: String) : LongInt;
|
||||||
|
Var
|
||||||
|
HostEnt : PHostEnt;
|
||||||
|
Begin
|
||||||
|
Host := Host + #0;
|
||||||
|
HostEnt := GetHostByName(@Host[1]);
|
||||||
|
|
||||||
|
If Assigned(HostEnt) Then
|
||||||
|
Result := PInAddr(HostEnt^.h_addr_list^)^.S_addr
|
||||||
|
Else
|
||||||
|
Result := LongInt(StrToNetAddr(Host));
|
||||||
|
// Result := NetAddrToStr(@Host[1]);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TSocketClass.Connect (Address: String; Port: Word) : Boolean;
|
||||||
|
Var
|
||||||
|
Sin : TINetSockAddr;
|
||||||
|
Begin
|
||||||
|
Result := False;
|
||||||
|
FSocketHandle := fpSocket(PF_INET, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
If FSocketHandle = -1 Then Exit;
|
||||||
|
|
||||||
|
FPeerName := Address;
|
||||||
|
|
||||||
|
FillChar(Sin, SizeOf(Sin), 0);
|
||||||
|
|
||||||
|
Sin.sin_Family := PF_INET;
|
||||||
|
Sin.sin_Port := htons(Port);
|
||||||
|
Sin.sin_Addr.S_Addr := ResolveAddress(Address);
|
||||||
|
|
||||||
|
FPeerIP := NetAddrToStr(Sin.Sin_Addr);
|
||||||
|
Result := fpConnect(FSocketHandle, @Sin, SizeOf(Sin)) = 0;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TSocketClass.WaitInit (Port: Word);
|
||||||
|
Var
|
||||||
|
SIN : TINetSockAddr;
|
||||||
|
Begin
|
||||||
|
FSocketHandle := fpSocket(PF_INET, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
SIN.sin_family := PF_INET;
|
||||||
|
SIN.sin_addr.s_addr := 0;
|
||||||
|
SIN.sin_port := htons(Port);
|
||||||
|
|
||||||
|
fpBind(FSocketHandle, @SIN, SizeOf(SIN));
|
||||||
|
|
||||||
|
SetBlocking(True);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TSocketClass.WaitConnection : TSocketClass;
|
||||||
|
Var
|
||||||
|
Sock : LongInt;
|
||||||
|
Client : TSocketClass;
|
||||||
|
PHE : PHostEnt;
|
||||||
|
SIN : TINetSockAddr;
|
||||||
|
Temp : LongInt;
|
||||||
|
Begin
|
||||||
|
Result := NIL;
|
||||||
|
|
||||||
|
If fpListen(FSocketHandle, 5) = -1 Then Exit;
|
||||||
|
|
||||||
|
Temp := SizeOf(SIN);
|
||||||
|
Sock := fpAccept(FSocketHandle, @SIN, @Temp);
|
||||||
|
|
||||||
|
If Sock = -1 Then Exit;
|
||||||
|
|
||||||
|
FPeerIP := NetAddrToStr(SIN.sin_addr);
|
||||||
|
|
||||||
|
PHE := GetHostByAddr(@SIN.sin_addr, 4, PF_INET);
|
||||||
|
If Not Assigned(PHE) Then
|
||||||
|
FPeerName := 'Unknown'
|
||||||
|
Else
|
||||||
|
FPeerName := StrPas(PHE^.h_name);
|
||||||
|
|
||||||
|
fpGetSockName(FSocketHandle, @SIN, SizeOf(SIN));
|
||||||
|
|
||||||
|
FHostIP := NetAddrToStr(SIN.sin_addr);
|
||||||
|
Client := TSocketClass.Create;
|
||||||
|
|
||||||
|
Client.SocketHandle := Sock;
|
||||||
|
Client.PeerName := FPeerName;
|
||||||
|
Client.PeerIP := FPeerIP;
|
||||||
|
Client.PeerPort := FPort;
|
||||||
|
Client.HostIP := FHostIP;
|
||||||
|
Client.FTelnetServer := FTelnetServer;
|
||||||
|
Client.FTelnetClient := FTelnetClient;
|
||||||
|
|
||||||
|
If FTelnetServer Then
|
||||||
|
Client.WriteStr(#255#251#001#255#251#003); // IAC WILL ECHO
|
||||||
|
|
||||||
|
Result := Client;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TSocketClass.Status (Str: String);
|
||||||
|
Var
|
||||||
|
Res : String;
|
||||||
|
Begin
|
||||||
|
Try
|
||||||
|
If SocketStatus.Count > 20 Then
|
||||||
|
SocketStatus.Delete(0);
|
||||||
|
|
||||||
|
Res := '(' + Copy(DateDos2Str(CurDateDos, 1), 1, 5) + ' ' + TimeDos2Str(CurDateDos, False) + ') ' + Str;
|
||||||
|
|
||||||
|
If Length(Res) > 74 Then Begin
|
||||||
|
SocketStatus.Add(Copy(Res, 1, 74));
|
||||||
|
|
||||||
|
If SocketStatus.Count > 20 Then
|
||||||
|
SocketStatus.Delete(0);
|
||||||
|
|
||||||
|
SocketStatus.Add(strRep(' ', 14) + Copy(Res, 75, 255));
|
||||||
|
End Else
|
||||||
|
SocketStatus.Add(Res);
|
||||||
|
Except
|
||||||
|
{ ignore exceptions here -- happens when socketstatus is NIL}
|
||||||
|
{ need to review criticals now that they are in FP's RTL}
|
||||||
|
End;
|
||||||
|
|
||||||
|
StatusUpdated := True;
|
||||||
|
End;
|
||||||
|
|
||||||
|
End.
|
|
@ -0,0 +1,268 @@
|
||||||
|
{$I M_OPS.PAS}
|
||||||
|
|
||||||
|
Unit m_Socket_Server;
|
||||||
|
|
||||||
|
Interface
|
||||||
|
|
||||||
|
Uses
|
||||||
|
Classes,
|
||||||
|
m_Socket_Class;
|
||||||
|
|
||||||
|
Type
|
||||||
|
TServerManager = Class;
|
||||||
|
TServerClient = Class;
|
||||||
|
TServerCreateProc = Function (Manager: TServerManager; Client: TSocketClass): TServerClient;
|
||||||
|
|
||||||
|
TServerManager = Class(TThread)
|
||||||
|
Server : TSocketClass;
|
||||||
|
ClientList : TList;
|
||||||
|
NewClientProc : TServerCreateProc;
|
||||||
|
ClientMax : LongInt;
|
||||||
|
ClientMaxIPs : LongInt;
|
||||||
|
ClientRefused : LongInt;
|
||||||
|
ClientBlocked : LongInt;
|
||||||
|
ClientTotal : LongInt;
|
||||||
|
ClientActive : LongInt;
|
||||||
|
Port : LongInt;
|
||||||
|
TextPath : String[80];
|
||||||
|
|
||||||
|
Constructor Create (PortNum, Max: Word; CreateProc: TServerCreateProc);
|
||||||
|
Destructor Destroy; Override;
|
||||||
|
Procedure Execute; Override;
|
||||||
|
Function CheckIP (IP, Mask: String) : Boolean;
|
||||||
|
Function IsBlockedIP (Var Client: TSocketClass) : Boolean;
|
||||||
|
Function DuplicateIPs (Var Client: TSocketClass) : Byte;
|
||||||
|
// Procedure Status (Str: String);
|
||||||
|
End;
|
||||||
|
|
||||||
|
TServerClient = Class(TThread)
|
||||||
|
Client : TSocketClass;
|
||||||
|
Manager : TServerManager;
|
||||||
|
|
||||||
|
Constructor Create (Owner: TServerManager; CliSock: TSocketClass);
|
||||||
|
Destructor Destroy; Override;
|
||||||
|
End;
|
||||||
|
|
||||||
|
//TServerTextClient = Class(TServerClient)
|
||||||
|
//End;
|
||||||
|
|
||||||
|
Implementation
|
||||||
|
|
||||||
|
Uses
|
||||||
|
m_Strings,
|
||||||
|
m_DateTime;
|
||||||
|
|
||||||
|
Constructor TServerManager.Create (PortNum, Max: Word; CreateProc: TServerCreateProc);
|
||||||
|
Var
|
||||||
|
Count : Byte;
|
||||||
|
Begin
|
||||||
|
Inherited Create(False);
|
||||||
|
|
||||||
|
Port := PortNum;
|
||||||
|
ClientMax := Max;
|
||||||
|
ClientRefused := 0;
|
||||||
|
ClientBlocked := 0;
|
||||||
|
ClientTotal := 0;
|
||||||
|
ClientActive := 0;
|
||||||
|
ClientMaxIPs := 0;
|
||||||
|
NewClientProc := CreateProc;
|
||||||
|
Server := TSocketClass.Create;
|
||||||
|
ClientList := TList.Create;
|
||||||
|
TextPath := '';
|
||||||
|
|
||||||
|
For Count := 1 to ClientMax Do
|
||||||
|
ClientList.Add(NIL);
|
||||||
|
|
||||||
|
FreeOnTerminate := False;
|
||||||
|
End;
|
||||||
|
|
||||||
|
(*
|
||||||
|
Procedure TServerManager.Status (Str: String);
|
||||||
|
Begin
|
||||||
|
If Server.SocketStatus = NIL Then
|
||||||
|
While Server.SocketStatus = NIL Do Begin
|
||||||
|
WriteLn('ITS NIL');
|
||||||
|
End;
|
||||||
|
|
||||||
|
Server.Status(Str);
|
||||||
|
End;
|
||||||
|
*)
|
||||||
|
|
||||||
|
Function TServerManager.CheckIP (IP, Mask: String) : Boolean;
|
||||||
|
Var
|
||||||
|
A : Byte;
|
||||||
|
Count : Byte;
|
||||||
|
Str : String;
|
||||||
|
Str2 : String;
|
||||||
|
EndIt : Byte;
|
||||||
|
Begin
|
||||||
|
Result := True;
|
||||||
|
|
||||||
|
For Count := 1 to 4 Do Begin
|
||||||
|
If Count < 4 Then Begin
|
||||||
|
Str := Copy(IP, 1, Pos('.', IP) - 1);
|
||||||
|
Str2 := Copy(Mask, 1, Pos('.', Mask) - 1);
|
||||||
|
Delete (IP, 1, Pos('.', IP));
|
||||||
|
Delete (Mask, 1, Pos('.', Mask));
|
||||||
|
End Else Begin
|
||||||
|
Str := Copy(IP, 1, Length(IP));
|
||||||
|
Str2 := Copy(Mask, 1, Length(Mask));
|
||||||
|
End;
|
||||||
|
|
||||||
|
For A := 1 to Length(Str) Do
|
||||||
|
If Str2[A] = '*' Then
|
||||||
|
Break
|
||||||
|
Else
|
||||||
|
If Str[A] <> Str2[A] Then Begin
|
||||||
|
Result := False;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
|
||||||
|
If Not Result Then Break;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TServerManager.IsBlockedIP (Var Client: TSocketClass) : Boolean;
|
||||||
|
Var
|
||||||
|
TF : Text;
|
||||||
|
Str : String;
|
||||||
|
Begin
|
||||||
|
Result := False;
|
||||||
|
FileMode := 66;
|
||||||
|
|
||||||
|
Assign (TF, TextPath + 'badip.txt');
|
||||||
|
Reset (TF);
|
||||||
|
|
||||||
|
If IoResult <> 0 Then Exit;
|
||||||
|
|
||||||
|
While Not Eof(TF) Do Begin
|
||||||
|
ReadLn (TF, Str);
|
||||||
|
If CheckIP (Client.PeerIP, Str) Then Begin
|
||||||
|
Result := True;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Close (TF);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Function TServerManager.DuplicateIPs (Var Client: TSocketClass) : Byte;
|
||||||
|
Var
|
||||||
|
Count : Byte;
|
||||||
|
Begin
|
||||||
|
Result := 0;
|
||||||
|
|
||||||
|
For Count := 0 to ClientMax - 1 Do
|
||||||
|
If ClientList[Count] <> NIL Then
|
||||||
|
If Client.PeerIP = TSocketClass(ClientList[Count]).PeerIP Then
|
||||||
|
Inc(Result);
|
||||||
|
End;
|
||||||
|
|
||||||
|
Procedure TServerManager.Execute;
|
||||||
|
Var
|
||||||
|
NewClient : TSocketClass;
|
||||||
|
Begin
|
||||||
|
Repeat Until Server <> NIL; // Synchronize with server class
|
||||||
|
Repeat Until Server.SocketStatus <> NIL; // Syncronize with status class
|
||||||
|
|
||||||
|
Server.WaitInit(Port);
|
||||||
|
|
||||||
|
If Terminated Then Exit;
|
||||||
|
|
||||||
|
Server.Status('Opening server socket on port ' + strI2S(Port));
|
||||||
|
|
||||||
|
Repeat
|
||||||
|
NewClient := Server.WaitConnection;
|
||||||
|
|
||||||
|
If NewClient = NIL Then Break; // time to shutdown the server...
|
||||||
|
|
||||||
|
If (ClientMax > 0) And (ClientActive >= ClientMax) Then Begin
|
||||||
|
Inc (ClientRefused);
|
||||||
|
Server.Status ('BUSY: ' + NewClient.PeerIP + ' (' + NewClient.PeerName + ')');
|
||||||
|
If Not NewClient.WriteFile(TextPath + 'busy.txt') Then NewClient.WriteLine('BUSY');
|
||||||
|
NewClient.Free;
|
||||||
|
End Else
|
||||||
|
If IsBlockedIP(NewClient) Then Begin
|
||||||
|
Inc (ClientBlocked);
|
||||||
|
Server.Status('BLOCK: ' + NewClient.PeerIP + ' (' + NewClient.PeerName + ')');
|
||||||
|
If Not NewClient.WriteFile(TextPath + 'blocked.txt') Then NewClient.WriteLine('BLOCKED');
|
||||||
|
NewClient.Free;
|
||||||
|
End Else
|
||||||
|
If (ClientMaxIPs > 0) and (DuplicateIPs(NewClient) > ClientMaxIPs) Then Begin
|
||||||
|
Inc (ClientRefused);
|
||||||
|
Server.Status('MULTI: ' + NewClient.PeerIP + ' (' + NewClient.PeerName + ')');
|
||||||
|
If Not NewClient.WriteFile(TextPath + 'dupeip.txt') Then NewClient.WriteLine('Only ' + strI2S(ClientMaxIPs) + ' connection(s) per user');
|
||||||
|
NewClient.Free;
|
||||||
|
End Else Begin
|
||||||
|
Inc (ClientTotal);
|
||||||
|
Inc (ClientActive);
|
||||||
|
Server.Status ('Connect: ' + NewClient.PeerIP + ' (' + NewClient.PeerName + ')');
|
||||||
|
NewClientProc(Self, NewClient);
|
||||||
|
End;
|
||||||
|
Until Terminated;
|
||||||
|
|
||||||
|
Server.Status ('Shutting down server...');
|
||||||
|
End;
|
||||||
|
|
||||||
|
Destructor TServerManager.Destroy;
|
||||||
|
Var
|
||||||
|
Count : LongInt;
|
||||||
|
Angry : Byte;
|
||||||
|
Begin
|
||||||
|
Angry := 20; // about 5 seconds before we get mad at thread...
|
||||||
|
|
||||||
|
ClientList.Pack;
|
||||||
|
|
||||||
|
While (ClientList.Count > 0) and (Angry > 0) Do Begin
|
||||||
|
For Count := 0 To ClientList.Count - 1 Do
|
||||||
|
If ClientList[Count] <> NIL Then Begin
|
||||||
|
TServerClient(ClientList[Count]).Client.Disconnect;
|
||||||
|
TServerClient(ClientList[Count]).Terminate;
|
||||||
|
End;
|
||||||
|
|
||||||
|
WaitMS(250);
|
||||||
|
|
||||||
|
Dec (Angry);
|
||||||
|
|
||||||
|
ClientList.Pack;
|
||||||
|
End;
|
||||||
|
|
||||||
|
ClientList.Free;
|
||||||
|
Server.Free;
|
||||||
|
|
||||||
|
Inherited Destroy;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Constructor TServerClient.Create (Owner: TServerManager; CliSock: TSocketClass);
|
||||||
|
Var
|
||||||
|
Count : Byte;
|
||||||
|
Begin
|
||||||
|
Manager := Owner;
|
||||||
|
Client := CliSock;
|
||||||
|
|
||||||
|
For Count := 0 to Manager.ClientMax - 1 Do
|
||||||
|
If Manager.ClientList[Count] = NIL Then Begin
|
||||||
|
Manager.ClientList[Count] := Self;
|
||||||
|
Break;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Inherited Create(False);
|
||||||
|
|
||||||
|
FreeOnTerminate := True;
|
||||||
|
End;
|
||||||
|
|
||||||
|
Destructor TServerClient.Destroy;
|
||||||
|
Begin
|
||||||
|
Client.Free;
|
||||||
|
|
||||||
|
Manager.ClientList[Manager.ClientList.IndexOf(Self)] := NIL;
|
||||||
|
|
||||||
|
If Manager.Server <> NIL Then
|
||||||
|
Manager.Server.StatusUpdated := True;
|
||||||
|
|
||||||
|
Dec (Manager.ClientActive);
|
||||||
|
|
||||||
|
Inherited Destroy;
|
||||||
|
End;
|
||||||
|
|
||||||
|
End.
|
Loading…
Reference in New Issue