1 /**************************************************************************** 2 * Copyright (c) 1998-2007,2008 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.99 2008/09/20 19:46:13 tom Exp $") 45 46 #include <fifo_defs.h> 47 48 #if USE_REENTRANT 49 #define GetEscdelay(sp) (sp)->_ESCDELAY 50 NCURSES_EXPORT(int) 51 NCURSES_PUBLIC_VAR(ESCDELAY) (void) 52 { 53 return SP ? GetEscdelay(SP) : 1000; 54 } 55 #else 56 #define GetEscdelay(sp) ESCDELAY 57 NCURSES_EXPORT_VAR(int) 58 ESCDELAY = 1000; /* max interval betw. chars in funkeys, in millisecs */ 59 #endif 60 61 #if NCURSES_EXT_FUNCS 62 NCURSES_EXPORT(int) 63 set_escdelay(int value) 64 { 65 int code = OK; 66 #if USE_REENTRANT 67 if (SP) { 68 SP->_ESCDELAY = value; 69 } else { 70 code = ERR; 71 } 72 #else 73 ESCDELAY = value; 74 #endif 75 return code; 76 } 77 #endif 78 79 static int 80 _nc_use_meta(WINDOW *win) 81 { 82 SCREEN *sp = _nc_screen_of(win); 83 return (sp ? sp->_use_meta : 0); 84 } 85 86 #ifdef NCURSES_WGETCH_EVENTS 87 #define TWAIT_MASK 7 88 #else 89 #define TWAIT_MASK 3 90 #endif 91 92 /* 93 * Check for mouse activity, returning nonzero if we find any. 94 */ 95 static int 96 check_mouse_activity(SCREEN *sp, int delay EVENTLIST_2nd(_nc_eventlist * evl)) 97 { 98 int rc; 99 100 #if USE_SYSMOUSE 101 if ((sp->_mouse_type == M_SYSMOUSE) 102 && (sp->_sysmouse_head < sp->_sysmouse_tail)) { 103 return 2; 104 } 105 #endif 106 rc = _nc_timed_wait(sp, TWAIT_MASK, delay, (int *) 0 EVENTLIST_2nd(evl)); 107 #if USE_SYSMOUSE 108 if ((sp->_mouse_type == M_SYSMOUSE) 109 && (sp->_sysmouse_head < sp->_sysmouse_tail) 110 && (rc == 0) 111 && (errno == EINTR)) { 112 rc |= 2; 113 } 114 #endif 115 return rc; 116 } 117 118 static NCURSES_INLINE int 119 fifo_peek(SCREEN *sp) 120 { 121 int ch = sp->_fifo[peek]; 122 TR(TRACE_IEVENT, ("peeking at %d", peek)); 123 124 p_inc(); 125 return ch; 126 } 127 128 static NCURSES_INLINE int 129 fifo_pull(SCREEN *sp) 130 { 131 int ch; 132 ch = sp->_fifo[head]; 133 TR(TRACE_IEVENT, ("pulling %s from %d", _nc_tracechar(sp, ch), head)); 134 135 if (peek == head) { 136 h_inc(); 137 peek = head; 138 } else 139 h_inc(); 140 141 #ifdef TRACE 142 if (USE_TRACEF(TRACE_IEVENT)) { 143 _nc_fifo_dump(sp); 144 _nc_unlock_global(tracef); 145 } 146 #endif 147 return ch; 148 } 149 150 static NCURSES_INLINE int 151 fifo_push(SCREEN *sp EVENTLIST_2nd(_nc_eventlist * evl)) 152 { 153 int n; 154 int ch = 0; 155 int mask = 0; 156 157 (void) mask; 158 if (tail == -1) 159 return ERR; 160 161 #ifdef HIDE_EINTR 162 again: 163 errno = 0; 164 #endif 165 166 #ifdef NCURSES_WGETCH_EVENTS 167 if (evl 168 #if USE_GPM_SUPPORT || USE_EMX_MOUSE || USE_SYSMOUSE 169 || (sp->_mouse_fd >= 0) 170 #endif 171 ) { 172 mask = check_mouse_activity(sp, -1 EVENTLIST_2nd(evl)); 173 } else 174 mask = 0; 175 176 if (mask & 4) { 177 T(("fifo_push: ungetch KEY_EVENT")); 178 _nc_ungetch(sp, KEY_EVENT); 179 return KEY_EVENT; 180 } 181 #elif USE_GPM_SUPPORT || USE_EMX_MOUSE || USE_SYSMOUSE 182 if (sp->_mouse_fd >= 0) { 183 mask = check_mouse_activity(sp, -1 EVENTLIST_2nd(evl)); 184 } 185 #endif 186 187 #if USE_GPM_SUPPORT || USE_EMX_MOUSE 188 if ((sp->_mouse_fd >= 0) && (mask & 2)) { 189 sp->_mouse_event(sp); 190 ch = KEY_MOUSE; 191 n = 1; 192 } else 193 #endif 194 #if USE_SYSMOUSE 195 if ((sp->_mouse_type == M_SYSMOUSE) 196 && (sp->_sysmouse_head < sp->_sysmouse_tail)) { 197 sp->_mouse_event(sp); 198 ch = KEY_MOUSE; 199 n = 1; 200 } else if ((sp->_mouse_type == M_SYSMOUSE) 201 && (mask <= 0) && errno == EINTR) { 202 sp->_mouse_event(sp); 203 ch = KEY_MOUSE; 204 n = 1; 205 } else 206 #endif 207 { /* Can block... */ 208 unsigned char c2 = 0; 209 n = read(sp->_ifd, &c2, 1); 210 ch = c2; 211 } 212 213 #ifdef HIDE_EINTR 214 /* 215 * Under System V curses with non-restarting signals, getch() returns 216 * with value ERR when a handled signal keeps it from completing. 217 * If signals restart system calls, OTOH, the signal is invisible 218 * except to its handler. 219 * 220 * We don't want this difference to show. This piece of code 221 * tries to make it look like we always have restarting signals. 222 */ 223 if (n <= 0 && errno == EINTR) 224 goto again; 225 #endif 226 227 if ((n == -1) || (n == 0)) { 228 TR(TRACE_IEVENT, ("read(%d,&ch,1)=%d, errno=%d", sp->_ifd, n, errno)); 229 ch = ERR; 230 } 231 TR(TRACE_IEVENT, ("read %d characters", n)); 232 233 sp->_fifo[tail] = ch; 234 sp->_fifohold = 0; 235 if (head == -1) 236 head = peek = tail; 237 t_inc(); 238 TR(TRACE_IEVENT, ("pushed %s at %d", _nc_tracechar(sp, ch), tail)); 239 #ifdef TRACE 240 if (USE_TRACEF(TRACE_IEVENT)) { 241 _nc_fifo_dump(sp); 242 _nc_unlock_global(tracef); 243 } 244 #endif 245 return ch; 246 } 247 248 static NCURSES_INLINE void 249 fifo_clear(SCREEN *sp) 250 { 251 memset(sp->_fifo, 0, sizeof(sp->_fifo)); 252 head = -1; 253 tail = peek = 0; 254 } 255 256 static int kgetch(SCREEN *EVENTLIST_2nd(_nc_eventlist * evl)); 257 258 static void 259 recur_wrefresh(WINDOW *win) 260 { 261 #ifdef USE_PTHREADS 262 SCREEN *sp = _nc_screen_of(win); 263 if (_nc_use_pthreads && sp != SP) { 264 SCREEN *save_SP; 265 266 /* temporarily switch to the window's screen to check/refresh */ 267 _nc_lock_global(curses); 268 save_SP = SP; 269 _nc_set_screen(sp); 270 recur_wrefresh(win); 271 _nc_set_screen(save_SP); 272 _nc_unlock_global(curses); 273 } else 274 #endif 275 if ((is_wintouched(win) || (win->_flags & _HASMOVED)) 276 && !(win->_flags & _ISPAD)) { 277 wrefresh(win); 278 } 279 } 280 281 static int 282 recur_wgetnstr(WINDOW *win, char *buf) 283 { 284 SCREEN *sp = _nc_screen_of(win); 285 int rc; 286 287 if (sp != 0) { 288 #ifdef USE_PTHREADS 289 if (_nc_use_pthreads && sp != SP) { 290 SCREEN *save_SP; 291 292 /* temporarily switch to the window's screen to get cooked input */ 293 _nc_lock_global(curses); 294 save_SP = SP; 295 _nc_set_screen(sp); 296 rc = recur_wgetnstr(win, buf); 297 _nc_set_screen(save_SP); 298 _nc_unlock_global(curses); 299 } else 300 #endif 301 { 302 sp->_called_wgetch = TRUE; 303 rc = wgetnstr(win, buf, MAXCOLUMNS); 304 sp->_called_wgetch = FALSE; 305 } 306 } else { 307 rc = ERR; 308 } 309 return rc; 310 } 311 312 NCURSES_EXPORT(int) 313 _nc_wgetch(WINDOW *win, 314 unsigned long *result, 315 int use_meta 316 EVENTLIST_2nd(_nc_eventlist * evl)) 317 { 318 SCREEN *sp; 319 int ch; 320 #ifdef NCURSES_WGETCH_EVENTS 321 long event_delay = -1; 322 #endif 323 324 T((T_CALLED("_nc_wgetch(%p)"), win)); 325 326 *result = 0; 327 328 sp = _nc_screen_of(win); 329 if (win == 0 || sp == 0) { 330 returnCode(ERR); 331 } 332 333 if (cooked_key_in_fifo()) { 334 recur_wrefresh(win); 335 *result = fifo_pull(sp); 336 returnCode(*result >= KEY_MIN ? KEY_CODE_YES : OK); 337 } 338 #ifdef NCURSES_WGETCH_EVENTS 339 if (evl && (evl->count == 0)) 340 evl = NULL; 341 event_delay = _nc_eventlist_timeout(evl); 342 #endif 343 344 /* 345 * Handle cooked mode. Grab a string from the screen, 346 * stuff its contents in the FIFO queue, and pop off 347 * the first character to return it. 348 */ 349 if (head == -1 && 350 !sp->_notty && 351 !sp->_raw && 352 !sp->_cbreak && 353 !sp->_called_wgetch) { 354 char buf[MAXCOLUMNS], *bufp; 355 int rc; 356 357 TR(TRACE_IEVENT, ("filling queue in cooked mode")); 358 359 rc = recur_wgetnstr(win, buf); 360 361 /* ungetch in reverse order */ 362 #ifdef NCURSES_WGETCH_EVENTS 363 if (rc != KEY_EVENT) 364 #endif 365 _nc_ungetch(sp, '\n'); 366 for (bufp = buf + strlen(buf); bufp > buf; bufp--) 367 _nc_ungetch(sp, bufp[-1]); 368 369 #ifdef NCURSES_WGETCH_EVENTS 370 /* Return it first */ 371 if (rc == KEY_EVENT) { 372 *result = rc; 373 } else 374 #endif 375 *result = fifo_pull(sp); 376 returnCode(*result >= KEY_MIN ? KEY_CODE_YES : OK); 377 } 378 379 if (win->_use_keypad != sp->_keypad_on) 380 _nc_keypad(sp, win->_use_keypad); 381 382 recur_wrefresh(win); 383 384 if (win->_notimeout || (win->_delay >= 0) || (sp->_cbreak > 1)) { 385 if (head == -1) { /* fifo is empty */ 386 int delay; 387 int rc; 388 389 TR(TRACE_IEVENT, ("timed delay in wgetch()")); 390 if (sp->_cbreak > 1) 391 delay = (sp->_cbreak - 1) * 100; 392 else 393 delay = win->_delay; 394 395 #ifdef NCURSES_WGETCH_EVENTS 396 if (event_delay >= 0 && delay > event_delay) 397 delay = event_delay; 398 #endif 399 400 TR(TRACE_IEVENT, ("delay is %d milliseconds", delay)); 401 402 rc = check_mouse_activity(sp, delay EVENTLIST_2nd(evl)); 403 404 #ifdef NCURSES_WGETCH_EVENTS 405 if (rc & 4) { 406 *result = KEY_EVENT; 407 returnCode(KEY_CODE_YES); 408 } 409 #endif 410 if (!rc) { 411 returnCode(ERR); 412 } 413 } 414 /* else go on to read data available */ 415 } 416 417 if (win->_use_keypad) { 418 /* 419 * This is tricky. We only want to get special-key 420 * events one at a time. But we want to accumulate 421 * mouse events until either (a) the mouse logic tells 422 * us it's picked up a complete gesture, or (b) 423 * there's a detectable time lapse after one. 424 * 425 * Note: if the mouse code starts failing to compose 426 * press/release events into clicks, you should probably 427 * increase the wait with mouseinterval(). 428 */ 429 int runcount = 0; 430 int rc; 431 432 do { 433 ch = kgetch(sp EVENTLIST_2nd(evl)); 434 if (ch == KEY_MOUSE) { 435 ++runcount; 436 if (sp->_mouse_inline(sp)) 437 break; 438 } 439 if (sp->_maxclick < 0) 440 break; 441 } while 442 (ch == KEY_MOUSE 443 && (((rc = check_mouse_activity(sp, sp->_maxclick 444 EVENTLIST_2nd(evl))) != 0 445 && !(rc & 4)) 446 || !sp->_mouse_parse(sp, runcount))); 447 #ifdef NCURSES_WGETCH_EVENTS 448 if ((rc & 4) && !ch == KEY_EVENT) { 449 _nc_ungetch(sp, ch); 450 ch = KEY_EVENT; 451 } 452 #endif 453 if (runcount > 0 && ch != KEY_MOUSE) { 454 #ifdef NCURSES_WGETCH_EVENTS 455 /* mouse event sequence ended by an event, report event */ 456 if (ch == KEY_EVENT) { 457 _nc_ungetch(sp, KEY_MOUSE); /* FIXME This interrupts a gesture... */ 458 } else 459 #endif 460 { 461 /* mouse event sequence ended by keystroke, store keystroke */ 462 _nc_ungetch(sp, ch); 463 ch = KEY_MOUSE; 464 } 465 } 466 } else { 467 if (head == -1) 468 fifo_push(sp EVENTLIST_2nd(evl)); 469 ch = fifo_pull(sp); 470 } 471 472 if (ch == ERR) { 473 #if USE_SIZECHANGE 474 if (_nc_handle_sigwinch(sp)) { 475 _nc_update_screensize(sp); 476 /* resizeterm can push KEY_RESIZE */ 477 if (cooked_key_in_fifo()) { 478 *result = fifo_pull(sp); 479 returnCode(*result >= KEY_MIN ? KEY_CODE_YES : OK); 480 } 481 } 482 #endif 483 returnCode(ERR); 484 } 485 486 /* 487 * If echo() is in effect, display the printable version of the 488 * key on the screen. Carriage return and backspace are treated 489 * specially by Solaris curses: 490 * 491 * If carriage return is defined as a function key in the 492 * terminfo, e.g., kent, then Solaris may return either ^J (or ^M 493 * if nonl() is set) or KEY_ENTER depending on the echo() mode. 494 * We echo before translating carriage return based on nonl(), 495 * since the visual result simply moves the cursor to column 0. 496 * 497 * Backspace is a different matter. Solaris curses does not 498 * translate it to KEY_BACKSPACE if kbs=^H. This does not depend 499 * on the stty modes, but appears to be a hardcoded special case. 500 * This is a difference from ncurses, which uses the terminfo entry. 501 * However, we provide the same visual result as Solaris, moving the 502 * cursor to the left. 503 */ 504 if (sp->_echo && !(win->_flags & _ISPAD)) { 505 chtype backup = (ch == KEY_BACKSPACE) ? '\b' : ch; 506 if (backup < KEY_MIN) 507 wechochar(win, backup); 508 } 509 510 /* 511 * Simulate ICRNL mode 512 */ 513 if ((ch == '\r') && sp->_nl) 514 ch = '\n'; 515 516 /* Strip 8th-bit if so desired. We do this only for characters that 517 * are in the range 128-255, to provide compatibility with terminals 518 * that display only 7-bit characters. Note that 'ch' may be a 519 * function key at this point, so we mustn't strip _those_. 520 */ 521 if (!use_meta) 522 if ((ch < KEY_MIN) && (ch & 0x80)) 523 ch &= 0x7f; 524 525 T(("wgetch returning : %s", _nc_tracechar(sp, ch))); 526 527 *result = ch; 528 returnCode(ch >= KEY_MIN ? KEY_CODE_YES : OK); 529 } 530 531 #ifdef NCURSES_WGETCH_EVENTS 532 NCURSES_EXPORT(int) 533 wgetch_events(WINDOW *win, _nc_eventlist * evl) 534 { 535 int code; 536 unsigned long value; 537 538 T((T_CALLED("wgetch_events(%p,%p)"), win, evl)); 539 code = _nc_wgetch(win, 540 &value, 541 _nc_use_meta(win) 542 EVENTLIST_2nd(evl)); 543 if (code != ERR) 544 code = value; 545 returnCode(code); 546 } 547 #endif 548 549 NCURSES_EXPORT(int) 550 wgetch(WINDOW *win) 551 { 552 int code; 553 unsigned long value; 554 555 T((T_CALLED("wgetch(%p)"), win)); 556 code = _nc_wgetch(win, 557 &value, 558 _nc_use_meta(win) 559 EVENTLIST_2nd((_nc_eventlist *) 0)); 560 if (code != ERR) 561 code = value; 562 returnCode(code); 563 } 564 565 /* 566 ** int 567 ** kgetch() 568 ** 569 ** Get an input character, but take care of keypad sequences, returning 570 ** an appropriate code when one matches the input. After each character 571 ** is received, set an alarm call based on ESCDELAY. If no more of the 572 ** sequence is received by the time the alarm goes off, pass through 573 ** the sequence gotten so far. 574 ** 575 ** This function must be called when there are no cooked keys in queue. 576 ** (that is head==-1 || peek==head) 577 ** 578 */ 579 580 static int 581 kgetch(SCREEN *sp EVENTLIST_2nd(_nc_eventlist * evl)) 582 { 583 TRIES *ptr; 584 int ch = 0; 585 int timeleft = GetEscdelay(sp); 586 587 TR(TRACE_IEVENT, ("kgetch() called")); 588 589 ptr = sp->_keytry; 590 591 for (;;) { 592 if (cooked_key_in_fifo() && sp->_fifo[head] >= KEY_MIN) { 593 break; 594 } else if (!raw_key_in_fifo()) { 595 ch = fifo_push(sp EVENTLIST_2nd(evl)); 596 if (ch == ERR) { 597 peek = head; /* the keys stay uninterpreted */ 598 return ERR; 599 } 600 #ifdef NCURSES_WGETCH_EVENTS 601 else if (ch == KEY_EVENT) { 602 peek = head; /* the keys stay uninterpreted */ 603 return fifo_pull(sp); /* Remove KEY_EVENT from the queue */ 604 } 605 #endif 606 } 607 608 ch = fifo_peek(sp); 609 if (ch >= KEY_MIN) { 610 /* If not first in queue, somebody put this key there on purpose in 611 * emergency. Consider it higher priority than the unfinished 612 * keysequence we are parsing. 613 */ 614 peek = head; 615 /* assume the key is the last in fifo */ 616 t_dec(); /* remove the key */ 617 return ch; 618 } 619 620 TR(TRACE_IEVENT, ("ch: %s", _nc_tracechar(sp, (unsigned char) ch))); 621 while ((ptr != NULL) && (ptr->ch != (unsigned char) ch)) 622 ptr = ptr->sibling; 623 624 if (ptr == NULL) { 625 TR(TRACE_IEVENT, ("ptr is null")); 626 break; 627 } 628 TR(TRACE_IEVENT, ("ptr=%p, ch=%d, value=%d", 629 ptr, ptr->ch, ptr->value)); 630 631 if (ptr->value != 0) { /* sequence terminated */ 632 TR(TRACE_IEVENT, ("end of sequence")); 633 if (peek == tail) 634 fifo_clear(sp); 635 else 636 head = peek; 637 return (ptr->value); 638 } 639 640 ptr = ptr->child; 641 642 if (!raw_key_in_fifo()) { 643 int rc; 644 645 TR(TRACE_IEVENT, ("waiting for rest of sequence")); 646 rc = check_mouse_activity(sp, timeleft EVENTLIST_2nd(evl)); 647 #ifdef NCURSES_WGETCH_EVENTS 648 if (rc & 4) { 649 TR(TRACE_IEVENT, ("interrupted by a user event")); 650 /* FIXME Should have preserved remainder timeleft for reuse... */ 651 peek = head; /* Restart interpreting later */ 652 return KEY_EVENT; 653 } 654 #endif 655 if (!rc) { 656 TR(TRACE_IEVENT, ("ran out of time")); 657 break; 658 } 659 } 660 } 661 ch = fifo_pull(sp); 662 peek = head; 663 return ch; 664 } 665