render.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  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. int ms_sleep(unsigned int ms) {
  20. int result = 0;
  21. struct timespec ts = {ms / 1000, (ms % 1000) * 1000000L};
  22. do {
  23. struct timespec ts_sleep = ts;
  24. result = nanosleep(&ts_sleep, &ts);
  25. } while ((-1 == result));
  26. return result;
  27. }
  28. void render_sleep(void) {
  29. if (render_overlimit)
  30. return;
  31. if (current_render.speed) { // * 100 still too slow.
  32. ms_sleep(current_render.speed * 10);
  33. }
  34. }
  35. /*
  36. Well SNAP! Mystic numbers don't remotely match ANSI color codes.
  37. 00 : Sets the current foreground to Black 0;30
  38. 01 : Sets the current foreground to Dark Blue 0;34
  39. 02 : Sets the current foreground to Dark Green 0;32
  40. 03 : Sets the current foreground to Dark Cyan 0;36
  41. 04 : Sets the current foreground to Dark Red 0;31
  42. 05 : Sets the current foreground to Dark Magenta 0;35
  43. 06 : Sets the current foreground to Brown 0;33
  44. 07 : Sets the current foreground to Grey 0;37
  45. 08 : Sets the current foreground to Dark Grey 1;30
  46. 09 : Sets the current foreground to Light Blue 1;34
  47. 10 : Sets the current foreground to Light Green 1;32
  48. 11 : Sets the current foreground to Light Cyan 1;36
  49. 12 : Sets the current foreground to Light Red 1;31
  50. 13 : Sets the current foreground to Light Magenta 1;35
  51. 14 : Sets the current foreground to Yellow 1;33
  52. 15 : Sets the current foreground to White 1;37
  53. 16 : Sets the current background to Black 40
  54. 17 : Sets the current background to Blue 44
  55. 18 : Sets the current background to Green 42
  56. 19 : Sets the current background to Cyan 46
  57. 20 : Sets the current background to Red 41
  58. 21 : Sets the current background to Magenta 45
  59. 22 : Sets the current background to Brown 43
  60. 23 : Sets the current background to Grey 47
  61. 24 : Sets the current background to black with blinking foreground 5;40
  62. 25 : Sets the current background to blue with blinking foreground 5;44
  63. 26 : Sets the current background to green with blinking foreground 5;42
  64. 27 : Sets the current background to cyan with blinking foreground 5;46
  65. 28 : Sets the current background to red with blinking foreground 5;41
  66. 29 : Sets the current background to magenta with blinking foreground 5;45
  67. 30 : Sets the current background to brown with blinking foreground 5;43
  68. 31 : Sets the current background to grey with blinking foreground 5;47
  69. Other things that Mystic does ...
  70. [A## - Move the cursor up ## lines
  71. [B## - Move the cursor down ## lines
  72. [C## - Move the cursor forward (to the right) ## columns
  73. [D## - Move the cursor backwards (to the left) ## columns
  74. [K - Clear from the current cursor position to the end of the line
  75. [L - Move cursor and erase data backwards from current column to column ##
  76. [X## - Move cursor to X coordinate ##
  77. [Y## - Move cursor to Y coordinate ##
  78. BS - Sends 1 destructive backspace sequence (ASCII 8-32-8)
  79. CL - Clears the screen (ANSI 1,1 locate and [2J or ASCII 12)
  80. CR - Send a carrage return and line feed (move to next line)
  81. RA - Restore the saved text attribute color
  82. RS - Restore the saved user's terminal screen
  83. SA - Save the current text attribute color
  84. SS - Save the entire user's terminal screen
  85. */
  86. // Covert MYSTIC color to (Proper) ANSI COLOR.
  87. const int MYSTIC[] = {0, 4, 2, 6, 1, 5, 3, 7};
  88. // ANSI_color = MYSTIC[ odd_mystic_color % 8 ]
  89. void write_color(int fd, int color) {
  90. char buffer[12];
  91. int slen;
  92. switch (color) {
  93. case 0:
  94. case 1:
  95. case 2:
  96. case 3:
  97. case 4:
  98. case 5:
  99. case 6:
  100. case 7:
  101. slen = snprintf(buffer, sizeof(buffer), "\x1b[0;3%dm", MYSTIC[color]);
  102. if (slen >= sizeof(buffer)) {
  103. ZF_LOGE("snprintf failed write_color(%d)", color);
  104. }
  105. break;
  106. case 8:
  107. case 9:
  108. case 10:
  109. case 11:
  110. case 12:
  111. case 13:
  112. case 14:
  113. case 15:
  114. slen = snprintf(buffer, sizeof(buffer), "\x1b[0;1;3%dm", MYSTIC[color - 8]);
  115. if (slen >= sizeof(buffer)) {
  116. ZF_LOGE("snprintf failed write_color(%d)", color);
  117. }
  118. break;
  119. case 16:
  120. case 17:
  121. case 18:
  122. case 19:
  123. case 20:
  124. case 21:
  125. case 22:
  126. case 23:
  127. slen = snprintf(buffer, sizeof(buffer), "\x1b[4%dm", MYSTIC[color - 16]);
  128. if (slen >= sizeof(buffer)) {
  129. ZF_LOGE("snprintf failed write_color(%d)", color);
  130. }
  131. break;
  132. case 24:
  133. case 25:
  134. case 26:
  135. case 27:
  136. case 28:
  137. case 29:
  138. case 30:
  139. case 31:
  140. slen = snprintf(buffer, sizeof(buffer), "\x1b[5;4%dm", MYSTIC[color - 24]);
  141. if (slen >= sizeof(buffer)) {
  142. ZF_LOGE("snprintf failed write_color(%d)", color);
  143. }
  144. break;
  145. default:
  146. buffer[0] = 0;
  147. break;
  148. }
  149. ZF_LOGD("write_color( %d ): %s", color, repr(buffer));
  150. write(fd, buffer, strlen(buffer));
  151. }
  152. int send_file(int fd, char *filename) {
  153. FILE *fp;
  154. char buffer[100];
  155. int read;
  156. fp = fopen(filename, "rb");
  157. if (fp == NULL) {
  158. ZF_LOGD("Failed to open %s", filename);
  159. return 0;
  160. }
  161. while ((read = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
  162. write(fd, buffer, read);
  163. };
  164. fclose(fp);
  165. return 1;
  166. }
  167. void send_goto(int fd, int x, int y) {
  168. char gbuffer[16];
  169. sprintf(gbuffer, "\x1b[%d;%dH", y, x);
  170. write(fd, gbuffer, strlen(gbuffer));
  171. }
  172. int send_file(int fd, int x, int y, char *filename) {
  173. FILE *fp;
  174. char buffer[100];
  175. int read;
  176. fp = fopen(filename, "rb");
  177. if (fp == NULL) {
  178. ZF_LOGD("Failed to open %s", filename);
  179. return 0;
  180. }
  181. send_goto(fd, x, y);
  182. y++;
  183. while ((read = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
  184. char *cp, *last_cp;
  185. buffer[read] = 0;
  186. last_cp = buffer;
  187. while ((cp = strchr(last_cp, '\n')) != NULL) {
  188. *cp = 0;
  189. write(fd, last_cp, strlen(last_cp));
  190. send_goto(fd, x, y);
  191. y++;
  192. last_cp = cp + 1;
  193. };
  194. write(fd, last_cp, strlen(last_cp));
  195. };
  196. fclose(fp);
  197. return 1;
  198. }
  199. /**
  200. * process_trigger( fd, *cp )
  201. *
  202. * This process a command trigger.
  203. * It has seen TRIGGER, and now it is
  204. * processing whatever comes after it.
  205. * It will perform the process, and
  206. * return the char * of whatever is next.
  207. */
  208. const char *process_trigger(int fd, const char *cp) {
  209. char ch;
  210. int i, x, y;
  211. ch = *cp;
  212. cp++;
  213. switch (ch) {
  214. case 'D':
  215. i = 0;
  216. if (isdigit(*cp)) {
  217. i = (*cp) - '0';
  218. cp++;
  219. };
  220. if (isdigit(*cp)) {
  221. i *= 10;
  222. i += (*cp) - '0';
  223. cp++;
  224. };
  225. if ((i > 0) && (i < 80)) {
  226. ZF_LOGI("DEL %02d", i);
  227. for (x = 0; x < i; x++) {
  228. write(fd, "\b \b", 3);
  229. }
  230. };
  231. break;
  232. case 'C': {
  233. i = 0;
  234. if (*cp == 'R') {
  235. cp++;
  236. const char *restore = color_restore(&console);
  237. write(fd, restore, strlen(restore));
  238. break;
  239. }
  240. if (isdigit(*cp)) {
  241. i = (*cp) - '0';
  242. cp++;
  243. };
  244. if (isdigit(*cp)) {
  245. i *= 10;
  246. i += (*cp) - '0';
  247. cp++;
  248. };
  249. write_color(fd, i);
  250. } break;
  251. case 'F':
  252. case 'f': {
  253. int pos = 0;
  254. if (ch == 'f') {
  255. pos = 1;
  256. x = (*cp) - '0';
  257. cp++;
  258. x *= 10;
  259. x += (*cp) - '0';
  260. cp++;
  261. y = (*cp) - '0';
  262. cp++;
  263. y *= 10;
  264. y += (*cp) - '0';
  265. cp++;
  266. ZF_LOGI("file at (%d, %d)", x, y);
  267. }
  268. // Ok, look for filename
  269. char ansifile[32] = "./hh/";
  270. char *ap = ansifile + strlen(ansifile);
  271. while (*cp != '.') {
  272. *ap = *cp;
  273. ap++;
  274. *ap = 0;
  275. cp++;
  276. };
  277. strcat(ansifile, ".ans");
  278. cp++;
  279. ZF_LOGD("FILE [%s]", ansifile);
  280. if (pos) {
  281. send_file(fd, x, y, ansifile);
  282. } else {
  283. send_file(fd, ansifile);
  284. };
  285. } break;
  286. case 'G': {
  287. x = 0;
  288. if (isdigit(*cp)) {
  289. x = (*cp) - '0';
  290. cp++;
  291. };
  292. if (isdigit(*cp)) {
  293. x *= 10;
  294. x += (*cp) - '0';
  295. cp++;
  296. };
  297. y = 0;
  298. if (isdigit(*cp)) {
  299. y = (*cp) - '0';
  300. cp++;
  301. };
  302. if (isdigit(*cp)) {
  303. y *= 10;
  304. y += (*cp) - '0';
  305. cp++;
  306. };
  307. char buffer[20]; // row ; column H
  308. ZF_LOGD("GOTO (%d,%d)", x, y);
  309. sprintf(buffer, "\x1b[%d;%dH", y, x);
  310. write(fd, buffer, strlen(buffer));
  311. } break;
  312. case 'R': {
  313. i = 0;
  314. if (isdigit(*cp)) {
  315. i = (*cp) - '0';
  316. cp++;
  317. };
  318. if ((i > 0) && (i < 10)) {
  319. ZF_LOGI("RENDER %d", i);
  320. current_render.effect = i;
  321. } else {
  322. current_render.effect = 0;
  323. }
  324. } break;
  325. case 'S': {
  326. i = 0;
  327. if (isdigit(*cp)) {
  328. i = (*cp) - '0';
  329. cp++;
  330. };
  331. if ((i > 0) && (i < 10)) {
  332. ZF_LOGI("SPEED %d", i);
  333. current_render.speed = i;
  334. } else {
  335. current_render.speed = 0;
  336. }
  337. } break;
  338. case 'P': {
  339. i = 0;
  340. if (isdigit(*cp)) {
  341. i = (*cp) - '0';
  342. cp++;
  343. };
  344. if ((i > 0) && (i < 10)) {
  345. ZF_LOGI("PAWS %d", i);
  346. // sleep(i);
  347. if (!render_overlimit) {
  348. sleep(i);
  349. };
  350. }
  351. } break;
  352. }
  353. return cp;
  354. }
  355. /**
  356. * render_effect( fd, ch )
  357. *
  358. * Displays the given character with whatever
  359. * rendering effect is currently active.
  360. * (If any).
  361. */
  362. void render_effect(int fd, char ch) {
  363. int effect = current_render.effect;
  364. int l;
  365. char space = ' ';
  366. char bs = '\b';
  367. switch (effect) {
  368. case 1:
  369. // CHAR + SPC + BS
  370. render_sleep();
  371. write(fd, &ch, 1);
  372. render_sleep();
  373. write(fd, &space, 1);
  374. render_sleep();
  375. render_sleep();
  376. write(fd, &bs, 1);
  377. break;
  378. case 2:
  379. // CHAR + 8 spaces + 8 BS
  380. render_sleep();
  381. write(fd, &ch, 1);
  382. for (l = 0; l < 8; l++) {
  383. render_sleep();
  384. write(fd, &space, 1);
  385. }
  386. for (l = 0; l < 8; l++) {
  387. render_sleep();
  388. write(fd, &bs, 1);
  389. }
  390. break;
  391. case 0:
  392. default:
  393. // NORMAL
  394. render_sleep();
  395. write(fd, &ch, 1);
  396. break;
  397. }
  398. }
  399. /**
  400. * render( fd, string_out )
  401. *
  402. * Render an entire string.
  403. * Handles TRIGGER.
  404. * Renders with effects.
  405. */
  406. void render(int fd, const char *string_out, int len) {
  407. const char *cp = string_out;
  408. const char *trigger = cp;
  409. time_t start = time(NULL);
  410. int elapsed;
  411. int over = 0;
  412. reset_render();
  413. // ZF_LOGV("render(%d, %s)", fd, repr(string_out));
  414. ZF_LOGV_MEM(string_out, len, "render(%d, %d bytes):", fd, len);
  415. // Check our time from time to time.
  416. // If we start running long, disable sleeps.
  417. while ((trigger = strnstr(cp, len - (cp - string_out), TRIGGER)) != NULL) {
  418. // There is special things to handle in here.
  419. while (cp != trigger) {
  420. elapsed = time(NULL) - start;
  421. if (elapsed > SLEEP_LIMIT) {
  422. render_overlimit = 1;
  423. current_render.speed = 0;
  424. };
  425. // write(fd, cp, 1 );
  426. render_effect(fd, *cp);
  427. cp++;
  428. };
  429. // ZF_LOGI( "at trigger: (%s)", cp);
  430. cp += strlen(TRIGGER);
  431. // Ok, we're pointing at the trigger -- do something.
  432. cp = process_trigger(fd, cp);
  433. // ZF_LOGI( "after trigger: (%s)", cp);
  434. };
  435. // We still might be under a rendering effect.
  436. while (cp < (string_out + len)) {
  437. elapsed = time(NULL) - start;
  438. if (elapsed > SLEEP_LIMIT) {
  439. render_overlimit = 1;
  440. current_render.speed = 0;
  441. };
  442. // write(fd, cp, 1);
  443. render_effect(fd, *cp);
  444. cp++;
  445. }
  446. }