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
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.
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]
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1988 AT&T */
28 /* All Rights Reserved */
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
40 #pragma ident "%Z%%M% %I% %E% SMI"
44 #include "curses_inc.h"
52 * Read a key typed from the terminal
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.
59 static int _getkey(int, chtype
*);
60 static int _fpk(void);
66 int i
= 0, j
, collapse
= 1;
69 chtype
*inputQ
= cur_term
->_input_queue
;
70 char *chars_onQ
= &(cur_term
->_chars_on_queue
);
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.
80 cur_term
->fl_typeahdok
= TRUE
;
84 if (cur_term
->_ungotten
> 0) {
85 cur_term
->_ungotten
--;
86 /* decode an ungetch()'d character */
89 /* Only read a character if there is no typeahead/peekahead. */
90 if (*chars_onQ
== 0) {
91 /* (*chars_onQ)++; MR */
96 if ((int)inp
== ERR
) {
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].
105 #endif /* FIONREAD */
112 fprintf(outf
, "TGETCH read '%s'\n", unctrl(inp
));
115 /* Check for arrow and function keys */
116 if (interpret
&& cur_term
->funckeystarter
[inp
])
117 collapse
= _getkey(interpret
- 1, &inp
);
120 /* Collapse the input queue to remove the escape */
121 /* sequence from the stack. */
124 (*chars_onQ
) -= collapse
;
126 inputQ
[i
++] = inputQ
[collapse
++];
137 if (cur_term
->_delay
== 0) {
140 (void) ioctl(cur_term
->_inputfd
, FIONREAD
, &arg
);
143 fprintf(outf
, "FIONREAD returns %d\n", arg
);
148 if (cur_term
->_delay
> 0) {
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
);
158 i
= read(cur_term
->_inputfd
, &c
, 1);
160 i
= read(cur_term
->_inputfd
, &c
, 1);
164 fprintf(outf
, "read from %d returns %d chars, first %o\n",
165 cur_term
->_inputfd
, i
, c
);
173 #endif /* !FIONREAD */
176 extern char *_asciify();
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.
189 _getkey(int blockpeek
, chtype
*inp
)
191 _KEY_MAP
**kp
= cur_term
->_keys
;
192 int key
, num_keys
= cur_term
->_ksz
;
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;
202 fprintf(outf
, "getkey(): looking in linear table, "
209 key
= cur_term
->_first_macro
;
214 for (; key
< num_keys
; key
++) {
215 if (kp
[key
]->_sends
[0] == *inp
) {
216 for (i
= 1; i
< INP_QSIZE
; i
++) {
218 if (kp
[key
]->_sends
[i
] == '\0')
220 /* partial match? peek ahead. */
221 if (*chars_onQ
== i
) {
223 inputQ
[i
] = (blockpeek
) ?
225 switch ((int)inputQ
[i
]) {
228 * Since -2 signifies a timeout we don't really
229 * want to put it on the queue so we decrement
235 fprintf(outf
, "Timed out\n");
244 * We have to decrement one because key will be
245 * incremented at the bottom of the out loop.
247 key
= (first
= blockpeek
=
248 cur_term
->_first_macro
) -
261 if (kp
[key
]->_sends
[i
] != inputQ
[i
])
266 if (kp
[key
]->_keyval
== KEY_MOUSE
) {
267 MOUSE_STATUS old_mouse
;
269 static int get_xterm_mouse(int, int *);
271 old_mouse
= Mouse_status
;
273 /* read the mouse status information */
276 rc
= -3; /* NOT IMPLEMENTED */
278 rc
= get_xterm_mouse(blockpeek
, &i
);
280 if (rc
== -1) /* read error */
282 else if (rc
== -2 || rc
== -3) /* timeout */
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]));
300 /* We found it! Read in any chars left in _sends */
302 if ((collapse
= i
) == INP_QSIZE
)
303 for (; kp
[key
]->_sends
[i
]; i
++)
306 /* move key to top of ordered list */
308 _KEY_MAP
*savekey
= kp
[key
];
312 if (key
> cur_term
->_first_macro
)
313 lorder
= &(cur_term
->_lastmacro_ordered
);
315 lorder
= &(cur_term
->_lastkey_ordered
);
317 * If we're below the last ordered key, swap next unordered
318 * key with this one and ripple from there.
321 kp
[key
] = kp
[(i
= ++(*lorder
))];
324 /* ripple the ordered keys down */
325 for (j
= i
--; j
> first
; )
329 *inp
= kp
[first
]->_keyval
;
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
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
*);
357 fprintf(outf
, "Did not match anything.\n");
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 */
368 * The routine returns the following:
369 * -3: not a mouse button event
371 * -1: the read failed
372 * [0, 1, 2, 3] - the first character in the mouse event
375 get_xterm_mouse(int blockpeek
, int *i
)
377 chtype
*inputQ
= cur_term
->_input_queue
; /* ??? */
379 chtype
*chars_onQ
= (chtype
*) &(cur_term
->_chars_on_queue
);
381 int char1
, char2
, c1
, c2
;
383 /* the first character should be 0, 1, 2, or 4 */
385 char1
= (inputQ
[(*i
)++] = (blockpeek
) ? _pk() : _fpk());
387 /* read error or timeout */
393 if (char1
< '0' || char1
> '3')
396 /* if the character is 1, 2, or 3 it must be followed by */
397 /* P, R, C, D, or T */
400 char2
= (inputQ
[(*i
)++] = (blockpeek
) ? _pk() : _fpk());
406 if (char2
!= 'P' && char2
!= 'R' && char2
!= 'C' &&
407 char2
!= 'D' && char2
!= 'T')
411 /* read X and Y coordinates of the mouse */
413 for (j
= 0; j
< 2; j
++) {
414 c1
= (inputQ
[(*i
)++] = (blockpeek
) ? _pk() : _fpk());
418 if (c1
>= ' ' && c1
<= '~') { /* ascii char */
423 } else if (char1
== 01 || char1
== 02) { /* ^A || ^B */
424 c2
= (inputQ
[(*i
)++] = (blockpeek
) ? _pk() : _fpk());
428 if (c2
>= ' ' && c2
<= '~') {
430 mx
= c1
* (c2
- ' ');
432 my
= c1
* (c2
- ' ');
439 /* read complete mouse event: update the Mouse_status structure */
447 BUTTON_STATUS(j
) = BUTTON_PRESSED
;
450 BUTTON_STATUS(j
) = BUTTON_RELEASED
;
453 BUTTON_STATUS(j
) = BUTTON_CLICKED
;
456 BUTTON_STATUS(j
) = BUTTON_DOUBLE_CLICKED
;
459 BUTTON_STATUS(j
) = BUTTON_TRIPLE_CLICKED
;
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.
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.
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.
492 static char sig_caught
;
495 #ifdef SIGPOLL /* Vr3 and beyond */
498 /* The following line causes a lint warning for "dummy" which is not used. */
499 _catch_alarm(int dummy
)
508 int infd
= cur_term
->_inputfd
;
510 #ifdef SIGPOLL /* Vr3 and beyond */
515 unsigned int oldalarm
, alarm(unsigned);
517 /* turn off any user alarms and set our own */
520 oldsig
= signal(SIGALRM
, _catch_alarm
);
522 rc
= read(cur_term
->_inputfd
, (char *)&c
, 1);
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.
534 if (sig_caught
&& rc
!= 1)
535 if (cur_term
->_check_fd
!= -1)
536 rc
= read(cur_term
->_check_fd
, (char *)&c
, 1);
539 int fcflags
= fcntl(infd
, F_GETFL
, 0);
541 (void) fcntl(infd
, F_SETFL
, fcflags
| O_NDELAY
);
542 rc
= read(infd
, (char *)&c
, 1);
543 (void) fcntl(infd
, F_SETFL
, fcflags
);
546 /* restore the user alarms */
547 (void) signal(SIGALRM
, oldsig
);
548 if (sig_caught
&& oldalarm
> 1)
550 (void) alarm(oldalarm
);
551 if (rc
== 1) /* got a character */
554 if (sig_caught
) /* timed out */
556 else /* EOF or got interrupted */
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.
575 infd
= 1 << cur_term
->_inputfd
;
576 outfd
= exfd
= (int *)NULL
;
578 t
.tv_usec
= 100000; /* 100 milliseconds */
579 rc
= select(20, &infd
, outfd
, exfd
, &t
);
582 rc
= read(fileno(f
), &c
, 1);
583 return (rc
== 1 ? c
: -1);
585 #endif /* FIONREAD */
588 * Plain peekchar function. Nothing fancy. This is just like _fpk
589 * but will wait forever rather than time out.
597 return ((read(cur_term
->_inputfd
, (char *)&c
, 1) == 1) ? c
: ERR
);
602 * SS-mouse: check if this mouse button event should map into
608 _map_button(chtype
*inp
)
610 SLK_MAP
*slk
= SP
->slk
;
615 /* first determine if this mouse button event should be */
616 /* mapped into function key */
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)))))
624 for (i
= 0; i
< num
; i
++) {
625 if (MOUSE_X_POS
< slk
->_labx
[i
])
627 if (MOUSE_X_POS
> slk
->_labx
[i
] + len
)