|  | @@ -4,8 +4,7 @@
 | 
	
		
			
				|  |  |  #include <stdio.h>
 | 
	
		
			
				|  |  |  #include <string.h>
 | 
	
		
			
				|  |  |  #include <termios.h>
 | 
	
		
			
				|  |  | -#include <unistd.h> // usleep()
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +#include <unistd.h>
 | 
	
		
			
				|  |  |  #include <sys/select.h>
 | 
	
		
			
				|  |  |  #include <sys/wait.h>
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -73,6 +72,10 @@ static void file_output_open(const char *const log_path) {
 | 
	
		
			
				|  |  |    zf_log_set_output_v(ZF_LOG_PUT_STD, 0, file_output_callback);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +#include "terminal.h"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +struct console_details console;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  |   * Display a repr of the given string.
 | 
	
		
			
				|  |  |   *
 | 
	
	
		
			
				|  | @@ -215,847 +218,7 @@ int string_insert(char *buffer, int max_length, int pos, const char *insert) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // Should this be passed around to the functions that use it?
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -struct render {
 | 
	
		
			
				|  |  | -  int speed;
 | 
	
		
			
				|  |  | -  int effect;
 | 
	
		
			
				|  |  | -} current_render;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -int render_overlimit = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -void reset_render(void) {
 | 
	
		
			
				|  |  | -  current_render.speed = 0;
 | 
	
		
			
				|  |  | -  current_render.effect = 0;
 | 
	
		
			
				|  |  | -  render_overlimit = 0;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -#define TRIGGER "^"
 | 
	
		
			
				|  |  | -// Max limit we'll sleep before ignoring effects/speed.
 | 
	
		
			
				|  |  | -#define SLEEP_LIMIT 30
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -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);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/*
 | 
	
		
			
				|  |  | -Terminal tracking
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -Actually, I believe I only really need to track the color information.
 | 
	
		
			
				|  |  | -Everything else, I'm not sure I really care about.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -struct console_details {
 | 
	
		
			
				|  |  | -  int posx, posy;
 | 
	
		
			
				|  |  | -  int savedx, savedy;
 | 
	
		
			
				|  |  | -  char ansi[20]; // current ANSI command being processed.
 | 
	
		
			
				|  |  | -  int in_ansi;
 | 
	
		
			
				|  |  | -  int fgcolor; // 0-7 // not 0-15
 | 
	
		
			
				|  |  | -  int bgcolor; // 0-7
 | 
	
		
			
				|  |  | -  int status;  // 0, 1 or 5 (Blink)
 | 
	
		
			
				|  |  | -} console;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -void console_init(struct console_details *cdp) {
 | 
	
		
			
				|  |  | -  cdp->posx = 0;
 | 
	
		
			
				|  |  | -  cdp->posy = 0;
 | 
	
		
			
				|  |  | -  cdp->savedx = 0;
 | 
	
		
			
				|  |  | -  cdp->savedy = 0;
 | 
	
		
			
				|  |  | -  cdp->in_ansi = 0;
 | 
	
		
			
				|  |  | -  cdp->fgcolor = 7;
 | 
	
		
			
				|  |  | -  cdp->bgcolor = 0;
 | 
	
		
			
				|  |  | -  cdp->status = 0;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -void ansi_color(struct console_details *cdp, int color) {
 | 
	
		
			
				|  |  | -  // ZF_LOGV("ansi_color(%d)", color);
 | 
	
		
			
				|  |  | -  if (color == 0) {
 | 
	
		
			
				|  |  | -    cdp->status = 0;
 | 
	
		
			
				|  |  | -    cdp->fgcolor = 7;
 | 
	
		
			
				|  |  | -    cdp->bgcolor = 0;
 | 
	
		
			
				|  |  | -    return;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (color == 1) {
 | 
	
		
			
				|  |  | -    cdp->status = 1;
 | 
	
		
			
				|  |  | -    return;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (color == 5) {
 | 
	
		
			
				|  |  | -    cdp->status = 5;
 | 
	
		
			
				|  |  | -    return;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if ((color >= 30) && (color <= 37)) {
 | 
	
		
			
				|  |  | -    cdp->fgcolor = color - 30;
 | 
	
		
			
				|  |  | -    return;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if ((color >= 40) && (color <= 47)) {
 | 
	
		
			
				|  |  | -    cdp->bgcolor = color - 40;
 | 
	
		
			
				|  |  | -    return;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  ZF_LOGD("ansi_color( %d ) is unknown to me.", color);
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -const char *color_restore(struct console_details *cdp) {
 | 
	
		
			
				|  |  | -  static char buffer[30];
 | 
	
		
			
				|  |  | -  if (cdp->status == 0) {
 | 
	
		
			
				|  |  | -    sprintf(buffer, "\x1b[0;3%d;4%dm", cdp->fgcolor, cdp->bgcolor);
 | 
	
		
			
				|  |  | -  } else {
 | 
	
		
			
				|  |  | -    sprintf(buffer, "\x1b[0;%d;3%d;4%dm", cdp->status, cdp->fgcolor,
 | 
	
		
			
				|  |  | -            cdp->bgcolor);
 | 
	
		
			
				|  |  | -  };
 | 
	
		
			
				|  |  | -  return buffer;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -void console_ansi(struct console_details *cdp, const char *ansi) {
 | 
	
		
			
				|  |  | -  int understood = 0;
 | 
	
		
			
				|  |  | -  const char *cp = ansi;
 | 
	
		
			
				|  |  | -  const char *last = ansi + strlen(ansi) - 1;
 | 
	
		
			
				|  |  | -  int number, number2;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  if (*cp == '\x1b') {
 | 
	
		
			
				|  |  | -    cp++;
 | 
	
		
			
				|  |  | -    // Ok, that's expected.
 | 
	
		
			
				|  |  | -    if (*cp == '[') {
 | 
	
		
			
				|  |  | -      cp++;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      // https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      switch (*last) {
 | 
	
		
			
				|  |  | -      case 'A':
 | 
	
		
			
				|  |  | -        // cursor up
 | 
	
		
			
				|  |  | -        if (cp == last) {
 | 
	
		
			
				|  |  | -          number = 1;
 | 
	
		
			
				|  |  | -        } else {
 | 
	
		
			
				|  |  | -          number = atoi(cp);
 | 
	
		
			
				|  |  | -          if (number < 1) {
 | 
	
		
			
				|  |  | -            ZF_LOGD("console_ansi( %s ): number error (%d)", repr(ansi),
 | 
	
		
			
				|  |  | -                    number);
 | 
	
		
			
				|  |  | -            number = 1;
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | -        };
 | 
	
		
			
				|  |  | -        cdp->posy -= number;
 | 
	
		
			
				|  |  | -        if (cdp->posy < 0) {
 | 
	
		
			
				|  |  | -          cdp->posy = 0;
 | 
	
		
			
				|  |  | -          ZF_LOGD(
 | 
	
		
			
				|  |  | -              "console_ansi( %s ): attempt to move above top of screen (%d)",
 | 
	
		
			
				|  |  | -              repr(ansi), number);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        understood = 1;
 | 
	
		
			
				|  |  | -        return;
 | 
	
		
			
				|  |  | -      case 'B':
 | 
	
		
			
				|  |  | -        // cursor down
 | 
	
		
			
				|  |  | -        if (cp == last) {
 | 
	
		
			
				|  |  | -          number = 1;
 | 
	
		
			
				|  |  | -        } else {
 | 
	
		
			
				|  |  | -          number = atoi(cp);
 | 
	
		
			
				|  |  | -          if (number < 1) {
 | 
	
		
			
				|  |  | -            ZF_LOGD("console_ansi( %s ): number error (%d)", repr(ansi),
 | 
	
		
			
				|  |  | -                    number);
 | 
	
		
			
				|  |  | -            number = 1;
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | -        };
 | 
	
		
			
				|  |  | -        cdp->posy += number;
 | 
	
		
			
				|  |  | -        // check range/"scroll"
 | 
	
		
			
				|  |  | -        understood = 1;
 | 
	
		
			
				|  |  | -        return;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      case 'C':
 | 
	
		
			
				|  |  | -        // cursor forward
 | 
	
		
			
				|  |  | -        if (cp == last) {
 | 
	
		
			
				|  |  | -          number = 1;
 | 
	
		
			
				|  |  | -        } else {
 | 
	
		
			
				|  |  | -          number = atoi(cp);
 | 
	
		
			
				|  |  | -          if (number < 1) {
 | 
	
		
			
				|  |  | -            ZF_LOGD("console_ansi( %s ): number error (%d)", repr(ansi),
 | 
	
		
			
				|  |  | -                    number);
 | 
	
		
			
				|  |  | -            number = 1;
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | -        };
 | 
	
		
			
				|  |  | -        cdp->posx += number;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        // Well.  According to the "spec", the screen limits are hard
 | 
	
		
			
				|  |  | -        // If the cursor is already at the edge of the screen, this has no
 | 
	
		
			
				|  |  | -        // effect. ^ This is *NOT* how any ANSI BBS terminal program acts!
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        while (cdp->posx > 79) {
 | 
	
		
			
				|  |  | -          cdp->posy++;
 | 
	
		
			
				|  |  | -          // check range/"scroll"
 | 
	
		
			
				|  |  | -          cdp->posx -= 79;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        understood = 1;
 | 
	
		
			
				|  |  | -        return;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      case 'D':
 | 
	
		
			
				|  |  | -        // cursor backwards
 | 
	
		
			
				|  |  | -        if (cp == last) {
 | 
	
		
			
				|  |  | -          number = 1;
 | 
	
		
			
				|  |  | -        } else {
 | 
	
		
			
				|  |  | -          number = atoi(cp);
 | 
	
		
			
				|  |  | -          if (number < 1) {
 | 
	
		
			
				|  |  | -            ZF_LOGD("console_ansi( %s ): number error (%d)", repr(ansi),
 | 
	
		
			
				|  |  | -                    number);
 | 
	
		
			
				|  |  | -            number = 0;
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | -        };
 | 
	
		
			
				|  |  | -        cdp->posx -= number;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        // Well.  According to the "spec", the screen limits are hard
 | 
	
		
			
				|  |  | -        // If the cursor is already at the edge of the screen, this has no
 | 
	
		
			
				|  |  | -        // effect. ^ This is *NOT* how any ANSI BBS terminal program acts!
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        while (cdp->posx < 0) {
 | 
	
		
			
				|  |  | -          cdp->posy--;
 | 
	
		
			
				|  |  | -          if (cdp->posy < 0) {
 | 
	
		
			
				|  |  | -            cdp->posy = 0;
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | -          cdp->posx += 79;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        understood = 1;
 | 
	
		
			
				|  |  | -        return;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      case 'H':
 | 
	
		
			
				|  |  | -        // cursor position
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if (*cp == ';') {
 | 
	
		
			
				|  |  | -          // Missing first number
 | 
	
		
			
				|  |  | -          number = 1;
 | 
	
		
			
				|  |  | -          cp++;
 | 
	
		
			
				|  |  | -          if (cp == last) {
 | 
	
		
			
				|  |  | -            // missing 2nd number as well?
 | 
	
		
			
				|  |  | -            number2 = 1;
 | 
	
		
			
				|  |  | -          } else {
 | 
	
		
			
				|  |  | -            number2 = atoi(cp);
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | -        } else {
 | 
	
		
			
				|  |  | -          // Ok, find the first number
 | 
	
		
			
				|  |  | -          number = atoi(cp);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -          cp = strchr(cp, ';');
 | 
	
		
			
				|  |  | -          if (cp == NULL) {
 | 
	
		
			
				|  |  | -            // Missing 2nd number
 | 
	
		
			
				|  |  | -            number2 = 1;
 | 
	
		
			
				|  |  | -          } else {
 | 
	
		
			
				|  |  | -            // 2nd number found, maybe.
 | 
	
		
			
				|  |  | -            cp++;
 | 
	
		
			
				|  |  | -            if (cp == last) {
 | 
	
		
			
				|  |  | -              number2 = 1;
 | 
	
		
			
				|  |  | -            } else {
 | 
	
		
			
				|  |  | -              number2 = atoi(cp);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        // Our positions start at zero, not one.
 | 
	
		
			
				|  |  | -        cdp->posx = number - 1;
 | 
	
		
			
				|  |  | -        cdp->posy = number2 - 1;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        understood = 1;
 | 
	
		
			
				|  |  | -        break;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      case 'J':
 | 
	
		
			
				|  |  | -        // clear
 | 
	
		
			
				|  |  | -        if (cp == last) {
 | 
	
		
			
				|  |  | -          number = 0;
 | 
	
		
			
				|  |  | -        } else {
 | 
	
		
			
				|  |  | -          number = atoi(cp);
 | 
	
		
			
				|  |  | -        };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        // clears ... part of the screen.
 | 
	
		
			
				|  |  | -        if (number == 2) {
 | 
	
		
			
				|  |  | -          cdp->posx = 0;
 | 
	
		
			
				|  |  | -          cdp->posy = 0;
 | 
	
		
			
				|  |  | -        };
 | 
	
		
			
				|  |  | -        understood = 1;
 | 
	
		
			
				|  |  | -        break;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      case 'm':
 | 
	
		
			
				|  |  | -        // color
 | 
	
		
			
				|  |  | -        if (cp == last) {
 | 
	
		
			
				|  |  | -          // nothing given, default to 0.
 | 
	
		
			
				|  |  | -          number = 0;
 | 
	
		
			
				|  |  | -          ansi_color(cdp, number);
 | 
	
		
			
				|  |  | -        } else {
 | 
	
		
			
				|  |  | -          while (cp != last) {
 | 
	
		
			
				|  |  | -            number = atoi(cp);
 | 
	
		
			
				|  |  | -            ansi_color(cdp, number);
 | 
	
		
			
				|  |  | -            cp++;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            while ((cp != last) && (isdigit(*cp))) {
 | 
	
		
			
				|  |  | -              cp++;
 | 
	
		
			
				|  |  | -            };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            if (cp != last) {
 | 
	
		
			
				|  |  | -              if (*cp == ';') {
 | 
	
		
			
				|  |  | -                cp++;
 | 
	
		
			
				|  |  | -              }
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        understood = 1;
 | 
	
		
			
				|  |  | -        break;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      case 't':
 | 
	
		
			
				|  |  | -      case 'r':
 | 
	
		
			
				|  |  | -      case 'h':
 | 
	
		
			
				|  |  | -      case '!':
 | 
	
		
			
				|  |  | -        // These are ones that I don't care about.
 | 
	
		
			
				|  |  | -      case 'n': // This is terminal detect -- give me cursor position
 | 
	
		
			
				|  |  | -        understood = 1;
 | 
	
		
			
				|  |  | -        break;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      default:
 | 
	
		
			
				|  |  | -        // unsure -- possibly not important
 | 
	
		
			
				|  |  | -        ZF_LOGD("console_ansi( %s ): ???", repr(ansi));
 | 
	
		
			
				|  |  | -        understood = 0;
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  if (!understood) {
 | 
	
		
			
				|  |  | -    ZF_LOGD("console_ansi( %s ): was not understood.", repr(ansi));
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/*
 | 
	
		
			
				|  |  | - * console_char()
 | 
	
		
			
				|  |  | - *  return whether or not we are still in_ansi
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -int console_char(struct console_details *cdp, char ch) {
 | 
	
		
			
				|  |  | -  char *cp;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  if (cdp->in_ansi) {
 | 
	
		
			
				|  |  | -    // Ok, append this char
 | 
	
		
			
				|  |  | -    cp = cdp->ansi + strlen(cdp->ansi);
 | 
	
		
			
				|  |  | -    *cp = ch;
 | 
	
		
			
				|  |  | -    cp++;
 | 
	
		
			
				|  |  | -    *cp = 0;
 | 
	
		
			
				|  |  | -    if (isalpha(ch)) {
 | 
	
		
			
				|  |  | -      // Ok!
 | 
	
		
			
				|  |  | -      console_ansi(cdp, cdp->ansi);
 | 
	
		
			
				|  |  | -      cdp->in_ansi = 0;
 | 
	
		
			
				|  |  | -      cdp->ansi[0] = 0;
 | 
	
		
			
				|  |  | -      return 1;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    return 1;
 | 
	
		
			
				|  |  | -  } else {
 | 
	
		
			
				|  |  | -    if (ch == '\x1b') {
 | 
	
		
			
				|  |  | -      cp = cdp->ansi;
 | 
	
		
			
				|  |  | -      *cp = ch;
 | 
	
		
			
				|  |  | -      cp++;
 | 
	
		
			
				|  |  | -      *cp = 0;
 | 
	
		
			
				|  |  | -      cdp->in_ansi = 1;
 | 
	
		
			
				|  |  | -      return 1;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    if (ch == '\r') {
 | 
	
		
			
				|  |  | -      // Carriage return
 | 
	
		
			
				|  |  | -      cdp->posx = 0;
 | 
	
		
			
				|  |  | -      return 0;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    if (ch == '\n') {
 | 
	
		
			
				|  |  | -      cdp->posy++;
 | 
	
		
			
				|  |  | -      // check range/"scroll"
 | 
	
		
			
				|  |  | -      return 0;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    if (ch == '\b') {
 | 
	
		
			
				|  |  | -      // Backspace.
 | 
	
		
			
				|  |  | -      if (cdp->posx > 0) {
 | 
	
		
			
				|  |  | -        cdp->posx--;
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      return 0;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    if (ch == '\f') {
 | 
	
		
			
				|  |  | -      // form feed
 | 
	
		
			
				|  |  | -      // treat as clear screen
 | 
	
		
			
				|  |  | -      cdp->posx = 0;
 | 
	
		
			
				|  |  | -      cdp->posy = 0;
 | 
	
		
			
				|  |  | -      return 0;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /*
 | 
	
		
			
				|  |  | -      I don't believe that anything else can possibly be here, other then an
 | 
	
		
			
				|  |  | -      actual printable character.  So!
 | 
	
		
			
				|  |  | -    */
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    cdp->posx++;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    if (cdp->posx > 79) {
 | 
	
		
			
				|  |  | -      cdp->posx = 0;
 | 
	
		
			
				|  |  | -      cdp->posy++;
 | 
	
		
			
				|  |  | -      // check range/"scroll"
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    return 0;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -void console_string(struct console_details *cdp, const char *chars) {
 | 
	
		
			
				|  |  | -  int x;
 | 
	
		
			
				|  |  | -  for (x = 0; x < strlen(chars); x++) {
 | 
	
		
			
				|  |  | -    console_char(cdp, chars[x]);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -void console_receive(struct console_details *cdp, const char *chars, int len) {
 | 
	
		
			
				|  |  | -  int x;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  for (x = 0; x < len; x++) {
 | 
	
		
			
				|  |  | -    console_char(cdp, chars[x]);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/*
 | 
	
		
			
				|  |  | -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];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  switch (color) {
 | 
	
		
			
				|  |  | -  case 0:
 | 
	
		
			
				|  |  | -  case 1:
 | 
	
		
			
				|  |  | -  case 2:
 | 
	
		
			
				|  |  | -  case 3:
 | 
	
		
			
				|  |  | -  case 4:
 | 
	
		
			
				|  |  | -  case 5:
 | 
	
		
			
				|  |  | -  case 6:
 | 
	
		
			
				|  |  | -  case 7:
 | 
	
		
			
				|  |  | -    sprintf(buffer, "\x1b[0;3%dm", MYSTIC[color]);
 | 
	
		
			
				|  |  | -    break;
 | 
	
		
			
				|  |  | -  case 8:
 | 
	
		
			
				|  |  | -  case 9:
 | 
	
		
			
				|  |  | -  case 10:
 | 
	
		
			
				|  |  | -  case 11:
 | 
	
		
			
				|  |  | -  case 12:
 | 
	
		
			
				|  |  | -  case 13:
 | 
	
		
			
				|  |  | -  case 14:
 | 
	
		
			
				|  |  | -  case 15:
 | 
	
		
			
				|  |  | -    sprintf(buffer, "\x1b[0;1;3%dm", MYSTIC[color - 8]);
 | 
	
		
			
				|  |  | -    break;
 | 
	
		
			
				|  |  | -  case 16:
 | 
	
		
			
				|  |  | -  case 17:
 | 
	
		
			
				|  |  | -  case 18:
 | 
	
		
			
				|  |  | -  case 19:
 | 
	
		
			
				|  |  | -  case 20:
 | 
	
		
			
				|  |  | -  case 21:
 | 
	
		
			
				|  |  | -  case 22:
 | 
	
		
			
				|  |  | -  case 23:
 | 
	
		
			
				|  |  | -    sprintf(buffer, "\x1b[4%dm", MYSTIC[color - 16]);
 | 
	
		
			
				|  |  | -    break;
 | 
	
		
			
				|  |  | -  case 24:
 | 
	
		
			
				|  |  | -  case 25:
 | 
	
		
			
				|  |  | -  case 26:
 | 
	
		
			
				|  |  | -  case 27:
 | 
	
		
			
				|  |  | -  case 28:
 | 
	
		
			
				|  |  | -  case 29:
 | 
	
		
			
				|  |  | -  case 30:
 | 
	
		
			
				|  |  | -  case 31:
 | 
	
		
			
				|  |  | -    sprintf(buffer, "\x1b[5;4%dm", MYSTIC[color - 24]);
 | 
	
		
			
				|  |  | -    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 - trigger), 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++;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +#include "render.h"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /*
 | 
	
		
			
				|  |  |      These are harry "timeout" events.
 | 
	
	
		
			
				|  | @@ -1670,6 +833,18 @@ int mangle(int fd, const char *buffer, int len) {
 | 
	
		
			
				|  |  |     W\x1[0;34mORDS with color changes in them, and work with them.
 | 
	
		
			
				|  |  |     without screwing up the color changes, of course.  :P
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  Example:
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +   Y\x1b[0;1mes \x1b[0;1;34m\x1b[0;1;34;44m N\x1b[0;1;44mo
 | 
	
		
			
				|  |  | +   Yes No
 | 
	
		
			
				|  |  | +   ^ This would be a job for a crazy regex.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +   I'd have to map the characters to positions in the buffer.  :S
 | 
	
		
			
				|  |  | +   I'd want mangle and wrangle to work.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +   The Message menu -- doesn't hardly get mangled at all (at least on
 | 
	
		
			
				|  |  | +   my test site).  Because all of the color changes break up the 
 | 
	
		
			
				|  |  | +   words in the menu.
 | 
	
		
			
				|  |  |     */
 | 
	
		
			
				|  |  |    x = rx_match(&WORDS, work);
 | 
	
		
			
				|  |  |    ZF_LOGD("found %d word groups", x);
 |