termemu.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  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 if (c == '7') {
  129. s->escape = 0;
  130. s->buflen = 0;
  131. s->save_x = callbacks->get_csr_x();
  132. s->save_y = callbacks->get_csr_y();
  133. } else if (c == '8') {
  134. s->escape = 0;
  135. s->buflen = 0;
  136. callbacks->set_csr(s->save_x, s->save_y);
  137. } else {
  138. /* This isn't a bracket, we're not actually escaped!
  139. * Get out of here! */
  140. ansi_dump_buffer(s);
  141. callbacks->writer(c);
  142. s->escape = 0;
  143. s->buflen = 0;
  144. return;
  145. }
  146. break;
  147. case 2:
  148. if (c >= ANSI_LOW && c <= ANSI_HIGH) {
  149. /* Woah, woah, let's see here. */
  150. char * pch; /* tokenizer pointer */
  151. char * save; /* strtok_r pointer */
  152. char * argv[MAX_ARGS]; /* escape arguments */
  153. /* Get rid of the front of the buffer */
  154. strtok_r(s->buffer,"[",&save);
  155. pch = strtok_r(NULL,";",&save);
  156. /* argc = Number of arguments, obviously */
  157. int argc = 0;
  158. while (pch != NULL) {
  159. argv[argc] = (char *)pch;
  160. ++argc;
  161. if (argc > MAX_ARGS)
  162. break;
  163. pch = strtok_r(NULL,";",&save);
  164. }
  165. /* Alright, let's do this */
  166. switch (c) {
  167. case ANSI_EXT_IOCTL:
  168. {
  169. if (argc > 0) {
  170. int arg = atoi(argv[0]);
  171. switch (arg) {
  172. case 1:
  173. callbacks->redraw_cursor();
  174. break;
  175. default:
  176. break;
  177. }
  178. }
  179. }
  180. break;
  181. case ANSI_SCP:
  182. {
  183. s->save_x = callbacks->get_csr_x();
  184. s->save_y = callbacks->get_csr_y();
  185. }
  186. break;
  187. case ANSI_RCP:
  188. {
  189. callbacks->set_csr(s->save_x, s->save_y);
  190. }
  191. break;
  192. case ANSI_SGR:
  193. /* Set Graphics Rendition */
  194. if (argc == 0) {
  195. /* Default = 0 */
  196. argv[0] = "0";
  197. argc = 1;
  198. }
  199. for (int i = 0; i < argc; ++i) {
  200. int arg = atoi(argv[i]);
  201. if (arg >= 100 && arg < 110) {
  202. /* Bright background */
  203. s->bg = 8 + (arg - 100);
  204. s->flags |= ANSI_SPECBG;
  205. } else if (arg >= 90 && arg < 100) {
  206. /* Bright foreground */
  207. s->fg = 8 + (arg - 90);
  208. } else if (arg >= 40 && arg < 49) {
  209. /* Set background */
  210. s->bg = arg - 40;
  211. s->flags |= ANSI_SPECBG;
  212. } else if (arg == 49) {
  213. s->bg = TERM_DEFAULT_BG;
  214. s->flags &= ~ANSI_SPECBG;
  215. } else if (arg >= 30 && arg < 39) {
  216. /* Set Foreground */
  217. s->fg = arg - 30;
  218. } else if (arg == 39) {
  219. /* Default Foreground */
  220. s->fg = 7;
  221. } else if (arg == 24) {
  222. /* Underline off */
  223. s->flags &= ~ANSI_UNDERLINE;
  224. } else if (arg == 23) {
  225. /* Oblique off */
  226. s->flags &= ~ANSI_ITALIC;
  227. } else if (arg == 21 || arg == 22) {
  228. /* Bold off */
  229. s->flags &= ~ANSI_BOLD;
  230. } else if (arg == 9) {
  231. /* X-OUT */
  232. s->flags |= ANSI_CROSS;
  233. } else if (arg == 7) {
  234. /* INVERT: Swap foreground / background */
  235. uint32_t temp = s->fg;
  236. s->fg = s->bg;
  237. s->bg = temp;
  238. } else if (arg == 6) {
  239. /* proprietary RGBA color support */
  240. if (i == 0) { break; }
  241. if (i < argc) {
  242. int r = atoi(argv[i+1]);
  243. int g = atoi(argv[i+2]);
  244. int b = atoi(argv[i+3]);
  245. int a = atoi(argv[i+4]);
  246. if (a == 0) a = 1; /* Override a = 0 */
  247. uint32_t c = rgba(r,g,b,a);
  248. if (atoi(argv[i-1]) == 48) {
  249. s->bg = c;
  250. s->flags |= ANSI_SPECBG;
  251. } else if (atoi(argv[i-1]) == 38) {
  252. s->fg = c;
  253. }
  254. i += 4;
  255. }
  256. } else if (arg == 5) {
  257. /* Supposed to be blink; instead, support X-term 256 colors */
  258. if (i == 0) { break; }
  259. if (i < argc) {
  260. if (atoi(argv[i-1]) == 48) {
  261. /* Background to i+1 */
  262. s->bg = atoi(argv[i+1]);
  263. s->flags |= ANSI_SPECBG;
  264. } else if (atoi(argv[i-1]) == 38) {
  265. /* Foreground to i+1 */
  266. s->fg = atoi(argv[i+1]);
  267. }
  268. ++i;
  269. }
  270. } else if (arg == 4) {
  271. /* UNDERLINE */
  272. s->flags |= ANSI_UNDERLINE;
  273. } else if (arg == 3) {
  274. /* ITALIC: Oblique */
  275. s->flags |= ANSI_ITALIC;
  276. } else if (arg == 2) {
  277. /* Konsole RGB color support */
  278. if (i == 0) { break; }
  279. if (i < argc - 2) {
  280. int r = atoi(argv[i+1]);
  281. int g = atoi(argv[i+2]);
  282. int b = atoi(argv[i+3]);
  283. uint32_t c = rgb(r,g,b);
  284. if (atoi(argv[i-1]) == 48) {
  285. /* Background to i+1 */
  286. s->bg = c;
  287. s->flags |= ANSI_SPECBG;
  288. } else if (atoi(argv[i-1]) == 38) {
  289. /* Foreground to i+1 */
  290. s->fg = c;
  291. }
  292. i += 3;
  293. }
  294. } else if (arg == 1) {
  295. /* BOLD/BRIGHT: Brighten the output color */
  296. s->flags |= ANSI_BOLD;
  297. } else if (arg == 0) {
  298. /* Reset everything */
  299. s->fg = TERM_DEFAULT_FG;
  300. s->bg = TERM_DEFAULT_BG;
  301. s->flags = TERM_DEFAULT_FLAGS;
  302. }
  303. }
  304. break;
  305. case ANSI_SHOW:
  306. if (argc > 0) {
  307. if (!strcmp(argv[0], "?1049")) {
  308. if (callbacks->switch_buffer) callbacks->switch_buffer(1);
  309. } else if (!strcmp(argv[0], "?1000")) {
  310. s->mouse_on |= TERMEMU_MOUSE_ENABLE;
  311. } else if (!strcmp(argv[0], "?1002")) {
  312. s->mouse_on |= TERMEMU_MOUSE_DRAG;
  313. } else if (!strcmp(argv[0], "?1006")) {
  314. s->mouse_on |= TERMEMU_MOUSE_SGR;
  315. } else if (!strcmp(argv[0], "?25")) {
  316. callbacks->set_csr_on(1);
  317. } else if (!strcmp(argv[0], "?2004")) {
  318. s->paste_mode = 1;
  319. }
  320. }
  321. break;
  322. case ANSI_HIDE:
  323. if (argc > 0) {
  324. if (!strcmp(argv[0], "?1049")) {
  325. if (callbacks->switch_buffer) callbacks->switch_buffer(0);
  326. } else if (!strcmp(argv[0], "?1000")) {
  327. s->mouse_on &= ~TERMEMU_MOUSE_ENABLE;
  328. } else if (!strcmp(argv[0], "?1002")) {
  329. s->mouse_on &= ~TERMEMU_MOUSE_DRAG;
  330. } else if (!strcmp(argv[0],"?1006")) {
  331. s->mouse_on &= ~TERMEMU_MOUSE_SGR;
  332. } else if (!strcmp(argv[0], "?25")) {
  333. callbacks->set_csr_on(0);
  334. } else if (!strcmp(argv[0], "?2004")) {
  335. s->paste_mode = 0;
  336. }
  337. }
  338. break;
  339. case ANSI_CUF:
  340. {
  341. int i = 1;
  342. if (argc) {
  343. i = atoi(argv[0]);
  344. }
  345. callbacks->set_csr(min(callbacks->get_csr_x() + i, s->width - 1), callbacks->get_csr_y());
  346. }
  347. break;
  348. case ANSI_CUU:
  349. {
  350. int i = 1;
  351. if (argc) {
  352. i = atoi(argv[0]);
  353. }
  354. callbacks->set_csr(callbacks->get_csr_x(), max(callbacks->get_csr_y() - i, 0));
  355. }
  356. break;
  357. case ANSI_CUD:
  358. {
  359. int i = 1;
  360. if (argc) {
  361. i = atoi(argv[0]);
  362. }
  363. callbacks->set_csr(callbacks->get_csr_x(), min(callbacks->get_csr_y() + i, s->height - 1));
  364. }
  365. break;
  366. case ANSI_CUB:
  367. {
  368. int i = 1;
  369. if (argc) {
  370. i = atoi(argv[0]);
  371. }
  372. callbacks->set_csr(max(callbacks->get_csr_x() - i,0), callbacks->get_csr_y());
  373. }
  374. break;
  375. case ANSI_CHA:
  376. if (argc < 1) {
  377. callbacks->set_csr(0,callbacks->get_csr_y());
  378. } else {
  379. callbacks->set_csr(min(max(atoi(argv[0]), 1), s->width) - 1, callbacks->get_csr_y());
  380. }
  381. break;
  382. case ANSI_CUP:
  383. if (argc < 2) {
  384. callbacks->set_csr(0,0);
  385. } else {
  386. callbacks->set_csr(min(max(atoi(argv[1]), 1), s->width) - 1, min(max(atoi(argv[0]), 1), s->height) - 1);
  387. }
  388. break;
  389. case ANSI_ED:
  390. if (argc < 1) {
  391. callbacks->cls(0);
  392. } else {
  393. callbacks->cls(atoi(argv[0]));
  394. }
  395. break;
  396. case ANSI_EL:
  397. {
  398. int what = 0, x = 0, y = 0;
  399. if (argc >= 1) {
  400. what = atoi(argv[0]);
  401. }
  402. if (what == 0) {
  403. x = callbacks->get_csr_x();
  404. y = s->width;
  405. } else if (what == 1) {
  406. x = 0;
  407. y = callbacks->get_csr_x();
  408. } else if (what == 2) {
  409. x = 0;
  410. y = s->width;
  411. }
  412. for (int i = x; i < y; ++i) {
  413. callbacks->set_cell(i, callbacks->get_csr_y(), ' ');
  414. }
  415. }
  416. break;
  417. case ANSI_DSR:
  418. {
  419. char out[24];
  420. sprintf(out, "\033[%d;%dR", callbacks->get_csr_y() + 1, callbacks->get_csr_x() + 1);
  421. callbacks->input_buffer_stuff(out);
  422. }
  423. break;
  424. case ANSI_SU:
  425. {
  426. int how_many = 1;
  427. if (argc > 0) {
  428. how_many = atoi(argv[0]);
  429. }
  430. callbacks->scroll(how_many);
  431. }
  432. break;
  433. case ANSI_SD:
  434. {
  435. int how_many = 1;
  436. if (argc > 0) {
  437. how_many = atoi(argv[0]);
  438. }
  439. callbacks->scroll(-how_many);
  440. }
  441. break;
  442. case ANSI_IL:
  443. {
  444. int how_many = 1;
  445. if (argc > 0) {
  446. how_many = atoi(argv[0]);
  447. }
  448. callbacks->insert_delete_lines(how_many);
  449. }
  450. break;
  451. case ANSI_DL:
  452. {
  453. int how_many = 1;
  454. if (argc > 0) {
  455. how_many = atoi(argv[0]);
  456. }
  457. callbacks->insert_delete_lines(-how_many);
  458. }
  459. break;
  460. case 'X':
  461. {
  462. int how_many = 1;
  463. if (argc > 0) {
  464. how_many = atoi(argv[0]);
  465. }
  466. for (int i = 0; i < how_many; ++i) {
  467. callbacks->writer(' ');
  468. }
  469. }
  470. break;
  471. case 'd':
  472. if (argc < 1) {
  473. callbacks->set_csr(callbacks->get_csr_x(), 0);
  474. } else {
  475. callbacks->set_csr(callbacks->get_csr_x(), atoi(argv[0]) - 1);
  476. }
  477. break;
  478. default:
  479. /* Meh */
  480. break;
  481. }
  482. /* Set the states */
  483. if (s->flags & ANSI_BOLD && s->fg < 9) {
  484. callbacks->set_color(s->fg % 8 + 8, s->bg);
  485. } else {
  486. callbacks->set_color(s->fg, s->bg);
  487. }
  488. /* Clear out the buffer */
  489. s->buflen = 0;
  490. s->escape = 0;
  491. return;
  492. } else {
  493. /* Still escaped */
  494. ansi_buf_add(s, c);
  495. }
  496. break;
  497. case 3:
  498. if (c == '\007') {
  499. /* Tokenize on semicolons, like we always do */
  500. char * pch; /* tokenizer pointer */
  501. char * save; /* strtok_r pointer */
  502. char * argv[MAX_ARGS]; /* escape arguments */
  503. /* Get rid of the front of the buffer */
  504. strtok_r(s->buffer,"]",&save);
  505. pch = strtok_r(NULL,";",&save);
  506. /* argc = Number of arguments, obviously */
  507. int argc = 0;
  508. while (pch != NULL) {
  509. argv[argc] = (char *)pch;
  510. ++argc;
  511. if (argc > MAX_ARGS) break;
  512. pch = strtok_r(NULL,";",&save);
  513. }
  514. /* Start testing the first argument for what command to use */
  515. if (argv[0]) {
  516. if (!strcmp(argv[0], "1")) {
  517. if (argc > 1) {
  518. callbacks->set_title(argv[1]);
  519. }
  520. } /* Currently, no other options */
  521. }
  522. /* Clear out the buffer */
  523. s->buflen = 0;
  524. s->escape = 0;
  525. return;
  526. } else {
  527. /* Still escaped */
  528. if (c == '\n' || s->buflen == 255) {
  529. ansi_dump_buffer(s);
  530. callbacks->writer(c);
  531. s->buflen = 0;
  532. s->escape = 0;
  533. return;
  534. }
  535. ansi_buf_add(s, c);
  536. }
  537. break;
  538. case 4:
  539. if (c == '0') {
  540. s->box = 1;
  541. } else if (c == 'B') {
  542. s->box = 0;
  543. } else {
  544. ansi_dump_buffer(s);
  545. callbacks->writer(c);
  546. }
  547. s->escape = 0;
  548. s->buflen = 0;
  549. break;
  550. case 5:
  551. if (c == 'q') {
  552. char out[24];
  553. sprintf(out, "\033T%d;%dq", callbacks->get_cell_width(), callbacks->get_cell_height());
  554. callbacks->input_buffer_stuff(out);
  555. s->escape = 0;
  556. s->buflen = 0;
  557. } else if (c == 's') {
  558. s->img_collected = 0;
  559. s->escape = 6;
  560. s->img_size = sizeof(uint32_t) * callbacks->get_cell_width() * callbacks->get_cell_height();
  561. if (!s->img_data) {
  562. s->img_data = malloc(s->img_size);
  563. }
  564. memset(s->img_data, 0x00, s->img_size);
  565. } else {
  566. ansi_dump_buffer(s);
  567. callbacks->writer(c);
  568. s->escape = 0;
  569. s->buflen = 0;
  570. }
  571. break;
  572. case 6:
  573. s->img_data[s->img_collected++] = c;
  574. if (s->img_collected == s->img_size) {
  575. callbacks->set_cell_contents(callbacks->get_csr_x(), callbacks->get_csr_y(), s->img_data);
  576. callbacks->set_csr(min(callbacks->get_csr_x() + 1, s->width - 1), callbacks->get_csr_y());
  577. s->escape = 0;
  578. s->buflen = 0;
  579. }
  580. break;
  581. }
  582. }
  583. void ansi_put(term_state_t * s, char c) {
  584. _spin_lock(&s->lock);
  585. _ansi_put(s, c);
  586. _spin_unlock(&s->lock);
  587. }
  588. term_state_t * ansi_init(term_state_t * s, int w, int y, term_callbacks_t * callbacks_in) {
  589. if (!s) {
  590. s = malloc(sizeof(term_state_t));
  591. }
  592. memset(s, 0x00, sizeof(term_state_t));
  593. /* Terminal Defaults */
  594. s->fg = TERM_DEFAULT_FG; /* Light grey */
  595. s->bg = TERM_DEFAULT_BG; /* Black */
  596. s->flags = TERM_DEFAULT_FLAGS; /* Nothing fancy*/
  597. s->width = w;
  598. s->height = y;
  599. s->box = 0;
  600. s->callbacks = callbacks_in;
  601. s->callbacks->set_color(s->fg, s->bg);
  602. s->mouse_on = 0;
  603. return s;
  604. }