From: jwalz Date: Sat, 5 Jan 2002 21:06:00 +0000 (+0000) Subject: *** empty log message *** X-Git-Tag: MOVE2GIT~3636 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a3b67bf09de34a0d00926876e517980e35ca9516;p=nethack *** empty log message *** --- diff --git a/sys/vms/vmstty.c b/sys/vms/vmstty.c new file mode 100644 index 000000000..3288e6379 --- /dev/null +++ b/sys/vms/vmstty.c @@ -0,0 +1,481 @@ +/* SCCS Id: @(#)vmstty.c 3.3 1995/07/09 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ +/* tty.c - (VMS) version */ + +#define NEED_VARARGS +#include "hack.h" +#include "wintty.h" +#include "tcap.h" + +#include +#include +#ifndef __GNUC__ +#include +#include +#include +#else /* values needed from missing include files */ +# define SMG$K_TRM_UP 274 +# define SMG$K_TRM_DOWN 275 +# define SMG$K_TRM_LEFT 276 +# define SMG$K_TRM_RIGHT 277 +# define TT$M_MECHTAB 0x00000100 /* hardware tab support */ +# define TT$M_MECHFORM 0x00080000 /* hardware form-feed support */ +# define TT$M_NOBRDCST 0x00020000 /* disable broadcast messages, but */ +# define TT2$M_BRDCSTMBX 0x00000010 /* catch them in associated mailbox */ +# define TT2$M_APP_KEYPAD 0x00800000 /* application vs numeric keypad mode */ +#endif /* __GNUC__ */ +#ifdef USE_QIO_INPUT +#include +#endif +#include +#include + +unsigned long lib$disable_ctrl(), lib$enable_ctrl(); +unsigned long sys$assign(), sys$dassgn(), sys$qiow(); +#ifndef USE_QIO_INPUT +unsigned long smg$create_virtual_keyboard(), smg$delete_virtual_keyboard(), + smg$read_keystroke(), smg$cancel_input(); +#else +static short FDECL(parse_function_key, (int)); +#endif +static void NDECL(setctty); +static void NDECL(resettty); + +#define vms_ok(sts) ((sts)&1) +#define META(c) ((c)|0x80) /* 8th bit */ +#define CTRL(c) ((c)&0x1F) +#define CMASK(c) (1< 0) { + /* we have buffered character(s) from previous read */ + kb_buf = *inp++; + --inc; + sts = SS$_NORMAL; + } else { + sts = sys$qiow(0, tt_chan, QIO_FUNC, &iosb, (void(*)())0, 0, + &kb_buf, sizeof kb_buf, 0, 0, 0, 0); + } + if (vms_ok(sts)) { + if (kb_buf == CTRL('C')) { + if (intr_char) gsignal(SIGINT); + key = (short)kb_buf; + } else if (kb_buf == '\r') { /* */ + key = (short)'\n'; + } else if (kb_buf == ESC || kb_buf == CSI || kb_buf == SS3) { + switch(parse_function_key((int)kb_buf)) { + case SMG$K_TRM_UP: key = iflags.num_pad ? '8' : 'k'; break; + case SMG$K_TRM_DOWN: key = iflags.num_pad ? '2' : 'j'; break; + case SMG$K_TRM_LEFT: key = iflags.num_pad ? '4' : 'h'; break; + case SMG$K_TRM_RIGHT: key = iflags.num_pad ? '6' : 'l'; break; + default: key = ESC; break; + } + } else { + key = (short)kb_buf; + } + } else if (sts == SS$_HANGUP || iosb.status == SS$_HANGUP + || sts == SS$_DEVOFFLINE) { + gsignal(SIGHUP); + key = ESC; + } else /*(this should never happen)*/ + key = getchar(); + +#else /*!USE_QIO_INPUT*/ + static volatile int recurse = 0; /* SMG is not AST re-entrant! */ + + if (recurse++ == 0 && kb != 0) { + smg$read_keystroke(&kb, &key); + switch (key) { + case SMG$K_TRM_UP: iflags.num_pad ? '8' : key = 'k'; break; + case SMG$K_TRM_DOWN: iflags.num_pad ? '2' : key = 'j'; break; + case SMG$K_TRM_LEFT: iflags.num_pad ? '4' : key = 'h'; break; + case SMG$K_TRM_RIGHT: iflags.num_pad ? '6' : key = 'l'; break; + case '\r': key = '\n'; break; + default: if (key > 255) key = ESC; + break; + } + } else { + /* abnormal input--either SMG didn't initialize properly or + vms_getchar() has been called recursively (via SIGINT handler). + */ + if (kb != 0) /* must have been a recursive call */ + smg$cancel_input(&kb); /* from an interrupt handler */ + key = getchar(); + } + --recurse; +#endif /* USE_QIO_INPUT */ + + return (int)key; +} + +#ifdef USE_QIO_INPUT + /* + * We've just gotten an character. Do a timed read to + * get any other characters, then try to parse them as an escape + * sequence. This isn't perfect, since there's no guarantee + * that a full escape sequence will be available, or even if one + * is, it might actually by regular input from a fast typist or + * a stalled input connection. {For packetized environments, + * cross plural(body_part(FINGER)) and hope for best. :-} + * + * This is needed to preserve compatability with SMG interface + * for two reasons: + * 1) retain support for arrow keys, and + * 2) treat other VTxxx function keys as for aborting + * various NetHack prompts. + * The second reason is compelling; otherwise remaining chars of + * an escape sequence get treated as inappropriate user commands. + * + * SMG code values for these key sequences fall in the range of + * 256 thru 3xx. The assignments are not particularly intuitive. + */ +/*= + -- Summary of VTxxx-style keyboards and transmitted escape sequences. -- +Keypad codes are prefixed by 7 bit (\033 O) or 8 bit SS3: + keypad: PF1 PF2 PF3 PF4 codes: P Q R S + 7 8 9 - w x y m + 4 5 6 . t u v n + 1 2 3 :en-: q r s : : + ...0... , :ter: ...p... l :M: +Arrows are prefixed by either SS3 or CSI (either 7 or 8 bit), depending on +whether the terminal is in application or numeric mode (ditto for PF keys): + arrows: A B D C +Additional function keys (vk201/vk401) generate CSI nn ~ (nn is 1 or 2 digits): + vk201 keys: F6 F7 F8 F9 F10 F11 F12 F13 F14 Help Do F17 F18 F19 F20 + 'nn' digits: 17 18 19 20 21 23 24 25 26 28 29 31 32 33 34 + alternate: ^C ^[ ^H ^J (when in VT100 mode) + edit keypad: digits: 1 2 3 + 4 5 6 +VT52 mode: arrows and PF keys send ESCx where x is in A-D or P-S. +=*/ + +static const char *arrow_or_PF = "ABCDPQRS", /* suffix char */ + *smg_keypad_codes = "PQRSpqrstuvwxyMmlnABDC"; + /* PF1..PF4,KP0..KP9,enter,dash,comma,dot,up-arrow,down,left,right */ + /* Ultimate return value is (index into smg_keypad_codes[] + 256). */ + +static short +parse_function_key(c) +register int c; +{ + struct _rd_iosb iosb; + unsigned long sts; + char seq_buf[15+1]; /* plenty room for escape sequence + slop */ + short result = ESC; /* translate to by default */ + + /* + * Read whatever we can from type-ahead buffer (1 second timeout). + * If the user typed an actual to deliberately abort + * something, he or she should be able to tolerate the necessary + * restriction of a negligible pause before typing anything else. + * We might already have [at least some of] an escape sequence from a + * previous read, particularly if user holds down the arrow keys... + */ + if (inc > 0) strncpy(seq_buf, inp, inc); + if (inc < (int)(sizeof seq_buf) - 1) { + sts = sys$qiow(0, tt_chan, QIO_FUNC|IO$M_TIMED, &iosb, (void(*)())0, 0, + seq_buf + inc, sizeof seq_buf - 1 - inc, 1, 0, 0, 0); + if (vms_ok(sts)) sts = iosb.status; + } else + sts = SS$_NORMAL; + if (vms_ok(sts) || sts == SS$_TIMEOUT) { + register int cnt = iosb.trm_offset + iosb.trm_siz + inc; + register char *p = seq_buf; + if (c == ESC) /* check for 7-bit vt100/ANSI, or vt52 */ + if (*p == '[' || *p == 'O') c = META(CTRL(*p++)), cnt--; + else if (strchr(arrow_or_PF, *p)) c = SS3; /*CSI*/ + if (cnt > 0 && (c == SS3 || (c == CSI && strchr(arrow_or_PF, *p)))) { + register char *q = strchr(smg_keypad_codes, *p); + if (q) result = 256 + (q - smg_keypad_codes); + p++, --cnt; /* one more char consumed */ + } else if (cnt > 1 && c == CSI) { + static short /* "CSI nn ~" -> F_keys[nn] */ + F_keys[35] = { ESC, /*(filler)*/ + 311, 312, 313, 314, 315, 316, /* E1-E6 */ + ESC, ESC, ESC, ESC, /*(more filler)*/ + 281, 282, 283, 284, 285, ESC, /* F1-F5 */ + 286, 287, 288, 289, 290, ESC, /* F6-F10*/ + 291, 292, 293, 294, ESC, /*F11-F14*/ + 295, 296, ESC, /*,, aka F15,F16*/ + 297, 298, 299, 300 /*F17-F20*/ + }; /* note: there are several missing nn in CSI nn ~ values */ + int nn; char *q; + *(p + cnt) = '\0'; /* terminate string */ + q = strchr(p, '~'); + if (q && sscanf(p, "%d~", &nn) == 1) { + if (nn > 0 && nn < SIZE(F_keys)) result = F_keys[nn]; + cnt -= (++q - p); + p = q; + } + } + if (cnt > 0) strncpy((inp = inputbuf), p, (inc = cnt)); + else inc = 0, inp = 0; + } + return result; +} +#endif /* USE_QIO_INPUT */ + +static void +setctty() +{ + struct _sm_iosb iosb; + unsigned long status; + + status = sys$qiow(0, tt_chan, IO$_SETMODE, &iosb, (void(*)())0, 0, + &sg.sm, sizeof sg.sm, 0, 0, 0, 0); + if (vms_ok(status)) status = iosb.status; + if (vms_ok(status)) { + /* try to force terminal into synch with TTDRIVER's setting */ + number_pad((sg.sm.tt2_char & TT2$M_APP_KEYPAD) ? -1 : 1); + } else { + raw_print(""); + errno = EVMSERR, vaxc$errno = status; + perror("NetHack(setctty: setmode)"); + wait_synch(); + } +} + +static void +resettty() /* atexit() routine */ +{ + if (settty_needed) { + bombing = TRUE; /* don't clear screen; preserve traceback info */ + settty((char *)0); + } + (void) sys$dassgn(tt_chan), tt_chan = 0; +} + +/* + * Get initial state of terminal, set ospeed (for termcap routines) + * and switch off tab expansion if necessary. + * Called by init_nhwindows() and resume_nhwindows() in wintty.c + * (for initial startup and for returning from '!' or ^Z). + */ +void +gettty() +{ + static char dev_tty[] = "TT:"; + static $DESCRIPTOR(tty_dsc, dev_tty); + int err = 0; + unsigned long status, zero = 0; + + if (tt_chan == 0) { /* do this stuff once only */ + iflags.cbreak = OFF, iflags.echo = ON; /* until setup is complete */ + status = sys$assign(&tty_dsc, &tt_chan, 0, 0); + if (!vms_ok(status)) { + raw_print(""), err++; + errno = EVMSERR, vaxc$errno = status; + perror("NetHack(gettty: $assign)"); + } + atexit(resettty); /* register an exit handler to reset things */ + } + status = sys$qiow(0, tt_chan, IO$_SENSEMODE, &sg.io, (void(*)())0, 0, + &sg.sm, sizeof sg.sm, 0, 0, 0, 0); + if (vms_ok(status)) status = sg.io.status; + if (!vms_ok(status)) { + raw_print(""), err++; + errno = EVMSERR, vaxc$errno = status; + perror("NetHack(gettty: sensemode)"); + } + ospeed = sg.io.xmt_speed; + erase_char = '\177'; /* , aka */ + kill_char = CTRL('U'); + intr_char = CTRL('C'); + (void) lib$enable_ctrl(&zero, &ctrl_mask); + /* Use the systems's values for lines and columns if it has any idea. */ + if (sg.sm.page_length) + LI = sg.sm.page_length; + if (sg.sm.page_width) + CO = sg.sm.page_width; + /* suppress tab and form-feed expansion, in case termcap uses them */ + tt_char_restore = sg.sm.tt_char; + tt_char_active = sg.sm.tt_char |= TT_SPECIAL_HANDLING; + tt2_char_restore = sg.sm.tt2_char; + tt2_char_active = sg.sm.tt2_char |= TT2_SPECIAL_HANDLING; +#if 0 /*[ defer until setftty() ]*/ + setctty(); +#endif + + if (err) wait_synch(); +} + +/* reset terminal to original state */ +void +settty(s) +const char *s; +{ + if (!bombing) end_screen(); + if (s) raw_print(s); + disable_broadcast_trapping(); +#if 0 /* let SMG's exit handler do the cleanup (as per doc) */ +/* #ifndef USE_QIO_INPUT */ + if (kb) smg$delete_virtual_keyboard(&kb), kb = 0; +#endif /* 0 (!USE_QIO_INPUT) */ + if (ctrl_mask) + (void) lib$enable_ctrl(&ctrl_mask, 0); + iflags.echo = ON; + iflags.cbreak = OFF; + /* reset original tab, form-feed, broadcast settings */ + sg.sm.tt_char = tt_char_restore; + sg.sm.tt2_char = tt2_char_restore; + setctty(); + + settty_needed = FALSE; +} + +/* same as settty, with no clearing of the screen */ +void +shuttty(s) +const char *s; +{ + bombing = TRUE; + settty(s); + bombing = FALSE; +} + +void +setftty() +{ + unsigned long mask = LIB$M_CLI_CTRLT | LIB$M_CLI_CTRLY; + + (void) lib$disable_ctrl(&mask, 0); + if (kb == 0) { /* do this stuff once only */ +#ifdef USE_QIO_INPUT + kb = tt_chan; +#else /*!USE_QIO_INPUT*/ + smg$create_virtual_keyboard(&kb); +#endif /*USE_QIO_INPUT*/ + init_broadcast_trapping(); + } + enable_broadcast_trapping(); /* no-op if !defined(MAIL) */ + iflags.cbreak = (kb != 0) ? ON : OFF; + iflags.echo = (kb != 0) ? OFF : ON; + /* disable tab & form-feed expansion; prepare for broadcast trapping */ + sg.sm.tt_char = tt_char_active; + sg.sm.tt2_char = tt2_char_active; + setctty(); + + start_screen(); + settty_needed = TRUE; +} + +void +intron() /* enable kbd interupts if enabled when game started */ +{ + intr_char = CTRL('C'); +} + +void +introff() /* disable kbd interrupts if required*/ +{ + intr_char = 0; +} + +#ifdef TIMED_DELAY + +extern unsigned long + FDECL(lib$emul, (const long *,const long *,const long *,long *)); +extern unsigned long sys$schdwk(), sys$hiber(); + +#define VMS_UNITS_PER_SECOND 10000000L /* hundreds of nanoseconds, 1e-7 */ +/* constant for conversion from milliseconds to VMS delta time (negative) */ +static const long mseconds_to_delta = VMS_UNITS_PER_SECOND / 1000L * -1L; + +/* sleep for specified number of milliseconds (note: the timer used + generally only has 10-millisecond resolution at the hardware level...) */ +void msleep(mseconds) +unsigned mseconds; /* milliseconds */ +{ + long pid = 0L, zero = 0L, msec, qtime[2]; + + msec = (long) mseconds; + if (msec > 0 && + /* qtime{0:63} = msec{0:31} * mseconds_to_delta{0:31} + zero{0:31} */ + vms_ok(lib$emul(&msec, &mseconds_to_delta, &zero, qtime))) { + /* schedule a wake-up call, then go to sleep */ + if (vms_ok(sys$schdwk(&pid, (genericptr_t)0, qtime, (long *)0))) + (void)sys$hiber(); + } +} + +#endif /* TIMED_DELAY */ + + +/* fatal error */ +/*VARARGS1*/ +void +error VA_DECL(const char *,s) + VA_START(s); + VA_INIT(s, const char *); + if(settty_needed) + settty((char *)0); + Vprintf(s,VA_ARGS); + (void) putchar('\n'); + VA_END(); + exit(EXIT_FAILURE); +}