| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554 | #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;}const char **image_data = NULL;int image_size = 0;void render_image(const char **lines, int size) {  image_data = lines;  image_size = size;}void reset_image(void) {  image_data = NULL;  image_size = 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;47Other 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 %d > size %d", slen, (int)sizeof(buffer));      buffer[0] = 0;    }    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 %d > size %d", slen, (int)sizeof(buffer));      buffer[0] = 0;    }    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 %d > size %d", slen, (int)sizeof(buffer));      buffer[0] = 0;    }    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 %d > size %d", slen, (int)sizeof(buffer));      buffer[0] = 0;    }    break;  default:    buffer[0] = 0;    break;  }  ZF_LOGD("write_color( %d ): %s", color, repr(buffer));  write(fd, buffer, strlen(buffer));}void send_goto(int fd, int x, int y) {  char gbuffer[16];  int slen;  slen = snprintf(gbuffer, sizeof(gbuffer), "\x1b[%d;%dH", y, x);  if (slen >= sizeof(gbuffer)) {    ZF_LOGE("snprintf %d > size %d", slen, (int)sizeof(gbuffer));    gbuffer[0] = 0;  }  write(fd, gbuffer, strlen(gbuffer));}int send_image(int fd) {  int i;  const char **lines = image_data;  if (lines == NULL) {    ZF_LOGE("No image data for send_image.");    return 0;  }  for (i = 0; i < image_size; i++) {    write(fd, lines[i], strlen(lines[i]));    write(fd, "\r\n", 2);  }  // success  reset_image();  return 1;}int send_image(int fd, int x, int y) {  int i;  const char **lines = image_data;  if (lines == NULL ) {    ZF_LOGE("No image data for send_image(%d,%d)", x, y);    return 0;  }  for(i = 0; i < image_size;i++) {  send_goto(fd, x, y);  y++;    write(fd, lines[i], strlen(lines[i]));  }    // success  reset_image();  return 1;}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;}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;  // no longer takes a filename.  :)  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);    }    ZF_LOGD("IMAGE (%d lines)", image_size);    if (pos) {      send_image(fd, x, y);    } else {      send_image(fd);    };  } 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    int slen;    ZF_LOGD("GOTO (%d,%d)", x, y);    slen = snprintf(buffer, sizeof(buffer), "\x1b[%d;%dH", y, x);    if (slen >= sizeof(buffer)) {      ZF_LOGE("snprintf %d > size %d", slen, (int)sizeof(buffer));      buffer[0] = 0;    }    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++;  }}
 |