home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / gnu / nethack-3.1 / sys / vms / vmstty.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-12-03  |  14.2 KB  |  455 lines

  1. /*    SCCS Id: @(#)vmstty.c    3.1    92/11/24    */
  2. /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
  3. /* NetHack may be freely redistributed.  See license for details. */
  4. /* tty.c - (VMS) version */
  5.  
  6. #define NEED_VARARGS
  7. #include "hack.h"
  8. #include "wintty.h"
  9. #include "termcap.h"
  10.  
  11. #include <descrip.h>
  12. #include <iodef.h>
  13. #ifndef __GNUC__
  14. #include <smgdef.h>
  15. #include <ttdef.h>
  16. #include <tt2def.h>
  17. #else    /* values needed from missing include files */
  18. # define SMG$K_TRM_UP     274
  19. # define SMG$K_TRM_DOWN  275
  20. # define SMG$K_TRM_LEFT  276
  21. # define SMG$K_TRM_RIGHT 277
  22. # define TT$M_MECHTAB     0x00000100    /* hardware tab support */
  23. # define TT$M_MECHFORM     0x00080000    /* hardware form-feed support */
  24. # define TT$M_NOBRDCST     0x00020000    /* disable broadcast messages, but  */
  25. # define TT2$M_BRDCSTMBX 0x00000010    /* catch them in associated mailbox */
  26. #endif /* __GNUC__ */
  27. #ifdef USE_QIO_INPUT
  28. #include <ssdef.h>
  29. #endif
  30. #include <errno.h>
  31. #include <signal.h>
  32.  
  33. unsigned long LIB$DISABLE_CTRL(), LIB$ENABLE_CTRL();
  34. unsigned long SYS$ASSIGN(), SYS$DASSGN(), SYS$QIOW();
  35. #ifndef USE_QIO_INPUT
  36. unsigned long SMG$CREATE_VIRTUAL_KEYBOARD(), SMG$DELETE_VIRTUAL_KEYBOARD(),
  37.           SMG$READ_KEYSTROKE(), SMG$CANCEL_INPUT();
  38. #else
  39. static short FDECL(parse_function_key, (int));
  40. #endif
  41. static void NDECL(setctty);
  42. static void NDECL(resettty);
  43.  
  44. #define vms_ok(sts) ((sts)&1)
  45. #define META(c)  ((c)|0x80)    /* 8th bit */
  46. #define CTRL(c)  ((c)&0x1F)
  47. #define CMASK(c) (1<<CTRL(c))
  48. #define LIB$M_CLI_CTRLT CMASK('T')    /* 0x00100000 */
  49. #define LIB$M_CLI_CTRLY CMASK('Y')    /* 0x02000000 */
  50. #define ESC '\033'
  51. #define CSI META(ESC)        /* '\233' */
  52. #define SS3 META(CTRL('O'))    /* '\217' */
  53.  
  54. extern short ospeed;
  55. char erase_char, intr_char, kill_char;
  56. static boolean settty_needed = FALSE,  bombing = FALSE;
  57. static unsigned long kb = 0;
  58. #ifdef USE_QIO_INPUT
  59. static char inputbuf[15+1], *inp = 0;
  60. static int  inc = 0;
  61. #endif
  62.  
  63. #define QIO_FUNC    IO$_TTYREADALL|IO$M_NOECHO|IO$M_TRMNOECHO
  64. #ifdef MAIL
  65. #define TT_SPECIAL_HANDLING  (TT$M_MECHTAB|TT$M_MECHFORM|TT$M_NOBRDCST)
  66. #define TT2_SPECIAL_HANDLING (TT2$M_BRDCSTMBX)
  67. #else
  68. #define TT_SPECIAL_HANDLING  (TT$M_MECHTAB|TT$M_MECHFORM)
  69. #define TT2_SPECIAL_HANDLING (0)
  70. #endif
  71. #define Uword unsigned short
  72. #define Ubyte unsigned char
  73. struct _rd_iosb {        /* i/o status block for read */
  74.     Uword    status,  trm_offset;
  75.     Uword    terminator,  trm_siz;
  76. };
  77. struct _wr_iosb {        /* i/o status block for write */
  78.     Uword    status,  byte_cnt;
  79.     unsigned   : 32;
  80. };
  81. struct _sm_iosb {        /* i/o status block for sense-mode qio */
  82.     Uword      status;
  83.     Ubyte      xmt_speed,  rcv_speed;
  84.     Ubyte      cr_fill,  lf_fill,  parity;
  85.     unsigned   : 8;
  86. };
  87. struct _sm_bufr {        /* sense-mode characteristics buffer */
  88.     Ubyte      class,  type;        /* class==DC$_TERM, type==(various) */
  89.     Uword      buf_siz;        /* aka page width */
  90. #define page_width buf_siz        /* number of columns */
  91.           unsigned  tt_char    : 24;    /* primary characteristics */
  92.           unsigned  page_length    :  8;    /* number of lines */
  93.           unsigned  tt2_char    : 32;    /* secondary characteristics */
  94. };
  95. static struct {
  96.     struct _sm_iosb io;
  97.     struct _sm_bufr sm;
  98. } sg = {{0},{0}};
  99. static unsigned short tt_chan = 0;
  100. static unsigned long  tt_char_restore = 0, tt_char_active = 0,
  101.               tt2_char_restore = 0, tt2_char_active = 0;
  102. static unsigned long  ctrl_mask = 0;
  103.  
  104. int
  105. vms_getchar()
  106. {
  107.     short key;
  108.  
  109. #ifdef USE_QIO_INPUT
  110.     struct _rd_iosb iosb;
  111.     unsigned long sts;
  112.     unsigned char kb_buf;
  113.  
  114.     if (inc > 0) {
  115.     /* we have buffered character(s) from previous read */
  116.     kb_buf = *inp++;
  117.     --inc;
  118.     sts = SS$_NORMAL;
  119.     } else {
  120.     sts = SYS$QIOW(0, tt_chan, QIO_FUNC, &iosb, (void(*)())0, 0,
  121.                &kb_buf, sizeof kb_buf, 0, 0, 0, 0);
  122.     }
  123.     if (vms_ok(sts)) {
  124.     if (kb_buf == CTRL('C')) {
  125.         if (intr_char) gsignal(SIGINT);
  126.         key = (short)kb_buf;
  127.     } else if (kb_buf == '\r') {    /* <return> */
  128.         key = (short)'\n';
  129.     } else if (kb_buf == ESC || kb_buf == CSI || kb_buf == SS3) {
  130.         switch(parse_function_key((int)kb_buf)) {
  131.           case SMG$K_TRM_UP:    key = flags.num_pad ? '8' : 'k';  break;
  132.           case SMG$K_TRM_DOWN:  key = flags.num_pad ? '2' : 'j';  break;
  133.           case SMG$K_TRM_LEFT:  key = flags.num_pad ? '4' : 'h';  break;
  134.           case SMG$K_TRM_RIGHT: key = flags.num_pad ? '6' : 'l';  break;
  135.           default:            key = ESC;    break;
  136.         }
  137.     } else {
  138.         key = (short)kb_buf;
  139.     }
  140.     } else if (sts == SS$_HANGUP || iosb.status == SS$_HANGUP) {
  141.     gsignal(SIGHUP);
  142.     key = ESC;
  143.     } else            /*(this should never happen)*/
  144.     key = getchar();
  145.  
  146. #else   /*!USE_QIO_INPUT*/
  147.     static volatile int recurse = 0;    /* SMG is not AST re-entrant! */
  148.  
  149.     if (recurse++ == 0 && kb != 0) {
  150.     SMG$READ_KEYSTROKE(&kb, &key);
  151.     switch (key) {
  152.       case SMG$K_TRM_UP:    flags.num_pad ? '8' : key = 'k';  break;
  153.       case SMG$K_TRM_DOWN:    flags.num_pad ? '2' : key = 'j';  break;
  154.       case SMG$K_TRM_LEFT:    flags.num_pad ? '4' : key = 'h';  break;
  155.       case SMG$K_TRM_RIGHT: flags.num_pad ? '6' : key = 'l';  break;
  156.       case '\r':        key = '\n'; break;
  157.       default:        if (key > 255)    key = ESC;
  158.                 break;
  159.     }
  160.     } else {
  161.     /* abnormal input--either SMG didn't initialize properly or
  162.        vms_getchar() has been called recursively (via SIGINT handler).
  163.      */
  164.     if (kb != 0)            /* must have been a recursive call */
  165.         SMG$CANCEL_INPUT(&kb);    /*  from an interrupt handler       */
  166.     key = getchar();
  167.     }
  168.     --recurse;
  169. #endif    /* USE_QIO_INPUT */
  170.  
  171.     return (int)key;
  172. }
  173.  
  174. #ifdef USE_QIO_INPUT
  175.        /*
  176.     * We've just gotten an <escape> character.  Do a timed read to
  177.     * get any other characters, then try to parse them as an escape
  178.     * sequence.  This isn't perfect, since there's no guarantee
  179.     * that a full escape sequence will be available, or even if one
  180.     * is, it might actually by regular input from a fast typist or
  181.     * a stalled input connection.  {For packetized environments,
  182.     * cross plural(body_part(FINGER)) and hope for best. :-}
  183.     *
  184.     * This is needed to preserve compatability with SMG interface
  185.     * for two reasons:
  186.     *    1) retain support for arrow keys, and
  187.     *    2) treat other VTxxx function keys as <esc> for aborting
  188.     *    various NetHack prompts.
  189.     * The second reason is compelling; otherwise remaining chars of
  190.     * an escape sequence get treated as inappropriate user commands.
  191.     *
  192.     * SMG code values for these key sequences fall in the range of
  193.     * 256 thru 3xx.  The assignments are not particularly intuitive.
  194.     */
  195. /*=
  196.      -- Summary of VTxxx-style keyboards and transmitted escape sequences. --
  197. Keypad codes are prefixed by 7 bit (\033 O) or 8 bit SS3:
  198.     keypad:  PF1 PF2 PF3 PF4       codes:    P   Q    R   S
  199.           7   8   9   -            w   x    y   m
  200.           4   5   6   .            t   u    v   n
  201.           1   2   3  :en-:        q   r    s  : :
  202.          ...0...  ,  :ter:           ...p...    l  :M:
  203. Arrows are prefixed by either SS3 or CSI (either 7 or 8 bit), depending on
  204. whether the terminal is in application or numeric mode (ditto for PF keys):
  205.     arrows: <up> <dwn> <lft> <rgt>        A   B    D   C
  206. Additional function keys (vk201/vk401) generate CSI nn ~ (nn is 1 or 2 digits):
  207.     vk201 keys:  F6 F7 F8 F9 F10   F11 F12 F13 F14  Help Do   F17 F18 F19 F20
  208.    'nn' digits:  17 18 19 20 21    23  24  25  26    28  29   31  32  33  34
  209.      alternate:  ^C           ^[  ^H  ^J        (when in VT100 mode)
  210.    edit keypad: <fnd> <ins> <rmv>     digits:    1   2    3
  211.         <sel> <prv> <nxt>        4   5    6
  212. VT52 mode:  arrows and PF keys send ESCx where x is in A-D or P-S.
  213. =*/
  214.  
  215. static const char *arrow_or_PF = "ABCDPQRS",    /* suffix char */
  216.           *smg_keypad_codes = "PQRSpqrstuvwxyMmlnABDC";
  217.     /* PF1..PF4,KP0..KP9,enter,dash,comma,dot,up-arrow,down,left,right */
  218.     /* Ultimate return value is (index into smg_keypad_codes[] + 256). */
  219.  
  220. static short
  221. parse_function_key(c)
  222. register int c;
  223. {
  224.     struct _rd_iosb iosb;
  225.     unsigned long sts;
  226.     char seq_buf[15+1];        /* plenty room for escape sequence + slop */
  227.     short result = ESC;        /* translate to <escape> by default */
  228.  
  229.     /*
  230.      * Read whatever we can from type-ahead buffer (1 second timeout).
  231.      * If the user typed an actual <escape> to deliberately abort
  232.      * something, he or she should be able to tolerate the necessary
  233.      * restriction of a negligible pause before typing anything else.
  234.      * We might already have [at least some of] an escape sequence from a
  235.      * previous read, particularly if user holds down the arrow keys...
  236.      */
  237.     if (inc > 0) strncpy(seq_buf, inp, inc);
  238.     if (inc < sizeof seq_buf - 1) {
  239.     sts = SYS$QIOW(0, tt_chan, QIO_FUNC|IO$M_TIMED, &iosb, (void(*)())0, 0,
  240.                seq_buf + inc, sizeof seq_buf - 1 - inc, 1, 0, 0, 0);
  241.     if (vms_ok(sts))  sts = iosb.status;
  242.     } else
  243.     sts = SS$_NORMAL;
  244.     if (vms_ok(sts) || sts == SS$_TIMEOUT) {
  245.     register int cnt = iosb.trm_offset + iosb.trm_siz + inc;
  246.     register char *p = seq_buf;
  247.     if (c == ESC)    /* check for 7-bit vt100/ANSI, or vt52 */
  248.         if (*p == '[' || *p == 'O') c = META(CTRL(*p++)),  cnt--;
  249.         else if (strchr(arrow_or_PF, *p)) c = SS3; /*CSI*/
  250.     if (cnt > 0 && (c == SS3 || (c == CSI && strchr(arrow_or_PF, *p)))) {
  251.         register char *q = strchr(smg_keypad_codes, *p);
  252.         if (q) result = 256 + (q - smg_keypad_codes);
  253.         p++,  --cnt;    /* one more char consumed */
  254.     } else if (cnt > 1 && c == CSI) {
  255.         static short    /* "CSI nn ~" -> F_keys[nn] */
  256.         F_keys[35] = {    ESC,                /*(filler)*/
  257.                 311, 312, 313, 314, 315, 316,    /* E1-E6 */
  258.                 ESC, ESC, ESC, ESC,       /*(more filler)*/
  259.                 281, 282, 283, 284, 285, ESC,    /* F1-F5 */
  260.                 286, 287, 288, 289, 290, ESC,    /* F6-F10*/
  261.                 291, 292, 293, 294, ESC,    /*F11-F14*/
  262.                 295, 296, ESC, /*<help>,<do>, aka F15,F16*/
  263.                 297, 298, 299, 300        /*F17-F20*/
  264.         };  /* note: there are several missing nn in CSI nn ~ values */
  265.         int nn;  char *q;
  266.         *(p + cnt) = '\0';    /* terminate string */
  267.         q = strchr(p, '~');
  268.         if (q && sscanf(p, "%d~", &nn) == 1) {
  269.         if (nn > 0 && nn < SIZE(F_keys)) result = F_keys[nn];
  270.         cnt -= (++q - p);
  271.         p = q;
  272.         }
  273.     }
  274.     if (cnt > 0) strncpy((inp = inputbuf), p, (inc = cnt));
  275.     else         inc = 0,  inp = 0;
  276.     }
  277.     return result;
  278. }
  279. #endif    /* USE_QIO_INPUT */
  280.  
  281. static void
  282. setctty()
  283. {
  284.     struct _sm_iosb iosb;
  285.     unsigned long status;
  286.  
  287.     status = SYS$QIOW(0, tt_chan, IO$_SETMODE, &iosb, (void(*)())0, 0,
  288.               &sg.sm, sizeof sg.sm, 0, 0, 0, 0);
  289.     if (vms_ok(status))  status = iosb.status;
  290.     if (!vms_ok(status)) {
  291.     raw_print("");
  292.     errno = EVMSERR,  vaxc$errno = status;
  293.     perror("NetHack(setctty: setmode)");
  294.     wait_synch();
  295.     }
  296. }
  297.  
  298. static void
  299. resettty(){            /* atexit() routine */
  300.     if (settty_needed) {
  301.     bombing = TRUE;     /* don't clear screen; preserve traceback info */
  302.     settty((char *)NULL);
  303.     }
  304.     (void) SYS$DASSGN(tt_chan),  tt_chan = 0;
  305. }
  306.  
  307. /*
  308.  * Get initial state of terminal, set ospeed (for termcap routines)
  309.  * and switch off tab expansion if necessary.
  310.  * Called by init_nhwindows() and resume_nhwindows() in wintty.c
  311.  * (for initial startup and for returning from '!' or ^Z).
  312.  */
  313. void
  314. gettty()
  315. {
  316.     static $DESCRIPTOR(tty_dsc, "TT:");
  317.     int err = 0;
  318.     unsigned long status, zero = 0;
  319.  
  320.     if (tt_chan == 0) {        /* do this stuff once only */
  321.     flags.cbreak = OFF,  flags.echo = ON;    /* until setup is complete */
  322.     status = SYS$ASSIGN(&tty_dsc, &tt_chan, 0, 0);
  323.     if (!vms_ok(status)) {
  324.         raw_print(""),  err++;
  325.         errno = EVMSERR,  vaxc$errno = status;
  326.         perror("NetHack(gettty: $assign)");
  327.     }
  328.     atexit(resettty);   /* register an exit handler to reset things */
  329.     }
  330.     status = SYS$QIOW(0, tt_chan, IO$_SENSEMODE, &sg.io, (void(*)())0, 0,
  331.               &sg.sm, sizeof sg.sm, 0, 0, 0, 0);
  332.     if (vms_ok(status))  status = sg.io.status;
  333.     if (!vms_ok(status)) {
  334.     raw_print(""),  err++;
  335.     errno = EVMSERR,  vaxc$errno = status;
  336.     perror("NetHack(gettty: sensemode)");
  337.     }
  338.     ospeed = sg.io.xmt_speed;
  339.     erase_char = '\177';    /* <rubout>, aka <delete> */
  340.     kill_char = CTRL('U');
  341.     intr_char = CTRL('C');
  342.     (void) LIB$ENABLE_CTRL(&zero, &ctrl_mask);
  343.     /* Use the systems's values for lines and columns if it has any idea. */
  344.     if (sg.sm.page_length)
  345.     LI = sg.sm.page_length;
  346.     if (sg.sm.page_width)
  347.     CO = sg.sm.page_width;
  348.     /* Determine whether TTDRIVER is doing tab and/or form-feed expansion;
  349.        if so, we want to suppress that but also restore it at final exit. */
  350.     if ((sg.sm.tt_char & TT_SPECIAL_HANDLING) != TT_SPECIAL_HANDLING
  351.      && (sg.sm.tt2_char & TT2_SPECIAL_HANDLING) != TT2_SPECIAL_HANDLING) {
  352.     tt_char_restore  = sg.sm.tt_char;
  353.     tt_char_active     = sg.sm.tt_char |= TT_SPECIAL_HANDLING;
  354.     tt2_char_restore = sg.sm.tt2_char;
  355.     tt2_char_active  = sg.sm.tt2_char |= TT2_SPECIAL_HANDLING;
  356. #if 0        /*[ defer until setftty() ]*/
  357.     setctty();
  358. #endif
  359.     } else    /* no need to take any action */
  360.     tt_char_restore = tt_char_active = 0,
  361.     tt2_char_restore = tt2_char_active = 0;
  362.     if (err) wait_synch();
  363. }
  364.  
  365. /* reset terminal to original state */
  366. void
  367. settty(s)
  368. const char *s;
  369. {
  370.     if (!bombing) {
  371.         end_screen();
  372.         if (s) raw_print(s);
  373.     }
  374.     disable_broadcast_trapping();
  375. #if 0        /* let SMG's exit handler do the cleanup (as per doc) */
  376. /* #ifndef USE_QIO_INPUT */
  377.     if (kb)  SMG$DELETE_VIRTUAL_KEYBOARD(&kb),  kb = 0;
  378. #endif    /* 0 (!USE_QIO_INPUT) */
  379.     if (ctrl_mask)
  380.         (void) LIB$ENABLE_CTRL(&ctrl_mask, 0);
  381.     flags.echo = ON;
  382.     flags.cbreak = OFF;
  383.     if (tt_char_restore != 0 || tt2_char_restore != 0) {
  384.         sg.sm.tt_char  = tt_char_restore;
  385.         sg.sm.tt2_char = tt2_char_restore;
  386.         setctty();
  387.     }
  388.     settty_needed = FALSE;
  389. }
  390.  
  391. /* same as settty, with no clearing of the screen */
  392. void
  393. shuttty(s)
  394. const char *s;
  395. {
  396.     if(s) raw_print(s);
  397.     bombing = TRUE;
  398.     settty(s);
  399.     bombing = FALSE;
  400. }
  401.  
  402. void
  403. setftty()
  404. {
  405.     unsigned long mask = LIB$M_CLI_CTRLT | LIB$M_CLI_CTRLY;
  406.  
  407.     (void) LIB$DISABLE_CTRL(&mask, 0);
  408.     if (kb == 0) {        /* do this stuff once only */
  409. #ifdef USE_QIO_INPUT
  410.         kb = tt_chan;
  411. #else   /*!USE_QIO_INPUT*/
  412.         SMG$CREATE_VIRTUAL_KEYBOARD(&kb);
  413. #endif  /*USE_QIO_INPUT*/
  414.         init_broadcast_trapping();
  415.     }
  416.     enable_broadcast_trapping();    /* no-op if !defined(MAIL) */
  417.     flags.cbreak = (kb != 0) ? ON : OFF;
  418.     flags.echo   = (kb != 0) ? OFF : ON;
  419.     /* disable tab & form-feed expansion */
  420.     if (tt_char_active != 0 || tt2_char_active != 0) {
  421.         sg.sm.tt_char  = tt_char_active;
  422.         sg.sm.tt2_char = tt2_char_active;
  423.         setctty();
  424.     }
  425.     start_screen();
  426.     settty_needed = TRUE;
  427. }
  428.  
  429. void
  430. intron()        /* enable kbd interupts if enabled when game started */
  431. {
  432.     intr_char = CTRL('C');
  433. }
  434.  
  435. void
  436. introff()        /* disable kbd interrupts if required*/
  437. {
  438.     intr_char = 0;
  439. }
  440.  
  441.  
  442. /* fatal error */
  443. /*VARARGS1*/
  444. void
  445. error VA_DECL(const char *,s)
  446.     VA_START(s);
  447.     VA_INIT(s, const char *);
  448.     if(settty_needed)
  449.         settty(NULL);
  450.     Vprintf(s,VA_ARGS);
  451.     (void) putchar('\n');
  452.     VA_END();
  453.     exit(1);
  454. }
  455.