#include "vterm.h" #include "../src/vterm_internal.h" // We pull in some internal bits too #include #include #include #define streq(a,b) (!strcmp(a,b)) #define strstartswith(a,b) (!strncmp(a,b,strlen(b))) static size_t inplace_hex2bytes(char *s) { char *inpos = s, *outpos = s; while(*inpos) { unsigned int ch; if(sscanf(inpos, "%2x", &ch) < 1) break; *outpos = ch; outpos += 1; inpos += 2; } return outpos - s; } static VTermModifier strpe_modifiers(char **strp) { VTermModifier state = 0; while((*strp)[0]) { switch(((*strp)++)[0]) { case 'S': state |= VTERM_MOD_SHIFT; break; case 'C': state |= VTERM_MOD_CTRL; break; case 'A': state |= VTERM_MOD_ALT; break; default: return state; } } return state; } static VTermKey strp_key(char *str) { static struct { char *name; VTermKey key; } keys[] = { { "Up", VTERM_KEY_UP }, { "Tab", VTERM_KEY_TAB }, { "Enter", VTERM_KEY_ENTER }, { "KP0", VTERM_KEY_KP_0 }, { "F1", VTERM_KEY_FUNCTION(1) }, { NULL, VTERM_KEY_NONE }, }; int i; for(i = 0; keys[i].name; i++) { if(streq(str, keys[i].name)) return keys[i].key; } return VTERM_KEY_NONE; } static void print_color(const VTermColor *col) { if (VTERM_COLOR_IS_RGB(col)) { printf("rgb(%d,%d,%d", col->red, col->green, col->blue); } else if (VTERM_COLOR_IS_INDEXED(col)) { printf("idx(%d", col->index); } else { printf("invalid(%d", col->type); } if (VTERM_COLOR_IS_DEFAULT_FG(col)) { printf(",is_default_fg"); } if (VTERM_COLOR_IS_DEFAULT_BG(col)) { printf(",is_default_bg"); } printf(")"); } static VTerm *vt; static VTermState *state; static VTermScreen *screen; static VTermEncodingInstance encoding; static void term_output(const char *s, size_t len, void *user UNUSED) { size_t i; printf("output "); for(i = 0; i < len; i++) printf("%x%s", (unsigned char)s[i], i < len-1 ? "," : "\n"); } static void printhex(const char *s, size_t len) { while(len--) printf("%02x", (uint8_t)(s++)[0]); } static int parser_text(const char bytes[], size_t len, void *user UNUSED) { size_t i; printf("text "); for(i = 0; i < len; i++) { unsigned char b = bytes[i]; if(b < 0x20 || b == 0x7f || (b >= 0x80 && b < 0xa0)) break; printf(i ? ",%x" : "%x", b); } printf("\n"); return i; } static int parser_control(unsigned char control, void *user UNUSED) { printf("control %02x\n", control); return 1; } static int parser_escape(const char bytes[], size_t len, void *user UNUSED) { if(bytes[0] >= 0x20 && bytes[0] < 0x30) { if(len < 2) return -1; len = 2; } else { len = 1; } printf("escape "); printhex(bytes, len); printf("\n"); return len; } static int parser_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user UNUSED) { int i; printf("csi %02x", command); if(leader && leader[0]) { printf(" L="); for(i = 0; leader[i]; i++) printf("%02x", leader[i]); } for(i = 0; i < argcount; i++) { char sep = i ? ',' : ' '; if(args[i] == CSI_ARG_MISSING) printf("%c*", sep); else printf("%c%ld%s", sep, CSI_ARG(args[i]), CSI_ARG_HAS_MORE(args[i]) ? "+" : ""); } if(intermed && intermed[0]) { printf(" I="); for(i = 0; intermed[i]; i++) printf("%02x", intermed[i]); } printf("\n"); return 1; } static int parser_osc(int command, VTermStringFragment frag, void *user UNUSED) { printf("osc "); if(frag.initial) { if(command == -1) printf("["); else printf("[%d;", command); } printhex(frag.str, frag.len); if(frag.final) printf("]"); printf("\n"); return 1; } static int parser_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user UNUSED) { printf("dcs "); if(frag.initial) { size_t i; printf("["); for(i = 0; i < commandlen; i++) printf("%02x", command[i]); } printhex(frag.str, frag.len); if(frag.final) printf("]"); printf("\n"); return 1; } static int parser_apc(VTermStringFragment frag, void *user UNUSED) { printf("apc "); if(frag.initial) printf("["); printhex(frag.str, frag.len); if(frag.final) printf("]"); printf("\n"); return 1; } static int parser_pm(VTermStringFragment frag, void *user UNUSED) { printf("pm "); if(frag.initial) printf("["); printhex(frag.str, frag.len); if(frag.final) printf("]"); printf("\n"); return 1; } static int parser_sos(VTermStringFragment frag, void *user UNUSED) { printf("sos "); if(frag.initial) printf("["); printhex(frag.str, frag.len); if(frag.final) printf("]"); printf("\n"); return 1; } static VTermParserCallbacks parser_cbs = { parser_text, // text parser_control, // control parser_escape, // escape parser_csi, // csi parser_osc, // osc parser_dcs, // dcs parser_apc, // apc parser_pm, // pm parser_sos, // sos NULL // resize }; static VTermStateFallbacks fallbacks = { parser_control, // control parser_csi, // csi parser_osc, // osc parser_dcs, // dcs parser_apc, // dcs parser_pm, // pm parser_sos // sos }; /* These callbacks are shared by State and Screen */ static int want_movecursor = 0; static VTermPos state_pos; static int movecursor(VTermPos pos, VTermPos oldpos UNUSED, int visible UNUSED, void *user UNUSED) { state_pos = pos; if(want_movecursor) printf("movecursor %d,%d\n", pos.row, pos.col); return 1; } static int want_scrollrect = 0; static int scrollrect(VTermRect rect, int downward, int rightward, void *user UNUSED) { if(!want_scrollrect) return 0; printf("scrollrect %d..%d,%d..%d => %+d,%+d\n", rect.start_row, rect.end_row, rect.start_col, rect.end_col, downward, rightward); return 1; } static int want_moverect = 0; static int moverect(VTermRect dest, VTermRect src, void *user UNUSED) { if(!want_moverect) return 0; printf("moverect %d..%d,%d..%d -> %d..%d,%d..%d\n", src.start_row, src.end_row, src.start_col, src.end_col, dest.start_row, dest.end_row, dest.start_col, dest.end_col); return 1; } static int want_settermprop = 0; static int settermprop(VTermProp prop, VTermValue *val, void *user UNUSED) { VTermValueType type; if(!want_settermprop) return 1; type = vterm_get_prop_type(prop); switch(type) { case VTERM_VALUETYPE_BOOL: printf("settermprop %d %s\n", prop, val->boolean ? "true" : "false"); return 1; case VTERM_VALUETYPE_INT: printf("settermprop %d %d\n", prop, val->number); return 1; case VTERM_VALUETYPE_STRING: printf("settermprop %d %s\"%.*s\"%s\n", prop, val->string.initial ? "[" : "", val->string.len, val->string.str, val->string.final ? "]" : ""); return 1; case VTERM_VALUETYPE_COLOR: printf("settermprop %d ", prop); print_color(&val->color); printf("\n"); return 1; case VTERM_N_VALUETYPES: return 0; } return 0; } // These callbacks are for State static int want_state_putglyph = 0; static int state_putglyph(VTermGlyphInfo *info, VTermPos pos, void *user UNUSED) { int i; if(!want_state_putglyph) return 1; printf("putglyph "); for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) printf(i ? ",%x" : "%x", info->chars[i]); printf(" %d %d,%d", info->width, pos.row, pos.col); if(info->protected_cell) printf(" prot"); if(info->dwl) printf(" dwl"); if(info->dhl) printf(" dhl-%s", info->dhl == 1 ? "top" : info->dhl == 2 ? "bottom" : "?" ); printf("\n"); return 1; } static int want_state_erase = 0; static int state_erase(VTermRect rect, int selective, void *user UNUSED) { if(!want_state_erase) return 1; printf("erase %d..%d,%d..%d%s\n", rect.start_row, rect.end_row, rect.start_col, rect.end_col, selective ? " selective" : ""); return 1; } static struct { int bold; int underline; int italic; int blink; int reverse; int conceal; int strike; int font; int small; int baseline; VTermColor foreground; VTermColor background; } state_pen; static int state_setpenattr(VTermAttr attr, VTermValue *val, void *user UNUSED) { switch(attr) { case VTERM_ATTR_BOLD: state_pen.bold = val->boolean; break; case VTERM_ATTR_UNDERLINE: state_pen.underline = val->number; break; case VTERM_ATTR_ITALIC: state_pen.italic = val->boolean; break; case VTERM_ATTR_BLINK: state_pen.blink = val->boolean; break; case VTERM_ATTR_REVERSE: state_pen.reverse = val->boolean; break; case VTERM_ATTR_CONCEAL: state_pen.conceal = val->boolean; break; case VTERM_ATTR_STRIKE: state_pen.strike = val->boolean; break; case VTERM_ATTR_FONT: state_pen.font = val->number; break; case VTERM_ATTR_SMALL: state_pen.small = val->boolean; break; case VTERM_ATTR_BASELINE: state_pen.baseline = val->number; break; case VTERM_ATTR_FOREGROUND: state_pen.foreground = val->color; break; case VTERM_ATTR_BACKGROUND: state_pen.background = val->color; break; case VTERM_N_ATTRS: return 0; } return 1; } static int state_setlineinfo(int row UNUSED, const VTermLineInfo *newinfo UNUSED, const VTermLineInfo *oldinfo UNUSED, void *user UNUSED) { return 1; } static int want_state_scrollback = 0; static int state_sb_clear(void *user UNUSED) { if(!want_state_scrollback) return 1; printf("sb_clear\n"); return 0; } VTermStateCallbacks state_cbs = { state_putglyph, // putglyph movecursor, // movecursor scrollrect, // scrollrect moverect, // moverect state_erase, // erase NULL, // initpen state_setpenattr, // setpenattr settermprop, // settermprop NULL, // bell NULL, // resize state_setlineinfo, // setlineinfo state_sb_clear, // sb_clear }; static int selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user UNUSED) { printf("selection-set mask=%04X ", mask); if(frag.initial) printf("["); printhex(frag.str, frag.len); if(frag.final) printf("]"); printf("\n"); return 1; } static int selection_query(VTermSelectionMask mask, void *user UNUSED) { printf("selection-query mask=%04X\n", mask); return 1; } VTermSelectionCallbacks selection_cbs = { .set = selection_set, .query = selection_query, }; static int want_screen_damage = 0; static int want_screen_damage_cells = 0; static int screen_damage(VTermRect rect, void *user UNUSED) { if(!want_screen_damage) return 1; printf("damage %d..%d,%d..%d", rect.start_row, rect.end_row, rect.start_col, rect.end_col); if(want_screen_damage_cells) { int equals = FALSE; int row; int col; for(row = rect.start_row; row < rect.end_row; row++) { int eol = rect.end_col; while(eol > rect.start_col) { VTermScreenCell cell; VTermPos pos; pos.row = row; pos.col = eol-1; vterm_screen_get_cell(screen, pos, &cell); if(cell.chars[0]) break; eol--; } if(eol == rect.start_col) break; if(!equals) printf(" ="), equals = TRUE; printf(" %d<", row); for(col = rect.start_col; col < eol; col++) { VTermScreenCell cell; VTermPos pos; pos.row = row; pos.col = col; vterm_screen_get_cell(screen, pos, &cell); printf(col == rect.start_col ? "%02X" : " %02X", cell.chars[0]); } printf(">"); } } printf("\n"); return 1; } static int want_screen_scrollback = 0; static int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user UNUSED) { int eol; int c; if(!want_screen_scrollback) return 1; eol = cols; while(eol && !cells[eol-1].chars[0]) eol--; printf("sb_pushline %d =", cols); for(c = 0; c < eol; c++) printf(" %02X", cells[c].chars[0]); printf("\n"); return 1; } static int screen_sb_popline(int cols, VTermScreenCell *cells, void *user UNUSED) { int col; if(!want_screen_scrollback) return 0; // All lines of scrollback contain "ABCDE" for(col = 0; col < cols; col++) { if(col < 5) cells[col].chars[0] = 'A' + col; else cells[col].chars[0] = 0; cells[col].width = 1; } printf("sb_popline %d\n", cols); return 1; } static int screen_sb_clear(void *user UNUSED) { if(!want_screen_scrollback) return 1; printf("sb_clear\n"); return 0; } VTermScreenCallbacks screen_cbs = { screen_damage, // damage moverect, // moverect movecursor, // movecursor settermprop, // settermprop NULL, // bell NULL, // resize screen_sb_pushline, // sb_pushline screen_sb_popline, // sb_popline screen_sb_clear, // sb_clear }; int main(int argc UNUSED, char **argv UNUSED) { char line[1024] = {0}; int flag; int err; setvbuf(stdout, NULL, _IONBF, 0); while(fgets(line, sizeof line, stdin)) { char *nl; size_t outlen; err = 0; if((nl = strchr(line, '\n'))) *nl = '\0'; if(streq(line, "INIT")) { if(!vt) vt = vterm_new(25, 80); // Somehow this makes tests fail // vterm_output_set_callback(vt, term_output, NULL); } else if(streq(line, "WANTPARSER")) { assert(vt); vterm_parser_set_callbacks(vt, &parser_cbs, NULL); } else if(strstartswith(line, "WANTSTATE") && (line[9] == '\0' || line[9] == ' ')) { int i = 9; int sense = 1; assert(vt); if(!state) { state = vterm_obtain_state(vt); vterm_state_set_callbacks(state, &state_cbs, NULL); vterm_state_set_selection_callbacks(state, &selection_cbs, NULL, NULL, 1024); vterm_state_set_bold_highbright(state, 1); vterm_state_reset(state, 1); } while(line[i] == ' ') i++; for( ; line[i]; i++) switch(line[i]) { case '+': sense = 1; break; case '-': sense = 0; break; case 'g': want_state_putglyph = sense; break; case 's': want_scrollrect = sense; break; case 'm': want_moverect = sense; break; case 'e': want_state_erase = sense; break; case 'p': want_settermprop = sense; break; case 'f': vterm_state_set_unrecognised_fallbacks(state, sense ? &fallbacks : NULL, NULL); break; case 'b': want_state_scrollback = sense; break; default: fprintf(stderr, "Unrecognised WANTSTATE flag '%c'\n", line[i]); } } else if(strstartswith(line, "WANTSCREEN") && (line[10] == '\0' || line[10] == ' ')) { int i = 10; int sense = 1; assert(vt); if(!screen) screen = vterm_obtain_screen(vt); vterm_screen_set_callbacks(screen, &screen_cbs, NULL); while(line[i] == ' ') i++; for( ; line[i]; i++) switch(line[i]) { case '-': sense = 0; break; case 'a': vterm_screen_enable_altscreen(screen, 1); break; case 'd': want_screen_damage = sense; break; case 'D': want_screen_damage = sense; want_screen_damage_cells = sense; break; case 'm': want_moverect = sense; break; case 'c': want_movecursor = sense; break; case 'p': want_settermprop = 1; break; case 'b': want_screen_scrollback = sense; break; case 'r': vterm_screen_enable_reflow(screen, sense); break; default: fprintf(stderr, "Unrecognised WANTSCREEN flag '%c'\n", line[i]); } } else if(sscanf(line, "UTF8 %d", &flag)) { vterm_set_utf8(vt, flag); } else if(streq(line, "RESET")) { if(state) { vterm_state_reset(state, 1); vterm_state_get_cursorpos(state, &state_pos); } if(screen) { vterm_screen_reset(screen, 1); } } else if(strstartswith(line, "RESIZE ")) { int rows, cols; char *linep = line + 7; while(linep[0] == ' ') linep++; sscanf(linep, "%d, %d", &rows, &cols); vterm_set_size(vt, rows, cols); } else if(strstartswith(line, "PUSH ")) { char *bytes = line + 5; size_t len = inplace_hex2bytes(bytes); assert(len); size_t written = vterm_input_write(vt, bytes, len); if(written < len) fprintf(stderr, "! short write\n"); } else if(streq(line, "WANTENCODING")) { // This isn't really external API but it's hard to get this out any // other way encoding.enc = vterm_lookup_encoding(ENC_UTF8, 'u'); if(encoding.enc->init) (*encoding.enc->init)(encoding.enc, encoding.data); } else if(strstartswith(line, "ENCIN ")) { char *bytes = line + 6; size_t len = inplace_hex2bytes(bytes); assert(len); uint32_t cp[1024]; int cpi = 0; size_t pos = 0; (*encoding.enc->decode)(encoding.enc, encoding.data, cp, &cpi, len, bytes, &pos, len); if(cpi > 0) { int i; printf("encout "); for(i = 0; i < cpi; i++) { printf(i ? ",%x" : "%x", cp[i]); } printf("\n"); } } else if(strstartswith(line, "INCHAR ")) { char *linep = line + 7; unsigned int c = 0; VTermModifier mod; while(linep[0] == ' ') linep++; mod = strpe_modifiers(&linep); sscanf(linep, " %x", &c); vterm_keyboard_unichar(vt, c, mod); } else if(strstartswith(line, "INKEY ")) { VTermModifier mod; VTermKey key; char *linep = line + 6; while(linep[0] == ' ') linep++; mod = strpe_modifiers(&linep); while(linep[0] == ' ') linep++; key = strp_key(linep); vterm_keyboard_key(vt, key, mod); } else if(strstartswith(line, "PASTE ")) { char *linep = line + 6; if(streq(linep, "START")) vterm_keyboard_start_paste(vt); else if(streq(linep, "END")) vterm_keyboard_end_paste(vt); else goto abort_line; } else if(strstartswith(line, "FOCUS ")) { assert(state); char *linep = line + 6; if(streq(linep, "IN")) vterm_state_focus_in(state); else if(streq(linep, "OUT")) vterm_state_focus_out(state); else goto abort_line; } else if(strstartswith(line, "MOUSEMOVE ")) { char *linep = line + 10; int row, col, len; VTermModifier mod; while(linep[0] == ' ') linep++; sscanf(linep, "%d,%d%n", &row, &col, &len); linep += len; while(linep[0] == ' ') linep++; mod = strpe_modifiers(&linep); vterm_mouse_move(vt, row, col, mod); } else if(strstartswith(line, "MOUSEBTN ")) { char *linep = line + 9; char press; int button, len; VTermModifier mod; while(linep[0] == ' ') linep++; sscanf(linep, "%c %d%n", &press, &button, &len); linep += len; while(linep[0] == ' ') linep++; mod = strpe_modifiers(&linep); vterm_mouse_button(vt, button, (press == 'd' || press == 'D'), mod); } else if(strstartswith(line, "SELECTION ")) { char *linep = line + 10; unsigned int mask; int len; VTermStringFragment frag = { 0 }; sscanf(linep, "%x%n", &mask, &len); linep += len; while(linep[0] == ' ') linep++; if(linep[0] == '[') { frag.initial = TRUE; linep++; while(linep[0] == ' ') linep++; } frag.len = inplace_hex2bytes(linep); frag.str = linep; assert(frag.len); linep += frag.len * 2; while(linep[0] == ' ') linep++; if(linep[0] == ']') { frag.final = TRUE; } vterm_state_send_selection(state, mask, frag); } else if(strstartswith(line, "DAMAGEMERGE ")) { assert(screen); char *linep = line + 12; while(linep[0] == ' ') linep++; if(streq(linep, "CELL")) vterm_screen_set_damage_merge(screen, VTERM_DAMAGE_CELL); else if(streq(linep, "ROW")) vterm_screen_set_damage_merge(screen, VTERM_DAMAGE_ROW); else if(streq(linep, "SCREEN")) vterm_screen_set_damage_merge(screen, VTERM_DAMAGE_SCREEN); else if(streq(linep, "SCROLL")) vterm_screen_set_damage_merge(screen, VTERM_DAMAGE_SCROLL); } else if(strstartswith(line, "DAMAGEFLUSH")) { assert(screen); vterm_screen_flush_damage(screen); } else if(line[0] == '?') { if(streq(line, "?cursor")) { assert(state); VTermPos pos; vterm_state_get_cursorpos(state, &pos); if(pos.row != state_pos.row) printf("! row mismatch: state=%d,%d event=%d,%d\n", pos.row, pos.col, state_pos.row, state_pos.col); else if(pos.col != state_pos.col) printf("! col mismatch: state=%d,%d event=%d,%d\n", pos.row, pos.col, state_pos.row, state_pos.col); else printf("%d,%d\n", state_pos.row, state_pos.col); } else if(strstartswith(line, "?pen ")) { assert(state); VTermValue val; char *linep = line + 5; while(linep[0] == ' ') linep++; #define BOOLSTR(v) ((v) ? "on" : "off") if(streq(linep, "bold")) { vterm_state_get_penattr(state, VTERM_ATTR_BOLD, &val); if(val.boolean != state_pen.bold) printf("! pen bold mismatch; state=%s, event=%s\n", BOOLSTR(val.boolean), BOOLSTR(state_pen.bold)); else printf("%s\n", BOOLSTR(state_pen.bold)); } else if(streq(linep, "underline")) { vterm_state_get_penattr(state, VTERM_ATTR_UNDERLINE, &val); if(val.boolean != state_pen.underline) printf("! pen underline mismatch; state=%d, event=%d\n", val.boolean, state_pen.underline); else printf("%d\n", state_pen.underline); } else if(streq(linep, "italic")) { vterm_state_get_penattr(state, VTERM_ATTR_ITALIC, &val); if(val.boolean != state_pen.italic) printf("! pen italic mismatch; state=%s, event=%s\n", BOOLSTR(val.boolean), BOOLSTR(state_pen.italic)); else printf("%s\n", BOOLSTR(state_pen.italic)); } else if(streq(linep, "blink")) { vterm_state_get_penattr(state, VTERM_ATTR_BLINK, &val); if(val.boolean != state_pen.blink) printf("! pen blink mismatch; state=%s, event=%s\n", BOOLSTR(val.boolean), BOOLSTR(state_pen.blink)); else printf("%s\n", BOOLSTR(state_pen.blink)); } else if(streq(linep, "reverse")) { vterm_state_get_penattr(state, VTERM_ATTR_REVERSE, &val); if(val.boolean != state_pen.reverse) printf("! pen reverse mismatch; state=%s, event=%s\n", BOOLSTR(val.boolean), BOOLSTR(state_pen.reverse)); else printf("%s\n", BOOLSTR(state_pen.reverse)); } else if(streq(linep, "font")) { vterm_state_get_penattr(state, VTERM_ATTR_FONT, &val); if(val.boolean != state_pen.font) printf("! pen font mismatch; state=%d, event=%d\n", val.boolean, state_pen.font); else printf("%d\n", state_pen.font); } else if(streq(linep, "small")) { vterm_state_get_penattr(state, VTERM_ATTR_SMALL, &val); if(val.boolean != state_pen.small) printf("! pen small mismatch; state=%s, event=%s\n", BOOLSTR(val.boolean), BOOLSTR(state_pen.small)); else printf("%s\n", BOOLSTR(state_pen.small)); } else if(streq(linep, "baseline")) { vterm_state_get_penattr(state, VTERM_ATTR_BASELINE, &val); if(val.number != state_pen.baseline) printf("! pen baseline mismatch: state=%d, event=%d\n", val.number, state_pen.baseline); else printf("%s\n", state_pen.baseline == VTERM_BASELINE_RAISE ? "raise" : state_pen.baseline == VTERM_BASELINE_LOWER ? "lower" : "normal"); } else if(streq(linep, "foreground")) { print_color(&state_pen.foreground); printf("\n"); } else if(streq(linep, "background")) { print_color(&state_pen.background); printf("\n"); } else printf("?\n"); } else if(strstartswith(line, "?lineinfo ")) { assert(state); char *linep = line + 10; int row; const VTermLineInfo *info; while(linep[0] == ' ') linep++; if(sscanf(linep, "%d", &row) < 1) { printf("! lineinfo unrecognised input\n"); goto abort_line; } info = vterm_state_get_lineinfo(state, row); if(info->doublewidth) printf("dwl "); if(info->doubleheight) printf("dhl "); if(info->continuation) printf("cont "); printf("\n"); } else if(strstartswith(line, "?screen_chars ")) { assert(screen); char *linep = line + 13; VTermRect rect; size_t len; while(linep[0] == ' ') linep++; if(sscanf(linep, "%d,%d,%d,%d", &rect.start_row, &rect.start_col, &rect.end_row, &rect.end_col) == 4) ; // fine else if(sscanf(linep, "%d", &rect.start_row) == 1) { rect.end_row = rect.start_row + 1; rect.start_col = 0; vterm_get_size(vt, NULL, &rect.end_col); } else { printf("! screen_chars unrecognised input\n"); goto abort_line; } len = vterm_screen_get_chars(screen, NULL, 0, rect); if(len == (size_t)-1) printf("! screen_chars error\n"); else if(len == 0) printf("\n"); else { uint32_t *chars = malloc(sizeof(uint32_t) * len); size_t i; vterm_screen_get_chars(screen, chars, len, rect); for(i = 0; i < len; i++) { printf("0x%02x%s", chars[i], i < len-1 ? "," : "\n"); } free(chars); } } else if(strstartswith(line, "?screen_text ")) { assert(screen); char *linep = line + 12; VTermRect rect; size_t len; while(linep[0] == ' ') linep++; if(sscanf(linep, "%d,%d,%d,%d", &rect.start_row, &rect.start_col, &rect.end_row, &rect.end_col) < 4) { printf("! screen_text unrecognised input\n"); goto abort_line; } len = vterm_screen_get_text(screen, NULL, 0, rect); if(len == (size_t)-1) printf("! screen_text error\n"); else if(len == 0) printf("\n"); else { // Put an overwrite guard at both ends of the buffer unsigned char *buffer = malloc(len + 4); unsigned char *text = buffer + 2; text[-2] = 0x55; text[-1] = 0xAA; text[len] = 0x55; text[len+1] = 0xAA; vterm_screen_get_text(screen, (char *)text, len, rect); if(text[-2] != 0x55 || text[-1] != 0xAA) printf("! screen_get_text buffer overrun left [%02x,%02x]\n", text[-2], text[-1]); else if(text[len] != 0x55 || text[len+1] != 0xAA) printf("! screen_get_text buffer overrun right [%02x,%02x]\n", text[len], text[len+1]); else { size_t i; for(i = 0; i < len; i++) { printf("0x%02x%s", text[i], i < len-1 ? "," : "\n"); } } free(buffer); } } else if(strstartswith(line, "?screen_cell ")) { assert(screen); char *linep = line + 12; int i; VTermPos pos; VTermScreenCell cell; while(linep[0] == ' ') linep++; if(sscanf(linep, "%d,%d\n", &pos.row, &pos.col) < 2) { printf("! screen_cell unrecognised input\n"); goto abort_line; } if(!vterm_screen_get_cell(screen, pos, &cell)) goto abort_line; printf("{"); for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell.chars[i]; i++) { printf("%s0x%x", i ? "," : "", cell.chars[i]); } printf("} width=%d attrs={", cell.width); if(cell.attrs.bold) printf("B"); if(cell.attrs.underline) printf("U%d", cell.attrs.underline); if(cell.attrs.italic) printf("I"); if(cell.attrs.blink) printf("K"); if(cell.attrs.reverse) printf("R"); if(cell.attrs.font) printf("F%d", cell.attrs.font); if(cell.attrs.small) printf("S"); if(cell.attrs.baseline) printf( cell.attrs.baseline == VTERM_BASELINE_RAISE ? "^" : "_"); printf("} "); if(cell.attrs.dwl) printf("dwl "); if(cell.attrs.dhl) printf("dhl-%s ", cell.attrs.dhl == 2 ? "bottom" : "top"); printf("fg="); vterm_screen_convert_color_to_rgb(screen, &cell.fg); print_color(&cell.fg); printf(" bg="); vterm_screen_convert_color_to_rgb(screen, &cell.bg); print_color(&cell.bg); printf("\n"); } else if(strstartswith(line, "?screen_eol ")) { assert(screen); VTermPos pos; char *linep = line + 12; while(linep[0] == ' ') linep++; if(sscanf(linep, "%d,%d\n", &pos.row, &pos.col) < 2) { printf("! screen_eol unrecognised input\n"); goto abort_line; } printf("%d\n", vterm_screen_is_eol(screen, pos)); } else if(strstartswith(line, "?screen_attrs_extent ")) { assert(screen); VTermPos pos; VTermRect rect; char *linep = line + 21; while(linep[0] == ' ') linep++; if(sscanf(linep, "%d,%d\n", &pos.row, &pos.col) < 2) { printf("! screen_attrs_extent unrecognised input\n"); goto abort_line; } rect.start_col = 0; rect.end_col = -1; if(!vterm_screen_get_attrs_extent(screen, &rect, pos, ~0)) { printf("! screen_attrs_extent failed\n"); goto abort_line; } printf("%d,%d-%d,%d\n", rect.start_row, rect.start_col, rect.end_row, rect.end_col); } else printf("?\n"); memset(line, 0, sizeof line); continue; } else abort_line: err = 1; outlen = vterm_output_get_buffer_current(vt); if(outlen > 0) { char outbuff[1024]; vterm_output_read(vt, outbuff, outlen); term_output(outbuff, outlen, NULL); } printf(err ? "?\n" : "DONE\n"); } vterm_free(vt); return 0; }