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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /* Copyright (c) 1988 AT&T */
27 /* All Rights Reserved */
28
29 /*
30 * University Copyright- Copyright (c) 1982, 1986, 1988
31 * The Regents of the University of California
32 * All Rights Reserved
33 *
34 * University Acknowledgment- Portions of this document are derived from
35 * software developed by the University of California, Berkeley, and its
36 * contributors.
37 */
38
39 /*LINTLIBRARY*/
40
41 #include "curses_inc.h"
42 #include <signal.h>
43 #include <unistd.h>
44 #ifndef FIONREAD
45 #include <fcntl.h>
46 #endif /* FIONREAD */
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
tgetch(int interpret)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
_readchar()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 static int get_xterm_mouse(int, int *);
180 static void _map_button(chtype *);
181
182 /*
183 * This algorithm is a "learning" algorithm. The premise is
184 * that keys used once are like to be used again and again.
185 * Since the time for a linear search of the table is so
186 * expensive, we move keys that are found up to the top of
187 * the list, making the access to a repeated key very fast and
188 * keys that have been used before close to the top.
189 */
190
191 static int
_getkey(int blockpeek,chtype * inp)192 _getkey(int blockpeek, chtype *inp)
193 {
194 _KEY_MAP **kp = cur_term->_keys;
195 int key, num_keys = cur_term->_ksz;
196 int i;
197 chtype *inputQ = cur_term->_input_queue;
198 char *chars_onQ = &(cur_term->_chars_on_queue);
199 char flag = cur_term->funckeystarter[*inp];
200 int first, collapse = 1;
201
202
203 #ifdef DEBUG
204 if (outf)
205 fprintf(outf, "getkey(): looking in linear table, "
206 "inp=%d\n", *inp);
207 #endif /* DEBUG */
208
209 if (flag & _KEY)
210 key = 0;
211 else {
212 key = cur_term->_first_macro;
213 blockpeek = TRUE;
214 }
215 first = key;
216
217 for (; key < num_keys; key++) {
218 if (kp[key]->_sends[0] == *inp) {
219 for (i = 1; i < INP_QSIZE; i++) {
220 /* found it? */
221 if (kp[key]->_sends[i] == '\0')
222 break;
223 /* partial match? peek ahead. */
224 if (*chars_onQ == i) {
225 (*chars_onQ)++;
226 inputQ[i] = (blockpeek) ?
227 _pk() : _fpk();
228 switch ((int)inputQ[i]) {
229 case -2:
230 /*
231 * Since -2 signifies a timeout we don't really
232 * want to put it on the queue so we decrement
233 * our counter.
234 */
235 (*chars_onQ)--;
236 #ifdef DEBUG
237 if (outf)
238 fprintf(outf, "Timed out\n");
239 #endif /* DEBUG */
240 if (flag & _MACRO) {
241 #ifdef DEBUG
242 if (outf)
243 fprintf(outf,
244 "Found macro\n");
245 #endif /* DEBUG */
246 /*
247 * We have to decrement one because key will be
248 * incremented at the bottom of the out loop.
249 */
250 key = (first = blockpeek =
251 cur_term->_first_macro) -
252 1;
253 goto outerloop;
254 }
255
256 /*FALLTHROUGH*/
257
258 case -1:
259 goto ret;
260 }
261 }
262
263 /* not this one? */
264 if (kp[key]->_sends[i] != inputQ[i])
265 goto outerloop;
266 }
267
268 /* SS-mouse */
269 if (kp[key]->_keyval == KEY_MOUSE) {
270 MOUSE_STATUS old_mouse;
271 int rc;
272
273 old_mouse = Mouse_status;
274
275 /* read the mouse status information */
276
277 if (mouse_info)
278 rc = -3; /* NOT IMPLEMENTED */
279 else
280 rc = get_xterm_mouse(blockpeek, &i);
281
282 if (rc == -1) /* read error */
283 goto ret;
284 else if (rc == -2 || rc == -3) /* timeout */
285 /* or not mouse */
286 goto outerloop;
287 else if (rc == 0) /* report mouse pos */
288 Mouse_status.changes |= 020;
289 else if (rc >= 1 && rc <= 3)
290 /* mouse button event */
291 Mouse_status.changes =
292 (((MOUSE_X_POS != old_mouse.x ||
293 MOUSE_Y_POS != old_mouse.y) << 3) |
294 ((Mouse_status.button[2] !=
295 old_mouse.button[2]) << 2) |
296 ((Mouse_status.button[1] !=
297 old_mouse.button[1]) << 1) |
298 (Mouse_status.button[0] !=
299 old_mouse.button[0]));
300 }
301
302 /* We found it! Read in any chars left in _sends */
303
304 if ((collapse = i) == INP_QSIZE)
305 for (; kp[key]->_sends[i]; i++)
306 (void) _fpk();
307
308 /* move key to top of ordered list */
309 if (key != first) {
310 _KEY_MAP *savekey = kp[key];
311 short *lorder;
312 int j;
313
314 if (key > cur_term->_first_macro)
315 lorder = &(cur_term->_lastmacro_ordered);
316 else
317 lorder = &(cur_term->_lastkey_ordered);
318 /*
319 * If we're below the last ordered key, swap next unordered
320 * key with this one and ripple from there.
321 */
322 if (key > *lorder)
323 kp[key] = kp[(i = ++(*lorder))];
324 else
325 i = key;
326 /* ripple the ordered keys down */
327 for (j = i--; j > first; )
328 kp[j--] = kp[i--];
329 kp[first] = savekey;
330 }
331 *inp = kp[first]->_keyval;
332
333 /*
334 * SS-mouse support: if mouse button event
335 * occured on top of the soft label, we may
336 * have to return the function key corresponding
337 * to that soft label
338 */
339
340 if (*inp == KEY_MOUSE && A_BUTTON_CHANGED &&
341 (MOUSE_Y_POS == LINES) &&
342 (SP->slk != (SLK_MAP *) NULL) &&
343 (SP->_map_mbe_to_key != 0)) {
344 _map_button(inp);
345 }
346
347 goto ret;
348 }
349 outerloop:
350 ;
351 }
352
353 ret:
354 /* key not found */
355 #ifdef DEBUG
356 if (outf)
357 if (key == num_keys)
358 fprintf(outf, "Did not match anything.\n");
359 #endif /* DEBUG */
360 return (collapse);
361 }
362
363
364 /* SS-mouse */
365 /* this function tries to read in information that follows KEY_MOUSE: */
366 /* the first character identifies what button is involved (1,2,or 3) */
367 /* if the first character is 0, we are dealing with report_mouse_pos */
368 /*
369 * The routine returns the following:
370 * -3: not a mouse button event
371 * -2: read timed out
372 * -1: the read failed
373 * [0, 1, 2, 3] - the first character in the mouse event
374 */
375 static int
get_xterm_mouse(int blockpeek,int * i)376 get_xterm_mouse(int blockpeek, int *i)
377 {
378 chtype *inputQ = cur_term->_input_queue; /* ??? */
379 /* LINTED */
380 chtype *chars_onQ = (chtype *) &(cur_term->_chars_on_queue);
381 int j, mx, my;
382 int char1, char2, c1, c2;
383
384 /* the first character should be 0, 1, 2, or 4 */
385
386 char1 = (inputQ[(*i)++] = (blockpeek) ? _pk() : _fpk());
387
388 /* read error or timeout */
389
390 if (char1 < 0)
391 return (char1);
392 (*chars_onQ)++;
393
394 if (char1 < '0' || char1 > '3')
395 return (-3);
396
397 /* if the character is 1, 2, or 3 it must be followed by */
398 /* P, R, C, D, or T */
399
400 if (char1 != '0') {
401 char2 = (inputQ[(*i)++] = (blockpeek) ? _pk() : _fpk());
402
403 if (char2 < 0)
404 return (char2);
405
406 (*chars_onQ)++;
407 if (char2 != 'P' && char2 != 'R' && char2 != 'C' &&
408 char2 != 'D' && char2 != 'T')
409 return (-3);
410 }
411
412 /* read X and Y coordinates of the mouse */
413
414 for (j = 0; j < 2; j++) {
415 c1 = (inputQ[(*i)++] = (blockpeek) ? _pk() : _fpk());
416 if (c1 < 0)
417 return (c1);
418 (*chars_onQ)++;
419 if (c1 >= ' ' && c1 <= '~') { /* ascii char */
420 if (j == 0)
421 mx = c1 - ' ';
422 else
423 my = c1 - ' ';
424 } else if (char1 == 01 || char1 == 02) { /* ^A || ^B */
425 c2 = (inputQ[(*i)++] = (blockpeek) ? _pk() : _fpk());
426 if (c2 < 0)
427 return (c2);
428 (*chars_onQ)++;
429 if (c2 >= ' ' && c2 <= '~') {
430 if (j == 0)
431 mx = c1 * (c2 - ' ');
432 else
433 my = c1 * (c2 - ' ');
434 } else
435 return (-3);
436 } else
437 return (-3);
438 }
439
440 /* read complete mouse event: update the Mouse_status structure */
441
442 MOUSE_X_POS = mx;
443 MOUSE_Y_POS = my;
444 j = char1 - '0';
445 if (j != 0) {
446 switch (char2) {
447 case 'P':
448 BUTTON_STATUS(j) = BUTTON_PRESSED;
449 break;
450 case 'R':
451 BUTTON_STATUS(j) = BUTTON_RELEASED;
452 break;
453 case 'C':
454 BUTTON_STATUS(j) = BUTTON_CLICKED;
455 break;
456 case 'D':
457 BUTTON_STATUS(j) = BUTTON_DOUBLE_CLICKED;
458 break;
459 case 'T':
460 BUTTON_STATUS(j) = BUTTON_TRIPLE_CLICKED;
461 break;
462 }
463 }
464 return (j);
465 }
466 /* SS-mouse-end */
467
468
469 /*
470 * Fast peek key. Like getchar but if the right flags are set, times out
471 * quickly if there is nothing waiting, returning -1.
472 * f is an output stdio descriptor, we read from the fileno.
473 * We wait for long enough for a terminal to send another character
474 * (at 15cps repeat rate, this is 67 ms, I'm using 100ms to allow
475 * a bit of a fudge factor) and time out more quickly.
476 * -2 is returned if we time out, -1 is returned if interrupted, and the
477 * character is returned otherwise.
478 */
479
480 #ifndef FIONREAD
481
482 /*
483 * Traditional implementation. The best resolution we have is 1 second,
484 * so we set a 1 second alarm and try to read. If we fail for 1 second,
485 * we assume there is no key waiting. Problem here is that 1 second is
486 * too long; people can type faster than this.
487 *
488 * Another possible implementation of changing VMIN/VTIME before and
489 * after each read does not work because the tty driver's timeout
490 * mechanism is too unreliable when the timeouts are changed too quickly.
491 */
492
493 static char sig_caught;
494
495 static
496 #ifdef SIGPOLL /* Vr3 and beyond */
497 void
498 #endif /* SIGPOLL */
499 /* The following line causes a lint warning for "dummy" which is not used. */
_catch_alarm(int dummy)500 _catch_alarm(int dummy)
501 {
502 sig_caught = 1;
503 }
504
505 static int
_fpk(void)506 _fpk(void)
507 {
508 unsigned char c;
509 int infd = cur_term->_inputfd;
510 ssize_t rc;
511 #ifdef SIGPOLL /* Vr3 and beyond */
512 void (*oldsig)(int);
513 #else /* SIGPOLL */
514 int (*oldsig)(int);
515 #endif /* SIGPOLL */
516 unsigned int oldalarm, alarm(unsigned);
517
518 /* turn off any user alarms and set our own */
519 oldalarm = alarm(0);
520 sig_caught = 0;
521 oldsig = signal(SIGALRM, _catch_alarm);
522 (void) alarm(1);
523 rc = read(cur_term->_inputfd, (char *)&c, 1);
524 (void) alarm(0);
525
526 /*
527 * This code is to take care of the possibility of
528 * the process getting swapped out in the middle of
529 * read() call above. The interrupt will cause the
530 * read() call to retur, even if a character is really
531 * on the clist. So we do a non-blocking read() to make
532 * sure that there really isn't a character there.
533 */
534
535 if (sig_caught && rc != 1)
536 if (cur_term->_check_fd != -1)
537 rc = read(cur_term->_check_fd, (char *)&c, 1);
538 else {
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
_fpk()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
_pk(void)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
_map_button(chtype * inp)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