1 /**************************************************************************** 2 * Copyright (c) 1998-2005,2006 Free Software Foundation, Inc. * 3 * * 4 * Permission is hereby granted, free of charge, to any person obtaining a * 5 * copy of this software and associated documentation files (the * 6 * "Software"), to deal in the Software without restriction, including * 7 * without limitation the rights to use, copy, modify, merge, publish, * 8 * distribute, distribute with modifications, sublicense, and/or sell * 9 * copies of the Software, and to permit persons to whom the Software is * 10 * furnished to do so, subject to the following conditions: * 11 * * 12 * The above copyright notice and this permission notice shall be included * 13 * in all copies or substantial portions of the Software. * 14 * * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 22 * * 23 * Except as contained in this notice, the name(s) of the above copyright * 24 * holders shall not be used in advertising or otherwise to promote the * 25 * sale, use or other dealings in this Software without prior written * 26 * authorization. * 27 ****************************************************************************/ 28 29 /**************************************************************************** 30 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 31 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 32 * and: Thomas E. Dickey 1996-on * 33 ****************************************************************************/ 34 35 /* 36 ** lib_getch.c 37 ** 38 ** The routine getch(). 39 ** 40 */ 41 42 #include <curses.priv.h> 43 44 MODULE_ID("$Id: lib_getch.c,v 1.75 2006/03/04 20:06:09 tom Exp $") 45 46 #include <fifo_defs.h> 47 48 NCURSES_EXPORT_VAR(int) 49 ESCDELAY = 1000; /* max interval betw. chars in funkeys, in millisecs */ 50 51 #ifdef NCURSES_WGETCH_EVENTS 52 #define TWAIT_MASK 7 53 #else 54 #define TWAIT_MASK 3 55 #endif 56 57 /* 58 * Check for mouse activity, returning nonzero if we find any. 59 */ 60 static int 61 check_mouse_activity(int delay EVENTLIST_2nd(_nc_eventlist * evl)) 62 { 63 int rc; 64 65 #if USE_SYSMOUSE 66 if ((SP->_mouse_type == M_SYSMOUSE) 67 && (SP->_sysmouse_head < SP->_sysmouse_tail)) { 68 return 2; 69 } 70 #endif 71 rc = _nc_timed_wait(TWAIT_MASK, delay, (int *) 0 EVENTLIST_2nd(evl)); 72 #if USE_SYSMOUSE 73 if ((SP->_mouse_type == M_SYSMOUSE) 74 && (SP->_sysmouse_head < SP->_sysmouse_tail) 75 && (rc == 0) 76 && (errno == EINTR)) { 77 rc |= 2; 78 } 79 #endif 80 return rc; 81 } 82 83 static NCURSES_INLINE int 84 fifo_peek(void) 85 { 86 int ch = SP->_fifo[peek]; 87 TR(TRACE_IEVENT, ("peeking at %d", peek)); 88 89 p_inc(); 90 return ch; 91 } 92 93 static NCURSES_INLINE int 94 fifo_pull(void) 95 { 96 int ch; 97 ch = SP->_fifo[head]; 98 TR(TRACE_IEVENT, ("pulling %s from %d", _tracechar(ch), head)); 99 100 if (peek == head) { 101 h_inc(); 102 peek = head; 103 } else 104 h_inc(); 105 106 #ifdef TRACE 107 if (_nc_tracing & TRACE_IEVENT) 108 _nc_fifo_dump(); 109 #endif 110 return ch; 111 } 112 113 static NCURSES_INLINE int 114 fifo_push(EVENTLIST_0th(_nc_eventlist * evl)) 115 { 116 int n; 117 int ch = 0; 118 int mask = 0; 119 120 (void) mask; 121 if (tail == -1) 122 return ERR; 123 124 #ifdef HIDE_EINTR 125 again: 126 errno = 0; 127 #endif 128 129 #ifdef NCURSES_WGETCH_EVENTS 130 if (evl 131 #if USE_GPM_SUPPORT || USE_EMX_MOUSE || USE_SYSMOUSE 132 || (SP->_mouse_fd >= 0) 133 #endif 134 ) { 135 mask = check_mouse_activity(-1 EVENTLIST_2nd(evl)); 136 } else 137 mask = 0; 138 139 if (mask & 4) { 140 T(("fifo_push: ungetch KEY_EVENT")); 141 ungetch(KEY_EVENT); 142 return KEY_EVENT; 143 } 144 #elif USE_GPM_SUPPORT || USE_EMX_MOUSE || USE_SYSMOUSE 145 if (SP->_mouse_fd >= 0) { 146 mask = check_mouse_activity(-1 EVENTLIST_2nd(evl)); 147 } 148 #endif 149 150 #if USE_GPM_SUPPORT || USE_EMX_MOUSE 151 if ((SP->_mouse_fd >= 0) && (mask & 2)) { 152 SP->_mouse_event(SP); 153 ch = KEY_MOUSE; 154 n = 1; 155 } else 156 #endif 157 #if USE_SYSMOUSE 158 if ((SP->_mouse_type == M_SYSMOUSE) 159 && (SP->_sysmouse_head < SP->_sysmouse_tail)) { 160 SP->_mouse_event(SP); 161 ch = KEY_MOUSE; 162 n = 1; 163 } else if ((SP->_mouse_type == M_SYSMOUSE) 164 && (mask <= 0) && errno == EINTR) { 165 SP->_mouse_event(SP); 166 ch = KEY_MOUSE; 167 n = 1; 168 } else 169 #endif 170 { /* Can block... */ 171 unsigned char c2 = 0; 172 n = read(SP->_ifd, &c2, 1); 173 ch = c2; 174 } 175 176 #ifdef HIDE_EINTR 177 /* 178 * Under System V curses with non-restarting signals, getch() returns 179 * with value ERR when a handled signal keeps it from completing. 180 * If signals restart system calls, OTOH, the signal is invisible 181 * except to its handler. 182 * 183 * We don't want this difference to show. This piece of code 184 * tries to make it look like we always have restarting signals. 185 */ 186 if (n <= 0 && errno == EINTR) 187 goto again; 188 #endif 189 190 if ((n == -1) || (n == 0)) { 191 TR(TRACE_IEVENT, ("read(%d,&ch,1)=%d, errno=%d", SP->_ifd, n, errno)); 192 ch = ERR; 193 } 194 TR(TRACE_IEVENT, ("read %d characters", n)); 195 196 SP->_fifo[tail] = ch; 197 SP->_fifohold = 0; 198 if (head == -1) 199 head = peek = tail; 200 t_inc(); 201 TR(TRACE_IEVENT, ("pushed %s at %d", _tracechar(ch), tail)); 202 #ifdef TRACE 203 if (_nc_tracing & TRACE_IEVENT) 204 _nc_fifo_dump(); 205 #endif 206 return ch; 207 } 208 209 static NCURSES_INLINE void 210 fifo_clear(void) 211 { 212 memset(SP->_fifo, 0, sizeof(SP->_fifo)); 213 head = -1; 214 tail = peek = 0; 215 } 216 217 static int kgetch(EVENTLIST_0th(_nc_eventlist * evl)); 218 219 #define wgetch_should_refresh(win) (\ 220 (is_wintouched(win) || (win->_flags & _HASMOVED)) \ 221 && !(win->_flags & _ISPAD)) 222 223 NCURSES_EXPORT(int) 224 _nc_wgetch(WINDOW *win, 225 unsigned long *result, 226 int use_meta 227 EVENTLIST_2nd(_nc_eventlist * evl)) 228 { 229 int ch; 230 #ifdef NCURSES_WGETCH_EVENTS 231 long event_delay = -1; 232 #endif 233 234 T((T_CALLED("_nc_wgetch(%p)"), win)); 235 236 *result = 0; 237 if (win == 0 || SP == 0) 238 returnCode(ERR); 239 240 if (cooked_key_in_fifo()) { 241 if (wgetch_should_refresh(win)) 242 wrefresh(win); 243 244 *result = fifo_pull(); 245 returnCode(OK); 246 } 247 #ifdef NCURSES_WGETCH_EVENTS 248 if (evl && (evl->count == 0)) 249 evl = NULL; 250 event_delay = _nc_eventlist_timeout(evl); 251 #endif 252 253 /* 254 * Handle cooked mode. Grab a string from the screen, 255 * stuff its contents in the FIFO queue, and pop off 256 * the first character to return it. 257 */ 258 if (head == -1 && 259 !SP->_notty && 260 !SP->_raw && 261 !SP->_cbreak && 262 !SP->_called_wgetch) { 263 char buf[MAXCOLUMNS], *sp; 264 int rc; 265 266 TR(TRACE_IEVENT, ("filling queue in cooked mode")); 267 268 SP->_called_wgetch = TRUE; 269 rc = wgetnstr(win, buf, MAXCOLUMNS); 270 SP->_called_wgetch = FALSE; 271 272 /* ungetch in reverse order */ 273 #ifdef NCURSES_WGETCH_EVENTS 274 if (rc != KEY_EVENT) 275 #endif 276 ungetch('\n'); 277 for (sp = buf + strlen(buf); sp > buf; sp--) 278 ungetch(sp[-1]); 279 280 #ifdef NCURSES_WGETCH_EVENTS 281 /* Return it first */ 282 if (rc == KEY_EVENT) { 283 *result = rc; 284 returnCode(OK); 285 } 286 #endif 287 288 *result = fifo_pull(); 289 returnCode(OK); 290 } 291 292 if (win->_use_keypad != SP->_keypad_on) 293 _nc_keypad(win->_use_keypad); 294 295 if (wgetch_should_refresh(win)) 296 wrefresh(win); 297 298 if (!win->_notimeout && (win->_delay >= 0 || SP->_cbreak > 1)) { 299 if (head == -1) { /* fifo is empty */ 300 int delay; 301 int rc; 302 303 TR(TRACE_IEVENT, ("timed delay in wgetch()")); 304 if (SP->_cbreak > 1) 305 delay = (SP->_cbreak - 1) * 100; 306 else 307 delay = win->_delay; 308 309 #ifdef NCURSES_WGETCH_EVENTS 310 if (event_delay >= 0 && delay > event_delay) 311 delay = event_delay; 312 #endif 313 314 TR(TRACE_IEVENT, ("delay is %d milliseconds", delay)); 315 316 rc = check_mouse_activity(delay EVENTLIST_2nd(evl)); 317 318 #ifdef NCURSES_WGETCH_EVENTS 319 if (rc & 4) { 320 *result = KEY_EVENT; 321 returnCode(OK); 322 } 323 #endif 324 if (!rc) 325 returnCode(ERR); 326 } 327 /* else go on to read data available */ 328 } 329 330 if (win->_use_keypad) { 331 /* 332 * This is tricky. We only want to get special-key 333 * events one at a time. But we want to accumulate 334 * mouse events until either (a) the mouse logic tells 335 * us it's picked up a complete gesture, or (b) 336 * there's a detectable time lapse after one. 337 * 338 * Note: if the mouse code starts failing to compose 339 * press/release events into clicks, you should probably 340 * increase the wait with mouseinterval(). 341 */ 342 int runcount = 0; 343 int rc; 344 345 do { 346 ch = kgetch(EVENTLIST_1st(evl)); 347 if (ch == KEY_MOUSE) { 348 ++runcount; 349 if (SP->_mouse_inline(SP)) 350 break; 351 } 352 if (SP->_maxclick < 0) 353 break; 354 } while 355 (ch == KEY_MOUSE 356 && (((rc = check_mouse_activity(SP->_maxclick 357 EVENTLIST_2nd(evl))) != 0 358 && !(rc & 4)) 359 || !SP->_mouse_parse(runcount))); 360 #ifdef NCURSES_WGETCH_EVENTS 361 if ((rc & 4) && !ch == KEY_EVENT) { 362 ungetch(ch); 363 ch = KEY_EVENT; 364 } 365 #endif 366 if (runcount > 0 && ch != KEY_MOUSE) { 367 #ifdef NCURSES_WGETCH_EVENTS 368 /* mouse event sequence ended by an event, report event */ 369 if (ch == KEY_EVENT) { 370 ungetch(KEY_MOUSE); /* FIXME This interrupts a gesture... */ 371 } else 372 #endif 373 { 374 /* mouse event sequence ended by keystroke, store keystroke */ 375 ungetch(ch); 376 ch = KEY_MOUSE; 377 } 378 } 379 } else { 380 if (head == -1) 381 fifo_push(EVENTLIST_1st(evl)); 382 ch = fifo_pull(); 383 } 384 385 if (ch == ERR) { 386 #if USE_SIZECHANGE 387 if (SP->_sig_winch) { 388 _nc_update_screensize(); 389 /* resizeterm can push KEY_RESIZE */ 390 if (cooked_key_in_fifo()) { 391 *result = fifo_pull(); 392 returnCode(*result >= KEY_MIN ? KEY_CODE_YES : OK); 393 } 394 } 395 #endif 396 returnCode(ERR); 397 } 398 399 /* 400 * If echo() is in effect, display the printable version of the 401 * key on the screen. Carriage return and backspace are treated 402 * specially by Solaris curses: 403 * 404 * If carriage return is defined as a function key in the 405 * terminfo, e.g., kent, then Solaris may return either ^J (or ^M 406 * if nonl() is set) or KEY_ENTER depending on the echo() mode. 407 * We echo before translating carriage return based on nonl(), 408 * since the visual result simply moves the cursor to column 0. 409 * 410 * Backspace is a different matter. Solaris curses does not 411 * translate it to KEY_BACKSPACE if kbs=^H. This does not depend 412 * on the stty modes, but appears to be a hardcoded special case. 413 * This is a difference from ncurses, which uses the terminfo entry. 414 * However, we provide the same visual result as Solaris, moving the 415 * cursor to the left. 416 */ 417 if (SP->_echo && !(win->_flags & _ISPAD)) { 418 chtype backup = (ch == KEY_BACKSPACE) ? '\b' : ch; 419 if (backup < KEY_MIN) 420 wechochar(win, backup); 421 } 422 423 /* 424 * Simulate ICRNL mode 425 */ 426 if ((ch == '\r') && SP->_nl) 427 ch = '\n'; 428 429 /* Strip 8th-bit if so desired. We do this only for characters that 430 * are in the range 128-255, to provide compatibility with terminals 431 * that display only 7-bit characters. Note that 'ch' may be a 432 * function key at this point, so we mustn't strip _those_. 433 */ 434 if (!use_meta) 435 if ((ch < KEY_MIN) && (ch & 0x80)) 436 ch &= 0x7f; 437 438 T(("wgetch returning : %s", _tracechar(ch))); 439 440 *result = ch; 441 returnCode(ch >= KEY_MIN ? KEY_CODE_YES : OK); 442 } 443 444 #ifdef NCURSES_WGETCH_EVENTS 445 NCURSES_EXPORT(int) 446 wgetch_events(WINDOW *win, _nc_eventlist * evl) 447 { 448 int code; 449 unsigned long value; 450 451 T((T_CALLED("wgetch_events(%p,%p)"), win, evl)); 452 code = _nc_wgetch(win, 453 &value, 454 SP->_use_meta 455 EVENTLIST_2nd(evl)); 456 if (code != ERR) 457 code = value; 458 returnCode(code); 459 } 460 #endif 461 462 NCURSES_EXPORT(int) 463 wgetch(WINDOW *win) 464 { 465 int code; 466 unsigned long value; 467 468 T((T_CALLED("wgetch(%p)"), win)); 469 code = _nc_wgetch(win, 470 &value, 471 (SP ? SP->_use_meta : 0) 472 EVENTLIST_2nd((_nc_eventlist *) 0)); 473 if (code != ERR) 474 code = value; 475 returnCode(code); 476 } 477 478 /* 479 ** int 480 ** kgetch() 481 ** 482 ** Get an input character, but take care of keypad sequences, returning 483 ** an appropriate code when one matches the input. After each character 484 ** is received, set an alarm call based on ESCDELAY. If no more of the 485 ** sequence is received by the time the alarm goes off, pass through 486 ** the sequence gotten so far. 487 ** 488 ** This function must be called when there are no cooked keys in queue. 489 ** (that is head==-1 || peek==head) 490 ** 491 */ 492 493 static int 494 kgetch(EVENTLIST_0th(_nc_eventlist * evl)) 495 { 496 struct tries *ptr; 497 int ch = 0; 498 int timeleft = ESCDELAY; 499 500 TR(TRACE_IEVENT, ("kgetch() called")); 501 502 ptr = SP->_keytry; 503 504 for (;;) { 505 if (cooked_key_in_fifo() && SP->_fifo[head] >= KEY_MIN) { 506 break; 507 } else if (!raw_key_in_fifo()) { 508 ch = fifo_push(EVENTLIST_1st(evl)); 509 if (ch == ERR) { 510 peek = head; /* the keys stay uninterpreted */ 511 return ERR; 512 } 513 #ifdef NCURSES_WGETCH_EVENTS 514 else if (ch == KEY_EVENT) { 515 peek = head; /* the keys stay uninterpreted */ 516 return fifo_pull(); /* Remove KEY_EVENT from the queue */ 517 } 518 #endif 519 } 520 521 ch = fifo_peek(); 522 if (ch >= KEY_MIN) { 523 /* If not first in queue, somebody put this key there on purpose in 524 * emergency. Consider it higher priority than the unfinished 525 * keysequence we are parsing. 526 */ 527 peek = head; 528 /* assume the key is the last in fifo */ 529 t_dec(); /* remove the key */ 530 return ch; 531 } 532 533 TR(TRACE_IEVENT, ("ch: %s", _tracechar((unsigned char) ch))); 534 while ((ptr != NULL) && (ptr->ch != (unsigned char) ch)) 535 ptr = ptr->sibling; 536 537 if (ptr == NULL) { 538 TR(TRACE_IEVENT, ("ptr is null")); 539 break; 540 } 541 TR(TRACE_IEVENT, ("ptr=%p, ch=%d, value=%d", 542 ptr, ptr->ch, ptr->value)); 543 544 if (ptr->value != 0) { /* sequence terminated */ 545 TR(TRACE_IEVENT, ("end of sequence")); 546 if (peek == tail) 547 fifo_clear(); 548 else 549 head = peek; 550 return (ptr->value); 551 } 552 553 ptr = ptr->child; 554 555 if (!raw_key_in_fifo()) { 556 int rc; 557 558 TR(TRACE_IEVENT, ("waiting for rest of sequence")); 559 rc = check_mouse_activity(timeleft EVENTLIST_2nd(evl)); 560 #ifdef NCURSES_WGETCH_EVENTS 561 if (rc & 4) { 562 TR(TRACE_IEVENT, ("interrupted by a user event")); 563 /* FIXME Should have preserved remainder timeleft for reuse... */ 564 peek = head; /* Restart interpreting later */ 565 return KEY_EVENT; 566 } 567 #endif 568 if (!rc) { 569 TR(TRACE_IEVENT, ("ran out of time")); 570 break; 571 } 572 } 573 } 574 ch = fifo_pull(); 575 peek = head; 576 return ch; 577 } 578