video.pp 35 KB

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