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 44MODULE_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 50NCURSES_EXPORT(int) 51NCURSES_PUBLIC_VAR(ESCDELAY) (void) 52{ 53 return SP ? GetEscdelay(SP) : 1000; 54} 55#else 56#define GetEscdelay(sp) ESCDELAY 57NCURSES_EXPORT_VAR(int) 58ESCDELAY = 1000; /* max interval betw. chars in funkeys, in millisecs */ 59#endif 60 61#if NCURSES_EXT_FUNCS 62NCURSES_EXPORT(int) 63set_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 79static 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 */ 95static int 96check_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 118static NCURSES_INLINE int 119fifo_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 128static NCURSES_INLINE int 129fifo_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 150static NCURSES_INLINE int 151fifo_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 248static NCURSES_INLINE void 249fifo_clear(SCREEN *sp) 250{ 251 memset(sp->_fifo, 0, sizeof(sp->_fifo)); 252 head = -1; 253 tail = peek = 0; 254} 255 256static int kgetch(SCREEN *EVENTLIST_2nd(_nc_eventlist * evl)); 257 258static void 259recur_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 281static int 282recur_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 312NCURSES_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 /* 480 * Get the ERR from queue -- it is from WINCH, 481 * so we should take it out, the "error" is handled. 482 */ 483 if (fifo_peek(sp) == -1) 484 fifo_pull(sp); 485 returnCode(*result >= KEY_MIN ? KEY_CODE_YES : OK); 486 } 487 } 488#endif 489 returnCode(ERR); 490 } 491 492 /* 493 * If echo() is in effect, display the printable version of the 494 * key on the screen. Carriage return and backspace are treated 495 * specially by Solaris curses: 496 * 497 * If carriage return is defined as a function key in the 498 * terminfo, e.g., kent, then Solaris may return either ^J (or ^M 499 * if nonl() is set) or KEY_ENTER depending on the echo() mode. 500 * We echo before translating carriage return based on nonl(), 501 * since the visual result simply moves the cursor to column 0. 502 * 503 * Backspace is a different matter. Solaris curses does not 504 * translate it to KEY_BACKSPACE if kbs=^H. This does not depend 505 * on the stty modes, but appears to be a hardcoded special case. 506 * This is a difference from ncurses, which uses the terminfo entry. 507 * However, we provide the same visual result as Solaris, moving the 508 * cursor to the left. 509 */ 510 if (sp->_echo && !(win->_flags & _ISPAD)) { 511 chtype backup = (ch == KEY_BACKSPACE) ? '\b' : ch; 512 if (backup < KEY_MIN) 513 wechochar(win, backup); 514 } 515 516 /* 517 * Simulate ICRNL mode 518 */ 519 if ((ch == '\r') && sp->_nl) 520 ch = '\n'; 521 522 /* Strip 8th-bit if so desired. We do this only for characters that 523 * are in the range 128-255, to provide compatibility with terminals 524 * that display only 7-bit characters. Note that 'ch' may be a 525 * function key at this point, so we mustn't strip _those_. 526 */ 527 if (!use_meta) 528 if ((ch < KEY_MIN) && (ch & 0x80)) 529 ch &= 0x7f; 530 531 T(("wgetch returning : %s", _nc_tracechar(sp, ch))); 532 533 *result = ch; 534 returnCode(ch >= KEY_MIN ? KEY_CODE_YES : OK); 535} 536 537#ifdef NCURSES_WGETCH_EVENTS 538NCURSES_EXPORT(int) 539wgetch_events(WINDOW *win, _nc_eventlist * evl) 540{ 541 int code; 542 unsigned long value; 543 544 T((T_CALLED("wgetch_events(%p,%p)"), win, evl)); 545 code = _nc_wgetch(win, 546 &value, 547 _nc_use_meta(win) 548 EVENTLIST_2nd(evl)); 549 if (code != ERR) 550 code = value; 551 returnCode(code); 552} 553#endif 554 555NCURSES_EXPORT(int) 556wgetch(WINDOW *win) 557{ 558 int code; 559 unsigned long value; 560 561 T((T_CALLED("wgetch(%p)"), win)); 562 code = _nc_wgetch(win, 563 &value, 564 _nc_use_meta(win) 565 EVENTLIST_2nd((_nc_eventlist *) 0)); 566 if (code != ERR) 567 code = value; 568 returnCode(code); 569} 570 571/* 572** int 573** kgetch() 574** 575** Get an input character, but take care of keypad sequences, returning 576** an appropriate code when one matches the input. After each character 577** is received, set an alarm call based on ESCDELAY. If no more of the 578** sequence is received by the time the alarm goes off, pass through 579** the sequence gotten so far. 580** 581** This function must be called when there are no cooked keys in queue. 582** (that is head==-1 || peek==head) 583** 584*/ 585 586static int 587kgetch(SCREEN *sp EVENTLIST_2nd(_nc_eventlist * evl)) 588{ 589 TRIES *ptr; 590 int ch = 0; 591 int timeleft = GetEscdelay(sp); 592 593 TR(TRACE_IEVENT, ("kgetch() called")); 594 595 ptr = sp->_keytry; 596 597 for (;;) { 598 if (cooked_key_in_fifo() && sp->_fifo[head] >= KEY_MIN) { 599 break; 600 } else if (!raw_key_in_fifo()) { 601 ch = fifo_push(sp EVENTLIST_2nd(evl)); 602 if (ch == ERR) { 603 peek = head; /* the keys stay uninterpreted */ 604 return ERR; 605 } 606#ifdef NCURSES_WGETCH_EVENTS 607 else if (ch == KEY_EVENT) { 608 peek = head; /* the keys stay uninterpreted */ 609 return fifo_pull(sp); /* Remove KEY_EVENT from the queue */ 610 } 611#endif 612 } 613 614 ch = fifo_peek(sp); 615 if (ch >= KEY_MIN) { 616 /* If not first in queue, somebody put this key there on purpose in 617 * emergency. Consider it higher priority than the unfinished 618 * keysequence we are parsing. 619 */ 620 peek = head; 621 /* assume the key is the last in fifo */ 622 t_dec(); /* remove the key */ 623 return ch; 624 } 625 626 TR(TRACE_IEVENT, ("ch: %s", _nc_tracechar(sp, (unsigned char) ch))); 627 while ((ptr != NULL) && (ptr->ch != (unsigned char) ch)) 628 ptr = ptr->sibling; 629 630 if (ptr == NULL) { 631 TR(TRACE_IEVENT, ("ptr is null")); 632 break; 633 } 634 TR(TRACE_IEVENT, ("ptr=%p, ch=%d, value=%d", 635 ptr, ptr->ch, ptr->value)); 636 637 if (ptr->value != 0) { /* sequence terminated */ 638 TR(TRACE_IEVENT, ("end of sequence")); 639 if (peek == tail) 640 fifo_clear(sp); 641 else 642 head = peek; 643 return (ptr->value); 644 } 645 646 ptr = ptr->child; 647 648 if (!raw_key_in_fifo()) { 649 int rc; 650 651 TR(TRACE_IEVENT, ("waiting for rest of sequence")); 652 rc = check_mouse_activity(sp, timeleft EVENTLIST_2nd(evl)); 653#ifdef NCURSES_WGETCH_EVENTS 654 if (rc & 4) { 655 TR(TRACE_IEVENT, ("interrupted by a user event")); 656 /* FIXME Should have preserved remainder timeleft for reuse... */ 657 peek = head; /* Restart interpreting later */ 658 return KEY_EVENT; 659 } 660#endif 661 if (!rc) { 662 TR(TRACE_IEVENT, ("ran out of time")); 663 break; 664 } 665 } 666 } 667 ch = fifo_pull(sp); 668 peek = head; 669 return ch; 670} 671