From 1756bdf30d5d12a265bef92d52259961c4965224 Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Tue, 21 Nov 2006 06:35:04 +0000 Subject: [PATCH] * Improved ANSI importer so that it supports a lot more of what is needed for basic vt220 emulation. --- cucul/import.c | 320 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 253 insertions(+), 67 deletions(-) diff --git a/cucul/import.c b/cucul/import.c index 6907034..470d3b0 100644 --- a/cucul/import.c +++ b/cucul/import.c @@ -47,7 +47,7 @@ struct ansi_grcm uint8_t fg, bg; /* ANSI-context fg/bg */ uint8_t efg, ebg; /* Effective (libcucul) fg/bg */ uint8_t dfg, dbg; /* Default fg/bg */ - uint8_t bold, negative, concealed; + uint8_t bold, blink, italics, negative, concealed, underline; }; static long int import_caca(cucul_canvas_t *, void const *, unsigned int); @@ -348,57 +348,109 @@ static long int import_ansi(cucul_canvas_t *cv, { struct ansi_grcm grcm; unsigned char const *buffer = (unsigned char const*)data; - unsigned int i, j, skip, dummy = 0; - unsigned int width = 0, height = 0, wch = 1; - uint32_t savedattr, resizeattr; - unsigned long int ch; + unsigned int i, j, skip, growx = 0, growy = 0, dummy = 0; + unsigned int width, height; + uint32_t savedattr, clearattr; int x = 0, y = 0, save_x = 0, save_y = 0; - cucul_set_canvas_size(cv, 0, 0); if(utf8) { + width = cv->width; + height = cv->height; + growx = !width; + growy = !height; + x = cv->frames[cv->frame].x; + y = cv->frames[cv->frame].y; grcm.dfg = CUCUL_DEFAULT; grcm.dbg = CUCUL_TRANSPARENT; } else { + cucul_set_canvas_size(cv, width = 80, height = 0); + growx = 0; + growy = 1; grcm.dfg = CUCUL_LIGHTGRAY; grcm.dbg = CUCUL_BLACK; - cucul_set_color_ansi(cv, CUCUL_LIGHTGRAY, CUCUL_BLACK); } - resizeattr = cucul_get_attr(cv, -1, -1); + savedattr = cucul_get_attr(cv, -1, -1); + cucul_set_color_ansi(cv, grcm.dfg, grcm.dbg); + clearattr = cucul_get_attr(cv, -1, -1); + + if(utf8) + cucul_set_attr(cv, savedattr); + /* FIXME: this is not right, we should store the grcm information as a + * persistent structure within the canvas. */ ansi_parse_grcm(cv, &grcm, 1, &dummy); for(i = 0; i < size; i += skip) { + uint32_t ch = 0; + int wch = 0; + skip = 1; - /* Wrap long lines */ - if((unsigned int)x >= 80) + if(!utf8 && buffer[i] == '\x1a' && i + 7 < size + && !memcmp(buffer + i + 1, "SAUCE00", 7)) + break; /* End before SAUCE data */ + + else if(buffer[i] == '\r') + { + x = 0; + } + + else if(buffer[i] == '\n') { x = 0; y++; } - if(buffer[i] == '\x1a' && size - i >= 8 - && !memcmp(buffer + i + 1, "SAUCE00", 7)) - break; /* End before SAUCE data */ + else if(buffer[i] == '\t') + { + x = (x + 7) & ~7; + } - if(buffer[i] == '\r') - continue; /* DOS sucks */ + else if(buffer[i] == '\x08') + { + if(x > 0) + x--; + } - if(buffer[i] == '\n') + else if(buffer[i] == '\x1b' && buffer[i + 1] == ']' + && buffer[i + 2] == '0' && buffer[i + 3] == ';') { - x = 0; - y++; - continue; + for(j = i + 4; j < size; j++) + if(buffer[j] == '\x07' || buffer[j] == '\x1b' + || buffer[j] == '\r' || buffer[j] == '\n') + break; + + if(j < size && buffer[j] == '\x07') + { + char *title = malloc(j - i - 4 + 1); + memcpy(title, buffer + i + 4, j - i - 4); + title[j - i - 4] = '\0'; + debug("ansi import: got display title '%s'", title); + skip += j - i; + free(title); + } + } + + /* If there are not enough characters to parse the escape sequence, + * wait until the next try. We require 3. */ + else if(buffer[i] == '\x1b' && i + 2 >= size) + break; + + /* XXX: What the fuck is this shit? */ + else if(buffer[i] == '\x1b' && buffer[i + 1] == '(' + && buffer[i + 2] == 'B') + { + skip += 2; } /* Interpret escape commands, as per Standard ECMA-48 "Control * Functions for Coded Character Sets", 5.4. Control sequences. */ - if(buffer[i] == '\x1b' && buffer[i + 1] == '[') + else if(buffer[i] == '\x1b' && buffer[i + 1] == '[') { unsigned int argc = 0, argv[101]; unsigned int param, inter, final; @@ -423,7 +475,8 @@ static long int import_ansi(cucul_canvas_t *cv, if(buffer[i + final] < 0x20 || buffer[i + final] > 0x2f) break; - if(buffer[i + final] < 0x40 || buffer[i + final] > 0x7e) + if(i + final >= size + || buffer[i + final] < 0x40 || buffer[i + final] > 0x7e) break; /* Invalid Final Byte */ skip += final; @@ -431,8 +484,9 @@ static long int import_ansi(cucul_canvas_t *cv, /* Sanity checks */ if(param < inter && buffer[i + param] >= 0x3c) { - fprintf(stderr, "private sequence \"^[[%.*s\"\n", - final - param + 1, buffer + i + param); + /* Private sequence, only parse what we know */ + debug("ansi import: private sequence \"^[[%.*s\"", + final - param + 1, buffer + i + param); continue; /* Private sequence, skip it entirely */ } @@ -459,64 +513,115 @@ static long int import_ansi(cucul_canvas_t *cv, * given in ECMA-48 8.3: Definition of control functions. */ switch(buffer[i + final]) { - case 'f': /* CUP - Cursor Position */ - case 'H': /* HVP - Character And Line Position */ + case 'H': /* CUP (0x48) - Cursor Position */ x = (argc > 1 && argv[1] > 0) ? argv[1] - 1 : 0; y = (argc > 0 && argv[0] > 0) ? argv[0] - 1 : 0; break; - case 'A': /* CUU - Cursor Up */ + case 'A': /* CUU (0x41) - Cursor Up */ y -= argc ? argv[0] : 1; if(y < 0) y = 0; break; - case 'B': /* CUD - Cursor Down */ + case 'B': /* CUD (0x42) - Cursor Down */ y += argc ? argv[0] : 1; break; - case 'C': /* CUF - Cursor Right */ + case 'C': /* CUF (0x43) - Cursor Right */ x += argc ? argv[0] : 1; break; - case 'D': /* CUB - Cursor Left */ + case 'D': /* CUB (0x44) - Cursor Left */ x -= argc ? argv[0] : 1; if(x < 0) x = 0; break; - case 's': /* Private (save cursor position) */ - save_x = x; - save_y = y; + case 'G': /* CHA (0x47) - Cursor Character Absolute */ + x = (argc && argv[0] > 0) ? argv[0] - 1 : 0; break; - case 'u': /* Private (reload cursor position) */ - x = save_x; - y = save_y; + case 'J': /* ED (0x4a) - Erase In Page */ + savedattr = cucul_get_attr(cv, -1, -1); + cucul_set_attr(cv, clearattr); + if(!argc || argv[0] == 0) + { + cucul_draw_line(cv, x, y, width, y, ' '); + cucul_fill_box(cv, 0, y + 1, width - 1, height - 1, ' '); + } + else if(argv[0] == 1) + { + cucul_fill_box(cv, 0, 0, width - 1, y - 1, ' '); + cucul_draw_line(cv, 0, y, x, y, ' '); + } + else if(argv[0] == 2) + //x = y = 0; + cucul_fill_box(cv, 0, 0, width - 1, height - 1, ' '); + cucul_set_attr(cv, savedattr); break; - case 'J': /* ED - Erase In Page */ - if(argv[0] == 2) - x = y = 0; + case 'K': /* EL (0x4b) - Erase In Line */ + if(!argc || argv[0] == 0) + cucul_draw_line(cv, x, y, width, y, ' '); + else if(argv[0] == 1) + cucul_draw_line(cv, 0, y, x, y, ' '); + else if(argv[0] == 2) + if((unsigned int)x < width) + cucul_draw_line(cv, x, y, width - 1, y, ' '); + //x = width; break; - case 'K': /* EL - Erase In Line */ - if(width < 80) + case 'P': /* DCH (0x50) - Delete Character */ + if(!argc || argv[0] == 0) + argv[0] = 1; /* echo -ne 'foobar\r\e[0P\n' */ + for(j = 0; (unsigned int)(j + argv[0]) < width; j++) + { + cucul_put_char(cv, j, y, + cucul_get_char(cv, j + argv[0], y)); + cucul_put_attr(cv, j, y, + cucul_get_attr(cv, j + argv[0], y)); + } +#if 0 + savedattr = cucul_get_attr(cv, -1, -1); + cucul_set_attr(cv, clearattr); + for( ; (unsigned int)j < width; j++) + cucul_put_char(cv, j, y, ' '); + cucul_set_attr(cv, savedattr); +#endif + case 'X': /* ECH (0x58) - Erase Character */ + if(argc && argv[0]) { savedattr = cucul_get_attr(cv, -1, -1); - cucul_set_attr(cv, resizeattr); - cucul_set_canvas_size(cv, width = 80, height); + cucul_set_attr(cv, clearattr); + cucul_draw_line(cv, x, y, x + argv[0] - 1, y, ' '); cucul_set_attr(cv, savedattr); } - for(j = x; j < 80; j++) - cucul_put_char(cv, j, y, ' '); - x = 80; + case 'd': /* VPA (0x64) - Line Position Absolute */ + y = (argc && argv[0] > 0) ? argv[0] - 1 : 0; + break; + case 'f': /* HVP (0x66) - Character And Line Position */ + x = (argc > 1 && argv[1] > 0) ? argv[1] - 1 : 0; + y = (argc > 0 && argv[0] > 0) ? argv[0] - 1 : 0; break; - case 'm': /* SGR - Select Graphic Rendition */ + case 'h': /* SM (0x68) - FIXME */ + debug("ansi import: set mode %i", argc ? (int)argv[0] : -1); + break; + case 'l': /* RM (0x6c) - FIXME */ + debug("ansi import: reset mode %i", argc ? (int)argv[0] : -1); + break; + case 'm': /* SGR (0x6d) - Select Graphic Rendition */ ansi_parse_grcm(cv, &grcm, argc, argv); break; + case 's': /* Private (save cursor position) */ + save_x = x; + save_y = y; + break; + case 'u': /* Private (reload cursor position) */ + x = save_x; + y = save_y; + break; default: - fprintf(stderr, "unknown command %c\n", buffer[i + final]); + debug("ansi import: unknown command \"^[[%.*s\"", + final - param + 1, buffer + i + param); break; } - - continue; } /* Get the character we’re going to paste */ - if(utf8) + else if(utf8) { unsigned int bytes; @@ -543,39 +648,76 @@ static long int import_ansi(cucul_canvas_t *cv, else { ch = cucul_cp437_to_utf32(buffer[i]); + wch = 1; } - /* Make sure the canvas is big enough. */ - if((unsigned int)x + wch > width) + /* Wrap long lines or grow horizontally */ + while((unsigned int)x + wch > width) { - savedattr = cucul_get_attr(cv, -1, -1); - cucul_set_attr(cv, resizeattr); - cucul_set_canvas_size(cv, width = x + wch, height); - cucul_set_attr(cv, savedattr); + if(growx) + { + savedattr = cucul_get_attr(cv, -1, -1); + cucul_set_attr(cv, clearattr); + cucul_set_canvas_size(cv, width = x + wch, height); + cucul_set_attr(cv, savedattr); + } + else + { + x -= width; + y++; + } } + /* Scroll or grow vertically */ if((unsigned int)y >= height) { savedattr = cucul_get_attr(cv, -1, -1); - cucul_set_attr(cv, resizeattr); - cucul_set_canvas_size(cv, width, height = y + 1); + cucul_set_attr(cv, clearattr); + if(growy) + { + cucul_set_canvas_size(cv, width, height = y + 1); + } + else + { + int lines = (y - height) + 1; + + for(j = 0; j + lines < height; j++) + { + memcpy(cv->attrs + j * cv->width, + cv->attrs + (j + lines) * cv->width, cv->width * 4); + memcpy(cv->chars + j * cv->width, + cv->chars + (j + lines) * cv->width, cv->width * 4); + } + cucul_fill_box(cv, 0, height - lines, + cv->width - 1, height - 1, ' '); + y -= lines; + } cucul_set_attr(cv, savedattr); } - /* Now paste our character */ - cucul_put_char(cv, x, y, ch); - x += wch; + /* Now paste our character, if any */ + if(wch) + { + cucul_put_char(cv, x, y, ch); + x += wch; + } } - if((unsigned int)y > height) + if(growy && (unsigned int)y > height) { savedattr = cucul_get_attr(cv, -1, -1); - cucul_set_attr(cv, resizeattr); + cucul_set_attr(cv, clearattr); cucul_set_canvas_size(cv, width, height = y); cucul_set_attr(cv, savedattr); } - return size; + cv->frames[cv->frame].x = x; + cv->frames[cv->frame].y = y; + + if(utf8) + cucul_set_attr(cv, savedattr); + + return i; } /* XXX : ANSI loader helper */ @@ -607,14 +749,23 @@ static void ansi_parse_grcm(cucul_canvas_t *cv, struct ansi_grcm *g, case 0: /* default rendition */ g->fg = g->dfg; g->bg = g->dbg; - g->bold = g->negative = g->concealed = 0; + g->bold = g->blink = g->italics = 0; + g->negative = g->concealed = g->underline = 0; break; case 1: /* bold or increased intensity */ g->bold = 1; break; + case 2: /* faint, decreased intensity or second colour */ + break; + case 3: /* italicized */ + g->italics = 1; + break; case 4: /* singly underlined */ + g->underline = 1; break; case 5: /* slowly blinking (less then 150 per minute) */ + case 6: /* rapidly blinking (150 per minute or more) */ + g->blink = 1; break; case 7: /* negative image */ g->negative = 1; @@ -622,20 +773,55 @@ static void ansi_parse_grcm(cucul_canvas_t *cv, struct ansi_grcm *g, case 8: /* concealed characters */ g->concealed = 1; break; - case 22: /* normal colour or normal intensity (neither bold nor faint) */ + case 9: /* crossed-out (characters still legible but marked as to be + * deleted */ + break; + case 21: /* doubly underlined */ + g->underline = 1; + break; + case 22: /* normal colour or normal intensity (neither bold nor + * faint) */ g->bold = 0; break; + case 23: /* not italicized, not fraktur */ + g->italics = 0; + break; + case 24: /* not underlined (neither singly nor doubly) */ + g->underline = 0; + break; + case 25: /* steady (not blinking) */ + g->blink = 0; + break; + case 26: /* (reserved for proportional spacing as specified in CCITT + * Recommendation T.61) */ + break; + case 27: /* positive image */ + g->negative = 0; + break; case 28: /* revealed characters */ g->concealed = 0; break; + case 29: /* not crossed out */ + break; + case 38: /* (reserved for future standardization, intended for setting + * character foreground colour as specified in ISO 8613-6 + * [CCITT Recommendation T.416]) */ + break; case 39: /* default display colour (implementation-defined) */ g->fg = g->dfg; break; + case 48: /* (reserved for future standardization, intended for setting + * character background colour as specified in ISO 8613-6 + * [CCITT Recommendation T.416]) */ + break; case 49: /* default background colour (implementation-defined) */ g->bg = g->dbg; break; + case 50: /* (reserved for cancelling the effect of the rendering + * aspect established by parameter value 26) */ + break; default: - fprintf(stderr, "unknown sgr %i\n", argv[j]); + debug("ansi import: unknown sgr %i", argv[j]); break; } }