1 /* @(#)terminal.c 1.46 21/07/22 Copyright 1984-2021 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static UConst char sccsid[] =
5 "@(#)terminal.c 1.46 21/07/22 Copyright 1984-2021 J. Schilling";
6 #endif
7 /*
8 * Upper layer support routines for TERMCAP
9 *
10 * Copyright (c) 1984-2021 J. Schilling
11 */
12 /*
13 * The contents of this file are subject to the terms of the
14 * Common Development and Distribution License, Version 1.0 only
15 * (the "License"). You may not use this file except in compliance
16 * with the License.
17 *
18 * See the file CDDL.Schily.txt in this distribution for details.
19 * A copy of the CDDL is also available via the Internet at
20 * http://www.opensource.org/licenses/cddl1.txt
21 *
22 * When distributing Covered Code, include this CDDL HEADER in each
23 * file and include the License file CDDL.Schily.txt from this distribution.
24 */
25
26 /*
27 * This package provides external callable functions for the various
28 * TERMCAP functions imported from ttycmds.c.
29 *
30 * EXPORTS:
31 * t_start() - init TERMCAP package
32 * t_begin() - init terminal
33 * t_done() - restore terminal state
34 * other - depending on f_* flags
35 *
36 * Various f_* flags are provided to tell our callers whether a function is
37 * available. These flags are used by the screen update routines to select
38 * the proper functions.
39 *
40 * This package implements many workarounds that implement missing
41 * functionality using other supported features from the terminal.
42 * A terminal can be supported if absolute cursor movement or relative
43 * cursor movement and cursor home are possible.
44 * All other funtionality can be emulated.
45 */
46 #include "ved.h"
47 #include "ttys.h"
48 #include "terminal.h" /* XXX */
49 #include <schily/fcntl.h>
50 #include <schily/ioctl.h> /* Need to be before termios.h (BSD botch) */
51 #include <schily/termios.h>
52 #include <schily/signal.h>
53
54 /*
55 * The following variables are used to tell our users
56 * whether a specific function is supported or not.
57 */
58 char f_home = 0; /* Function Cursor Home */
59 char f_era_screen = 1; /* Function clear to end of screen */
60 char f_era_line = 1; /* Function clear to end of line */
61 char f_ins_line = 0; /* Function insert line */
62 char f_del_line = 0; /* Function lelete line */
63 char f_ins_char = 0; /* Function insert character */
64 char f_del_char = 0; /* Function delete character */
65 char f_alternate_video = 0; /* Function alternate video */
66 char f_move_cursor = 0; /* Function move cursor */
67 char f_clear_screen = 0; /* Function clear screen & home cursor */
68 char f_left = 0; /* Function cursor left */
69 char f_right = 0; /* Function cursor right */
70 char f_up = 0; /* Function cursor up */
71 char f_down = 0; /* Function cursor down */
72 char f_set_scroll_region = 0; /* Function set scrolling region */
73 char f_scr_up = 0; /* Function scroll up */
74 char f_scr_down = 0; /* Function scroll down */
75
76 #ifdef SIGWINCH
77 LOCAL void winch __PR((int signo));
78 #endif
79 EXPORT Uchar *t_start __PR((ewin_t *wp));
80 EXPORT void t_begin __PR((void));
81 EXPORT void t_done __PR((void));
82 EXPORT void t_error __PR((ewin_t *wp, char *s));
83 EXPORT void t_home __PR((ewin_t *wp));
84 EXPORT void t_left __PR((ewin_t *wp, int n));
85 EXPORT void t_right __PR((ewin_t *wp, int n));
86 EXPORT void t_up __PR((ewin_t *wp, int n));
87 EXPORT void t_down __PR((ewin_t *wp, int n));
88 EXPORT void t_move __PR((ewin_t *wp, int row, int col));
89 EXPORT void t_move_abs __PR((ewin_t *wp, int row, int col));
90 EXPORT void t_clear __PR((ewin_t *wp));
91 EXPORT void t_cleos __PR((ewin_t *wp));
92 EXPORT void t_cleol __PR((ewin_t *wp));
93 EXPORT void t__cleol __PR((ewin_t *wp, int do_move));
94 EXPORT void t_insch __PR((ewin_t *wp, char *str));
95 EXPORT void t_insln __PR((ewin_t *wp, int n));
96 EXPORT void t_delch __PR((ewin_t *wp, int n));
97 EXPORT void t_delln __PR((ewin_t *wp, int n));
98 EXPORT void t_alt __PR((char *str));
99 EXPORT void t_setscroll __PR((ewin_t *wp, int beg, int end));
100 EXPORT void t_scrup __PR((ewin_t *wp, int n));
101 EXPORT void t_scrdown __PR((ewin_t *wp, int n));
102
103 /*
104 * Support for run-time window size changes.
105 */
106 #ifdef SIGWINCH
107 LOCAL void
winch(signo)108 winch(signo)
109 int signo;
110 {
111 #if defined(TIOCGWINSZ) || defined(TIOCGSIZE)
112 extern ewin_t rootwin;
113 int tty = -1;
114 int opsize = rootwin.psize;
115 int ollen = rootwin.llen;
116 register int lines = 0;
117 register int cols = 0;
118 #ifdef TIOCGWINSZ
119 struct winsize ws;
120 #else
121 struct ttysize ts;
122 #endif
123
124 if (signo != 0)
125 signal(signo, winch);
126
127 #ifdef HAVE__DEV_TTY
128 tty = open("/dev/tty", O_RDONLY);
129 #endif
130 if (tty == -1)
131 tty = fileno(stderr);
132
133 #ifdef TIOCGWINSZ
134 if (ioctl(tty, TIOCGWINSZ, (char *)&ws) >= 0) {
135 lines = ws.ws_row;
136 cols = ws.ws_col;
137 }
138 #else
139 if (ioctl(tty, TIOCGSIZE, (char *)&ts) >= 0) {
140 lines = ts.ts_lines;
141 cols = ts.ts_cols;
142 }
143 #endif
144 #ifdef HAVE__DEV_TTY
145 if (tty >= 0 && tty != fileno(stderr))
146 close(tty);
147 #endif
148
149 if (lines == 0 || cols == 0 || lines > 999 || cols > 999)
150 return;
151
152 #if defined(__CYGWIN32__) || defined(__CYGWIN__)
153 cols--; /* Either a ioctl() or a window bug */
154 #endif
155 rootwin.psize = lines - 1;
156 rootwin.llen = cols - 1;
157 #ifdef AUTO_MARGIN_BOTCH
158 if (b_auto_right_margin) /* XXX */
159 rootwin.llen--;
160 #endif
161
162 if ((signo == SIGWINCH) && (rootwin.psize != opsize || rootwin.llen != ollen)) {
163 if (rootwin.optline == opsize/2 || rootwin.optline > rootwin.psize)
164 rootwin.optline = rootwin.psize/2;
165 {
166 extern ewin_t rootwin; /* XXX -> (ewin_t *)0 ??? */
167 initmsgsize(&rootwin);
168 writenum(&rootwin, rootwin.curnum);
169 vredisp(&rootwin); /* XXX -> (ewin_t *)0 ??? */
170 update(&rootwin); /* XXX -> (ewin_t *)0 ??? */
171 }
172 flush();
173 }
174 #endif /* defined(TIOCGWINSZ) || defined(TIOCGSIZE) */
175 }
176 #endif
177
178 /*
179 * Global export routine to initialize the TERMCAP package.
180 *
181 * Returns:
182 * NULL - ok
183 * != NULL - error message
184 */
185 EXPORT Uchar *
t_start(wp)186 t_start(wp)
187 ewin_t *wp;
188 {
189 Uchar *emsg;
190
191 if ((emsg = UC tty_start(putoutchar)) != NULL)
192 return (emsg);
193 wp->psize = tty_pagesize()-1;
194 wp->llen = tty_linelength()-1;
195 #ifdef AUTO_MARGIN_BOTCH
196 if (b_auto_right_margin) /* XXX */
197 wp->llen--;
198 #endif
199 #if defined(__CYGWIN32__) || defined(__CYGWIN__)
200 wp->llen--; /* Either a ioctl() or a window bug */
201 #endif
202
203 #ifdef SIGWINCH
204 signal(SIGWINCH, winch);
205 winch(0);
206 #endif
207 if (TTYhome || TTYcursor)
208 f_home = 1;
209 if (TTYinslines || (TTYcursor && TTYsscroll && TTYsdown))
210 f_ins_line = 1;
211 if (TTYdellines || (TTYcursor && TTYsscroll && TTYsup))
212 f_del_line = 1;
213 if (TTYinschars)
214 f_ins_char = 1;
215 if (TTYdelchars)
216 f_del_char = 1;
217 if (TTYaltvideo)
218 f_alternate_video = 1;
219 if (TTYcursor || (TTYhome && TTYdown && TTYright))
220 f_move_cursor = 1;
221 if (TTYclrscreen)
222 f_clear_screen = 1;
223 if (TTYleft || TTYcursor)
224 f_left = 1;
225 if (TTYright || TTYcursor)
226 f_right = 1;
227 if (TTYup || TTYcursor)
228 f_up = 1;
229 if (TTYdown || TTYcursor)
230 f_down = 1;
231 if (TTYsscroll && f_move_cursor)
232 f_set_scroll_region = 1;
233 if (TTYsup)
234 f_scr_up = 1;
235 if (TTYsdown)
236 f_scr_down = 1;
237
238 return (UC 0);
239 }
240
241
242 /*
243 * Put Terminal into a mode useful for the TERMCAP packet.
244 * This is the function that is intended to be called from upper level.
245 */
246 EXPORT void
t_begin()247 t_begin()
248 {
249 tty_init();
250 }
251
252 /*
253 * Restore Terminal mode to general purpose mode.
254 * This is the function that is intended to be called from upper level.
255 */
256 EXPORT void
t_done()257 t_done()
258 {
259 tty_term();
260 }
261
262 /*
263 * Indicate that someone tried to call a function that is
264 * not available with the current terminal.
265 */
266 EXPORT void
t_error(wp,s)267 t_error(wp, s)
268 ewin_t *wp;
269 char *s;
270 {
271 if (f_move_cursor) {
272 writemsg(wp, "missing terminal cap: %s ", s);
273 } else {
274 ringbell();
275 output(UC "missing terminal cap: ");
276 output(UC s);
277 output(UC " ");
278 }
279 }
280
281 /*
282 * Move the cursor home.
283 * Do not the emulated t_move(). It may not always work correctly.
284 */
285 EXPORT void
t_home(wp)286 t_home(wp)
287 ewin_t *wp;
288 {
289 if (TTYhome) {
290 (*TTYhome)();
291 cpos.vp = cpos.hp = 0;
292 } else if (TTYcursor) { /* Nur fuer t_error */
293 (*TTYcursor)(cpos.vp = 0, cpos.hp = 0); /* nicht t_move()!! */
294 } else {
295 t_error(wp, "HOME");
296 }
297 }
298
299 /*
300 * Move the cursor left n rows.
301 * Be smart and use the function that will emmit the fewest number of chars.
302 */
303 EXPORT void
t_left(wp,n)304 t_left(wp, n)
305 ewin_t *wp;
306 register int n;
307 {
308 if (n > cpos.hp)
309 n = cpos.hp;
310 if (n <= 0)
311 return;
312 if (TTYleft) {
313 (*TTYleft)(n);
314 cpos.hp -= n;
315 } else {
316 t_move(wp, cpos.vp, cpos.hp-n);
317 }
318 }
319
320 /*
321 * Move the cursor right n rows.
322 * Be smart and use the function that will emmit the fewest number of chars.
323 */
324 EXPORT void
t_right(wp,n)325 t_right(wp, n)
326 ewin_t *wp;
327 register int n;
328 {
329 if (n > wp->llen - cpos.hp)
330 n = wp->llen - cpos.hp;
331 if (n <= 0)
332 return;
333 if (TTYright) {
334 (*TTYright)(n);
335 cpos.hp += n;
336 } else {
337 t_move(wp, cpos.vp, cpos.hp+n);
338 }
339 }
340
341 /*
342 * Move the cursor up n lines.
343 * Be smart and use the function that will emmit the fewest number of chars.
344 */
345 EXPORT void
t_up(wp,n)346 t_up(wp, n)
347 ewin_t *wp;
348 register int n;
349 {
350 if (n > cpos.vp)
351 n = cpos.vp;
352 if (n <= 0)
353 return;
354 if (TTYup) {
355 (*TTYup)(n);
356 cpos.vp -= n;
357 } else {
358 t_move(wp, cpos.vp-n, cpos.hp);
359 }
360 }
361
362 /*
363 * Move the cursor down n lines.
364 * Be smart and use the function that will emmit the fewest number of chars.
365 */
366 EXPORT void
t_down(wp,n)367 t_down(wp, n)
368 ewin_t *wp;
369 register int n;
370 {
371 if (n > wp->psize - cpos.vp)
372 n = wp->psize - cpos.vp;
373 if (n <= 0)
374 return;
375 if (TTYdown) {
376 (*TTYdown)(n);
377 cpos.vp += n;
378 } else {
379 t_move(wp, cpos.vp+n, cpos.hp);
380 }
381 }
382
383 /*
384 * Move cursor. Try to use relative cursor movement if possible.
385 */
386 EXPORT void
t_move(wp,row,col)387 t_move(wp, row, col) /* y , x */
388 ewin_t *wp;
389 register int row;
390 register int col;
391 {
392 register int size;
393
394 if (row > wp->psize)
395 row = wp->psize;
396 if (col > wp->llen)
397 col = wp->llen;
398 if (cpos.vp > wp->psize || cpos.vp < 0 || /* Niemand weisz, wo der Curs */
399 cpos.hp > wp->llen || cpos.hp < 0) { /* wirklich steht also Absolut*/
400 t_move_abs(wp, row, col);
401 } else if (row == cpos.vp) {
402 size = col - cpos.hp;
403 if (size <= 0 && TTYleft && size > -300)
404 t_left(wp, -size);
405 else if (size > 0 && TTYright && size < 300)
406 t_right(wp, size);
407 else
408 t_move_abs(wp, row, col);
409 } else if (col == cpos.hp) {
410 size = row - cpos.vp;
411 if (size <= 0 && TTYup && size > -300)
412 t_up(wp, -size);
413 else if (size > 0 && TTYdown && size < 300)
414 t_down(wp, size);
415 else
416 t_move_abs(wp, row, col);
417 } else {
418 t_move_abs(wp, row, col);
419 }
420 }
421
422 /*
423 * Move cursor. Only absolute cursor movement allowed.
424 */
425 EXPORT void
t_move_abs(wp,row,col)426 t_move_abs(wp, row, col) /* y , x */
427 ewin_t *wp;
428 int row;
429 int col;
430 {
431 if (TTYcursor) {
432 (*TTYcursor)(cpos.vp = row, cpos.hp = col);
433 } else if (TTYhome && TTYdown && TTYright) {
434 (*TTYhome)();
435 (*TTYdown)(cpos.vp = row);
436 (*TTYright)(cpos.hp = col);
437 } else {
438 t_error(wp, "MCRA");
439 }
440 }
441
442 /*
443 * Clear screen.
444 */
445 EXPORT void
t_clear(wp)446 t_clear(wp)
447 ewin_t *wp;
448 {
449 if (TTYclrscreen) {
450 (*TTYclrscreen)(wp);
451 cpos.hp = cpos.vp = 0;
452 } else if (TTYhome || TTYcursor) {
453 t_home(wp);
454 t_cleos(wp);
455 } else {
456 t_error(wp, "CLSC");
457 }
458 }
459
460 /*
461 * Clear from cursor to end of screen.
462 * Be smart and use emulation if necessary.
463 */
464 EXPORT void
t_cleos(wp)465 t_cleos(wp)
466 ewin_t *wp;
467 {
468 if (TTYclr_endscr) {
469 (*TTYclr_endscr)(wp);
470 } else {
471 int ov = cpos.vp;
472 int oh = cpos.hp;
473 register int tempv = cpos.vp;
474
475 /*
476 * We need to erase to the end of screen manually.
477 * Use the function t__cleol() which will do this job
478 * as good as possible.
479 */
480 t__cleol(wp, 0);
481 while (++tempv <= wp->psize) {
482 if (b_auto_right_margin)
483 cpos.hp = -1; /* force abs cursor movement */
484 t_move(wp, tempv, 0);
485 t__cleol(wp, 0);
486 }
487 if (b_auto_right_margin)
488 cpos.hp = -1; /* force abs cursor movement */
489 t_move(wp, ov, oh);
490 }
491 }
492
493 /*
494 * Clear from cursor to end of line.
495 */
496 EXPORT void
t_cleol(wp)497 t_cleol(wp)
498 ewin_t *wp;
499 {
500 t__cleol(wp, 1);
501 }
502
503 /*
504 * Clear from cursor to end of line.
505 * Be smart and use emulation if necessary.
506 */
507 EXPORT void
t__cleol(wp,do_move)508 t__cleol(wp, do_move)
509 ewin_t *wp;
510 int do_move;
511 {
512 if (TTYclrendln) {
513 (*TTYclrendln)();
514 } else {
515 int oh = cpos.hp;
516 register int temp;
517
518 /*
519 * We need to erase to the end of the line manually.
520 * Be carefully with terminals that do automatic wrapping.
521 */
522 temp = wp->llen-cpos.hp+1;
523 if (b_auto_right_margin && cpos.vp >= wp->psize)
524 temp--;
525 while (--temp >= 0) {
526 putoutchar(' ');
527 cpos.hp++;
528 }
529 if (do_move) {
530 if (b_auto_right_margin)
531 cpos.hp = -1; /* force abs cursor movement */
532 t_move(wp, cpos.vp, oh);
533 }
534 }
535 }
536
537 /*
538 * Insert n chars.
539 */
540 EXPORT void
t_insch(wp,str)541 t_insch(wp, str)
542 ewin_t *wp;
543 char *str;
544
545 {
546 if (TTYinschars) {
547 (*TTYinschars)(str);
548 cpos.hp += strlen(str);
549 } else {
550 t_error(wp, "INSC");
551 }
552 }
553
554 /*
555 * Insert n lines.
556 */
557 EXPORT void
t_insln(wp,n)558 t_insln(wp, n)
559 ewin_t *wp;
560 int n;
561 {
562 if (n <= 0)
563 return;
564 if (TTYinslines) {
565 (*TTYinslines)(n);
566 } else if (TTYsscroll && TTYsdown && TTYcursor) {
567 (*TTYsscroll)(cpos.vp, wp->psize);
568 (*TTYcursor)(cpos.vp, 0);
569 (*TTYsdown)(n);
570 (*TTYsscroll)(0, wp->psize);
571 (*TTYcursor)(cpos.vp, cpos.hp);
572 } else {
573 t_error(wp, "INSL");
574 }
575 }
576
577 /*
578 * Delete n chars.
579 */
580 EXPORT void
t_delch(wp,n)581 t_delch(wp, n)
582 ewin_t *wp;
583 int n;
584 {
585 if (n <= 0)
586 return;
587 if (TTYdelchars)
588 (*TTYdelchars)(n);
589 else
590 t_error(wp, "DELC");
591 }
592
593 /*
594 * Delete n lines.
595 */
596 EXPORT void
t_delln(wp,n)597 t_delln(wp, n)
598 ewin_t *wp;
599 int n;
600 {
601 if (n <= 0)
602 return;
603 if (TTYdellines) {
604 (*TTYdellines)(n);
605 /* cpos.hp = 0;*/ /* das ist definitiv falsch */
606 if (cpos.hp) /* niemand weis, wo der Cursor danach steht */
607 cpos.hp = -1;
608 } else if (TTYsscroll && TTYsup && TTYcursor) {
609 (*TTYsscroll)(cpos.vp, wp->psize);
610 (*TTYcursor)(wp->psize, 0);
611 (*TTYsup)(n);
612 (*TTYsscroll)(0, wp->psize);
613 (*TTYcursor)(cpos.vp, cpos.hp = 0);
614 } else {
615 t_error(wp, "DELL");
616 }
617 }
618
619 /*
620 * Output 'string' in stand-out mode.
621 */
622 EXPORT void
t_alt(str)623 t_alt(str)
624 char *str;
625 {
626 if (TTYaltvideo)
627 (*TTYaltvideo)(str);
628 else
629 output(UC str);
630 }
631
632 /*
633 * Set scrolling region.
634 */
635 EXPORT void
t_setscroll(wp,beg,end)636 t_setscroll(wp, beg, end)
637 ewin_t *wp;
638 register int beg;
639 register int end;
640 {
641 if (end > wp->psize)
642 end = wp->psize;
643 if (beg < 0)
644 beg = 0;
645
646 if (TTYsscroll) {
647 (*TTYsscroll)(beg, end);
648 t_move(wp, cpos.vp, cpos.hp);
649 } else {
650 t_error(wp, "SSCR");
651 }
652 }
653
654 /*
655 * Scroll content of current scrolling region up.
656 */
657 EXPORT void
t_scrup(wp,n)658 t_scrup(wp, n)
659 ewin_t *wp;
660 register int n;
661 {
662 if (n > wp->psize - cpos.vp)
663 n = wp->psize - cpos.vp;
664 if (n <= 0)
665 return;
666 if (TTYsup) {
667 (*TTYsup)(n);
668 cpos.vp += n;
669 } else {
670 t_error(wp, "SCUP");
671 }
672 }
673
674 /*
675 * Scroll content of current scrolling region down.
676 */
677 EXPORT void
t_scrdown(wp,n)678 t_scrdown(wp, n)
679 ewin_t *wp;
680 register int n;
681 {
682 if (n > cpos.vp)
683 n = cpos.vp;
684 if (n <= 0)
685 return;
686 if (TTYsdown) {
687 (*TTYsdown)(n);
688 cpos.vp -= n;
689 } else {
690 t_error(wp, "SDWN");
691 }
692 }
693