video.pp 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2000 by Florian Klaempfl
  4. member of the Free Pascal development team
  5. Video unit for linux
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. unit Video;
  13. {$I-}
  14. {$GOTO on}
  15. {*****************************************************************************}
  16. interface
  17. {*****************************************************************************}
  18. {$i videoh.inc}
  19. {*****************************************************************************}
  20. implementation
  21. {*****************************************************************************}
  22. uses baseunix,termio,strings
  23. {$ifdef linux},linuxvcs{$endif};
  24. {$i video.inc}
  25. type Tconsole_type=(ttyNetwork
  26. {$ifdef linux},ttyLinux{$endif}
  27. ,ttyFreeBSD
  28. ,ttyNetBSD);
  29. Tconversion=(cv_none,
  30. cv_vga_to_acs);
  31. Ttermcode=(
  32. enter_alt_charset_mode,
  33. exit_alt_charset_mode,
  34. clear_screen,
  35. cursor_home,
  36. cursor_normal,
  37. cursor_visible,
  38. cursor_invisible,
  39. enter_ca_mode,
  40. exit_ca_mode,
  41. exit_am_mode,
  42. ena_acs
  43. );
  44. Ttermcodes=array[Ttermcode] of Pchar;
  45. Ptermcodes=^Ttermcodes;
  46. const term_codes_ansi:Ttermcodes=
  47. (#$1B#$5B#$31#$31#$6D, {enter_alt_charset_mode}
  48. #$1B#$5B#$31#$30#$6D, {exit_alt_charset_mode}
  49. #$1B#$5B#$48#$1B#$5B#$4A, {clear_screen}
  50. #$1B#$5B#$48, {cursor_home}
  51. nil, {cursor_normal}
  52. nil, {cursor_visible}
  53. nil, {cursor_invisible}
  54. nil, {enter_ca_mode}
  55. nil, {exit_ca_mode}
  56. nil, {exit_am_mode}
  57. nil); {ena_acs}
  58. term_codes_freebsd:Ttermcodes=
  59. (nil, {enter_alt_charset_mode}
  60. nil, {exit_alt_charset_mode}
  61. #$1B#$5B#$48#$1B#$5B#$4A, {clear_screen}
  62. #$1B#$5B#$48, {cursor_home}
  63. #$1B#$5B#$3D#$30#$43, {cursor_normal}
  64. #$1B#$5B#$3D#$31#$43, {cursor_visible}
  65. nil, {cursor_invisible}
  66. nil, {enter_ca_mode}
  67. nil, {exit_ca_mode}
  68. nil, {exit_am_mode}
  69. nil); {ena_acs}
  70. term_codes_linux:Ttermcodes=
  71. (#$1B#$5B#$31#$31#$6D, {enter_alt_charset_mode}
  72. #$1B#$5B#$31#$30#$6D, {exit_alt_charset_mode}
  73. #$1B#$5B#$48#$1B#$5B#$4A, {clear_screen}
  74. #$1B#$5B#$48, {cursor_home}
  75. #$1B#$5B#$3F#$32#$35#$68#$1B#$5B#$3F#$30#$63, {cursor_normal}
  76. #$1B#$5B#$3F#$32#$35#$68#$1B#$5B#$3F#$30#$63, {cursor_visible}
  77. #$1B#$5B#$3F#$32#$35#$6C, {cursor_invisible}
  78. nil, {enter_ca_mode}
  79. nil, {exit_ca_mode}
  80. nil, {exit_am_mode}
  81. nil); {ena_acs}
  82. term_codes_vt100:Ttermcodes=
  83. (#$0E, {enter_alt_charset_mode}
  84. #$0F, {exit_alt_charset_mode}
  85. #$1B#$5B#$48#$1B#$5B#$4A{#$24#$3C#$35#$30#$3E}, {clear_screen}
  86. #$1B#$5B#$48, {cursor_home}
  87. nil, {cursor_normal}
  88. nil, {cursor_visible}
  89. nil, {cursor_invisible}
  90. nil, {enter_ca_mode}
  91. nil, {exit_ca_mode}
  92. #$1B#$5B#$3F#$37#$6C, {exit_am_mode}
  93. #$1B#$28#$42#$1B#$29#$30); {ena_acs}
  94. term_codes_vt220:Ttermcodes=
  95. (#$1B#$28#$30{#$24#$3C#$32#$3E}, {enter_alt_charset_mode}
  96. #$1B#$28#$42{#$24#$3C#$34#$3E}, {exit_alt_charset_mode}
  97. #$1B#$5B#$48#$1B#$5B#$4A, {clear_screen}
  98. #$1B#$5B#$48, {cursor_home}
  99. nil, {cursor_normal}
  100. nil, {cursor_visible}
  101. nil, {cursor_invisible}
  102. nil, {enter_ca_mode}
  103. nil, {exit_ca_mode}
  104. #$1B#$5B#$3F#$37#$6C, {exit_am_mode}
  105. #$1B#$29#$30); {ena_acs}
  106. term_codes_xterm:Ttermcodes=
  107. (#$0E, {enter_alt_charset_mode}
  108. #$0F, {exit_alt_charset_mode}
  109. #$1B#$5B#$48#$1B#$5B#$32#$4A, {clear_screen}
  110. #$1B#$5B#$48, {cursor_home}
  111. #$1B#$5B#$3F#$31#$32#$6C#$1B#$5B#$3F#$32#$35#$68, {cursor_normal}
  112. #$1B#$5B#$3F#$31#$32#$3B#$32#$35#$68, {cursor_visible}
  113. #$1B#$5B#$3F#$32#$35#$6C, {cursor_invisible}
  114. #$1B#$5B#$3F#$31#$30#$34#$39#$68, {enter_ca_mode}
  115. #$1B#$5B#$3F#$31#$30#$34#$39#$6C, {exit_ca_mode}
  116. #$1B#$5B#$3F#$37#$6C, {exit_am_mode}
  117. #$1B#$28#$42#$1B#$29#$30); {ena_acs}
  118. const terminal_names:array[0..8] of string[7]=(
  119. 'ansi',
  120. 'cons',
  121. 'eterm',
  122. 'gnome',
  123. 'konsole',
  124. 'linux',
  125. 'vt100',
  126. 'vt220',
  127. 'xterm');
  128. terminal_data:array[0..8] of Ptermcodes=(
  129. @term_codes_ansi,
  130. @term_codes_freebsd,
  131. @term_codes_xterm,
  132. @term_codes_xterm,
  133. @term_codes_xterm,
  134. @term_codes_linux,
  135. @term_codes_vt100,
  136. @term_codes_vt220,
  137. @term_codes_xterm);
  138. const convert:Tconversion=cv_none;
  139. var
  140. LastCursorType : byte;
  141. TtyFd: Longint;
  142. Console: Tconsole_type;
  143. cur_term_strings:Ptermcodes;
  144. {$ifdef logging}
  145. f: file;
  146. const
  147. logstart: string = '';
  148. nl: char = #10;
  149. logend: string = #10#10;
  150. {$endif logging}
  151. {$ifdef cpui386}
  152. {$ASMMODE ATT}
  153. {$endif cpui386}
  154. const
  155. { can_delete_term : boolean = false;}
  156. ACSIn : string = '';
  157. ACSOut : string = '';
  158. in_ACS : boolean =false;
  159. function convert_vga_to_acs(ch:char):word;
  160. {Ch contains a character in the VGA character set (i.e. codepage 437).
  161. This routine tries to convert some VGA symbols as well as possible to the
  162. xterm alternate character set.
  163. Return type is word to allow expanding to UCS-2 characters in the
  164. future.}
  165. begin
  166. case ch of
  167. #18:
  168. convert_vga_to_acs:=word('|');
  169. #24, #30: {}
  170. convert_vga_to_acs:=word('^');
  171. #25, #31: {}
  172. convert_vga_to_acs:=word('v');
  173. #26, #16: {Never introduce a ctrl-Z ... }
  174. convert_vga_to_acs:=word('>');
  175. {#27,} #17: {}
  176. convert_vga_to_acs:=word('<');
  177. #176, #177, #178: {°±²}
  178. convert_vga_to_acs:=$f800+word('a');
  179. #180, #181, #182, #185: {´µ¶¹}
  180. convert_vga_to_acs:=$f800+word('u');
  181. #183, #184, #187, #191: {·¸»¿}
  182. convert_vga_to_acs:=$f800+word('k');
  183. #188, #189, #190, #217: {¼½¾Ù}
  184. convert_vga_to_acs:=$f800+word('j');
  185. #192, #200, #211, #212: {ÀÈÓÔ}
  186. convert_vga_to_acs:=$f800+word('m');
  187. #193, #202, #207, #208: {ÁÊÏÐ}
  188. convert_vga_to_acs:=$f800+word('v');
  189. #194, #203, #209, #210: {ÂËÑÒ}
  190. convert_vga_to_acs:=$f800+word('w');
  191. #195, #198, #199, #204: {ÃÆÇÌ}
  192. convert_vga_to_acs:=$f800+word('t');
  193. #196, #205: {ÄÍ}
  194. convert_vga_to_acs:=$f800+word('q');
  195. #179, #186: {³º}
  196. convert_vga_to_acs:=$f800+word('x');
  197. #197, #206, #215, #216: {ÅÎר}
  198. convert_vga_to_acs:=$f800+word('n');
  199. #201, #213, #214, #218: {ÉÕÖÚ}
  200. convert_vga_to_acs:=$f800+word('l');
  201. #254: { þ }
  202. convert_vga_to_acs:=word('*');
  203. { Shadows for Buttons }
  204. #220 { Ü },
  205. #223: { ß }
  206. convert_vga_to_acs:=$f800+word('a');
  207. else
  208. convert_vga_to_acs:=word(ch);
  209. end;
  210. end;
  211. function SendEscapeSeqNdx(Ndx:Ttermcode) : boolean;
  212. var
  213. P{,pdelay}:PChar;
  214. begin
  215. SendEscapeSeqNdx:=false;
  216. if not assigned(cur_term_Strings) then
  217. exit{RunError(219)};
  218. P:=cur_term_Strings^[Ndx];
  219. if assigned(p) then
  220. begin { Do not transmit the delays }
  221. { pdelay:=strpos(p,'$<');
  222. if assigned(pdelay) then
  223. pdelay^:=#0;}
  224. fpWrite(stdoutputhandle, P^, StrLen(P));
  225. SendEscapeSeqNdx:=true;
  226. { if assigned(pdelay) then
  227. pdelay^:='$';}
  228. end;
  229. end;
  230. procedure SendEscapeSeq(const S: String);
  231. begin
  232. fpWrite(stdoutputhandle, S[1], Length(S));
  233. end;
  234. function IntStr(l:longint):string;
  235. begin
  236. Str(l,intstr);
  237. end;
  238. Function XY2Ansi(x,y,ox,oy:longint):String;
  239. {
  240. Returns a string with the escape sequences to go to X,Y on the screen.
  241. Note that x, y, ox, oy are 1-based (i.e. top-left corner of the screen
  242. is (1, 1)), while SetCursorPos parameters and CursorX and CursorY
  243. are 0-based (top-left corner of the screen is (0, 0)).
  244. }
  245. var delta:longint;
  246. direction:char;
  247. movement:string[32];
  248. begin
  249. if ((x=1) and (oy+1=y)) and (console<>ttyfreebsd) then
  250. begin
  251. XY2Ansi:=#13#10;
  252. exit;
  253. end;
  254. direction:='H';
  255. if y=oy then
  256. begin
  257. if x=ox then
  258. begin
  259. XY2Ansi:='';
  260. exit;
  261. end;
  262. if x=1 then
  263. begin
  264. XY2Ansi:=#13;
  265. exit;
  266. end;
  267. delta:=ox-x;
  268. direction:=char(byte('C')+byte(x<=ox));
  269. end;
  270. if x=ox then
  271. begin
  272. delta:=oy-y;
  273. direction:=char(byte('A')+byte(y>oy));
  274. end;
  275. if direction='H' then
  276. movement:=intstr(y)+';'+intstr(x)
  277. else
  278. movement:=intstr(abs(delta));
  279. xy2ansi:=#27'['+movement+direction;
  280. end;
  281. const ansitbl:array[0..7] of char='04261537';
  282. Function Attr2Ansi(Attr,OAttr:longint):string;
  283. {
  284. Convert Attr to an Ansi String, the Optimal code is calculate
  285. with use of the old OAttr
  286. }
  287. var
  288. hstr : string[16];
  289. OFg,OBg,Fg,Bg : longint;
  290. procedure AddSep(ch:char);
  291. begin
  292. if length(hstr)>0 then
  293. hstr:=hstr+';';
  294. hstr:=hstr+ch;
  295. end;
  296. begin
  297. if Attr=OAttr then
  298. begin
  299. Attr2Ansi:='';
  300. exit;
  301. end;
  302. Hstr:='';
  303. Fg:=Attr and $f;
  304. Bg:=Attr shr 4;
  305. OFg:=OAttr and $f;
  306. OBg:=OAttr shr 4;
  307. if (OFg<>7) or (Fg=7) or ((OFg>7) and (Fg<8)) or ((OBg>7) and (Bg<8)) then
  308. begin
  309. hstr:='0';
  310. OFg:=7;
  311. OBg:=0;
  312. end;
  313. if (Fg>7) and (OFg<8) then
  314. begin
  315. AddSep('1');
  316. OFg:=OFg or 8;
  317. end;
  318. if (Bg and 8)<>(OBg and 8) then
  319. begin
  320. AddSep('5');
  321. OBg:=OBg or 8;
  322. end;
  323. if (Fg<>OFg) then
  324. begin
  325. AddSep('3');
  326. hstr:=hstr+AnsiTbl[fg and 7];
  327. end;
  328. if (Bg<>OBg) then
  329. begin
  330. AddSep('4');
  331. hstr:=hstr+AnsiTbl[bg and 7];
  332. end;
  333. if hstr='0' then
  334. hstr:='';
  335. Attr2Ansi:=#27'['+hstr+'m';
  336. end;
  337. procedure UpdateTTY(Force:boolean);
  338. type
  339. tchattr=packed record
  340. {$ifdef ENDIAN_LITTLE}
  341. ch : char;
  342. attr : byte;
  343. {$else}
  344. attr : byte;
  345. ch : char;
  346. {$endif}
  347. end;
  348. var
  349. outbuf : array[0..1023+255] of char;
  350. chattr : tchattr;
  351. skipped : boolean;
  352. outptr,
  353. spaces,
  354. eol,
  355. x,y,
  356. LastX,LastY,
  357. SpaceAttr,
  358. LastAttr : longint;
  359. p,pold : pvideocell;
  360. LastLineWidth : Longint;
  361. procedure transform_using_acs(var st:string);
  362. var res:string;
  363. i:byte;
  364. c:char;
  365. converted:word;
  366. begin
  367. res:='';
  368. for i:=1 to length(st) do
  369. begin
  370. c:=st[i];
  371. converted:=convert_vga_to_acs(c);
  372. if converted and $ff00=$f800 then
  373. begin
  374. if not in_ACS then
  375. begin
  376. res:=res+ACSIn;
  377. in_ACS:=true;
  378. end;
  379. c:=char(converted and $ff);
  380. end
  381. else
  382. if in_ACS then
  383. begin
  384. res:=res+ACSOut+Attr2Ansi(LastAttr,0);
  385. in_ACS:=false;
  386. end;
  387. res:=res+c;
  388. end;
  389. st:=res;
  390. end;
  391. procedure outdata(hstr:string);
  392. begin
  393. If Length(HStr)>0 Then
  394. Begin
  395. while (eol>0) do
  396. begin
  397. hstr:=#13#10+hstr;
  398. dec(eol);
  399. end;
  400. if (convert=cv_vga_to_acs) and (ACSIn<>'') and (ACSOut<>'') then
  401. transform_using_acs(Hstr);
  402. move(hstr[1],outbuf[outptr],length(hstr));
  403. inc(outptr,length(hstr));
  404. if outptr>=1024 then
  405. begin
  406. {$ifdef logging}
  407. blockwrite(f,logstart[1],length(logstart));
  408. blockwrite(f,nl,1);
  409. blockwrite(f,outptr,sizeof(outptr));
  410. blockwrite(f,nl,1);
  411. blockwrite(f,outbuf,outptr);
  412. blockwrite(f,nl,1);
  413. {$endif logging}
  414. fpWrite(stdoutputhandle,outbuf,outptr);
  415. outptr:=0;
  416. end;
  417. end;
  418. end;
  419. procedure OutClr(c:byte);
  420. begin
  421. if c=LastAttr then
  422. exit;
  423. OutData(Attr2Ansi(c,LastAttr));
  424. LastAttr:=c;
  425. end;
  426. procedure OutSpaces;
  427. begin
  428. if (Spaces=0) then
  429. exit;
  430. OutClr(SpaceAttr);
  431. OutData(Space(Spaces));
  432. LastX:=x;
  433. LastY:=y;
  434. Spaces:=0;
  435. end;
  436. (*
  437. function GetTermString(ndx:Ttermcode):String;
  438. var
  439. P{,pdelay}: PChar;
  440. begin
  441. GetTermString:='';
  442. if not assigned(cur_term_Strings) then
  443. exit{RunError(219)};
  444. P:=cur_term_Strings^[Ndx];
  445. if assigned(p) then
  446. begin { Do not transmit the delays }
  447. { pdelay:=strpos(p,'$<');
  448. if assigned(pdelay) then
  449. pdelay^:=#0;}
  450. GetTermString:=StrPas(p);
  451. { if assigned(pdelay) then
  452. pdelay^:='$';}
  453. end;
  454. end;
  455. *)
  456. begin
  457. OutPtr:=0;
  458. Eol:=0;
  459. skipped:=true;
  460. p:=PVideoCell(VideoBuf);
  461. pold:=PVideoCell(OldVideoBuf);
  462. { init Attr, X,Y and set autowrap off }
  463. SendEscapeSeq(#27'[m'#27'[?7l'{#27'[H'} );
  464. // 1.0.x: SendEscapeSeq(#27'[m'{#27'[H'});
  465. LastAttr:=7;
  466. LastX:=-1;
  467. LastY:=-1;
  468. for y:=1 to ScreenHeight do
  469. begin
  470. SpaceAttr:=0;
  471. Spaces:=0;
  472. LastLineWidth:=ScreenWidth;
  473. If (y=ScreenHeight) And (Console=ttyFreeBSD) {And :am: is on} Then
  474. LastLineWidth:=ScreenWidth-2;
  475. for x:=1 to LastLineWidth do
  476. begin
  477. if (not force) and (p^=pold^) then
  478. begin
  479. if (Spaces>0) then
  480. OutSpaces;
  481. skipped:=true;
  482. end
  483. else
  484. begin
  485. if skipped then
  486. begin
  487. OutData(XY2Ansi(x,y,LastX,LastY));
  488. LastX:=x;
  489. LastY:=y;
  490. skipped:=false;
  491. end;
  492. chattr:=tchattr(p^);
  493. if chattr.ch in [#0,#255] then
  494. chattr.ch:=' ';
  495. if chattr.ch=' ' then
  496. begin
  497. if Spaces=0 then
  498. SpaceAttr:=chattr.Attr;
  499. if (chattr.attr and $f0)=(spaceattr and $f0) then
  500. chattr.Attr:=SpaceAttr
  501. else
  502. begin
  503. OutSpaces;
  504. SpaceAttr:=chattr.Attr;
  505. end;
  506. inc(Spaces);
  507. end
  508. else
  509. begin
  510. if (Spaces>0) then
  511. OutSpaces;
  512. if ord(chattr.ch)<32 then
  513. begin
  514. Chattr.Attr:= $ff xor Chattr.Attr;
  515. ChAttr.ch:= chr(ord(chattr.ch)+ord('A')-1);
  516. end;
  517. if LastAttr<>chattr.Attr then
  518. OutClr(chattr.Attr);
  519. OutData(chattr.ch);
  520. LastX:=x+1;
  521. LastY:=y;
  522. end;
  523. p^:=tvideocell(chattr);
  524. end;
  525. inc(p);
  526. inc(pold);
  527. end;
  528. if (Spaces>0) then
  529. OutSpaces;
  530. if force then
  531. inc(eol)
  532. else
  533. skipped:=true;
  534. end;
  535. eol:=0;
  536. {if am in capabilities? Then}
  537. If (Console=ttyFreeBSD) and (Plongint(p)^<>plongint(pold)^) Then
  538. Begin
  539. OutData(XY2Ansi(ScreenWidth,ScreenHeight,LastX,LastY));
  540. OutData(#8);
  541. {Output last char}
  542. chattr:=tchattr(p[1]);
  543. if LastAttr<>chattr.Attr then
  544. OutClr(chattr.Attr);
  545. OutData(chattr.ch);
  546. inc(LastX);
  547. // OutData(XY2Ansi(ScreenWidth-1,ScreenHeight,LastX,LastY));
  548. // OutData(GetTermString(Insert_character));
  549. OutData(#8+#27+'[1@');
  550. chattr:=tchattr(p^);
  551. if LastAttr<>chattr.Attr then
  552. OutClr(chattr.Attr);
  553. OutData(chattr.ch);
  554. inc(LastX);
  555. end;
  556. OutData(XY2Ansi(CursorX+1,CursorY+1,LastX,LastY));
  557. {$ifdef logging}
  558. blockwrite(f,logstart[1],length(logstart));
  559. blockwrite(f,nl,1);
  560. blockwrite(f,outptr,sizeof(outptr));
  561. blockwrite(f,nl,1);
  562. blockwrite(f,outbuf,outptr);
  563. blockwrite(f,nl,1);
  564. {$endif logging}
  565. fpWrite(stdoutputhandle,outbuf,outptr);
  566. if in_ACS then
  567. SendEscapeSeqNdx(exit_alt_charset_mode);
  568. {turn autowrap on}
  569. // SendEscapeSeq(#27'[?7h');
  570. end;
  571. {$ifdef linux}
  572. procedure update_vcsa(force:boolean);
  573. const max_updates=64;
  574. label update,update_all,equal_loop,unequal_loop;
  575. var position,update_count,i:word;
  576. update_positions:array[0..max_updates-1] of word;
  577. update_lengths:array[0..max_updates-1] of word;
  578. begin
  579. if force then
  580. goto update_all;
  581. update_count:=0;
  582. i:=0;
  583. equal_loop:
  584. repeat
  585. if videobuf^[i]<>oldvideobuf^[i] then
  586. goto unequal_loop;
  587. inc(i);
  588. until i>videobufsize div 2;
  589. goto update;
  590. unequal_loop:
  591. if update_count>=max_updates then
  592. goto update_all;
  593. update_positions[update_count]:=i;
  594. update_lengths[update_count]:=0;
  595. inc(update_count);
  596. repeat
  597. if videobuf^[i]=oldvideobuf^[i] then
  598. goto equal_loop;
  599. inc(i);
  600. inc(update_lengths[update_count-1]);
  601. until i>videobufsize div 2;
  602. update:
  603. for i:=1 to update_count do
  604. begin
  605. position:=update_positions[i-1];
  606. fppwrite(ttyfd,videobuf^[position],update_lengths[i-1]*2,4+position*2);
  607. end;
  608. exit;
  609. update_all:
  610. fppwrite(ttyfd,videobuf^,videobufsize,4);
  611. end;
  612. {$endif}
  613. var
  614. preInitVideoTio, postInitVideoTio: termio.termios;
  615. inputRaw, outputRaw: boolean;
  616. procedure saveRawSettings(const tio: termio.termios);
  617. Begin
  618. with tio do
  619. begin
  620. inputRaw :=
  621. ((c_iflag and (IGNBRK or BRKINT or PARMRK or ISTRIP or
  622. INLCR or IGNCR or ICRNL or IXON)) = 0) and
  623. ((c_lflag and (ECHO or ECHONL or ICANON or ISIG or IEXTEN)) = 0);
  624. outPutRaw :=
  625. ((c_oflag and OPOST) = 0) and
  626. ((c_cflag and (CSIZE or PARENB)) = 0) and
  627. ((c_cflag and CS8) <> 0);
  628. end;
  629. end;
  630. procedure restoreRawSettings(tio: termio.termios);
  631. begin
  632. with tio do
  633. begin
  634. if inputRaw then
  635. begin
  636. c_iflag := c_iflag and (not (IGNBRK or BRKINT or PARMRK or ISTRIP or
  637. INLCR or IGNCR or ICRNL or IXON));
  638. c_lflag := c_lflag and
  639. (not (ECHO or ECHONL or ICANON or ISIG or IEXTEN));
  640. c_cc[VMIN]:=1;
  641. c_cc[VTIME]:=0;
  642. end;
  643. if outPutRaw then
  644. begin
  645. c_oflag := c_oflag and not(OPOST);
  646. c_cflag := c_cflag and not(CSIZE or PARENB) or CS8;
  647. end;
  648. end;
  649. TCSetAttr(1,TCSANOW,tio);
  650. end;
  651. procedure prepareInitVideo;
  652. begin
  653. TCGetAttr(1,preInitVideoTio);
  654. saveRawSettings(preInitVideoTio);
  655. end;
  656. procedure videoInitDone;
  657. begin
  658. TCGetAttr(1,postInitVideoTio);
  659. restoreRawSettings(postInitVideoTio);
  660. end;
  661. procedure prepareDoneVideo;
  662. var
  663. tio: termio.termios;
  664. begin
  665. TCGetAttr(1,tio);
  666. saveRawSettings(tio);
  667. TCSetAttr(1,TCSANOW,postInitVideoTio);
  668. end;
  669. procedure doneVideoDone;
  670. begin
  671. restoreRawSettings(preInitVideoTio);
  672. end;
  673. procedure SysInitVideo;
  674. var
  675. FName: String;
  676. WS: packed record
  677. ws_row, ws_col, ws_xpixel, ws_ypixel: Word;
  678. end;
  679. { Err: Longint;}
  680. { prev_term : TerminalCommon_ptr1;}
  681. term:string;
  682. i:word;
  683. {$ifdef Linux}
  684. s:string[15];
  685. {$endif}
  686. {$ifdef freebsd}
  687. ThisTTY: String[30];
  688. {$endif}
  689. const font_vga:array[0..6] of char=#15#27'%@'#27'(U';
  690. font_custom:array[0..2] of char=#27'(K';
  691. begin
  692. { check for tty }
  693. if (IsATTY(stdinputhandle)=1) then
  694. begin
  695. { save current terminal characteristics and remove rawness }
  696. prepareInitVideo;
  697. { running on a tty, find out whether locally or remotely }
  698. TTyfd:=-1;
  699. Console:=TTyNetwork; {Default: Network or other vtxxx tty}
  700. cur_term_strings:=@term_codes_vt100; {Default: vt100}
  701. {$ifdef linux}
  702. if vcs_device>=0 then
  703. begin
  704. str(vcs_device,s);
  705. fname:='/dev/vcsa'+s;
  706. { open console, $1b6=rw-rw-rw- }
  707. ttyfd:=fpopen(fname,$1b6,O_RDWR);
  708. if ttyfd<>-1 then
  709. console:=ttylinux
  710. else
  711. if try_grab_vcsa then
  712. begin
  713. ttyfd:=fpopen(fname,$1b6,O_RDWR);
  714. if ttyfd<>-1 then
  715. console:=ttylinux;
  716. end;
  717. end;
  718. {$endif}
  719. {$ifdef freebsd}
  720. ThisTTY:=TTYName(stdinputhandle);
  721. if copy(ThisTTY, 1, 9) = '/dev/ttyv' then {FreeBSD has these}
  722. begin
  723. { check for (Free?)BSD native}
  724. if (ThisTTY[10]>='0') and (ThisTTY[10]<='9') Then
  725. Console:=ttyFreeBSD; {TTYFd ?}
  726. end;
  727. {$endif}
  728. term:=fpgetenv('TERM');
  729. for i:=low(terminal_names) to high(terminal_names) do
  730. if copy(term,1,length(terminal_names[i]))=terminal_names[i] then
  731. cur_term_strings:=terminal_data[i];
  732. if cur_term_strings=@term_codes_freebsd then
  733. console:=ttyFreeBSD;
  734. {$ifdef linux}
  735. if (console<>ttylinux) then
  736. begin
  737. {$endif}
  738. if cur_term_strings=@term_codes_linux then
  739. begin
  740. {Executed in case ttylinux is false (i.e. no vcsa), but
  741. TERM=linux.}
  742. {Enable the VGA character set (codepage 437,850,....)}
  743. fpwrite(stdoutputhandle,font_vga,7);
  744. end
  745. else
  746. {No VGA font :( }
  747. fpwrite(stdoutputhandle,font_custom,3);
  748. { running on a remote terminal, no error with /dev/vcsa }
  749. {$ifdef linux}
  750. end;
  751. {$endif}
  752. fpioctl(stdinputhandle, TIOCGWINSZ, @WS);
  753. if WS.ws_Col=0 then
  754. WS.ws_Col:=80;
  755. if WS.ws_Row=0 then
  756. WS.ws_Row:=25;
  757. ScreenWidth:=WS.ws_Col;
  758. { TDrawBuffer only has FVMaxWidth elements
  759. larger values lead to crashes }
  760. if ScreenWidth> FVMaxWidth then
  761. ScreenWidth:=FVMaxWidth;
  762. ScreenHeight:=WS.ws_Row;
  763. CursorX:=0;
  764. CursorY:=0;
  765. LastCursorType:=$ff;
  766. ScreenColor:=True;
  767. { Start with a clear screen }
  768. {$ifdef linux}
  769. if Console<>ttylinux then
  770. begin
  771. {$endif}
  772. SendEscapeSeqNdx(cursor_home);
  773. SendEscapeSeqNdx(cursor_normal);
  774. SendEscapeSeqNdx(cursor_visible);
  775. SendEscapeSeqNdx(enter_ca_mode);
  776. SetCursorType(crUnderLine);
  777. If Console=ttyFreeBSD Then
  778. SendEscapeSeqNdx(exit_am_mode);
  779. {$ifdef linux}
  780. end;
  781. {$endif}
  782. if assigned(cur_term_Strings) then
  783. begin
  784. ACSIn:=StrPas(cur_term_strings^[enter_alt_charset_mode]);
  785. ACSOut:=StrPas(cur_term_strings^[exit_alt_charset_mode]);
  786. if (ACSIn<>'') and (ACSOut<>'') then
  787. SendEscapeSeqNdx(ena_acs);
  788. { if pos('$<',ACSIn)>0 then
  789. ACSIn:=Copy(ACSIn,1,Pos('$<',ACSIn)-1);
  790. if pos('$<',ACSOut)>0 then
  791. ACSOut:=Copy(ACSOut,1,Pos('$<',ACSOut)-1);}
  792. If fpGetEnv('TERM')='xterm' then
  793. convert:=cv_vga_to_acs; {use of acs for xterm is ok}
  794. end
  795. else
  796. begin
  797. ACSIn:='';
  798. ACSOut:='';
  799. end;
  800. {$ifdef logging}
  801. assign(f,'video.log');
  802. rewrite(f,1);
  803. {$endif logging}
  804. { save new terminal characteristics and possible restore rawness }
  805. videoInitDone;
  806. end
  807. else
  808. ErrorCode:=errVioInit; { not a TTY }
  809. end;
  810. procedure SysDoneVideo;
  811. var font_custom:array[0..2] of char=#27'(K';
  812. begin
  813. prepareDoneVideo;
  814. SetCursorType(crUnderLine);
  815. {$ifdef linux}
  816. if Console=ttylinux then
  817. SetCursorPos(0,0)
  818. else
  819. begin
  820. {$endif}
  821. SendEscapeSeqNdx(exit_ca_mode);
  822. SendEscapeSeqNdx(cursor_home);
  823. SendEscapeSeqNdx(cursor_normal);
  824. SendEscapeSeqNdx(cursor_visible);
  825. SendEscapeSeq(#27'[H');
  826. if cur_term_strings=@term_codes_linux then
  827. begin
  828. {Executed in case ttylinux is false (i.e. no vcsa), but
  829. TERM=linux.}
  830. {Enable the character set set through setfont}
  831. fpwrite(stdoutputhandle,font_custom,3);
  832. end;
  833. {$ifdef linux}
  834. end;
  835. {$endif}
  836. ACSIn:='';
  837. ACSOut:='';
  838. doneVideoDone;
  839. {$ifdef logging}
  840. close(f);
  841. {$endif logging}
  842. end;
  843. procedure SysClearScreen;
  844. begin
  845. {$ifdef linux}
  846. if Console=ttylinux then
  847. UpdateScreen(true)
  848. else
  849. begin
  850. {$endif}
  851. SendEscapeSeq(#27'[0m');
  852. SendEscapeSeqNdx(clear_screen);
  853. {$ifdef linux}
  854. end;
  855. {$endif}
  856. end;
  857. procedure SysUpdateScreen(Force: Boolean);
  858. var
  859. DoUpdate : boolean;
  860. i : longint;
  861. p1,p2 : plongint;
  862. begin
  863. (*
  864. if not force then
  865. begin
  866. {$ifdef cpui386}
  867. asm
  868. pushl %esi
  869. pushl %edi
  870. movl VideoBuf,%esi
  871. movl OldVideoBuf,%edi
  872. movl VideoBufSize,%ecx
  873. shrl $2,%ecx
  874. repe
  875. cmpsl
  876. setne DoUpdate
  877. popl %edi
  878. popl %esi
  879. end;
  880. {$else not cpui386}
  881. p1:=plongint(VideoBuf);
  882. p2:=plongint(OldVideoBuf);
  883. for i:=0 to VideoBufSize div 2 do
  884. if (p1^<>p2^) then
  885. begin
  886. DoUpdate:=true;
  887. break;
  888. end
  889. else
  890. begin
  891. { Inc does add sizeof(longint) to both pointer values }
  892. inc(p1);
  893. inc(p2);
  894. end;
  895. {$endif not cpui386}
  896. end
  897. else
  898. DoUpdate:=true;
  899. if not DoUpdate then
  900. exit;*)
  901. {$ifdef linux}
  902. if Console=ttylinux then
  903. update_vcsa(force)
  904. else
  905. {$endif}
  906. UpdateTTY(force);
  907. Move(VideoBuf^, OldVideoBuf^, VideoBufSize);
  908. end;
  909. function SysGetCapabilities: Word;
  910. begin
  911. { about cpColor... we should check the terminfo database... }
  912. SysGetCapabilities:=cpUnderLine + cpBlink + cpColor;
  913. end;
  914. procedure SysSetCursorPos(NewCursorX, NewCursorY: Word);
  915. var
  916. Pos : array [1..2] of Byte;
  917. begin
  918. if (CursorX=NewCursorX) and (CursorY=NewCursorY) then
  919. exit;
  920. {$ifdef linux}
  921. if Console=ttylinux then
  922. begin
  923. Pos[1]:=NewCursorX;
  924. Pos[2]:=NewCursorY;
  925. fppwrite(ttyfd,pos,2,2);
  926. end
  927. else
  928. {$endif}
  929. { newcursorx,y and CursorX,Y are 0 based ! }
  930. SendEscapeSeq(XY2Ansi(NewCursorX+1,NewCursorY+1,CursorX+1,CursorY+1));
  931. CursorX:=NewCursorX;
  932. CursorY:=NewCursorY;
  933. end;
  934. function SysGetCursorType: Word;
  935. begin
  936. SysGetCursorType:=LastCursorType;
  937. end;
  938. procedure SysSetCursorType(NewType: Word);
  939. begin
  940. If LastCursorType=NewType then
  941. exit;
  942. LastCursorType:=NewType;
  943. case NewType of
  944. crBlock :
  945. Begin
  946. If not SendEscapeSeqNdx(cursor_visible) then
  947. If Console<>ttyFreeBSD Then // should be done only for linux?
  948. SendEscapeSeq(#27'[?17;0;64c');
  949. End;
  950. crHidden :
  951. Begin
  952. If not SendEscapeSeqNdx(cursor_invisible) then
  953. If Console<>ttyFreeBSD Then
  954. SendEscapeSeq(#27'[?1c');
  955. End;
  956. else
  957. begin
  958. If not SendEscapeSeqNdx(cursor_normal) then
  959. If Console<>ttyFreeBSD Then
  960. SendEscapeSeq(#27'[?2c');
  961. end;
  962. end;
  963. end;
  964. Const
  965. SysVideoDriver : TVideoDriver = (
  966. InitDriver : @SysInitVideo;
  967. DoneDriver : @SysDoneVideo;
  968. UpdateScreen : @SysUpdateScreen;
  969. ClearScreen : @SysClearScreen;
  970. SetVideoMode : Nil;
  971. GetVideoModeCount : Nil;
  972. GetVideoModeData : Nil;
  973. SetCursorPos : @SysSetCursorPos;
  974. GetCursorType : @SysGetCursorType;
  975. SetCursorType : @SysSetCursorType;
  976. GetCapabilities : @SysGetCapabilities;
  977. );
  978. initialization
  979. SetVideoDriver(SysVideoDriver);
  980. end.