render.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. #include <ctype.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <time.h>
  5. #include <unistd.h> // usleep
  6. #include "render.h"
  7. #include "terminal.h"
  8. #include "zf_log.h"
  9. extern const char *strnstr(const char *source, int len, const char *needle);
  10. extern const char *repr(const char *data);
  11. extern struct console_details console;
  12. struct render current_render;
  13. int render_overlimit = 0;
  14. void reset_render(void) {
  15. current_render.speed = 0;
  16. current_render.effect = 0;
  17. render_overlimit = 0;
  18. }
  19. // or possibly a vector, and pop the image pointer off
  20. // allowing for multiple images.
  21. const char **image_data = NULL;
  22. int image_size = 0;
  23. void render_image(const char **lines, int size) {
  24. image_data = lines;
  25. image_size = size;
  26. }
  27. void reset_image(void) {
  28. image_data = NULL;
  29. image_size = 0;
  30. }
  31. int ms_sleep(unsigned int ms) {
  32. int result = 0;
  33. struct timespec ts = {ms / 1000, (ms % 1000) * 1000000L};
  34. do {
  35. struct timespec ts_sleep = ts;
  36. result = nanosleep(&ts_sleep, &ts);
  37. } while ((-1 == result));
  38. return result;
  39. }
  40. void render_sleep(void) {
  41. if (render_overlimit)
  42. return;
  43. if (current_render.speed) { // * 100 still too slow.
  44. ms_sleep(current_render.speed * 10);
  45. }
  46. }
  47. /*
  48. Well SNAP! Mystic numbers don't remotely match ANSI color codes.
  49. 00 : Sets the current foreground to Black 0;30
  50. 01 : Sets the current foreground to Dark Blue 0;34
  51. 02 : Sets the current foreground to Dark Green 0;32
  52. 03 : Sets the current foreground to Dark Cyan 0;36
  53. 04 : Sets the current foreground to Dark Red 0;31
  54. 05 : Sets the current foreground to Dark Magenta 0;35
  55. 06 : Sets the current foreground to Brown 0;33
  56. 07 : Sets the current foreground to Grey 0;37
  57. 08 : Sets the current foreground to Dark Grey 1;30
  58. 09 : Sets the current foreground to Light Blue 1;34
  59. 10 : Sets the current foreground to Light Green 1;32
  60. 11 : Sets the current foreground to Light Cyan 1;36
  61. 12 : Sets the current foreground to Light Red 1;31
  62. 13 : Sets the current foreground to Light Magenta 1;35
  63. 14 : Sets the current foreground to Yellow 1;33
  64. 15 : Sets the current foreground to White 1;37
  65. 16 : Sets the current background to Black 40
  66. 17 : Sets the current background to Blue 44
  67. 18 : Sets the current background to Green 42
  68. 19 : Sets the current background to Cyan 46
  69. 20 : Sets the current background to Red 41
  70. 21 : Sets the current background to Magenta 45
  71. 22 : Sets the current background to Brown 43
  72. 23 : Sets the current background to Grey 47
  73. 24 : Sets the current background to black with blinking foreground 5;40
  74. 25 : Sets the current background to blue with blinking foreground 5;44
  75. 26 : Sets the current background to green with blinking foreground 5;42
  76. 27 : Sets the current background to cyan with blinking foreground 5;46
  77. 28 : Sets the current background to red with blinking foreground 5;41
  78. 29 : Sets the current background to magenta with blinking foreground 5;45
  79. 30 : Sets the current background to brown with blinking foreground 5;43
  80. 31 : Sets the current background to grey with blinking foreground 5;47
  81. Other things that Mystic does ...
  82. [A## - Move the cursor up ## lines
  83. [B## - Move the cursor down ## lines
  84. [C## - Move the cursor forward (to the right) ## columns
  85. [D## - Move the cursor backwards (to the left) ## columns
  86. [K - Clear from the current cursor position to the end of the line
  87. [L - Move cursor and erase data backwards from current column to column ##
  88. [X## - Move cursor to X coordinate ##
  89. [Y## - Move cursor to Y coordinate ##
  90. BS - Sends 1 destructive backspace sequence (ASCII 8-32-8)
  91. CL - Clears the screen (ANSI 1,1 locate and [2J or ASCII 12)
  92. CR - Send a carrage return and line feed (move to next line)
  93. RA - Restore the saved text attribute color
  94. RS - Restore the saved user's terminal screen
  95. SA - Save the current text attribute color
  96. SS - Save the entire user's terminal screen
  97. */
  98. // Covert MYSTIC color to (Proper) ANSI COLOR.
  99. const int MYSTIC[] = {0, 4, 2, 6, 1, 5, 3, 7};
  100. // ANSI_color = MYSTIC[ odd_mystic_color % 8 ]
  101. void write_color(int fd, int color) {
  102. char buffer[12];
  103. int slen;
  104. switch (color) {
  105. case 0:
  106. case 1:
  107. case 2:
  108. case 3:
  109. case 4:
  110. case 5:
  111. case 6:
  112. case 7:
  113. slen = snprintf(buffer, sizeof(buffer), "\x1b[0;3%dm", MYSTIC[color]);
  114. if (slen >= sizeof(buffer)) {
  115. ZF_LOGE("snprintf %d > size %d", slen, (int)sizeof(buffer));
  116. buffer[0] = 0;
  117. }
  118. break;
  119. case 8:
  120. case 9:
  121. case 10:
  122. case 11:
  123. case 12:
  124. case 13:
  125. case 14:
  126. case 15:
  127. slen = snprintf(buffer, sizeof(buffer), "\x1b[0;1;3%dm", MYSTIC[color - 8]);
  128. if (slen >= sizeof(buffer)) {
  129. ZF_LOGE("snprintf %d > size %d", slen, (int)sizeof(buffer));
  130. buffer[0] = 0;
  131. }
  132. break;
  133. case 16:
  134. case 17:
  135. case 18:
  136. case 19:
  137. case 20:
  138. case 21:
  139. case 22:
  140. case 23:
  141. slen = snprintf(buffer, sizeof(buffer), "\x1b[4%dm", MYSTIC[color - 16]);
  142. if (slen >= sizeof(buffer)) {
  143. ZF_LOGE("snprintf %d > size %d", slen, (int)sizeof(buffer));
  144. buffer[0] = 0;
  145. }
  146. break;
  147. case 24:
  148. case 25:
  149. case 26:
  150. case 27:
  151. case 28:
  152. case 29:
  153. case 30:
  154. case 31:
  155. slen = snprintf(buffer, sizeof(buffer), "\x1b[5;4%dm", MYSTIC[color - 24]);
  156. if (slen >= sizeof(buffer)) {
  157. ZF_LOGE("snprintf %d > size %d", slen, (int)sizeof(buffer));
  158. buffer[0] = 0;
  159. }
  160. break;
  161. default:
  162. buffer[0] = 0;
  163. break;
  164. }
  165. ZF_LOGD("write_color( %d ): %s", color, repr(buffer));
  166. write(fd, buffer, strlen(buffer));
  167. }
  168. void send_goto(int fd, int x, int y) {
  169. char gbuffer[16];
  170. int slen;
  171. slen = snprintf(gbuffer, sizeof(gbuffer), "\x1b[%d;%dH", y, x);
  172. if (slen >= sizeof(gbuffer)) {
  173. ZF_LOGE("snprintf %d > size %d", slen, (int)sizeof(gbuffer));
  174. gbuffer[0] = 0;
  175. }
  176. write(fd, gbuffer, strlen(gbuffer));
  177. }
  178. int send_image(int fd) {
  179. int i;
  180. const char **lines = image_data;
  181. if (lines == NULL) {
  182. ZF_LOGE("No image data for send_image.");
  183. return 0;
  184. }
  185. for (i = 0; i < image_size; i++) {
  186. write(fd, lines[i], strlen(lines[i]));
  187. write(fd, "\r\n", 2);
  188. }
  189. // success
  190. reset_image();
  191. return 1;
  192. }
  193. int send_image(int fd, int x, int y) {
  194. int i;
  195. const char **lines = image_data;
  196. if (lines == NULL) {
  197. ZF_LOGE("No image data for send_image(%d,%d)", x, y);
  198. return 0;
  199. }
  200. for (i = 0; i < image_size; i++) {
  201. send_goto(fd, x, y);
  202. y++;
  203. write(fd, lines[i], strlen(lines[i]));
  204. }
  205. // success
  206. reset_image();
  207. return 1;
  208. }
  209. int send_file(int fd, char *filename) {
  210. FILE *fp;
  211. char buffer[100];
  212. int read;
  213. fp = fopen(filename, "rb");
  214. if (fp == NULL) {
  215. ZF_LOGD("Failed to open %s", filename);
  216. return 0;
  217. }
  218. while ((read = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
  219. write(fd, buffer, read);
  220. };
  221. fclose(fp);
  222. return 1;
  223. }
  224. int send_file(int fd, int x, int y, char *filename) {
  225. FILE *fp;
  226. char buffer[100];
  227. int read;
  228. fp = fopen(filename, "rb");
  229. if (fp == NULL) {
  230. ZF_LOGD("Failed to open %s", filename);
  231. return 0;
  232. }
  233. send_goto(fd, x, y);
  234. y++;
  235. while ((read = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
  236. char *cp, *last_cp;
  237. buffer[read] = 0;
  238. last_cp = buffer;
  239. while ((cp = strchr(last_cp, '\n')) != NULL) {
  240. *cp = 0;
  241. write(fd, last_cp, strlen(last_cp));
  242. send_goto(fd, x, y);
  243. y++;
  244. last_cp = cp + 1;
  245. };
  246. write(fd, last_cp, strlen(last_cp));
  247. };
  248. fclose(fp);
  249. return 1;
  250. }
  251. int digit(std::string str, size_t &pos, int digits = 1) {
  252. int i = 0;
  253. while (digits > 0) {
  254. if (std::isdigit(str[pos])) {
  255. i *= 10;
  256. i += str[pos] - '0';
  257. pos++;
  258. } else {
  259. ZF_LOGE("digit fail");
  260. }
  261. digits--;
  262. };
  263. return i;
  264. }
  265. /**
  266. * process_trigger( fd, str, &pos )
  267. *
  268. * This process a command trigger.
  269. * It has seen TRIGGER, and now it is
  270. * processing whatever comes after it.
  271. * It will perform the process, and
  272. * update pos to whatever is next.
  273. */
  274. void process_trigger(int fd, std::string str, size_t &pos) {
  275. char ch = str[pos];
  276. int i, x, y;
  277. pos++;
  278. switch (ch) {
  279. case 'D':
  280. i = digit(str, pos, 2);
  281. if ((i > 0) && (i < 80)) {
  282. ZF_LOGI("DEL %02d", i);
  283. for (x = 0; x < i; x++) {
  284. write(fd, "\b \b", 3);
  285. }
  286. };
  287. break;
  288. case 'C': {
  289. i = 0;
  290. if (str[pos] == 'R') {
  291. pos++;
  292. const char *restore = color_restore(&console);
  293. write(fd, restore, strlen(restore));
  294. break;
  295. }
  296. i = digit(str, pos, 2);
  297. write_color(fd, i);
  298. } break;
  299. // no longer takes a filename. :)
  300. case 'F':
  301. case 'f': {
  302. int UsePos = 0;
  303. if (ch == 'f') {
  304. UsePos = 1;
  305. x = digit(str, pos, 2);
  306. y = digit(str, pos, 2);
  307. ZF_LOGI("file at (%d, %d)", x, y);
  308. }
  309. ZF_LOGD("IMAGE (%d lines)", image_size);
  310. if (UsePos) {
  311. send_image(fd, x, y);
  312. } else {
  313. send_image(fd);
  314. };
  315. } break;
  316. case 'G': {
  317. x = digit(str, pos, 2);
  318. y = digit(str, pos, 2);
  319. char buffer[20]; // row ; column H
  320. int slen;
  321. ZF_LOGD("GOTO (%d,%d)", x, y);
  322. slen = snprintf(buffer, sizeof(buffer), "\x1b[%d;%dH", y, x);
  323. if (slen >= sizeof(buffer)) {
  324. ZF_LOGE("snprintf %d > size %d", slen, (int)sizeof(buffer));
  325. buffer[0] = 0;
  326. }
  327. write(fd, buffer, strlen(buffer));
  328. } break;
  329. case 'R': {
  330. i = digit(str, pos);
  331. if ((i > 0) && (i < 10)) {
  332. ZF_LOGI("RENDER %d", i);
  333. current_render.effect = i;
  334. } else {
  335. current_render.effect = 0;
  336. }
  337. } break;
  338. case 'S': {
  339. i = digit(str, pos);
  340. if ((i > 0) && (i < 10)) {
  341. ZF_LOGI("SPEED %d", i);
  342. current_render.speed = i;
  343. } else {
  344. current_render.speed = 0;
  345. }
  346. } break;
  347. case 'P': {
  348. i = digit(str, pos);
  349. if ((i > 0) && (i < 10)) {
  350. ZF_LOGI("PAWS %d", i);
  351. // sleep(i);
  352. if (!render_overlimit) {
  353. sleep(i);
  354. };
  355. }
  356. } break;
  357. }
  358. }
  359. /**
  360. * process_trigger( fd, *cp )
  361. *
  362. * This process a command trigger.
  363. * It has seen TRIGGER, and now it is
  364. * processing whatever comes after it.
  365. * It will perform the process, and
  366. * return the char * of whatever is next.
  367. */
  368. const char *process_trigger(int fd, const char *cp) {
  369. char ch;
  370. int i, x, y;
  371. ch = *cp;
  372. cp++;
  373. switch (ch) {
  374. case 'D':
  375. i = 0;
  376. if (isdigit(*cp)) {
  377. i = (*cp) - '0';
  378. cp++;
  379. };
  380. if (isdigit(*cp)) {
  381. i *= 10;
  382. i += (*cp) - '0';
  383. cp++;
  384. };
  385. if ((i > 0) && (i < 80)) {
  386. ZF_LOGI("DEL %02d", i);
  387. for (x = 0; x < i; x++) {
  388. write(fd, "\b \b", 3);
  389. }
  390. };
  391. break;
  392. case 'C': {
  393. i = 0;
  394. if (*cp == 'R') {
  395. cp++;
  396. const char *restore = color_restore(&console);
  397. write(fd, restore, strlen(restore));
  398. break;
  399. }
  400. if (isdigit(*cp)) {
  401. i = (*cp) - '0';
  402. cp++;
  403. };
  404. if (isdigit(*cp)) {
  405. i *= 10;
  406. i += (*cp) - '0';
  407. cp++;
  408. };
  409. write_color(fd, i);
  410. } break;
  411. // no longer takes a filename. :)
  412. case 'F':
  413. case 'f': {
  414. int pos = 0;
  415. if (ch == 'f') {
  416. pos = 1;
  417. x = (*cp) - '0';
  418. cp++;
  419. x *= 10;
  420. x += (*cp) - '0';
  421. cp++;
  422. y = (*cp) - '0';
  423. cp++;
  424. y *= 10;
  425. y += (*cp) - '0';
  426. cp++;
  427. ZF_LOGI("file at (%d, %d)", x, y);
  428. }
  429. ZF_LOGD("IMAGE (%d lines)", image_size);
  430. if (pos) {
  431. send_image(fd, x, y);
  432. } else {
  433. send_image(fd);
  434. };
  435. } break;
  436. case 'G': {
  437. x = 0;
  438. if (isdigit(*cp)) {
  439. x = (*cp) - '0';
  440. cp++;
  441. };
  442. if (isdigit(*cp)) {
  443. x *= 10;
  444. x += (*cp) - '0';
  445. cp++;
  446. };
  447. y = 0;
  448. if (isdigit(*cp)) {
  449. y = (*cp) - '0';
  450. cp++;
  451. };
  452. if (isdigit(*cp)) {
  453. y *= 10;
  454. y += (*cp) - '0';
  455. cp++;
  456. };
  457. char buffer[20]; // row ; column H
  458. int slen;
  459. ZF_LOGD("GOTO (%d,%d)", x, y);
  460. slen = snprintf(buffer, sizeof(buffer), "\x1b[%d;%dH", y, x);
  461. if (slen >= sizeof(buffer)) {
  462. ZF_LOGE("snprintf %d > size %d", slen, (int)sizeof(buffer));
  463. buffer[0] = 0;
  464. }
  465. write(fd, buffer, strlen(buffer));
  466. } break;
  467. case 'R': {
  468. i = 0;
  469. if (isdigit(*cp)) {
  470. i = (*cp) - '0';
  471. cp++;
  472. };
  473. if ((i > 0) && (i < 10)) {
  474. ZF_LOGI("RENDER %d", i);
  475. current_render.effect = i;
  476. } else {
  477. current_render.effect = 0;
  478. }
  479. } break;
  480. case 'S': {
  481. i = 0;
  482. if (isdigit(*cp)) {
  483. i = (*cp) - '0';
  484. cp++;
  485. };
  486. if ((i > 0) && (i < 10)) {
  487. ZF_LOGI("SPEED %d", i);
  488. current_render.speed = i;
  489. } else {
  490. current_render.speed = 0;
  491. }
  492. } break;
  493. case 'P': {
  494. i = 0;
  495. if (isdigit(*cp)) {
  496. i = (*cp) - '0';
  497. cp++;
  498. };
  499. if ((i > 0) && (i < 10)) {
  500. ZF_LOGI("PAWS %d", i);
  501. // sleep(i);
  502. if (!render_overlimit) {
  503. sleep(i);
  504. };
  505. }
  506. } break;
  507. }
  508. return cp;
  509. }
  510. /**
  511. * render_effect( fd, ch )
  512. *
  513. * Displays the given character with whatever
  514. * rendering effect is currently active.
  515. * (If any).
  516. */
  517. void render_effect(int fd, char ch) {
  518. int effect = current_render.effect;
  519. int l;
  520. char space = ' ';
  521. char bs = '\b';
  522. switch (effect) {
  523. case 1:
  524. // CHAR + SPC + BS
  525. render_sleep();
  526. write(fd, &ch, 1);
  527. render_sleep();
  528. write(fd, &space, 1);
  529. render_sleep();
  530. render_sleep();
  531. write(fd, &bs, 1);
  532. break;
  533. case 2:
  534. // CHAR + 8 spaces + 8 BS
  535. render_sleep();
  536. write(fd, &ch, 1);
  537. for (l = 0; l < 8; l++) {
  538. render_sleep();
  539. write(fd, &space, 1);
  540. }
  541. for (l = 0; l < 8; l++) {
  542. render_sleep();
  543. write(fd, &bs, 1);
  544. }
  545. break;
  546. case 0:
  547. default:
  548. // NORMAL
  549. render_sleep();
  550. write(fd, &ch, 1);
  551. break;
  552. }
  553. }
  554. #ifdef UNWANTED
  555. /**
  556. * render( fd, string_out )
  557. *
  558. * Render an entire string.
  559. * Handles TRIGGER.
  560. * Renders with effects.
  561. */
  562. void render(int fd, const char *string_out, int len) {
  563. const char *cp = string_out;
  564. const char *trigger = cp;
  565. time_t start = time(NULL);
  566. int elapsed;
  567. int over = 0;
  568. reset_render();
  569. // ZF_LOGV("render(%d, %s)", fd, repr(string_out));
  570. ZF_LOGV_MEM(string_out, len, "render(%d, %d bytes):", fd, len);
  571. // Check our time from time to time.
  572. // If we start running long, disable sleeps.
  573. while ((trigger = strnstr(cp, len - (cp - string_out), TRIGGER)) != NULL) {
  574. // There is special things to handle in here.
  575. while (cp != trigger) {
  576. elapsed = time(NULL) - start;
  577. if (elapsed > SLEEP_LIMIT) {
  578. render_overlimit = 1;
  579. current_render.speed = 0;
  580. };
  581. // write(fd, cp, 1 );
  582. render_effect(fd, *cp);
  583. cp++;
  584. };
  585. // ZF_LOGI( "at trigger: (%s)", cp);
  586. cp += strlen(TRIGGER);
  587. // Ok, we're pointing at the trigger -- do something.
  588. cp = process_trigger(fd, cp);
  589. // ZF_LOGI( "after trigger: (%s)", cp);
  590. };
  591. // We still might be under a rendering effect.
  592. while (cp < (string_out + len)) {
  593. elapsed = time(NULL) - start;
  594. if (elapsed > SLEEP_LIMIT) {
  595. render_overlimit = 1;
  596. current_render.speed = 0;
  597. };
  598. // write(fd, cp, 1);
  599. render_effect(fd, *cp);
  600. cp++;
  601. }
  602. }
  603. #endif
  604. /**
  605. * render( fd, string_out )
  606. *
  607. * Render an entire string.
  608. * Handles TRIGGER.
  609. * Renders with effects.
  610. */
  611. void render(int fd, std::string &string_out) {
  612. reset_render();
  613. time_t start = time(NULL);
  614. size_t pos = 0;
  615. int elapsed;
  616. size_t tpos;
  617. while ((tpos = string_out.find(TRIGGER, pos)) != std::string::npos) {
  618. while (pos != tpos) {
  619. elapsed = time(NULL) - start;
  620. if (elapsed > SLEEP_LIMIT) {
  621. render_overlimit = 1;
  622. current_render.speed = 0;
  623. }
  624. render_effect(fd, string_out[pos]);
  625. pos++;
  626. }
  627. pos += strlen(TRIGGER);
  628. process_trigger(fd, string_out, pos);
  629. }
  630. while (pos < string_out.size()) {
  631. elapsed = time(NULL) - start;
  632. if (elapsed > SLEEP_LIMIT) {
  633. render_overlimit = 1;
  634. current_render.speed = 0;
  635. }
  636. render_effect(fd, string_out[pos]);
  637. pos++;
  638. }
  639. }