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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 /* Copyright (c) 1988 AT&T */
27 /* All Rights Reserved */
30 * University Copyright- Copyright (c) 1982, 1986, 1988
31 * The Regents of the University of California
34 * University Acknowledgment- Portions of this document are derived from
35 * software developed by the University of California, Berkeley, and its
41 #include "curses_inc.h"
49 * Read a key typed from the terminal
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.
56 static int _getkey(int, chtype
*);
57 static int _fpk(void);
63 int i
= 0, j
, collapse
= 1;
66 chtype
*inputQ
= cur_term
->_input_queue
;
67 char *chars_onQ
= &(cur_term
->_chars_on_queue
);
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.
77 cur_term
->fl_typeahdok
= TRUE
;
81 if (cur_term
->_ungotten
> 0) {
82 cur_term
->_ungotten
--;
83 /* decode an ungetch()'d character */
86 /* Only read a character if there is no typeahead/peekahead. */
87 if (*chars_onQ
== 0) {
88 /* (*chars_onQ)++; MR */
93 if ((int)inp
== ERR
) {
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].
102 #endif /* FIONREAD */
109 fprintf(outf
, "TGETCH read '%s'\n", unctrl(inp
));
112 /* Check for arrow and function keys */
113 if (interpret
&& cur_term
->funckeystarter
[inp
])
114 collapse
= _getkey(interpret
- 1, &inp
);
117 /* Collapse the input queue to remove the escape */
118 /* sequence from the stack. */
121 (*chars_onQ
) -= collapse
;
123 inputQ
[i
++] = inputQ
[collapse
++];
134 if (cur_term
->_delay
== 0) {
137 (void) ioctl(cur_term
->_inputfd
, FIONREAD
, &arg
);
140 fprintf(outf
, "FIONREAD returns %d\n", arg
);
145 if (cur_term
->_delay
> 0) {
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
);
155 i
= read(cur_term
->_inputfd
, &c
, 1);
157 i
= read(cur_term
->_inputfd
, &c
, 1);
161 fprintf(outf
, "read from %d returns %d chars, first %o\n",
162 cur_term
->_inputfd
, i
, c
);
170 #endif /* !FIONREAD */
173 extern char *_asciify();
176 static int get_xterm_mouse(int, int *);
177 static void _map_button(chtype
*);
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 char 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
;
270 old_mouse
= Mouse_status
;
272 /* read the mouse status information */
275 rc
= -3; /* NOT IMPLEMENTED */
277 rc
= get_xterm_mouse(blockpeek
, &i
);
279 if (rc
== -1) /* read error */
281 else if (rc
== -2 || rc
== -3) /* timeout */
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]));
299 /* We found it! Read in any chars left in _sends */
301 if ((collapse
= i
) == INP_QSIZE
)
302 for (; kp
[key
]->_sends
[i
]; i
++)
305 /* move key to top of ordered list */
307 _KEY_MAP
*savekey
= kp
[key
];
311 if (key
> cur_term
->_first_macro
)
312 lorder
= &(cur_term
->_lastmacro_ordered
);
314 lorder
= &(cur_term
->_lastkey_ordered
);
316 * If we're below the last ordered key, swap next unordered
317 * key with this one and ripple from there.
320 kp
[key
] = kp
[(i
= ++(*lorder
))];
323 /* ripple the ordered keys down */
324 for (j
= i
--; j
> first
; )
328 *inp
= kp
[first
]->_keyval
;
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
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)) {
355 fprintf(outf
, "Did not match anything.\n");
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 */
366 * The routine returns the following:
367 * -3: not a mouse button event
369 * -1: the read failed
370 * [0, 1, 2, 3] - the first character in the mouse event
373 get_xterm_mouse(int blockpeek
, int *i
)
375 chtype
*inputQ
= cur_term
->_input_queue
; /* ??? */
377 chtype
*chars_onQ
= (chtype
*) &(cur_term
->_chars_on_queue
);
379 int char1
, char2
, c1
, c2
;
381 /* the first character should be 0, 1, 2, or 4 */
383 char1
= (inputQ
[(*i
)++] = (blockpeek
) ? _pk() : _fpk());
385 /* read error or timeout */
391 if (char1
< '0' || char1
> '3')
394 /* if the character is 1, 2, or 3 it must be followed by */
395 /* P, R, C, D, or T */
398 char2
= (inputQ
[(*i
)++] = (blockpeek
) ? _pk() : _fpk());
404 if (char2
!= 'P' && char2
!= 'R' && char2
!= 'C' &&
405 char2
!= 'D' && char2
!= 'T')
409 /* read X and Y coordinates of the mouse */
411 for (j
= 0; j
< 2; j
++) {
412 c1
= (inputQ
[(*i
)++] = (blockpeek
) ? _pk() : _fpk());
416 if (c1
>= ' ' && c1
<= '~') { /* ascii char */
421 } else if (char1
== 01 || char1
== 02) { /* ^A || ^B */
422 c2
= (inputQ
[(*i
)++] = (blockpeek
) ? _pk() : _fpk());
426 if (c2
>= ' ' && c2
<= '~') {
428 mx
= c1
* (c2
- ' ');
430 my
= c1
* (c2
- ' ');
437 /* read complete mouse event: update the Mouse_status structure */
445 BUTTON_STATUS(j
) = BUTTON_PRESSED
;
448 BUTTON_STATUS(j
) = BUTTON_RELEASED
;
451 BUTTON_STATUS(j
) = BUTTON_CLICKED
;
454 BUTTON_STATUS(j
) = BUTTON_DOUBLE_CLICKED
;
457 BUTTON_STATUS(j
) = BUTTON_TRIPLE_CLICKED
;
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.
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.
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.
490 static char sig_caught
;
493 #ifdef SIGPOLL /* Vr3 and beyond */
496 /* The following line causes a lint warning for "dummy" which is not used. */
497 _catch_alarm(int dummy
)
506 int infd
= cur_term
->_inputfd
;
508 #ifdef SIGPOLL /* Vr3 and beyond */
513 unsigned int oldalarm
, alarm(unsigned);
515 /* turn off any user alarms and set our own */
518 oldsig
= signal(SIGALRM
, _catch_alarm
);
520 rc
= read(cur_term
->_inputfd
, (char *)&c
, 1);
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.
532 if (sig_caught
&& rc
!= 1)
533 if (cur_term
->_check_fd
!= -1)
534 rc
= read(cur_term
->_check_fd
, (char *)&c
, 1);
537 int fcflags
= fcntl(infd
, F_GETFL
, 0);
539 (void) fcntl(infd
, F_SETFL
, fcflags
| O_NDELAY
);
540 rc
= read(infd
, (char *)&c
, 1);
541 (void) fcntl(infd
, F_SETFL
, fcflags
);
544 /* restore the user alarms */
545 (void) signal(SIGALRM
, oldsig
);
546 if (sig_caught
&& oldalarm
> 1)
548 (void) alarm(oldalarm
);
549 if (rc
== 1) /* got a character */
552 if (sig_caught
) /* timed out */
554 else /* EOF or got interrupted */
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.
573 infd
= 1 << cur_term
->_inputfd
;
574 outfd
= exfd
= (int *)NULL
;
576 t
.tv_usec
= 100000; /* 100 milliseconds */
577 rc
= select(20, &infd
, outfd
, exfd
, &t
);
580 rc
= read(fileno(f
), &c
, 1);
581 return (rc
== 1 ? c
: -1);
583 #endif /* FIONREAD */
586 * Plain peekchar function. Nothing fancy. This is just like _fpk
587 * but will wait forever rather than time out.
595 return ((read(cur_term
->_inputfd
, (char *)&c
, 1) == 1) ? c
: ERR
);
600 * SS-mouse: check if this mouse button event should map into
606 _map_button(chtype
*inp
)
608 SLK_MAP
*slk
= SP
->slk
;
613 /* first determine if this mouse button event should be */
614 /* mapped into function key */
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)))))
622 for (i
= 0; i
< num
; i
++) {
623 if (MOUSE_X_POS
< slk
->_labx
[i
])
625 if (MOUSE_X_POS
> slk
->_labx
[i
] + len
)