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