xref: /illumos-gate/usr/src/lib/libxcurses2/src/libc/xcurses/newterm.c (revision 5d9d9091f564c198a760790b0bfa72c44e17912b)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1995-1999 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 /* LINTLIBRARY */
28 
29 /*
30  * newterm.c
31  *
32  * XCurses Library
33  *
34  * Copyright 1990, 1995 by Mortice Kern Systems Inc.  All rights reserved.
35  *
36  */
37 
38 #ifdef M_RCSID
39 #ifndef lint
40 static char const rcsID[] =
41 "$Header: /team/ps/sun_xcurses/archive/local_changes/xcurses/src/lib/"
42 "libxcurses/src/libc/xcurses/rcs/newterm.c 1.13 1998/06/04 19:55:52 "
43 "cbates Exp $";
44 #endif
45 #endif
46 
47 #include <sys/isa_defs.h>
48 #include <private.h>
49 #include <m_wio.h>
50 #include <errno.h>
51 #include <signal.h>
52 #include <stdlib.h>
53 #include <string.h>
54 
55 int	LINES, COLS;
56 int	COLORS, COLOR_PAIRS;
57 
58 WINDOW	*curscr;
59 WINDOW	*stdscr;
60 SCREEN	*__m_screen;
61 
62 static short	assume_one_line = FALSE;
63 
64 /*
65  * Assume terminal has only one screen line by restricting those
66  * capabilities that assume more than one line.  This function must
67  * be called before initscr() or newterm().
68  *
69  * This flag will reset after initscr() or newterm() so that subsequent
70  * calls to newterm(), without a preceding call to filter(), will load
71  * an unmodified terminal.  THIS IS NOT HISTORICAL PRACTICE, BUT DEEMED
72  * USEFUL.
73  */
74 void
75 filter(void)
76 {
77 	assume_one_line = TRUE;
78 }
79 
80 /*
81  * SIGTSTP Handler.
82  */
83 /* ARGSUSED */
84 void
85 tstp(int signo)
86 {
87 #ifdef SIGTSTP
88 	/*
89 	 * Only permit SIGTSTP if the curent process is the process
90 	 * group leader.  If the process is not the current group
91 	 * leader, then suspending the current process will suspend
92 	 * other members of the process group, such as the parent
93 	 * process.
94 	 */
95 	if (getpid() == getpgrp()) {
96 		(void) endwin();
97 
98 #ifdef SIG_UNBLOCK
99 		{
100 			sigset_t unblock;
101 
102 			(void) sigemptyset(&unblock);
103 			(void) sigaddset(&unblock, SIGTSTP);
104 			(void) sigprocmask(SIG_UNBLOCK, &unblock,
105 				(sigset_t *) 0);
106 		}
107 #endif /* SIG_UNBLOCK */
108 		(void) signal(SIGTSTP, SIG_DFL);
109 		(void) kill(0, SIGTSTP);
110 	} else {
111 		(void) beep();
112 	}
113 
114 	(void) signal(SIGTSTP, tstp);
115 	(void) wrefresh(curscr);
116 #else /* no SIGTSTP */
117 	(void) beep();
118 #endif /* SIGTSTP */
119 }
120 
121 int	__m_slk_format = -1;
122 
123 /*
124  * Do real soft label key initialisation once setupterm() have been called
125  * to load the current terminal.  Determine whether the terminal supplies
126  * soft label keys, or whether we have to fake it by using the last line
127  * of a terminal screen.
128  */
129 /* ARGSUSED */
130 int
131 __m_slk_init(SCREEN *sp, int style)
132 {
133 	int	code;
134 
135 	code = ERR;
136 
137 	(void) memset(&sp->_slk, 0, sizeof (sp->_slk));
138 
139 	/* Does the terminal have a method to program the soft label key? */
140 	if (plab_norm != NULL || pkey_plab  != NULL) {
141 		code = OK;
142 		goto done;
143 	}
144 
145 	/* We have to fake it. */
146 	if (lines < 2)
147 		goto done;
148 
149 	sp->_slk._w = subwin(sp->_newscr, 1, 0, lines-1, 0);
150 	if (sp->_slk._w == NULL)
151 		goto done;
152 
153 	/* Test suite seems to expect this */
154 	(void) wattrset(sp->_slk._w, A_DIM|A_REVERSE);
155 	(void) ripoffline(-1, 0);
156 	code = OK;
157 done:
158 	return (code);
159 }
160 
161 /*
162  * The XCurses specification is unclear how ripoffline() would
163  * affect newterm().  We assume that it can't be used with newterm()
164  * and that it only affects initscr(), which is responsible for
165  * creating stdscr.
166  */
167 t_rip	rip = { 0 };
168 
169 /*
170  * If line is positive (1), one line is removed from the beginning of
171  * stdscr; else if line is negative (-1), one line is removed from the end.
172  */
173 int
174 ripoffline(int line, int (*init)(WINDOW *, int))
175 {
176 	int	i;
177 
178 	i = rip.top - rip.bottom;
179 
180 	if (line != 0 && i < M_CURSES_MAX_RIPOFFLINE) {
181 		rip.line[i].init = init;
182 		if (line < 0)
183 			rip.line[i].dy = --rip.bottom;
184 		else
185 			rip.line[i].dy = rip.top++;
186 	}
187 
188 	return (OK);
189 }
190 
191 /*
192  * Create a new terminal screen.  Used if a program is going to be sending
193  * output to more than one terminal.  It returns a SCREEN* for the terminal.
194  * The parameters are a terminal name, output FILE*, and input FILE*.  If
195  * the terminal name is null then $TERM is used.  The program must also
196  * call endwin() for each terminal being used before exiting from curses.
197  * If newterm() is called more than once for the same terminal, the first
198  * terminal referred to must be the last one for which endwin() is called.
199  */
200 SCREEN *
201 newterm(char *term, FILE *out_fp, FILE *in_fp)
202 {
203 	WINDOW	*w;
204 	t_wide_io	*wio;
205 	SCREEN	*sp, *osp;
206 	int	i, n, y, errret;
207 
208 	/*
209 	 * Input stream should be unbuffered so that m_tfgetc() works
210 	 * correctly on BSD and SUN systems.
211 	 */
212 	(void) setvbuf(in_fp, (char *) 0, _IONBF, BUFSIZ);
213 #if 0
214 /*
215  * Not sure whether we really want to concern ourselves with the output
216  * buffer scheme.  Might be best to leave it upto the application to
217  * deal with buffer schemes and when to perform flushes.
218  *
219  * MKS Vi uses MKS Curses and so must support the ability to switch in
220  * and out of Curses mode when switching from Vi to Ex and back.
221  * Problem is that in Vi mode you would prefer full buffered output to
222  * give updates a smoother appearance and Ex mode you require line
223  * buffered in order to see prompts and messages.
224  */
225 	(void) setvbuf(out_fp, (char *) 0, _IOLBF, BUFSIZ);
226 #endif
227 	errno = 0;
228 
229 	if (__m_setupterm(term, fileno(in_fp), fileno(out_fp), &errret)
230 		== ERR) {
231 		switch (errret) {
232 		case -1:
233 			errno = ENOMEM;
234 			break;
235 		case 2:
236 			errno = ENAMETOOLONG;
237 			break;
238 		case 0:
239 		default:
240 			errno = ENOENT;
241 			break;
242 		}
243 		goto error1;
244 	}
245 
246 	if (__m_doupdate_init())
247 		goto error1;
248 
249 	if ((sp = (SCREEN *) calloc(1, sizeof (*sp))) == NULL)
250 		goto error1;
251 
252 	sp->_kfd = -1;
253 	sp->_if = in_fp;
254 	sp->_of = out_fp;
255 	sp->_term = cur_term;
256 
257 	sp->_unget._size = __m_decode_init((t_decode **) &sp->_decode);
258 
259 	/*
260 	 * Maximum length of a multbyte key sequence, including
261 	 * multibyte characters and terminal function keys.
262 	 */
263 	if (sp->_unget._size < (M_TYPEAHEAD_SIZE + MB_LEN_MAX))
264 		sp->_unget._size = M_TYPEAHEAD_SIZE + MB_LEN_MAX;
265 
266 	sp->_unget._stack = calloc((size_t) sp->_unget._size,
267 		sizeof (*sp->_unget._stack));
268 	if (sp->_unget._stack == NULL)
269 		goto error2;
270 
271 	if ((wio = (t_wide_io *) calloc(1, sizeof (*wio))) == NULL)
272 		goto error2;
273 
274 	/* Setup wide input for XCurses. */
275 	wio->get = (int (*)(void *)) wgetch;
276 	wio->unget = __xc_ungetc;
277 	wio->reset = __xc_clearerr;
278 	wio->iserror = __xc_ferror;
279 	wio->iseof = __xc_feof;
280 	sp->_in = wio;
281 
282 	if (assume_one_line) {
283 		/* Assume only one line. */
284 		lines = 1;
285 
286 		/* Disable capabilities that assume more than one line. */
287 		clear_screen = clr_eos = cursor_up = cursor_down = NULL;
288 		cursor_home = cursor_to_ll = cursor_address = NULL;
289 		row_address = parm_up_cursor = parm_down_cursor = NULL;
290 
291 		/* Re-evaluate the cursor motion costs. */
292 		__m_mvcur_cost();
293 
294 		/* Reset flag for subsequent calls to newterm(). */
295 		assume_one_line = FALSE;
296 	}
297 
298 	if ((sp->_curscr = newwin(lines, columns, 0, 0)) == NULL)
299 		goto error2;
300 
301 	if ((sp->_newscr = newwin(lines, columns, 0, 0)) == NULL)
302 		goto error2;
303 
304 #if defined(_LP64)
305 	sp->_hash = (unsigned int *) calloc(lines, sizeof (*sp->_hash));
306 #else
307 	sp->_hash = (unsigned long *) calloc(lines, sizeof (*sp->_hash));
308 #endif
309 	if (sp->_hash == NULL)
310 		goto error2;
311 
312 	if (0 <= __m_slk_format && __m_slk_init(sp, __m_slk_format) == ERR) {
313 		goto error2;
314 	}
315 
316 	/*
317 	 * doupdate() will perform the final screen preparations like
318 	 * enter_ca_mode, reset_prog_mode() (to assert the termios
319 	 * changes), etc.
320 	 */
321 	sp->_flags |= S_ENDWIN;
322 
323 #ifdef SIGTSTP
324 	(void) signal(SIGTSTP, tstp);
325 #endif
326 	/* Assert that __m_screen is set to the new terminal. */
327 	osp = set_term(sp);
328 
329 	/* Disable echo in tty driver, Curses does software echo. */
330 	PTERMIOS(_prog)->c_lflag &= ~ECHO;
331 
332 	/* Enable mappnig of cr -> nl on input and nl -> crlf on output. */
333 	PTERMIOS(_prog)->c_iflag |= ICRNL;
334 	PTERMIOS(_prog)->c_oflag |= OPOST;
335 #ifdef ONLCR
336 	PTERMIOS(_prog)->c_oflag |= ONLCR;
337 #endif
338 	cur_term->_flags |= __TERM_NL_IS_CRLF;
339 
340 #ifdef TAB0
341 	/* Use real tabs. */
342 	PTERMIOS(_prog)->c_oflag &= ~(TAB1|TAB2|TAB3);
343 #endif
344 
345 	/*
346 	 * Default to 'cbreak' mode as per
347 	 * test /tset/CAPIxcurses/fcbreak/fcbreak1{4}
348 	 */
349 	cur_term->_flags &= ~__TERM_HALF_DELAY;
350 
351 	/*
352 	 * Default to 'idcok' mode as per
353 	 * test /tset/CAPIxcurses/fidcok/fidcok1{3}
354 	 */
355 	__m_screen->_flags |= S_INS_DEL_CHAR;
356 
357 	PTERMIOS(_prog)->c_cc[VMIN] = 1;
358 	PTERMIOS(_prog)->c_cc[VTIME] = 0;
359 	PTERMIOS(_prog)->c_lflag &= ~ICANON;
360 
361 	(void) __m_tty_set_prog_mode();
362 	(void) __m_set_echo(1);
363 	(void) typeahead(fileno(in_fp));
364 
365 	(void) __m_slk_clear(1);
366 
367 	n = rip.top - rip.bottom;
368 	if (stdscr == NULL) {
369 		stdscr = newwin(lines - n, 0, rip.top, 0);
370 		if (stdscr == NULL)
371 			goto error3;
372 	}
373 	/*
374 	 * Create and initialise ripped off line windows.
375 	 * It is the application's responsiblity to free the
376 	 * windows when the application terminates.
377 	 */
378 	for (i = 0; i < n; ++i) {
379 		if (rip.line[i].created)
380 			continue;
381 		y = rip.line[i].dy;
382 		if (y < 0)
383 			y += lines;
384 
385 		w = newwin(1, 0, y, 0);
386 		if (rip.line[i].init != (int (*)(WINDOW *, int)) 0)
387 			(void) (*rip.line[i].init)(w, columns);
388 		rip.line[i].created = 1;
389 	}
390 	LINES = stdscr->_maxy = sp->_curscr->_maxy - n;
391 
392 	return (sp);
393 error3:
394 	(void) set_term(osp);
395 error2:
396 	delscreen(sp);
397 error1:
398 	return (NULL);
399 }
400 
401 /*
402  * Free storage associated with a screen structure.
403  * NOTE endwin() does not do this.
404  */
405 void
406 delscreen(SCREEN *sp)
407 {
408 	if (sp != NULL) {
409 		if (sp->_slk._w != NULL)
410 			(void) delwin(sp->_slk._w);
411 
412 		(void) delwin(sp->_newscr);
413 		(void) delwin(sp->_curscr);
414 		(void) del_curterm(sp->_term);
415 
416 		__m_decode_free((t_decode **) &sp->_decode);
417 
418 		if (sp->_hash != NULL)
419 			free(sp->_hash);
420 
421 		if (sp->_unget._stack != NULL)
422 			free(sp->_unget._stack);
423 
424 		if (sp->_in != NULL)
425 			free(sp->_in);
426 
427 		free(sp);
428 	}
429 }
430 
431 /*
432  * Switch current terminal for Curses layer.
433  */
434 SCREEN *
435 set_term(SCREEN *screen)
436 {
437 	SCREEN	*osp = __m_screen;
438 
439 	if (screen != NULL) {
440 		(void) set_curterm(screen->_term);
441 		curscr = screen->_curscr;
442 		__m_screen = screen;
443 
444 		LINES = lines;
445 		COLS = columns;
446 		COLORS = max_colors;
447 		COLOR_PAIRS = max_pairs;
448 	}
449 
450 	return (osp);
451 }
452 
453 int
454 typeahead(int fd)
455 {
456 	__m_screen->_flags &= ~S_ISATTY;
457 	if (fd != -1) {
458 		if (isatty(fd)) {
459 			__m_screen->_kfd = fd;
460 			__m_screen->_flags |= S_ISATTY;
461 		} else {
462 			__m_screen->_kfd = -1;
463 		}
464 	}
465 
466 	return (OK);
467 }
468 
469 int
470 __m_set_echo(int bf)
471 {
472 	int	old;
473 
474 	old = (__m_screen->_flags & S_ECHO) == S_ECHO;
475 
476 	__m_screen->_flags &= ~S_ECHO;
477 	if (bf)
478 		__m_screen->_flags |= S_ECHO;
479 
480 	return (old);
481 }
482