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-1998, 2000 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* LINTLIBRARY */ 30 31 /* 32 * wgetch.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 rcsID[] = 43 "$Header: /team/ps/sun_xcurses/archive/local_changes/xcurses/src/lib/" 44 "libxcurses/src/libc/xcurses/rcs/wgetch.c 1.23 1998/06/05 16:38:43 " 45 "cbates Exp $"; 46 #endif 47 #endif 48 49 #include <private.h> 50 #include <string.h> 51 #include <errno.h> 52 53 static struct termios read_termios; 54 55 int 56 iqContainsFullLine(void) 57 { 58 int i; 59 60 if (!(PTERMIOS(_prog)->c_lflag & ICANON)) { 61 /* 62 * Non-canonical mode ... 63 * Don't care about full lines ... continue 64 */ 65 return (1); 66 } 67 if (read_termios.c_lflag & ICANON) { 68 /* 69 * Terminal subsystem worries about lines etc. ... 70 * continue ... 71 */ 72 return (1); 73 } 74 /* We turned off ICANON so we have to do it ourselves */ 75 if ((read_termios.c_cc[VMIN] == 0) && 76 (read_termios.c_cc[VTIME] != 0)) { 77 /* We set delay mode. Only error if noting in the read */ 78 return (!iqIsEmpty()); 79 } 80 for (i = __m_screen->_unget._count - 1; i >= 0; i--) { 81 int ch = __m_screen->_unget._stack[i]; 82 if (PTERMIOS(_shell)->c_cc[VEOL] == ch) 83 return (1); 84 if ('\n' == ch) 85 return (1); 86 } 87 return (0); 88 } 89 90 void 91 iqPush(unsigned int ch) 92 { 93 if (__m_screen->_unget._count >= __m_screen->_unget._size) 94 return; 95 __m_screen->_unget._stack[__m_screen->_unget._count++] = 96 (int) ch; 97 } 98 99 void 100 iqAdd(unsigned int ch) 101 { 102 int count; 103 104 if (++(__m_screen->_unget._count) >= __m_screen->_unget._size) 105 __m_screen->_unget._count = __m_screen->_unget._size - 1; 106 count = __m_screen->_unget._count - 1; 107 if (count) { 108 (void) memmove(__m_screen->_unget._stack + 1, 109 __m_screen->_unget._stack, count * sizeof (int)); 110 } 111 __m_screen->_unget._stack[0] = (int) ch; 112 } 113 114 int 115 iqIsEmpty(void) 116 { 117 return (__m_screen->_unget._count == 0); 118 } 119 120 void 121 iqReset(void) 122 { 123 __m_screen->_unget._count = 0; 124 } 125 126 /* Assumes count > 0 */ 127 int 128 iqPull(void) 129 { 130 int ch; 131 132 ch = __m_screen->_unget._stack[--(__m_screen->_unget._count)]; 133 return (ch); 134 } 135 136 /* Discard n characters from front of Q */ 137 void 138 iqTrash(int n) 139 { 140 __m_screen->_unget._count -= n; 141 if (__m_screen->_unget._count < 0) { 142 __m_screen->_unget._count = 0; 143 } 144 } 145 146 int 147 iqGetNth(int n) 148 { 149 int ch; 150 151 if (__m_screen->_unget._count - n <= 0) { 152 return (EOF); 153 } 154 ch = __m_screen->_unget._stack[__m_screen->_unget._count - n - 1]; 155 return (ch); 156 } 157 158 159 struct termios 160 __m_tty_override_mode(int vmin, int vtime) 161 { 162 struct termios rval; 163 struct termios newstuff; 164 165 rval = newstuff = *PTERMIOS(_actual); 166 167 /* If halfdelay mode. Leave canonical mode intact */ 168 if (!(vmin == 0 && vtime == 0) && 169 (cur_term->_flags & __TERM_HALF_DELAY)) 170 return (rval); 171 172 /* If blocking mode. Leave canonical mode intact */ 173 if (vmin == 1) 174 return (rval); 175 176 /* VMIN and VTIME trash VEOL and VEOF so canonical cannot work */ 177 newstuff.c_cc[VMIN] = (cc_t) vmin; 178 newstuff.c_cc[VTIME] = (cc_t) vtime; 179 newstuff.c_lflag &= ~ICANON; 180 181 (void) __m_tty_set(&newstuff); 182 return (rval); 183 } 184 185 int 186 __m_read_input_char(int *pChar) 187 { 188 if (req_for_input != NULL) { 189 (void) TPUTS(req_for_input, 1, __m_outc); 190 } 191 clearerr(__m_screen->_if); 192 *pChar = 0; 193 /* save actual setting for later test */ 194 read_termios = *PTERMIOS(_actual); 195 196 errno = 0; 197 if ((*pChar = fgetc(__m_screen->_if)) == EOF) { 198 return ((errno) ? ERR : OK); 199 } 200 201 if (((PTERMIOS(_prog)->c_cflag & CSIZE) != CS8) && (*pChar != EOF)) 202 *pChar &= 0x7f; 203 return (OK); 204 } 205 206 int 207 __m_typeahead_read_input_char(int *pChar) 208 { 209 unsigned char ch; 210 ssize_t r; 211 212 if (req_for_input != NULL) { 213 (void) TPUTS(req_for_input, 1, __m_outc); 214 } 215 216 *pChar = 0; 217 /* save actual setting for later test */ 218 read_termios = *PTERMIOS(_actual); 219 220 errno = 0; 221 if ((r = read(__m_screen->_kfd, (void *)&ch, 1)) > 0) { 222 if ((PTERMIOS(_prog)->c_cflag & CSIZE) != CS8) { 223 *pChar = ch & 0x7f; 224 } else { 225 *pChar = (int)ch; 226 } 227 return (OK); 228 } else if (r == 0) { 229 *pChar = EOF; 230 return (OK); 231 } else { 232 return (ERR); 233 } 234 } 235 236 237 static int klugeTypeaheadInGetch = 0; 238 239 int 240 pollTypeahead(void) 241 { 242 struct termios save; 243 int ch; 244 245 if (!(__m_screen->_flags & S_ISATTY) || 246 !(__m_screen->_flags & S_TYPEAHEAD_OK)) { 247 /* Typeahead disabled */ 248 return (0); 249 } 250 save = __m_tty_override_mode(0, 0); 251 while (__m_typeahead_read_input_char(&ch) == OK) { 252 if (ch == EOF) 253 break; 254 iqAdd(ch); 255 } 256 (void) __m_tty_set(&save); 257 /* if in wgetch, always do refresh */ 258 return ((klugeTypeaheadInGetch) ? 0 : !iqIsEmpty()); 259 } 260 261 /* 262 * Push single-byte character back onto the input queue. 263 * 264 * MKS EXTENSION permits the return value of wgetch(), which 265 * can be a KEY_ value, to be pushed back. 266 */ 267 int 268 ungetch(int ch) 269 { 270 iqPush(ch); 271 return (OK); 272 } 273 274 /* 275 * Return true if the SCREEN's stream has an I/O error. 276 * Ignore the window parameter. 277 */ 278 /* ARGSUSED */ 279 int 280 __xc_ferror(void *w) 281 { 282 return (ferror(__m_screen->_if)); 283 } 284 285 /* ARGSUSED */ 286 int 287 __xc_ungetc(int ch, void *w) 288 { 289 iqPush(ch); 290 return (1); 291 } 292 293 /* 294 * Return true if the SCREEN's stream has seen EOF. 295 * Ignore the window parameter. 296 */ 297 /* ARGSUSED */ 298 int 299 __xc_feof(void *w) 300 { 301 return (feof(__m_screen->_if)); 302 } 303 304 /* 305 * Clear the error and eof flags of the SCREEN's stream. 306 * Ignore the window parameter. 307 */ 308 /* ARGSUSED */ 309 void 310 __xc_clearerr(void *w) 311 { 312 clearerr(__m_screen->_if); 313 } 314 315 int 316 __m_echo(WINDOW *w, int ch) 317 { 318 if (!(__m_screen->_flags & S_ECHO)) 319 return (ch); 320 if (!(0 <= ch && ch != EOF)) { 321 (void) beep(); 322 return (ERR); 323 } 324 if (ch == '\b') { 325 if (w->_curx <= 0) { 326 (void) beep(); 327 return (ch); 328 } 329 w->_curx--; 330 (void) wdelch(w); 331 } else { 332 (void) waddch(w, ch); 333 } 334 (void) wrefresh(w); 335 return (ch); 336 } 337 338 int 339 wgetch(WINDOW *w) 340 { 341 t_decode *node; 342 int ch, i, timeout; 343 struct termios save; 344 345 __m_screen->_flags |= S_TYPEAHEAD_OK; 346 347 klugeTypeaheadInGetch = 1; 348 (void) wrefresh(w); 349 klugeTypeaheadInGetch = 0; 350 351 if (iqIsEmpty()) { 352 save = __m_tty_override_mode(w->_vmin, w->_vtime); 353 if (__m_read_input_char(&ch) == ERR) { 354 (void) __m_tty_set(&save); 355 return (ERR); 356 } 357 if (!((ch == EOF) && (PTERMIOS(_prog)->c_lflag & ICANON))) { 358 /* Put EOF on Q only in non-canonical mode */ 359 iqAdd(ch); 360 } 361 (void) __m_tty_set(&save); 362 } 363 ch = iqGetNth(0); 364 if (!iqContainsFullLine()) { 365 return (ERR); 366 } 367 368 /* 369 * Only check for function keys if keypad is true and we 370 * did not read a KEY_ value (which are < 0), nor EOF. 371 * It is conceivable that a KEY_ was pushed back with 372 * ungetch(). 373 */ 374 if ((w->_flags & W_USE_KEYPAD) && 0 <= ch && ch != EOF) { 375 /* 376 * Treat the termios ERASE key the same as key_backspace. 377 * 378 * We used to change the key_backspace entry to be a string 379 * containing the ERASE key in setupterm(), but this would 380 * then disable the real terminfo entry for the backspace key. 381 * Apparently VT300 terminals change the key code/sequence 382 * of the backspace key in application keypad mode. 383 * See SR 6014. 384 * 385 * Refer to _shell instead of _prog, since _shell will 386 * correctly reflect the user's prefered settings, whereas 387 * _prog may not have been initialised if both input and 388 * output have been redirected. 389 */ 390 #ifdef _POSIX_VDISABLE 391 if (PTERMIOS(_shell)->c_cc[VERASE] != _POSIX_VDISABLE) 392 #endif 393 if (ch == PTERMIOS(_shell)->c_cc[VERASE]) { 394 /* Discard ch from Q */ 395 (void) iqPull(); 396 return (KEY_BACKSPACE); 397 } 398 399 /* Begin check for function key. */ 400 node = (t_decode *) __m_screen->_decode; 401 402 /* Use input stack as a queue. */ 403 timeout = w->_flags & W_USE_TIMEOUT; 404 for (i = 1; ; i++) { 405 while (node->ch != ch) { 406 node = node->sibling; 407 if (node == NULL) 408 goto uncoded; 409 } 410 411 /* Found funuction key? */ 412 if (node->key != 0) { 413 /* Trash all input used to make the FKey */ 414 iqTrash(i); 415 return (__m_echo(w, node->key)); 416 } 417 418 /* 419 * Get next candidate character - 420 * either from Q or input 421 */ 422 if ((ch = iqGetNth(i)) == EOF) { 423 /* 424 * Setup interbyte timer (once only). 425 * fgetc() will return EOF if no input received, 426 * which may not be a true EOF. 427 */ 428 if (timeout) { 429 (void) __m_tty_override_mode(0, 430 M_CURSES_INTERBYTE_TIME); 431 } 432 timeout = 0; 433 if (__m_read_input_char(&ch) == ERR) 434 return (ERR); 435 /* Timeout or real eof. */ 436 if (ch == EOF) 437 break; 438 iqAdd(ch); 439 } 440 441 /* Incomplete sequence, continue. */ 442 node = node->child; 443 } 444 } 445 uncoded: 446 /* Return first byte received or EOF. */ 447 ch = iqPull(); 448 return (__m_echo(w, ch)); 449 } 450