1 /* $OpenBSD: lib_getch.c,v 1.1 1999/01/18 19:09:46 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.41 1998/09/26 23:34:53 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 int delay; 236 237 T(("timed delay in wgetch()")); 238 if (SP->_cbreak > 1) 239 delay = (SP->_cbreak - 1) * 100; 240 else 241 delay = win->_delay; 242 243 T(("delay is %d milliseconds", delay)); 244 245 if (head == -1) /* fifo is empty */ 246 if (!_nc_timed_wait(3, delay, (int *)0)) 247 returnCode(ERR); 248 /* else go on to read data available */ 249 } 250 251 if (win->_use_keypad) { 252 /* 253 * This is tricky. We only want to get special-key 254 * events one at a time. But we want to accumulate 255 * mouse events until either (a) the mouse logic tells 256 * us it's picked up a complete gesture, or (b) 257 * there's a detectable time lapse after one. 258 * 259 * Note: if the mouse code starts failing to compose 260 * press/release events into clicks, you should probably 261 * increase the wait with mouseinterval(). 262 */ 263 int runcount = 0; 264 265 do { 266 ch = kgetch(win); 267 if (ch == KEY_MOUSE) 268 { 269 ++runcount; 270 if (SP->_mouse_inline(SP)) 271 break; 272 } 273 } while 274 (ch == KEY_MOUSE 275 && (_nc_timed_wait(3, SP->_maxclick, (int *)0) 276 || !SP->_mouse_parse(runcount))); 277 if (runcount > 0 && ch != KEY_MOUSE) 278 { 279 /* mouse event sequence ended by keystroke, push it */ 280 ungetch(ch); 281 ch = KEY_MOUSE; 282 } 283 } else { 284 if (head == -1) 285 fifo_push(); 286 ch = fifo_pull(); 287 } 288 289 if (ch == ERR) 290 { 291 #if USE_SIZECHANGE 292 if(SP->_sig_winch) 293 { 294 _nc_update_screensize(); 295 /* resizeterm can push KEY_RESIZE */ 296 if(cooked_key_in_fifo()) 297 { 298 ch = fifo_pull(); 299 T(("wgetch returning (pre-cooked): %#x = %s", ch, _trace_key(ch));) 300 returnCode(ch); 301 } 302 } 303 #endif 304 T(("wgetch returning ERR")); 305 returnCode(ERR); 306 } 307 308 /* 309 * Simulate ICRNL mode 310 */ 311 if ((ch == '\r') && SP->_nl) 312 ch = '\n'; 313 314 /* Strip 8th-bit if so desired. We do this only for characters that 315 * are in the range 128-255, to provide compatibility with terminals 316 * that display only 7-bit characters. Note that 'ch' may be a 317 * function key at this point, so we mustn't strip _those_. 318 */ 319 if ((ch < KEY_MIN) && (ch & 0x80)) 320 if (!SP->_use_meta) 321 ch &= 0x7f; 322 323 if (SP->_echo && ch < KEY_MIN && !(win->_flags & _ISPAD)) 324 wechochar(win, (chtype)ch); 325 326 T(("wgetch returning : %#x = %s", ch, _trace_key(ch))); 327 328 returnCode(ch); 329 } 330 331 332 /* 333 ** int 334 ** kgetch() 335 ** 336 ** Get an input character, but take care of keypad sequences, returning 337 ** an appropriate code when one matches the input. After each character 338 ** is received, set an alarm call based on ESCDELAY. If no more of the 339 ** sequence is received by the time the alarm goes off, pass through 340 ** the sequence gotten so far. 341 ** 342 ** This function must be called when there is no cooked keys in queue. 343 ** (that is head==-1 || peek==head) 344 ** 345 */ 346 347 static int 348 kgetch(WINDOW *win GCC_UNUSED) 349 { 350 struct tries *ptr; 351 int ch = 0; 352 int timeleft = ESCDELAY; 353 354 TR(TRACE_IEVENT, ("kgetch(%p) called", win)); 355 356 ptr = SP->_keytry; 357 358 for(;;) 359 { 360 if (!raw_key_in_fifo()) 361 { 362 if(fifo_push() == ERR) 363 { 364 peek = head; /* the keys stay uninterpreted */ 365 return ERR; 366 } 367 } 368 ch = fifo_peek(); 369 if (ch >= KEY_MIN) 370 { 371 peek = head; 372 /* assume the key is the last in fifo */ 373 t_dec(); /* remove the key */ 374 return ch; 375 } 376 377 TR(TRACE_IEVENT, ("ch: %s", _trace_key((unsigned char)ch))); 378 while ((ptr != NULL) && (ptr->ch != (unsigned char)ch)) 379 ptr = ptr->sibling; 380 #ifdef TRACE 381 if (ptr == NULL) 382 {TR(TRACE_IEVENT, ("ptr is null"));} 383 else 384 TR(TRACE_IEVENT, ("ptr=%p, ch=%d, value=%d", 385 ptr, ptr->ch, ptr->value)); 386 #endif /* TRACE */ 387 388 if (ptr == NULL) 389 break; 390 391 if (ptr->value != 0) { /* sequence terminated */ 392 TR(TRACE_IEVENT, ("end of sequence")); 393 if (peek == tail) 394 fifo_clear(); 395 else 396 head = peek; 397 return(ptr->value); 398 } 399 400 ptr = ptr->child; 401 402 if (!raw_key_in_fifo()) 403 { 404 TR(TRACE_IEVENT, ("waiting for rest of sequence")); 405 if (!_nc_timed_wait(3, timeleft, &timeleft)) { 406 TR(TRACE_IEVENT, ("ran out of time")); 407 break; 408 } 409 } 410 } 411 ch = fifo_pull(); 412 peek = head; 413 return ch; 414 } 415