video.pp 29 KB

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