|
@@ -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);
|