int any_DSR(MENU_ARGS, const char *text, void (*explain) (char *report)) { char *report; unsigned pmode = (unsigned) ((*text == '?') ? 1 : 0); vt_move(1, 1); printf("Testing DSR: %s\n", the_title); set_tty_raw(TRUE); set_tty_echo(FALSE); do_csi("%s", text); report = get_reply(); vt_move(3, 10); chrprint(report); if ((report = skip_csi(report)) != 0 && strlen(report) > (1 + pmode) && (!pmode || (*report++ == '?'))) { if (explain != 0) (*explain) (report); else show_result(SHOW_SUCCESS); } else { show_result(SHOW_FAILURE); } restore_ttymodes(); vt_move(max_lines - 1, 1); return MENU_HOLD; }
/* * VT200 and up * * Test to ensure that 'ech' (erase character) is honored, with no parameter, * explicit parameter, and longer than the screen width (to ensure that the * terminal doesn't try to wrap-around the erasure). */ static int tst_ECH(MENU_ARGS) { int i; int last = max_lines - 4; decaln(); for (i = 1; i <= max_lines; i++) { cup(i, min_cols - i - 2); do_csi("X"); /* make sure default-parameter works */ cup(i, min_cols - i - 1); printf("*"); ech(min_cols); printf("*"); /* this should be adjacent, in the upper-right corner */ } vt_move(last, 1); vt_clear(0); vt_move(last, min_cols - (last + 10)); println("diagonal: ^^ (clear)"); println("ECH test: there should be E's with a gap before diagonal of **'s"); println("The lower-right diagonal region should be cleared. Nothing else."); return MENU_HOLD; }
/* Test User-Preferred Supplemental Set - VT320 */ static int tst_DECRQUPSS(MENU_ARGS) { char *report; const char *show; __(vt_move(1, 1), println("Testing DECRQUPSS/DECAUPSS Window Report")); set_tty_raw(TRUE); set_tty_echo(FALSE); do_csi("&u"); report = get_reply(); vt_move(3, 10); chrprint(report); if ((report = skip_dcs(report)) != 0 && strip_terminator(report)) { if (!strcmp(report, "0!u%5")) show = "DEC Supplemental Graphic"; else if (!strcmp(report, "1!uA")) show = "ISO Latin-1 supplemental"; else show = "unknown"; } else { show = SHOW_FAILURE; } show_result("%s", show); restore_ttymodes(); vt_move(max_lines - 1, 1); return MENU_HOLD; }
/* Request Terminal State Report */ static int tst_DECRQTSR(MENU_ARGS) { char *report; const char *show; vt_move(1, 1); println("Testing Terminal State Reports (DECRQTSR/DECTSR)"); set_tty_raw(TRUE); set_tty_echo(FALSE); do_csi("1$u"); report = get_reply(); vt_move(3, 10); chrprint(report); if ((report = skip_dcs(report)) != 0 && strip_terminator(report) && !strncmp(report, "1$s", (size_t) 3)) { show = SHOW_SUCCESS; } else { show = SHOW_FAILURE; } show_result("%s", show); restore_ttymodes(); vt_move(max_lines - 1, 1); return MENU_HOLD; }
static void check_rc(int row, int col) { char *report; char *params; char expected[80]; sprintf(expected, "%d;%dR", row, col); set_tty_raw(TRUE); set_tty_echo(FALSE); do_csi("6n"); report = get_reply(); restore_ttymodes(); vt_move(row, 1); el(2); if ((params = skip_csi(report)) == 0 || strcmp(params, expected) != 0) { printf("cursor save/restore %s, got \"%s\", expected \"%s\"", SHOW_FAILURE, params, expected); } else { printf("cursor save/restore %s", SHOW_SUCCESS); } }
/* Test Window Report - VT340, VT420 */ static int tst_DECRQDE(MENU_ARGS) { char *report; char chr; int Ph, Pw, Pml, Pmt, Pmp; vt_move(1, 1); println("Testing DECRQDE/DECRPDE Window Report"); set_tty_raw(TRUE); set_tty_echo(FALSE); do_csi("\"v"); report = get_reply(); vt_move(3, 10); chrprint(report); if ((report = skip_csi(report)) != 0 && sscanf(report, "%d;%d;%d;%d;%d\"%c", &Ph, &Pw, &Pml, &Pmt, &Pmp, &chr) == 6 && chr == 'w') { vt_move(5, 10); show_result("lines:%d, cols:%d, left col:%d, top line:%d, page %d", Ph, Pw, Pml, Pmt, Pmp); } else { show_result(SHOW_FAILURE); } restore_ttymodes(); vt_move(max_lines - 1, 1); return MENU_HOLD; }
int set_level(int request) { if (cur_level < 0) find_levels(); if (LOG_ENABLED) fprintf(log_fp, "set_level(%d)\n", request); if (request > max_level) { printf("Sorry, this terminal supports only VT%d\n", terminal_id()); return FALSE; } if (request != cur_level) { if (request == 0) { rm("?2"); /* Reset ANSI (VT100) mode, Set VT52 mode */ input_8bits = FALSE; output_8bits = FALSE; } else { if (cur_level == 0) { esc("<"); /* Enter ANSI mode (VT100 mode) */ } if (request == 1) { input_8bits = FALSE; output_8bits = FALSE; } if (request > 1) do_csi("6%d;%d\"p", request, !input_8bits); else do_csi("61\"p"); } padding(5); /* FIXME: may not be needed */ cur_level = request; } if (LOG_ENABLED) fprintf(log_fp, "...set_level(%d) in=%d, out=%d\n", cur_level, input_8bits ? 8 : 7, output_8bits ? 8 : 7); return TRUE; }
int any_decrqpsr(MENU_ARGS, int Ps) { char *report; vt_move(1, 1); printf("Testing DECRQPSR: %s\n", the_title); set_tty_raw(TRUE); set_tty_echo(FALSE); do_csi("%d$w", Ps); report = get_reply(); vt_move(3, 10); chrprint(report); if ((report = skip_dcs(report)) != 0) { if (strip_terminator(report) && *report == Ps + '0' && !strncmp(report + 1, "$u", (size_t) 2)) { show_result("%s (valid request)", SHOW_SUCCESS); switch (Ps) { case 1: show_DECCIR(report); break; case 2: show_DECTABSR(report); break; } } else { show_result(SHOW_FAILURE); } } else { show_result(SHOW_FAILURE); } restore_ttymodes(); vt_move(max_lines - 1, 1); return MENU_HOLD; }
int tst_movements(MENU_ARGS) { /* Test of: CUF (Cursor Forward) CUB (Cursor Backward) CUD (Cursor Down) IND (Index) NEL (Next Line) CUU (Cursor Up) RI (Reverse Index) CUP (Cursor Position) HVP (Horizontal and Vertical Position) ED (Erase in Display) EL (Erase in Line) DECALN (Screen Alignment Display) DECAWM (Autowrap) <CR> <BS> Cursor control characters inside CSI sequences */ int i, row, col, pass, width, hlfxtra; const char *ctext = "This is a correct sentence"; set_tty_crmod(TRUE); /* want to disable tab/space conversion */ for (pass = 0; pass <= 1; pass++) { int inner_l, inner_r; if (pass == 0) { deccolm(FALSE); width = min_cols; } else { deccolm(TRUE); width = max_cols; } /* Compute left/right columns for a 60-column box centered in 'width' */ inner_l = (width - 60) / 2; inner_r = 61 + inner_l; hlfxtra = (width - 80) / 2; if (LOG_ENABLED) fprintf(log_fp, "tst_movements box(%d cols)\n", pass ? max_cols : min_cols); decaln(); cup(9, inner_l); ed(1); cup(18, 60 + hlfxtra); ed(0); el(1); cup(9, inner_r); el(0); /* 132: 36..97 */ /* 80: 10..71 */ for (row = 10; row <= 16; row++) { cup(row, inner_l); el(1); cup(row, inner_r); el(0); } cup(17, 30); el(2); for (col = 1; col <= width; col++) { hvp(max_lines, col); printf("*"); hvp(1, col); printf("*"); } cup(2, 2); for (row = 2; row <= max_lines - 1; row++) { printf("+"); cub(1); ind(); } cup(max_lines - 1, width - 1); for (row = max_lines - 1; row >= 2; row--) { printf("+"); cub(1); ri(); } cup(2, 1); for (row = 2; row <= max_lines - 1; row++) { printf("*"); cup(row, width); printf("*"); cub(10); if (row < 10) nel(); else printf("\n"); } cup(2, 10); cub(42 + hlfxtra); cuf(2); for (col = 3; col <= width - 2; col++) { printf("+"); cuf(0); cub(2); cuf(1); } cup(max_lines - 1, inner_r - 1); cuf(42 + hlfxtra); cub(2); for (col = width - 2; col >= 3; col--) { printf("+"); cub(1); cuf(1); cub(0); printf("%c", 8); } cup(1, 1); cuu(10); cuu(1); cuu(0); cup(max_lines, width); cud(10); cud(1); cud(0); cup(10, 2 + inner_l); for (row = 10; row <= 15; row++) { for (col = 2 + inner_l; col <= inner_r - 2; col++) printf(" "); cud(1); cub(58); } cuu(5); cuf(1); printf("The screen should be cleared, and have an unbroken bor-"); cup(12, inner_l + 3); printf("der of *'s and +'s around the edge, and exactly in the"); cup(13, inner_l + 3); printf("middle there should be a frame of E's around this text"); cup(14, inner_l + 3); printf("with one (1) free position around it. "); holdit(); } deccolm(FALSE); /* DECAWM demo */ for (pass = 0; pass <= 1; pass++) { static char on_left[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; static char on_right[] = "abcdefghijklmnopqrstuvwxyz"; int height = sizeof(on_left) - 1; int region = max_lines - 6; if (LOG_ENABLED) fprintf(log_fp, "tst_movements wrap(%d cols)\n", pass ? max_cols : min_cols); /* note: DECCOLM clears the screen */ if (pass == 0) { deccolm(FALSE); width = min_cols; } else { deccolm(TRUE); width = max_cols; } println("Test of autowrap, mixing control and print characters."); println("The left/right margins should have letters in order:"); decstbm(3, region + 3); decom(TRUE); /* this also homes the cursor */ for (i = 0; i < height; ++i) { switch (i % 4) { case 0: /* draw characters as-is, for reference */ __(cup(region + 1, 1), printf("%c", on_left[i])); __(cup(region + 1, width), printf("%c", on_right[i])); printf("\n"); break; case 1: /* simple wrapping */ __(cup(region, width), printf("%c%c", on_right[i - 1], on_left[i])); /* backspace at right margin */ __(cup(region + 1, width), printf("%c%c %c", on_left[i], BS, on_right[i])); printf("\n"); break; case 2: /* tab to right margin */ __(cup(region + 1, width), printf("%c%c%c%c%c%c", on_left[i], BS, BS, TAB, TAB, on_right[i])); __(cup(region + 1, 2), printf("%c%c\n", BS, on_left[i])); break; default: /* newline at right margin */ __(cup(region + 1, width), printf("\n")); __(cup(region, 1), printf("%c", on_left[i])); __(cup(region, width), printf("%c", on_right[i])); break; } } decom(FALSE); decstbm(0, 0); cup(max_lines - 2, 1); holdit(); } deccolm(FALSE); /* 80 cols */ if (LOG_ENABLED) fprintf(log_fp, "tst_movements cursor-controls in ESC sequences\n"); vt_clear(2); vt_move(1, 1); println("Test of cursor-control characters inside ESC sequences."); println("Below should be four identical lines:"); println(""); println("A B C D E F G H I"); for (i = 1; i < 10; i++) { printf("%c", '@' + i); do_csi("2%cC", BS); /* Two forward, one backspace */ } println(""); /* Now put CR in CUF sequence. */ printf("A "); for (i = 2; i < 10; i++) printf("%s%c%dC%c", csi_output(), CR, 2 * i - 2, '@' + i); println(""); /* Now put VT in CUU sequence. */ rm("20"); for (i = 1; i < 10; i++) { printf("%c ", '@' + i); do_csi("1\013A"); } println(""); println(""); holdit(); if (LOG_ENABLED) fprintf(log_fp, "tst_movements leading zeros in ESC sequences\n"); vt_clear(2); vt_move(1, 1); println("Test of leading zeros in ESC sequences."); printf("Two lines below you should see the sentence \"%s\".", ctext); for (col = 1; *ctext; col++) printf("%s00000000004;00000000%dH%c", csi_output(), col, *ctext++); cup(20, 1); restore_ttymodes(); return MENU_HOLD; }
static void decscusr(int parm) { do_csi("%d q", parm); }
size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len) { size_t pos = 0; const char *string_start; switch(vt->parser.state) { case NORMAL: case CSI_LEADER: case CSI_ARGS: case CSI_INTERMED: case ESC: string_start = NULL; break; case STRING: case ESC_IN_STRING: string_start = bytes; break; } #define ENTER_STRING_STATE(st) do { vt->parser.state = STRING; string_start = bytes + pos + 1; } while(0) #define ENTER_STATE(st) do { vt->parser.state = st; string_start = NULL; } while(0) #define ENTER_NORMAL_STATE() ENTER_STATE(NORMAL) for( ; pos < len; pos++) { unsigned char c = bytes[pos]; if(c == 0x00 || c == 0x7f) { // NUL, DEL if(vt->parser.state >= STRING) { more_string(vt, string_start, bytes + pos - string_start); string_start = bytes + pos + 1; } continue; } if(c == 0x18 || c == 0x1a) { // CAN, SUB ENTER_NORMAL_STATE(); continue; } else if(c == 0x1b) { // ESC vt->parser.intermedlen = 0; if(vt->parser.state == STRING) vt->parser.state = ESC_IN_STRING; else ENTER_STATE(ESC); continue; } else if(c == 0x07 && // BEL, can stand for ST in OSC or DCS state vt->parser.state == STRING) { // fallthrough } else if(c < 0x20) { // other C0 if(vt->parser.state >= STRING) more_string(vt, string_start, bytes + pos - string_start); do_control(vt, c); if(vt->parser.state >= STRING) string_start = bytes + pos + 1; continue; } // else fallthrough switch(vt->parser.state) { case ESC_IN_STRING: if(c == 0x5c) { // ST vt->parser.state = STRING; done_string(vt, string_start, bytes + pos - string_start - 1); ENTER_NORMAL_STATE(); break; } vt->parser.state = ESC; // else fallthrough case ESC: switch(c) { case 0x50: // DCS start_string(vt, VTERM_PARSER_DCS); ENTER_STRING_STATE(); break; case 0x5b: // CSI vt->parser.csi_leaderlen = 0; ENTER_STATE(CSI_LEADER); break; case 0x5d: // OSC start_string(vt, VTERM_PARSER_OSC); ENTER_STRING_STATE(); break; default: if(is_intermed(c)) { if(vt->parser.intermedlen < INTERMED_MAX-1) vt->parser.intermed[vt->parser.intermedlen++] = c; } else if(!vt->parser.intermedlen && c >= 0x40 && c < 0x60) { do_control(vt, c + 0x40); ENTER_NORMAL_STATE(); } else if(c >= 0x30 && c < 0x7f) { do_escape(vt, c); ENTER_NORMAL_STATE(); } else { DEBUG_LOG("TODO: Unhandled byte %02x in Escape\n", c); } } break; case CSI_LEADER: /* Extract leader bytes 0x3c to 0x3f */ if(c >= 0x3c && c <= 0x3f) { if(vt->parser.csi_leaderlen < CSI_LEADER_MAX-1) vt->parser.csi_leader[vt->parser.csi_leaderlen++] = c; break; } /* else fallthrough */ vt->parser.csi_leader[vt->parser.csi_leaderlen] = 0; vt->parser.csi_argi = 0; vt->parser.csi_args[0] = CSI_ARG_MISSING; vt->parser.state = CSI_ARGS; /* fallthrough */ case CSI_ARGS: /* Numerical value of argument */ if(c >= '0' && c <= '9') { if(vt->parser.csi_args[vt->parser.csi_argi] == CSI_ARG_MISSING) vt->parser.csi_args[vt->parser.csi_argi] = 0; vt->parser.csi_args[vt->parser.csi_argi] *= 10; vt->parser.csi_args[vt->parser.csi_argi] += c - '0'; break; } if(c == ':') { vt->parser.csi_args[vt->parser.csi_argi] |= CSI_ARG_FLAG_MORE; c = ';'; } if(c == ';') { vt->parser.csi_argi++; vt->parser.csi_args[vt->parser.csi_argi] = CSI_ARG_MISSING; break; } /* else fallthrough */ vt->parser.csi_argi++; vt->parser.intermedlen = 0; vt->parser.state = CSI_INTERMED; case CSI_INTERMED: if(is_intermed(c)) { if(vt->parser.intermedlen < INTERMED_MAX-1) vt->parser.intermed[vt->parser.intermedlen++] = c; break; } else if(c == 0x1b) { /* ESC in CSI cancels */ } else if(c >= 0x40 && c <= 0x7e) { vt->parser.intermed[vt->parser.intermedlen] = 0; do_csi(vt, c); } /* else was invalid CSI */ ENTER_NORMAL_STATE(); break; case STRING: if(c == 0x07 || (c == 0x9c && !vt->mode.utf8)) { done_string(vt, string_start, bytes + pos - string_start); ENTER_NORMAL_STATE(); } break; case NORMAL: if(c >= 0x80 && c < 0xa0 && !vt->mode.utf8) { switch(c) { case 0x90: // DCS start_string(vt, VTERM_PARSER_DCS); ENTER_STRING_STATE(); break; case 0x9b: // CSI ENTER_STATE(CSI_LEADER); break; case 0x9d: // OSC start_string(vt, VTERM_PARSER_OSC); ENTER_STRING_STATE(); break; default: do_control(vt, c); break; } } else { size_t eaten = 0; if(vt->parser.callbacks && vt->parser.callbacks->text) eaten = (*vt->parser.callbacks->text)(bytes + pos, len - pos, vt->parser.cbdata); if(!eaten) { DEBUG_LOG("libvterm: Text callback did not consume any input\n"); /* force it to make progress */ eaten = 1; } pos += (eaten - 1); // we'll ++ it again in a moment } break; } } return len; }