video.pp 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193
  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. Function Attr2Ansi(Attr,OAttr:longint):string;
  309. {
  310. Convert Attr to an Ansi String, the Optimal code is calculate
  311. with use of the old OAttr
  312. }
  313. var
  314. hstr : string[16];
  315. OFg,OBg,Fg,Bg : longint;
  316. procedure AddSep(ch:char);
  317. begin
  318. if length(hstr)>0 then
  319. hstr:=hstr+';';
  320. hstr:=hstr+ch;
  321. end;
  322. begin
  323. if Attr=OAttr then
  324. begin
  325. Attr2Ansi:='';
  326. exit;
  327. end;
  328. Hstr:='';
  329. Fg:=Attr and $f;
  330. Bg:=Attr shr 4;
  331. OFg:=OAttr and $f;
  332. OBg:=OAttr shr 4;
  333. if (OFg<>7) or (Fg=7) or ((OFg>7) and (Fg<8)) or ((OBg>7) and (Bg<8)) then
  334. begin
  335. hstr:='0';
  336. OFg:=7;
  337. OBg:=0;
  338. end;
  339. if (Fg>7) and (OFg<8) then
  340. begin
  341. AddSep('1');
  342. OFg:=OFg or 8;
  343. end;
  344. if (Bg and 8)<>(OBg and 8) then
  345. begin
  346. AddSep('5');
  347. OBg:=OBg or 8;
  348. end;
  349. if (Fg<>OFg) then
  350. begin
  351. AddSep('3');
  352. hstr:=hstr+AnsiTbl[fg and 7];
  353. end;
  354. if (Bg<>OBg) then
  355. begin
  356. AddSep('4');
  357. hstr:=hstr+AnsiTbl[bg and 7];
  358. end;
  359. if hstr='0' then
  360. hstr:='';
  361. Attr2Ansi:=#27'['+hstr+'m';
  362. end;
  363. procedure UpdateTTY(Force:boolean);
  364. type
  365. tchattr=packed record
  366. {$ifdef ENDIAN_LITTLE}
  367. ch : char;
  368. attr : byte;
  369. {$else}
  370. attr : byte;
  371. ch : char;
  372. {$endif}
  373. end;
  374. var
  375. outbuf : array[0..1023+255] of char;
  376. chattr : tchattr;
  377. skipped : boolean;
  378. outptr,
  379. spaces,
  380. eol,
  381. x,y,
  382. LastX,LastY,
  383. SpaceAttr,
  384. LastAttr : longint;
  385. p,pold : pvideocell;
  386. LastLineWidth : Longint;
  387. function transform_cp437_to_iso01(const st:string):string;
  388. var i:byte;
  389. c:char;
  390. converted:word;
  391. begin
  392. transform_cp437_to_iso01:='';
  393. for i:=1 to length(st) do
  394. begin
  395. c:=st[i];
  396. case c of
  397. #0..#31:
  398. converted:=convert_lowascii_to_iso01[c];
  399. #128..#255:
  400. converted:=convert_cp437_to_iso01[c];
  401. else
  402. converted:=byte(c);
  403. end;
  404. if converted and $ff00=$f800 then
  405. begin
  406. if not in_ACS then
  407. begin
  408. transform_cp437_to_iso01:=transform_cp437_to_iso01+ACSIn;
  409. in_ACS:=true;
  410. end;
  411. c:=char(converted and $ff);
  412. end
  413. else
  414. if in_ACS then
  415. begin
  416. transform_cp437_to_iso01:=transform_cp437_to_iso01+ACSOut+
  417. Attr2Ansi(LastAttr,0);
  418. in_ACS:=false;
  419. end;
  420. transform_cp437_to_iso01:=transform_cp437_to_iso01+c;
  421. end;
  422. end;
  423. function transform_cp850_to_iso01(const st:string):string;
  424. var i:byte;
  425. c:char;
  426. converted:word;
  427. begin
  428. transform_cp850_to_iso01:='';
  429. for i:=1 to length(st) do
  430. begin
  431. c:=st[i];
  432. case c of
  433. #0..#31:
  434. converted:=convert_lowascii_to_iso01[c];
  435. #128..#255:
  436. converted:=convert_cp850_to_iso01[c];
  437. else
  438. converted:=byte(c);
  439. end;
  440. if converted and $ff00=$f800 then
  441. begin
  442. if not in_ACS then
  443. begin
  444. transform_cp850_to_iso01:=transform_cp850_to_iso01+ACSIn;
  445. in_ACS:=true;
  446. end;
  447. end
  448. else
  449. if in_ACS then
  450. begin
  451. transform_cp850_to_iso01:=transform_cp850_to_iso01+ACSOut+
  452. Attr2Ansi(LastAttr,0);
  453. in_ACS:=false;
  454. end;
  455. c:=char(converted and $ff);
  456. transform_cp850_to_iso01:=transform_cp850_to_iso01+c;
  457. end;
  458. end;
  459. function transform_linuxlowascii_to_vga(const st:string):string;
  460. var i:byte;
  461. c:char;
  462. converted:word;
  463. begin
  464. transform_linuxlowascii_to_vga:='';
  465. for i:=1 to length(st) do
  466. begin
  467. c:=st[i];
  468. case c of
  469. #0..#31:
  470. converted:=convert_linuxlowascii_to_vga[c];
  471. else
  472. converted:=byte(c);
  473. end;
  474. c:=char(converted and $ff);
  475. transform_linuxlowascii_to_vga:=transform_linuxlowascii_to_vga+c;
  476. end;
  477. end;
  478. function transform(const hstr:string):string;
  479. begin
  480. case convert of
  481. cv_linuxlowascii_to_vga:
  482. transform:=transform_linuxlowascii_to_vga(hstr);
  483. cv_cp437_to_iso01:
  484. transform:=transform_cp437_to_iso01(hstr);
  485. cv_cp850_to_iso01:
  486. transform:=transform_cp850_to_iso01(hstr);
  487. else
  488. transform:=hstr;
  489. end;
  490. end;
  491. procedure outdata(hstr:string);
  492. begin
  493. If Length(HStr)>0 Then
  494. Begin
  495. while (eol>0) do
  496. begin
  497. hstr:=#13#10+hstr;
  498. dec(eol);
  499. end;
  500. { if (convert=cv_vga_to_acs) and (ACSIn<>'') and (ACSOut<>'') then
  501. transform_using_acs(Hstr);}
  502. move(hstr[1],outbuf[outptr],length(hstr));
  503. inc(outptr,length(hstr));
  504. if outptr>=1024 then
  505. begin
  506. {$ifdef logging}
  507. blockwrite(f,logstart[1],length(logstart));
  508. blockwrite(f,nl,1);
  509. blockwrite(f,outptr,sizeof(outptr));
  510. blockwrite(f,nl,1);
  511. blockwrite(f,outbuf,outptr);
  512. blockwrite(f,nl,1);
  513. {$endif logging}
  514. fpWrite(stdoutputhandle,outbuf,outptr);
  515. outptr:=0;
  516. end;
  517. end;
  518. end;
  519. procedure OutClr(c:byte);
  520. begin
  521. if c=LastAttr then
  522. exit;
  523. OutData(Attr2Ansi(c,LastAttr));
  524. LastAttr:=c;
  525. end;
  526. procedure OutSpaces;
  527. begin
  528. if (Spaces=0) then
  529. exit;
  530. OutClr(SpaceAttr);
  531. OutData(Space(Spaces));
  532. LastX:=x;
  533. LastY:=y;
  534. Spaces:=0;
  535. end;
  536. (*
  537. function GetTermString(ndx:Ttermcode):String;
  538. var
  539. P{,pdelay}: PChar;
  540. begin
  541. GetTermString:='';
  542. if not assigned(cur_term_Strings) then
  543. exit{RunError(219)};
  544. P:=cur_term_Strings^[Ndx];
  545. if assigned(p) then
  546. begin { Do not transmit the delays }
  547. { pdelay:=strpos(p,'$<');
  548. if assigned(pdelay) then
  549. pdelay^:=#0;}
  550. GetTermString:=StrPas(p);
  551. { if assigned(pdelay) then
  552. pdelay^:='$';}
  553. end;
  554. end;
  555. *)
  556. begin
  557. OutPtr:=0;
  558. Eol:=0;
  559. skipped:=true;
  560. p:=PVideoCell(VideoBuf);
  561. pold:=PVideoCell(OldVideoBuf);
  562. { init Attr, X,Y and set autowrap off }
  563. SendEscapeSeq(#27'[m'#27'[?7l'{#27'[H'} );
  564. // 1.0.x: SendEscapeSeq(#27'[m'{#27'[H'});
  565. LastAttr:=7;
  566. LastX:=-1;
  567. LastY:=-1;
  568. for y:=1 to ScreenHeight do
  569. begin
  570. SpaceAttr:=0;
  571. Spaces:=0;
  572. LastLineWidth:=ScreenWidth;
  573. If (y=ScreenHeight) And (Console=ttyFreeBSD) {And :am: is on} Then
  574. LastLineWidth:=ScreenWidth-2;
  575. for x:=1 to LastLineWidth do
  576. begin
  577. if (not force) and (p^=pold^) then
  578. begin
  579. if (Spaces>0) then
  580. OutSpaces;
  581. skipped:=true;
  582. end
  583. else
  584. begin
  585. if skipped then
  586. begin
  587. OutData(XY2Ansi(x,y,LastX,LastY));
  588. LastX:=x;
  589. LastY:=y;
  590. skipped:=false;
  591. end;
  592. chattr:=tchattr(p^);
  593. { if chattr.ch in [#0,#255] then
  594. chattr.ch:=' ';}
  595. if chattr.ch=' ' then
  596. begin
  597. if Spaces=0 then
  598. SpaceAttr:=chattr.Attr;
  599. if (chattr.attr and $f0)=(spaceattr and $f0) then
  600. chattr.Attr:=SpaceAttr
  601. else
  602. begin
  603. OutSpaces;
  604. SpaceAttr:=chattr.Attr;
  605. end;
  606. inc(Spaces);
  607. end
  608. else
  609. begin
  610. if (Spaces>0) then
  611. OutSpaces;
  612. { if ord(chattr.ch)<32 then
  613. begin
  614. Chattr.Attr:= $ff xor Chattr.Attr;
  615. ChAttr.ch:=chr(ord(chattr.ch)+ord('A')-1);
  616. end;}
  617. if LastAttr<>chattr.Attr then
  618. OutClr(chattr.Attr);
  619. OutData(transform(chattr.ch));
  620. LastX:=x+1;
  621. LastY:=y;
  622. end;
  623. p^:=tvideocell(chattr);
  624. end;
  625. inc(p);
  626. inc(pold);
  627. end;
  628. if (Spaces>0) then
  629. OutSpaces;
  630. if force then
  631. inc(eol)
  632. else
  633. skipped:=true;
  634. end;
  635. eol:=0;
  636. {if am in capabilities? Then}
  637. if (Console=ttyFreeBSD) and (Plongint(p)^<>plongint(pold)^) Then
  638. begin
  639. OutData(XY2Ansi(ScreenWidth,ScreenHeight,LastX,LastY));
  640. OutData(#8);
  641. {Output last char}
  642. chattr:=tchattr(p[1]);
  643. if LastAttr<>chattr.Attr then
  644. OutClr(chattr.Attr);
  645. OutData(transform(chattr.ch));
  646. inc(LastX);
  647. // OutData(XY2Ansi(ScreenWidth-1,ScreenHeight,LastX,LastY));
  648. // OutData(GetTermString(Insert_character));
  649. OutData(#8+#27+'[1@');
  650. chattr:=tchattr(p^);
  651. if LastAttr<>chattr.Attr then
  652. OutClr(chattr.Attr);
  653. OutData(transform(chattr.ch));
  654. inc(LastX);
  655. end;
  656. OutData(XY2Ansi(CursorX+1,CursorY+1,LastX,LastY));
  657. if in_ACS then
  658. begin
  659. {If the program crashes and the ACS is still enabled, the user's
  660. keyboard will output strange characters. Therefore we disable the
  661. acs after each screen update, so the risk that it happens is greatly
  662. reduced.}
  663. { SendEscapeSeqNdx(exit_alt_charset_mode);}
  664. outdata(acsout);
  665. in_acs:=false;
  666. end;
  667. {$ifdef logging}
  668. blockwrite(f,logstart[1],length(logstart));
  669. blockwrite(f,nl,1);
  670. blockwrite(f,outptr,sizeof(outptr));
  671. blockwrite(f,nl,1);
  672. blockwrite(f,outbuf,outptr);
  673. blockwrite(f,nl,1);
  674. {$endif logging}
  675. fpWrite(stdoutputhandle,outbuf,outptr);
  676. {turn autowrap on}
  677. // SendEscapeSeq(#27'[?7h');
  678. end;
  679. {$ifdef linux}
  680. procedure update_vcsa(force:boolean);
  681. const max_updates=64;
  682. label update,update_all,equal_loop,unequal_loop;
  683. var position,update_count,i:word;
  684. update_positions:array[0..max_updates-1] of word;
  685. update_lengths:array[0..max_updates-1] of word;
  686. begin
  687. if force then
  688. goto update_all;
  689. update_count:=0;
  690. i:=0;
  691. equal_loop:
  692. repeat
  693. if videobuf^[i]<>oldvideobuf^[i] then
  694. goto unequal_loop;
  695. inc(i);
  696. until i>videobufsize div 2;
  697. goto update;
  698. unequal_loop:
  699. if update_count>=max_updates then
  700. goto update_all;
  701. update_positions[update_count]:=i;
  702. update_lengths[update_count]:=0;
  703. inc(update_count);
  704. repeat
  705. if videobuf^[i]=oldvideobuf^[i] then
  706. goto equal_loop;
  707. inc(i);
  708. inc(update_lengths[update_count-1]);
  709. until i>videobufsize div 2;
  710. update:
  711. for i:=1 to update_count do
  712. begin
  713. position:=update_positions[i-1];
  714. fppwrite(ttyfd,videobuf^[position],update_lengths[i-1]*2,4+position*2);
  715. end;
  716. exit;
  717. update_all:
  718. fppwrite(ttyfd,videobuf^,videobufsize,4);
  719. end;
  720. {$endif}
  721. var
  722. preInitVideoTio, postInitVideoTio: termio.termios;
  723. inputRaw, outputRaw: boolean;
  724. procedure saveRawSettings(const tio: termio.termios);
  725. begin
  726. with tio do
  727. begin
  728. inputRaw :=
  729. ((c_iflag and (IGNBRK or BRKINT or PARMRK or ISTRIP or
  730. INLCR or IGNCR or ICRNL or IXON)) = 0) and
  731. ((c_lflag and (ECHO or ECHONL or ICANON or ISIG or IEXTEN)) = 0);
  732. outPutRaw :=
  733. ((c_oflag and OPOST) = 0) and
  734. ((c_cflag and (CSIZE or PARENB)) = 0) and
  735. ((c_cflag and CS8) <> 0);
  736. end;
  737. end;
  738. procedure restoreRawSettings(tio: termio.termios);
  739. begin
  740. with tio do
  741. begin
  742. if inputRaw then
  743. begin
  744. c_iflag := c_iflag and (not (IGNBRK or BRKINT or PARMRK or ISTRIP or
  745. INLCR or IGNCR or ICRNL or IXON));
  746. c_lflag := c_lflag and
  747. (not (ECHO or ECHONL or ICANON or ISIG or IEXTEN));
  748. c_cc[VMIN]:=1;
  749. c_cc[VTIME]:=0;
  750. end;
  751. if outPutRaw then
  752. begin
  753. c_oflag := c_oflag and not(OPOST);
  754. c_cflag := c_cflag and not(CSIZE or PARENB) or CS8;
  755. end;
  756. end;
  757. TCSetAttr(1,TCSANOW,tio);
  758. end;
  759. procedure decide_codepages;
  760. var s:string;
  761. begin
  762. {$ifdef linux}
  763. if console=ttyLinux then
  764. begin
  765. s:=upcase(fpgetenv('CONSOLEFONT_CP'));
  766. if s='CP437' then
  767. external_codepage:=cp437
  768. else if s='CP850' then
  769. external_codepage:=cp850;
  770. end;
  771. {$endif}
  772. {A non-vcsa Linux console can display most control characters, but not all.}
  773. if {$ifdef linux}(console<>ttyLinux) and{$endif}
  774. (cur_term_strings=@term_codes_linux) then
  775. convert:=cv_linuxlowascii_to_vga;
  776. case external_codepage of
  777. iso01: {West Europe}
  778. begin
  779. internal_codepage:=cp850;
  780. convert:=cv_cp850_to_iso01;
  781. end;
  782. iso02: {East Europe}
  783. internal_codepage:=cp852;
  784. iso05: {Cyrillic}
  785. internal_codepage:=cp866;
  786. else
  787. if internal_codepage in vga_codepages then
  788. internal_codepage:=external_codepage
  789. else
  790. {We don't know how to convert to the external codepage. Use codepage
  791. 437 in the hope that the actual font has similarity to codepage 437.}
  792. internal_codepage:=cp437;
  793. end;
  794. end;
  795. procedure prepareInitVideo;
  796. begin
  797. TCGetAttr(1,preInitVideoTio);
  798. saveRawSettings(preInitVideoTio);
  799. end;
  800. procedure videoInitDone;
  801. begin
  802. TCGetAttr(1,postInitVideoTio);
  803. restoreRawSettings(postInitVideoTio);
  804. end;
  805. procedure prepareDoneVideo;
  806. var
  807. tio: termio.termios;
  808. begin
  809. TCGetAttr(1,tio);
  810. saveRawSettings(tio);
  811. TCSetAttr(1,TCSANOW,postInitVideoTio);
  812. end;
  813. procedure doneVideoDone;
  814. begin
  815. restoreRawSettings(preInitVideoTio);
  816. end;
  817. procedure SysInitVideo;
  818. var
  819. FName: String;
  820. WS: packed record
  821. ws_row, ws_col, ws_xpixel, ws_ypixel: Word;
  822. end;
  823. { Err: Longint;}
  824. { prev_term : TerminalCommon_ptr1;}
  825. term:string;
  826. i:word;
  827. {$ifdef Linux}
  828. s:string[15];
  829. {$endif}
  830. {$ifdef freebsd}
  831. ThisTTY: String[30];
  832. {$endif}
  833. const font_vga:array[0..11] of char=#15#27'%@'#27'(U'#27'[3h';
  834. font_custom:array[0..2] of char=#27'(K';
  835. begin
  836. { check for tty }
  837. if (IsATTY(stdinputhandle)=1) then
  838. begin
  839. { save current terminal characteristics and remove rawness }
  840. prepareInitVideo;
  841. { running on a tty, find out whether locally or remotely }
  842. TTyfd:=-1;
  843. Console:=TTyNetwork; {Default: Network or other vtxxx tty}
  844. cur_term_strings:=@term_codes_vt100; {Default: vt100}
  845. external_codepage:=iso01; {Default: ISO-8859-1}
  846. {$ifdef linux}
  847. if vcs_device>=0 then
  848. begin
  849. str(vcs_device,s);
  850. fname:='/dev/vcsa'+s;
  851. { open console, $1b6=rw-rw-rw- }
  852. ttyfd:=fpopen(fname,$1b6,O_RDWR);
  853. if ttyfd<>-1 then
  854. console:=ttylinux
  855. else
  856. if try_grab_vcsa then
  857. begin
  858. ttyfd:=fpopen(fname,$1b6,O_RDWR);
  859. if ttyfd<>-1 then
  860. begin
  861. console:=ttylinux;
  862. external_codepage:=cp437; {VCSA defaults to codepage 437.}
  863. end;
  864. end;
  865. end;
  866. {$endif}
  867. {$ifdef freebsd}
  868. ThisTTY:=TTYName(stdinputhandle);
  869. if copy(ThisTTY, 1, 9) = '/dev/ttyv' then {FreeBSD has these}
  870. begin
  871. { check for (Free?)BSD native}
  872. if (ThisTTY[10]>='0') and (ThisTTY[10]<='9') Then
  873. Console:=ttyFreeBSD; {TTYFd ?}
  874. end;
  875. {$endif}
  876. term:=fpgetenv('TERM');
  877. for i:=low(terminal_names) to high(terminal_names) do
  878. if copy(term,1,length(terminal_names[i]))=terminal_names[i] then
  879. cur_term_strings:=terminal_data[i];
  880. if cur_term_strings=@term_codes_freebsd then
  881. console:=ttyFreeBSD;
  882. {$ifdef linux}
  883. if (console<>ttylinux) then
  884. begin
  885. {$endif}
  886. if cur_term_strings=@term_codes_linux then
  887. begin
  888. {Executed in case ttylinux is false (i.e. no vcsa), but
  889. TERM=linux.}
  890. {Enable the VGA character set (codepage 437,850,....)}
  891. fpwrite(stdoutputhandle,font_vga,sizeof(font_vga));
  892. external_codepage:=cp437; {Now default to codepage 437.}
  893. end
  894. else
  895. {No VGA font :( }
  896. fpwrite(stdoutputhandle,font_custom,sizeof(font_vga));
  897. { running on a remote terminal, no error with /dev/vcsa }
  898. {$ifdef linux}
  899. end;
  900. {$endif}
  901. fpioctl(stdinputhandle, TIOCGWINSZ, @WS);
  902. if WS.ws_Col=0 then
  903. WS.ws_Col:=80;
  904. if WS.ws_Row=0 then
  905. WS.ws_Row:=25;
  906. ScreenWidth:=WS.ws_Col;
  907. { TDrawBuffer only has FVMaxWidth elements
  908. larger values lead to crashes }
  909. if ScreenWidth> FVMaxWidth then
  910. ScreenWidth:=FVMaxWidth;
  911. ScreenHeight:=WS.ws_Row;
  912. CursorX:=0;
  913. CursorY:=0;
  914. LastCursorType:=$ff;
  915. ScreenColor:=True;
  916. { Start with a clear screen }
  917. {$ifdef linux}
  918. if Console<>ttylinux then
  919. begin
  920. {$endif}
  921. SendEscapeSeqNdx(cursor_home);
  922. SendEscapeSeqNdx(cursor_normal);
  923. SendEscapeSeqNdx(cursor_visible_underline);
  924. SendEscapeSeqNdx(enter_ca_mode);
  925. SetCursorType(crUnderLine);
  926. If Console=ttyFreeBSD Then
  927. SendEscapeSeqNdx(exit_am_mode);
  928. {$ifdef linux}
  929. end;
  930. {$endif}
  931. { Always true because of vt100 default...
  932. if assigned(cur_term_Strings) then
  933. begin}
  934. ACSIn:=StrPas(cur_term_strings^[enter_alt_charset_mode]);
  935. ACSOut:=StrPas(cur_term_strings^[exit_alt_charset_mode]);
  936. if (ACSIn<>'') and (ACSOut<>'') then
  937. SendEscapeSeqNdx(ena_acs);
  938. (* If fpGetEnv('TERM')='xterm' then
  939. convert:=cv_vga_to_acs; {use of acs for xterm is ok}*)
  940. { end
  941. else
  942. begin
  943. ACSIn:='';
  944. ACSOut:='';
  945. end;}
  946. {$ifdef logging}
  947. assign(f,'video.log');
  948. rewrite(f,1);
  949. {$endif logging}
  950. { save new terminal characteristics and possible restore rawness }
  951. videoInitDone;
  952. end
  953. else
  954. ErrorCode:=errVioInit; { not a TTY }
  955. decide_codepages;
  956. end;
  957. procedure SysDoneVideo;
  958. var font_custom:array[0..2] of char=#27'(K';
  959. begin
  960. prepareDoneVideo;
  961. SetCursorType(crUnderLine);
  962. {$ifdef linux}
  963. if Console=ttylinux then
  964. SetCursorPos(0,0)
  965. else
  966. begin
  967. {$endif}
  968. SendEscapeSeqNdx(exit_ca_mode);
  969. SendEscapeSeqNdx(cursor_home);
  970. SendEscapeSeqNdx(cursor_normal);
  971. SendEscapeSeqNdx(cursor_visible_underline);
  972. SendEscapeSeq(#27'[H');
  973. if cur_term_strings=@term_codes_linux then
  974. begin
  975. {Executed in case ttylinux is false (i.e. no vcsa), but
  976. TERM=linux.}
  977. {Enable the character set set through setfont}
  978. fpwrite(stdoutputhandle,font_custom,3);
  979. end;
  980. {$ifdef linux}
  981. end;
  982. {$endif}
  983. ACSIn:='';
  984. ACSOut:='';
  985. doneVideoDone;
  986. {$ifdef logging}
  987. close(f);
  988. {$endif logging}
  989. end;
  990. procedure SysClearScreen;
  991. begin
  992. {$ifdef linux}
  993. if Console=ttylinux then
  994. UpdateScreen(true)
  995. else
  996. begin
  997. {$endif}
  998. SendEscapeSeq(#27'[0m');
  999. SendEscapeSeqNdx(clear_screen);
  1000. {$ifdef linux}
  1001. end;
  1002. {$endif}
  1003. end;
  1004. procedure SysUpdateScreen(Force: Boolean);
  1005. var
  1006. DoUpdate : boolean;
  1007. i : longint;
  1008. p1,p2 : plongint;
  1009. begin
  1010. {$ifdef linux}
  1011. if console=ttylinux then
  1012. update_vcsa(force)
  1013. else
  1014. {$endif}
  1015. updateTTY(force);
  1016. move(VideoBuf^,OldVideoBuf^,VideoBufSize);
  1017. end;
  1018. function SysGetCapabilities: Word;
  1019. begin
  1020. { about cpColor... we should check the terminfo database... }
  1021. SysGetCapabilities:=cpUnderLine + cpBlink + cpColor;
  1022. end;
  1023. procedure SysSetCursorPos(NewCursorX, NewCursorY: Word);
  1024. var
  1025. Pos : array [1..2] of Byte;
  1026. begin
  1027. if (CursorX=NewCursorX) and (CursorY=NewCursorY) then
  1028. exit;
  1029. {$ifdef linux}
  1030. if Console=ttylinux then
  1031. begin
  1032. Pos[1]:=NewCursorX;
  1033. Pos[2]:=NewCursorY;
  1034. fppwrite(ttyfd,pos,2,2);
  1035. end
  1036. else
  1037. {$endif}
  1038. { newcursorx,y and CursorX,Y are 0 based ! }
  1039. SendEscapeSeq(XY2Ansi(NewCursorX+1,NewCursorY+1,CursorX+1,CursorY+1));
  1040. CursorX:=NewCursorX;
  1041. CursorY:=NewCursorY;
  1042. end;
  1043. function SysGetCursorType: Word;
  1044. begin
  1045. SysGetCursorType:=LastCursorType;
  1046. end;
  1047. procedure SysSetCursorType(NewType: Word);
  1048. begin
  1049. If LastCursorType=NewType then
  1050. exit;
  1051. LastCursorType:=NewType;
  1052. case NewType of
  1053. crBlock:
  1054. SendEscapeSeqNdx(cursor_visible_block);
  1055. crHidden:
  1056. SendEscapeSeqNdx(cursor_invisible);
  1057. else
  1058. SendEscapeSeqNdx(cursor_normal);
  1059. end;
  1060. end;
  1061. Const
  1062. SysVideoDriver : TVideoDriver = (
  1063. InitDriver : @SysInitVideo;
  1064. DoneDriver : @SysDoneVideo;
  1065. UpdateScreen : @SysUpdateScreen;
  1066. ClearScreen : @SysClearScreen;
  1067. SetVideoMode : Nil;
  1068. GetVideoModeCount : Nil;
  1069. GetVideoModeData : Nil;
  1070. SetCursorPos : @SysSetCursorPos;
  1071. GetCursorType : @SysGetCursorType;
  1072. SetCursorType : @SysSetCursorType;
  1073. GetCapabilities : @SysGetCapabilities;
  1074. );
  1075. initialization
  1076. SetVideoDriver(SysVideoDriver);
  1077. end.