1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660 |
- {
- This file is part of the Free Pascal run time library.
- Copyright (c) 1999-2000 by Michael Van Canneyt and Peter Vreman,
- members 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
- {$i crth.inc}
- Const
- { Controlling consts }
- Flushing = false; {if true then don't buffer output}
- ConsoleMaxX = 1024;
- ConsoleMaxY = 1024;
- ScreenHeight : longint = 25;
- ScreenWidth : longint = 80;
- Type
- TCharAttr=packed record
- ch : char;
- attr : byte;
- end;
- TConsoleBuf=Array[0..ConsoleMaxX*ConsoleMaxY-1] of TCharAttr;
- PConsoleBuf=^TConsoleBuf;
- var
- ConsoleBuf : PConsoleBuf;
- Implementation
- uses BaseUnix ,unix, termio;
- {
- The definitions of TextRec and FileRec are in separate files.
- }
- {$i textrec.inc}
- Const
- OldTextAttr : byte = $07;
- Var
- CurrX,CurrY : Byte;
- OutputRedir, InputRedir : boolean; { is the output/input being redirected (not a TTY) }
- {$ifdef debugcrt}
- DebugFile : Text;
- {$endif}
- {*****************************************************************************
- Some Handy Functions Not in the System.PP
- *****************************************************************************}
- {$ifdef debugcrt}
- Procedure Debug(Msg : string);
- begin
- Writeln(DebugFile,Msg);
- end;
- {$endif}
- 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:longint):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
- // this workaround should improve behaviour on some terminals.
- // debian bug 216057 but I also observed this with video on FreeBSD
- if x=screenwidth then
- XY2Ansi:=#27'['+Str(y)+';'+Str(x)+'H'
- else
- // end workaround
- XY2Ansi:='';
- exit;
- end;
- {$ifdef Linux} // linux CRT shortcut
- if x=1 then
- begin
- XY2Ansi:=#13;
- exit;
- end;
- {$endif}
- 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;
- {$ifdef Linux} // this shortcut isn't for everybody
- if (x=1) and (oy+1=y) then
- XY2Ansi:=#13#10
- else
- {$endif}
- XY2Ansi:=#27'['+Str(y)+';'+Str(x)+'H';
- End;
- const
- AnsiTbl : string[8]='04261537';
- Function Attr2Ansi(Attr,OAttr:longint):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 : longint;
- 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:=OAttr and $f;
- OBg:=OAttr 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:longint):longint;
- {
- Convert an Escape sequence to an attribute value, uses Oattr as the last
- color written
- }
- var
- i,j : longint;
- 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
- fpWrite(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 : longint;
- 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 : longint;
- 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:=fpread(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:longint);
- {
- 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;
- if OutputRedir then
- begin
- if longint(y)-longint(CurrY)=1 then
- ttySendStr(#10);
- end
- else
- ttySendStr(XY2Ansi(x,y,CurrX,CurrY));
- CurrX:=x;
- CurrY:=y;
- end;
- procedure ttyColor(a:longint);
- {
- Set Attribute to A, only output if not the last attribute is set
- }
- begin
- if a<>OldTextAttr then
- begin
- if not OutputRedir then
- ttySendStr(Attr2Ansi(a,OldTextAttr));
- 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
- idx,i : longint;
- begin
- ttySendStr(s);
- {Update MemCopy}
- idx:=(CurrY-1)*ScreenWidth-1;
- for i:=1 to length(s) do
- if s[i]=#8 then
- begin
- if CurrX>1 then
- dec(CurrX);
- end
- else
- begin
- ConsoleBuf^[idx+CurrX].ch:=s[i];
- ConsoleBuf^[idx+CurrX].attr:=TextAttr;
- inc(CurrX);
- If CurrX>ScreenWidth then
- CurrX:=$FF; // Mark as invalid.
- end;
- end;
- Function FullWin:boolean;
- {
- Full Screen 80x25? Window(1,1,80,25) is used, allows faster routines
- }
- begin
- FullWin:=(WindMinX=1) and (WindMinY=1) and
- (WindMaxX=ScreenWidth) and (WindMaxY=ScreenHeight);
- end;
- procedure LineWrite(const temp:String);
- {
- Write a Line to the screen, doesn't write on 80,25 under Dos
- the Current CurrX is set to WindMax. NO MEMORY UPDATE!
- }
- begin
- CurrX:=WindMaxX+1;
- ttySendStr(Temp);
- end;
- Procedure DoEmptyLine(y,xl,xh:Longint);
- {
- Write an empty line at row Y from column Xl to Xh. Memory is also updated.
- }
- Var
- len : Longint;
- blank_with_attribute : TCharAttr;
- Begin
- ttyGotoXY(xl,y);
- len:=xh-xl+1;
- LineWrite(Space(len));
- blank_with_attribute.ch:=' ';
- blank_with_attribute.attr:=TextAttr;
- FillWord(ConsoleBuf^[(y-1)*ScreenWidth+xl-1],len,word(blank_with_attribute));
- End;
- procedure DoScrollLine(y1,y2,xl,xh:longint);
- {
- Move Line y1 to y2, use only columns Xl-Xh, Memory is updated also
- }
- var
- Temp : string;
- idx,
- OldAttr,
- x,attr : longint;
- begin
- ttyGotoXY(xl,y2);
- { precalc ConsoleBuf[] y-offset }
- idx:=(y1-1)*ScreenWidth-1;
- { update screen }
- OldAttr:=$ff;
- Temp:='';
- For x:=xl To xh Do
- Begin
- attr:=ConsoleBuf^[idx+x].attr;
- if (attr<>OldAttr) and (not OutputRedir) then
- begin
- temp:=temp+Attr2Ansi(Attr,OldAttr);
- OldAttr:=Attr;
- end;
- Temp:=Temp+ConsoleBuf^[idx+x].ch;
- if (x=xh) or (length(Temp)>240) then
- begin
- LineWrite(Temp);
- Temp:='';
- end;
- End;
- {Update memory copy}
- Move(ConsoleBuf^[(y1-1)*ScreenWidth+xl-1],ConsoleBuf^[(y2-1)*ScreenWidth+xl-1],(xh-xl+1)*2);
- end;
- Procedure TextColor(Color: Byte);
- {
- Switch foregroundcolor
- }
- var AddBlink : byte;
- Begin
- If (Color>15) Then
- AddBlink:=Blink
- else
- AddBlink:=0;
- ttyColor((Color and $f) or (TextAttr and $70) or AddBlink);
- End;
- Procedure TextBackground(Color: Byte);
- {
- Switch backgroundcolor
- }
- Begin
- TextAttr:=((Color shl 4) and ($f0 and not Blink)) or (TextAttr and ($0f OR Blink));
- ttyColor(TextAttr);
- 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: tcrtcoord; Y: tcrtcoord);
- {
- Go to coordinates X,Y in the current window.
- }
- Begin
- If (X>0) and (X<=WindMaxX- WindMinX+1) and
- (Y>0) and (Y<=WindMaxY-WindMinY+1) Then
- Begin
- Inc(X,WindMinX-1);
- Inc(Y,WindMinY-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;
- WindMinX:=X1;
- WindMaxX:=X2;
- WindMinY:=Y1;
- WindMaxY:=Y2;
- 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 1,1
- }
- Var
- CY,i : Longint;
- oldflush : boolean;
- blank_with_attribute : TCharAttr;
- Begin
- { See if color has changed }
- if OldTextAttr<>TextAttr then
- begin
- i:=TextAttr;
- TextAttr:=OldTextAttr;
- ttyColor(i);
- end;
- oldflush:=ttySetFlush(Flushing);
- if FullWin then
- begin
- if not OutputRedir then
- ttySendStr(#27'[H'#27'[2J');
- CurrX:=1;
- CurrY:=1;
- blank_with_attribute.ch := ' ';
- blank_with_attribute.attr := TextAttr;
- FillWord(ConsoleBuf^,ScreenWidth*ScreenHeight,word(blank_with_attribute));
- end
- else
- begin
- For Cy:=WindMinY To WindMaxY Do
- DoEmptyLine(Cy,WindMinX,WindMaxX);
- GoToXY(1,1);
- end;
- ttySetFlush(oldflush);
- End;
- Procedure ClrEol;
- {
- Clear from current position to end of line.
- }
- var
- len,i : longint;
- IsLastLine : boolean;
- Begin
- { See if color has changed }
- if OldTextAttr<>TextAttr then
- begin
- i:=TextAttr;
- TextAttr:=OldTextAttr;
- ttyColor(i);
- end;
- if FullWin or (WindMaxX = ScreenWidth) then
- begin
- if not OutputRedir then
- ttySendStr(#27'[K');
- end
- else
- begin
- { Tweak WindMaxx and WindMaxy so no scrolling happends }
- len:=WindMaxX-CurrX+1;
- IsLastLine:=false;
- if CurrY=WindMaxY then
- begin
- inc(WindMaxX,3);
- inc(WindMaxY,2);
- IsLastLine:=true;
- end;
- ttySendStr(Space(len));
- if IsLastLine then
- begin
- dec(WindMaxX,3);
- dec(WindMaxY,2);
- end;
- ttyGotoXY(0,0);
- end;
- End;
- Function WhereX: tcrtcoord;
- {
- Return current X-position of cursor.
- }
- Begin
- WhereX:=CurrX-WindMinX+1;
- End;
- Function WhereY: tcrtcoord;
- {
- Return current Y-position of cursor.
- }
- Begin
- WhereY:=CurrY-WindMinY+1;
- End;
- Procedure ScrollScrnRegionUp(xl,yl,xh,yh, count: longint);
- {
- 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: longint);
- {
- 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;
- {*************************************************************************
- KeyBoard
- *************************************************************************}
- Const
- KeyBufferSize = 20;
- var
- KeyBuffer : Array[0..KeyBufferSize-1] of Char;
- KeyPut,
- KeySend : longint;
- Procedure PushKey(Ch:char);
- Var
- Tmp : Longint;
- 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 : longint;
- Begin
- Idx:=Pos(ch,AltKeyStr);
- if Idx>0 then
- FAltKey:=byte(AltCodeStr[Idx])
- else
- FAltKey:=0;
- End;
- { This one doesn't care about keypresses already processed by readkey }
- { and waiting in the KeyBuffer, only about waiting keypresses at the }
- { TTYLevel (including ones that are waiting in the TTYRecvChar buffer) }
- function sysKeyPressed: boolean;
- var
- fdsin : tfdSet;
- begin
- if (InCnt>0) then
- sysKeyPressed:=true
- else
- begin
- fpFD_ZERO(fdsin);
- fpFD_SET(TTYin,fdsin);
- sysKeypressed:=(fpSelect(TTYIn+1,@fdsin,nil,nil,0)>0);
- end;
- end;
- Function KeyPressed:Boolean;
- Begin
- Keypressed := (KeySend<>KeyPut) or sysKeyPressed;
- End;
- Function ReadKey:char;
- Var
- ch : char;
- OldState,
- State : longint;
- FDS : TFDSet;
- Begin
- {Check Buffer first}
- if KeySend<>KeyPut then
- begin
- ReadKey:=PopKey;
- exit;
- end;
- {Wait for Key}
- { Only if none are waiting! (JM) }
- if not sysKeyPressed then
- begin
- FpFD_ZERO (FDS);
- fpFD_SET (0,FDS);
- fpSelect (1,@FDS,nil,nil,nil);
- end;
- ch:=ttyRecvChar;
- {Esc Found ?}
- CASE ch OF
- #27: begin
- State:=1;
- Delay(10);
- { This has to be sysKeyPressed and not "keyPressed", since after }
- { one iteration keyPressed will always be true because of the }
- { pushKey commands (JM) }
- while (State<>0) and (sysKeyPressed) 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;
- {$IFDEF Unix}
- 'O': State:=7;
- {$ENDIF}
- 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);
- {$IFDEF FREEBSD}
- {'E' - Center key, not handled in DOS TP7}
- 'F' : PushExt(79); {End}
- 'G': PushExt(81); {PageDown}
- {$ELSE}
- 'G' : PushKey('5'); {Center key, Linux}
- {$ENDIF}
- 'H' : PushExt(71);
- {$IFDEF FREEBSD}
- 'I' : PushExt(73); {PageUp}
- {$ENDIF}
- 'K' : PushExt(79);
- {$IFDEF FREEBSD}
- 'L' : PushExt(82); {Insert - Deekoo}
- 'M' : PushExt(59); {F1-F10 - Deekoo}
- 'N' : PushExt(60); {F2}
- 'O' : PushExt(61); {F3}
- 'P' : PushExt(62); {F4}
- 'Q' : PushExt(63); {F5}
- 'R' : PushExt(64); {F6}
- 'S' : PushExt(65); {F7}
- 'T' : PushExt(66); {F8}
- 'U' : PushExt(67); {F9}
- 'V' : PushExt(68); {F10}
- {Not sure if TP/BP handles F11 and F12 like this normally;
- In pcemu, a TP7 executable handles 'em this way, though.}
- 'W' : PushExt(133); {F11}
- 'X' : PushExt(134); {F12}
- 'Y' : PushExt(84); {Shift-F1}
- 'Z' : PushExt(85); {Shift-F2}
- 'a' : PushExt(86); {Shift-F3}
- 'b' : PushExt(87); {Shift-F4}
- 'c' : PushExt(88); {Shift-F5}
- 'd' : PushExt(89); {Shift-F6}
- 'e' : PushExt(90); {Shift-F7}
- 'f' : PushExt(91); {Shift-F8}
- 'g' : PushExt(92); {Shift-F9}
- 'h' : PushExt(93); {Shift-F10}
- 'i' : PushExt(135); {Shift-F11}
- 'j' : PushExt(136); {Shift-F12}
- 'k' : PushExt(94); {Ctrl-F1}
- 'l' : PushExt(95);
- 'm' : PushExt(96);
- 'n' : PushExt(97);
- 'o' : PushExt(98);
- 'p' : PushExt(99);
- 'q' : PushExt(100);
- 'r' : PushExt(101);
- 's' : PushExt(102);
- 't' : PushExt(103); {Ctrl-F10}
- 'u' : PushExt(137); {Ctrl-F11}
- 'v' : PushExt(138); {Ctrl-F12}
- {$ENDIF}
- '1' : State:=4;
- '2' : State:=5;
- '3' : State:=6;
- '4' : PushExt(79);
- '5' : PushExt(73);
- '6' : PushExt(81);
- else
- begin
- PushKey(ch);
- PushKey('[');
- PushKey(#27);
- end;
- end;
- if ch in ['4'..'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 {Esc[1}
- case ch of
- '~' : PushExt(71);
- '5' : State := 8;
- '7' : PushExt(64);
- '8' : PushExt(65);
- '9' : PushExt(66);
- end;
- if not (Ch in ['~', '5']) then
- State:=255;
- end;
- 5 : begin {Esc[2}
- case ch of
- '~' : PushExt(82);
- '0' : pushExt(67);
- '1' : PushExt(68);
- '3' : PushExt(133); {F11}
- {Esc[23~ is also shift-F1,shift-F11}
- '4' : PushExt(134); {F12}
- {Esc[24~ is also shift-F2,shift-F12}
- '5' : PushExt(86); {Shift-F3}
- '6' : PushExt(87); {Shift-F4}
- '8' : PushExt(88); {Shift-F5}
- '9' : PushExt(89); {Shift-F6}
- end;
- if (Ch<>'~') then
- State:=255;
- end;
- 6 : begin {Esc[3}
- case ch of
- '~' : PushExt(83); {Del}
- '1' : PushExt(90); {Shift-F7}
- '2' : PushExt(91); {Shift-F8}
- '3' : PushExt(92); {Shift-F9}
- '4' : PushExt(93); {Shift-F10}
- end;
- if (Ch<>'~') then
- State:=255;
- end;
- {$ifdef Unix}
- 7 : begin {Esc[O}
- case ch of
- 'A' : PushExt(72);
- 'B' : PushExt(80);
- 'C' : PushExt(77);
- 'D' : PushExt(75);
- 'P' : PushExt(59);
- 'Q' : PushExt(60);
- 'R' : PushExt(61);
- 'S' : PushExt(62);
- end;
- end;
- {$endif}
- 8 : begin {Esc[15}
- case ch of
- '~' : PushExt(63);
- end;
- end;
- 255 : ;
- end;
- if State<>0 then
- Delay(10);
- end;
- if State=1 then
- PushKey(ch);
- end;
- #127: PushKey(#8);
- else PushKey(ch);
- End;
- ReadKey:=PopKey;
- End;
- Procedure Delay(MS: Word);
- {
- Wait for DTime milliseconds.
- }
- Begin
- fpSelect(0,nil,nil,nil,MS);
- End;
- {****************************************************************************
- Write(ln)/Read(ln) support
- ****************************************************************************}
- procedure DoLn;
- begin
- if CurrY=WindMaxY then
- begin
- if FullWin then
- begin
- ttySendStr(#10#13);
- CurrX:=WindMinX;
- CurrY:=WindMaxY;
- end
- else
- begin
- ScrollScrnRegionUp(WindMinX,WindMinY,WindMaxX,WindMaxY,1);
- ttyGotoXY(WindMinX,WindMaxY);
- end;
- end
- else
- ttyGotoXY(WindMinX,CurrY+1);
- end;
- var
- Lastansi : boolean;
- AnsiCode : string;
- Procedure DoWrite(const s:String);
- {
- Write string to screen, parse most common AnsiCodes
- }
- var
- found,
- OldFlush : boolean;
- x,y,
- i,j,
- SendBytes : longint;
- function AnsiPara(var hstr:string):byte;
- var
- k,j : longint;
- 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 : longint;
- begin
- while (SendBytes>0) do
- begin
- LeftX:=WindMaxX-CurrX+1;
- if (SendBytes>=LeftX) 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(x,y);
- end;
- 'J' : if AnsiPara(AnsiCode)=2 then
- ClrScr;
- 'K' : ClrEol;
- 'A' : GotoXY(CurrX,Max(CurrY-AnsiPara(AnsiCode),WindMinY));
- 'B' : GotoXY(CurrX,Min(CurrY+AnsiPara(AnsiCode),WindMaxY));
- 'C' : GotoXY(Min(CurrX+AnsiPara(AnsiCode),WindMaxX),CurrY);
- 'D' : GotoXY(Max(CurrX-AnsiPara(AnsiCode),WindMinX),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(WindMinX,CurrY);
- end;
- #10 : begin {NL}
- SendText;
- DoLn;
- end;
- #9 : begin {Tab}
- SendText;
- ttyWrite(Space(9-((CurrX-1) and $08)));
- end;
- #8 : begin {BackSpace}
- SendText;
- ttyWrite(#8);
- 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;
- idx,i : Longint;
- oldflush : boolean;
- Begin
- oldflush:=ttySetFlush(Flushing);
- idx:=0;
- while (F.BufPos>0) do
- begin
- i:=F.BufPos;
- if i>255 then
- i:=255;
- Move(F.BufPTR^[idx],Temp[1],i);
- SetLength(Temp,i);
- DoWrite(Temp);
- dec(F.BufPos,i);
- inc(idx,i);
- end;
- ttySetFlush(oldFLush);
- CrtWrite:=0;
- End;
- Function CrtRead(Var F: TextRec): Integer;
- {
- Read from CRT associated file.
- }
- var
- c : char;
- i : longint;
- Begin
- if isATTY(F.Handle)=1 then
- begin
- F.BufPos := 0;
- i := 0;
- repeat
- c := readkey;
- case c of
- { ignore special keys }
- #0:
- c:= readkey;
- { Backspace }
- #8:
- if i > 0 then
- begin
- if not(OutputRedir or InputRedir) then
- write(#8#32#8);
- dec(i);
- end;
- { Unhandled extended key }
- #27:;
- { CR }
- #13:
- begin
- F.BufPtr^[i] := #10;
- if not(OutputRedir or InputRedir) then
- write(#10);
- inc(i);
- end;
- else
- begin
- if not(OutputRedir or InputRedir) then
- write(c);
- F.BufPtr^[i] := c;
- inc(i);
- end;
- end;
- until (c in [#10,#13]) or (i >= F.BufSize);
- F.BufEnd := i;
- CrtRead := 0;
- exit;
- end;
- F.BufEnd:=fpRead(F.Handle, F.BufPtr^, F.BufSize);
- { fix #13 only's -> #10 to overcome terminal setting }
- for i:=1to F.BufEnd do
- begin
- if (F.BufPtr^[i-1]=#13) and (F.BufPtr^[i]<>#10) then
- F.BufPtr^[i-1]:=#10;
- end;
- F.BufPos:=F.BufEnd;
- if not(OutputRedir or InputRedir) then
- CrtWrite(F)
- else F.BufPos := 0;
- CrtRead:=0;
- End;
- Function CrtReturn(Var F:TextRec):Integer;
- Begin
- CrtReturn:=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
- begin
- TextRec(F).InOutFunc:=@CrtWrite;
- TextRec(F).FlushFunc:=@CrtWrite;
- end
- Else
- begin
- F.Mode:=fmInput;
- TextRec(F).InOutFunc:=@CrtRead;
- TextRec(F).FlushFunc:=@CrtReturn;
- end;
- TextRec(F).CloseFunc:=@CrtClose;
- CrtOpen:=0;
- End;
- procedure AssignCrt(var F: Text);
- {
- Assign a file to the console. All output on file goes to console instead.
- }
- begin
- Assign(F,'');
- TextRec(F).OpenFunc:=@CrtOpen;
- end;
- {******************************************************************************
- High Level Functions
- ******************************************************************************}
- Procedure DelLine;
- {
- Delete current line. Scroll subsequent lines up
- }
- Begin
- ScrollScrnRegionUp(WindMinX, CurrY, WindMaxX, WindMaxY, 1);
- End;
- Procedure InsLine;
- {
- Insert line at current cursor position. Scroll subsequent lines down.
- }
- Begin
- ScrollScrnRegionDown(WindMinX, CurrY, WindMaxX, WindMaxY, 1);
- End;
- {$ifdef linux}
- {$define havekiocsound}
- const KIOCSOUND = $4B2F; // start sound generation (0 for off)
- {$else}
- {$ifdef FreeBSD}
- const KIOCSOUND =$20004b3f;
- {$define havekiocsound}
- {$endif}
- {$endif}
- // ioctl might fail e.g. in putty. A redirect check is not enough,
- // needs check for physical console too.
- Procedure Sound(Hz: Word);
- begin
- {$ifdef havekiocsound}
- if (not OutputRedir) and (hz>0) then
- fpIoctl(TextRec(Output).Handle, KIOCSOUND, Pointer(1193180 div Hz));
- {$endif}
- end;
- Procedure NoSound;
- begin
- {$ifdef havekiocsound}
- if not OutputRedir then
- fpIoctl(TextRec(Output).Handle, KIOCSOUND, nil);
- {$endif}
- end;
- Procedure TextMode (Mode: word);
- {
- Only Clears Screen under linux}
- begin
- ClrScr;
- end;
- {******************************************************************************
- Extra
- ******************************************************************************}
- procedure CursorBig;
- begin
- ttySendStr(#27'[?17;0;64c');
- end;
- procedure CursorOn;
- begin
- ttySendStr(#27'[?2c');
- end;
- procedure CursorOff;
- begin
- ttySendStr(#27'[?1c');
- end;
- {******************************************************************************
- Initialization
- ******************************************************************************}
- var
- OldIO : termio.TermIos;
- inputRaw, outputRaw: boolean;
- procedure saveRawSettings(const tio: termio.termios);
- Begin
- with tio do
- begin
- inputRaw :=
- ((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);
- outPutRaw :=
- ((c_oflag and OPOST) = 0) and
- ((c_cflag and (CSIZE or PARENB)) = 0) and
- ((c_cflag and CS8) <> 0);
- end;
- end;
- procedure restoreRawSettings(tio: termio.termios);
- begin
- with tio do
- begin
- if inputRaw 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 outPutRaw then
- begin
- c_oflag := c_oflag and not(OPOST);
- c_cflag := c_cflag and not(CSIZE or PARENB) or CS8;
- end;
- end;
- end;
- Procedure SetRawMode(b:boolean);
- Var
- Tio : Termios;
- Begin
- if b then
- begin
- TCGetAttr(1,Tio);
- SaveRawSettings(Tio);
- OldIO:=Tio;
- CFMakeRaw(Tio);
- end
- else
- begin
- RestoreRawSettings(OldIO);
- Tio:=OldIO;
- end;
- TCSetAttr(1,TCSANOW,Tio);
- End;
- procedure GetXY(var x,y:byte);
- var
- fds : tfdSet;
- i,j,
- readed : longint;
- buf : array[0..255] of char;
- s : string[16];
- begin
- x:=0;
- y:=0;
- s:=#27'[6n';
- fpWrite(0,s[1],length(s));
- fpFD_ZERO(fds);
- fpFD_SET(1,fds);
- readed:=0;
- repeat
- if (fpSelect(2,@fds,nil,nil,1000)>0) then
- begin
- readed:=readed+fpRead(1,buf[readed],sizeof(buf)-readed);
- i:=0;
- while (i+5<readed) and (buf[i]<>#27) and (buf[i+1]<>'[') do
- inc(i);
- if i+5<readed then
- begin
- s:=space(16);
- move(buf[i+2],s[1],16);
- j:=Pos('R',s);
- if j>0 then
- begin
- i:=Pos(';',s);
- Val(Copy(s,1,i-1),y);
- Val(Copy(s,i+1,j-(i+1)),x);
- break;
- end;
- end;
- end
- else
- break;
- until false;
- end;
- Procedure GetConsoleBuf;
- var
- WinInfo : TWinSize;
- begin
- if Assigned(ConsoleBuf) then
- FreeMem(ConsoleBuf,ScreenHeight*ScreenWidth*2);
- ScreenWidth:=0;
- ScreenHeight:=0;
- if (not OutputRedir) and (fpIOCtl(TextRec(Output).Handle,TIOCGWINSZ,@Wininfo)>=0) then
- begin
- ScreenWidth:=Wininfo.ws_col;
- ScreenHeight:=Wininfo.ws_row;
- end;
- // Set some arbitrary defaults which make some sense...
- If (ScreenWidth=0) then
- ScreenWidth:=80;
- If (ScreenHeight=0) then
- ScreenHeight:=25;
- GetMem(ConsoleBuf,ScreenHeight*ScreenWidth*2);
- FillChar(ConsoleBuf^,ScreenHeight*ScreenWidth*2,0);
- end;
- Initialization
- {$ifdef debugcrt}
- Assign(DebugFile,'debug.txt');
- ReWrite(DebugFile);
- {$endif}
- { Redirect the standard output }
- assigncrt(Output);
- Rewrite(Output);
- TextRec(Output).Handle:=StdOutputHandle;
- assigncrt(Input);
- Reset(Input);
- TextRec(Input).Handle:=StdInputHandle;
- { Are we redirected to a file ? }
- OutputRedir:= IsAtty(TextRec(Output).Handle)<>1;
- { does the input come from another console or from a file? }
- InputRedir :=
- (IsAtty(TextRec(Input).Handle)<>1) or
- (not OutputRedir and
- (TTYName(TextRec(Input).Handle) <> TTYName(TextRec(Output).Handle)));
- { Get Size of terminal and set WindMax to the window }
- GetConsoleBuf;
- WindMinX:=1;
- WindMinY:=1;
- WindMaxX:=ScreenWidth;
- WindMaxY:=ScreenHeight;
- WindMax:=((ScreenHeight-1) Shl 8)+(ScreenWidth-1);
- {Get Current X&Y or Reset to Home}
- if OutputRedir then
- begin
- CurrX:=1;
- CurrY:=1;
- end
- else
- begin
- { Set default Terminal Settings }
- SetRawMode(True);
- { Get current X,Y if not set already }
- 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;
- Finalization
- {$ifdef debugcrt}
- Close(DebugFile);
- {$endif}
- ttyFlushOutput;
- if not OutputRedir then
- SetRawMode(False);
- { remove console buf }
- if Assigned(ConsoleBuf) then
- FreeMem(ConsoleBuf,ScreenHeight*ScreenWidth*2);
- End.
|