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 #ifdef DEBUG
45 #include <ctype.h>
46 #endif /* DEBUG */
47
48 /*
49 * Read a key typed from the terminal
50 *
51 * interpret: = 0 for single-char key only
52 * = 1 for matching function key and macro patterns.
53 * = 2 same as 1 but no time-out for funckey matching.
54 */
55
56 static int _getkey(int, chtype *);
57 static int _fpk(void);
58 static int _pk(void);
59
60 chtype
tgetch(int interpret)61 tgetch(int interpret)
62 {
63 int i = 0, j, collapse = 1;
64 #define WAIT3 333
65 chtype inp;
66 chtype *inputQ = cur_term->_input_queue;
67 char *chars_onQ = &(cur_term->_chars_on_queue);
68
69 #ifdef SYSV
70 /*
71 * Register the fact that getch is being used so
72 * that typeahead checking can be done.
73 * This code should GO AWAY when a poll() or FIONREAD can
74 * be done on the file descriptor as then the check
75 * will be non-destructive.
76 */
77 cur_term->fl_typeahdok = TRUE;
78 #endif /* SYSV */
79
80 /* ask for input */
81 if (cur_term->_ungotten > 0) {
82 cur_term->_ungotten--;
83 /* decode an ungetch()'d character */
84 inp = -inputQ[0];
85 } else {
86 /* Only read a character if there is no typeahead/peekahead. */
87 if (*chars_onQ == 0) {
88 /* (*chars_onQ)++; MR */
89 #ifdef FIONREAD
90 inp = _readchar();
91 #else /* FIONREAD */
92 inp = (chtype) _pk();
93 if ((int)inp == ERR) {
94 /*
95 * interpret is set to 0 so that down below we don't
96 * drop into getkey since we already know there can't be
97 * a key that starts with -1. Also, we don't want to
98 * access funckeystarter[-1].
99 */
100 interpret = FALSE;
101 }
102 #endif /* FIONREAD */
103 (*chars_onQ)++;
104 } else
105 inp = inputQ[0];
106
107 #ifdef DEBUG
108 if (outf)
109 fprintf(outf, "TGETCH read '%s'\n", unctrl(inp));
110 #endif /* DEBUG */
111
112 /* Check for arrow and function keys */
113 if (interpret && cur_term->funckeystarter[inp])
114 collapse = _getkey(interpret - 1, &inp);
115 }
116
117 /* Collapse the input queue to remove the escape */
118 /* sequence from the stack. */
119
120 j = *chars_onQ;
121 (*chars_onQ) -= collapse;
122 while (collapse < j)
123 inputQ[i++] = inputQ[collapse++];
124 return (inp);
125 }
126
127 #ifdef FIONREAD
128 static int
_readchar()129 _readchar()
130 {
131 int i;
132 unsigned char c;
133
134 if (cur_term->_delay == 0) {
135 int arg;
136
137 (void) ioctl(cur_term->_inputfd, FIONREAD, &arg);
138 #ifdef DEBUG
139 if (outf)
140 fprintf(outf, "FIONREAD returns %d\n", arg);
141 #endif /* DEBUG */
142 if (arg < 1)
143 return (-1);
144 } else
145 if (cur_term->_delay > 0) {
146 char c;
147 int infd;
148
149 infd = 1 << cur_term->_inputfd;
150 t.tv_sec = cur_term->_delay / 1000;
151 t.tv_usec = (cur_term->_delay % 1000) * 1000;
152 i = select(20, &infd, (int *)NULL, (int *)NULL, &t);
153 if (i < 0)
154 return (ERR);
155 i = read(cur_term->_inputfd, &c, 1);
156 } else
157 i = read(cur_term->_inputfd, &c, 1);
158
159 #ifdef DEBUG
160 if (outf)
161 fprintf(outf, "read from %d returns %d chars, first %o\n",
162 cur_term->_inputfd, i, c);
163 #endif /* DEBUG */
164
165 if (i > 0)
166 return (c);
167 else
168 return (ERR);
169 }
170 #endif /* !FIONREAD */
171
172 #ifdef DEBUG
173 extern char *_asciify();
174 #endif /* DEBUG */
175
176 static int get_xterm_mouse(int, int *);
177 static void _map_button(chtype *);
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
_getkey(int blockpeek,chtype * inp)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 char 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
270 old_mouse = Mouse_status;
271
272 /* read the mouse status information */
273
274 if (mouse_info)
275 rc = -3; /* NOT IMPLEMENTED */
276 else
277 rc = get_xterm_mouse(blockpeek, &i);
278
279 if (rc == -1) /* read error */
280 goto ret;
281 else if (rc == -2 || rc == -3) /* timeout */
282 /* or not mouse */
283 goto outerloop;
284 else if (rc == 0) /* report mouse pos */
285 Mouse_status.changes |= 020;
286 else if (rc >= 1 && rc <= 3)
287 /* mouse button event */
288 Mouse_status.changes =
289 (((MOUSE_X_POS != old_mouse.x ||
290 MOUSE_Y_POS != old_mouse.y) << 3) |
291 ((Mouse_status.button[2] !=
292 old_mouse.button[2]) << 2) |
293 ((Mouse_status.button[1] !=
294 old_mouse.button[1]) << 1) |
295 (Mouse_status.button[0] !=
296 old_mouse.button[0]));
297 }
298
299 /* We found it! Read in any chars left in _sends */
300
301 if ((collapse = i) == INP_QSIZE)
302 for (; kp[key]->_sends[i]; i++)
303 (void) _fpk();
304
305 /* move key to top of ordered list */
306 if (key != first) {
307 _KEY_MAP *savekey = kp[key];
308 short *lorder;
309 int j;
310
311 if (key > cur_term->_first_macro)
312 lorder = &(cur_term->_lastmacro_ordered);
313 else
314 lorder = &(cur_term->_lastkey_ordered);
315 /*
316 * If we're below the last ordered key, swap next unordered
317 * key with this one and ripple from there.
318 */
319 if (key > *lorder)
320 kp[key] = kp[(i = ++(*lorder))];
321 else
322 i = key;
323 /* ripple the ordered keys down */
324 for (j = i--; j > first; )
325 kp[j--] = kp[i--];
326 kp[first] = savekey;
327 }
328 *inp = kp[first]->_keyval;
329
330 /*
331 * SS-mouse support: if mouse button event
332 * occured on top of the soft label, we may
333 * have to return the function key corresponding
334 * to that soft label
335 */
336
337 if (*inp == KEY_MOUSE && A_BUTTON_CHANGED &&
338 (MOUSE_Y_POS == LINES) &&
339 (SP->slk != (SLK_MAP *) NULL) &&
340 (SP->_map_mbe_to_key != 0)) {
341 _map_button(inp);
342 }
343
344 goto ret;
345 }
346 outerloop:
347 ;
348 }
349
350 ret:
351 /* key not found */
352 #ifdef DEBUG
353 if (outf)
354 if (key == num_keys)
355 fprintf(outf, "Did not match anything.\n");
356 #endif /* DEBUG */
357 return (collapse);
358 }
359
360
361 /* SS-mouse */
362 /* this function tries to read in information that follows KEY_MOUSE: */
363 /* the first character identifies what button is involved (1,2,or 3) */
364 /* if the first character is 0, we are dealing with report_mouse_pos */
365 /*
366 * The routine returns the following:
367 * -3: not a mouse button event
368 * -2: read timed out
369 * -1: the read failed
370 * [0, 1, 2, 3] - the first character in the mouse event
371 */
372 static int
get_xterm_mouse(int blockpeek,int * i)373 get_xterm_mouse(int blockpeek, int *i)
374 {
375 chtype *inputQ = cur_term->_input_queue; /* ??? */
376 /* LINTED */
377 chtype *chars_onQ = (chtype *) &(cur_term->_chars_on_queue);
378 int j, mx, my;
379 int char1, char2, c1, c2;
380
381 /* the first character should be 0, 1, 2, or 4 */
382
383 char1 = (inputQ[(*i)++] = (blockpeek) ? _pk() : _fpk());
384
385 /* read error or timeout */
386
387 if (char1 < 0)
388 return (char1);
389 (*chars_onQ)++;
390
391 if (char1 < '0' || char1 > '3')
392 return (-3);
393
394 /* if the character is 1, 2, or 3 it must be followed by */
395 /* P, R, C, D, or T */
396
397 if (char1 != '0') {
398 char2 = (inputQ[(*i)++] = (blockpeek) ? _pk() : _fpk());
399
400 if (char2 < 0)
401 return (char2);
402
403 (*chars_onQ)++;
404 if (char2 != 'P' && char2 != 'R' && char2 != 'C' &&
405 char2 != 'D' && char2 != 'T')
406 return (-3);
407 }
408
409 /* read X and Y coordinates of the mouse */
410
411 for (j = 0; j < 2; j++) {
412 c1 = (inputQ[(*i)++] = (blockpeek) ? _pk() : _fpk());
413 if (c1 < 0)
414 return (c1);
415 (*chars_onQ)++;
416 if (c1 >= ' ' && c1 <= '~') { /* ascii char */
417 if (j == 0)
418 mx = c1 - ' ';
419 else
420 my = c1 - ' ';
421 } else if (char1 == 01 || char1 == 02) { /* ^A || ^B */
422 c2 = (inputQ[(*i)++] = (blockpeek) ? _pk() : _fpk());
423 if (c2 < 0)
424 return (c2);
425 (*chars_onQ)++;
426 if (c2 >= ' ' && c2 <= '~') {
427 if (j == 0)
428 mx = c1 * (c2 - ' ');
429 else
430 my = c1 * (c2 - ' ');
431 } else
432 return (-3);
433 } else
434 return (-3);
435 }
436
437 /* read complete mouse event: update the Mouse_status structure */
438
439 MOUSE_X_POS = mx;
440 MOUSE_Y_POS = my;
441 j = char1 - '0';
442 if (j != 0) {
443 switch (char2) {
444 case 'P':
445 BUTTON_STATUS(j) = BUTTON_PRESSED;
446 break;
447 case 'R':
448 BUTTON_STATUS(j) = BUTTON_RELEASED;
449 break;
450 case 'C':
451 BUTTON_STATUS(j) = BUTTON_CLICKED;
452 break;
453 case 'D':
454 BUTTON_STATUS(j) = BUTTON_DOUBLE_CLICKED;
455 break;
456 case 'T':
457 BUTTON_STATUS(j) = BUTTON_TRIPLE_CLICKED;
458 break;
459 }
460 }
461 return (j);
462 }
463 /* SS-mouse-end */
464
465
466 /*
467 * Fast peek key. Like getchar but if the right flags are set, times out
468 * quickly if there is nothing waiting, returning -1.
469 * f is an output stdio descriptor, we read from the fileno.
470 * We wait for long enough for a terminal to send another character
471 * (at 15cps repeat rate, this is 67 ms, I'm using 100ms to allow
472 * a bit of a fudge factor) and time out more quickly.
473 * -2 is returned if we time out, -1 is returned if interrupted, and the
474 * character is returned otherwise.
475 */
476
477 #ifndef FIONREAD
478
479 /*
480 * Traditional implementation. The best resolution we have is 1 second,
481 * so we set a 1 second alarm and try to read. If we fail for 1 second,
482 * we assume there is no key waiting. Problem here is that 1 second is
483 * too long; people can type faster than this.
484 *
485 * Another possible implementation of changing VMIN/VTIME before and
486 * after each read does not work because the tty driver's timeout
487 * mechanism is too unreliable when the timeouts are changed too quickly.
488 */
489
490 static char sig_caught;
491
492 static
493 #ifdef SIGPOLL /* Vr3 and beyond */
494 void
495 #endif /* SIGPOLL */
496 /* The following line causes a lint warning for "dummy" which is not used. */
_catch_alarm(int dummy)497 _catch_alarm(int dummy)
498 {
499 sig_caught = 1;
500 }
501
502 static int
_fpk(void)503 _fpk(void)
504 {
505 unsigned char c;
506 int infd = cur_term->_inputfd;
507 ssize_t rc;
508 #ifdef SIGPOLL /* Vr3 and beyond */
509 void (*oldsig)(int);
510 #else /* SIGPOLL */
511 int (*oldsig)(int);
512 #endif /* SIGPOLL */
513 unsigned int oldalarm, alarm(unsigned);
514
515 /* turn off any user alarms and set our own */
516 oldalarm = alarm(0);
517 sig_caught = 0;
518 oldsig = signal(SIGALRM, _catch_alarm);
519 (void) alarm(1);
520 rc = read(cur_term->_inputfd, (char *)&c, 1);
521 (void) alarm(0);
522
523 /*
524 * This code is to take care of the possibility of
525 * the process getting swapped out in the middle of
526 * read() call above. The interrupt will cause the
527 * read() call to retur, even if a character is really
528 * on the clist. So we do a non-blocking read() to make
529 * sure that there really isn't a character there.
530 */
531
532 if (sig_caught && rc != 1)
533 if (cur_term->_check_fd != -1)
534 rc = read(cur_term->_check_fd, (char *)&c, 1);
535 else {
536 #include <fcntl.h>
537 int fcflags = fcntl(infd, F_GETFL, 0);
538
539 (void) fcntl(infd, F_SETFL, fcflags | O_NDELAY);
540 rc = read(infd, (char *)&c, 1);
541 (void) fcntl(infd, F_SETFL, fcflags);
542 }
543
544 /* restore the user alarms */
545 (void) signal(SIGALRM, oldsig);
546 if (sig_caught && oldalarm > 1)
547 oldalarm--;
548 (void) alarm(oldalarm);
549 if (rc == 1) /* got a character */
550 return (c);
551 else
552 if (sig_caught) /* timed out */
553 return (-2);
554 else /* EOF or got interrupted */
555 return (-1);
556 }
557 #else /* FIONREAD */
558 /*
559 * If we have the select system call, we can do much better than the
560 * traditional method. Even if we don't have the real 4.2BSD select, we
561 * can emulate it with napms and FIONREAD. napms might be done with only
562 * 1 second resolution, but this is no worse than what we have in the
563 * traditional implementation.
564 */
565 static
_fpk()566 _fpk()
567 {
568 int infd, rc;
569 int *outfd, *exfd;
570 unsigned char c;
571 struct timeval t;
572
573 infd = 1 << cur_term->_inputfd;
574 outfd = exfd = (int *)NULL;
575 t.tv_sec = 0;
576 t.tv_usec = 100000; /* 100 milliseconds */
577 rc = select(20, &infd, outfd, exfd, &t);
578 if (rc < 0)
579 return (-2);
580 rc = read(fileno(f), &c, 1);
581 return (rc == 1 ? c : -1);
582 }
583 #endif /* FIONREAD */
584
585 /*
586 * Plain peekchar function. Nothing fancy. This is just like _fpk
587 * but will wait forever rather than time out.
588 */
589
590 static int
_pk(void)591 _pk(void)
592 {
593 unsigned char c;
594
595 return ((read(cur_term->_inputfd, (char *)&c, 1) == 1) ? c : ERR);
596 }
597
598
599 /*
600 * SS-mouse: check if this mouse button event should map into
601 * function key
602 */
603
604
605 static void
_map_button(chtype * inp)606 _map_button(chtype *inp)
607 {
608 SLK_MAP *slk = SP->slk;
609 int num = slk->_num;
610 int len = slk->_len;
611 int i;
612
613 /* first determine if this mouse button event should be */
614 /* mapped into function key */
615
616 if (!(SP->_map_mbe_to_key &
617 ((BUTTON_CHANGED(3) << (10 + BUTTON_STATUS(3))) |
618 (BUTTON_CHANGED(2) << (5 + BUTTON_STATUS(2))) |
619 (BUTTON_CHANGED(1) << BUTTON_STATUS(1)))))
620 return;
621
622 for (i = 0; i < num; i++) {
623 if (MOUSE_X_POS < slk->_labx[i])
624 break;
625 if (MOUSE_X_POS > slk->_labx[i] + len)
626 continue;
627 *inp = KEY_F(1) + i;
628 break;
629 }
630 }
631