123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- /*
- Terminal tracking
- Actually, I believe I only really need to track the color information.
- Everything else, I'm not sure I really care about.
- */
- #include <string.h>
- #include <ctype.h>
- #include <stdlib.h>
- #include <stdio.h> // sprintf
- #include "terminal.h"
- #include "zf_log.h"
- extern const char *repr(const char *data);
- 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]);
- }
- }
|