123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497 |
- #include <ctype.h>
- #include <stdio.h>
- #include <string.h>
- #include <time.h>
- #include <unistd.h> // usleep
- #include "render.h"
- #include "terminal.h"
- #include "zf_log.h"
- extern const char *strnstr(const char *source, int len, const char *needle);
- extern const char *repr(const char *data);
- extern struct console_details console;
- struct render current_render;
- int render_overlimit = 0;
- void reset_render(void) {
- current_render.speed = 0;
- current_render.effect = 0;
- render_overlimit = 0;
- }
- int ms_sleep(unsigned int ms) {
- int result = 0;
- struct timespec ts = {ms / 1000, (ms % 1000) * 1000000L};
- do {
- struct timespec ts_sleep = ts;
- result = nanosleep(&ts_sleep, &ts);
- } while ((-1 == result));
- return result;
- }
- void render_sleep(void) {
- if (render_overlimit)
- return;
- if (current_render.speed) { // * 100 still too slow.
- ms_sleep(current_render.speed * 10);
- }
- }
- /*
- Well SNAP! Mystic numbers don't remotely match ANSI color codes.
- 00 : Sets the current foreground to Black 0;30
- 01 : Sets the current foreground to Dark Blue 0;34
- 02 : Sets the current foreground to Dark Green 0;32
- 03 : Sets the current foreground to Dark Cyan 0;36
- 04 : Sets the current foreground to Dark Red 0;31
- 05 : Sets the current foreground to Dark Magenta 0;35
- 06 : Sets the current foreground to Brown 0;33
- 07 : Sets the current foreground to Grey 0;37
- 08 : Sets the current foreground to Dark Grey 1;30
- 09 : Sets the current foreground to Light Blue 1;34
- 10 : Sets the current foreground to Light Green 1;32
- 11 : Sets the current foreground to Light Cyan 1;36
- 12 : Sets the current foreground to Light Red 1;31
- 13 : Sets the current foreground to Light Magenta 1;35
- 14 : Sets the current foreground to Yellow 1;33
- 15 : Sets the current foreground to White 1;37
- 16 : Sets the current background to Black 40
- 17 : Sets the current background to Blue 44
- 18 : Sets the current background to Green 42
- 19 : Sets the current background to Cyan 46
- 20 : Sets the current background to Red 41
- 21 : Sets the current background to Magenta 45
- 22 : Sets the current background to Brown 43
- 23 : Sets the current background to Grey 47
- 24 : Sets the current background to black with blinking foreground 5;40
- 25 : Sets the current background to blue with blinking foreground 5;44
- 26 : Sets the current background to green with blinking foreground 5;42
- 27 : Sets the current background to cyan with blinking foreground 5;46
- 28 : Sets the current background to red with blinking foreground 5;41
- 29 : Sets the current background to magenta with blinking foreground 5;45
- 30 : Sets the current background to brown with blinking foreground 5;43
- 31 : Sets the current background to grey with blinking foreground 5;47
- Other things that Mystic does ...
- [A## - Move the cursor up ## lines
- [B## - Move the cursor down ## lines
- [C## - Move the cursor forward (to the right) ## columns
- [D## - Move the cursor backwards (to the left) ## columns
- [K - Clear from the current cursor position to the end of the line
- [L - Move cursor and erase data backwards from current column to column ##
- [X## - Move cursor to X coordinate ##
- [Y## - Move cursor to Y coordinate ##
- BS - Sends 1 destructive backspace sequence (ASCII 8-32-8)
- CL - Clears the screen (ANSI 1,1 locate and [2J or ASCII 12)
- CR - Send a carrage return and line feed (move to next line)
- RA - Restore the saved text attribute color
- RS - Restore the saved user's terminal screen
- SA - Save the current text attribute color
- SS - Save the entire user's terminal screen
- */
- // Covert MYSTIC color to (Proper) ANSI COLOR.
- const int MYSTIC[] = {0, 4, 2, 6, 1, 5, 3, 7};
- // ANSI_color = MYSTIC[ odd_mystic_color % 8 ]
- void write_color(int fd, int color) {
- char buffer[12];
- int slen;
- switch (color) {
- case 0:
- case 1:
- case 2:
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- slen = snprintf(buffer, sizeof(buffer), "\x1b[0;3%dm", MYSTIC[color]);
- if (slen >= sizeof(buffer)) {
- ZF_LOGE("snprintf failed write_color(%d)", color);
- }
- break;
- case 8:
- case 9:
- case 10:
- case 11:
- case 12:
- case 13:
- case 14:
- case 15:
- slen = snprintf(buffer, sizeof(buffer), "\x1b[0;1;3%dm", MYSTIC[color - 8]);
- if (slen >= sizeof(buffer)) {
- ZF_LOGE("snprintf failed write_color(%d)", color);
- }
- break;
- case 16:
- case 17:
- case 18:
- case 19:
- case 20:
- case 21:
- case 22:
- case 23:
- slen = snprintf(buffer, sizeof(buffer), "\x1b[4%dm", MYSTIC[color - 16]);
- if (slen >= sizeof(buffer)) {
- ZF_LOGE("snprintf failed write_color(%d)", color);
- }
- break;
- case 24:
- case 25:
- case 26:
- case 27:
- case 28:
- case 29:
- case 30:
- case 31:
- slen = snprintf(buffer, sizeof(buffer), "\x1b[5;4%dm", MYSTIC[color - 24]);
- if (slen >= sizeof(buffer)) {
- ZF_LOGE("snprintf failed write_color(%d)", color);
- }
- break;
- default:
- buffer[0] = 0;
- break;
- }
- ZF_LOGD("write_color( %d ): %s", color, repr(buffer));
- write(fd, buffer, strlen(buffer));
- }
- int send_file(int fd, char *filename) {
- FILE *fp;
- char buffer[100];
- int read;
- fp = fopen(filename, "rb");
- if (fp == NULL) {
- ZF_LOGD("Failed to open %s", filename);
- return 0;
- }
- while ((read = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
- write(fd, buffer, read);
- };
- fclose(fp);
- return 1;
- }
- void send_goto(int fd, int x, int y) {
- char gbuffer[16];
- sprintf(gbuffer, "\x1b[%d;%dH", y, x);
- write(fd, gbuffer, strlen(gbuffer));
- }
- int send_file(int fd, int x, int y, char *filename) {
- FILE *fp;
- char buffer[100];
- int read;
- fp = fopen(filename, "rb");
- if (fp == NULL) {
- ZF_LOGD("Failed to open %s", filename);
- return 0;
- }
- send_goto(fd, x, y);
- y++;
- while ((read = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
- char *cp, *last_cp;
- buffer[read] = 0;
- last_cp = buffer;
- while ((cp = strchr(last_cp, '\n')) != NULL) {
- *cp = 0;
- write(fd, last_cp, strlen(last_cp));
- send_goto(fd, x, y);
- y++;
- last_cp = cp + 1;
- };
- write(fd, last_cp, strlen(last_cp));
- };
- fclose(fp);
- return 1;
- }
- /**
- * process_trigger( fd, *cp )
- *
- * This process a command trigger.
- * It has seen TRIGGER, and now it is
- * processing whatever comes after it.
- * It will perform the process, and
- * return the char * of whatever is next.
- */
- const char *process_trigger(int fd, const char *cp) {
- char ch;
- int i, x, y;
- ch = *cp;
- cp++;
- switch (ch) {
- case 'D':
- i = 0;
- if (isdigit(*cp)) {
- i = (*cp) - '0';
- cp++;
- };
- if (isdigit(*cp)) {
- i *= 10;
- i += (*cp) - '0';
- cp++;
- };
- if ((i > 0) && (i < 80)) {
- ZF_LOGI("DEL %02d", i);
- for (x = 0; x < i; x++) {
- write(fd, "\b \b", 3);
- }
- };
- break;
- case 'C': {
- i = 0;
- if (*cp == 'R') {
- cp++;
- const char *restore = color_restore(&console);
- write(fd, restore, strlen(restore));
- break;
- }
- if (isdigit(*cp)) {
- i = (*cp) - '0';
- cp++;
- };
- if (isdigit(*cp)) {
- i *= 10;
- i += (*cp) - '0';
- cp++;
- };
- write_color(fd, i);
- } break;
- case 'F':
- case 'f': {
- int pos = 0;
- if (ch == 'f') {
- pos = 1;
- x = (*cp) - '0';
- cp++;
- x *= 10;
- x += (*cp) - '0';
- cp++;
- y = (*cp) - '0';
- cp++;
- y *= 10;
- y += (*cp) - '0';
- cp++;
- ZF_LOGI("file at (%d, %d)", x, y);
- }
- // Ok, look for filename
- char ansifile[32] = "./hh/";
- char *ap = ansifile + strlen(ansifile);
- while (*cp != '.') {
- *ap = *cp;
- ap++;
- *ap = 0;
- cp++;
- };
- strcat(ansifile, ".ans");
- cp++;
- ZF_LOGD("FILE [%s]", ansifile);
- if (pos) {
- send_file(fd, x, y, ansifile);
- } else {
- send_file(fd, ansifile);
- };
- } break;
- case 'G': {
- x = 0;
- if (isdigit(*cp)) {
- x = (*cp) - '0';
- cp++;
- };
- if (isdigit(*cp)) {
- x *= 10;
- x += (*cp) - '0';
- cp++;
- };
- y = 0;
- if (isdigit(*cp)) {
- y = (*cp) - '0';
- cp++;
- };
- if (isdigit(*cp)) {
- y *= 10;
- y += (*cp) - '0';
- cp++;
- };
- char buffer[20]; // row ; column H
- ZF_LOGD("GOTO (%d,%d)", x, y);
- sprintf(buffer, "\x1b[%d;%dH", y, x);
- write(fd, buffer, strlen(buffer));
- } break;
- case 'R': {
- i = 0;
- if (isdigit(*cp)) {
- i = (*cp) - '0';
- cp++;
- };
- if ((i > 0) && (i < 10)) {
- ZF_LOGI("RENDER %d", i);
- current_render.effect = i;
- } else {
- current_render.effect = 0;
- }
- } break;
- case 'S': {
- i = 0;
- if (isdigit(*cp)) {
- i = (*cp) - '0';
- cp++;
- };
- if ((i > 0) && (i < 10)) {
- ZF_LOGI("SPEED %d", i);
- current_render.speed = i;
- } else {
- current_render.speed = 0;
- }
- } break;
- case 'P': {
- i = 0;
- if (isdigit(*cp)) {
- i = (*cp) - '0';
- cp++;
- };
- if ((i > 0) && (i < 10)) {
- ZF_LOGI("PAWS %d", i);
- // sleep(i);
- if (!render_overlimit) {
- sleep(i);
- };
- }
- } break;
- }
- return cp;
- }
- /**
- * render_effect( fd, ch )
- *
- * Displays the given character with whatever
- * rendering effect is currently active.
- * (If any).
- */
- void render_effect(int fd, char ch) {
- int effect = current_render.effect;
- int l;
- char space = ' ';
- char bs = '\b';
- switch (effect) {
- case 1:
- // CHAR + SPC + BS
- render_sleep();
- write(fd, &ch, 1);
- render_sleep();
- write(fd, &space, 1);
- render_sleep();
- render_sleep();
- write(fd, &bs, 1);
- break;
- case 2:
- // CHAR + 8 spaces + 8 BS
- render_sleep();
- write(fd, &ch, 1);
- for (l = 0; l < 8; l++) {
- render_sleep();
- write(fd, &space, 1);
- }
- for (l = 0; l < 8; l++) {
- render_sleep();
- write(fd, &bs, 1);
- }
- break;
- case 0:
- default:
- // NORMAL
- render_sleep();
- write(fd, &ch, 1);
- break;
- }
- }
- /**
- * render( fd, string_out )
- *
- * Render an entire string.
- * Handles TRIGGER.
- * Renders with effects.
- */
- void render(int fd, const char *string_out, int len) {
- const char *cp = string_out;
- const char *trigger = cp;
- time_t start = time(NULL);
- int elapsed;
- int over = 0;
- reset_render();
- // ZF_LOGV("render(%d, %s)", fd, repr(string_out));
- ZF_LOGV_MEM(string_out, len, "render(%d, %d bytes):", fd, len);
- // Check our time from time to time.
- // If we start running long, disable sleeps.
- while ((trigger = strnstr(cp, len - (cp - string_out), TRIGGER)) != NULL) {
- // There is special things to handle in here.
- while (cp != trigger) {
- elapsed = time(NULL) - start;
- if (elapsed > SLEEP_LIMIT) {
- render_overlimit = 1;
- current_render.speed = 0;
- };
- // write(fd, cp, 1 );
- render_effect(fd, *cp);
- cp++;
- };
- // ZF_LOGI( "at trigger: (%s)", cp);
- cp += strlen(TRIGGER);
- // Ok, we're pointing at the trigger -- do something.
- cp = process_trigger(fd, cp);
- // ZF_LOGI( "after trigger: (%s)", cp);
- };
- // We still might be under a rendering effect.
- while (cp < (string_out + len)) {
- elapsed = time(NULL) - start;
- if (elapsed > SLEEP_LIMIT) {
- render_overlimit = 1;
- current_render.speed = 0;
- };
- // write(fd, cp, 1);
- render_effect(fd, *cp);
- cp++;
- }
- }
|