1 /* $OpenBSD: lib_getch.c,v 1.7 2000/10/08 22:46:58 millert Exp $ */ 2 3 /**************************************************************************** 4 * Copyright (c) 1998,1999,2000 Free Software Foundation, Inc. * 5 * * 6 * Permission is hereby granted, free of charge, to any person obtaining a * 7 * copy of this software and associated documentation files (the * 8 * "Software"), to deal in the Software without restriction, including * 9 * without limitation the rights to use, copy, modify, merge, publish, * 10 * distribute, distribute with modifications, sublicense, and/or sell * 11 * copies of the Software, and to permit persons to whom the Software is * 12 * furnished to do so, subject to the following conditions: * 13 * * 14 * The above copyright notice and this permission notice shall be included * 15 * in all copies or substantial portions of the Software. * 16 * * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 20 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 23 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 24 * * 25 * Except as contained in this notice, the name(s) of the above copyright * 26 * holders shall not be used in advertising or otherwise to promote the * 27 * sale, use or other dealings in this Software without prior written * 28 * authorization. * 29 ****************************************************************************/ 30 31 /**************************************************************************** 32 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 33 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 34 ****************************************************************************/ 35 36 /* 37 ** lib_getch.c 38 ** 39 ** The routine getch(). 40 ** 41 */ 42 43 #include <curses.priv.h> 44 45 MODULE_ID("$From: lib_getch.c,v 1.49 2000/07/29 15:45:24 tom Exp $") 46 47 #include <fifo_defs.h> 48 49 int ESCDELAY = 1000; /* max interval betw. chars in funkeys, in millisecs */ 50 51 #ifdef USE_EMX_MOUSE 52 # include <sys/select.h> 53 static int 54 kbd_mouse_read(unsigned char *p) 55 { 56 fd_set fdset; 57 int nums = SP->_ifd + 1; 58 59 for (;;) { 60 FD_ZERO(&fdset); 61 FD_SET(SP->_ifd, &fdset); 62 if (SP->_checkfd >= 0) { 63 FD_SET(SP->_checkfd, &fdset); 64 if (SP->_checkfd >= nums) 65 nums = SP->_checkfd + 1; 66 } 67 if (SP->_mouse_fd >= 0) { 68 FD_SET(SP->_mouse_fd, &fdset); 69 if (SP->_mouse_fd >= nums) 70 nums = SP->_mouse_fd + 1; 71 } 72 if (select(nums, &fdset, NULL, NULL, NULL) >= 0) { 73 int n; 74 75 if (SP->_mouse_fd >= 0 76 && FD_ISSET(SP->_mouse_fd, &fdset)) { /* Prefer mouse */ 77 n = read(SP->_mouse_fd, p, 1); 78 } else { 79 n = read(SP->_ifd, p, 1); 80 } 81 return n; 82 } 83 if (errno != EINTR) { 84 return -1; 85 } 86 } 87 } 88 #endif /* USE_EMX_MOUSE */ 89 90 static inline int 91 fifo_peek(void) 92 { 93 int ch = SP->_fifo[peek]; 94 TR(TRACE_IEVENT, ("peeking at %d", peek)); 95 96 p_inc(); 97 return ch; 98 } 99 100 static inline int 101 fifo_pull(void) 102 { 103 int ch; 104 ch = SP->_fifo[head]; 105 TR(TRACE_IEVENT, ("pulling %d from %d", ch, head)); 106 107 if (peek == head) { 108 h_inc(); 109 peek = head; 110 } else 111 h_inc(); 112 113 #ifdef TRACE 114 if (_nc_tracing & TRACE_IEVENT) 115 _nc_fifo_dump(); 116 #endif 117 return ch; 118 } 119 120 static inline int 121 fifo_push(void) 122 { 123 int n; 124 unsigned int ch; 125 126 if (tail == -1) 127 return ERR; 128 129 #ifdef HIDE_EINTR 130 again: 131 errno = 0; 132 #endif 133 134 #if USE_GPM_SUPPORT 135 if ((SP->_mouse_fd >= 0) 136 && (_nc_timed_wait(3, -1, (int *) 0) & 2)) { 137 SP->_mouse_event(SP); 138 ch = KEY_MOUSE; 139 n = 1; 140 } else 141 #endif 142 { 143 unsigned char c2 = 0; 144 #ifdef USE_EMX_MOUSE 145 n = kbd_mouse_read(&c2); 146 #else 147 n = read(SP->_ifd, &c2, 1); 148 #endif 149 ch = c2 & 0xff; 150 } 151 152 #ifdef HIDE_EINTR 153 /* 154 * Under System V curses with non-restarting signals, getch() returns 155 * with value ERR when a handled signal keeps it from completing. 156 * If signals restart system calls, OTOH, the signal is invisible 157 * except to its handler. 158 * 159 * We don't want this difference to show. This piece of code 160 * tries to make it look like we always have restarting signals. 161 */ 162 if (n <= 0 && errno == EINTR) 163 goto again; 164 #endif 165 166 if ((n == -1) || (n == 0)) { 167 TR(TRACE_IEVENT, ("read(%d,&ch,1)=%d, errno=%d", SP->_ifd, n, errno)); 168 ch = ERR; 169 } 170 TR(TRACE_IEVENT, ("read %d characters", n)); 171 172 SP->_fifo[tail] = ch; 173 SP->_fifohold = 0; 174 if (head == -1) 175 head = peek = tail; 176 t_inc(); 177 TR(TRACE_IEVENT, ("pushed %#x at %d", ch, tail)); 178 #ifdef TRACE 179 if (_nc_tracing & TRACE_IEVENT) 180 _nc_fifo_dump(); 181 #endif 182 return ch; 183 } 184 185 static inline void 186 fifo_clear(void) 187 { 188 int i; 189 for (i = 0; i < FIFO_SIZE; i++) 190 SP->_fifo[i] = 0; 191 head = -1; 192 tail = peek = 0; 193 } 194 195 static int kgetch(WINDOW *); 196 197 #define wgetch_should_refresh(win) (\ 198 (is_wintouched(win) || (win->_flags & _HASMOVED)) \ 199 && !(win->_flags & _ISPAD)) 200 201 int 202 wgetch(WINDOW *win) 203 { 204 int ch; 205 206 T((T_CALLED("wgetch(%p)"), win)); 207 208 if (!win) 209 returnCode(ERR); 210 211 if (cooked_key_in_fifo()) { 212 if (wgetch_should_refresh(win)) 213 wrefresh(win); 214 215 ch = fifo_pull(); 216 T(("wgetch returning (pre-cooked): %#x = %s", ch, _trace_key(ch))); 217 returnCode(ch); 218 } 219 220 /* 221 * Handle cooked mode. Grab a string from the screen, 222 * stuff its contents in the FIFO queue, and pop off 223 * the first character to return it. 224 */ 225 if (head == -1 && !SP->_raw && !SP->_cbreak) { 226 char buf[MAXCOLUMNS], *sp; 227 228 TR(TRACE_IEVENT, ("filling queue in cooked mode")); 229 230 wgetnstr(win, buf, MAXCOLUMNS); 231 232 /* ungetch in reverse order */ 233 ungetch('\n'); 234 for (sp = buf + strlen(buf); sp > buf; sp--) 235 ungetch(sp[-1]); 236 237 returnCode(fifo_pull()); 238 } 239 240 if (wgetch_should_refresh(win)) 241 wrefresh(win); 242 243 if (!win->_notimeout && (win->_delay >= 0 || SP->_cbreak > 1)) { 244 int delay; 245 246 TR(TRACE_IEVENT, ("timed delay in wgetch()")); 247 if (SP->_cbreak > 1) 248 delay = (SP->_cbreak - 1) * 100; 249 else 250 delay = win->_delay; 251 252 TR(TRACE_IEVENT, ("delay is %d milliseconds", delay)); 253 254 if (head == -1) /* fifo is empty */ 255 if (!_nc_timed_wait(3, delay, (int *) 0)) 256 returnCode(ERR); 257 /* else go on to read data available */ 258 } 259 260 if (win->_use_keypad) { 261 /* 262 * This is tricky. We only want to get special-key 263 * events one at a time. But we want to accumulate 264 * mouse events until either (a) the mouse logic tells 265 * us it's picked up a complete gesture, or (b) 266 * there's a detectable time lapse after one. 267 * 268 * Note: if the mouse code starts failing to compose 269 * press/release events into clicks, you should probably 270 * increase the wait with mouseinterval(). 271 */ 272 int runcount = 0; 273 274 do { 275 ch = kgetch(win); 276 if (ch == KEY_MOUSE) { 277 ++runcount; 278 if (SP->_mouse_inline(SP)) 279 break; 280 } 281 } while 282 (ch == KEY_MOUSE 283 && (_nc_timed_wait(3, SP->_maxclick, (int *) 0) 284 || !SP->_mouse_parse(runcount))); 285 if (runcount > 0 && ch != KEY_MOUSE) { 286 /* mouse event sequence ended by keystroke, push it */ 287 ungetch(ch); 288 ch = KEY_MOUSE; 289 } 290 } else { 291 if (head == -1) 292 fifo_push(); 293 ch = fifo_pull(); 294 } 295 296 if (ch == ERR) { 297 #if USE_SIZECHANGE 298 if (SP->_sig_winch) { 299 _nc_update_screensize(); 300 /* resizeterm can push KEY_RESIZE */ 301 if (cooked_key_in_fifo()) { 302 ch = fifo_pull(); 303 T(("wgetch returning (pre-cooked): %#x = %s", ch, _trace_key(ch))); 304 returnCode(ch); 305 } 306 } 307 #endif 308 T(("wgetch returning ERR")); 309 returnCode(ERR); 310 } 311 312 /* 313 * If echo() is in effect, display the printable version of the 314 * key on the screen. Carriage return and backspace are treated 315 * specially by Solaris curses: 316 * 317 * If carriage return is defined as a function key in the 318 * terminfo, e.g., kent, then Solaris may return either ^J (or ^M 319 * if nonl() is set) or KEY_ENTER depending on the echo() mode. 320 * We echo before translating carriage return based on nonl(), 321 * since the visual result simply moves the cursor to column 0. 322 * 323 * Backspace is a different matter. Solaris curses does not 324 * translate it to KEY_BACKSPACE if kbs=^H. This does not depend 325 * on the stty modes, but appears to be a hardcoded special case. 326 * This is a difference from ncurses, which uses the terminfo entry. 327 * However, we provide the same visual result as Solaris, moving the 328 * cursor to the left. 329 */ 330 if (SP->_echo && !(win->_flags & _ISPAD)) { 331 chtype backup = (ch == KEY_BACKSPACE) ? '\b' : ch; 332 if (backup < KEY_MIN) 333 wechochar(win, backup); 334 } 335 336 /* 337 * Simulate ICRNL mode 338 */ 339 if ((ch == '\r') && SP->_nl) 340 ch = '\n'; 341 342 /* Strip 8th-bit if so desired. We do this only for characters that 343 * are in the range 128-255, to provide compatibility with terminals 344 * that display only 7-bit characters. Note that 'ch' may be a 345 * function key at this point, so we mustn't strip _those_. 346 */ 347 if ((ch < KEY_MIN) && (ch & 0x80)) 348 if (!SP->_use_meta) 349 ch &= 0x7f; 350 351 T(("wgetch returning : %#x = %s", ch, _trace_key(ch))); 352 353 returnCode(ch); 354 } 355 356 /* 357 ** int 358 ** kgetch() 359 ** 360 ** Get an input character, but take care of keypad sequences, returning 361 ** an appropriate code when one matches the input. After each character 362 ** is received, set an alarm call based on ESCDELAY. If no more of the 363 ** sequence is received by the time the alarm goes off, pass through 364 ** the sequence gotten so far. 365 ** 366 ** This function must be called when there is no cooked keys in queue. 367 ** (that is head==-1 || peek==head) 368 ** 369 */ 370 371 static int 372 kgetch(WINDOW *win GCC_UNUSED) 373 { 374 struct tries *ptr; 375 int ch = 0; 376 int timeleft = ESCDELAY; 377 378 TR(TRACE_IEVENT, ("kgetch(%p) called", win)); 379 380 ptr = SP->_keytry; 381 382 for (;;) { 383 if (!raw_key_in_fifo()) { 384 if (fifo_push() == ERR) { 385 peek = head; /* the keys stay uninterpreted */ 386 return ERR; 387 } 388 } 389 ch = fifo_peek(); 390 if (ch >= KEY_MIN) { 391 peek = head; 392 /* assume the key is the last in fifo */ 393 t_dec(); /* remove the key */ 394 return ch; 395 } 396 397 TR(TRACE_IEVENT, ("ch: %s", _trace_key((unsigned char) ch))); 398 while ((ptr != NULL) && (ptr->ch != (unsigned char) ch)) 399 ptr = ptr->sibling; 400 #ifdef TRACE 401 if (ptr == NULL) { 402 TR(TRACE_IEVENT, ("ptr is null")); 403 } else 404 TR(TRACE_IEVENT, ("ptr=%p, ch=%d, value=%d", 405 ptr, ptr->ch, ptr->value)); 406 #endif /* TRACE */ 407 408 if (ptr == NULL) 409 break; 410 411 if (ptr->value != 0) { /* sequence terminated */ 412 TR(TRACE_IEVENT, ("end of sequence")); 413 if (peek == tail) 414 fifo_clear(); 415 else 416 head = peek; 417 return (ptr->value); 418 } 419 420 ptr = ptr->child; 421 422 if (!raw_key_in_fifo()) { 423 TR(TRACE_IEVENT, ("waiting for rest of sequence")); 424 if (!_nc_timed_wait(3, timeleft, &timeleft)) { 425 TR(TRACE_IEVENT, ("ran out of time")); 426 break; 427 } 428 } 429 } 430 ch = fifo_pull(); 431 peek = head; 432 return ch; 433 } 434