video.pp 35 KB

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