1 /* $OpenBSD: lib_getch.c,v 1.10 2001/01/22 18:01:39 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.54 2000/12/10 02:43:27 tom Exp $") 46 47 #include <fifo_defs.h> 48 49 NCURSES_EXPORT_VAR(int) 50 ESCDELAY = 1000; /* max interval betw. chars in funkeys, in millisecs */ 51 52 static inline int 53 fifo_peek(void) 54 { 55 int ch = SP->_fifo[peek]; 56 TR(TRACE_IEVENT, ("peeking at %d", peek)); 57 58 p_inc(); 59 return ch; 60 } 61 62 static inline int 63 fifo_pull(void) 64 { 65 int ch; 66 ch = SP->_fifo[head]; 67 TR(TRACE_IEVENT, ("pulling %d from %d", ch, head)); 68 69 if (peek == head) { 70 h_inc(); 71 peek = head; 72 } else 73 h_inc(); 74 75 #ifdef TRACE 76 if (_nc_tracing & TRACE_IEVENT) 77 _nc_fifo_dump(); 78 #endif 79 return ch; 80 } 81 82 static inline int 83 fifo_push(void) 84 { 85 int n; 86 int ch; 87 88 if (tail == -1) 89 return ERR; 90 91 #ifdef HIDE_EINTR 92 again: 93 errno = 0; 94 #endif 95 96 #if USE_GPM_SUPPORT || defined(USE_EMX_MOUSE) 97 if ((SP->_mouse_fd >= 0) 98 && (_nc_timed_wait(3, -1, (int *) 0) & 2)) { 99 SP->_mouse_event(SP); 100 ch = KEY_MOUSE; 101 n = 1; 102 } else 103 #endif 104 { 105 unsigned char c2 = 0; 106 n = read(SP->_ifd, &c2, 1); 107 ch = CharOf(c2); 108 } 109 110 #ifdef HIDE_EINTR 111 /* 112 * Under System V curses with non-restarting signals, getch() returns 113 * with value ERR when a handled signal keeps it from completing. 114 * If signals restart system calls, OTOH, the signal is invisible 115 * except to its handler. 116 * 117 * We don't want this difference to show. This piece of code 118 * tries to make it look like we always have restarting signals. 119 */ 120 if (n <= 0 && errno == EINTR) 121 goto again; 122 #endif 123 124 if ((n == -1) || (n == 0)) { 125 TR(TRACE_IEVENT, ("read(%d,&ch,1)=%d, errno=%d", SP->_ifd, n, errno)); 126 ch = ERR; 127 } 128 TR(TRACE_IEVENT, ("read %d characters", n)); 129 130 SP->_fifo[tail] = ch; 131 SP->_fifohold = 0; 132 if (head == -1) 133 head = peek = tail; 134 t_inc(); 135 TR(TRACE_IEVENT, ("pushed %#x at %d", ch, tail)); 136 #ifdef TRACE 137 if (_nc_tracing & TRACE_IEVENT) 138 _nc_fifo_dump(); 139 #endif 140 return ch; 141 } 142 143 static inline void 144 fifo_clear(void) 145 { 146 int i; 147 for (i = 0; i < FIFO_SIZE; i++) 148 SP->_fifo[i] = 0; 149 head = -1; 150 tail = peek = 0; 151 } 152 153 static int kgetch(WINDOW *); 154 155 #define wgetch_should_refresh(win) (\ 156 (is_wintouched(win) || (win->_flags & _HASMOVED)) \ 157 && !(win->_flags & _ISPAD)) 158 159 NCURSES_EXPORT(int) 160 wgetch(WINDOW *win) 161 { 162 int ch; 163 164 T((T_CALLED("wgetch(%p)"), win)); 165 166 if (!win) 167 returnCode(ERR); 168 169 if (cooked_key_in_fifo()) { 170 if (wgetch_should_refresh(win)) 171 wrefresh(win); 172 173 ch = fifo_pull(); 174 T(("wgetch returning (pre-cooked): %#x = %s", ch, _trace_key(ch))); 175 returnCode(ch); 176 } 177 178 /* 179 * Handle cooked mode. Grab a string from the screen, 180 * stuff its contents in the FIFO queue, and pop off 181 * the first character to return it. 182 */ 183 if (head == -1 && !SP->_raw && !SP->_cbreak) { 184 char buf[MAXCOLUMNS], *sp; 185 186 TR(TRACE_IEVENT, ("filling queue in cooked mode")); 187 188 wgetnstr(win, buf, MAXCOLUMNS); 189 190 /* ungetch in reverse order */ 191 ungetch('\n'); 192 for (sp = buf + strlen(buf); sp > buf; sp--) 193 ungetch(sp[-1]); 194 195 returnCode(fifo_pull()); 196 } 197 198 if (wgetch_should_refresh(win)) 199 wrefresh(win); 200 201 if (!win->_notimeout && (win->_delay >= 0 || SP->_cbreak > 1)) { 202 int delay; 203 204 TR(TRACE_IEVENT, ("timed delay in wgetch()")); 205 if (SP->_cbreak > 1) 206 delay = (SP->_cbreak - 1) * 100; 207 else 208 delay = win->_delay; 209 210 TR(TRACE_IEVENT, ("delay is %d milliseconds", delay)); 211 212 if (head == -1) /* fifo is empty */ 213 if (!_nc_timed_wait(3, delay, (int *) 0)) 214 returnCode(ERR); 215 /* else go on to read data available */ 216 } 217 218 if (win->_use_keypad) { 219 /* 220 * This is tricky. We only want to get special-key 221 * events one at a time. But we want to accumulate 222 * mouse events until either (a) the mouse logic tells 223 * us it's picked up a complete gesture, or (b) 224 * there's a detectable time lapse after one. 225 * 226 * Note: if the mouse code starts failing to compose 227 * press/release events into clicks, you should probably 228 * increase the wait with mouseinterval(). 229 */ 230 int runcount = 0; 231 232 do { 233 ch = kgetch(win); 234 if (ch == KEY_MOUSE) { 235 ++runcount; 236 if (SP->_mouse_inline(SP)) 237 break; 238 } 239 } while 240 (ch == KEY_MOUSE 241 && (_nc_timed_wait(3, SP->_maxclick, (int *) 0) 242 || !SP->_mouse_parse(runcount))); 243 if (runcount > 0 && ch != KEY_MOUSE) { 244 /* mouse event sequence ended by keystroke, push it */ 245 ungetch(ch); 246 ch = KEY_MOUSE; 247 } 248 } else { 249 if (head == -1) 250 fifo_push(); 251 ch = fifo_pull(); 252 } 253 254 if (ch == ERR) { 255 #if USE_SIZECHANGE 256 if (SP->_sig_winch) { 257 _nc_update_screensize(); 258 /* resizeterm can push KEY_RESIZE */ 259 if (cooked_key_in_fifo()) { 260 ch = fifo_pull(); 261 T(("wgetch returning (pre-cooked): %#x = %s", ch, _trace_key(ch))); 262 returnCode(ch); 263 } 264 } 265 #endif 266 T(("wgetch returning ERR")); 267 returnCode(ERR); 268 } 269 270 /* 271 * If echo() is in effect, display the printable version of the 272 * key on the screen. Carriage return and backspace are treated 273 * specially by Solaris curses: 274 * 275 * If carriage return is defined as a function key in the 276 * terminfo, e.g., kent, then Solaris may return either ^J (or ^M 277 * if nonl() is set) or KEY_ENTER depending on the echo() mode. 278 * We echo before translating carriage return based on nonl(), 279 * since the visual result simply moves the cursor to column 0. 280 * 281 * Backspace is a different matter. Solaris curses does not 282 * translate it to KEY_BACKSPACE if kbs=^H. This does not depend 283 * on the stty modes, but appears to be a hardcoded special case. 284 * This is a difference from ncurses, which uses the terminfo entry. 285 * However, we provide the same visual result as Solaris, moving the 286 * cursor to the left. 287 */ 288 if (SP->_echo && !(win->_flags & _ISPAD)) { 289 chtype backup = (ch == KEY_BACKSPACE) ? '\b' : ch; 290 if (backup < KEY_MIN) 291 wechochar(win, backup); 292 } 293 294 /* 295 * Simulate ICRNL mode 296 */ 297 if ((ch == '\r') && SP->_nl) 298 ch = '\n'; 299 300 /* Strip 8th-bit if so desired. We do this only for characters that 301 * are in the range 128-255, to provide compatibility with terminals 302 * that display only 7-bit characters. Note that 'ch' may be a 303 * function key at this point, so we mustn't strip _those_. 304 */ 305 if ((ch < KEY_MIN) && (ch & 0x80)) 306 if (!SP->_use_meta) 307 ch &= 0x7f; 308 309 T(("wgetch returning : %#x = %s", ch, _trace_key(ch))); 310 311 returnCode(ch); 312 } 313 314 /* 315 ** int 316 ** kgetch() 317 ** 318 ** Get an input character, but take care of keypad sequences, returning 319 ** an appropriate code when one matches the input. After each character 320 ** is received, set an alarm call based on ESCDELAY. If no more of the 321 ** sequence is received by the time the alarm goes off, pass through 322 ** the sequence gotten so far. 323 ** 324 ** This function must be called when there is no cooked keys in queue. 325 ** (that is head==-1 || peek==head) 326 ** 327 */ 328 329 static int 330 kgetch(WINDOW *win GCC_UNUSED) 331 { 332 struct tries *ptr; 333 int ch = 0; 334 int timeleft = ESCDELAY; 335 336 TR(TRACE_IEVENT, ("kgetch(%p) called", win)); 337 338 ptr = SP->_keytry; 339 340 for (;;) { 341 if (!raw_key_in_fifo()) { 342 if (fifo_push() == ERR) { 343 peek = head; /* the keys stay uninterpreted */ 344 return ERR; 345 } 346 } 347 ch = fifo_peek(); 348 if (ch >= KEY_MIN) { 349 peek = head; 350 /* assume the key is the last in fifo */ 351 t_dec(); /* remove the key */ 352 return ch; 353 } 354 355 TR(TRACE_IEVENT, ("ch: %s", _trace_key((unsigned char) ch))); 356 while ((ptr != NULL) && (ptr->ch != (unsigned char) ch)) 357 ptr = ptr->sibling; 358 #ifdef TRACE 359 if (ptr == NULL) { 360 TR(TRACE_IEVENT, ("ptr is null")); 361 } else 362 TR(TRACE_IEVENT, ("ptr=%p, ch=%d, value=%d", 363 ptr, ptr->ch, ptr->value)); 364 #endif /* TRACE */ 365 366 if (ptr == NULL) 367 break; 368 369 if (ptr->value != 0) { /* sequence terminated */ 370 TR(TRACE_IEVENT, ("end of sequence")); 371 if (peek == tail) 372 fifo_clear(); 373 else 374 head = peek; 375 return (ptr->value); 376 } 377 378 ptr = ptr->child; 379 380 if (!raw_key_in_fifo()) { 381 TR(TRACE_IEVENT, ("waiting for rest of sequence")); 382 if (!_nc_timed_wait(3, timeleft, &timeleft)) { 383 TR(TRACE_IEVENT, ("ran out of time")); 384 break; 385 } 386 } 387 } 388 ch = fifo_pull(); 389 peek = head; 390 return ch; 391 } 392