2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A/* Copyright (c) 1988 AT&T */
2N/A/* All Rights Reserved */
2N/A
2N/A/*
2N/A * University Copyright- Copyright (c) 1982, 1986, 1988
2N/A * The Regents of the University of California
2N/A * All Rights Reserved
2N/A *
2N/A * University Acknowledgment- Portions of this document are derived from
2N/A * software developed by the University of California, Berkeley, and its
2N/A * contributors.
2N/A */
2N/A
2N/A/*LINTLIBRARY*/
2N/A
2N/A#include "curses_inc.h"
2N/A#include <signal.h>
2N/A#include <unistd.h>
2N/A#ifdef DEBUG
2N/A#include <ctype.h>
2N/A#endif /* DEBUG */
2N/A
2N/A/*
2N/A * Read a key typed from the terminal
2N/A *
2N/A * interpret: = 0 for single-char key only
2N/A * = 1 for matching function key and macro patterns.
2N/A * = 2 same as 1 but no time-out for funckey matching.
2N/A */
2N/A
2N/Astatic int _getkey(int, chtype *);
2N/Astatic int _fpk(void);
2N/Astatic int _pk(void);
2N/A
2N/Achtype
2N/Atgetch(int interpret)
2N/A{
2N/A int i = 0, j, collapse = 1;
2N/A#define WAIT3 333
2N/A chtype inp;
2N/A chtype *inputQ = cur_term->_input_queue;
2N/A char *chars_onQ = &(cur_term->_chars_on_queue);
2N/A
2N/A#ifdef SYSV
2N/A /*
2N/A * Register the fact that getch is being used so
2N/A * that typeahead checking can be done.
2N/A * This code should GO AWAY when a poll() or FIONREAD can
2N/A * be done on the file descriptor as then the check
2N/A * will be non-destructive.
2N/A */
2N/A cur_term->fl_typeahdok = TRUE;
2N/A#endif /* SYSV */
2N/A
2N/A /* ask for input */
2N/A if (cur_term->_ungotten > 0) {
2N/A cur_term->_ungotten--;
2N/A /* decode an ungetch()'d character */
2N/A inp = -inputQ[0];
2N/A } else {
2N/A /* Only read a character if there is no typeahead/peekahead. */
2N/A if (*chars_onQ == 0) {
2N/A /* (*chars_onQ)++; MR */
2N/A#ifdef FIONREAD
2N/A inp = _readchar();
2N/A#else /* FIONREAD */
2N/A inp = (chtype) _pk();
2N/A if ((int)inp == ERR) {
2N/A /*
2N/A * interpret is set to 0 so that down below we don't
2N/A * drop into getkey since we already know there can't be
2N/A * a key that starts with -1. Also, we don't want to
2N/A * access funckeystarter[-1].
2N/A */
2N/A interpret = FALSE;
2N/A }
2N/A#endif /* FIONREAD */
2N/A (*chars_onQ)++;
2N/A } else
2N/A inp = inputQ[0];
2N/A
2N/A#ifdef DEBUG
2N/A if (outf)
2N/A fprintf(outf, "TGETCH read '%s'\n", unctrl(inp));
2N/A#endif /* DEBUG */
2N/A
2N/A /* Check for arrow and function keys */
2N/A if (interpret && cur_term->funckeystarter[inp])
2N/A collapse = _getkey(interpret - 1, &inp);
2N/A }
2N/A
2N/A /* Collapse the input queue to remove the escape */
2N/A /* sequence from the stack. */
2N/A
2N/A j = *chars_onQ;
2N/A (*chars_onQ) -= collapse;
2N/A while (collapse < j)
2N/A inputQ[i++] = inputQ[collapse++];
2N/A return (inp);
2N/A}
2N/A
2N/A#ifdef FIONREAD
2N/Astatic int
2N/A_readchar()
2N/A{
2N/A int i;
2N/A unsigned char c;
2N/A
2N/A if (cur_term->_delay == 0) {
2N/A int arg;
2N/A
2N/A (void) ioctl(cur_term->_inputfd, FIONREAD, &arg);
2N/A#ifdef DEBUG
2N/A if (outf)
2N/A fprintf(outf, "FIONREAD returns %d\n", arg);
2N/A#endif /* DEBUG */
2N/A if (arg < 1)
2N/A return (-1);
2N/A } else
2N/A if (cur_term->_delay > 0) {
2N/A char c;
2N/A int infd;
2N/A
2N/A infd = 1 << cur_term->_inputfd;
2N/A t.tv_sec = cur_term->_delay / 1000;
2N/A t.tv_usec = (cur_term->_delay % 1000) * 1000;
2N/A i = select(20, &infd, (int *)NULL, (int *)NULL, &t);
2N/A if (i < 0)
2N/A return (ERR);
2N/A i = read(cur_term->_inputfd, &c, 1);
2N/A } else
2N/A i = read(cur_term->_inputfd, &c, 1);
2N/A
2N/A#ifdef DEBUG
2N/A if (outf)
2N/A fprintf(outf, "read from %d returns %d chars, first %o\n",
2N/A cur_term->_inputfd, i, c);
2N/A#endif /* DEBUG */
2N/A
2N/A if (i > 0)
2N/A return (c);
2N/A else
2N/A return (ERR);
2N/A}
2N/A#endif /* !FIONREAD */
2N/A
2N/A#ifdef DEBUG
2N/Aextern char *_asciify();
2N/A#endif /* DEBUG */
2N/A
2N/Astatic int get_xterm_mouse(int, int *);
2N/Astatic void _map_button(chtype *);
2N/A
2N/A/*
2N/A * This algorithm is a "learning" algorithm. The premise is
2N/A * that keys used once are like to be used again and again.
2N/A * Since the time for a linear search of the table is so
2N/A * expensive, we move keys that are found up to the top of
2N/A * the list, making the access to a repeated key very fast and
2N/A * keys that have been used before close to the top.
2N/A */
2N/A
2N/Astatic int
2N/A_getkey(int blockpeek, chtype *inp)
2N/A{
2N/A _KEY_MAP **kp = cur_term->_keys;
2N/A int key, num_keys = cur_term->_ksz;
2N/A int i;
2N/A chtype *inputQ = cur_term->_input_queue;
2N/A char *chars_onQ = &(cur_term->_chars_on_queue);
2N/A char flag = cur_term->funckeystarter[*inp];
2N/A int first, collapse = 1;
2N/A
2N/A
2N/A#ifdef DEBUG
2N/A if (outf)
2N/A fprintf(outf, "getkey(): looking in linear table, "
2N/A "inp=%d\n", *inp);
2N/A#endif /* DEBUG */
2N/A
2N/A if (flag & _KEY)
2N/A key = 0;
2N/A else {
2N/A key = cur_term->_first_macro;
2N/A blockpeek = TRUE;
2N/A }
2N/A first = key;
2N/A
2N/A for (; key < num_keys; key++) {
2N/A if (kp[key]->_sends[0] == *inp) {
2N/A for (i = 1; i < INP_QSIZE; i++) {
2N/A /* found it? */
2N/A if (kp[key]->_sends[i] == '\0')
2N/A break;
2N/A /* partial match? peek ahead. */
2N/A if (*chars_onQ == i) {
2N/A (*chars_onQ)++;
2N/A inputQ[i] = (blockpeek) ?
2N/A _pk() : _fpk();
2N/A switch ((int)inputQ[i]) {
2N/A case -2:
2N/A /*
2N/A * Since -2 signifies a timeout we don't really
2N/A * want to put it on the queue so we decrement
2N/A * our counter.
2N/A */
2N/A (*chars_onQ)--;
2N/A#ifdef DEBUG
2N/A if (outf)
2N/A fprintf(outf, "Timed out\n");
2N/A#endif /* DEBUG */
2N/A if (flag & _MACRO) {
2N/A#ifdef DEBUG
2N/A if (outf)
2N/A fprintf(outf,
2N/A "Found macro\n");
2N/A#endif /* DEBUG */
2N/A /*
2N/A * We have to decrement one because key will be
2N/A * incremented at the bottom of the out loop.
2N/A */
2N/A key = (first = blockpeek =
2N/A cur_term->_first_macro) -
2N/A 1;
2N/A goto outerloop;
2N/A }
2N/A
2N/A /*FALLTHROUGH*/
2N/A
2N/A case -1:
2N/A goto ret;
2N/A }
2N/A }
2N/A
2N/A /* not this one? */
2N/A if (kp[key]->_sends[i] != inputQ[i])
2N/A goto outerloop;
2N/A }
2N/A
2N/A /* SS-mouse */
2N/A if (kp[key]->_keyval == KEY_MOUSE) {
2N/A MOUSE_STATUS old_mouse;
2N/A int rc;
2N/A
2N/A old_mouse = Mouse_status;
2N/A
2N/A /* read the mouse status information */
2N/A
2N/A if (mouse_info)
2N/A rc = -3; /* NOT IMPLEMENTED */
2N/A else
2N/A rc = get_xterm_mouse(blockpeek, &i);
2N/A
2N/A if (rc == -1) /* read error */
2N/A goto ret;
2N/A else if (rc == -2 || rc == -3) /* timeout */
2N/A /* or not mouse */
2N/A goto outerloop;
2N/A else if (rc == 0) /* report mouse pos */
2N/A Mouse_status.changes |= 020;
2N/A else if (rc >= 1 && rc <= 3)
2N/A /* mouse button event */
2N/A Mouse_status.changes =
2N/A (((MOUSE_X_POS != old_mouse.x ||
2N/A MOUSE_Y_POS != old_mouse.y) << 3) |
2N/A ((Mouse_status.button[2] !=
2N/A old_mouse.button[2]) << 2) |
2N/A ((Mouse_status.button[1] !=
2N/A old_mouse.button[1]) << 1) |
2N/A (Mouse_status.button[0] !=
2N/A old_mouse.button[0]));
2N/A }
2N/A
2N/A /* We found it! Read in any chars left in _sends */
2N/A
2N/A if ((collapse = i) == INP_QSIZE)
2N/A for (; kp[key]->_sends[i]; i++)
2N/A (void) _fpk();
2N/A
2N/A /* move key to top of ordered list */
2N/A if (key != first) {
2N/A _KEY_MAP *savekey = kp[key];
2N/A short *lorder;
2N/A int j;
2N/A
2N/A if (key > cur_term->_first_macro)
2N/A lorder = &(cur_term->_lastmacro_ordered);
2N/A else
2N/A lorder = &(cur_term->_lastkey_ordered);
2N/A /*
2N/A * If we're below the last ordered key, swap next unordered
2N/A * key with this one and ripple from there.
2N/A */
2N/A if (key > *lorder)
2N/A kp[key] = kp[(i = ++(*lorder))];
2N/A else
2N/A i = key;
2N/A /* ripple the ordered keys down */
2N/A for (j = i--; j > first; )
2N/A kp[j--] = kp[i--];
2N/A kp[first] = savekey;
2N/A }
2N/A *inp = kp[first]->_keyval;
2N/A
2N/A /*
2N/A * SS-mouse support: if mouse button event
2N/A * occured on top of the soft label, we may
2N/A * have to return the function key corresponding
2N/A * to that soft label
2N/A */
2N/A
2N/A if (*inp == KEY_MOUSE && A_BUTTON_CHANGED &&
2N/A (MOUSE_Y_POS == LINES) &&
2N/A (SP->slk != (SLK_MAP *) NULL) &&
2N/A (SP->_map_mbe_to_key != 0)) {
2N/A _map_button(inp);
2N/A }
2N/A
2N/A goto ret;
2N/A }
2N/Aouterloop:
2N/A ;
2N/A }
2N/A
2N/Aret:
2N/A /* key not found */
2N/A#ifdef DEBUG
2N/A if (outf)
2N/A if (key == num_keys)
2N/A fprintf(outf, "Did not match anything.\n");
2N/A#endif /* DEBUG */
2N/A return (collapse);
2N/A}
2N/A
2N/A
2N/A/* SS-mouse */
2N/A/* this function tries to read in information that follows KEY_MOUSE: */
2N/A/* the first character identifies what button is involved (1,2,or 3) */
2N/A/* if the first character is 0, we are dealing with report_mouse_pos */
2N/A/*
2N/A * The routine returns the following:
2N/A * -3: not a mouse button event
2N/A * -2: read timed out
2N/A * -1: the read failed
2N/A * [0, 1, 2, 3] - the first character in the mouse event
2N/A */
2N/Astatic int
2N/Aget_xterm_mouse(int blockpeek, int *i)
2N/A{
2N/A chtype *inputQ = cur_term->_input_queue; /* ??? */
2N/A /* LINTED */
2N/A chtype *chars_onQ = (chtype *) &(cur_term->_chars_on_queue);
2N/A int j, mx, my;
2N/A int char1, char2, c1, c2;
2N/A
2N/A /* the first character should be 0, 1, 2, or 4 */
2N/A
2N/A char1 = (inputQ[(*i)++] = (blockpeek) ? _pk() : _fpk());
2N/A
2N/A /* read error or timeout */
2N/A
2N/A if (char1 < 0)
2N/A return (char1);
2N/A (*chars_onQ)++;
2N/A
2N/A if (char1 < '0' || char1 > '3')
2N/A return (-3);
2N/A
2N/A /* if the character is 1, 2, or 3 it must be followed by */
2N/A /* P, R, C, D, or T */
2N/A
2N/A if (char1 != '0') {
2N/A char2 = (inputQ[(*i)++] = (blockpeek) ? _pk() : _fpk());
2N/A
2N/A if (char2 < 0)
2N/A return (char2);
2N/A
2N/A (*chars_onQ)++;
2N/A if (char2 != 'P' && char2 != 'R' && char2 != 'C' &&
2N/A char2 != 'D' && char2 != 'T')
2N/A return (-3);
2N/A }
2N/A
2N/A /* read X and Y coordinates of the mouse */
2N/A
2N/A for (j = 0; j < 2; j++) {
2N/A c1 = (inputQ[(*i)++] = (blockpeek) ? _pk() : _fpk());
2N/A if (c1 < 0)
2N/A return (c1);
2N/A (*chars_onQ)++;
2N/A if (c1 >= ' ' && c1 <= '~') { /* ascii char */
2N/A if (j == 0)
2N/A mx = c1 - ' ';
2N/A else
2N/A my = c1 - ' ';
2N/A } else if (char1 == 01 || char1 == 02) { /* ^A || ^B */
2N/A c2 = (inputQ[(*i)++] = (blockpeek) ? _pk() : _fpk());
2N/A if (c2 < 0)
2N/A return (c2);
2N/A (*chars_onQ)++;
2N/A if (c2 >= ' ' && c2 <= '~') {
2N/A if (j == 0)
2N/A mx = c1 * (c2 - ' ');
2N/A else
2N/A my = c1 * (c2 - ' ');
2N/A } else
2N/A return (-3);
2N/A } else
2N/A return (-3);
2N/A }
2N/A
2N/A /* read complete mouse event: update the Mouse_status structure */
2N/A
2N/A MOUSE_X_POS = mx;
2N/A MOUSE_Y_POS = my;
2N/A j = char1 - '0';
2N/A if (j != 0) {
2N/A switch (char2) {
2N/A case 'P':
2N/A BUTTON_STATUS(j) = BUTTON_PRESSED;
2N/A break;
2N/A case 'R':
2N/A BUTTON_STATUS(j) = BUTTON_RELEASED;
2N/A break;
2N/A case 'C':
2N/A BUTTON_STATUS(j) = BUTTON_CLICKED;
2N/A break;
2N/A case 'D':
2N/A BUTTON_STATUS(j) = BUTTON_DOUBLE_CLICKED;
2N/A break;
2N/A case 'T':
2N/A BUTTON_STATUS(j) = BUTTON_TRIPLE_CLICKED;
2N/A break;
2N/A }
2N/A }
2N/A return (j);
2N/A}
2N/A/* SS-mouse-end */
2N/A
2N/A
2N/A/*
2N/A * Fast peek key. Like getchar but if the right flags are set, times out
2N/A * quickly if there is nothing waiting, returning -1.
2N/A * f is an output stdio descriptor, we read from the fileno.
2N/A * We wait for long enough for a terminal to send another character
2N/A * (at 15cps repeat rate, this is 67 ms, I'm using 100ms to allow
2N/A * a bit of a fudge factor) and time out more quickly.
2N/A * -2 is returned if we time out, -1 is returned if interrupted, and the
2N/A * character is returned otherwise.
2N/A */
2N/A
2N/A#ifndef FIONREAD
2N/A
2N/A/*
2N/A * Traditional implementation. The best resolution we have is 1 second,
2N/A * so we set a 1 second alarm and try to read. If we fail for 1 second,
2N/A * we assume there is no key waiting. Problem here is that 1 second is
2N/A * too long; people can type faster than this.
2N/A *
2N/A * Another possible implementation of changing VMIN/VTIME before and
2N/A * after each read does not work because the tty driver's timeout
2N/A * mechanism is too unreliable when the timeouts are changed too quickly.
2N/A */
2N/A
2N/Astatic char sig_caught;
2N/A
2N/Astatic
2N/A#ifdef SIGPOLL /* Vr3 and beyond */
2N/Avoid
2N/A#endif /* SIGPOLL */
2N/A/* The following line causes a lint warning for "dummy" which is not used. */
2N/A_catch_alarm(int dummy)
2N/A{
2N/A sig_caught = 1;
2N/A}
2N/A
2N/Astatic int
2N/A_fpk(void)
2N/A{
2N/A unsigned char c;
2N/A int infd = cur_term->_inputfd;
2N/A ssize_t rc;
2N/A#ifdef SIGPOLL /* Vr3 and beyond */
2N/A void (*oldsig)(int);
2N/A#else /* SIGPOLL */
2N/A int (*oldsig)(int);
2N/A#endif /* SIGPOLL */
2N/A unsigned int oldalarm, alarm(unsigned);
2N/A
2N/A /* turn off any user alarms and set our own */
2N/A oldalarm = alarm(0);
2N/A sig_caught = 0;
2N/A oldsig = signal(SIGALRM, _catch_alarm);
2N/A (void) alarm(1);
2N/A rc = read(cur_term->_inputfd, (char *)&c, 1);
2N/A (void) alarm(0);
2N/A
2N/A /*
2N/A * This code is to take care of the possibility of
2N/A * the process getting swapped out in the middle of
2N/A * read() call above. The interrupt will cause the
2N/A * read() call to retur, even if a character is really
2N/A * on the clist. So we do a non-blocking read() to make
2N/A * sure that there really isn't a character there.
2N/A */
2N/A
2N/A if (sig_caught && rc != 1)
2N/A if (cur_term->_check_fd != -1)
2N/A rc = read(cur_term->_check_fd, (char *)&c, 1);
2N/A else {
2N/A#include <fcntl.h>
2N/A int fcflags = fcntl(infd, F_GETFL, 0);
2N/A
2N/A (void) fcntl(infd, F_SETFL, fcflags | O_NDELAY);
2N/A rc = read(infd, (char *)&c, 1);
2N/A (void) fcntl(infd, F_SETFL, fcflags);
2N/A }
2N/A
2N/A /* restore the user alarms */
2N/A (void) signal(SIGALRM, oldsig);
2N/A if (sig_caught && oldalarm > 1)
2N/A oldalarm--;
2N/A (void) alarm(oldalarm);
2N/A if (rc == 1) /* got a character */
2N/A return (c);
2N/A else
2N/A if (sig_caught) /* timed out */
2N/A return (-2);
2N/A else /* EOF or got interrupted */
2N/A return (-1);
2N/A}
2N/A#else /* FIONREAD */
2N/A/*
2N/A * If we have the select system call, we can do much better than the
2N/A * traditional method. Even if we don't have the real 4.2BSD select, we
2N/A * can emulate it with napms and FIONREAD. napms might be done with only
2N/A * 1 second resolution, but this is no worse than what we have in the
2N/A * traditional implementation.
2N/A */
2N/Astatic
2N/A_fpk()
2N/A{
2N/A int infd, rc;
2N/A int *outfd, *exfd;
2N/A unsigned char c;
2N/A struct timeval t;
2N/A
2N/A infd = 1 << cur_term->_inputfd;
2N/A outfd = exfd = (int *)NULL;
2N/A t.tv_sec = 0;
2N/A t.tv_usec = 100000; /* 100 milliseconds */
2N/A rc = select(20, &infd, outfd, exfd, &t);
2N/A if (rc < 0)
2N/A return (-2);
2N/A rc = read(fileno(f), &c, 1);
2N/A return (rc == 1 ? c : -1);
2N/A}
2N/A#endif /* FIONREAD */
2N/A
2N/A/*
2N/A * Plain peekchar function. Nothing fancy. This is just like _fpk
2N/A * but will wait forever rather than time out.
2N/A */
2N/A
2N/Astatic int
2N/A_pk(void)
2N/A{
2N/A unsigned char c;
2N/A
2N/A return ((read(cur_term->_inputfd, (char *)&c, 1) == 1) ? c : ERR);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * SS-mouse: check if this mouse button event should map into
2N/A * function key
2N/A */
2N/A
2N/A
2N/Astatic void
2N/A_map_button(chtype *inp)
2N/A{
2N/A SLK_MAP *slk = SP->slk;
2N/A int num = slk->_num;
2N/A int len = slk->_len;
2N/A int i;
2N/A
2N/A /* first determine if this mouse button event should be */
2N/A /* mapped into function key */
2N/A
2N/A if (!(SP->_map_mbe_to_key &
2N/A ((BUTTON_CHANGED(3) << (10 + BUTTON_STATUS(3))) |
2N/A (BUTTON_CHANGED(2) << (5 + BUTTON_STATUS(2))) |
2N/A (BUTTON_CHANGED(1) << BUTTON_STATUS(1)))))
2N/A return;
2N/A
2N/A for (i = 0; i < num; i++) {
2N/A if (MOUSE_X_POS < slk->_labx[i])
2N/A break;
2N/A if (MOUSE_X_POS > slk->_labx[i] + len)
2N/A continue;
2N/A *inp = KEY_F(1) + i;
2N/A break;
2N/A }
2N/A}