1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1988 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 /*LINTLIBRARY*/ 43 44 #include "curses_inc.h" 45 #include <signal.h> 46 #include <unistd.h> 47 #ifdef DEBUG 48 #include <ctype.h> 49 #endif /* DEBUG */ 50 51 /* 52 * Read a key typed from the terminal 53 * 54 * interpret: = 0 for single-char key only 55 * = 1 for matching function key and macro patterns. 56 * = 2 same as 1 but no time-out for funckey matching. 57 */ 58 59 static int _getkey(int, chtype *); 60 static int _fpk(void); 61 static int _pk(void); 62 63 chtype 64 tgetch(int interpret) 65 { 66 int i = 0, j, collapse = 1; 67 #define WAIT3 333 68 chtype inp; 69 chtype *inputQ = cur_term->_input_queue; 70 char *chars_onQ = &(cur_term->_chars_on_queue); 71 72 #ifdef SYSV 73 /* 74 * Register the fact that getch is being used so 75 * that typeahead checking can be done. 76 * This code should GO AWAY when a poll() or FIONREAD can 77 * be done on the file descriptor as then the check 78 * will be non-destructive. 79 */ 80 cur_term->fl_typeahdok = TRUE; 81 #endif /* SYSV */ 82 83 /* ask for input */ 84 if (cur_term->_ungotten > 0) { 85 cur_term->_ungotten--; 86 /* decode an ungetch()'d character */ 87 inp = -inputQ[0]; 88 } else { 89 /* Only read a character if there is no typeahead/peekahead. */ 90 if (*chars_onQ == 0) { 91 /* (*chars_onQ)++; MR */ 92 #ifdef FIONREAD 93 inp = _readchar(); 94 #else /* FIONREAD */ 95 inp = (chtype) _pk(); 96 if ((int)inp == ERR) { 97 /* 98 * interpret is set to 0 so that down below we don't 99 * drop into getkey since we already know there can't be 100 * a key that starts with -1. Also, we don't want to 101 * access funckeystarter[-1]. 102 */ 103 interpret = FALSE; 104 } 105 #endif /* FIONREAD */ 106 (*chars_onQ)++; 107 } else 108 inp = inputQ[0]; 109 110 #ifdef DEBUG 111 if (outf) 112 fprintf(outf, "TGETCH read '%s'\n", unctrl(inp)); 113 #endif /* DEBUG */ 114 115 /* Check for arrow and function keys */ 116 if (interpret && cur_term->funckeystarter[inp]) 117 collapse = _getkey(interpret - 1, &inp); 118 } 119 120 /* Collapse the input queue to remove the escape */ 121 /* sequence from the stack. */ 122 123 j = *chars_onQ; 124 (*chars_onQ) -= collapse; 125 while (collapse < j) 126 inputQ[i++] = inputQ[collapse++]; 127 return (inp); 128 } 129 130 #ifdef FIONREAD 131 static int 132 _readchar() 133 { 134 int i; 135 unsigned char c; 136 137 if (cur_term->_delay == 0) { 138 int arg; 139 140 (void) ioctl(cur_term->_inputfd, FIONREAD, &arg); 141 #ifdef DEBUG 142 if (outf) 143 fprintf(outf, "FIONREAD returns %d\n", arg); 144 #endif /* DEBUG */ 145 if (arg < 1) 146 return (-1); 147 } else 148 if (cur_term->_delay > 0) { 149 char c; 150 int infd; 151 152 infd = 1 << cur_term->_inputfd; 153 t.tv_sec = cur_term->_delay / 1000; 154 t.tv_usec = (cur_term->_delay % 1000) * 1000; 155 i = select(20, &infd, (int *)NULL, (int *)NULL, &t); 156 if (i < 0) 157 return (ERR); 158 i = read(cur_term->_inputfd, &c, 1); 159 } else 160 i = read(cur_term->_inputfd, &c, 1); 161 162 #ifdef DEBUG 163 if (outf) 164 fprintf(outf, "read from %d returns %d chars, first %o\n", 165 cur_term->_inputfd, i, c); 166 #endif /* DEBUG */ 167 168 if (i > 0) 169 return (c); 170 else 171 return (ERR); 172 } 173 #endif /* !FIONREAD */ 174 175 #ifdef DEBUG 176 extern char *_asciify(); 177 #endif /* DEBUG */ 178 179 /* 180 * This algorithm is a "learning" algorithm. The premise is 181 * that keys used once are like to be used again and again. 182 * Since the time for a linear search of the table is so 183 * expensive, we move keys that are found up to the top of 184 * the list, making the access to a repeated key very fast and 185 * keys that have been used before close to the top. 186 */ 187 188 static int 189 _getkey(int blockpeek, chtype *inp) 190 { 191 _KEY_MAP **kp = cur_term->_keys; 192 int key, num_keys = cur_term->_ksz; 193 int i; 194 chtype *inputQ = cur_term->_input_queue; 195 char *chars_onQ = &(cur_term->_chars_on_queue), 196 flag = cur_term->funckeystarter[*inp]; 197 int first, collapse = 1; 198 199 200 #ifdef DEBUG 201 if (outf) 202 fprintf(outf, "getkey(): looking in linear table, " 203 "inp=%d\n", *inp); 204 #endif /* DEBUG */ 205 206 if (flag & _KEY) 207 key = 0; 208 else { 209 key = cur_term->_first_macro; 210 blockpeek = TRUE; 211 } 212 first = key; 213 214 for (; key < num_keys; key++) { 215 if (kp[key]->_sends[0] == *inp) { 216 for (i = 1; i < INP_QSIZE; i++) { 217 /* found it? */ 218 if (kp[key]->_sends[i] == '\0') 219 break; 220 /* partial match? peek ahead. */ 221 if (*chars_onQ == i) { 222 (*chars_onQ)++; 223 inputQ[i] = (blockpeek) ? 224 _pk() : _fpk(); 225 switch ((int)inputQ[i]) { 226 case -2: 227 /* 228 * Since -2 signifies a timeout we don't really 229 * want to put it on the queue so we decrement 230 * our counter. 231 */ 232 (*chars_onQ)--; 233 #ifdef DEBUG 234 if (outf) 235 fprintf(outf, "Timed out\n"); 236 #endif /* DEBUG */ 237 if (flag & _MACRO) { 238 #ifdef DEBUG 239 if (outf) 240 fprintf(outf, 241 "Found macro\n"); 242 #endif /* DEBUG */ 243 /* 244 * We have to decrement one because key will be 245 * incremented at the bottom of the out loop. 246 */ 247 key = (first = blockpeek = 248 cur_term->_first_macro) - 249 1; 250 goto outerloop; 251 } 252 253 /*FALLTHROUGH*/ 254 255 case -1: 256 goto ret; 257 } 258 } 259 260 /* not this one? */ 261 if (kp[key]->_sends[i] != inputQ[i]) 262 goto outerloop; 263 } 264 265 /* SS-mouse */ 266 if (kp[key]->_keyval == KEY_MOUSE) { 267 MOUSE_STATUS old_mouse; 268 int rc; 269 static int get_xterm_mouse(int, int *); 270 271 old_mouse = Mouse_status; 272 273 /* read the mouse status information */ 274 275 if (mouse_info) 276 rc = -3; /* NOT IMPLEMENTED */ 277 else 278 rc = get_xterm_mouse(blockpeek, &i); 279 280 if (rc == -1) /* read error */ 281 goto ret; 282 else if (rc == -2 || rc == -3) /* timeout */ 283 /* or not mouse */ 284 goto outerloop; 285 else if (rc == 0) /* report mouse pos */ 286 Mouse_status.changes |= 020; 287 else if (rc >= 1 && rc <= 3) 288 /* mouse button event */ 289 Mouse_status.changes = 290 (((MOUSE_X_POS != old_mouse.x || 291 MOUSE_Y_POS != old_mouse.y) << 3) | 292 ((Mouse_status.button[2] != 293 old_mouse.button[2]) << 2) | 294 ((Mouse_status.button[1] != 295 old_mouse.button[1]) << 1) | 296 (Mouse_status.button[0] != 297 old_mouse.button[0])); 298 } 299 300 /* We found it! Read in any chars left in _sends */ 301 302 if ((collapse = i) == INP_QSIZE) 303 for (; kp[key]->_sends[i]; i++) 304 (void) _fpk(); 305 306 /* move key to top of ordered list */ 307 if (key != first) { 308 _KEY_MAP *savekey = kp[key]; 309 short *lorder; 310 int j; 311 312 if (key > cur_term->_first_macro) 313 lorder = &(cur_term->_lastmacro_ordered); 314 else 315 lorder = &(cur_term->_lastkey_ordered); 316 /* 317 * If we're below the last ordered key, swap next unordered 318 * key with this one and ripple from there. 319 */ 320 if (key > *lorder) 321 kp[key] = kp[(i = ++(*lorder))]; 322 else 323 i = key; 324 /* ripple the ordered keys down */ 325 for (j = i--; j > first; ) 326 kp[j--] = kp[i--]; 327 kp[first] = savekey; 328 } 329 *inp = kp[first]->_keyval; 330 331 /* 332 * SS-mouse support: if mouse button event 333 * occured on top of the soft label, we may 334 * have to return the function key corresponding 335 * to that soft label 336 */ 337 338 if (*inp == KEY_MOUSE && A_BUTTON_CHANGED && 339 (MOUSE_Y_POS == LINES) && 340 (SP->slk != (SLK_MAP *) NULL) && 341 (SP->_map_mbe_to_key != 0)) { 342 static void _map_button(chtype *); 343 _map_button(inp); 344 } 345 346 goto ret; 347 } 348 outerloop: 349 ; 350 } 351 352 ret: 353 /* key not found */ 354 #ifdef DEBUG 355 if (outf) 356 if (key == num_keys) 357 fprintf(outf, "Did not match anything.\n"); 358 #endif /* DEBUG */ 359 return (collapse); 360 } 361 362 363 /* SS-mouse */ 364 /* this function tries to read in information that follows KEY_MOUSE: */ 365 /* the first character identifies what button is involved (1,2,or 3) */ 366 /* if the first character is 0, we are dealing with report_mouse_pos */ 367 /* 368 * The routine returns the following: 369 * -3: not a mouse button event 370 * -2: read timed out 371 * -1: the read failed 372 * [0, 1, 2, 3] - the first character in the mouse event 373 */ 374 static int 375 get_xterm_mouse(int blockpeek, int *i) 376 { 377 chtype *inputQ = cur_term->_input_queue; /* ??? */ 378 /* LINTED */ 379 chtype *chars_onQ = (chtype *) &(cur_term->_chars_on_queue); 380 int j, mx, my; 381 int char1, char2, c1, c2; 382 383 /* the first character should be 0, 1, 2, or 4 */ 384 385 char1 = (inputQ[(*i)++] = (blockpeek) ? _pk() : _fpk()); 386 387 /* read error or timeout */ 388 389 if (char1 < 0) 390 return (char1); 391 (*chars_onQ)++; 392 393 if (char1 < '0' || char1 > '3') 394 return (-3); 395 396 /* if the character is 1, 2, or 3 it must be followed by */ 397 /* P, R, C, D, or T */ 398 399 if (char1 != '0') { 400 char2 = (inputQ[(*i)++] = (blockpeek) ? _pk() : _fpk()); 401 402 if (char2 < 0) 403 return (char2); 404 405 (*chars_onQ)++; 406 if (char2 != 'P' && char2 != 'R' && char2 != 'C' && 407 char2 != 'D' && char2 != 'T') 408 return (-3); 409 } 410 411 /* read X and Y coordinates of the mouse */ 412 413 for (j = 0; j < 2; j++) { 414 c1 = (inputQ[(*i)++] = (blockpeek) ? _pk() : _fpk()); 415 if (c1 < 0) 416 return (c1); 417 (*chars_onQ)++; 418 if (c1 >= ' ' && c1 <= '~') { /* ascii char */ 419 if (j == 0) 420 mx = c1 - ' '; 421 else 422 my = c1 - ' '; 423 } else if (char1 == 01 || char1 == 02) { /* ^A || ^B */ 424 c2 = (inputQ[(*i)++] = (blockpeek) ? _pk() : _fpk()); 425 if (c2 < 0) 426 return (c2); 427 (*chars_onQ)++; 428 if (c2 >= ' ' && c2 <= '~') { 429 if (j == 0) 430 mx = c1 * (c2 - ' '); 431 else 432 my = c1 * (c2 - ' '); 433 } else 434 return (-3); 435 } else 436 return (-3); 437 } 438 439 /* read complete mouse event: update the Mouse_status structure */ 440 441 MOUSE_X_POS = mx; 442 MOUSE_Y_POS = my; 443 j = char1 - '0'; 444 if (j != 0) { 445 switch (char2) { 446 case 'P': 447 BUTTON_STATUS(j) = BUTTON_PRESSED; 448 break; 449 case 'R': 450 BUTTON_STATUS(j) = BUTTON_RELEASED; 451 break; 452 case 'C': 453 BUTTON_STATUS(j) = BUTTON_CLICKED; 454 break; 455 case 'D': 456 BUTTON_STATUS(j) = BUTTON_DOUBLE_CLICKED; 457 break; 458 case 'T': 459 BUTTON_STATUS(j) = BUTTON_TRIPLE_CLICKED; 460 break; 461 } 462 } 463 return (j); 464 } 465 /* SS-mouse-end */ 466 467 468 /* 469 * Fast peek key. Like getchar but if the right flags are set, times out 470 * quickly if there is nothing waiting, returning -1. 471 * f is an output stdio descriptor, we read from the fileno. 472 * We wait for long enough for a terminal to send another character 473 * (at 15cps repeat rate, this is 67 ms, I'm using 100ms to allow 474 * a bit of a fudge factor) and time out more quickly. 475 * -2 is returned if we time out, -1 is returned if interrupted, and the 476 * character is returned otherwise. 477 */ 478 479 #ifndef FIONREAD 480 481 /* 482 * Traditional implementation. The best resolution we have is 1 second, 483 * so we set a 1 second alarm and try to read. If we fail for 1 second, 484 * we assume there is no key waiting. Problem here is that 1 second is 485 * too long; people can type faster than this. 486 * 487 * Another possible implementation of changing VMIN/VTIME before and 488 * after each read does not work because the tty driver's timeout 489 * mechanism is too unreliable when the timeouts are changed too quickly. 490 */ 491 492 static char sig_caught; 493 494 static 495 #ifdef SIGPOLL /* Vr3 and beyond */ 496 void 497 #endif /* SIGPOLL */ 498 /* The following line causes a lint warning for "dummy" which is not used. */ 499 _catch_alarm(int dummy) 500 { 501 sig_caught = 1; 502 } 503 504 static int 505 _fpk(void) 506 { 507 unsigned char c; 508 int infd = cur_term->_inputfd; 509 ssize_t rc; 510 #ifdef SIGPOLL /* Vr3 and beyond */ 511 void (*oldsig)(int); 512 #else /* SIGPOLL */ 513 int (*oldsig)(int); 514 #endif /* SIGPOLL */ 515 unsigned int oldalarm, alarm(unsigned); 516 517 /* turn off any user alarms and set our own */ 518 oldalarm = alarm(0); 519 sig_caught = 0; 520 oldsig = signal(SIGALRM, _catch_alarm); 521 (void) alarm(1); 522 rc = read(cur_term->_inputfd, (char *)&c, 1); 523 (void) alarm(0); 524 525 /* 526 * This code is to take care of the possibility of 527 * the process getting swapped out in the middle of 528 * read() call above. The interrupt will cause the 529 * read() call to retur, even if a character is really 530 * on the clist. So we do a non-blocking read() to make 531 * sure that there really isn't a character there. 532 */ 533 534 if (sig_caught && rc != 1) 535 if (cur_term->_check_fd != -1) 536 rc = read(cur_term->_check_fd, (char *)&c, 1); 537 else { 538 #include <fcntl.h> 539 int fcflags = fcntl(infd, F_GETFL, 0); 540 541 (void) fcntl(infd, F_SETFL, fcflags | O_NDELAY); 542 rc = read(infd, (char *)&c, 1); 543 (void) fcntl(infd, F_SETFL, fcflags); 544 } 545 546 /* restore the user alarms */ 547 (void) signal(SIGALRM, oldsig); 548 if (sig_caught && oldalarm > 1) 549 oldalarm--; 550 (void) alarm(oldalarm); 551 if (rc == 1) /* got a character */ 552 return (c); 553 else 554 if (sig_caught) /* timed out */ 555 return (-2); 556 else /* EOF or got interrupted */ 557 return (-1); 558 } 559 #else /* FIONREAD */ 560 /* 561 * If we have the select system call, we can do much better than the 562 * traditional method. Even if we don't have the real 4.2BSD select, we 563 * can emulate it with napms and FIONREAD. napms might be done with only 564 * 1 second resolution, but this is no worse than what we have in the 565 * traditional implementation. 566 */ 567 static 568 _fpk() 569 { 570 int infd, rc; 571 int *outfd, *exfd; 572 unsigned char c; 573 struct timeval t; 574 575 infd = 1 << cur_term->_inputfd; 576 outfd = exfd = (int *)NULL; 577 t.tv_sec = 0; 578 t.tv_usec = 100000; /* 100 milliseconds */ 579 rc = select(20, &infd, outfd, exfd, &t); 580 if (rc < 0) 581 return (-2); 582 rc = read(fileno(f), &c, 1); 583 return (rc == 1 ? c : -1); 584 } 585 #endif /* FIONREAD */ 586 587 /* 588 * Plain peekchar function. Nothing fancy. This is just like _fpk 589 * but will wait forever rather than time out. 590 */ 591 592 static int 593 _pk(void) 594 { 595 unsigned char c; 596 597 return ((read(cur_term->_inputfd, (char *)&c, 1) == 1) ? c : ERR); 598 } 599 600 601 /* 602 * SS-mouse: check if this mouse button event should map into 603 * function key 604 */ 605 606 607 static void 608 _map_button(chtype *inp) 609 { 610 SLK_MAP *slk = SP->slk; 611 int num = slk->_num; 612 int len = slk->_len; 613 int i; 614 615 /* first determine if this mouse button event should be */ 616 /* mapped into function key */ 617 618 if (!(SP->_map_mbe_to_key & 619 ((BUTTON_CHANGED(3) << (10 + BUTTON_STATUS(3))) | 620 (BUTTON_CHANGED(2) << (5 + BUTTON_STATUS(2))) | 621 (BUTTON_CHANGED(1) << BUTTON_STATUS(1))))) 622 return; 623 624 for (i = 0; i < num; i++) { 625 if (MOUSE_X_POS < slk->_labx[i]) 626 break; 627 if (MOUSE_X_POS > slk->_labx[i] + len) 628 continue; 629 *inp = KEY_F(1) + i; 630 break; 631 } 632 } 633