1/**************************************************************************** 2 * Copyright (c) 1998-2003,2005 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.73 2005/06/11 18:08:57 tom Exp $") 45 46#include <fifo_defs.h> 47 48NCURSES_EXPORT_VAR(int) 49ESCDELAY = 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 */ 60static int 61check_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 83static inline int 84fifo_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 93static inline int 94fifo_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 113static inline int 114fifo_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 209static inline void 210fifo_clear(void) 211{ 212 memset(SP->_fifo, 0, sizeof(SP->_fifo)); 213 head = -1; 214 tail = peek = 0; 215} 216 217static 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 223NCURSES_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 int delay; 300 301 TR(TRACE_IEVENT, ("timed delay in wgetch()")); 302 if (SP->_cbreak > 1) 303 delay = (SP->_cbreak - 1) * 100; 304 else 305 delay = win->_delay; 306 307#ifdef NCURSES_WGETCH_EVENTS 308 if (event_delay >= 0 && delay > event_delay) 309 delay = event_delay; 310#endif 311 312 TR(TRACE_IEVENT, ("delay is %d milliseconds", delay)); 313 314 if (head == -1) { /* fifo is empty */ 315 int rc = check_mouse_activity(delay EVENTLIST_2nd(evl)); 316 317#ifdef NCURSES_WGETCH_EVENTS 318 if (rc & 4) { 319 *result = KEY_EVENT; 320 returnCode(OK); 321 } 322#endif 323 if (!rc) 324 returnCode(ERR); 325 } 326 /* else go on to read data available */ 327 } 328 329 if (win->_use_keypad) { 330 /* 331 * This is tricky. We only want to get special-key 332 * events one at a time. But we want to accumulate 333 * mouse events until either (a) the mouse logic tells 334 * us it's picked up a complete gesture, or (b) 335 * there's a detectable time lapse after one. 336 * 337 * Note: if the mouse code starts failing to compose 338 * press/release events into clicks, you should probably 339 * increase the wait with mouseinterval(). 340 */ 341 int runcount = 0; 342 int rc; 343 344 do { 345 ch = kgetch(EVENTLIST_1st(evl)); 346 if (ch == KEY_MOUSE) { 347 ++runcount; 348 if (SP->_mouse_inline(SP)) 349 break; 350 } 351 if (SP->_maxclick < 0) 352 break; 353 } while 354 (ch == KEY_MOUSE 355 && (((rc = check_mouse_activity(SP->_maxclick 356 EVENTLIST_2nd(evl))) != 0 357 && !(rc & 4)) 358 || !SP->_mouse_parse(runcount))); 359#ifdef NCURSES_WGETCH_EVENTS 360 if ((rc & 4) && !ch == KEY_EVENT) { 361 ungetch(ch); 362 ch = KEY_EVENT; 363 } 364#endif 365 if (runcount > 0 && ch != KEY_MOUSE) { 366#ifdef NCURSES_WGETCH_EVENTS 367 /* mouse event sequence ended by an event, report event */ 368 if (ch == KEY_EVENT) { 369 ungetch(KEY_MOUSE); /* FIXME This interrupts a gesture... */ 370 } else 371#endif 372 { 373 /* mouse event sequence ended by keystroke, store keystroke */ 374 ungetch(ch); 375 ch = KEY_MOUSE; 376 } 377 } 378 } else { 379 if (head == -1) 380 fifo_push(EVENTLIST_1st(evl)); 381 ch = fifo_pull(); 382 } 383 384 if (ch == ERR) { 385#if USE_SIZECHANGE 386 if (SP->_sig_winch) { 387 _nc_update_screensize(); 388 /* resizeterm can push KEY_RESIZE */ 389 if (cooked_key_in_fifo()) { 390 *result = fifo_pull(); 391 returnCode(*result >= KEY_MIN ? KEY_CODE_YES : OK); 392 } 393 } 394#endif 395 returnCode(ERR); 396 } 397 398 /* 399 * If echo() is in effect, display the printable version of the 400 * key on the screen. Carriage return and backspace are treated 401 * specially by Solaris curses: 402 * 403 * If carriage return is defined as a function key in the 404 * terminfo, e.g., kent, then Solaris may return either ^J (or ^M 405 * if nonl() is set) or KEY_ENTER depending on the echo() mode. 406 * We echo before translating carriage return based on nonl(), 407 * since the visual result simply moves the cursor to column 0. 408 * 409 * Backspace is a different matter. Solaris curses does not 410 * translate it to KEY_BACKSPACE if kbs=^H. This does not depend 411 * on the stty modes, but appears to be a hardcoded special case. 412 * This is a difference from ncurses, which uses the terminfo entry. 413 * However, we provide the same visual result as Solaris, moving the 414 * cursor to the left. 415 */ 416 if (SP->_echo && !(win->_flags & _ISPAD)) { 417 chtype backup = (ch == KEY_BACKSPACE) ? '\b' : ch; 418 if (backup < KEY_MIN) 419 wechochar(win, backup); 420 } 421 422 /* 423 * Simulate ICRNL mode 424 */ 425 if ((ch == '\r') && SP->_nl) 426 ch = '\n'; 427 428 /* Strip 8th-bit if so desired. We do this only for characters that 429 * are in the range 128-255, to provide compatibility with terminals 430 * that display only 7-bit characters. Note that 'ch' may be a 431 * function key at this point, so we mustn't strip _those_. 432 */ 433 if (!use_meta) 434 if ((ch < KEY_MIN) && (ch & 0x80)) 435 ch &= 0x7f; 436 437 T(("wgetch returning : %s", _tracechar(ch))); 438 439 *result = ch; 440 returnCode(ch >= KEY_MIN ? KEY_CODE_YES : OK); 441} 442 443#ifdef NCURSES_WGETCH_EVENTS 444NCURSES_EXPORT(int) 445wgetch_events(WINDOW *win, _nc_eventlist * evl) 446{ 447 int code; 448 unsigned long value; 449 450 T((T_CALLED("wgetch_events(%p,%p)"), win, evl)); 451 code = _nc_wgetch(win, 452 &value, 453 SP->_use_meta 454 EVENTLIST_2nd(evl)); 455 if (code != ERR) 456 code = value; 457 returnCode(code); 458} 459#endif 460 461NCURSES_EXPORT(int) 462wgetch(WINDOW *win) 463{ 464 int code; 465 unsigned long value; 466 467 T((T_CALLED("wgetch(%p)"), win)); 468 code = _nc_wgetch(win, 469 &value, 470 (SP ? SP->_use_meta : 0) 471 EVENTLIST_2nd((_nc_eventlist *) 0)); 472 if (code != ERR) 473 code = value; 474 returnCode(code); 475} 476 477/* 478** int 479** kgetch() 480** 481** Get an input character, but take care of keypad sequences, returning 482** an appropriate code when one matches the input. After each character 483** is received, set an alarm call based on ESCDELAY. If no more of the 484** sequence is received by the time the alarm goes off, pass through 485** the sequence gotten so far. 486** 487** This function must be called when there are no cooked keys in queue. 488** (that is head==-1 || peek==head) 489** 490*/ 491 492static int 493kgetch(EVENTLIST_0th(_nc_eventlist * evl)) 494{ 495 struct tries *ptr; 496 int ch = 0; 497 int timeleft = ESCDELAY; 498 499 TR(TRACE_IEVENT, ("kgetch() called")); 500 501 ptr = SP->_keytry; 502 503 for (;;) { 504 if (cooked_key_in_fifo() && SP->_fifo[head] >= KEY_MIN) { 505 break; 506 } else if (!raw_key_in_fifo()) { 507 ch = fifo_push(EVENTLIST_1st(evl)); 508 if (ch == ERR) { 509 peek = head; /* the keys stay uninterpreted */ 510 return ERR; 511 } 512#ifdef NCURSES_WGETCH_EVENTS 513 else if (ch == KEY_EVENT) { 514 peek = head; /* the keys stay uninterpreted */ 515 return fifo_pull(); /* Remove KEY_EVENT from the queue */ 516 } 517#endif 518 } 519 520 ch = fifo_peek(); 521 if (ch >= KEY_MIN) { 522 /* If not first in queue, somebody put this key there on purpose in 523 * emergency. Consider it higher priority than the unfinished 524 * keysequence we are parsing. 525 */ 526 peek = head; 527 /* assume the key is the last in fifo */ 528 t_dec(); /* remove the key */ 529 return ch; 530 } 531 532 TR(TRACE_IEVENT, ("ch: %s", _tracechar((unsigned char) ch))); 533 while ((ptr != NULL) && (ptr->ch != (unsigned char) ch)) 534 ptr = ptr->sibling; 535 536 if (ptr == NULL) { 537 TR(TRACE_IEVENT, ("ptr is null")); 538 break; 539 } 540 TR(TRACE_IEVENT, ("ptr=%p, ch=%d, value=%d", 541 ptr, ptr->ch, ptr->value)); 542 543 if (ptr->value != 0) { /* sequence terminated */ 544 TR(TRACE_IEVENT, ("end of sequence")); 545 if (peek == tail) 546 fifo_clear(); 547 else 548 head = peek; 549 return (ptr->value); 550 } 551 552 ptr = ptr->child; 553 554 if (!raw_key_in_fifo()) { 555 int rc; 556 557 TR(TRACE_IEVENT, ("waiting for rest of sequence")); 558 rc = check_mouse_activity(timeleft EVENTLIST_2nd(evl)); 559#ifdef NCURSES_WGETCH_EVENTS 560 if (rc & 4) { 561 TR(TRACE_IEVENT, ("interrupted by a user event")); 562 /* FIXME Should have preserved remainder timeleft for reusal... */ 563 peek = head; /* Restart interpreting later */ 564 return KEY_EVENT; 565 } 566#endif 567 if (!rc) { 568 TR(TRACE_IEVENT, ("ran out of time")); 569 break; 570 } 571 } 572 } 573 ch = fifo_pull(); 574 peek = head; 575 return ch; 576} 577