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