termemu.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. /* vim: tabstop=4 shiftwidth=4 noexpandtab
  2. * This file is part of ToaruOS and is released under the terms
  3. * of the NCSA / University of Illinois License - see LICENSE.md
  4. * Copyright (C) 2014-2018 K. Lange
  5. *
  6. * Portable library for terminal emulation.
  7. */
  8. #ifdef _KERNEL_
  9. # include <kernel/system.h>
  10. # include <kernel/types.h>
  11. # include <kernel/logging.h>
  12. static void _spin_lock(volatile int * foo) { return; }
  13. static void _spin_unlock(volatile int * foo) { return; }
  14. # define rgba(r,g,b,a) (((uint32_t)a * 0x1000000) + ((uint32_t)r * 0x10000) + ((uint32_t)g * 0x100) + ((uint32_t)b * 0x1))
  15. # define rgb(r,g,b) rgba(r,g,b,0xFF)
  16. # define atof(i) (0.0f)
  17. #include <toaru/termemu.h>
  18. #else
  19. #include <stdlib.h>
  20. #include <math.h>
  21. #include <string.h>
  22. #include <stdio.h>
  23. #include <toaru/graphics.h>
  24. #include <toaru/termemu.h>
  25. #include <toaru/spinlock.h>
  26. #define _spin_lock spin_lock
  27. #define _spin_unlock spin_unlock
  28. #endif
  29. #define MAX_ARGS 1024
  30. static wchar_t box_chars[] = L"▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥";
  31. /* Returns the lower of two shorts */
  32. static uint16_t min(uint16_t a, uint16_t b) {
  33. return (a < b) ? a : b;
  34. }
  35. /* Returns the higher of two shorts */
  36. static uint16_t max(uint16_t a, uint16_t b) {
  37. return (a > b) ? a : b;
  38. }
  39. /* Write the contents of the buffer, as they were all non-escaped data. */
  40. static void ansi_dump_buffer(term_state_t * s) {
  41. for (int i = 0; i < s->buflen; ++i) {
  42. s->callbacks->writer(s->buffer[i]);
  43. }
  44. }
  45. /* Add to the internal buffer for the ANSI parser */
  46. static void ansi_buf_add(term_state_t * s, char c) {
  47. if (s->buflen >= TERM_BUF_LEN-1) return;
  48. s->buffer[s->buflen] = c;
  49. s->buflen++;
  50. s->buffer[s->buflen] = '\0';
  51. }
  52. static int to_eight(uint32_t codepoint, char * out) {
  53. memset(out, 0x00, 7);
  54. if (codepoint < 0x0080) {
  55. out[0] = (char)codepoint;
  56. } else if (codepoint < 0x0800) {
  57. out[0] = 0xC0 | (codepoint >> 6);
  58. out[1] = 0x80 | (codepoint & 0x3F);
  59. } else if (codepoint < 0x10000) {
  60. out[0] = 0xE0 | (codepoint >> 12);
  61. out[1] = 0x80 | ((codepoint >> 6) & 0x3F);
  62. out[2] = 0x80 | (codepoint & 0x3F);
  63. } else if (codepoint < 0x200000) {
  64. out[0] = 0xF0 | (codepoint >> 18);
  65. out[1] = 0x80 | ((codepoint >> 12) & 0x3F);
  66. out[2] = 0x80 | ((codepoint >> 6) & 0x3F);
  67. out[3] = 0x80 | ((codepoint) & 0x3F);
  68. } else if (codepoint < 0x4000000) {
  69. out[0] = 0xF8 | (codepoint >> 24);
  70. out[1] = 0x80 | (codepoint >> 18);
  71. out[2] = 0x80 | ((codepoint >> 12) & 0x3F);
  72. out[3] = 0x80 | ((codepoint >> 6) & 0x3F);
  73. out[4] = 0x80 | ((codepoint) & 0x3F);
  74. } else {
  75. out[0] = 0xF8 | (codepoint >> 30);
  76. out[1] = 0x80 | ((codepoint >> 24) & 0x3F);
  77. out[2] = 0x80 | ((codepoint >> 18) & 0x3F);
  78. out[3] = 0x80 | ((codepoint >> 12) & 0x3F);
  79. out[4] = 0x80 | ((codepoint >> 6) & 0x3F);
  80. out[5] = 0x80 | ((codepoint) & 0x3F);
  81. }
  82. return strlen(out);
  83. }
  84. static void _ansi_put(term_state_t * s, char c) {
  85. term_callbacks_t * callbacks = s->callbacks;
  86. switch (s->escape) {
  87. case 0:
  88. /* We are not escaped, check for escape character */
  89. if (c == ANSI_ESCAPE) {
  90. /*
  91. * Enable escape mode, setup a buffer,
  92. * fill the buffer, get out of here.
  93. */
  94. s->escape = 1;
  95. s->buflen = 0;
  96. ansi_buf_add(s, c);
  97. return;
  98. } else if (c == 0) {
  99. return;
  100. } else {
  101. if (s->box && c >= 'a' && c <= 'z') {
  102. char buf[7];
  103. char *w = (char *)&buf;
  104. to_eight(box_chars[c-'a'], w);
  105. while (*w) {
  106. callbacks->writer(*w);
  107. w++;
  108. }
  109. } else {
  110. callbacks->writer(c);
  111. }
  112. }
  113. break;
  114. case 1:
  115. /* We're ready for [ */
  116. if (c == ANSI_BRACKET) {
  117. s->escape = 2;
  118. ansi_buf_add(s, c);
  119. } else if (c == ANSI_BRACKET_RIGHT) {
  120. s->escape = 3;
  121. ansi_buf_add(s, c);
  122. } else if (c == ANSI_OPEN_PAREN) {
  123. s->escape = 4;
  124. ansi_buf_add(s, c);
  125. } else if (c == 'T') {
  126. s->escape = 5;
  127. ansi_buf_add(s, c);
  128. } else {
  129. /* This isn't a bracket, we're not actually escaped!
  130. * Get out of here! */
  131. ansi_dump_buffer(s);
  132. callbacks->writer(c);
  133. s->escape = 0;
  134. s->buflen = 0;
  135. return;
  136. }
  137. break;
  138. case 2:
  139. if (c >= ANSI_LOW && c <= ANSI_HIGH) {
  140. /* Woah, woah, let's see here. */
  141. char * pch; /* tokenizer pointer */
  142. char * save; /* strtok_r pointer */
  143. char * argv[MAX_ARGS]; /* escape arguments */
  144. /* Get rid of the front of the buffer */
  145. strtok_r(s->buffer,"[",&save);
  146. pch = strtok_r(NULL,";",&save);
  147. /* argc = Number of arguments, obviously */
  148. int argc = 0;
  149. while (pch != NULL) {
  150. argv[argc] = (char *)pch;
  151. ++argc;
  152. if (argc > MAX_ARGS)
  153. break;
  154. pch = strtok_r(NULL,";",&save);
  155. }
  156. /* Alright, let's do this */
  157. switch (c) {
  158. case ANSI_EXT_IOCTL:
  159. {
  160. if (argc > 0) {
  161. int arg = atoi(argv[0]);
  162. switch (arg) {
  163. case 1:
  164. callbacks->redraw_cursor();
  165. break;
  166. default:
  167. break;
  168. }
  169. }
  170. }
  171. break;
  172. case ANSI_SCP:
  173. {
  174. s->save_x = callbacks->get_csr_x();
  175. s->save_y = callbacks->get_csr_y();
  176. }
  177. break;
  178. case ANSI_RCP:
  179. {
  180. callbacks->set_csr(s->save_x, s->save_y);
  181. }
  182. break;
  183. case ANSI_SGR:
  184. /* Set Graphics Rendition */
  185. if (argc == 0) {
  186. /* Default = 0 */
  187. argv[0] = "0";
  188. argc = 1;
  189. }
  190. for (int i = 0; i < argc; ++i) {
  191. int arg = atoi(argv[i]);
  192. if (arg >= 100 && arg < 110) {
  193. /* Bright background */
  194. s->bg = 8 + (arg - 100);
  195. s->flags |= ANSI_SPECBG;
  196. } else if (arg >= 90 && arg < 100) {
  197. /* Bright foreground */
  198. s->fg = 8 + (arg - 90);
  199. } else if (arg >= 40 && arg < 49) {
  200. /* Set background */
  201. s->bg = arg - 40;
  202. s->flags |= ANSI_SPECBG;
  203. } else if (arg == 49) {
  204. s->bg = TERM_DEFAULT_BG;
  205. s->flags &= ~ANSI_SPECBG;
  206. } else if (arg >= 30 && arg < 39) {
  207. /* Set Foreground */
  208. s->fg = arg - 30;
  209. } else if (arg == 39) {
  210. /* Default Foreground */
  211. s->fg = 7;
  212. } else if (arg == 24) {
  213. /* Underline off */
  214. s->flags &= ~ANSI_UNDERLINE;
  215. } else if (arg == 23) {
  216. /* Oblique off */
  217. s->flags &= ~ANSI_ITALIC;
  218. } else if (arg == 21 || arg == 22) {
  219. /* Bold off */
  220. s->flags &= ~ANSI_BOLD;
  221. } else if (arg == 9) {
  222. /* X-OUT */
  223. s->flags |= ANSI_CROSS;
  224. } else if (arg == 7) {
  225. /* INVERT: Swap foreground / background */
  226. uint32_t temp = s->fg;
  227. s->fg = s->bg;
  228. s->bg = temp;
  229. } else if (arg == 6) {
  230. /* proprietary RGBA color support */
  231. if (i == 0) { break; }
  232. if (i < argc) {
  233. int r = atoi(argv[i+1]);
  234. int g = atoi(argv[i+2]);
  235. int b = atoi(argv[i+3]);
  236. int a = atoi(argv[i+4]);
  237. if (a == 0) a = 1; /* Override a = 0 */
  238. uint32_t c = rgba(r,g,b,a);
  239. if (atoi(argv[i-1]) == 48) {
  240. s->bg = c;
  241. s->flags |= ANSI_SPECBG;
  242. } else if (atoi(argv[i-1]) == 38) {
  243. s->fg = c;
  244. }
  245. i += 4;
  246. }
  247. } else if (arg == 5) {
  248. /* Supposed to be blink; instead, support X-term 256 colors */
  249. if (i == 0) { break; }
  250. if (i < argc) {
  251. if (atoi(argv[i-1]) == 48) {
  252. /* Background to i+1 */
  253. s->bg = atoi(argv[i+1]);
  254. s->flags |= ANSI_SPECBG;
  255. } else if (atoi(argv[i-1]) == 38) {
  256. /* Foreground to i+1 */
  257. s->fg = atoi(argv[i+1]);
  258. }
  259. ++i;
  260. }
  261. } else if (arg == 4) {
  262. /* UNDERLINE */
  263. s->flags |= ANSI_UNDERLINE;
  264. } else if (arg == 3) {
  265. /* ITALIC: Oblique */
  266. s->flags |= ANSI_ITALIC;
  267. } else if (arg == 2) {
  268. /* Konsole RGB color support */
  269. if (i == 0) { break; }
  270. if (i < argc - 2) {
  271. int r = atoi(argv[i+1]);
  272. int g = atoi(argv[i+2]);
  273. int b = atoi(argv[i+3]);
  274. uint32_t c = rgb(r,g,b);
  275. if (atoi(argv[i-1]) == 48) {
  276. /* Background to i+1 */
  277. s->bg = c;
  278. s->flags |= ANSI_SPECBG;
  279. } else if (atoi(argv[i-1]) == 38) {
  280. /* Foreground to i+1 */
  281. s->fg = c;
  282. }
  283. i += 3;
  284. }
  285. } else if (arg == 1) {
  286. /* BOLD/BRIGHT: Brighten the output color */
  287. s->flags |= ANSI_BOLD;
  288. } else if (arg == 0) {
  289. /* Reset everything */
  290. s->fg = TERM_DEFAULT_FG;
  291. s->bg = TERM_DEFAULT_BG;
  292. s->flags = TERM_DEFAULT_FLAGS;
  293. }
  294. }
  295. break;
  296. case ANSI_SHOW:
  297. if (argc > 0) {
  298. if (!strcmp(argv[0], "?1049")) {
  299. if (callbacks->switch_buffer) callbacks->switch_buffer(1);
  300. } else if (!strcmp(argv[0], "?1000")) {
  301. s->mouse_on = 1;
  302. } else if (!strcmp(argv[0], "?1002")) {
  303. s->mouse_on = 2;
  304. } else if (!strcmp(argv[0], "?25")) {
  305. callbacks->set_csr_on(1);
  306. }
  307. }
  308. break;
  309. case ANSI_HIDE:
  310. if (argc > 0) {
  311. if (!strcmp(argv[0], "?1049")) {
  312. if (callbacks->switch_buffer) callbacks->switch_buffer(0);
  313. } else if (!strcmp(argv[0], "?1000")) {
  314. s->mouse_on = 0;
  315. } else if (!strcmp(argv[0], "?1002")) {
  316. s->mouse_on = 0;
  317. } else if (!strcmp(argv[0], "?25")) {
  318. callbacks->set_csr_on(0);
  319. }
  320. }
  321. break;
  322. case ANSI_CUF:
  323. {
  324. int i = 1;
  325. if (argc) {
  326. i = atoi(argv[0]);
  327. }
  328. callbacks->set_csr(min(callbacks->get_csr_x() + i, s->width - 1), callbacks->get_csr_y());
  329. }
  330. break;
  331. case ANSI_CUU:
  332. {
  333. int i = 1;
  334. if (argc) {
  335. i = atoi(argv[0]);
  336. }
  337. callbacks->set_csr(callbacks->get_csr_x(), max(callbacks->get_csr_y() - i, 0));
  338. }
  339. break;
  340. case ANSI_CUD:
  341. {
  342. int i = 1;
  343. if (argc) {
  344. i = atoi(argv[0]);
  345. }
  346. callbacks->set_csr(callbacks->get_csr_x(), min(callbacks->get_csr_y() + i, s->height - 1));
  347. }
  348. break;
  349. case ANSI_CUB:
  350. {
  351. int i = 1;
  352. if (argc) {
  353. i = atoi(argv[0]);
  354. }
  355. callbacks->set_csr(max(callbacks->get_csr_x() - i,0), callbacks->get_csr_y());
  356. }
  357. break;
  358. case ANSI_CHA:
  359. if (argc < 1) {
  360. callbacks->set_csr(0,callbacks->get_csr_y());
  361. } else {
  362. callbacks->set_csr(min(max(atoi(argv[0]), 1), s->width) - 1, callbacks->get_csr_y());
  363. }
  364. break;
  365. case ANSI_CUP:
  366. if (argc < 2) {
  367. callbacks->set_csr(0,0);
  368. } else {
  369. callbacks->set_csr(min(max(atoi(argv[1]), 1), s->width) - 1, min(max(atoi(argv[0]), 1), s->height) - 1);
  370. }
  371. break;
  372. case ANSI_ED:
  373. if (argc < 1) {
  374. callbacks->cls(0);
  375. } else {
  376. callbacks->cls(atoi(argv[0]));
  377. }
  378. break;
  379. case ANSI_EL:
  380. {
  381. int what = 0, x = 0, y = 0;
  382. if (argc >= 1) {
  383. what = atoi(argv[0]);
  384. }
  385. if (what == 0) {
  386. x = callbacks->get_csr_x();
  387. y = s->width;
  388. } else if (what == 1) {
  389. x = 0;
  390. y = callbacks->get_csr_x();
  391. } else if (what == 2) {
  392. x = 0;
  393. y = s->width;
  394. }
  395. for (int i = x; i < y; ++i) {
  396. callbacks->set_cell(i, callbacks->get_csr_y(), ' ');
  397. }
  398. }
  399. break;
  400. case ANSI_DSR:
  401. {
  402. char out[24];
  403. sprintf(out, "\033[%d;%dR", callbacks->get_csr_y() + 1, callbacks->get_csr_x() + 1);
  404. callbacks->input_buffer_stuff(out);
  405. }
  406. break;
  407. case ANSI_SU:
  408. {
  409. int how_many = 1;
  410. if (argc > 0) {
  411. how_many = atoi(argv[0]);
  412. }
  413. callbacks->scroll(how_many);
  414. }
  415. break;
  416. case ANSI_SD:
  417. {
  418. int how_many = 1;
  419. if (argc > 0) {
  420. how_many = atoi(argv[0]);
  421. }
  422. callbacks->scroll(-how_many);
  423. }
  424. break;
  425. case 'X':
  426. {
  427. int how_many = 1;
  428. if (argc > 0) {
  429. how_many = atoi(argv[0]);
  430. }
  431. for (int i = 0; i < how_many; ++i) {
  432. callbacks->writer(' ');
  433. }
  434. }
  435. break;
  436. case 'd':
  437. if (argc < 1) {
  438. callbacks->set_csr(callbacks->get_csr_x(), 0);
  439. } else {
  440. callbacks->set_csr(callbacks->get_csr_x(), atoi(argv[0]) - 1);
  441. }
  442. break;
  443. default:
  444. /* Meh */
  445. break;
  446. }
  447. /* Set the states */
  448. if (s->flags & ANSI_BOLD && s->fg < 9) {
  449. callbacks->set_color(s->fg % 8 + 8, s->bg);
  450. } else {
  451. callbacks->set_color(s->fg, s->bg);
  452. }
  453. /* Clear out the buffer */
  454. s->buflen = 0;
  455. s->escape = 0;
  456. return;
  457. } else {
  458. /* Still escaped */
  459. ansi_buf_add(s, c);
  460. }
  461. break;
  462. case 3:
  463. if (c == '\007') {
  464. /* Tokenize on semicolons, like we always do */
  465. char * pch; /* tokenizer pointer */
  466. char * save; /* strtok_r pointer */
  467. char * argv[MAX_ARGS]; /* escape arguments */
  468. /* Get rid of the front of the buffer */
  469. strtok_r(s->buffer,"]",&save);
  470. pch = strtok_r(NULL,";",&save);
  471. /* argc = Number of arguments, obviously */
  472. int argc = 0;
  473. while (pch != NULL) {
  474. argv[argc] = (char *)pch;
  475. ++argc;
  476. if (argc > MAX_ARGS) break;
  477. pch = strtok_r(NULL,";",&save);
  478. }
  479. /* Start testing the first argument for what command to use */
  480. if (argv[0]) {
  481. if (!strcmp(argv[0], "1")) {
  482. if (argc > 1) {
  483. callbacks->set_title(argv[1]);
  484. }
  485. } /* Currently, no other options */
  486. }
  487. /* Clear out the buffer */
  488. s->buflen = 0;
  489. s->escape = 0;
  490. return;
  491. } else {
  492. /* Still escaped */
  493. if (c == '\n' || s->buflen == 255) {
  494. ansi_dump_buffer(s);
  495. callbacks->writer(c);
  496. s->buflen = 0;
  497. s->escape = 0;
  498. return;
  499. }
  500. ansi_buf_add(s, c);
  501. }
  502. break;
  503. case 4:
  504. if (c == '0') {
  505. s->box = 1;
  506. } else if (c == 'B') {
  507. s->box = 0;
  508. } else {
  509. ansi_dump_buffer(s);
  510. callbacks->writer(c);
  511. }
  512. s->escape = 0;
  513. s->buflen = 0;
  514. break;
  515. case 5:
  516. if (c == 'q') {
  517. char out[24];
  518. sprintf(out, "\033T%d;%dq", callbacks->get_cell_width(), callbacks->get_cell_height());
  519. callbacks->input_buffer_stuff(out);
  520. s->escape = 0;
  521. s->buflen = 0;
  522. } else if (c == 's') {
  523. s->img_collected = 0;
  524. s->escape = 6;
  525. s->img_size = sizeof(uint32_t) * callbacks->get_cell_width() * callbacks->get_cell_height();
  526. if (!s->img_data) {
  527. s->img_data = malloc(s->img_size);
  528. }
  529. memset(s->img_data, 0x00, s->img_size);
  530. } else {
  531. ansi_dump_buffer(s);
  532. callbacks->writer(c);
  533. s->escape = 0;
  534. s->buflen = 0;
  535. }
  536. break;
  537. case 6:
  538. s->img_data[s->img_collected++] = c;
  539. if (s->img_collected == s->img_size) {
  540. callbacks->set_cell_contents(callbacks->get_csr_x(), callbacks->get_csr_y(), s->img_data);
  541. callbacks->set_csr(min(callbacks->get_csr_x() + 1, s->width - 1), callbacks->get_csr_y());
  542. s->escape = 0;
  543. s->buflen = 0;
  544. }
  545. break;
  546. }
  547. }
  548. void ansi_put(term_state_t * s, char c) {
  549. _spin_lock(&s->lock);
  550. _ansi_put(s, c);
  551. _spin_unlock(&s->lock);
  552. }
  553. term_state_t * ansi_init(term_state_t * s, int w, int y, term_callbacks_t * callbacks_in) {
  554. if (!s) {
  555. s = malloc(sizeof(term_state_t));
  556. }
  557. memset(s, 0x00, sizeof(term_state_t));
  558. /* Terminal Defaults */
  559. s->fg = TERM_DEFAULT_FG; /* Light grey */
  560. s->bg = TERM_DEFAULT_BG; /* Black */
  561. s->flags = TERM_DEFAULT_FLAGS; /* Nothing fancy*/
  562. s->width = w;
  563. s->height = y;
  564. s->box = 0;
  565. s->callbacks = callbacks_in;
  566. s->callbacks->set_color(s->fg, s->bg);
  567. s->mouse_on = 0;
  568. return s;
  569. }