You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1144 rivejä
39 KiB

  1. /*
  2. * neercs console-based window manager
  3. * Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net>
  4. * 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org>
  5. * 2008-2010 Pascal Terjan <pterjan@linuxfr.org>
  6. * All Rights Reserved
  7. *
  8. * This program is free software. It comes without any warranty, to
  9. * the extent permitted by applicable law. You can redistribute it
  10. * and/or modify it under the terms of the Do What The Fuck You Want
  11. * To Public License, Version 2, as published by Sam Hocevar. See
  12. * http://sam.zoy.org/wtfpl/COPYING for more details.
  13. */
  14. #if defined HAVE_CONFIG_H
  15. # include "config.h"
  16. #endif
  17. #include <string.h>
  18. #include "core.h"
  19. using namespace std;
  20. using namespace lol;
  21. #include "../neercs.h"
  22. #include "term.h"
  23. /* DEC ACS with common extensions */
  24. static uint32_t dec_acs(uint32_t uc)
  25. {
  26. switch (uc)
  27. {
  28. case '+':
  29. return 0x2192; /* RIGHTWARDS ARROW */
  30. case ',':
  31. return 0x2190; /* LEFTWARDS ARROW */
  32. case '-':
  33. return 0x2191; /* UPWARDS ARROW */
  34. case '.':
  35. return 0x2193; /* DOWNWARDS ARROW */
  36. case '0':
  37. return 0x25AE; /* BLACK VERTICAL RECTANGLE */
  38. case '_':
  39. return 0x25AE; /* BLACK VERTICAL RECTANGLE */
  40. case '`':
  41. return 0x25C6; /* BLACK DIAMOND */
  42. case 'a':
  43. return 0x2592; /* MEDIUM SHADE */
  44. case 'b':
  45. return 0x2409; /* SYMBOL FOR HORIZONTAL TABULATION */
  46. case 'c':
  47. return 0x240C; /* SYMBOL FOR FORM FEED */
  48. case 'd':
  49. return 0x240D; /* SYMBOL FOR CARRIAGE RETURN */
  50. case 'e':
  51. return 0x240A; /* SYMBOL FOR LINE FEED */
  52. case 'f':
  53. return 0x00B0; /* DEGREE SIGN */
  54. case 'g':
  55. return 0x00B1; /* PLUS-MINUS SIGN */
  56. case 'h':
  57. return 0x2424; /* SYMBOL FOR NEWLINE */
  58. case 'i':
  59. return 0x240B; /* SYMBOL FOR VERTICAL TABULATION */
  60. case 'j':
  61. return 0x2518; /* BOX DRAWINGS LIGHT UP AND LEFT */
  62. case 'k':
  63. return 0x2510; /* BOX DRAWINGS LIGHT DOWN AND LEFT */
  64. case 'l':
  65. return 0x250C; /* BOX DRAWINGS LIGHT DOWN AND RIGHT */
  66. case 'm':
  67. return 0x2514; /* BOX DRAWINGS LIGHT UP AND RIGHT */
  68. case 'n':
  69. return 0x253C; /* BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
  70. case 'o':
  71. return 0x23BA; /* HORIZONTAL SCAN LINE-1 */
  72. case 'p':
  73. return 0x23BB; /* HORIZONTAL SCAN LINE-3 */
  74. case 'q':
  75. return 0x2500; /* BOX DRAWINGS LIGHT HORIZONTAL */
  76. case 'r':
  77. return 0x23BC; /* HORIZONTAL SCAN LINE-7 */
  78. case 's':
  79. return 0x23BD; /* HORIZONTAL SCAN LINE-9 */
  80. case 't':
  81. return 0x251C; /* BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
  82. case 'u':
  83. return 0x2524; /* BOX DRAWINGS LIGHT VERTICAL AND LEFT */
  84. case 'v':
  85. return 0x2534; /* BOX DRAWINGS LIGHT UP AND HORIZONTAL */
  86. case 'w':
  87. return 0x252C; /* BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
  88. case 'x':
  89. return 0x2502; /* BOX DRAWINGS LIGHT VERTICAL */
  90. case 'y':
  91. return 0x2264; /* LESS-THAN OR EQUAL TO */
  92. case 'z':
  93. return 0x2265; /* GREATER-THAN OR EQUAL TO */
  94. case '{':
  95. return 0x03C0; /* GREEK SMALL LETTER PI */
  96. case '|':
  97. return 0x2260; /* NOT EQUAL TO */
  98. case '}':
  99. return 0x00A3; /* POUND SIGN */
  100. case '~':
  101. return 0x00B7; /* MIDDLE DOT */
  102. default:
  103. return uc;
  104. }
  105. };
  106. #define LITERAL2CHAR(i0,i1) (((i0) << 8) | (i1))
  107. #define LITERAL3CHAR(i0,i1,i2) LITERAL2CHAR(LITERAL2CHAR(i0, i1), i2)
  108. inline int Term::ReadChar(unsigned char c, int *x, int *y)
  109. {
  110. if (c == '\r')
  111. {
  112. *x = 0;
  113. }
  114. else if (c == '\n')
  115. {
  116. *x = 0;
  117. *y = *y + 1;
  118. }
  119. else if (c == '\a')
  120. {
  121. /* FIXME TODO: in_bell ? */
  122. // if (!m_bell)
  123. // screen_list->in_bell = 10;
  124. m_bell = 1;
  125. }
  126. else if (c == '\t')
  127. {
  128. *x = (*x + 7) & ~7;
  129. }
  130. else if (c == '\x08')
  131. {
  132. if (*x > 0)
  133. *x = *x - 1;
  134. }
  135. else if (c == '\x0b')
  136. {
  137. /* Vertical tab */
  138. /* Not sure about the real meaning of it, just y++ for now */
  139. if (*y < caca_get_canvas_height(m_caca))
  140. *y = *y + 1;
  141. }
  142. else if (c == '\x0e')
  143. {
  144. /* Shift Out (Ctrl-N) -> Switch to Alternate Character Set: invokes
  145. the G1 character set. */
  146. m_conv_state.glr[0] = 1;
  147. }
  148. else if (c == '\x0f')
  149. {
  150. /* Shift In (Ctrl-O) -> Switch to Standard Character Set: invokes the
  151. G0 character set. */
  152. m_conv_state.glr[0] = 0;
  153. }
  154. else
  155. {
  156. return 1;
  157. }
  158. return 0;
  159. }
  160. inline int Term::ReadDuplet(unsigned char const *buffer, unsigned int *skip,
  161. int top, int bottom, int width, int height)
  162. {
  163. int i = 0, j, k;
  164. unsigned int dummy = 0;
  165. /* Single Shift Select of G2 Character Set (SS2: 0x8e): affects next
  166. character only */
  167. if (buffer[i] == '\033' && buffer[i + 1] == 'N')
  168. {
  169. m_conv_state.ss = 2;
  170. *skip += 1;
  171. }
  172. /* Reverse Index (RI) go up one line, reverse scroll if necessary */
  173. else if (buffer[i] == '\033' && buffer[i + 1] == 'M')
  174. {
  175. /* FIXME : not sure about the meaning of 'go up one line' and 'if
  176. necessary' words. Implemented as a scroller only. */
  177. for (j = bottom - 1; j > top; j--)
  178. {
  179. for (k = 0; k < width; k++)
  180. {
  181. caca_put_char(m_caca, k, j, caca_get_char(m_caca, k, j - 1));
  182. caca_put_attr(m_caca, k, j, caca_get_attr(m_caca, k, j - 1));
  183. }
  184. }
  185. caca_draw_line(m_caca, 0, top - 1, width - 1, top - 1, ' ');
  186. *skip += 1;
  187. }
  188. /* Single Shift Select of G3 Character Set (SS2: 0x8f): affects next
  189. character only */
  190. else if (buffer[i] == '\033' && buffer[i + 1] == 'O')
  191. {
  192. m_conv_state.ss = 3;
  193. *skip += 1;
  194. }
  195. /* LOCKING-SHIFT TWO (LS2), ISO 2022, ECMA-48 (1986), ISO 6429 : 1988 */
  196. else if (buffer[i] == '\033' && buffer[i + 1] == 'n')
  197. {
  198. m_conv_state.glr[0] = 2;
  199. *skip += 1;
  200. }
  201. /* LOCKING-SHIFT THREE (LS3) ISO 2022, ECMA-48 (1986), ISO 6429 : 1988 */
  202. else if (buffer[i] == '\033' && buffer[i + 1] == 'o')
  203. {
  204. m_conv_state.glr[0] = 3;
  205. *skip += 1;
  206. }
  207. /* RESET TO INITIAL STATE (RIS), ECMA-48 (1986), ISO 6429 : 1988 */
  208. else if (buffer[i] == '\033' && buffer[i + 1] == 'c')
  209. {
  210. m_dfg = CACA_DEFAULT;
  211. m_dbg = CACA_DEFAULT;
  212. caca_set_color_ansi(m_caca, m_dfg, m_dbg);
  213. m_clearattr = caca_get_attr(m_caca, -1, -1);
  214. ReadGrcm(1, &dummy);
  215. m_conv_state.Reset();
  216. *skip += 1;
  217. }
  218. /* Coding Method Delimiter (CMD), ECMA-48 (1991), ISO/IEC 6429:1992 (ISO
  219. IR 189) */
  220. else if (buffer[i] == '\033' && buffer[i + 1] == 'd')
  221. {
  222. m_conv_state.Reset();
  223. *skip += 1;
  224. }
  225. else
  226. {
  227. return 1;
  228. }
  229. return 0;
  230. }
  231. size_t Term::ReadAnsi(void const *data, size_t size)
  232. {
  233. unsigned char const *buffer = (unsigned char const *)data;
  234. unsigned int i, j, k, skip, dummy = 0;
  235. unsigned int width, height, top, bottom;
  236. uint32_t savedattr;
  237. int x = 0, y = 0, save_x = 0, save_y = 0;
  238. char b[100];
  239. debug("ansi : import_term\n");
  240. width = caca_get_canvas_width(m_caca);
  241. height = caca_get_canvas_height(m_caca);
  242. x = caca_get_cursor_x(m_caca);
  243. y = caca_get_cursor_y(m_caca);
  244. top = 1;
  245. bottom = height;
  246. if (!m_init)
  247. {
  248. m_dfg = CACA_LIGHTGRAY;
  249. m_dbg = CACA_BLACK;
  250. caca_set_color_ansi(m_caca, m_dfg, m_dbg);
  251. m_clearattr = caca_get_attr(m_caca, -1, -1);
  252. ReadGrcm(1, &dummy);
  253. m_conv_state.Reset();
  254. m_init = 1;
  255. }
  256. for (i = 0; i < size; i += skip)
  257. {
  258. uint32_t ch = 0;
  259. int wch = 0;
  260. skip = 1;
  261. /* Control codes (ASCII < \x20) */
  262. if (!ReadChar(buffer[i], &x, &y))
  263. {
  264. }
  265. /* If there are not enough characters to parse the escape sequence,
  266. wait until the next try. We require 3. */
  267. else if (buffer[i] == '\033' && i + 2 >= size)
  268. break;
  269. else if (!ReadDuplet(&buffer[i], &skip, top, bottom, width, height))
  270. {
  271. }
  272. /* GZDM4, G0-Designators, multi, 94^n chars [grandfathered short form
  273. from ISO 2022:1986] */
  274. else if (buffer[i] == '\033' && buffer[i + 1] == '$'
  275. && (buffer[i + 2] >= '@') && (buffer[i + 2] <= 'C'))
  276. {
  277. m_conv_state.gn[0] = LITERAL2CHAR('$', buffer[i + 2]);
  278. skip += 2;
  279. }
  280. /* GnDMx Gn-Designators, 9x^n chars; need one more char to distinguish
  281. these */
  282. else if (buffer[i] == '\033' && buffer[i + 1] == '$'
  283. && (i + 3 >= size))
  284. break;
  285. /* GZD4 G0-Designator, 94 chars */
  286. else if (buffer[i] == '\033' && buffer[i + 1] == '(')
  287. {
  288. m_conv_state.gn[0] = buffer[i + 2];
  289. skip += 2;
  290. }
  291. /* G1D4 G1-Designator, 94 chars */
  292. else if (buffer[i] == '\033' && buffer[i + 1] == ')')
  293. {
  294. m_conv_state.gn[1] = buffer[i + 2];
  295. skip += 2;
  296. }
  297. /* G2D4 G2-Designator, 94 chars */
  298. else if (buffer[i] == '\033' && buffer[i + 1] == '*')
  299. {
  300. m_conv_state.gn[2] = buffer[i + 2];
  301. skip += 2;
  302. }
  303. /* G3D4 G3-Designator, 94 chars */
  304. else if (buffer[i] == '\033' && buffer[i + 1] == '+')
  305. {
  306. m_conv_state.gn[3] = buffer[i + 2];
  307. skip += 2;
  308. }
  309. /* G2D6 G2-Designator, 96 chars */
  310. else if (buffer[i] == '\033' && buffer[i + 1] == '.')
  311. {
  312. m_conv_state.gn[2] = LITERAL2CHAR('.', buffer[i + 2]);
  313. skip += 2;
  314. }
  315. /* G3D6 G3-Designator, 96 chars */
  316. else if (buffer[i] == '\033' && buffer[i + 1] == '/')
  317. {
  318. m_conv_state.gn[3] = LITERAL2CHAR('.', buffer[i + 2]);
  319. skip += 2;
  320. }
  321. /* GZDM4 G0-Designator, 94^n chars */
  322. else if (buffer[i] == '\033' && buffer[i + 1] == '$'
  323. && buffer[i + 2] == '(')
  324. {
  325. m_conv_state.gn[0] = LITERAL2CHAR('$', buffer[i + 3]);
  326. skip += 3;
  327. }
  328. /* G1DM4 G1-Designator, 94^n chars */
  329. else if (buffer[i] == '\033' && buffer[i + 1] == '$'
  330. && buffer[i + 2] == ')')
  331. {
  332. m_conv_state.gn[1] = LITERAL2CHAR('$', buffer[i + 3]);
  333. skip += 3;
  334. }
  335. /* G2DM4 G2-Designator, 94^n chars */
  336. else if (buffer[i] == '\033' && buffer[i + 1] == '$'
  337. && buffer[i + 2] == '*')
  338. {
  339. m_conv_state.gn[2] = LITERAL2CHAR('$', buffer[i + 3]);
  340. skip += 3;
  341. }
  342. /* G3DM4 G3-Designator, 94^n chars */
  343. else if (buffer[i] == '\033' && buffer[i + 1] == '$'
  344. && buffer[i + 2] == '+')
  345. {
  346. m_conv_state.gn[3] = LITERAL2CHAR('$', buffer[i + 3]);
  347. skip += 3;
  348. }
  349. /* G2DM6 G2-Designator, 96^n chars */
  350. else if (buffer[i] == '\033' && buffer[i + 1] == '$'
  351. && buffer[i + 2] == '.')
  352. {
  353. m_conv_state.gn[2] = LITERAL3CHAR('$', '.', buffer[i + 3]);
  354. skip += 3;
  355. }
  356. /* G3DM6 G3-Designator, 96^n chars */
  357. else if (buffer[i] == '\033' && buffer[i + 1] == '$'
  358. && buffer[i + 2] == '/')
  359. {
  360. m_conv_state.gn[3] = LITERAL3CHAR('$', '.', buffer[i + 3]);
  361. skip += 3;
  362. }
  363. else if (buffer[i] == '\033' && buffer[i + 1] == '#')
  364. {
  365. debug("ansi private '#' sequence\n");
  366. switch (buffer[i + 2])
  367. {
  368. case '8': /* DECALN Fills the entire screen area with
  369. uppercase Es for screen focus and
  370. alignment. */
  371. for (j = 0; j < height; j++)
  372. {
  373. for (k = 0; k < width; k++)
  374. {
  375. caca_put_char(m_caca, k, j, 'E');
  376. }
  377. }
  378. x = 0;
  379. y = 0;
  380. skip += 2;
  381. break;
  382. default:
  383. debug("Unknow private sequence 'ESC#%c'\n", buffer[i + 2]);
  384. continue;
  385. }
  386. }
  387. /* Interpret escape commands, as per Standard ECMA-48 "Control
  388. Functions for Coded Character Sets", 5.4. Control sequences. */
  389. else if (buffer[i] == '\033' && buffer[i + 1] == '[')
  390. {
  391. unsigned int argc = 0, argv[101];
  392. unsigned int param, inter, junk, final;
  393. if (buffer[i + 2] == '?')
  394. {
  395. debug("CSI? %c%c%c%c%c\n",
  396. buffer[i + 3], buffer[i + 4], buffer[i + 5],
  397. buffer[i + 6], buffer[i + 7]);
  398. }
  399. /* Compute offsets to parameter bytes, intermediate bytes and to
  400. the final byte. Only the final byte is mandatory, there can be
  401. zero of the others. 0 param=2 inter final final+1
  402. +-----+------------------+---------------------+-----------------+
  403. | CSI | parameter bytes | intermediate bytes | final byte | | |
  404. 0x30 - 0x3f | 0x20 - 0x2f | 0x40 - 0x7e | | ^[[ | 0123456789:;<=>?
  405. | SPC !"#$%&'()*+,-./ | azAZ@[\]^_`{|}~ |
  406. +-----+------------------+---------------------+-----------------+ */
  407. param = 2;
  408. /* vttest use to interleave control characters (\014 CR or \010
  409. BS) into CSI sequences, either directly after ESC[ or after
  410. param. Can't find anything related to this in any documentation
  411. nor XTerm sources, thought. */
  412. for (junk = param; i + junk < size; junk++)
  413. if (buffer[i + junk] < 0x20)
  414. {
  415. ReadChar(buffer[i + junk], &x, &y);
  416. }
  417. else
  418. {
  419. break;
  420. }
  421. /* Intermediate offset */
  422. for (inter = junk; i + inter < size; inter++)
  423. if (buffer[i + inter] < 0x30 || buffer[i + inter] > 0x3f)
  424. {
  425. break;
  426. }
  427. /* Interleaved character */
  428. for (junk = inter; i + junk < size; junk++)
  429. if (buffer[i + junk] < 0x20)
  430. {
  431. ReadChar(buffer[i + junk], &x, &y);
  432. }
  433. else
  434. {
  435. break;
  436. }
  437. /* Final Byte offset */
  438. for (final = junk; i + final < size; final++)
  439. if (buffer[i + final] < 0x20 || buffer[i + final] > 0x2f)
  440. {
  441. break;
  442. }
  443. if (i + final >= size
  444. || buffer[i + final] < 0x40 || buffer[i + final] > 0x7e)
  445. {
  446. debug("ansi Invalid Final Byte (%d %c)\n", buffer[i + final],
  447. buffer[i + final]);
  448. break; /* Invalid Final Byte */
  449. }
  450. skip += final;
  451. /* Sanity checks */
  452. if (param < inter && buffer[i + param] >= 0x3c)
  453. {
  454. /* Private sequence, only parse what we know */
  455. debug("ansi import: private sequence \"^[[%.*s\"",
  456. final - param + 1, buffer + i + param);
  457. /* FIXME better parsing */
  458. if (buffer[i + 2] == '?')
  459. {
  460. char arg[5], *end;
  461. int a = 0;
  462. int c, p, Pm;
  463. for (p = 0; p < 4; p++)
  464. {
  465. if (buffer[i + 3 + p] >= '0'
  466. && buffer[i + 3 + p] <= '9')
  467. {
  468. arg[a] = buffer[i + 3 + p];
  469. arg[a + 1] = 0;
  470. a++;
  471. debug("private a now '%s'\n", arg);
  472. }
  473. else
  474. {
  475. break;
  476. }
  477. }
  478. Pm = strtol(arg, &end, 10);
  479. c = buffer[i + 3 + (end - arg)];
  480. debug("ansi private mouse : command %c, arg %d", c, Pm);
  481. if (c == 'h') /* DECSET DEC Private Mode Set */
  482. {
  483. switch (Pm)
  484. {
  485. /* FIXME Handle different modes */
  486. case 9:
  487. debug("mouse : X10 mode\n");
  488. m_report_mouse = MOUSE_X10;
  489. break;
  490. case 1000: /* Send Mouse X & Y on button press
  491. and release. */
  492. debug("mouse : VT200 mode\n");
  493. m_report_mouse = MOUSE_VT200;
  494. break;
  495. case 1001: /* Use Hilite Mouse Tracking. */
  496. debug("mouse : VT200_HIGHLIGHT mode\n");
  497. m_report_mouse = MOUSE_VT200_HIGHLIGHT;
  498. break;
  499. case 1002: /* Use Cell Motion Mouse Tracking. */
  500. debug("mouse : BTN mode\n");
  501. m_report_mouse = MOUSE_BTN_EVENT;
  502. break;
  503. case 1003: /* Use All Motion Mouse Tracking. */
  504. debug("mouse : ANY mode\n");
  505. m_report_mouse = MOUSE_ANY_EVENT;
  506. break;
  507. default:
  508. break;
  509. }
  510. }
  511. else if (c == 'l') /* DECRST DEC Private Mode Reset */
  512. {
  513. Pm = atoi(arg);
  514. switch (Pm)
  515. {
  516. /* FIXME Handle different modes */
  517. case 9:
  518. case 1000: /* Send Mouse X & Y on button press
  519. and release. */
  520. case 1001: /* Use Hilite Mouse Tracking. */
  521. case 1002: /* Use Cell Motion Mouse Tracking. */
  522. case 1003: /* Use All Motion Mouse Tracking. */
  523. m_report_mouse = MOUSE_NONE;
  524. debug("ansi private mouse : NOT reporting mouse");
  525. break;
  526. default:
  527. break;
  528. }
  529. }
  530. }
  531. continue; /* Private sequence, skip it entirely */
  532. }
  533. if (final - param > 100)
  534. continue; /* Suspiciously long sequence, skip it */
  535. /* Parse parameter bytes as per ECMA-48 5.4.2: Parameter string
  536. format */
  537. if (param < inter)
  538. {
  539. argv[0] = 0;
  540. for (j = param; j < inter; j++)
  541. {
  542. if (buffer[i + j] == ';')
  543. argv[++argc] = 0;
  544. else if (buffer[i + j] >= '0' && buffer[i + j] <= '9')
  545. argv[argc] = 10 * argv[argc] + (buffer[i + j] - '0');
  546. }
  547. argc++;
  548. }
  549. /* Interpret final byte. The code representations are given in
  550. ECMA-48 5.4: Control sequences, and the code definitions are
  551. given in ECMA-48 8.3: Definition of control functions. */
  552. debug("ansi import: command '%c'", buffer[i + final]);
  553. switch (buffer[i + final])
  554. {
  555. case 'A': /* CUU (0x41) - Cursor Up */
  556. y -= argc ? argv[0] : 1;
  557. if (y < 0)
  558. y = 0;
  559. break;
  560. case 'B': /* CUD (0x42) - Cursor Down */
  561. y += argc ? argv[0] : 1;
  562. break;
  563. case 'C': /* CUF (0x43) - Cursor Right */
  564. x += argc ? argv[0] : 1;
  565. break;
  566. case 'D': /* CUB (0x44) - Cursor Left */
  567. x -= argc ? argv[0] : 1;
  568. if (x < 0)
  569. x = 0;
  570. break;
  571. case 'G': /* CHA (0x47) - Cursor Character Absolute */
  572. x = (argc && argv[0] > 0) ? argv[0] - 1 : 0;
  573. break;
  574. case 'H': /* CUP (0x48) - Cursor Position */
  575. x = (argc > 1 && argv[1] > 0) ? argv[1] - 1 : 0;
  576. y = (argc > 0 && argv[0] > 0) ? argv[0] - 1 : 0;
  577. debug("ansi CUP : Cursor at %dx%d\n", x, y);
  578. break;
  579. case 'J': /* ED (0x4a) - Erase In Page */
  580. savedattr = caca_get_attr(m_caca, -1, -1);
  581. caca_set_attr(m_caca, m_clearattr);
  582. if (!argc || argv[0] == 0)
  583. {
  584. caca_draw_line(m_caca, x, y, width, y, ' ');
  585. caca_fill_box(m_caca, 0, y + 1, width, height - 1, ' ');
  586. }
  587. else if (argv[0] == 1)
  588. {
  589. caca_fill_box(m_caca, 0, 0, width, y, ' ');
  590. caca_draw_line(m_caca, 0, y, x, y, ' ');
  591. }
  592. else if (argv[0] == 2)
  593. {
  594. // x = y = 0;
  595. caca_fill_box(m_caca, 0, 0, width, height, ' ');
  596. }
  597. caca_set_attr(m_caca, savedattr);
  598. break;
  599. case 'K': /* EL (0x4b) - Erase In Line */
  600. debug("ansi EL : cursor at %dx%d\n", x, y);
  601. if (!argc || argv[0] == 0)
  602. {
  603. caca_draw_line(m_caca, x, y, width, y, ' ');
  604. }
  605. else if (argv[0] == 1)
  606. {
  607. caca_draw_line(m_caca, 0, y, x, y, ' ');
  608. }
  609. else if (argv[0] == 2)
  610. {
  611. caca_draw_line(m_caca, 0, y, width, y, ' ');
  612. }
  613. break;
  614. case 'L': /* IL - Insert line */
  615. {
  616. unsigned int nb_lines = argc ? argv[0] : 1;
  617. for (j = bottom - 1; j >= (unsigned int)y + nb_lines; j--)
  618. {
  619. for (k = 0; k < width; k++)
  620. {
  621. caca_put_char(m_caca, k, j,
  622. caca_get_char(m_caca, k,
  623. j - nb_lines));
  624. caca_put_attr(m_caca, k, j,
  625. caca_get_attr(m_caca, k,
  626. j - nb_lines));
  627. }
  628. caca_draw_line(m_caca, 0, j - nb_lines, width,
  629. j - nb_lines, ' ');
  630. }
  631. }
  632. break;
  633. case 'P': /* DCH (0x50) - Delete Character */
  634. if (!argc || argv[0] == 0)
  635. argv[0] = 1; /* echo -ne 'foobar\r\e[0P\n' */
  636. for (j = x; (unsigned int)(j + argv[0]) < width; j++)
  637. {
  638. caca_put_char(m_caca, j, y,
  639. caca_get_char(m_caca, j + argv[0], y));
  640. caca_put_attr(m_caca, j, y,
  641. caca_get_attr(m_caca, j + argv[0], y));
  642. }
  643. break;
  644. #if 0
  645. savedattr = caca_get_attr(m_caca, -1, -1);
  646. caca_set_attr(m_caca, m_clearattr);
  647. for (; (unsigned int)j < width; j++)
  648. caca_put_char(m_caca, j, y, ' ');
  649. caca_set_attr(m_caca, savedattr);
  650. #endif
  651. case 'X': /* ECH (0x58) - Erase Character */
  652. if (argc && argv[0])
  653. {
  654. savedattr = caca_get_attr(m_caca, -1, -1);
  655. caca_set_attr(m_caca, m_clearattr);
  656. caca_draw_line(m_caca, x, y, x + argv[0] - 1, y, ' ');
  657. caca_set_attr(m_caca, savedattr);
  658. }
  659. case 'c': /* DA -- Device Attributes */
  660. /*
  661. 0 Base VT100, no options 1 Processor options (STP) 2
  662. Advanced video option (AVO) 3 AVO and STP 4 Graphics
  663. processor option (GPO) 5 GPO and STP 6 GPO and AVO 7 GPO,
  664. STP, and AVO */
  665. /* Warning, argument is Pn */
  666. debug("ansi Got command c, argc %d, argv[0] (%d)\n", argc,
  667. argv[0], argv[0]);
  668. if (!argc || argv[0] == 0)
  669. {
  670. SendAnsi("\x1b[?1;0c");
  671. }
  672. else
  673. {
  674. switch (argv[0])
  675. {
  676. case 1:
  677. SendAnsi("\x1b[?\x1;\x1c");
  678. break;
  679. case 2:
  680. SendAnsi("\x1b[?\x1;\x2c");
  681. break;
  682. case 3:
  683. SendAnsi("\x1b[?\x1;\x3c");
  684. break;
  685. case 4:
  686. SendAnsi("\x1b[?\x1;\x4c");
  687. break;
  688. case 5:
  689. SendAnsi("\x1b[?\x1;\x5c");
  690. break;
  691. case 6:
  692. SendAnsi("\x1b[?\x1;\x6c");
  693. break;
  694. case 7:
  695. SendAnsi("\x1b[?\x1;\x7c");
  696. break;
  697. default:
  698. debug("Unsupported DA option '%d'\n", argv[0]);
  699. break;
  700. }
  701. }
  702. break;
  703. case 'd': /* VPA (0x64) - Line Position Absolute */
  704. y = (argc && argv[0] > 0) ? argv[0] - 1 : 0;
  705. break;
  706. case 'f': /* HVP (0x66) - Character And Line Position */
  707. x = (argc > 1 && argv[1] > 0) ? argv[1] - 1 : 0;
  708. y = (argc > 0 && argv[0] > 0) ? argv[0] - 1 : 0;
  709. break;
  710. case 'g': /* TBC -- Tabulation Clear */
  711. break;
  712. case 'r': /* FIXME */
  713. if (argc == 2) /* DCSTBM - Set top and bottom margin */
  714. {
  715. debug("DCSTBM %d %d", argv[0], argv[1]);
  716. top = argv[0];
  717. bottom = argv[1];
  718. }
  719. else
  720. debug("ansi import: command r with %d params", argc);
  721. break;
  722. case 'h': /* SM (0x68) - FIXME */
  723. debug("ansi import: set mode %i", argc ? (int)argv[0] : -1);
  724. break;
  725. case 'l': /* RM (0x6c) - FIXME */
  726. debug("ansi import: reset mode %i", argc ? (int)argv[0] : -1);
  727. break;
  728. case 'm': /* SGR (0x6d) - Select Graphic Rendition */
  729. if (argc)
  730. ReadGrcm(argc, argv);
  731. else
  732. ReadGrcm(1, &dummy);
  733. break;
  734. case 'n':
  735. debug("ansi command n, argc %d, argv[0] %d\n", argc, argv[0]);
  736. if (!argc)
  737. break;
  738. switch (argv[0])
  739. {
  740. case 5:
  741. /* Term ok */
  742. SendAnsi("\x1b[0n");
  743. break;
  744. case 6:
  745. /* Cursor Position */
  746. sprintf(b, "\x1b[%d;%dR", y + 1, x + 1);
  747. SendAnsi(b);
  748. break;
  749. }
  750. break;
  751. case 's': /* Private (save cursor position) */
  752. save_x = x;
  753. save_y = y;
  754. break;
  755. case 'u': /* Private (reload cursor position) */
  756. x = save_x;
  757. y = save_y;
  758. break;
  759. default:
  760. debug("ansi import: unknown command \"^[%.*s\"",
  761. final - param + 1, buffer + i + param);
  762. break;
  763. }
  764. }
  765. /* Parse OSC stuff. */
  766. else if (buffer[i] == '\033' && buffer[i + 1] == ']')
  767. {
  768. char *string;
  769. unsigned int command = 0;
  770. unsigned int mode = 2, semicolon, final;
  771. for (semicolon = mode; i + semicolon < size; semicolon++)
  772. {
  773. if (buffer[i + semicolon] < '0' || buffer[i + semicolon] > '9')
  774. break;
  775. command = 10 * command + (buffer[i + semicolon] - '0');
  776. }
  777. if (i + semicolon >= size || buffer[i + semicolon] != ';')
  778. break; /* Invalid Mode */
  779. for (final = semicolon + 1; i + final < size; final++)
  780. if (buffer[i + final] < 0x20)
  781. break;
  782. if (i + final >= size || buffer[i + final] != '\a')
  783. break; /* Not enough data or no bell found */
  784. /* FIXME: XTerm also reacts to <ESC><backslash> and <ST> */
  785. /* FIXME: differenciate between not enough data (try again) and
  786. invalid data (print shit) */
  787. skip += final;
  788. string = (char *)malloc(final - (semicolon + 1) + 1);
  789. memcpy(string, buffer + i + (semicolon + 1),
  790. final - (semicolon + 1));
  791. string[final - (semicolon + 1)] = '\0';
  792. debug("ansi import: got OSC command %i string '%s'", command,
  793. string);
  794. if (command == 0 || command == 2)
  795. {
  796. if (m_title)
  797. free(m_title);
  798. m_title = string;
  799. }
  800. else
  801. free(string);
  802. }
  803. /* Get the character we’re going to paste */
  804. else
  805. {
  806. size_t bytes;
  807. if (i + 6 < size)
  808. {
  809. ch = caca_utf8_to_utf32((char const *)(buffer + i), &bytes);
  810. }
  811. else
  812. {
  813. /* Add a trailing zero to what we're going to read */
  814. char tmp[7];
  815. memcpy(tmp, buffer + i, size - i);
  816. tmp[size - i] = '\0';
  817. ch = caca_utf8_to_utf32(tmp, &bytes);
  818. }
  819. if (!bytes)
  820. {
  821. /* If the Unicode is invalid, assume it was latin1. */
  822. ch = buffer[i];
  823. bytes = 1;
  824. }
  825. /* very incomplete ISO-2022 implementation tailored to DEC ACS */
  826. if (m_conv_state.cs == '@')
  827. {
  828. if (((ch > ' ') && (ch <= '~'))
  829. &&
  830. (m_conv_state.
  831. gn[m_conv_state.ss ? m_conv_state.
  832. gn[m_conv_state.ss] : m_conv_state.glr[0]] == '0'))
  833. {
  834. ch = dec_acs(ch);
  835. }
  836. else if (((ch > 0x80) && (ch < 0xff))
  837. && (m_conv_state.gn[m_conv_state.glr[1]] == '0'))
  838. {
  839. ch = dec_acs(ch + ' ' - 0x80);
  840. }
  841. }
  842. m_conv_state.ss = 0; /* no single-shift (GL) */
  843. wch = caca_utf32_is_fullwidth(ch) ? 2 : 1;
  844. skip += bytes - 1;
  845. }
  846. /* Wrap long lines or grow horizontally */
  847. while ((unsigned int)x + wch > width)
  848. {
  849. x -= width;
  850. y++;
  851. }
  852. /* Scroll or grow vertically */
  853. if ((unsigned int)y >= bottom)
  854. {
  855. int lines = (y - bottom) + 1;
  856. savedattr = caca_get_attr(m_caca, -1, -1);
  857. for (j = top - 1; j + lines < bottom; j++)
  858. {
  859. for (k = 0; k < width; k++)
  860. {
  861. caca_put_char(m_caca, k, j,
  862. caca_get_char(m_caca, k, j + lines));
  863. caca_put_attr(m_caca, k, j,
  864. caca_get_attr(m_caca, k, j + lines));
  865. }
  866. }
  867. caca_set_attr(m_caca, m_clearattr);
  868. caca_fill_box(m_caca, 0, bottom - lines, width, bottom - 1, ' ');
  869. y -= lines;
  870. caca_set_attr(m_caca, savedattr);
  871. }
  872. /* Now paste our character, if any */
  873. if (wch)
  874. {
  875. caca_put_char(m_caca, x, y, ch);
  876. x += wch;
  877. }
  878. }
  879. caca_gotoxy(m_caca, x, y);
  880. if (i)
  881. m_changed = 1;
  882. return i;
  883. }
  884. size_t Term::SendAnsi(char const *str)
  885. {
  886. size_t ret = strlen(str);
  887. /* FIXME TODO: not implemented */
  888. Log::Debug("Sending %d-character ANSI sequence\n", (int)ret);
  889. return ret;
  890. }
  891. /* Coding Method Delimiter (CMD), ECMA-48 (1991), ISO/IEC 6429:1992 (ISO IR
  892. 189) */
  893. void Iso2022Conversion::Reset()
  894. {
  895. cs = '@'; /* ISO-2022 coding system */
  896. cn[0] = '@'; /* ISO 646 C0 control charset */
  897. cn[1] = 'C'; /* ISO 6429-1983 C1 control charset */
  898. glr[0] = 0; /* G0 in GL */
  899. glr[1] = 2; /* G2 in GR */
  900. gn[0] = 'B'; /* US-ASCII G0 charset */
  901. gn[1] = '0'; /* DEC ACS G1 charset */
  902. gn[2] = LITERAL2CHAR('.', 'A'); /* ISO 8859-1 G2 charset */
  903. gn[3] = LITERAL2CHAR('.', 'A'); /* ISO 8859-1 G3 charset */
  904. ss = 0; /* no single-shift (GL) */
  905. ctrl8bit = 1;
  906. }
  907. /* XXX : ANSI loader helper */
  908. void Term::ReadGrcm(unsigned int argc, unsigned int const *argv)
  909. {
  910. static uint8_t const ansi2caca[] = {
  911. CACA_BLACK, CACA_RED, CACA_GREEN, CACA_BROWN,
  912. CACA_BLUE, CACA_MAGENTA, CACA_CYAN, CACA_LIGHTGRAY
  913. };
  914. unsigned int j;
  915. uint8_t efg, ebg; /* Effective (libcaca) fg/bg */
  916. for (j = 0; j < argc; j++)
  917. {
  918. /* Defined in ECMA-48 8.3.117: SGR - SELECT GRAPHIC RENDITION */
  919. if (argv[j] >= 30 && argv[j] <= 37)
  920. m_fg = ansi2caca[argv[j] - 30];
  921. else if (argv[j] >= 40 && argv[j] <= 47)
  922. m_bg = ansi2caca[argv[j] - 40];
  923. else if (argv[j] >= 90 && argv[j] <= 97)
  924. m_fg = ansi2caca[argv[j] - 90] + 8;
  925. else if (argv[j] >= 100 && argv[j] <= 107)
  926. m_bg = ansi2caca[argv[j] - 100] + 8;
  927. else
  928. switch (argv[j])
  929. {
  930. case 0: /* default rendition */
  931. m_fg = m_dfg;
  932. m_bg = m_dbg;
  933. m_bold = m_blink = m_italics = m_negative
  934. = m_concealed = m_underline = m_faint = m_strike
  935. = m_proportional = 0;
  936. break;
  937. case 1: /* bold or increased intensity */
  938. m_bold = 1;
  939. break;
  940. case 2: /* faint, decreased intensity or second colour
  941. */
  942. m_faint = 1;
  943. break;
  944. case 3: /* italicized */
  945. m_italics = 1;
  946. break;
  947. case 4: /* singly underlined */
  948. m_underline = 1;
  949. break;
  950. case 5: /* slowly blinking (less then 150 per minute) */
  951. case 6: /* rapidly blinking (150 per minute or more) */
  952. m_blink = 1;
  953. break;
  954. case 7: /* negative image */
  955. m_negative = 1;
  956. break;
  957. case 8: /* concealed characters */
  958. m_concealed = 1;
  959. break;
  960. case 9: /* crossed-out (characters still legible but
  961. marked as to be deleted */
  962. m_strike = 1;
  963. break;
  964. case 21: /* doubly underlined */
  965. m_underline = 1;
  966. break;
  967. case 22: /* normal colour or normal intensity (neither
  968. bold nor faint) */
  969. m_bold = m_faint = 0;
  970. break;
  971. case 23: /* not italicized, not fraktur */
  972. m_italics = 0;
  973. break;
  974. case 24: /* not underlined (neither singly nor doubly) */
  975. m_underline = 0;
  976. break;
  977. case 25: /* steady (not blinking) */
  978. m_blink = 0;
  979. break;
  980. case 26: /* (reserved for proportional spacing as
  981. specified in CCITT Recommendation T.61) */
  982. m_proportional = 1;
  983. break;
  984. case 27: /* positive image */
  985. m_negative = 0;
  986. break;
  987. case 28: /* revealed characters */
  988. m_concealed = 0;
  989. break;
  990. case 29: /* not crossed out */
  991. m_strike = 0;
  992. break;
  993. case 38: /* (reserved for future standardization,
  994. intended for setting character foreground
  995. colour as specified in ISO 8613-6 [CCITT
  996. Recommendation T.416]) */
  997. break;
  998. case 39: /* default display colour
  999. (implementation-defined) */
  1000. m_fg = m_dfg;
  1001. break;
  1002. case 48: /* (reserved for future standardization,
  1003. intended for setting character background
  1004. colour as specified in ISO 8613-6 [CCITT
  1005. Recommendation T.416]) */
  1006. break;
  1007. case 49: /* default background colour
  1008. (implementation-defined) */
  1009. m_bg = m_dbg;
  1010. break;
  1011. case 50: /* (reserved for cancelling the effect of the
  1012. rendering aspect established by parameter
  1013. value 26) */
  1014. m_proportional = 0;
  1015. break;
  1016. default:
  1017. debug("ansi import: unknown sgr %i", argv[j]);
  1018. break;
  1019. }
  1020. }
  1021. if (m_concealed)
  1022. {
  1023. efg = ebg = CACA_TRANSPARENT;
  1024. }
  1025. else
  1026. {
  1027. efg = m_negative ? m_bg : m_fg;
  1028. ebg = m_negative ? m_fg : m_bg;
  1029. if (m_bold)
  1030. {
  1031. if (efg < 8)
  1032. efg += 8;
  1033. else if (efg == CACA_DEFAULT)
  1034. efg = CACA_WHITE;
  1035. }
  1036. }
  1037. caca_set_color_ansi(m_caca, efg, ebg);
  1038. }