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