123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826 |
- {
- $Id$
- This file is part of the Free Pascal run time library.
- Copyright (c) 1999-2000 by Florian Klaempfl
- member of the Free Pascal development team
- Video unit for linux
- 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 Video;
- interface
- {$i videoh.inc}
- implementation
- uses
- Linux, Strings, TermInfo;
- {$i video.inc}
- var
- LastCursorType : byte;
- TtyFd: Longint;
- Console: Boolean;
- OldVideoBuf: PVideoBuf;
- {$ifdef logging}
- f: file;
- const
- logstart: string = '';
- nl: char = #10;
- logend: string = #10#10;
- {$endif logging}
- {$ASMMODE ATT}
- const
- can_delete_term : boolean = false;
- ACSIn : string = '';
- ACSOut : string = '';
- InACS : boolean =false;
- function IsACS(var ch,ACSchar : char): boolean;
- begin
- IsACS:=false;
- case ch of
- #24, #30: {}
- ch:='^';
- #25, #31: {}
- ch:='v';
- #26, #16: {Never introduce a ctrl-Z ... }
- ch:='>';
- {#27,needed in Escape sequences} #17: {}
- ch:='<';
- #176, #177, #178: {°±²}
- begin
- IsACS:=true;
- ACSChar:='a';
- end;
- #180, #181, #182, #185: {´µ¶¹}
- begin
- IsACS:=true;
- ACSChar:='u';
- end;
- #183, #184, #187, #191: {·¸»¿}
- begin
- IsACS:=true;
- ACSChar:='k';
- end;
- #188, #189, #190, #217: {¼½¾Ù}
- begin
- IsACS:=true;
- ACSChar:='j';
- end;
- #192, #200, #211, #212: {ÀÈÓÔ}
- begin
- IsACS:=true;
- ACSChar:='m';
- end;
- #193, #202, #207, #208: {ÁÊÏÐ}
- begin
- IsACS:=true;
- ACSChar:='v';
- end;
- #194, #203, #209, #210: {ÂËÑÒ}
- begin
- IsACS:=true;
- ACSChar:='w';
- end;
- #195, #198, #199, #204: {ÃÆÇÌ}
- begin
- IsACS:=true;
- ACSChar:='t';
- end;
- #196, #205: {ÄÍ}
- begin
- IsACS:=true;
- ACSChar:='q';
- end;
- #179, #186: {³º}
- begin
- IsACS:=true;
- ACSChar:='x';
- end;
- #197, #206, #215, #216: {ÅÎר}
- begin
- IsACS:=true;
- ACSChar:='n';
- end;
- #201, #213, #214, #218: {ÉÕÖÚ}
- begin
- IsACS:=true;
- ACSChar:='l';
- end;
- #254: { þ }
- begin
- ch:='*';
- end;
- { Shadows for Buttons }
- #220: { Ü }
- begin
- IsACS:=true;
- ACSChar:='a';
- end;
- #223: { ß }
- begin
- IsACS:=true;
- ACSChar:='a';
- end;
- end;
- end;
- function SendEscapeSeqNdx(Ndx: Word) : boolean;
- var
- P,pdelay: PChar;
- begin
- SendEscapeSeqNdx:=false;
- if not assigned(cur_term_Strings) then
- exit{RunError(219)};
- P:=cur_term_Strings^[Ndx];
- if assigned(p) then
- begin { Do not transmit the delays }
- pdelay:=strpos(p,'$<');
- if assigned(pdelay) then
- pdelay^:=#0;
- fdWrite(TTYFd, P^, StrLen(P));
- SendEscapeSeqNdx:=true;
- if assigned(pdelay) then
- pdelay^:='$';
- end;
- end;
- procedure SendEscapeSeq(const S: String);
- begin
- fdWrite(TTYFd, S[1], Length(S));
- end;
- Function IntStr(l:longint):string;
- var
- s : string;
- begin
- Str(l,s);
- IntStr:=s;
- end;
- 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
- XY2Ansi:='';
- exit;
- end;
- if x=1 then
- begin
- XY2Ansi:=#13;
- exit;
- end;
- if x>ox then
- begin
- XY2Ansi:=#27'['+IntStr(x-ox)+'C';
- exit;
- end
- else
- begin
- XY2Ansi:=#27'['+IntStr(ox-x)+'D';
- exit;
- end;
- end;
- if x=ox then
- begin
- if y>oy then
- begin
- XY2Ansi:=#27'['+IntStr(y-oy)+'B';
- exit;
- end
- else
- begin
- XY2Ansi:=#27'['+IntStr(oy-y)+'A';
- exit;
- end;
- end;
- if (x=1) and (oy+1=y) then
- XY2Ansi:=#13#10
- else
- XY2Ansi:=#27'['+IntStr(y)+';'+IntStr(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;
- procedure TransformUsingACS(var st : string);
- var
- res : string;
- i : longint;
- ch,ACSch : char;
- begin
- res:='';
- for i:=1 to length(st) do
- begin
- ch:=st[i];
- if IsACS(ch,ACSch) then
- begin
- if not InACS then
- begin
- res:=res+ACSIn;
- InACS:=true;
- end;
- res:=res+ACSch;
- end
- else
- begin
- if InACS then
- begin
- res:=res+ACSOut;
- InACS:=false;
- end;
- res:=res+ch;
- end;
- end;
- st:=res;
- end;
- procedure UpdateTTY(Force:boolean);
- type
- tchattr=packed record
- ch : char;
- attr : byte;
- end;
- var
- outbuf : array[0..1023+255] of char;
- chattr : tchattr;
- skipped : boolean;
- outptr,
- spaces,
- eol,
- x,y,
- LastX,LastY,
- SpaceAttr,
- LastAttr : longint;
- p,pold : pvideocell;
- procedure outdata(hstr:string);
- begin
- while (eol>0) do
- begin
- hstr:=#13#10+hstr;
- dec(eol);
- end;
- if NoExtendedFrame and (ACSIn<>'') and (ACSOut<>'') then
- TransformUsingACS(Hstr);
- move(hstr[1],outbuf[outptr],length(hstr));
- inc(outptr,length(hstr));
- if outptr>=1024 then
- begin
- {$ifdef logging}
- blockwrite(f,logstart[1],length(logstart));
- blockwrite(f,nl,1);
- blockwrite(f,outptr,sizeof(outptr));
- blockwrite(f,nl,1);
- blockwrite(f,outbuf,outptr);
- blockwrite(f,nl,1);
- {$endif logging}
- fdWrite(TTYFd,outbuf,outptr);
- outptr:=0;
- end;
- end;
- procedure OutClr(c:byte);
- begin
- if c=LastAttr then
- exit;
- OutData(Attr2Ansi(c,LastAttr));
- LastAttr:=c;
- end;
- procedure OutSpaces;
- begin
- if (Spaces=0) then
- exit;
- OutClr(SpaceAttr);
- OutData(Space(Spaces));
- LastX:=x;
- LastY:=y;
- Spaces:=0;
- end;
- begin
- OutPtr:=0;
- Eol:=0;
- skipped:=true;
- p:=PVideoCell(VideoBuf);
- pold:=PVideoCell(OldVideoBuf);
- { init Attr and X,Y }
- SendEscapeSeq(#27'[m'{#27'[H'});
- LastAttr:=7;
- LastX:=-1;
- LastY:=-1;
- for y:=1 to ScreenHeight do
- begin
- SpaceAttr:=0;
- Spaces:=0;
- for x:=1 to ScreenWidth do
- begin
- if (not force) and (p^=pold^) then
- begin
- if (Spaces>0) then
- OutSpaces;
- skipped:=true;
- end
- else
- begin
- if skipped then
- begin
- OutData(XY2Ansi(x,y,LastX,LastY));
- LastX:=x;
- LastY:=y;
- skipped:=false;
- end;
- chattr:=tchattr(p^);
- if chattr.ch in [#0,#255] then
- chattr.ch:=' ';
- if chattr.ch=' ' then
- begin
- if Spaces=0 then
- SpaceAttr:=chattr.Attr;
- if (chattr.attr and $f0)=(spaceattr and $f0) then
- chattr.Attr:=SpaceAttr
- else
- begin
- OutSpaces;
- SpaceAttr:=chattr.Attr;
- end;
- inc(Spaces);
- end
- else
- begin
- if (Spaces>0) then
- OutSpaces;
- if ord(chattr.ch)<32 then
- begin
- Chattr.Attr:= $ff xor Chattr.Attr;
- ChAttr.ch:= chr(ord(chattr.ch)+ord('A')-1);
- end;
- if LastAttr<>chattr.Attr then
- OutClr(chattr.Attr);
- OutData(chattr.ch);
- LastX:=x+1;
- LastY:=y;
- end;
- p^:=tvideocell(chattr);
- end;
- inc(p);
- inc(pold);
- end;
- if (Spaces>0) then
- OutSpaces;
- if force then
- inc(eol);
- end;
- eol:=0;
- OutData(XY2Ansi(CursorX,CursorY,LastX,LastY));
- {$ifdef logging}
- blockwrite(f,logstart[1],length(logstart));
- blockwrite(f,nl,1);
- blockwrite(f,outptr,sizeof(outptr));
- blockwrite(f,nl,1);
- blockwrite(f,outbuf,outptr);
- blockwrite(f,nl,1);
- {$endif logging}
- fdWrite(TTYFd,outbuf,outptr);
- if InACS then
- SendEscapeSeqNdx(exit_alt_charset_mode);
- end;
- var
- InitialVideoTio, preInitVideoTio, postInitVideoTio: linux.termios;
- inputRaw, outputRaw: boolean;
- procedure saveRawSettings(const tio: linux.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: linux.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;
- TCSetAttr(1,TCSANOW,tio);
- end;
- procedure TargetEntry;
- begin
- TCGetAttr(1,InitialVideoTio);
- end;
- procedure TargetExit;
- begin
- TCSetAttr(1,TCSANOW,InitialVideoTio);
- end;
- procedure prepareInitVideo;
- begin
- TCGetAttr(1,preInitVideoTio);
- saveRawSettings(preInitVideoTio);
- end;
- procedure videoInitDone;
- begin
- TCGetAttr(1,postInitVideoTio);
- restoreRawSettings(postInitVideoTio);
- end;
- procedure prepareDoneVideo;
- var
- tio: linux.termios;
- begin
- TCGetAttr(1,tio);
- saveRawSettings(tio);
- TCSetAttr(1,TCSANOW,postInitVideoTio);
- end;
- procedure doneVideoDone;
- begin
- restoreRawSettings(preInitVideoTio);
- end;
- procedure InitVideo;
- const
- fontstr : string[3]=#27'(K';
- var
- ThisTTY: String[30];
- FName: String;
- WS: packed record
- ws_row, ws_col, ws_xpixel, ws_ypixel: Word;
- end;
- Err: Longint;
- prev_term : TerminalCommon_ptr1;
- begin
- {$ifndef CPUI386}
- LowAscii:=false;
- {$endif CPUI386}
- if VideoBufSize<>0 then
- begin
- clearscreen;
- if Console then
- SetCursorPos(1,1)
- else
- begin
- if not SendEscapeSeqNdx(cursor_home) then
- SendEscapeSeq(#27'[H');
- end;
- exit;
- end;
- { check for tty }
- ThisTTY:=TTYName(stdinputhandle);
- if IsATTY(stdinputhandle) then
- begin
- { save current terminal characteristics and remove rawness }
- prepareInitVideo;
- { write code to set a correct font }
- fdWrite(stdoutputhandle,fontstr[1],length(fontstr));
- { running on a tty, find out whether locally or remotely }
- if (Copy(ThisTTY, 1, 8) = '/dev/tty') and
- (ThisTTY[9] >= '0') and (ThisTTY[9] <= '9') then
- begin
- { running on the console }
- FName:='/dev/vcsa' + ThisTTY[9];
- TTYFd:=fdOpen(FName, Octal(666), Open_RdWr); { open console }
- end
- else
- TTYFd:=-1;
- if TTYFd<>-1 then
- Console:=true
- else
- begin
- { running on a remote terminal, no error with /dev/vcsa }
- Console:=False;
- LowAscii:=false;
- TTYFd:=stdoutputhandle;
- end;
- ioctl(stdinputhandle, TIOCGWINSZ, @WS);
- if WS.ws_Col=0 then
- WS.ws_Col:=80;
- if WS.ws_Row=0 then
- WS.ws_Row:=25;
- ScreenWidth:=WS.ws_Col;
- { TDrawBuffer only has FVMaxWidth elements
- larger values lead to crashes }
- if ScreenWidth> FVMaxWidth then
- ScreenWidth:=FVMaxWidth;
- ScreenHeight:=WS.ws_Row;
- CursorX:=1;
- CursorY:=1;
- ScreenColor:=True;
- { allocate pmode memory buffer }
- VideoBufSize:=ScreenWidth*ScreenHeight*2;
- GetMem(VideoBuf,VideoBufSize);
- GetMem(OldVideoBuf,VideoBufSize);
- { Start with a clear screen }
- if not Console then
- begin
- prev_term:=cur_term;
- setupterm(nil, stdoutputhandle, err);
- can_delete_term:=assigned(prev_term) and (prev_term<>cur_term);
- SendEscapeSeqNdx(cursor_home);
- SendEscapeSeqNdx(cursor_normal);
- SendEscapeSeqNdx(cursor_visible);
- SendEscapeSeqNdx(enter_ca_mode);
- SetCursorType(crUnderLine);
- end
- else if not assigned(cur_term) then
- begin
- setupterm(nil, stdoutputhandle, err);
- can_delete_term:=false;
- end;
- if assigned(cur_term_Strings) then
- begin
- ACSIn:=StrPas(cur_term_Strings^[enter_alt_charset_mode]);
- ACSOut:=StrPas(cur_term_Strings^[exit_alt_charset_mode]);
- if (ACSIn<>'') and (ACSOut<>'') then
- SendEscapeSeqNdx(ena_acs);
- if pos('$<',ACSIn)>0 then
- ACSIn:=Copy(ACSIn,1,Pos('$<',ACSIn)-1);
- if pos('$<',ACSOut)>0 then
- ACSOut:=Copy(ACSOut,1,Pos('$<',ACSOut)-1);
- end
- else
- begin
- ACSIn:='';
- ACSOut:='';
- end;
- ClearScreen;
- {$ifdef logging}
- assign(f,'video.log');
- rewrite(f,1);
- {$endif logging}
- { save new terminal characteristics and possible restore rawness }
- videoInitDone;
- end
- else
- ErrorCode:=errVioInit; { not a TTY }
- end;
- procedure DoneVideo;
- begin
- if VideoBufSize=0 then
- exit;
- prepareDoneVideo;
- ClearScreen;
- if Console then
- SetCursorPos(1,1)
- else
- begin
- SendEscapeSeqNdx(exit_ca_mode);
- SendEscapeSeqNdx(cursor_home);
- SendEscapeSeqNdx(cursor_normal);
- SendEscapeSeqNdx(cursor_visible);
- SetCursorType(crUnderLine);
- SendEscapeSeq(#27'[H');
- end;
- FreeMem(VideoBuf,VideoBufSize);
- FreeMem(OldVideoBuf,VideoBufSize);
- VideoBufSize:=0;
- ACSIn:='';
- ACSOut:='';
- doneVideoDone;
- if can_delete_term then
- begin
- del_curterm(cur_term);
- can_delete_term:=false;
- end;
- {$ifdef logging}
- close(f);
- {$endif logging}
- end;
- procedure ClearScreen;
- begin
- FillWord(VideoBuf^,VideoBufSize shr 1,$0720);
- if Console then
- UpdateScreen(true)
- else
- begin
- SendEscapeSeq(#27'[0m');
- SendEscapeSeqNdx(clear_screen);
- end;
- end;
- procedure UpdateScreen(Force: Boolean);
- var
- DoUpdate : boolean;
- begin
- if LockUpdateScreen<>0 then
- exit;
- if not force then
- begin
- {$ifdef i386}
- asm
- movl VideoBuf,%esi
- movl OldVideoBuf,%edi
- movl VideoBufSize,%ecx
- shrl $2,%ecx
- repe
- cmpsl
- setne DoUpdate
- end;
- {$endif i386}
- end
- else
- DoUpdate:=true;
- if not DoUpdate then
- exit;
- if Console then
- begin
- fdSeek(TTYFd, 4, Seek_Set);
- fdWrite(TTYFd, VideoBuf^,VideoBufSize);
- end
- else
- begin
- UpdateTTY(force);
- end;
- Move(VideoBuf^, OldVideoBuf^, VideoBufSize);
- end;
- function GetCapabilities: Word;
- begin
- { about cpColor... we should check the terminfo database... }
- GetCapabilities:=cpUnderLine + cpBlink + cpColor;
- end;
- procedure SetCursorPos(NewCursorX, NewCursorY: Word);
- var
- Pos : array [1..2] of Byte;
- begin
- if Console then
- begin
- fdSeek(TTYFd, 2, Seek_Set);
- Pos[1]:=NewCursorX;
- Pos[2]:=NewCursorY;
- fdWrite(TTYFd, Pos, 2);
- end
- else
- begin
- { newcursorx,y is 0 based ! }
- SendEscapeSeq(XY2Ansi(NewCursorX+1,NewCursorY+1,0,0));
- end;
- CursorX:=NewCursorX+1;
- CursorY:=NewCursorY+1;
- end;
- function GetCursorType: Word;
- begin
- GetCursorType:=LastCursorType;
- end;
- procedure SetCursorType(NewType: Word);
- begin
- LastCursorType:=NewType;
- case NewType of
- crBlock :
- Begin
- If not SendEscapeSeqNdx(cursor_visible) then
- SendEscapeSeq(#27'[?17;0;64c');
- End;
- crHidden :
- Begin
- If not SendEscapeSeqNdx(cursor_invisible) then
- SendEscapeSeq(#27'[?1c');
- End;
- else
- begin
- If not SendEscapeSeqNdx(cursor_normal) then
- SendEscapeSeq(#27'[?2c');
- end;
- end;
- end;
- function DefaultVideoModeSelector(const VideoMode: TVideoMode; Params: Longint): Boolean;
- begin
- DefaultVideoModeSelector:=false;
- end;
- procedure RegisterVideoModes;
- begin
- end;
- initialization
- RegisterVideoModes;
- finalization
- UnRegisterVideoModes;
- end.
- {
- $Log$
- Revision 1.1 2001-01-13 11:03:58 peter
- * API 2 RTL commit
- }
|