video.pp 35 KB

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