1 /*
2 * VMS terminal driver
3 *
4 * Uses SMG to lookup capabilities of the terminal (see SYS$SYSTEM:SMGTERMS.TXT
5 * and SYS$SYSTEM:TERMTABLE.TXT)
6 *
7 * Originally written by Curtis Smith in 1987, this module has been rewritten
8 * by Tom Dickey and Clark Morgan to:
9 *
10 * -- simplify it,
11 * -- extend it to support a wider variety of terminal types,
12 * -- add VT220 keyboard mappings,
13 * -- support syntax highlighting,
14 * -- support wide and narrow screen resolutions,
15 * -- support visual bells.
16 *
17 * $Id: vmsvt.c,v 1.69 2010/02/14 18:43:48 tom Exp $
18 *
19 */
20
21 #include "estruct.h" /* Emacs' structures */
22 #include "edef.h" /* Emacs' definitions */
23 #include "nefunc.h" /* f_backchar_to_bol */
24
25 #if DISP_VMSVT
26
27 #include <descrip.h> /* Descriptor definitions */
28 #include <iodef.h> /* to get IO$_SENSEMODE */
29 #include <ttdef.h> /* to get TT$_UNKNOWN */
30 #include <stsdef.h> /* to get $VMS_STATUS_SUCCESS */
31
32 #include <starlet.h>
33 #include <smgtrmptr.h> /* to get SMG$K_??? definitions */
34 #include <smg$routines.h>
35 #include <ssdef.h>
36
37 #include <descrip.h>
38 #include <iodef.h>
39 #include <ttdef.h>
40 #include <tt2def.h>
41
42 /* function prototypes needed for the dispatch table */
43 static OUTC_DCL vmsvt_putc(int c);
44 static int vmsvt_typahead(void);
45 static void vmsvt_flush(void);
46 static void vmsvt_scrollregion(int top, int bot);
47 static void vmsvt_scroll_reg(int from, int to, int n);
48 static void vmsvt_scroll_delins(int from, int to, int n);
49
50 /* SMG stuff (just like termcap) */
51 static int already_open;
52 static int termtype;
53 static char *tc_SO; /* begin standout (reverse) */
54 static char *tc_SE; /* end standout (reverse) */
55 static char *erase_to_end_line;
56 static char *erase_whole_display;
57
58 static char *delete_line;
59 static char *insert_line;
60 static char *scroll_forw;
61 static char *scroll_back;
62 static char *scroll_regn;
63
64 static char *set_narrow;
65 static char *set_wide;
66
67 static long wide_cols;
68 static long narrow_cols;
69
70 #if OPT_VIDEO_ATTRS
71 static char *tc_MD; /* begin bold */
72 static char *tc_ME; /* end bold */
73 static char *tc_US; /* begin underscore */
74 static char *tc_UE; /* end underscore */
75 #endif
76
77 #if OPT_FLASH
78 static char *dark_on;
79 static char *dark_off;
80 #endif
81 /* *INDENT-OFF* */
82 struct vmskeyseqs {
83 char *seq;
84 int code;
85 };
86 static struct vmskeyseqs vt100seqs[] =
87 {
88 { "\033[A", KEY_Up },
89 { "\033[B", KEY_Down },
90 { "\033[C", KEY_Right },
91 { "\033[D", KEY_Left },
92 { "\033OP", KEY_KP_F1 },
93 { "\033OQ", KEY_KP_F2 },
94 { "\033OR", KEY_KP_F3 },
95 { "\033OS", KEY_KP_F4 },
96 /* these are transmitted by xterm and similar programs */
97 { "\033[11~", KEY_F1 },
98 { "\033[12~", KEY_F2 },
99 { "\033[13~", KEY_F3 },
100 { "\033[14~", KEY_F4 },
101 { "\033[15~", KEY_F5 },
102 /* vt220 keys (F1-F5 do not transmit control sequences) include vt100 */
103 { "\033[17~", KEY_F6 },
104 { "\033[18~", KEY_F7 },
105 { "\033[19~", KEY_F8 },
106 { "\033[20~", KEY_F9 },
107 { "\033[21~", KEY_F10 },
108 { "\033[23~", KEY_F11 },
109 { "\033[24~", KEY_F12 },
110 { "\033[25~", KEY_F13 },
111 { "\033[26~", KEY_F14 },
112 { "\033[28~", KEY_F15 },
113 { "\033[29~", KEY_F16 },
114 { "\033[31~", KEY_F17 },
115 { "\033[32~", KEY_F18 },
116 { "\033[33~", KEY_F19 },
117 { "\033[34~", KEY_F20 },
118 { "\033[3~", KEY_Delete }, /* aka "Remove" */
119 { "\033[6~", KEY_Next },
120 { "\033[5~", KEY_Prior }, /* aka "Prev" */
121 { "\033[1~", KEY_Find },
122 { "\033[2~", KEY_Insert },
123 { "\033[4~", KEY_Select },
124 /* 8-bit codes are the same, substituting the first two characters */
125 { "\233A", KEY_Up },
126 { "\233B", KEY_Down },
127 { "\233C", KEY_Right },
128 { "\233D", KEY_Left },
129 { "\217P", KEY_KP_F1 },
130 { "\217Q", KEY_KP_F2 },
131 { "\217R", KEY_KP_F3 },
132 { "\217S", KEY_KP_F4 },
133 /* these are transmitted by xterm */
134 { "\233[11~", KEY_F1 },
135 { "\233[12~", KEY_F2 },
136 { "\233[13~", KEY_F3 },
137 { "\233[14~", KEY_F4 },
138 { "\233[15~", KEY_F5 },
139 /* vt220 keys (F1-F5 do not transmit control sequences) include vt100 */
140 { "\23317~", KEY_F6 },
141 { "\23318~", KEY_F7 },
142 { "\23319~", KEY_F8 },
143 { "\23320~", KEY_F9 },
144 { "\23321~", KEY_F10 },
145 { "\23323~", KEY_F11 },
146 { "\23324~", KEY_F12 },
147 { "\23325~", KEY_F13 },
148 { "\23326~", KEY_F14 },
149 { "\23328~", KEY_F15 },
150 { "\23329~", KEY_F16 },
151 { "\23331~", KEY_F17 },
152 { "\23332~", KEY_F18 },
153 { "\23333~", KEY_F19 },
154 { "\23334~", KEY_F20 },
155 { "\2333~", KEY_Delete }, /* aka "Remove" */
156 { "\2336~", KEY_Next },
157 { "\2335~", KEY_Prior }, /* aka "Prev" */
158 { "\2331~", KEY_Find },
159 { "\2332~", KEY_Insert },
160 { "\2334~", KEY_Select },
161 };
162 static struct vmskeyseqs vt52seqs[] =
163 {
164 { "\33A", KEY_Up },
165 { "\33B", KEY_Down },
166 { "\33C", KEY_Right },
167 { "\33D", KEY_Left },
168 { "\33P", KEY_KP_F1 },
169 { "\33Q", KEY_KP_F2 },
170 { "\33R", KEY_KP_F3 },
171 { "\33S", KEY_KP_F4 },
172 };
173 /* *INDENT-ON* */
174
175 typedef struct {
176 USHORT status; /* I/O completion status */
177 USHORT count; /* byte transfer count */
178 int dev_dep_data; /* device-dependent data */
179 } QIO_SB; /* This is a QIO I/O Status Block */
180
181 #define NIBUF 1024 /* Input buffer size */
182 #define NOBUF 1024 /* MM says big buffers win! */
183 #define EFN 0 /* Event flag */
184
185 static char obuf[NOBUF]; /* Output buffer */
186 static int nobuf; /* # of bytes in above */
187
188 static char ibuf[NIBUF]; /* Input buffer */
189 static int nibuf; /* # of bytes in above */
190
191 static int ibufi; /* Read index */
192
193 /*
194 * Public data also used by spawn.c
195 */
196 int oldmode[3]; /* Old TTY mode bits */
197 int newmode[3]; /* New TTY mode bits */
198 short iochan; /* TTY I/O channel */
199
200 /***
201 * Send a string to vmsvt_putc
202 *
203 * Nothing returned
204 ***/
205 static void
vmsvt_puts(char * string)206 vmsvt_puts(char *string) /* String to write */
207 {
208 if (string)
209 while (*string != EOS)
210 vmsvt_putc(*string++);
211 }
212
213 /***
214 * 2-argument request, e.g., for cursor movement.
215 *
216 * Nothing returned
217 ***/
218 static void
vmsvt_tgoto(request_code,parm1,parm2)219 vmsvt_tgoto(request_code, parm1, parm2)
220 {
221 char buffer[32];
222 int ret_length;
223 static int max_buffer_length = sizeof(buffer);
224 static int arg_list[3] =
225 {2};
226
227 register int i;
228
229 if (!already_open) {
230 printf("\n");
231 return;
232 }
233
234 /* Set the arguments into the arg_list array
235 */
236 arg_list[1] = parm1;
237 arg_list[2] = parm2;
238
239 if (!$VMS_STATUS_SUCCESS(smg$get_term_data(&termtype,
240 &request_code,
241 &max_buffer_length,
242 &ret_length,
243 buffer,
244 arg_list))) {
245 vmsvt_puts("OOPS");
246 return;
247 } else if (ret_length > 0) {
248 char *cp = buffer;
249 while (ret_length-- > 0)
250 vmsvt_putc(*cp++);
251 }
252 }
253
254 static void
vmsvt_move(int row,int col)255 vmsvt_move(int row, int col)
256 {
257 vmsvt_tgoto(SMG$K_SET_CURSOR_ABS, row + 1, col + 1);
258 }
259
260 #if OPT_VIDEO_ATTRS
261 static void
vmsvt_attr(UINT attr)262 vmsvt_attr(UINT attr)
263 {
264 #define VA_SGR (VASEL|VAREV|VAUL|VAITAL|VABOLD)
265 /* *INDENT-OFF* */
266 static const struct {
267 char **start;
268 char **end;
269 UINT mask;
270 } tbl[] = {
271 { &tc_SO, &tc_SE, VASEL|VAREV },
272 { &tc_US, &tc_UE, VAUL },
273 { &tc_US, &tc_UE, VAITAL },
274 { &tc_MD, &tc_ME, VABOLD },
275 };
276 /* *INDENT-ON* */
277
278 static UINT last;
279
280 attr = VATTRIB(attr);
281 if (attr & VASPCOL) {
282 attr = VCOLORATTR((VCOLORNUM(attr) & (NCOLORS - 1)));
283 } else {
284 attr &= ~(VAML | VAMLFOC);
285 }
286
287 if (attr != last) {
288 register size_t n;
289 register char *s;
290 UINT diff = attr ^ last;
291 int ends = FALSE;
292
293 /* turn OFF old attributes */
294 for (n = 0; n < TABLESIZE(tbl); n++) {
295 if ((tbl[n].mask & diff) != 0
296 && (tbl[n].mask & attr) == 0
297 && (s = *(tbl[n].end)) != 0) {
298 vmsvt_puts(s);
299 #if OPT_COLOR
300 if (!ends) /* do this once */
301 reinitialize_colors();
302 #endif
303 ends = TRUE;
304 diff &= ~(tbl[n].mask);
305 }
306 }
307
308 /* turn ON new attributes */
309 for (n = 0; n < TABLESIZE(tbl); n++) {
310 if ((tbl[n].mask & diff) != 0
311 && (tbl[n].mask & attr) != 0
312 && (s = *(tbl[n].start)) != 0) {
313 vmsvt_puts(s);
314 diff &= ~(tbl[n].mask);
315 }
316 }
317
318 if (tc_SO != 0 && tc_SE != 0) {
319 if (ends && (attr & (VAREV | VASEL))) {
320 vmsvt_puts(tc_SO);
321 } else if (diff & VA_SGR) { /* we didn't find it */
322 vmsvt_puts(tc_SE);
323 }
324 }
325 #if OPT_COLOR
326 if (attr & VACOLOR)
327 tcapfcol(VCOLORNUM(attr));
328 else if (given_fcolor != gfcolor)
329 tcapfcol(gfcolor);
330 #endif
331 last = attr;
332 }
333 }
334
335 #else /* highlighting is a minimum attribute */
336
337 /*
338 * Change reverse video status.
339 * FALSE = normal video, TRUE = reverse video
340 */
341 static void
tcaprev(UINT state)342 tcaprev(UINT state)
343 {
344 static int revstate = -1;
345
346 if (state == revstate)
347 return;
348 revstate = state;
349 if (state)
350 vmsvt_puts(tc_SO);
351 else
352 vmsvt_puts(tc_SE);
353 }
354
355 #endif /* OPT_VIDEO_ATTRS */
356
357 static void
vmsvt_eeol(void)358 vmsvt_eeol(void)
359 {
360 vmsvt_puts(erase_to_end_line);
361 }
362
363 static void
vmsvt_eeop(void)364 vmsvt_eeop(void)
365 {
366 vmsvt_puts(erase_whole_display);
367 }
368
369 static void
cache_capabilities(void)370 cache_capabilities(void)
371 {
372 /* *INDENT-OFF* */
373 static struct caps_struct {
374 long code;
375 char **cap_string;
376 } tbl[] = {
377 { SMG$K_BEGIN_REVERSE, &tc_SO },
378 { SMG$K_END_REVERSE, &tc_SE },
379 #if OPT_VIDEO_ATTRS
380 { SMG$K_BEGIN_BOLD, &tc_MD },
381 { SMG$K_END_BOLD, &tc_ME },
382 { SMG$K_BEGIN_UNDERSCORE, &tc_US },
383 { SMG$K_END_UNDERSCORE, &tc_UE },
384 #endif
385 #if OPT_FLASH
386 { SMG$K_DARK_SCREEN, &dark_on },
387 { SMG$K_LIGHT_SCREEN, &dark_off },
388 #endif
389 { SMG$K_ERASE_TO_END_LINE, &erase_to_end_line },
390 { SMG$K_ERASE_WHOLE_DISPLAY, &erase_whole_display },
391 { SMG$K_INSERT_LINE, &insert_line }, /* al */
392 { SMG$K_DELETE_LINE, &delete_line }, /* dl */
393 { SMG$K_SCROLL_FORWARD, &scroll_forw }, /* SF */
394 { SMG$K_SCROLL_REVERSE, &scroll_back }, /* SR */
395 { SMG$K_SET_SCROLL_REGION, &scroll_regn }, /* CS */
396 { SMG$K_WIDTH_NARROW, &set_narrow }, /* 80 columns */
397 { SMG$K_WIDTH_WIDE, &set_wide }, /* 132 columns */
398 };
399 /* *INDENT-ON* */
400
401 char *buf;
402 struct caps_struct *csp;
403 int i, buf_len, ret_len;
404 long rqst_code;
405 static char storage[1024];
406
407 for (i = 0, buf = storage, csp = tbl; i < TABLESIZE(tbl); i++, csp++) {
408 buf_len = (storage + sizeof(storage)) - buf;
409 if (!$VMS_STATUS_SUCCESS(smg$get_term_data(&termtype,
410 &csp->code,
411 &buf_len,
412 &ret_len,
413 buf)) || ret_len == 0) {
414 *(csp->cap_string) = NULL;
415 } else {
416 buf[ret_len] = 0;
417 *(csp->cap_string) = buf;
418 buf += ret_len + 1;
419 }
420 }
421 rqst_code = SMG$K_COLUMNS;
422 if (!$VMS_STATUS_SUCCESS(smg$get_numeric_data(&termtype,
423 &rqst_code,
424 &narrow_cols))) {
425 narrow_cols = 80;
426 }
427 rqst_code = SMG$K_WIDE_SCREEN_COLUMNS;
428 if (!$VMS_STATUS_SUCCESS(smg$get_numeric_data(&termtype,
429 &rqst_code,
430 &wide_cols))) {
431 wide_cols = 132;
432 }
433 term.maxcols = wide_cols;
434 }
435
436 /** I/O information block definitions **/
437 struct iosb { /* I/O status block */
438 short i_cond; /* Condition value */
439 short i_xfer; /* Transfer count */
440 long i_info; /* Device information */
441 };
442 struct termchar { /* Terminal characteristics */
443 char t_class; /* Terminal class */
444 char t_type; /* Terminal type */
445 short t_width; /* Terminal width in characters */
446 long t_mandl; /* Terminal's mode and length */
447 long t_extend; /* Extended terminal characteristics */
448 };
449 static struct termchar tc; /* Terminal characteristics */
450
451 static void
get_terminal_type(void)452 get_terminal_type(void)
453 {
454 short fd;
455 int status, deassign_status;
456 struct iosb iostatus;
457 $DESCRIPTOR(devnam, "SYS$COMMAND");
458
459 /* Assign input to a channel */
460 if (!$VMS_STATUS_SUCCESS(status = sys$assign(&devnam, &fd, 0, 0)))
461 tidy_exit(status);
462
463 /* Get terminal characteristics */
464 status = sys$qiow(0, /* Wait on event flag zero */
465 fd, /* Channel to input terminal */
466 IO$_SENSEMODE, /* Get current characteristic */
467 &iostatus, /* Status after operation */
468 0, 0, /* No AST service */
469 &tc, /* Terminal characteristics buf */
470 sizeof(tc), /* Size of the buffer */
471 0, 0, 0, 0); /* P3-P6 unused */
472
473 /* De-assign the input device */
474 if (!$VMS_STATUS_SUCCESS(deassign_status = sys$dassgn(fd)))
475 tidy_exit(deassign_status);
476
477 /* Jump out if bad qiow status */
478 if (!$VMS_STATUS_SUCCESS(status))
479 tidy_exit(status);
480 if ((iostatus.i_cond & 1) == 0)
481 tidy_exit(iostatus.i_cond);
482 }
483
484 static void
vmsvt_open(void)485 vmsvt_open(void)
486 {
487 QIO_SB iosb;
488 int status;
489 $DESCRIPTOR(odsc, "SYS$COMMAND");
490
491 int i, keyseq_tablesize;
492 struct vmskeyseqs *keyseqs;
493
494 if (!already_open) {
495 already_open = TRUE;
496
497 get_terminal_type();
498 if (tc.t_type == TT$_UNKNOWN) {
499 printf("Terminal type is unknown!\n");
500 printf("Set your terminal type using $ SET TERMINAL/INQUIRE\n");
501 tidy_exit(3);
502 }
503 if (tc.t_type != TT$_VT52) {
504 keyseqs = vt100seqs;
505 keyseq_tablesize = TABLESIZE(vt100seqs);
506 } else {
507 keyseqs = vt52seqs;
508 keyseq_tablesize = TABLESIZE(vt52seqs);
509 }
510
511 /* Access the system terminal definition table for the */
512 /* information of the terminal type returned by IO$_SENSEMODE */
513 if (!$VMS_STATUS_SUCCESS(smg$init_term_table_by_type(&tc.t_type, &termtype)))
514 return;
515
516 /* Set sizes */
517 term.rows = ((UINT) tc.t_mandl >> 24);
518 term.cols = tc.t_width;
519
520 if (term.maxrows < term.rows)
521 term.maxrows = term.rows;
522
523 if (term.maxcols < term.cols)
524 term.maxcols = term.cols;
525
526 cache_capabilities();
527
528 revexist = (tc_SO != NULL && tc_SE != NULL);
529 eolexist = erase_whole_display != NULL;
530
531 /*
532 * I tried 'vmsgetstr()' for a VT100 terminal and it had no codes
533 * for insert_line or for delete_line. (Have to work up a test for
534 * that) - TD
535 */
536
537 if (scroll_regn && scroll_back) {
538 if (scroll_forw == NULL) /* assume '\n' scrolls forward */
539 scroll_forw = "\n";
540 term.scroll = vmsvt_scroll_reg;
541 } else if (delete_line && insert_line)
542 term.scroll = vmsvt_scroll_delins;
543 else
544 term.scroll = nullterm_scroll;
545
546 /* Set resolution */
547 (void) strcpy(screen_desc,
548 (term.cols == narrow_cols)
549 ? "NORMAL"
550 : "WIDE");
551
552 /* Open terminal I/O drivers */
553 status = sys$assign(&odsc, &iochan, 0, 0);
554 if (status != SS$_NORMAL)
555 tidy_exit(status);
556 status = sys$qiow(EFN, iochan, IO$_SENSEMODE, &iosb, 0, 0,
557 oldmode, sizeof(oldmode), 0, 0, 0, 0);
558 if (status != SS$_NORMAL
559 || iosb.status != SS$_NORMAL)
560 tidy_exit(status);
561 newmode[0] = oldmode[0];
562 newmode[1] = oldmode[1];
563 newmode[1] |= TT$M_NOBRDCST; /* turn on no-broadcast */
564 newmode[1] &= ~TT$M_TTSYNC;
565 newmode[1] &= ~TT$M_ESCAPE; /* turn off escape-processing */
566 newmode[1] &= ~TT$M_HOSTSYNC;
567 newmode[1] &= ~TT$M_NOTYPEAHD; /* turn off no-typeahead */
568 newmode[2] = oldmode[2];
569 newmode[2] |= TT2$M_PASTHRU; /* turn on pass-through */
570 newmode[2] |= TT2$M_ALTYPEAHD; /* turn on big typeahead buffer */
571 status = sys$qiow(EFN, iochan, IO$_SETMODE, &iosb, 0, 0,
572 newmode, sizeof(newmode), 0, 0, 0, 0);
573 if (status != SS$_NORMAL
574 || iosb.status != SS$_NORMAL)
575 tidy_exit(status);
576 term.rows = (newmode[1] >> 24);
577 term.cols = newmode[0] >> 16;
578
579 /* make sure backspace is bound to backspace */
580 asciitbl[backspc] = &f_backchar_to_bol;
581
582 /* Set predefined keys */
583 for (i = keyseq_tablesize; i--;)
584 addtosysmap(keyseqs[i].seq, strlen(keyseqs[i].seq), keyseqs[i].code);
585 }
586 }
587
588 /* copied/adapted from 'tcap.c' 19-apr-1993 dickey@software.org */
589
590 /* move howmany lines starting at from to to */
591 static void
vmsvt_scroll_reg(int from,int to,int n)592 vmsvt_scroll_reg(int from, int to, int n)
593 {
594 int i;
595 if (to == from)
596 return;
597 if (to < from) {
598 vmsvt_scrollregion(to, from + n - 1);
599 vmsvt_move(from + n - 1, 0);
600 for (i = from - to; i > 0; i--)
601 vmsvt_puts(scroll_forw);
602 } else { /* from < to */
603 vmsvt_scrollregion(from, to + n - 1);
604 vmsvt_move(from, 0);
605 for (i = to - from; i > 0; i--)
606 vmsvt_puts(scroll_back);
607 }
608 vmsvt_scrollregion(0, term.rows - 1);
609 }
610
611 /*
612 OPT_PRETTIER_SCROLL is prettier but slower -- it scrolls
613 a line at a time instead of all at once.
614 */
615
616 /* move howmany lines starting at from to to */
617 static void
vmsvt_scroll_delins(int from,int to,int n)618 vmsvt_scroll_delins(int from, int to, int n)
619 {
620 int i;
621 if (to == from)
622 return;
623 /* patch: should make this more like 'tcap.c', or merge logic somehow */
624 #if OPT_PRETTIER_SCROLL
625 if (absol(from - to) > 1) {
626 vmsvt_scroll_delins(from, (from < to) ? to - 1 : to + 1, n);
627 if (from < to)
628 from = to - 1;
629 else
630 from = to + 1;
631 }
632 #endif
633 if (to < from) {
634 vmsvt_move(to, 0);
635 for (i = from - to; i > 0; i--)
636 vmsvt_puts(delete_line);
637 vmsvt_move(to + n, 0);
638 for (i = from - to; i > 0; i--)
639 vmsvt_puts(insert_line);
640 } else {
641 vmsvt_move(from + n, 0);
642 for (i = to - from; i > 0; i--)
643 vmsvt_puts(delete_line);
644 vmsvt_move(from, 0);
645 for (i = to - from; i > 0; i--)
646 vmsvt_puts(insert_line);
647 }
648 }
649
650 /* cs is set up just like cm, so we use tgoto... */
651 static void
vmsvt_scrollregion(int top,int bot)652 vmsvt_scrollregion(int top, int bot)
653 {
654 vmsvt_tgoto(SMG$K_SET_SCROLL_REGION, top + 1, bot + 1);
655 }
656
657 /***
658 * Change screen resolution
659 *
660 * support these values: WIDE -> 132 columns, NORMAL -> 80 columns
661 *
662 * T -> if resolution successfully changed, F otherwise.
663 ***/
664 static int
vmsvt_cres(const char * res)665 vmsvt_cres(const char *res)
666 {
667 char buf[NLINE];
668 int rc = FALSE;
669
670 if (tc.t_type == TT$_VT52) {
671 mlforce("[sres not supported for VT52-style terminals]");
672 return (rc);
673 }
674
675 strcpy(buf, res);
676 mkupper(buf);
677 if (strcmp(buf, "WIDE") == 0 && set_wide != 0) {
678 vmsvt_puts(set_wide);
679 term.cols = wide_cols;
680 rc = TRUE;
681 } else if (strcmp(buf, "NORMAL") == 0 && set_narrow != 0) {
682 vmsvt_puts(set_narrow);
683 term.cols = narrow_cols;
684 rc = TRUE;
685 } else
686 mlforce("[invalid sres value (use NORMAL or WIDE)]");
687 if (rc)
688 newwidth(1, term.cols);
689 return (rc);
690 }
691
692 static void
vmsvt_close(void)693 vmsvt_close(void)
694 {
695 if (already_open) {
696 /*
697 * Note: this code used to check for errors when closing the output,
698 * but it didn't work properly (left the screen set in 1-line mode)
699 * when I was running as system manager, so I took out the error
700 * checking -- T.Dickey 94/7/15.
701 */
702 int status;
703 QIO_SB iosb;
704
705 if (tc.t_type != TT$_VT52) {
706 /*
707 * Restore terminal width to previous state (if necessary) and then
708 * cleanup as usual.
709 */
710 if (tc.t_width != term.cols)
711 vmsvt_cres((tc.t_width == narrow_cols) ? "NORMAL" : "WIDE");
712 }
713 vmsvt_flush();
714 status = sys$qiow(EFN, iochan, IO$_SETMODE, &iosb, 0, 0,
715 oldmode, sizeof(oldmode), 0, 0, 0, 0);
716 if (status != SS$_IVCHAN)
717 (void) sys$dassgn(iochan);
718
719 already_open = FALSE;
720 }
721 }
722
723 /***
724 * Ring the bell
725 *
726 * Nothing returned
727 ***/
728 static void
vmsvt_beep(void)729 vmsvt_beep(void)
730 {
731 #if OPT_FLASH
732 int hit = 0;
733
734 if (global_g_val(GMDFLASH) && dark_off != NULL && dark_on != NULL) {
735 hit = 1;
736 vmsvt_puts(dark_off);
737 term.flush();
738 catnap(200, FALSE);
739 vmsvt_puts(dark_on);
740 }
741 if (!hit && tc.t_type != TT$_VT52) {
742 /* *INDENT-OFF* */
743 static char *seq[][2] = {
744 { NULL, NULL }, /* vtflash = off */
745 { VTFLASH_NORMSEQ, VTFLASH_REVSEQ }, /* reverse */
746 { VTFLASH_REVSEQ, VTFLASH_NORMSEQ }, /* normal */
747 };
748 /* *INDENT-ON* */
749
750 char *str1, *str2;
751 int val;
752
753 val = global_g_val(GVAL_VTFLASH);
754 str1 = seq[val][0];
755 if (str1) {
756 str2 = seq[val][1];
757 vmsvt_puts(str1);
758 term.flush();
759 catnap(200, FALSE);
760 vmsvt_puts(str2);
761 hit = 1;
762 }
763 }
764 if (!hit)
765 #endif
766 vmsvt_putc(BEL);
767 }
768
769 static void
read_vms_tty(int length)770 read_vms_tty(int length)
771 {
772 int status;
773 QIO_SB iosb;
774 int term[2] =
775 {0, 0};
776 unsigned mask = (IO$_READVBLK
777 | IO$M_NOECHO
778 | IO$M_NOFILTR
779 | IO$M_TRMNOECHO);
780
781 status = sys$qiow(EFN, iochan,
782 ((length == 1)
783 ? mask
784 : mask | IO$M_TIMED),
785 &iosb, 0, 0, ibuf, length, 0, term, 0, 0);
786
787 if (status != SS$_NORMAL)
788 tidy_exit(status);
789 if (iosb.status == SS$_ENDOFFILE)
790 tidy_exit(status);
791
792 nibuf = iosb.count;
793 ibufi = 0;
794 }
795
796 /*
797 * Read a character from the terminal, performing no editing and doing no echo
798 * at all. More complex in VMS than almost anyplace else, which figures.
799 */
800 static int
vmsvt_getc(void)801 vmsvt_getc(void)
802 {
803 while (ibufi >= nibuf) {
804 if (!vmsvt_typahead())
805 read_vms_tty(1);
806 }
807 return (ibuf[ibufi++] & 0xFF); /* Allow multinational */
808 }
809
810 /*
811 * Write a character to the display. On VMS, terminal output is buffered, and
812 * we just put the characters in the big array, after checking for overflow.
813 */
814 static OUTC_DCL
vmsvt_putc(int c)815 vmsvt_putc(int c)
816 {
817 if (nobuf >= NOBUF)
818 vmsvt_flush();
819 obuf[nobuf++] = c;
820 OUTC_RET c;
821 }
822
823 static int
vmsvt_typahead(void)824 vmsvt_typahead(void)
825 {
826 if (ibufi >= nibuf) {
827 read_vms_tty(NIBUF);
828 return (nibuf > 0);
829 }
830 return TRUE;
831 }
832
833 /*
834 * Flush terminal buffer. Does real work where the terminal output is buffered
835 * up. A no-operation on systems where byte at a time terminal I/O is done.
836 */
837 static void
vmsvt_flush(void)838 vmsvt_flush(void)
839 {
840 QIO_SB iosb;
841
842 if (nobuf != 0) {
843 (void) sys$qiow(EFN, iochan, IO$_WRITELBLK | IO$M_NOFORMAT,
844 &iosb, 0, 0, obuf, nobuf, 0, 0, 0, 0);
845 nobuf = 0;
846 }
847 }
848
849 static void
vmsvt_clean(int f)850 vmsvt_clean(int f)
851 {
852 if (f)
853 term.openup();
854
855 term.flush();
856 term.close();
857 term.kclose();
858 }
859
860 static void
vmsvt_unclean(void)861 vmsvt_unclean(void)
862 {
863 }
864
865 /* Dispatch table. */
866 TERM term =
867 {
868 24, /* Max number of rows allowable */
869 /* Filled in */ 0,
870 /* Current number of rows used */
871 132, /* Max number of columns */
872 /* Filled in */ 0,
873 /* Current number of columns */
874 dumb_set_encoding,
875 dumb_get_encoding,
876 vmsvt_open, /* Open terminal at the start */
877 vmsvt_close, /* Close terminal at end */
878 nullterm_kopen, /* Open keyboard */
879 nullterm_kclose, /* Close keyboard */
880 vmsvt_clean, /* cleanup keyboard */
881 vmsvt_unclean, /* uncleanup keyboard */
882 nullterm_openup,
883 vmsvt_getc, /* Get character from keyboard */
884 vmsvt_putc, /* Put character to display */
885 vmsvt_typahead, /* char ready for reading */
886 vmsvt_flush, /* Flush output buffers */
887 vmsvt_move, /* Move cursor, origin 0 */
888 vmsvt_eeol, /* Erase to end of line */
889 vmsvt_eeop, /* Erase to end of page */
890 vmsvt_beep, /* Beep */
891 #if OPT_VIDEO_ATTRS
892 vmsvt_attr, /* Set attribute video state */
893 #else
894 vmsvt_rev, /* Set reverse video state */
895 #endif
896 vmsvt_cres, /* Change screen resolution */
897 nullterm_setfore, /* N/A: Set foreground color */
898 nullterm_setback, /* N/A: Set background color */
899 nullterm_setpal, /* N/A: Set palette colors */
900 nullterm_setccol,
901 nullterm_scroll, /* set at init-time */
902 nullterm_pflush,
903 nullterm_icursor,
904 nullterm_settitle,
905 nullterm_watchfd,
906 nullterm_unwatchfd,
907 nullterm_cursorvis,
908 nullterm_mopen,
909 nullterm_mclose,
910 nullterm_mevent,
911 };
912
913 #endif
914