Logo Search packages:      
Sourcecode: vile version File versions

vmsvt.c

/*
 * VMS terminal driver
 *
 * Uses SMG to lookup capabilities of the terminal (see SYS$SYSTEM:SMGTERMS.TXT
 * and SYS$SYSTEM:TERMTABLE.TXT)
 *
 * Originally written by Curtis Smith in 1987, this module has been rewritten
 * by Tom Dickey and Clark Morgan to:
 *
 *   -- simplify it,
 *   -- extend it to support a wider variety of terminal types,
 *   -- add VT220 keyboard mappings,
 *   -- support syntax highlighting,
 *   -- support wide and narrow screen resolutions,
 *   -- support visual bells.
 *
 * $Header: /usr/build/vile/vile/RCS/vmsvt.c,v 1.55 2002/01/09 00:28:12 tom Exp $
 *
 */

#include    "estruct.h"       /* Emacs' structures          */
#include    "edef.h"          /* Emacs' definitions         */

#if   DISP_VMSVT

#include    <descrip.h>       /* Descriptor definitions     */
#include    <iodef.h>         /* to get IO$_SENSEMODE       */
#include    <ttdef.h>         /* to get TT$_UNKNOWN         */
#include    <stsdef.h>        /* to get $VMS_STATUS_SUCCESS */

#include    <starlet.h>
#include    <smgtrmptr.h>           /* to get SMG$K_??? definitions */
#include    <smg$routines.h>
#include    <ssdef.h>

/* function prototypes needed for the dispatch table */
static      void  vmsscrollregion (int top, int bot);
static      void  vmsscroll_reg (int from, int to, int n);
static      void  vmsscroll_delins (int from, int to, int n);
static      void  vmsopen (void);
static      void  vmsclose (void);
static      void  vmsmove (int row, int col);
static      void  vmseeol (void);
static      void  vmseeop (void);
static      void  vmsbeep (void);
#if OPT_VIDEO_ATTRS
static      void  vmsattr ( UINT attr );
#else
static      void  vmsrev      ( UINT state );
#endif
static      int   vmscres (const char *);

/* SMG stuff (just like termcap) */
static      int   initialized;
static      int   termtype;
static      char *      tc_SO;      /* begin standout (reverse) */
static      char *      tc_SE;      /* end standout (reverse) */
static      char *      erase_to_end_line;
static      char *      erase_whole_display;

static      char *      delete_line;
static      char *      insert_line;
static      char *      scroll_forw;
static      char *      scroll_back;
static      char *      scroll_regn;

static      char *      set_narrow;
static      char *      set_wide;

static      long  wide_cols;
static      long  narrow_cols;

#if OPT_VIDEO_ATTRS
static      char *      tc_MD;            /* begin bold */
static      char *      tc_ME;            /* end bold */
static      char *      tc_US;            /* begin underscore */
static      char *      tc_UE;            /* end underscore */
#endif

#if OPT_FLASH
static      char *      dark_on;
static      char *      dark_off;
#endif

/* Dispatch table. All hard fields just point into the terminal I/O code. */
TERM  term  = {
      24,                     /* Max number of rows allowable */
      /* Filled in */ 0,            /* Current number of rows used      */
      132,                    /* Max number of columns      */
      /* Filled in */ 0,            /* Current number of columns  */
      100,                    /* # times thru update to pause */
      vmsopen,                /* Open terminal at the start */
      vmsclose,               /* Close terminal at end      */
      nullterm_kopen,               /* Open keyboard        */
      nullterm_kclose,        /* Close keyboard       */
      ttgetc,                       /* Get character from keyboard      */
      ttputc,                       /* Put character to display   */
      tttypahead,             /* char ready for reading     */
      ttflush,                /* Flush output buffers       */
      vmsmove,                /* Move cursor, origin 0      */
      vmseeol,                /* Erase to end of line       */
      vmseeop,                /* Erase to end of page       */
      vmsbeep,                /* Beep                       */
#if OPT_VIDEO_ATTRS
      vmsattr,                /* Set attribute video state  */
#else
      vmsrev,                       /* Set reverse video state    */
#endif
      vmscres,                /* Change screen resolution   */
      nullterm_setfore,       /* N/A: Set foreground color  */
      nullterm_setback,       /* N/A: Set background color  */
      nullterm_setpal,        /* N/A: Set palette colors    */
      nullterm_setccol,
      nullterm_scroll,        /* set at init-time           */
      nullterm_pflush,
      nullterm_icursor,
      nullterm_settitle,
      nullterm_watchfd,
      nullterm_unwatchfd,
      nullterm_cursorvis,
};

struct vmskeyseqs
{
      char  *seq;
      int   code;
};

struct vmskeyseqs vt100seqs[] =
{
      { "\033[A",   KEY_Up },
      { "\033[B",   KEY_Down },
      { "\033[C",   KEY_Right },
      { "\033[D",   KEY_Left },
      { "\033OP",   KEY_KP_F1 },
      { "\033OQ",   KEY_KP_F2 },
      { "\033OR",   KEY_KP_F3 },
      { "\033OS",   KEY_KP_F4 },
      /* vt220 keys (F1-F5 do not transmit control sequences) include vt100 */
      { "\033[17~", KEY_F6 },
      { "\033[18~", KEY_F7 },
      { "\033[19~", KEY_F8 },
      { "\033[20~", KEY_F9 },
      { "\033[21~", KEY_F10 },
      { "\033[23~", KEY_F11 },
      { "\033[24~", KEY_F12 },
      { "\033[25~", KEY_F13 },
      { "\033[26~", KEY_F14 },
      { "\033[28~", KEY_F15 },
      { "\033[29~", KEY_F16 },
      { "\033[31~", KEY_F17 },
      { "\033[32~", KEY_F18 },
      { "\033[33~", KEY_F19 },
      { "\033[34~", KEY_F20 },
      { "\033[3~",  KEY_Delete },   /* aka "Remove" */
      { "\033[6~",  KEY_Next },
      { "\033[5~",  KEY_Prior },    /* aka "Prev" */
      { "\033[1~",  KEY_Find },
      { "\033[2~",  KEY_Insert },
      { "\033[4~",  KEY_Select },
      /* 8-bit codes are the same, substituting the first two characters */
      { "\233A",    KEY_Up },
      { "\233B",    KEY_Down },
      { "\233C",    KEY_Right },
      { "\233D",    KEY_Left },
      { "\217P",    KEY_KP_F1 },
      { "\217Q",    KEY_KP_F2 },
      { "\217R",    KEY_KP_F3 },
      { "\217S",    KEY_KP_F4 },
      /* vt220 keys (F1-F5 do not transmit control sequences) include vt100 */
      { "\23317~",  KEY_F6 },
      { "\23318~",  KEY_F7 },
      { "\23319~",  KEY_F8 },
      { "\23320~",  KEY_F9 },
      { "\23321~",  KEY_F10 },
      { "\23323~",  KEY_F11 },
      { "\23324~",  KEY_F12 },
      { "\23325~",  KEY_F13 },
      { "\23326~",  KEY_F14 },
      { "\23328~",  KEY_F15 },
      { "\23329~",  KEY_F16 },
      { "\23331~",  KEY_F17 },
      { "\23332~",  KEY_F18 },
      { "\23333~",  KEY_F19 },
      { "\23334~",  KEY_F20 },
      { "\2333~",   KEY_Delete },   /* aka "Remove" */
      { "\2336~",   KEY_Next },
      { "\2335~",   KEY_Prior },    /* aka "Prev" */
      { "\2331~",   KEY_Find },
      { "\2332~",   KEY_Insert },
      { "\2334~",   KEY_Select },
};
struct vmskeyseqs vt52seqs[] =
{
      { "\33A", KEY_Up },
      { "\33B", KEY_Down },
      { "\33C", KEY_Right },
      { "\33D", KEY_Left },
      { "\33P", KEY_KP_F1 },
      { "\33Q", KEY_KP_F2 },
      { "\33R", KEY_KP_F3 },
      { "\33S", KEY_KP_F4 },
};

/***
 *  ttputs  -  Send a string to ttputc
 *
 *  Nothing returned
 ***/
static void
ttputs(char * string)               /* String to write            */
{
      if (string)
            while (*string != EOS)
                  ttputc(*string++);
}

/***
 *  vmsvt_tgoto - 2-argument request, e.g., for cursor movement.
 *
 *  Nothing returned
 ***/
static void
vmsvt_tgoto(request_code, parm1, parm2)
{
      char buffer[32];
      int ret_length;
      static int max_buffer_length = sizeof(buffer);
      static int arg_list[3] = { 2 };

      register int i;

      if (!initialized) {
            printf("\n");
            return;
      }

      /* Set the arguments into the arg_list array
       */
      arg_list[1] = parm1;
      arg_list[2] = parm2;

      if (!$VMS_STATUS_SUCCESS(smg$get_term_data(
            &termtype,
            &request_code,
            &max_buffer_length,
            &ret_length,
            buffer,
            arg_list)))
      {
            ttputs("OOPS");
            return;
      }
      else if (ret_length > 0)
      {
            char *cp = buffer;
            while (ret_length-- > 0)
                  ttputc(*cp++);
      }
}


static void
vmsmove (int row, int col)
{
      vmsvt_tgoto(SMG$K_SET_CURSOR_ABS, row+1, col+1);
}

#if OPT_VIDEO_ATTRS
static void
vmsattr(UINT attr)
{
#define VA_SGR (VASEL|VAREV|VAUL|VAITAL|VABOLD)
      static      const struct      {
            char  **start;
            char  **end;
            UINT  mask;
      } tbl[] = {
            { &tc_SO, &tc_SE, VASEL|VAREV },
            { &tc_US, &tc_UE, VAUL },
            { &tc_US, &tc_UE, VAITAL },
            { &tc_MD, &tc_ME, VABOLD },
      };
      static      UINT last;

      attr = VATTRIB(attr);
      if (attr & VASPCOL) {
            attr = VCOLORATTR((VCOLORNUM(attr) & (NCOLORS - 1)));
      } else {
            attr &= ~(VAML|VAMLFOC);
      }

      if (attr != last) {
            register size_t n;
            register char *s;
            UINT  diff = attr ^ last;
            int   ends = FALSE;

            /* turn OFF old attributes */
            for (n = 0; n < TABLESIZE(tbl); n++) {
                  if ((tbl[n].mask & diff) != 0
                   && (tbl[n].mask & attr) == 0
                   && (s = *(tbl[n].end))  != 0) {
                        ttputs(s);
#if OPT_COLOR
                        if (!ends)  /* do this once */
                              reinitialize_colors();
#endif
                        ends = TRUE;
                        diff &= ~(tbl[n].mask);
                  }
            }

            /* turn ON new attributes */
            for (n = 0; n < TABLESIZE(tbl); n++) {
                  if ((tbl[n].mask & diff)  != 0
                   && (tbl[n].mask & attr)  != 0
                   && (s = *(tbl[n].start)) != 0) {
                        ttputs(s);
                        diff &= ~(tbl[n].mask);
                  }
            }

            if (tc_SO != 0 && tc_SE != 0) {
                  if (ends && (attr & (VAREV|VASEL))) {
                        ttputs(tc_SO);
                  } else if (diff & VA_SGR) {  /* we didn't find it */
                        ttputs(tc_SE);
                  }
            }
#if OPT_COLOR
            if (attr & VACOLOR)
                  tcapfcol(VCOLORNUM(attr));
            else if (given_fcolor != gfcolor)
                  tcapfcol(gfcolor);
#endif
            last = attr;
      }
}

#else /* highlighting is a minimum attribute */

static void
tcaprev(          /* change reverse video status */
UINT state)       /* FALSE = normal video, TRUE = reverse video */
{
      static int revstate = -1;
      if (state == revstate)
            return;
      revstate = state;
      if (state)
            ttputs(tc_SO);
      else
            ttputs(tc_SE);
}

#endif      /* OPT_VIDEO_ATTRS */

static void
vmseeol(void)
{
      ttputs(erase_to_end_line);
}


static void
vmseeop(void)
{
      ttputs(erase_whole_display);
}



static void
cache_capabilities(void)
{
    static struct caps_struct
    {
      long  code;
      char **cap_string;
    } tbl[] =
    {
      { SMG$K_BEGIN_REVERSE,       &tc_SO },
      { SMG$K_END_REVERSE,         &tc_SE },
#if OPT_VIDEO_ATTRS
      { SMG$K_BEGIN_BOLD,          &tc_MD },
      { SMG$K_END_BOLD,      &tc_ME },
      { SMG$K_BEGIN_UNDERSCORE,    &tc_US },
      { SMG$K_END_UNDERSCORE,      &tc_UE },
#endif
#if OPT_FLASH
      { SMG$K_DARK_SCREEN,         &dark_on },
      { SMG$K_LIGHT_SCREEN,        &dark_off },
#endif
      { SMG$K_ERASE_TO_END_LINE,   &erase_to_end_line },
      { SMG$K_ERASE_WHOLE_DISPLAY, &erase_whole_display },
      { SMG$K_INSERT_LINE,         &insert_line },    /* al */
      { SMG$K_DELETE_LINE,         &delete_line },    /* dl */
      { SMG$K_SCROLL_FORWARD,      &scroll_forw },    /* SF */
      { SMG$K_SCROLL_REVERSE,      &scroll_back },    /* SR */
      { SMG$K_SET_SCROLL_REGION,   &scroll_regn },    /* CS */
      { SMG$K_WIDTH_NARROW,        &set_narrow },     /* 80 columns */
      { SMG$K_WIDTH_WIDE,          &set_wide }, /* 132 columns */
    };
    char           *buf;
    struct caps_struct *csp;
    int                  i, buf_len, ret_len;
    long           rqst_code;
    static char          storage[1024];

    for (i = 0, buf = storage, csp = tbl; i < TABLESIZE(tbl); i++, csp++)
    {
      buf_len = (storage + sizeof(storage)) - buf;
      if (!$VMS_STATUS_SUCCESS(smg$get_term_data(&termtype,
                         &csp->code,
                         &buf_len,
                         &ret_len,
                         buf)) || ret_len == 0)
      {
          *(csp->cap_string) = NULL;
      }
      else
      {
          buf[ret_len]       = 0;
          *(csp->cap_string) = buf;
          buf                 += ret_len + 1;
      }
    }
    rqst_code = SMG$K_COLUMNS;
    if (!$VMS_STATUS_SUCCESS(smg$get_numeric_data(&termtype,
                                      &rqst_code,
                                      &narrow_cols)))
    {
      narrow_cols = 80;
    }
    rqst_code = SMG$K_WIDE_SCREEN_COLUMNS;
    if (!$VMS_STATUS_SUCCESS(smg$get_numeric_data(&termtype,
                                      &rqst_code,
                                      &wide_cols)))
    {
      wide_cols = 132;
    }
    term.maxcols = wide_cols;
}

/** I/O information block definitions **/
struct iosb {                 /* I/O status block                 */
      short i_cond;           /* Condition value                  */
      short i_xfer;           /* Transfer count             */
      long  i_info;           /* Device information               */
};
struct termchar {       /* Terminal characteristics         */
      char  t_class;    /* Terminal class             */
      char  t_type;           /* Terminal type              */
      short t_width;    /* Terminal width in characters           */
      long  t_mandl;    /* Terminal's mode and length       */
      long  t_extend;   /* Extended terminal characteristics      */
};
static struct termchar tc;    /* Terminal characteristics         */

static void
get_terminal_type(void)
{
      short fd;
      int status, deassign_status;
      struct iosb iostatus;
      $DESCRIPTOR(devnam, "SYS$COMMAND");

      /* Assign input to a channel */
      if (!$VMS_STATUS_SUCCESS(status = sys$assign(&devnam, &fd, 0, 0)))
            tidy_exit(status);

      /* Get terminal characteristics */
      status = sys$qiow(            /* Queue and wait       */
            0,                /* Wait on event flag zero    */
            fd,               /* Channel to input terminal  */
            IO$_SENSEMODE,          /* Get current characteristic */
            &iostatus,        /* Status after operation     */
            0, 0,             /* No AST service       */
            &tc,              /* Terminal characteristics buf */
            sizeof(tc),       /* Size of the buffer         */
            0, 0, 0, 0);            /* P3-P6 unused               */

      /* De-assign the input device */
      if (!$VMS_STATUS_SUCCESS(deassign_status = sys$dassgn(fd)))
            tidy_exit(deassign_status);

      /* Jump out if bad qiow status */
      if (!$VMS_STATUS_SUCCESS(status))
            tidy_exit(status);
      if ((iostatus.i_cond & 1) == 0)
            tidy_exit(iostatus.i_cond);
}

static void
vmsopen(void)
{
      int   i, keyseq_tablesize;
      struct vmskeyseqs *keyseqs;

      get_terminal_type();
      if (tc.t_type == TT$_UNKNOWN)
      {
            printf("Terminal type is unknown!\n");
            printf("Set your terminal type using $ SET TERMINAL/INQUIRE\n");
            tidy_exit(3);
      }
      if (tc.t_type != TT$_VT52)
      {
            keyseqs = vt100seqs;
            keyseq_tablesize = TABLESIZE(vt100seqs);
      }
      else
      {
            keyseqs = vt52seqs;
            keyseq_tablesize = TABLESIZE(vt52seqs);
      }

      /* Access the system terminal definition table for the            */
      /* information of the terminal type returned by IO$_SENSEMODE     */
      if (!$VMS_STATUS_SUCCESS(smg$init_term_table_by_type(&tc.t_type, &termtype)))
            return;

      /* Set sizes */
      term.rows = ((UINT) tc.t_mandl >> 24);
      term.cols = tc.t_width;

      if (term.maxrows < term.rows)
            term.maxrows = term.rows;

      if (term.maxcols < term.cols)
            term.maxcols = term.cols;

      cache_capabilities();

      revexist = (tc_SO != NULL && tc_SE != NULL);
      eolexist = erase_whole_display != NULL;

      /*
       * I tried 'vmsgetstr()' for a VT100 terminal and it had no codes
       * for insert_line or for delete_line.    (Have to work up a test for
       * that) - TD
       */

      if (scroll_regn && scroll_back)
      {
            if (scroll_forw == NULL) /* assume '\n' scrolls forward */
                  scroll_forw = "\n";
            term.scroll = vmsscroll_reg;
      }
      else if (delete_line && insert_line)
            term.scroll = vmsscroll_delins;
      else
            term.scroll = nullterm_scroll;

      /* Set resolution */
      (void) strcpy(screen_desc, (term.cols == narrow_cols) ? "NORMAL" : "WIDE");

      /* Open terminal I/O drivers */
      ttopen();

      /* Set predefined keys */
      for (i = keyseq_tablesize; i--; )
            addtosysmap(keyseqs[i].seq, strlen(keyseqs[i].seq), keyseqs[i].code);

      initialized = TRUE;
}


/* copied/adapted from 'tcap.c' 19-apr-1993 dickey@software.org */

/* move howmany lines starting at from to to */
static void
vmsscroll_reg(int from, int to, int n)
{
      int i;
      if (to == from) return;
      if (to < from) {
            vmsscrollregion(to, from + n - 1);
            vmsmove(from + n - 1,0);
            for (i = from - to; i > 0; i--)
                  ttputs(scroll_forw);
      } else { /* from < to */
            vmsscrollregion(from, to + n - 1);
            vmsmove(from,0);
            for (i = to - from; i > 0; i--)
                  ttputs(scroll_back);
      }
      vmsscrollregion(0, term.rows-1);
}

/*
OPT_PRETTIER_SCROLL is prettier but slower -- it scrolls
            a line at a time instead of all at once.
*/

/* move howmany lines starting at from to to */
static void
vmsscroll_delins(int from, int to, int n)
{
      int i;
      if (to == from) return;
      /* patch: should make this more like 'tcap.c', or merge logic somehow */
#if OPT_PRETTIER_SCROLL
      if (absol(from-to) > 1) {
            vmsscroll_delins(from, (from<to) ? to-1:to+1, n);
            if (from < to)
                  from = to-1;
            else
                  from = to+1;
      }
#endif
      if (to < from) {
            vmsmove(to,0);
            for (i = from - to; i > 0; i--)
                  ttputs(delete_line);
            vmsmove(to+n,0);
            for (i = from - to; i > 0; i--)
                  ttputs(insert_line);
      } else {
            vmsmove(from+n,0);
            for (i = to - from; i > 0; i--)
                  ttputs(delete_line);
            vmsmove(from,0);
            for (i = to - from; i > 0; i--)
                  ttputs(insert_line);
      }
}

/* cs is set up just like cm, so we use tgoto... */
static void
vmsscrollregion(int top, int bot)
{
      vmsvt_tgoto(SMG$K_SET_SCROLL_REGION, top+1, bot+1);
}

/***
 *  vmscres  -    Change screen resolution
 *
 *  support these values:   WIDE -> 132 columns, NORMAL -> 80 columns
 *
 *  T -> if resolution successfully changed, F otherwise.
 ***/
static int
vmscres(const char *res)
{
    char buf[NLINE];
    int      rc = FALSE;

    if (tc.t_type == TT$_VT52)
    {
      mlforce("[sres not supported for VT52-style terminals]");
      return (rc);
    }

    strcpy(buf, res);
    mkupper(buf);
    if (strcmp(buf, "WIDE") == 0 && set_wide != 0)
    {
      ttputs(set_wide);
      term.cols = wide_cols;
      rc      = TRUE;
    }
    else if (strcmp(buf, "NORMAL") == 0 && set_narrow != 0)
    {
      ttputs(set_narrow);
      term.cols = narrow_cols;
      rc      = TRUE;
    }
    else
      mlforce("[invalid sres value (use NORMAL or WIDE)]");
    if (rc)
      newwidth(1, term.cols);
    return (rc);
}



static void
vmsclose(void)
{
    if (tc.t_type != TT$_VT52)
    {
      /*
       * Restore terminal width to previous state (if necessary) and then
       * cleanup as usual.
       */

      if (tc.t_width != term.cols)
          vmscres((tc.t_width == narrow_cols) ? "NORMAL" : "WIDE");
    }
    ttclose();
}



/***
 *  vmsbeep  -    Ring the bell
 *
 *  Nothing returned
 ***/
static void
vmsbeep(void)
{
#if OPT_FLASH
      int hit = 0;

      if (global_g_val(GMDFLASH) && dark_off != NULL && dark_on != NULL)
      {
            hit = 1;
            ttputs(dark_off);
            term.flush();
            catnap(200, FALSE);
            ttputs(dark_on);
      }
      if (! hit && tc.t_type != TT$_VT52)
      {
            static char *seq[][2] =
            {
                  { NULL, NULL },                /* vtflash = off */
                  { VTFLASH_NORMSEQ, VTFLASH_REVSEQ }, /* reverse */
                  { VTFLASH_REVSEQ, VTFLASH_NORMSEQ }, /* normal  */
            };
            char *str1, *str2;
            int  val;

            val  = global_g_val(GVAL_VTFLASH);
            str1 = seq[val][0];
            if (str1)
            {
                  str2 = seq[val][1];
                  ttputs(str1);
                  term.flush();
                  catnap(200, FALSE);
                  ttputs(str2);
                  hit = 1;
            }
      }
      if (! hit)
#endif
      ttputc(BEL);
}

#else

void
vmsvt_dummy(void)
{
}

#endif

Generated by  Doxygen 1.6.0   Back to index