{
    $Id$
    This file is part of the Free Pascal run time library.
    Copyright (c) 1993,97 by Michael Van Canneyt,
    member of the Free Pascal development team.

    See the file COPYING.FPC, included in this distribution,
    for details about the copyright.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 **********************************************************************}
unit Crt;
Interface

Const
{ Controlling consts }
  Flushing=false;                       {if true then don't buffer output}
  ScreenWidth  = 80;
  ScreenHeight = 25;

{ CRT modes }
  BW40          = 0;            { 40x25 B/W on Color Adapter }
  CO40          = 1;            { 40x25 Color on Color Adapter }
  BW80          = 2;            { 80x25 B/W on Color Adapter }
  CO80          = 3;            { 80x25 Color on Color Adapter }
  Mono          = 7;            { 80x25 on Monochrome Adapter }
  Font8x8       = 256;          { Add-in for ROM font }

{ Mode constants for 3.0 compatibility }
  C40           = CO40;
  C80           = CO80;

{ Foreground and background color constants }
  Black         = 0;
  Blue          = 1;
  Green         = 2;
  Cyan          = 3;
  Red           = 4;
  Magenta       = 5;
  Brown         = 6;
  LightGray     = 7;

{ Foreground color constants }
  DarkGray      = 8;
  LightBlue     = 9;
  LightGreen    = 10;
  LightCyan     = 11;
  LightRed      = 12;
  LightMagenta  = 13;
  Yellow        = 14;
  White         = 15;

{ Add-in for blinking }
  Blink         = 128;

{Other Defaults}

  TextAttr   : Byte = $07;
  LastMode   : Word = 3;
  WindMin    : Word = $0;
  WindMax    : Word = $184f;
var
  CheckBreak,
  CheckEOF,
  CheckSnow,
  DirectVideo: Boolean;

Procedure AssignCrt(Var F: Text);
Function  KeyPressed: Boolean;
Function  ReadKey: Char;
Procedure TextMode(Mode: Integer);
Procedure Window(X1, Y1, X2, Y2: Byte);
Procedure GoToXy(X: Byte; Y: Byte);
Function  WhereX: Byte;
Function  WhereY: Byte;
Procedure ClrScr;
Procedure ClrEol;
Procedure InsLine;
Procedure DelLine;
Procedure TextColor(Color: Byte);
Procedure TextBackground(Color: Byte);
Procedure LowVideo;
Procedure HighVideo;
Procedure NormVideo;
Procedure Delay(DTime: Word);
Procedure Sound(Hz: Word);
Procedure NoSound;


Implementation

uses Linux;

{
  The definitions of TextRec and FileRec are in separate files.
}
{$i textrec.inc}
{$i filerec.inc}

Const
  OldTextAttr : byte = $07;

Type
  TScreen=Array[1..ScreenHeight,1..ScreenWidth] of char;
  TScreenColors=Array[1..ScreenHeight,1..ScreenWidth] of byte;

Var
  Scrn        : TScreen;
  ScrnCol     : TScreenColors;
  CurrX,CurrY : Byte;
  ExitSave    : Pointer;

{*****************************************************************************
                    Some Handy Functions Not in the System.PP
*****************************************************************************}

Function Str(l:longint):string;
{
  Return a String of the longint
}
var
  hstr : string[32];
begin
  System.Str(l,hstr);
  Str:=hstr;
end;



Function Max(l1,l2:longint):longint;
{
  Return the maximum of l1 and l2
}
begin
  if l1>l2 then
   Max:=l1
  else
   Max:=l2;
end;



Function Min(l1,l2:longint):longint;
{
  Return the minimum of l1 and l2
}
begin
  if l1<l2 then
   Min:=l1
  else
   Min:=l2;
end;


{*****************************************************************************
                      Optimal AnsiString Conversion Routines
*****************************************************************************}

Function XY2Ansi(x,y,ox,oy:byte):String;
{
  Returns a string with the escape sequences to go to X,Y on the screen
}
Begin
  if y=oy then
   begin
     if x=ox then
      begin
        XY2Ansi:='';
        exit;
      end;
     if x=1 then
      begin
        XY2Ansi:=#13;
        exit;
      end;
     if x>ox then
      begin
        XY2Ansi:=#27'['+Str(x-ox)+'C';
        exit;
      end
     else
      begin
        XY2Ansi:=#27'['+Str(ox-x)+'D';
        exit;
      end;
   end;
  if x=ox then
   begin
     if y>oy then
      begin
        XY2Ansi:=#27'['+Str(y-oy)+'B';
        exit;
      end
     else
      begin
        XY2Ansi:=#27'['+Str(oy-y)+'A';
        exit;
      end;
   end;
  if (x=1) and (oy+1=y) then
   XY2Ansi:=#13#10
  else
   XY2Ansi:=#27'['+Str(y)+';'+Str(x)+'H';
End;



const
  AnsiTbl : string[8]='04261537';
Function Attr2Ansi(Attr,OAttr:byte):string;
{
  Convert Attr to an Ansi String, the Optimal code is calculate
  with use of the old OAttr
}
var
  hstr : string[16];
  OFg,OBg,Fg,Bg : byte;

  procedure AddSep(ch:char);
  begin
    if length(hstr)>0 then
     hstr:=hstr+';';
    hstr:=hstr+ch;
  end;

begin
  if Attr=OAttr then
   begin
     Attr2Ansi:='';
     exit;
   end;
  Hstr:='';
  Fg:=Attr and $f;
  Bg:=Attr shr 4;
  OFg:=Attr and $f;
  OBg:=Attr shr 4;
  if (OFg<>7) or (Fg=7) or ((OFg>7) and (Fg<8)) or ((OBg>7) and (Bg<8)) then
   begin
     hstr:='0';
     OFg:=7;
     OBg:=0;
   end;
  if (Fg>7) and (OFg<8) then
   begin
     AddSep('1');
     OFg:=OFg or 8;
   end;
  if (Bg and 8)<>(OBg and 8) then
   begin
     AddSep('5');
     OBg:=OBg or 8;
   end;
  if (Fg<>OFg) then
   begin
     AddSep('3');
     hstr:=hstr+AnsiTbl[(Fg and 7)+1];
   end;
  if (Bg<>OBg) then
   begin
     AddSep('4');
     hstr:=hstr+AnsiTbl[(Bg and 7)+1];
   end;
  if hstr='0' then
   hstr:='';
  Attr2Ansi:=#27'['+hstr+'m';
end;



Function Ansi2Attr(Const HStr:String;oattr:byte):byte;
{
  Convert an Escape sequence to an attribute value, uses Oattr as the last
  color written
}
var
  i,j : byte;
begin
  i:=2;
  if (Length(HStr)<3) or (Hstr[1]<>#27) or (Hstr[2]<>'[') then
   i:=255;
  while (i<length(Hstr)) do
   begin
     inc(i);
     case Hstr[i] of
      '0' : OAttr:=7;
      '1' : OAttr:=OAttr or $8;
      '5' : OAttr:=OAttr or $80;
      '3' : begin
              inc(i);
              j:=pos(Hstr[i],AnsiTbl);
              if j>0 then
               OAttr:=(OAttr and $f8) or (j-1);
            end;
      '4' : begin
              inc(i);
              j:=pos(Hstr[i],AnsiTbl);
              if j>0 then
               OAttr:=(OAttr and $8f) or ((j-1) shl 4);
            end;
      'm' : i:=length(HStr);
     end;
   end;
  Ansi2Attr:=OAttr;
end;



{*****************************************************************************
                          Buffered StdIn/StdOut IO
*****************************************************************************}

const
  ttyIn=0;  {Handles for stdin/stdout}
  ttyOut=1;
  ttyFlush:boolean=true;
{Buffered Input/Output}
  InSize=256;
  OutSize=1024;
var
  InBuf  : array[0..InSize-1] of char;
  InCnt,
  InHead,
  InTail : longint;
  OutBuf : array[0..OutSize-1] of char;
  OutCnt : longint;


{Flush Output Buffer}
procedure ttyFlushOutput;
begin
  if OutCnt>0 then
   begin
     fdWrite(ttyOut,OutBuf,OutCnt);
     OutCnt:=0;
   end;
end;



Function ttySetFlush(b:boolean):boolean;
begin
  ttySetFlush:=ttyFlush;
  ttyFlush:=b;
  if ttyFlush then
   ttyFlushOutput;
end;


{Send Char to Remote}
Procedure ttySendChar(c:char);
Begin
  if OutCnt<OutSize then
   begin
     OutBuf[OutCnt]:=c;
     inc(OutCnt);
   end;
{Full ?}
  if (OutCnt>=OutSize) then
   ttyFlushOutput;
End;



{Send String to Remote}
procedure ttySendStr(const hstr:string);
var
  i : word;
begin
  for i:=1to length(hstr) do
   ttySendChar(hstr[i]);
  if ttyFlush then
   ttyFlushOutput;
end;



{Get Char from Remote}
function ttyRecvChar:char;
var
  Readed,i : word;
begin
{Buffer Empty? Yes, Input from StdIn}
  if (InHead=InTail) then
   begin
   {Calc Amount of Chars to Read}
     i:=InSize-InHead;
     if InTail>InHead then
      i:=InTail-InHead;
   {Read}
     Readed:=fdRead(TTYIn,InBuf[InHead],i);
   {Increase Counters}
     inc(InCnt,Readed);
     inc(InHead,Readed);
   {Wrap if End has Reached}
     if InHead>=InSize then
      InHead:=0;
   end;
{Check Buffer}
  if (InCnt=0) then
   ttyRecvChar:=#0
  else
   begin
     ttyRecvChar:=InBuf[InTail];
     dec(InCnt);
     inc(InTail);
     if InTail>=InSize then
      InTail:=0;
   end;
end;


{*****************************************************************************
                       Screen Routines not Window Depended
*****************************************************************************}

procedure ttyGotoXY(x,y:byte);
{
  Goto XY on the Screen, if a value is 0 the goto the current
  postion of that value and always recalc the ansicode for it
}
begin
  if x=0 then
   begin
     x:=CurrX;
     CurrX:=$ff;
   end;
  if y=0 then
   begin
     y:=CurrY;
     CurrY:=$ff;
   end;
  ttySendStr(XY2Ansi(x,y,CurrX,CurrY));
  CurrX:=x;
  CurrY:=y;
end;



procedure ttyColor(a:byte);
{
  Set Attribute to A, only output if not the last attribute is set
}
begin
  if a<>TextAttr then
   begin
     ttySendStr(Attr2Ansi(a,TextAttr));
     TextAttr:=a;
     OldTextAttr:=a;
   end;
end;



procedure ttyWrite(const s:string);
{
  Write a string to the output, memory copy and Current X&Y are also updated
}
var
  i : word;
begin
  ttySendStr(s);
{Update MemCopy}
  for i:=1to length(s) do
   begin
     Scrn[CurrY,CurrX]:=s[i];
     ScrnCol[CurrY,CurrX]:=TextAttr;
     inc(CurrX);
     if CurrX>ScreenWidth then
      CurrX:=ScreenWidth;
   end;
end;



Function WinMinX: Byte;
{
  Current Minimum X coordinate
}
Begin
  WinMinX:=(WindMin and $ff)+1;
End;



Function WinMinY: Byte;
{
  Current Minimum Y Coordinate
}
Begin
  WinMinY:=(WindMin shr 8)+1;
End;



Function WinMaxX: Byte;
{
  Current Maximum X coordinate
}
Begin
  WinMaxX:=(WindMax and $ff)+1;
End;



Function WinMaxY: Byte;
{
  Current Maximum Y coordinate;
}
Begin
  WinMaxY:=(WindMax shr 8) + 1;
End;



Function FullWin:boolean;
{
  Full Screen 80x25? Window(1,1,80,25) is used, allows faster routines
}
begin
  FullWin:=(WindMax-WindMin=$184f);
end;


procedure LineWrite(temp:String);
{
  Write a Line to the screen, doesn't write on 80,25 under Dos
  the Current CurrX is set to WinMax. NO MEMORY UPDATE!
}
begin
  CurrX:=WinMaxX+1;
  if (CurrX>=ScreenWidth) then
   CurrX:=WinMaxX;
  ttySendStr(Temp);
end;



procedure DoEmptyLine(y,xl,xh:byte);
{
  Write an Empty line at Row Y from Col Xl to XH, Memory is also updated
}
begin
  ttyGotoXY(xl,y);
  LineWrite(Space(xh-xl+1));
  FillChar(Scrn[y],ScreenWidth,' ');
  FillChar(ScrnCol[y],ScreenWidth,TextAttr);
end;



procedure DoScrollLine(y1,y2,xl,xh:byte);
{
  Move Line y1 to y2, use only columns Xl-Xh, Memory is updated also
}
var
  Temp    : string;
  OldAttr,
  x,attr : byte;
begin
  ttyGotoXY(xl,y2);
  OldAttr:=$ff;
  Temp:='';
  For x:=xl To xh Do
   Begin
     attr:=ScrnCol[y1,x];
     if attr<>OldAttr then
      begin
        temp:=temp+Attr2Ansi(Attr,OldAttr);
        OldAttr:=Attr;
      end;
     Temp:=Temp+Scrn[y1,x];
     if (x=xh) or (length(Temp)>240) then
      begin
        LineWrite(Temp);
        Temp:='';
      end;
   End;
{Update memory copy}
  Move(Scrn[y1,1],Scrn[y2,1],ScreenWidth);
  Move(ScrnCol[y1,1],ScrnCol[y2,1],ScreenWidth);
end;



Procedure TextColor(Color: Byte);
{
  Switch foregroundcolor
}
Begin
  ttyColor((Color and $8f) or (TextAttr and $70));
End;



Procedure TextBackground(Color: Byte);
{
  Switch backgroundcolor
}
Begin
  ttyColor((Color shl 4) or (TextAttr and $0f));
End;



Procedure HighVideo;
{
  Set highlighted output.
}
Begin
  TextColor(TextAttr Or $08);
End;



Procedure LowVideo;
{
  Set normal output
}
Begin
  TextColor(TextAttr And $77);
End;



Procedure NormVideo;
{
  Set normal back and foregroundcolors.
}
Begin
  TextColor(7);
  TextBackGround(0);
End;



Procedure GotoXy(X: Byte; Y: Byte);
{
  Go to coordinates X,Y in the current window.
}
Begin
  If (X>0) and (X<=WinMaxX- WinMinX+1) and
     (Y>0) and (Y<=WinMaxY-WinMinY+1) Then
   Begin
     Inc(X,WinMinX-1);
     Inc(Y,WinMinY-1);
     ttyGotoXY(x,y);
   End;
End;



Procedure Window(X1, Y1, X2, Y2: Byte);
{
  Set screen window to the specified coordinates.
}
Begin
  if (X1>X2) or (X2>ScreenWidth) or
     (Y1>Y2) or (Y2>ScreenHeight) then
   exit;
  WindMin:=((Y1-1) Shl 8)+(X1-1);
  WindMax:=((Y2-1) Shl 8)+(X2-1);
  GoToXY(1,1);
End;



Procedure ClrScr;
{
  Clear the current window, and set the cursor on x1,y1
}
Var
  CY        : Integer;
  oldflush  : boolean;
Begin
  oldflush:=ttySetFlush(Flushing);
  if FullWin then
   begin
     ttySendStr(#27'[H'#27'[2J');
     CurrX:=1;
     CurrY:=1;
     FillChar(Scrn,sizeof(Scrn),' ');
     FillChar(ScrnCol,sizeof(ScrnCol),TextAttr);
   end
  else
   begin
     For Cy:=WinMinY To WinMaxY Do
      DoEmptyLine(Cy,WinMinX,WinMaxX);
     GoToXY(1,1);
   end;
  ttySetFlush(oldflush);
End;



Procedure ClrEol;
{
  Clear from current position to end of line.
}
Begin
  if FullWin then
   ttySendStr(#27'[K')
  else

   begin

     ttySendStr(Space(WinMaxX-CurrX));
     ttyGotoXY(0,CurrY);
   end;

End;



Function WhereX: Byte;
{
  Return current X-position of cursor.
}
Begin
  WhereX:=CurrX-WinMinX+1;
End;



Function WhereY: Byte;
{
  Return current Y-position of cursor.
}
Begin
  WhereY:=CurrY-WinMinY+1;
End;



Procedure ScrollScrnRegionUp(xl,yl,xh,yh, count: Byte);
{
  Scroll the indicated region count lines up. The empty lines are filled
  with blanks in the current color. The screen position is restored
  afterwards.
}
Var
  y,oldx,oldy : byte;
  oldflush    : boolean;
Begin
  oldflush:=ttySetFlush(Flushing);
  oldx:=CurrX;
  oldy:=CurrY;
{Scroll}
  For y:=yl to yh-count do
   DoScrollLine(y+count,y,xl,xh);
{Restore TextAttr}
  ttySendStr(Attr2Ansi(TextAttr,$ff));
{Fill the rest with empty lines}
  for y:=yh-count+1 to yh do
   DoEmptyLine(y,xl,xh);
{Restore current position}
  ttyGotoXY(OldX,OldY);
  ttySetFlush(oldflush);
End;



Procedure ScrollScrnRegionDown(xl,yl,xh,yh, count: Byte);
{
  Scroll the indicated region count lines down. The empty lines are filled
  with blanks in the current color. The screen position is restored
  afterwards.
}
Var
  y,oldx,oldy : byte;
  oldflush    : boolean;
Begin
  oldflush:=ttySetFlush(Flushing);
  oldx:=CurrX;
  oldy:=CurrY;
{Scroll}
  for y:=yh downto yl+count do
   DoScrollLine(y-count,y,xl,xh);
{Restore TextAttr}
  ttySendStr(Attr2Ansi(TextAttr,$ff));
{Fill the rest with empty lines}
  for y:=yl to yl+count-1 do
   DoEmptyLine(y,xl,xh);
{Restore current position}
  ttyGotoXY(OldX,OldY);
  ttySetFlush(oldflush);
End;



Procedure ScrollWindow(xl,yl,xh,yh : Byte; count: LongInt);
{
  Scroll the indicated region up or down, depending on the sign
  of count.
}
begin
  If ((xl>xh) or (xh>ScreenWidth)) or
     ((yl>yh) or (yl>ScreenHeight)) or
     (abs(Count)>yh-yl+1) then
   exit;
  If count<0 then
   ScrollScrnRegionDown (xl,yl,xh,yh,abs(count))
  else
   ScrollScrnRegionUp (xl,yl,xh,yh,count);
end;



{*************************************************************************
                            KeyBoard
*************************************************************************}

Const
  KeyBufferSize = 20;
var
  KeyBuffer : Array[0..KeyBufferSize-1] of Char;
  KeyPut,
  KeySend   : Byte;

Procedure PushKey(Ch:char);
Var
  Tmp : Word;
Begin
  Tmp:=KeyPut;
  Inc(KeyPut);
  If KeyPut>=KeyBufferSize Then
   KeyPut:=0;
  If KeyPut<>KeySend Then
   KeyBuffer[Tmp]:=Ch
  Else
   KeyPut:=Tmp;
End;



Function PopKey:char;
Begin
  If KeyPut<>KeySend Then
   Begin
     PopKey:=KeyBuffer[KeySend];
     Inc(KeySend);
     If KeySend>=KeyBufferSize Then
      KeySend:=0;
   End
  Else
   PopKey:=#0;
End;



Procedure PushExt(b:byte);
begin
  PushKey(#0);
  PushKey(chr(b));
end;



const
  AltKeyStr  : string[38]='qwertyuiopasdfghjklzxcvbnm1234567890-=';
  AltCodeStr : string[38]=#016#017#018#019#020#021#022#023#024#025#030#031#032#033#034#035#036#037#038+
                          #044#045#046#047#048#049#050#120#121#122#123#124#125#126#127#128#129#130#131;
Function FAltKey(ch:char):byte;
var
  Idx : byte;
Begin
  Idx:=Pos(ch,AltKeyStr);
  if Idx>0 then
   FAltKey:=byte(AltCodeStr[Idx])
  else
   FAltKey:=0;
End;



Function KeyPressed:Boolean;
var
  fdsin : fdSet;
Begin
  if (KeySend<>KeyPut) or (InCnt>0) then
   KeyPressed:=true
  else
   begin
     FD_Zero(fdsin);
     fd_Set(TTYin,fdsin);
     Keypressed:=(Select(TTYIn+1,@fdsin,nil,nil,0)>0);
   end;
End;



Function ReadKey:char;
Var
  ch       : char;
  OldState,
  State    : Word;
Begin
{Check Buffer first}
  if KeySend<>KeyPut then
   begin
     ReadKey:=PopKey;
     exit;
   end;
{Wait for Key}
  repeat
  until keypressed;
  ch:=ttyRecvChar;
{Esc Found ?}
  If (ch=#27) then
   begin
     State:=1;
     Delay(10);
     while (State<>0) and (KeyPressed) do
      begin
        ch:=ttyRecvChar;
        OldState:=State;
        State:=0;
        case OldState of
        1 : begin {Esc}
              case ch of
          'a'..'z',
          '0'..'9',
           '-','=' : PushExt(FAltKey(ch));
               #10 : PushKey(#10);
               '[' : State:=2;
               else
                begin
                  PushKey(ch);
                  PushKey(#27);
                end;
               end;
            end;
        2 : begin {Esc[}
              case ch of
               '[' : State:=3;
               'A' : PushExt(72);
               'B' : PushExt(80);
               'C' : PushExt(77);
               'D' : PushExt(75);
               'G' : PushKey('5');
               'H' : PushExt(71);
               'K' : PushExt(79);
               '1' : State:=4;
               '2' : State:=5;
               '3' : PushExt(83);
               '4' : PushExt(79);
               '5' : PushExt(73);
               '6' : PushExt(81);
              else
               begin
                 PushKey(ch);
                 PushKey('[');
                 PushKey(#27);
               end;
              end;
              if ch in ['3'..'6'] then
               State:=255;
            end;
        3 : begin {Esc[[}
              case ch of
               'A' : PushExt(59);
               'B' : PushExt(60);
               'C' : PushExt(61);
               'D' : PushExt(62);
               'E' : PushExt(63);
              end;
            end;
        4 : begin
              case ch of
               '~' : PushExt(71);
               '7' : PushExt(64);
               '8' : PushExt(65);
               '9' : PushExt(66);
              end;
              if (Ch<>'~') then
               State:=255;
            end;
        5 : begin
              case ch of
               '~' : PushExt(82);
               '0' : pushExt(67);
               '1' : PushExt(68);
               '3' : PushExt(133);
               '4' : PushExt(134);
              end;
              if (Ch<>'~') then
               State:=255;
            end;
      255 : ;
        end;
        if State<>0 then
         Delay(10);
      end;
     if State=1 then
      PushKey(ch);

   end
  else
   Begin
     case ch of
     #127 : PushExt(83);
     else
      PushKey(ch);
     end;
   End;
  ReadKey:=PopKey;
End;



Procedure Delay(DTime: Word);
{
  Wait for DTime milliseconds.
}
Begin
  Select(0,nil,nil,nil,DTime);
End;


{****************************************************************************
                          HighLevel Crt Functions
****************************************************************************}

procedure DoLn;
begin
  if CurrY=WinMaxY then
   begin
     if FullWin then
      begin
        ttySendStr(#10#13);
        CurrX:=WinMinX;
        CurrY:=WinMaxY;
      end
     else
      begin
        ScrollScrnRegionUp(WinMinX,WinMinY,WinMaxX,WinMaxY,1);
        ttyGotoXY(WinMinX,WinMaxY);
      end;
   end
  else
   ttyGotoXY(WinMinX,CurrY+1);
end;


var
  Lastansi  : boolean;
  AnsiCode  : string[32];
Procedure DoWrite(const s:String);
{
  Write string to screen, parse most common AnsiCodes
}
var
  found,
  OldFlush  : boolean;
  x,y       : byte;
  i,j,
  SendBytes : word;

  function AnsiPara(var hstr:string):byte;
  var
    k,j  : byte;
    code : word;
  begin
    j:=pos(';',hstr);
    if j=0 then
     j:=length(hstr);
    val(copy(hstr,3,j-3),k,code);
    Delete(hstr,3,j-2);
    if k=0 then
     k:=1;
    AnsiPara:=k;
  end;

  procedure SendText;
  var
    LeftX : word;
  begin
    while (SendBytes>0) do
     begin
       LeftX:=WinMaxX-CurrX+1;
       if (SendBytes>LeftX) or (CurrX+SendBytes=81) then
        begin
          ttyWrite(Copy(s,i-SendBytes,LeftX));
          dec(SendBytes,LeftX);
          DoLn;
        end
       else
        begin
          ttyWrite(Copy(s,i-SendBytes,SendBytes));
          SendBytes:=0;
        end;
     end;
  end;

begin
  oldflush:=ttySetFlush(Flushing);
{ Support textattr:= changing }

  if OldTextAttr<>TextAttr then
   begin
     i:=TextAttr;
     TextAttr:=OldTextAttr;
     ttyColor(i);
   end;
{ write the stuff }

  SendBytes:=0;
  i:=1;
  while (i<=length(s)) do
   begin
     if (s[i]=#27) or (LastAnsi) then
      begin
        SendText;
        LastAnsi:=false;
        j:=i;
        found:=false;
        while (j<=length(s)) and (not found) do
         begin
           found:=not (s[j] in [#27,'[','0'..'9',';','?']);
           inc(j);
         end;
        Ansicode:=AnsiCode+Copy(s,i,j-i);
        if found then
         begin
           case AnsiCode[length(AnsiCode)] of
            'm' : ttyColor(Ansi2Attr(AnsiCode,TextAttr));
            'H' : begin {No other way :( Coz First Para=Y}
                    y:=AnsiPara(AnsiCode);
                    x:=AnsiPara(AnsiCode);
                    GotoXY(y,x);
                  end;
            'J' : if AnsiPara(AnsiCode)=2 then
                   ClrScr;
            'K' : ClrEol;
            'A' : GotoXY(CurrX,Max(CurrY-AnsiPara(AnsiCode),WinMinY));
            'B' : GotoXY(CurrX,Min(CurrY+AnsiPara(AnsiCode),WinMaxY));
            'C' : GotoXY(Min(CurrX+AnsiPara(AnsiCode),WinMaxX),CurrY);
            'D' : GotoXY(Max(CurrX-AnsiPara(AnsiCode),WinMinX),CurrY);
            'h' : ; {Stupid Thedraw [?7h Code}
           else
            found:=false;
           end;
         end
        else
         begin
           LastAnsi:=true;
           found:=true;
         end;
      {Clear AnsiCode?}
        if not LastAnsi then
         AnsiCode:='';
      {Increase Idx or SendBytes}
        if found then
         i:=j-1
        else
         inc(SendBytes);
      end
     else
      begin
        LastAnsi:=false;
        case s[i] of
         #13 : begin {CR}
                 SendText;
                 ttyGotoXY(WinMinX,CurrY);
               end;
         #10 : begin {NL}
                 SendText;
                 DoLn;
               end;
          #9 : begin {Tab}
                SendText;
                ttyWrite(Space(9-((CurrX-1) and $08)));
              end;
        else
         inc(SendBytes);
        end;
      end;
     inc(i);
   end;
  if SendBytes>0 then
   SendText;
  ttySetFlush(oldFLush);
end;



Function CrtWrite(Var F: TextRec): Integer;

{
  Top level write function for CRT
}
Var
  Temp     : String;
Begin
  Move(F.BufPTR^[0],Temp[1],F.BufPos);
  temp[0]:=chr(F.BufPos);
  DoWrite(Temp);
  F.BufPos:=0;
  CrtWrite:=0;
End;



Function CrtClose(Var F: TextRec): Integer;

{
  Close CRT associated file.
}
Begin
  F.Mode:=fmClosed;
  CrtClose:=0;
End;



Function CrtOpen(Var F: TextRec): Integer;

{
  Open CRT associated file.
}
Begin
  If F.Mode = fmOutput Then
   CrtOpen:=0
  Else
   CrtOpen:=5;
End;



Function CrtRead(Var F: TextRec): Integer;

{
  Read from CRT associated file.
}
Begin
  F.BufEnd:=fdRead(LongInt(F.Handle), F.BufPtr^, F.BufSize);
  F.BufPos:=F.BufEnd;
  CrtWrite(F);
  CrtRead:=0;
End;



Function CrtInOut(Var F: TextRec): Integer;

{
  InOut function for CRT associated file.
}
Begin
  Case F.Mode of
   fmInput: CrtInOut:=CrtRead(F);
   fmOutput: CrtInOut:=CrtWrite(F);
  End;
End;



Procedure AssignCrt(Var F: Text);
{
  Assign a file to the console. All output on file goes to console instead.
}
Begin
  TextRec(F).Mode:=fmClosed;
  TextRec(F).BufSize:=SizeOf(TextBuf);
  TextRec(F).BufPtr:=@TextRec(F).Buffer;
  TextRec(F).BufPos:=0;
  TextRec(F).OpenFunc:=@CrtOpen;
  TextRec(F).InOutFunc:=@CrtInOut;
  TextRec(F).FlushFunc:=@CrtWrite;
  TextRec(F).CloseFunc:=@CrtClose;
  TextRec(F).Name[0]:='.';
  TextRec(F).Name[1]:=#0;
End;



Procedure DelLine;
{
  Delete current line. Scroll subsequent lines up
}
Begin
  ScrollScrnRegionUp(WinMinX, CurrY, WinMaxX, WinMaxY, 1);
End;



Procedure InsLine;
{
  Insert line at current cursor position. Scroll subsequent lines down.
}
Begin
  ScrollScrnRegionDown(WinMinX, CurrY, WinMaxX, WinMaxY, 1);
End;



Procedure Sound(Hz: Word);
{
  Does nothing under linux
}
begin
end;



Procedure NoSound;
{
  Does nothing under linux
}
begin
end;



Procedure TextMode(Mode: Integer);
{
  Only Clears Screen under linux
}
begin
  ClrScr;
end;


{******************************************************************************
                               Initialization
******************************************************************************}

var
  OldIO : TermIos;
Procedure SetRawMode(b:boolean);
Var
  Tio : Termios;
Begin
  if b then
   begin
     TCGetAttr(1,Tio);
     OldIO:=Tio;
     CFMakeRaw(Tio);
     Tio.C_IFlag:=Tio.C_IFlag or ICRNL;
   end
  else
   Tio:=OldIO;
  TCSetAttr(1,TCSANOW,Tio);

End;



procedure GetXY(var x,y:byte);
var
  fds    : fdSet;
  i,j,
  readed : longint;
  buf    : array[0..255] of char;
  s      : string[16];
begin
  x:=0;
  y:=0;
  s:=#27'[6n';
  fdWrite(0,s[1],length(s));
  FD_Zero(fds);
  FD_Set(1,fds);
  if (Select(2,@fds,nil,nil,1000)>0) then
   begin
     readed:=fdRead(1,buf,sizeof(buf));
     i:=0;
     while (i+4<readed) and (buf[i]<>#27) and (buf[i+1]<>'[') do
      inc(i);
     if i+4<readed then
      begin
        s[1]:=#16;
        move(buf[i+2],s[1],16);
        i:=Pos(';',s);
        if i>0 then
         begin
           Val(Copy(s,1,i-1),y);
           j:=Pos('R',s);
           if j=0 then
            j:=length(s)+1;
           Val(Copy(s,i+1,j-i),x);
         end;
      end;
   end;
end;



Procedure CrtExit;

{
  We need to restore normal keyboard mode upon exit !!
}
Begin
  ttyFlushOutput;
  SetRawMode(False);
  ExitProc:=ExitSave;
End;



Begin
{Hook Exit}
  ExitSave:=ExitProc;
  ExitProc:=@CrtExit;
{Assign Input and Output to Crt}
  AssignCrt(Output);
  AssignCrt(Input);
  TextRec(Output).Mode:=fmOutput;
  TextRec(Input).Mode:=fmInput;
{Set default Terminal Settings}
  SetRawMode(True);
{Get Current X&Y or Reset to Home}

  GetXY(CurrX,CurrY);
  if (CurrX=0) then
   begin
     CurrX:=1;
     CurrY:=1;
     ttySendStr(#27'[H');
   end;

{Reset Attribute (TextAttr=7 at startup)}

  ttySendStr(#27'[m');
End.
{
  $Log$
  Revision 1.1  1998-03-25 11:18:43  root
  Initial revision

  Revision 1.10  1998/03/16 23:38:52  peter
    + support for textattr:= setting between writes

  Revision 1.9  1998/01/26 12:00:45  michael
  + Added log at the end

  revision 1.8
  date: 1998/01/20 00:18:14;  author: peter;  state: Exp;  lines: +2 -2
    * column 80 works now the same as under dos (wraps to next line)
  ----------------------------
  revision 1.7
  date: 1998/01/19 10:04:02;  author: michael;  state: Exp;  lines: +2 -2
  * Bugfix from Peter Vreman.
  ----------------------------
  revision 1.6
  date: 1998/01/05 13:43:17;  author: michael;  state: Exp;  lines: +129 -109
  * Minor bugfixes, bugs appeared when making/compiling examples.
    (by Peter Vreman)
  ----------------------------
  revision 1.5
  date: 1997/12/28 17:53:04;  author: michael;  state: Exp;  lines: +52 -4
  * Bug fixed : CR now sends CR/LF; importat for Readln. (Peter Vreman fixed
    this)
  * GetXY function now tries to read initial conditions.
  ----------------------------
  revision 1.4
  date: 1997/12/19 15:22:14;  author: michael;  state: Exp;  lines: +10 -22
  * changed setrawmode to use termios functions in linux unit.
  ----------------------------
  revision 1.3
  date: 1997/12/15 12:44:56;  author: michael;  state: Exp;  lines: +6 -1
  * added key handling for xterm.
  ----------------------------
  revision 1.2
  date: 1997/12/01 12:31:14;  author: michael;  state: Exp;  lines: +12 -16
  + Added copyright reference in header.
  ----------------------------
  revision 1.1
  date: 1997/11/27 08:33:54;  author: michael;  state: Exp;
  Initial revision
  ----------------------------
  revision 1.1.1.1
  date: 1997/11/27 08:33:54;  author: michael;  state: Exp;  lines: +0 -0
  FPC RTL CVS start
  =============================================================================


  Pre CVS Log:


  Version    Date       What/who
  -------    ----       -------

  0.1        96/97      Written by Mark May (mmay@ndaco.com)
                        Major overhaul to improve screen output performance by

                        Michael Van Canneyt  (michael@tfdec1.fys.kuleuven.ac.be)
                        Also added some documentation.
                        Balazs Scheidler changed color handling to lookup-table
  0.2        7/97       changes by  Hans-Peter Zorn <hzorn@aixterm1.urz.uni-heidelberg.de>
                        to make key-handling work in an xterm.
                        Alt-key becomes ESC + Key.

  0.3        10/97      Modified by Matthias K"oppe

                        <mkoeppe@csmd.cs.uni-magdeburg.de> :
                        Fixed key decoding for function keys F6..F12


  0.4        11/97      Modified by Peter Vreman <pfv@worldonline.nl>
                        Optimised keyboard handling.


  0.5        11/97      Modified by Peter Vreman <pfv@worldonline.nl>
                        Inplemented bigger buffer for output, ANSI codes
                        are handled too.

                        Unit now depends on linux unit only.

  0.6        01/98      Modified by Peter Vreman
                        Implemented missing function/procedures, the interface
                        is now 100% the same BP7.
                        The screen is not cleared at startup anymore. The
                        cursor stays at the current position.
}
