/* $Id: vt320.c,v 1.13 2007/01/07 17:03:32 tom Exp $ */

/*
 * Reference:  VT330/VT340 Programmer Reference Manual (EK-VT3XX-TP-001)
 */
#include <vttest.h>
#include <ttymodes.h>
#include <esc.h>

static void
show_Locator_Status(char *report)
{
  int pos = 0;
  int code = scanto(report, &pos, 'n');
  char *show;

  switch (code) {
  case 53:
    show = "No locator";
    break;
  case 50:
    show = "Locator ready";
    break;
  case 58:
    show = "Locator busy";
    break;
  default:
    show = SHOW_FAILURE;
  }
  show_result(show);
}

/*
 * Though some people refer to the locator controls as "vt220", it appears in
 * later terminals (documented in the vt320 manual, but introduced as in UWS).
 */
int
tst_DSR_locator(MENU_ARGS)
{
  return any_DSR(PASS_ARGS, "?53n", show_Locator_Status);
}

static void
show_ExtendedCursorPosition(char *report)
{
  int pos = 0;
  int Pl = scan_any(report, &pos, 'R');
  int Pc = scan_any(report, &pos, 'R');
  int Pp = scan_any(report, &pos, 'R');

  if (Pl != 0 && Pc != 0) {
    if (Pp != 0)
      show_result("Line %d, Column %d, Page %d", Pl, Pc, Pp);
    else
      show_result("Line %d, Column %d (Page?)", Pl, Pc);
  } else
    show_result(SHOW_FAILURE);
}

/* vt340/vt420 & up */
int
tst_DSR_cursor(MENU_ARGS)
{
  return any_DSR(PASS_ARGS, "?6n", show_ExtendedCursorPosition);
}

/******************************************************************************/

static int
tst_device_status(MENU_ARGS)
{
  /* *INDENT-OFF* */
  static MENU my_menu[] = {
      { "Exit",                                              0 },
      { "Test Keyboard Status",                              tst_DSR_keyboard },
      { "Test Printer Status",                               tst_DSR_printer },
      { "Test UDK Status",                                   tst_DSR_userkeys },
      { "Test Locator Status",                               tst_DSR_locator },
      { "Test Extended Cursor-Position (DECXCPR)",           tst_DSR_cursor },
      { "",                                                  0 }
    };
  /* *INDENT-ON* */

  do {
    vt_clear(2);
    __(title(0), printf("VT320 Device Status Reports (DSR)"));
    __(title(2), println("Choose test type:"));
  } while (menu(my_menu));
  return MENU_NOHOLD;
}

/******************************************************************************/

/*
 * DECCIR returns single characters separated by semicolons.  It's not clear
 * (unless you test on a DEC terminal) from the documentation, which only cites
 * their values.  This function returns an equivalent-value, recovering from
 * the bogus implementations that return a decimal number.
 */
static int
scan_chr(char *str, int *pos, int toc)
{
  int value = 0;
  while (str[*pos] != '\0' && str[*pos] != toc) {
    value = (value * 256) + (unsigned char) str[*pos];
    *pos += 1;
  }
  if (str[*pos] == toc)
    *pos += 1;
  return value;
}

/*
 * From Kermit 3.13 & VT220 pocket guide
 *
 * Request  CSI 1 $ w             cursor information report
 * Response DCS 1 $ u Pr; Pc; Pp; Srend; Satt; Sflag; Pgl; Pgr; Scss; Sdesig ST
 *        where   Pr is cursor row (counted from origin as 1,1)
 *                Pc is cursor column
 *                Pp is 1, video page, a constant for VT320s
 *                Srend = 40h + 8 (rev video on) + 4 (blinking on)
 *                                 + 2 (underline on) + 1 (bold on)
 *                Satt = 40h + 1  (selective erase on)
 *                Sflag = 40h + 8 (autowrap pending) + 4 (SS3 pending)
 *                                + 2 (SS2 pending) + 1 (Origin mode on)
 *                Pgl = char set in GL (0 = G0, 1 = G1, 2 = G2, 3 = G3)
 *                Pgr = char set in GR (same as for Pgl)
 *                Scss = 40h + 8 (G3 is 96 char) + 4 (G2 is 96 char)
 *                                + 2 (G1 is 96 char) + 1 (G0 is 96 char)
 *                Sdesig is string of character idents for sets G0...G3, with
 *                                no separators between set idents.
 *                If NRCs are active the set idents (all 94 byte types) are:
 *                British         A       Italian         Y
 *                Dutch           4       Norwegian/Danish ' (hex 60) or E or 6
 *                Finnish         5 or C  Portuguese      %6 or g or L
 *                French          R or f  Spanish         Z
 *                French Canadian 9 or Q  Swedish         7 or H
 *                German          K       Swiss           =
 *                Hebrew          %=
 *                (MS Kermit uses any choice when there are multiple)
 */

#define show_DECCIR_flag(value, mask, string) \
  if (value & mask) { value &= ~mask; show_result(string); }

static void
show_DECCIR(char *report)
{
  int Pr, Pc, Pp, Srend, Satt, Sflag, Pgl, Pgr, Scss;
  int pos = 3;                  /* skip "1$u" */
  int n;

  Pr = scanto(report, &pos, ';');
  Pc = scanto(report, &pos, ';');
  Pp = scanto(report, &pos, ';');
  vt_move(5, 10);
  show_result("Cursor (%d,%d), page %d", Pr, Pc, Pp);

  Srend = scan_chr(report, &pos, ';');
  vt_move(6, 10);
  if (Srend & 0x40) {
    show_DECCIR_flag(Srend, 0x40, "Rendition:");
    if (Srend == 0)
      show_result(" normal");
    show_DECCIR_flag(Srend, 0x08, " reverse");
    show_DECCIR_flag(Srend, 0x04, " blinking");
    show_DECCIR_flag(Srend, 0x02, " underline");
    show_DECCIR_flag(Srend, 0x01, " bold");
  }
  if (Srend)
    show_result(" -> unknown rendition (0x%x)", Srend);

  Satt = scan_chr(report, &pos, ';');
  vt_move(7, 10);
  switch (Satt) {
  case 0x40:
    show_result("Selective erase: off");
    break;
  case 0x41:
    show_result("Selective erase: ON");
    break;
  default:
    show_result("Selective erase: unknown (0x%x)", Satt);
  }

  Sflag = scan_chr(report, &pos, ';');
  vt_move(8, 10);
  if (Sflag & 0x40) {
    show_DECCIR_flag(Sflag, 0x40, "Flags:");
    show_DECCIR_flag(Sflag, 0x08, " autowrap pending");
    show_DECCIR_flag(Sflag, 0x04, " SS3 pending");
    show_DECCIR_flag(Sflag, 0x02, " SS2 pending");
    show_DECCIR_flag(Sflag, 0x01, " origin-mode on");
  } else {
    show_result(" -> unknown flag (0x%x)", Sflag);
  }

  Pgl = scanto(report, &pos, ';');
  Pgr = scanto(report, &pos, ';');
  vt_move(9, 10);
  show_result("Char set in GL: G%d, Char set in GR: G%d", Pgl, Pgr);

  Scss = scan_chr(report, &pos, ';');
  vt_move(10, 10);
  if (Scss & 0x40) {
    show_DECCIR_flag(Scss, 0x40, "Char set sizes:");
    show_DECCIR_flag(Scss, 0x08, " G3 is 96 char");
    show_DECCIR_flag(Scss, 0x04, " G2 is 96 char");
    show_DECCIR_flag(Scss, 0x02, " G1 is 96 char");
    show_DECCIR_flag(Scss, 0x01, " G0 is 96 char");   /* VT420 manual says this cannot happen */
  } else {
    show_result(" -> unknown char set size (0x%x)", Scss);
  }

  n = 11;
  vt_move(n, 10);
  show_result("Character set idents for G0...G3: ");
  println("");
  while (report[pos] != '\0') {
    char *result = parse_Sdesig(report, &pos);
    show_result("            %s\n", result);
    println("");
  }
}

/******************************************************************************/

/*
 * Request  CSI 2 $ w             tab stop report
 * Response DCS 2 $ u Pc/Pc/...Pc ST
 *        Pc are column numbers (from 1) where tab stops occur. Note the
 *        separator "/" occurs in a real VT320 but should have been ";".
 */
static void
show_DECTABSR(char *report)
{
  int pos = 3;                  /* skip "2$u" */
  int stop;
  char *buffer = (char *) malloc(strlen(report));

  *buffer = '\0';
  strcat(report, "/");  /* simplify scanning */
  while ((stop = scanto(report, &pos, '/')) != 0) {
    sprintf(buffer + strlen(buffer), " %d", stop);
  }
  println("");
  show_result("Tab stops:%s", buffer);
  free(buffer);
}

/******************************************************************************/

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", 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;
}

static int
tst_DECCIR(MENU_ARGS)
{
  return any_decrqpsr(PASS_ARGS, 1);
}

static int
tst_DECTABSR(MENU_ARGS)
{
  return any_decrqpsr(PASS_ARGS, 2);
}

/* 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;
}

/* Test User-Preferred Supplemental Set - VT320 */
static int
tst_DECRQUPSS(MENU_ARGS)
{
  char *report;
  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(show);

  restore_ttymodes();
  vt_move(max_lines - 1, 1);
  return MENU_HOLD;
}

/* Request Terminal State Report */
static int
tst_DECRQTSR(MENU_ARGS)
{
  char *report;
  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", 3)) {
    show = SHOW_SUCCESS;
  } else {
    show = SHOW_FAILURE;
  }
  show_result(show);

  restore_ttymodes();
  vt_move(max_lines - 1, 1);
  return MENU_HOLD;
}

/******************************************************************************/

static int use_DECRPM;

int
set_DECRPM(int level)
{
  int result = use_DECRPM;
  use_DECRPM = level;
  return result;
}

typedef struct {
  int mode;
  char *name;
  int level;
} DECRPM_DATA;

#define DATA(name,level) { name, #name, level }

static int
tst_ISO_DECRPM(MENU_ARGS)
{
  int j, Pa, Ps;
  char chr;
  char *report;
  /* *INDENT-OFF* */
  DECRPM_DATA ansi_modes[] = { /* this list is sorted by code, not name */
    DATA( GATM, 3 /* guarded area transfer (disabled) */),
    DATA( KAM,  3 /* keyboard action */),
    DATA( CRM,  3 /* control representation (setup) */),
    DATA( IRM,  3 /* insert/replace */),
    DATA( SRTM, 3 /* status reporting transfer (disabled) */),
    DATA( ERM,  9 /* erasure mode (non-DEC) */),
    DATA( VEM,  3 /* vertical editing (disabled) */),
    DATA( BDSM, 9 /* bi-directional support mode (non-DEC) */),
    DATA( DCSM, 9 /* device component select mode (non-DEC) */),
    DATA( HEM,  3 /* horizontal editing (disabled) */),
    DATA( PUM,  3 /* positioning unit (disabled) */),
    DATA( SRM,  3 /* send/receive */),
    DATA( FEAM, 3 /* format effector action (disabled) */),
    DATA( FETM, 3 /* format effector transfer (disabled) */),
    DATA( MATM, 3 /* multiple area transfer (disabled) */),
    DATA( TTM,  3 /* transfer termination (disabled) */),
    DATA( SATM, 3 /* selected area transfer (disabled) */),
    DATA( TSM,  3 /* tabulation stop (disabled) */),
    DATA( EBM,  3 /* editing boundary (disabled) */),
    DATA( LNM,  3 /* line feed/new line */) };
  /* *INDENT-ON* */

  vt_move(1, 1);
  printf("Testing %s\n", the_title);

  set_tty_raw(TRUE);
  set_tty_echo(FALSE);

  for (j = 0; j < TABLESIZE(ansi_modes); j++) {
    if (use_DECRPM < ansi_modes[j].level)
      continue;
    do_csi("%d$p", ansi_modes[j].mode);
    report = instr();
    printf("\n      %3d: %-10s", ansi_modes[j].mode, ansi_modes[j].name);
    if (LOG_ENABLED)
      fprintf(log_fp, "Testing %s\n", ansi_modes[j].name);
    chrprint(report);
    if ((report = skip_csi(report)) != 0
        && sscanf(report, "%d;%d$%c", &Pa, &Ps, &chr) == 3
        && Pa == ansi_modes[j].mode
        && chr == 'y') {
      switch (Ps) {
      case 0:
        show_result(" unknown");
        break;
      case 1:
        show_result(" set");
        break;
      case 2:
        show_result(" reset");
        break;
      case 3:
        show_result(" permanently set");
        break;
      case 4:
        show_result(" permanently reset");
        break;
      default:
        show_result(" ?");
        break;
      }
    } else {
      show_result(SHOW_FAILURE);
    }
  }

  restore_ttymodes();
  vt_move(max_lines - 1, 1);
  return MENU_HOLD;
}

static int
tst_DEC_DECRPM(MENU_ARGS)
{
  int j, Pa, Ps;
  char chr;
  char *report;
  char *show;
  /* *INDENT-OFF* */
  DECRPM_DATA dec_modes[] = { /* this list is sorted by code, not name */
    DATA( DECCKM,  3 /* cursor keys */),
    DATA( DECANM,  3 /* ANSI */),
    DATA( DECCOLM, 3 /* column */),
    DATA( DECSCLM, 3 /* scrolling */),
    DATA( DECSCNM, 3 /* screen */),
    DATA( DECOM,   3 /* origin */),
    DATA( DECAWM,  3 /* autowrap */),
    DATA( DECARM,  3 /* autorepeat */),
    DATA( DECEDM,  3 /* edit */),
    DATA( DECLTM,  3 /* line transmit */),
    DATA( DECSCFDM,3 /* space compression field delimiter */),
    DATA( DECTEM,  3 /* transmission execution */),
    DATA( DECEKEM, 3 /* edit key execution */),
    DATA( DECPFF,  3 /* print form feed */),
    DATA( DECPEX,  3 /* printer extent */),
    DATA( DECTCEM, 3 /* text cursor enable */),
    DATA( DECRLM,  5 /* left-to-right */),
    DATA( DECTEK,  3 /* 4010/4014 emulation */),
    DATA( DECHEM,  5 /* Hebrew encoding */),
    DATA( DECNRCM, 3 /* national replacement character set */),
    DATA( DECGEPM, 3 /* graphics expanded print */),
    DATA( DECGPCM, 3 /* graphics print color */),
    DATA( DECGPCS, 3 /* graphics print color syntax */),
    DATA( DECGPBM, 3 /* graphics print background */),
    DATA( DECGRPM, 3 /* graphics rotated print */),
    DATA( DEC131TM,3 /* VT131 transmit */),
    DATA( DECNAKB, 5 /* Greek/N-A Keyboard Mapping */),
    DATA( DECHCCM, 3 /* horizontal cursor coupling (disabled) */),
    DATA( DECVCCM, 3 /* vertical cursor coupling */),
    DATA( DECPCCM, 3 /* page cursor coupling */),
    DATA( DECNKM,  3 /* numeric keypad */),
    DATA( DECBKM,  3 /* backarrow key */),
    DATA( DECKBUM, 3 /* keyboard usage */),
    DATA( DECVSSM, 4 /* vertical split */),
    DATA( DECXRLM, 3 /* transmit rate linking */),
    DATA( DECKPM,  4 /* keyboard positioning */),
    DATA( DECNCSM, 5 /* no clearing screen on column change */),
    DATA( DECRLCM, 5 /* right-to-left copy */),
    DATA( DECCRTSM,5 /* CRT save */),
    DATA( DECARSM, 5 /* auto resize */),
    DATA( DECMCM,  5 /* modem control */),
    DATA( DECAAM,  5 /* auto answerback */),
    DATA( DECCANSM,5 /* conceal answerback */),
    DATA( DECNULM, 5 /* null */),
    DATA( DECHDPXM,5 /* half duplex */),
    DATA( DECESKM, 5 /* enable secondary keyboard language */),
    DATA( DECOSCNM,5 /* overscan */),
    DATA( DECFWM,  5 /* framed windows */),
    DATA( DECRPL,  5 /* review previous lines */),
    DATA( DECHWUM, 5 /* host wake-up mode (CRT and energy saver) */),
    DATA( DECATCUM,5 /* alternate text color underline */),
    DATA( DECATCBM,5 /* alternate text color blink */),
    DATA( DECBBSM, 5 /* bold and blink style */),
    DATA( DECECM,  5 /* erase color */),
  };
  /* *INDENT-ON* */

  vt_move(1, 1);
  printf("Testing %s\n", the_title);

  set_tty_raw(TRUE);
  set_tty_echo(FALSE);

  for (j = 0; j < TABLESIZE(dec_modes); j++) {
    if (use_DECRPM < dec_modes[j].level)
      continue;
    do_csi("?%d$p", dec_modes[j].mode);
    report = instr();
    printf("\n      %3d: %-10s", dec_modes[j].mode, dec_modes[j].name);
    if (LOG_ENABLED)
      fprintf(log_fp, "Testing %s\n", dec_modes[j].name);
    chrprint(report);
    if ((report = skip_csi(report)) != 0
        && sscanf(report, "?%d;%d$%c", &Pa, &Ps, &chr) == 3
        && Pa == dec_modes[j].mode
        && chr == 'y') {
      switch (Ps) {
      case 0:
        show = "unknown";
        break;
      case 1:
        show = "set";
        break;
      case 2:
        show = "reset";
        break;
      case 3:
        show = "permanently set";
        break;
      case 4:
        show = "permanently reset";
        break;
      default:
        show = "?";
        break;
      }
    } else {
      show = SHOW_FAILURE;
    }
    show_result(show);
  }

  restore_ttymodes();
  vt_move(max_lines - 1, 1);
  return MENU_HOLD;
}

#undef DATA

/******************************************************************************/

int
tst_DECRPM(MENU_ARGS)
{
  /* *INDENT-OFF* */
  static MENU my_menu[] = {
      { "Exit",                                              0 },
      { "ANSI Mode Report (DECRPM)",                         tst_ISO_DECRPM },
      { "DEC Mode Report (DECRPM)",                          tst_DEC_DECRPM },
      { "",                                                  0 }
    };
  /* *INDENT-ON* */

  do {
    vt_clear(2);
    __(title(0), printf("Request Mode (DECRQM)/Report Mode (DECRPM)"));
    __(title(2), println("Choose test type:"));
  } while (menu(my_menu));
  return MENU_NOHOLD;
}

/******************************************************************************/

/*
 * FIXME: The VT420 manual says that a valid response begins "DCS 0 $ r",
 * however I see "DCS 1 $ r" on a real VT420, consistently.
 */
int
any_decrqss(char *msg, char *func)
{
  char *report;
  char *show;

  vt_move(1, 1);
  printf("Testing DECRQSS: %s\n", msg);

  set_tty_raw(TRUE);
  set_tty_echo(FALSE);

  decrqss(func);
  report = get_reply();
  vt_move(3, 10);
  chrprint(report);
  switch (parse_decrqss(report, func)) {
  case 1:
    show = "ok (valid request)";
    break;
  case 0:
    show = "invalid request";
    break;
  default:
    show = SHOW_FAILURE;
    break;
  }
  show_result(show);

  restore_ttymodes();
  vt_move(max_lines - 1, 1);
  return MENU_HOLD;
}

static int
rpt_DECSASD(MENU_ARGS)
{
  return any_decrqss(the_title, "$}");
}

static int
rpt_DECSCA(MENU_ARGS)
{
  return any_decrqss(the_title, "\"q");
}

static int
rpt_DECSCL(MENU_ARGS)
{
  return any_decrqss(the_title, "\"p");
}

static int
rpt_DECSCPP(MENU_ARGS)
{
  return any_decrqss(the_title, "$|");
}

static int
rpt_DECSLPP(MENU_ARGS)
{
  return any_decrqss(the_title, "t");
}

static int
rpt_DECSSDT(MENU_ARGS)
{
  return any_decrqss(the_title, "$~");
}

static int
rpt_DECSTBM(MENU_ARGS)
{
  return any_decrqss(the_title, "r");
}

static int
rpt_SGR(MENU_ARGS)
{
  return any_decrqss(the_title, "m");
}

static int
rpt_DECTLTC(MENU_ARGS)
{
  return any_decrqss(the_title, "'s");
}

static int
rpt_DECTTC(MENU_ARGS)
{
  return any_decrqss(the_title, "|");
}

int
tst_vt320_DECRQSS(MENU_ARGS)
{
  /* *INDENT-OFF* */
  static MENU my_menu[] = {
      { "Exit",                                              0 },
      { "Select active status display (DECSASD)",            rpt_DECSASD },
      { "Set character attribute (DECSCA)",                  rpt_DECSCA },
      { "Set conformance level (DECSCL)",                    rpt_DECSCL },
      { "Set columns per page (DECSCPP)",                    rpt_DECSCPP },
      { "Set lines per page (DECSLPP)",                      rpt_DECSLPP },
      { "Set status line type (DECSSDT)",                    rpt_DECSSDT },
      { "Set top and bottom margins (DECSTBM)",              rpt_DECSTBM },
      { "Select graphic rendition (SGR)",                    rpt_SGR },
      { "Set transmit termination character (DECTTC)",       rpt_DECTTC },
      { "Transmission line termination character (DECTLTC)", rpt_DECTLTC },
      { "",                                                  0 }
    };
  /* *INDENT-ON* */

  do {
    vt_clear(2);
    __(title(0), printf("VT320 Status-String Reports"));
    __(title(2), println("Choose test type:"));
  } while (menu(my_menu));
  return MENU_NOHOLD;
}

/******************************************************************************/

/*
 * The main vt100 module tests CUP, HVP, CUF, CUB, CUU, CUD
 */
int
tst_vt320_cursor(MENU_ARGS)
{
  /* *INDENT-OFF* */
  static MENU my_menu[] = {
      { "Exit",                                              0 },
      { "Test Pan down (SU)",                                tst_SU },
      { "Test Pan up (SD)",                                  tst_SD},
      { "Test Vertical Cursor Coupling (DECVCCM)",           not_impl },
      { "Test Page Cursor Coupling (DECPCCM)",               not_impl },
      { "",                                                  0 }
    };
  /* *INDENT-ON* */

  do {
    vt_clear(2);
    __(title(0), printf("VT320 Cursor-Movement Tests"));
    __(title(2), println("Choose test type:"));
  } while (menu(my_menu));
  return MENU_NOHOLD;
}

/******************************************************************************/

static int
tst_vt320_report_terminal(MENU_ARGS)
{
  /* *INDENT-OFF* */
  static MENU my_menu[] = {
      { "Exit",                                              0 },
      { "Restore Terminal State (DECRSTS)",                  not_impl },
      { "Terminal State Report (DECRQTS/DECTSR)",            tst_DECRQTSR },
      { "",                                                  0 }
    };
  /* *INDENT-ON* */

  do {
    vt_clear(2);
    __(title(0), printf("VT320 Terminal State Reports"));
    __(title(2), println("Choose test type:"));
  } while (menu(my_menu));
  return MENU_NOHOLD;
}

/******************************************************************************/

int
tst_vt320_report_presentation(MENU_ARGS)
{
  /* *INDENT-OFF* */
  static MENU my_menu[] = {
      { "Exit",                                              0 },
      { "Cursor Information Report (DECCIR)",                tst_DECCIR },
      { "Tab Stop Report (DECTABSR)",                        tst_DECTABSR },
      { "Request Mode (DECRQM)/Report Mode (DECRPM)",        tst_DECRPM },
      { "Restore Presentation State (DECRSPS)",              not_impl },
      { "Status-String Report (DECRQSS)",                    tst_vt320_DECRQSS },
      { "",                                                  0 }
  };
  /* *INDENT-ON* */

  int old_DECRPM = set_DECRPM(3);

  do {
    vt_clear(2);
    __(title(0), printf("VT320 Presentation State Reports"));
    __(title(2), println("Choose test type:"));
  } while (menu(my_menu));
  set_DECRPM(old_DECRPM);
  return MENU_NOHOLD;
}

/******************************************************************************/

int
tst_vt320_reports(MENU_ARGS)
{
  /* *INDENT-OFF* */
  static MENU my_menu[] = {
      { "Exit",                                              0 },
      { "Test VT220 features",                               tst_vt220_reports },
      { "Test Device Status Report (DSR)",                   tst_device_status },
      { "Test Presentation State Reports",                   tst_vt320_report_presentation },
      { "Test Terminal State Reports",                       tst_vt320_report_terminal },
      { "Test User-Preferred Supplemental Set (DECAUPSS)",   tst_DECRQUPSS },
      { "Test Window Report (DECRPDE)",                      tst_DECRQDE },
      { "",                                                  0 }
    };
  /* *INDENT-ON* */

  do {
    vt_clear(2);
    __(title(0), printf("VT320 Reports"));
    __(title(2), println("Choose test type:"));
  } while (menu(my_menu));
  return MENU_NOHOLD;
}

/******************************************************************************/

/* vt340/vt420 & up */
static int
tst_PageMovement(MENU_ARGS)
{
  /* *INDENT-OFF* */
  static MENU my_menu[] = {
      { "Exit",                                              0 },
      { "Test Next Page (NP)",                               not_impl },
      { "Test Preceding Page (PP)",                          not_impl },
      { "Test Page Position Absolute (PPA)",                 not_impl },
      { "Test Page Position Backward (PPB)",                 not_impl },
      { "Test Page Position Relative (PPR)",                 not_impl },
      { "",                                                  0 }
    };
  /* *INDENT-ON* */

  do {
    vt_clear(2);
    __(title(0), printf("Page Format Tests"));
    __(title(2), println("Choose test type:"));
  } while (menu(my_menu));
  return MENU_NOHOLD;
}

/******************************************************************************/

/* vt340/vt420 & up */
int
tst_vt320_screen(MENU_ARGS)
{
  /* *INDENT-OFF* */
  static MENU my_menu[] = {
      { "Exit",                                              0 },
      { "Test VT220 features",                               tst_vt220_screen },
      { "Test Status line (DECSASD/DECSSDT)",                tst_statusline },
      { "",                                                  0 }
    };
  /* *INDENT-ON* */

  do {
    vt_clear(2);
    __(title(0), printf("VT320 Screen-Display Tests"));
    __(title(2), println("Choose test type:"));
  } while (menu(my_menu));
  return MENU_NOHOLD;
}

/******************************************************************************/

int
tst_vt320(MENU_ARGS)
{
  /* *INDENT-OFF* */
  static MENU my_menu[] = {
      { "Exit",                                              0 },
      { "Test VT220 features",                               tst_vt220 },
      { "Test cursor-movement",                              tst_vt320_cursor },
      { "Test page-movement controls",                       tst_PageMovement },
      { "Test reporting functions",                          tst_vt320_reports },
      { "Test screen-display functions",                     tst_vt320_screen },
      { "",                                                  0 }
    };
  /* *INDENT-ON* */

  do {
    vt_clear(2);
    __(title(0), printf("VT320 Tests"));
    __(title(2), println("Choose test type:"));
  } while (menu(my_menu));
  return MENU_NOHOLD;
}
