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