Back to index

tetex-bin  3.0
lib_getch.c
Go to the documentation of this file.
00001 /****************************************************************************
00002  * Copyright (c) 1998-2001,2002 Free Software Foundation, Inc.              *
00003  *                                                                          *
00004  * Permission is hereby granted, free of charge, to any person obtaining a  *
00005  * copy of this software and associated documentation files (the            *
00006  * "Software"), to deal in the Software without restriction, including      *
00007  * without limitation the rights to use, copy, modify, merge, publish,      *
00008  * distribute, distribute with modifications, sublicense, and/or sell       *
00009  * copies of the Software, and to permit persons to whom the Software is    *
00010  * furnished to do so, subject to the following conditions:                 *
00011  *                                                                          *
00012  * The above copyright notice and this permission notice shall be included  *
00013  * in all copies or substantial portions of the Software.                   *
00014  *                                                                          *
00015  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
00016  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
00017  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
00018  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
00019  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
00020  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
00021  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
00022  *                                                                          *
00023  * Except as contained in this notice, the name(s) of the above copyright   *
00024  * holders shall not be used in advertising or otherwise to promote the     *
00025  * sale, use or other dealings in this Software without prior written       *
00026  * authorization.                                                           *
00027  ****************************************************************************/
00028 
00029 /****************************************************************************
00030  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
00031  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
00032  ****************************************************************************/
00033 
00034 /*
00035 **     lib_getch.c
00036 **
00037 **     The routine getch().
00038 **
00039 */
00040 
00041 #include <curses.priv.h>
00042 
00043 MODULE_ID("$Id: lib_getch.c,v 1.71 2003/05/17 23:49:28 tom Exp $")
00044 
00045 #include <fifo_defs.h>
00046 
00047 NCURSES_EXPORT_VAR(int)
00048 ESCDELAY = 1000;            /* max interval betw. chars in funkeys, in millisecs */
00049 
00050 #ifdef NCURSES_WGETCH_EVENTS
00051 #define TWAIT_MASK 7
00052 #else
00053 #define TWAIT_MASK 3
00054 #endif
00055 
00056 /*
00057  * Check for mouse activity, returning nonzero if we find any.
00058  */
00059 static int
00060 check_mouse_activity(int delay EVENTLIST_2nd(_nc_eventlist * evl))
00061 {
00062     int rc;
00063 
00064 #if USE_SYSMOUSE
00065     if ((SP->_mouse_type == M_SYSMOUSE)
00066        && (SP->_sysmouse_head < SP->_sysmouse_tail)) {
00067        return 2;
00068     }
00069 #endif
00070     rc = _nc_timed_wait(TWAIT_MASK, delay, (int *) 0 EVENTLIST_2nd(evl));
00071 #if USE_SYSMOUSE
00072     if ((SP->_mouse_type == M_SYSMOUSE)
00073        && (SP->_sysmouse_head < SP->_sysmouse_tail)
00074        && (rc == 0)
00075        && (errno == EINTR)) {
00076        rc |= 2;
00077     }
00078 #endif
00079     return rc;
00080 }
00081 
00082 static inline int
00083 fifo_peek(void)
00084 {
00085     int ch = SP->_fifo[peek];
00086     TR(TRACE_IEVENT, ("peeking at %d", peek));
00087 
00088     p_inc();
00089     return ch;
00090 }
00091 
00092 static inline int
00093 fifo_pull(void)
00094 {
00095     int ch;
00096     ch = SP->_fifo[head];
00097     TR(TRACE_IEVENT, ("pulling %s from %d", _tracechar(ch), head));
00098 
00099     if (peek == head) {
00100        h_inc();
00101        peek = head;
00102     } else
00103        h_inc();
00104 
00105 #ifdef TRACE
00106     if (_nc_tracing & TRACE_IEVENT)
00107        _nc_fifo_dump();
00108 #endif
00109     return ch;
00110 }
00111 
00112 static inline int
00113 fifo_push(EVENTLIST_0th(_nc_eventlist * evl))
00114 {
00115     int n;
00116     int ch = 0;
00117     int mask = 0;
00118 
00119     (void) mask;
00120     if (tail == -1)
00121        return ERR;
00122 
00123 #ifdef HIDE_EINTR
00124   again:
00125     errno = 0;
00126 #endif
00127 
00128 #ifdef NCURSES_WGETCH_EVENTS
00129     if (evl
00130 #if USE_GPM_SUPPORT || USE_EMX_MOUSE || USE_SYSMOUSE
00131        || (SP->_mouse_fd >= 0)
00132 #endif
00133        ) {
00134        mask = check_mouse_activity(-1 EVENTLIST_2nd(evl));
00135     } else
00136        mask = 0;
00137 
00138     if (mask & 4) {
00139        T(("fifo_push: ungetch KEY_EVENT"));
00140        ungetch(KEY_EVENT);
00141        return KEY_EVENT;
00142     }
00143 #elif USE_GPM_SUPPORT || USE_EMX_MOUSE || USE_SYSMOUSE
00144     if (SP->_mouse_fd >= 0) {
00145        mask = check_mouse_activity(-1 EVENTLIST_2nd(evl));
00146     }
00147 #endif
00148 
00149 #if USE_GPM_SUPPORT || USE_EMX_MOUSE
00150     if ((SP->_mouse_fd >= 0) && (mask & 2)) {
00151        SP->_mouse_event(SP);
00152        ch = KEY_MOUSE;
00153        n = 1;
00154     } else
00155 #endif
00156 #if USE_SYSMOUSE
00157        if ((SP->_mouse_type == M_SYSMOUSE)
00158            && (SP->_sysmouse_head < SP->_sysmouse_tail)) {
00159        SP->_mouse_event(SP);
00160        ch = KEY_MOUSE;
00161        n = 1;
00162     } else if ((SP->_mouse_type == M_SYSMOUSE)
00163               && (mask <= 0) && errno == EINTR) {
00164        SP->_mouse_event(SP);
00165        ch = KEY_MOUSE;
00166        n = 1;
00167     } else
00168 #endif
00169     {                       /* Can block... */
00170        unsigned char c2 = 0;
00171        n = read(SP->_ifd, &c2, 1);
00172        ch = c2;
00173     }
00174 
00175 #ifdef HIDE_EINTR
00176     /*
00177      * Under System V curses with non-restarting signals, getch() returns
00178      * with value ERR when a handled signal keeps it from completing.
00179      * If signals restart system calls, OTOH, the signal is invisible
00180      * except to its handler.
00181      *
00182      * We don't want this difference to show.  This piece of code
00183      * tries to make it look like we always have restarting signals.
00184      */
00185     if (n <= 0 && errno == EINTR)
00186        goto again;
00187 #endif
00188 
00189     if ((n == -1) || (n == 0)) {
00190        TR(TRACE_IEVENT, ("read(%d,&ch,1)=%d, errno=%d", SP->_ifd, n, errno));
00191        ch = ERR;
00192     }
00193     TR(TRACE_IEVENT, ("read %d characters", n));
00194 
00195     SP->_fifo[tail] = ch;
00196     SP->_fifohold = 0;
00197     if (head == -1)
00198        head = peek = tail;
00199     t_inc();
00200     TR(TRACE_IEVENT, ("pushed %s at %d", _tracechar(ch), tail));
00201 #ifdef TRACE
00202     if (_nc_tracing & TRACE_IEVENT)
00203        _nc_fifo_dump();
00204 #endif
00205     return ch;
00206 }
00207 
00208 static inline void
00209 fifo_clear(void)
00210 {
00211     memset(SP->_fifo, 0, sizeof(SP->_fifo));
00212     head = -1;
00213     tail = peek = 0;
00214 }
00215 
00216 static int kgetch(EVENTLIST_0th(_nc_eventlist * evl));
00217 
00218 #define wgetch_should_refresh(win) (\
00219        (is_wintouched(win) || (win->_flags & _HASMOVED)) \
00220        && !(win->_flags & _ISPAD))
00221 
00222 NCURSES_EXPORT(int)
00223 _nc_wgetch(WINDOW *win,
00224           unsigned long *result,
00225           int use_meta
00226           EVENTLIST_2nd(_nc_eventlist * evl))
00227 {
00228     int ch;
00229 #ifdef NCURSES_WGETCH_EVENTS
00230     long event_delay = -1;
00231 #endif
00232 
00233     T((T_CALLED("_nc_wgetch(%p)"), win));
00234 
00235     *result = 0;
00236     if (!win)
00237        returnCode(ERR);
00238 
00239     if (cooked_key_in_fifo()) {
00240        if (wgetch_should_refresh(win))
00241            wrefresh(win);
00242 
00243        *result = fifo_pull();
00244        returnCode(OK);
00245     }
00246 #ifdef NCURSES_WGETCH_EVENTS
00247     if (evl && (evl->count == 0))
00248        evl = NULL;
00249     event_delay = _nc_eventlist_timeout(evl);
00250 #endif
00251 
00252     /*
00253      * Handle cooked mode.  Grab a string from the screen,
00254      * stuff its contents in the FIFO queue, and pop off
00255      * the first character to return it.
00256      */
00257     if (head == -1 &&
00258        !SP->_notty &&
00259        !SP->_raw &&
00260        !SP->_cbreak &&
00261        !SP->_called_wgetch) {
00262        char buf[MAXCOLUMNS], *sp;
00263        int rc;
00264 
00265        TR(TRACE_IEVENT, ("filling queue in cooked mode"));
00266 
00267        SP->_called_wgetch = TRUE;
00268        rc = wgetnstr(win, buf, MAXCOLUMNS);
00269        SP->_called_wgetch = FALSE;
00270 
00271        /* ungetch in reverse order */
00272 #ifdef NCURSES_WGETCH_EVENTS
00273        if (rc != KEY_EVENT)
00274 #endif
00275            ungetch('\n');
00276        for (sp = buf + strlen(buf); sp > buf; sp--)
00277            ungetch(sp[-1]);
00278 
00279 #ifdef NCURSES_WGETCH_EVENTS
00280        /* Return it first */
00281        if (rc == KEY_EVENT) {
00282            *result = rc;
00283            returnCode(OK);
00284        }
00285 #endif
00286 
00287        *result = fifo_pull();
00288        returnCode(OK);
00289     }
00290 
00291     if (win->_use_keypad != SP->_keypad_on)
00292        _nc_keypad(win->_use_keypad);
00293 
00294     if (wgetch_should_refresh(win))
00295        wrefresh(win);
00296 
00297     if (!win->_notimeout && (win->_delay >= 0 || SP->_cbreak > 1)) {
00298        int delay;
00299 
00300        TR(TRACE_IEVENT, ("timed delay in wgetch()"));
00301        if (SP->_cbreak > 1)
00302            delay = (SP->_cbreak - 1) * 100;
00303        else
00304            delay = win->_delay;
00305 
00306 #ifdef NCURSES_WGETCH_EVENTS
00307        if (event_delay >= 0 && delay > event_delay)
00308            delay = event_delay;
00309 #endif
00310 
00311        TR(TRACE_IEVENT, ("delay is %d milliseconds", delay));
00312 
00313        if (head == -1) {    /* fifo is empty */
00314            int rc = check_mouse_activity(delay EVENTLIST_2nd(evl));
00315 
00316 #ifdef NCURSES_WGETCH_EVENTS
00317            if (rc & 4) {
00318               *result = KEY_EVENT;
00319               returnCode(OK);
00320            }
00321 #endif
00322            if (!rc)
00323               returnCode(ERR);
00324        }
00325        /* else go on to read data available */
00326     }
00327 
00328     if (win->_use_keypad) {
00329        /*
00330         * This is tricky.  We only want to get special-key
00331         * events one at a time.  But we want to accumulate
00332         * mouse events until either (a) the mouse logic tells
00333         * us it's picked up a complete gesture, or (b)
00334         * there's a detectable time lapse after one.
00335         *
00336         * Note: if the mouse code starts failing to compose
00337         * press/release events into clicks, you should probably
00338         * increase the wait with mouseinterval().
00339         */
00340        int runcount = 0;
00341        int rc;
00342 
00343        do {
00344            ch = kgetch(EVENTLIST_1st(evl));
00345            if (ch == KEY_MOUSE) {
00346               ++runcount;
00347               if (SP->_mouse_inline(SP))
00348                   break;
00349            }
00350            if (SP->_maxclick < 0)
00351               break;
00352        } while
00353            (ch == KEY_MOUSE
00354             && (((rc = check_mouse_activity(SP->_maxclick
00355                                         EVENTLIST_2nd(evl))) != 0
00356                 && !(rc & 4))
00357                || !SP->_mouse_parse(runcount)));
00358 #ifdef NCURSES_WGETCH_EVENTS
00359        if ((rc & 4) && !ch == KEY_EVENT) {
00360            ungetch(ch);
00361            ch = KEY_EVENT;
00362        }
00363 #endif
00364        if (runcount > 0 && ch != KEY_MOUSE) {
00365 #ifdef NCURSES_WGETCH_EVENTS
00366            /* mouse event sequence ended by an event, report event */
00367            if (ch == KEY_EVENT) {
00368               ungetch(KEY_MOUSE);  /* FIXME This interrupts a gesture... */
00369            } else
00370 #endif
00371            {
00372               /* mouse event sequence ended by keystroke, store keystroke */
00373               ungetch(ch);
00374               ch = KEY_MOUSE;
00375            }
00376        }
00377     } else {
00378        if (head == -1)
00379            fifo_push(EVENTLIST_1st(evl));
00380        ch = fifo_pull();
00381     }
00382 
00383     if (ch == ERR) {
00384 #if USE_SIZECHANGE
00385        if (SP->_sig_winch) {
00386            _nc_update_screensize();
00387            /* resizeterm can push KEY_RESIZE */
00388            if (cooked_key_in_fifo()) {
00389               *result = fifo_pull();
00390               returnCode(*result >= KEY_MIN ? KEY_CODE_YES : OK);
00391            }
00392        }
00393 #endif
00394        returnCode(ERR);
00395     }
00396 
00397     /*
00398      * If echo() is in effect, display the printable version of the
00399      * key on the screen.  Carriage return and backspace are treated
00400      * specially by Solaris curses:
00401      *
00402      * If carriage return is defined as a function key in the
00403      * terminfo, e.g., kent, then Solaris may return either ^J (or ^M
00404      * if nonl() is set) or KEY_ENTER depending on the echo() mode. 
00405      * We echo before translating carriage return based on nonl(),
00406      * since the visual result simply moves the cursor to column 0.
00407      *
00408      * Backspace is a different matter.  Solaris curses does not
00409      * translate it to KEY_BACKSPACE if kbs=^H.  This does not depend
00410      * on the stty modes, but appears to be a hardcoded special case.
00411      * This is a difference from ncurses, which uses the terminfo entry.
00412      * However, we provide the same visual result as Solaris, moving the
00413      * cursor to the left.
00414      */
00415     if (SP->_echo && !(win->_flags & _ISPAD)) {
00416        chtype backup = (ch == KEY_BACKSPACE) ? '\b' : ch;
00417        if (backup < KEY_MIN)
00418            wechochar(win, backup);
00419     }
00420 
00421     /*
00422      * Simulate ICRNL mode
00423      */
00424     if ((ch == '\r') && SP->_nl)
00425        ch = '\n';
00426 
00427     /* Strip 8th-bit if so desired.  We do this only for characters that
00428      * are in the range 128-255, to provide compatibility with terminals
00429      * that display only 7-bit characters.  Note that 'ch' may be a
00430      * function key at this point, so we mustn't strip _those_.
00431      */
00432     if (!use_meta)
00433        if ((ch < KEY_MIN) && (ch & 0x80))
00434            ch &= 0x7f;
00435 
00436     T(("wgetch returning : %s", _tracechar(ch)));
00437 
00438     *result = ch;
00439     returnCode(ch >= KEY_MIN ? KEY_CODE_YES : OK);
00440 }
00441 
00442 #ifdef NCURSES_WGETCH_EVENTS
00443 NCURSES_EXPORT(int)
00444 wgetch_events(WINDOW *win, _nc_eventlist * evl)
00445 {
00446     int code;
00447     unsigned long value;
00448 
00449     T((T_CALLED("wgetch_events(%p,%p)"), win, evl));
00450     code = _nc_wgetch(win,
00451                     &value,
00452                     SP->_use_meta
00453                     EVENTLIST_2nd(evl));
00454     if (code != ERR)
00455        code = value;
00456     returnCode(code);
00457 }
00458 #endif
00459 
00460 NCURSES_EXPORT(int)
00461 wgetch(WINDOW *win)
00462 {
00463     int code;
00464     unsigned long value;
00465 
00466     T((T_CALLED("wgetch(%p)"), win));
00467     code = _nc_wgetch(win,
00468                     &value,
00469                     SP->_use_meta
00470                     EVENTLIST_2nd((_nc_eventlist *) 0));
00471     if (code != ERR)
00472        code = value;
00473     returnCode(code);
00474 }
00475 
00476 /*
00477 **      int
00478 **      kgetch()
00479 **
00480 **      Get an input character, but take care of keypad sequences, returning
00481 **      an appropriate code when one matches the input.  After each character
00482 **      is received, set an alarm call based on ESCDELAY.  If no more of the
00483 **      sequence is received by the time the alarm goes off, pass through
00484 **      the sequence gotten so far.
00485 **
00486 **     This function must be called when there are no cooked keys in queue.
00487 **     (that is head==-1 || peek==head)
00488 **
00489 */
00490 
00491 static int
00492 kgetch(EVENTLIST_0th(_nc_eventlist * evl))
00493 {
00494     struct tries *ptr;
00495     int ch = 0;
00496     int timeleft = ESCDELAY;
00497 
00498     TR(TRACE_IEVENT, ("kgetch() called"));
00499 
00500     ptr = SP->_keytry;
00501 
00502     for (;;) {
00503        if (cooked_key_in_fifo() && SP->_fifo[head] >= KEY_MIN) {
00504            break;
00505        } else if (!raw_key_in_fifo()) {
00506            ch = fifo_push(EVENTLIST_1st(evl));
00507            if (ch == ERR) {
00508               peek = head;  /* the keys stay uninterpreted */
00509               return ERR;
00510            }
00511 #ifdef NCURSES_WGETCH_EVENTS
00512            else if (ch == KEY_EVENT) {
00513               peek = head;  /* the keys stay uninterpreted */
00514               return fifo_pull();  /* Remove KEY_EVENT from the queue */
00515            }
00516 #endif
00517        }
00518 
00519        ch = fifo_peek();
00520        if (ch >= KEY_MIN) {
00521            /* If not first in queue, somebody put this key there on purpose in
00522             * emergency.  Consider it higher priority than the unfinished
00523             * keysequence we are parsing.
00524             */
00525            peek = head;
00526            /* assume the key is the last in fifo */
00527            t_dec();         /* remove the key */
00528            return ch;
00529        }
00530 
00531        TR(TRACE_IEVENT, ("ch: %s", _tracechar((unsigned char) ch)));
00532        while ((ptr != NULL) && (ptr->ch != (unsigned char) ch))
00533            ptr = ptr->sibling;
00534 
00535        if (ptr == NULL) {
00536            TR(TRACE_IEVENT, ("ptr is null"));
00537            break;
00538        }
00539        TR(TRACE_IEVENT, ("ptr=%p, ch=%d, value=%d",
00540                        ptr, ptr->ch, ptr->value));
00541 
00542        if (ptr->value != 0) {      /* sequence terminated */
00543            TR(TRACE_IEVENT, ("end of sequence"));
00544            if (peek == tail)
00545               fifo_clear();
00546            else
00547               head = peek;
00548            return (ptr->value);
00549        }
00550 
00551        ptr = ptr->child;
00552 
00553        if (!raw_key_in_fifo()) {
00554            int rc;
00555 
00556            TR(TRACE_IEVENT, ("waiting for rest of sequence"));
00557            rc = check_mouse_activity(timeleft EVENTLIST_2nd(evl));
00558 #ifdef NCURSES_WGETCH_EVENTS
00559            if (rc & 4) {
00560               TR(TRACE_IEVENT, ("interrupted by a user event"));
00561               /* FIXME Should have preserved remainder timeleft for reusal... */
00562               peek = head;  /* Restart interpreting later */
00563               return KEY_EVENT;
00564            }
00565 #endif
00566            if (!rc) {
00567               TR(TRACE_IEVENT, ("ran out of time"));
00568               break;
00569            }
00570        }
00571     }
00572     ch = fifo_pull();
00573     peek = head;
00574     return ch;
00575 }