Back to index

tetex-bin  3.0
lib_twait.c
Go to the documentation of this file.
00001 /****************************************************************************
00002  * Copyright (c) 1998-2003,2004 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_twait.c
00036 **
00037 **     The routine _nc_timed_wait().
00038 **
00039 **     (This file was originally written by Eric Raymond; however except for
00040 **     comments, none of the original code remains - T.Dickey).
00041 */
00042 
00043 #include <curses.priv.h>
00044 
00045 #ifdef __BEOS__
00046 #undef false
00047 #undef true
00048 #include <OS.h>
00049 #endif
00050 
00051 #if USE_FUNC_POLL
00052 # if HAVE_SYS_TIME_H
00053 #  include <sys/time.h>
00054 # endif
00055 #elif HAVE_SELECT
00056 # if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
00057 #  include <sys/time.h>
00058 # endif
00059 # if HAVE_SYS_SELECT_H
00060 #  include <sys/select.h>
00061 # endif
00062 #endif
00063 
00064 MODULE_ID("$Id: lib_twait.c,v 1.50 2004/09/25 22:53:43 tom Exp $")
00065 
00066 static long
00067 _nc_gettime(bool first)
00068 {
00069     long res;
00070 
00071 #if HAVE_GETTIMEOFDAY
00072 # define PRECISE_GETTIME 1
00073     static struct timeval t0;
00074     struct timeval t1;
00075     gettimeofday(&t1, (struct timezone *) 0);
00076     if (first) {
00077        t0 = t1;
00078        res = 0;
00079     } else {
00080        /* .tv_sec and .tv_usec are unsigned, be careful when subtracting */
00081        if (t0.tv_usec > t1.tv_usec) {     /* Convert 1s in 1e6 microsecs */
00082            t1.tv_usec += 1000000;
00083            t1.tv_sec--;
00084        }
00085        res = (t1.tv_sec - t0.tv_sec) * 1000
00086            + (t1.tv_usec - t0.tv_usec) / 1000;
00087     }
00088 #else
00089 # define PRECISE_GETTIME 0
00090     static time_t t0;
00091     time_t t1 = time((time_t *) 0);
00092     if (first) {
00093        t0 = t1;
00094     }
00095     res = (t1 - t0) * 1000;
00096 #endif
00097     TR(TRACE_IEVENT, ("%s time: %ld msec", first ? "get" : "elapsed", res));
00098     return res;
00099 }
00100 
00101 #ifdef NCURSES_WGETCH_EVENTS
00102 NCURSES_EXPORT(int)
00103 _nc_eventlist_timeout(_nc_eventlist * evl)
00104 {
00105     _nc_event **ev, **last;
00106     int event_delay = -1;
00107 
00108     if (evl != 0) {
00109 
00110        ev = evl->events;
00111        last = ev + evl->count;
00112 
00113        while (ev < last) {
00114            if ((*ev)->type == _NC_EVENT_TIMEOUT_MSEC) {
00115               event_delay = (*ev)->data.timeout_msec;
00116               if (event_delay < 0)
00117                   event_delay = LONG_MAX; /* FIXME Is this defined? */
00118            }
00119        }
00120     }
00121     return event_delay;
00122 }
00123 #endif /* NCURSES_WGETCH_EVENTS */
00124 
00125 /*
00126  * Wait a specified number of milliseconds, returning nonzero if the timer
00127  * didn't expire before there is activity on the specified file descriptors.
00128  * The file-descriptors are specified by the mode:
00129  *     0 - none (absolute time)
00130  *     1 - ncurses' normal input-descriptor
00131  *     2 - mouse descriptor, if any
00132  *     3 - either input or mouse.
00133  *
00134  * Experimental:  if NCURSES_WGETCH_EVENTS is defined, (mode & 4) determines
00135  * whether to pay attention to evl argument.  If set, the smallest of
00136  * millisecond and of timeout of evl is taken.
00137  *
00138  * We return a mask that corresponds to the mode (e.g., 2 for mouse activity).
00139  *
00140  * If the milliseconds given are -1, the wait blocks until activity on the
00141  * descriptors.
00142  */
00143 NCURSES_EXPORT(int)
00144 _nc_timed_wait(int mode,
00145               int milliseconds,
00146               int *timeleft
00147               EVENTLIST_2nd(_nc_eventlist * evl))
00148 {
00149     int fd;
00150     int count;
00151     int result;
00152 
00153 #ifdef NCURSES_WGETCH_EVENTS
00154     int timeout_is_event = 0;
00155 #endif
00156 
00157 #if USE_FUNC_POLL
00158     struct pollfd fd_list[2];
00159     struct pollfd *fds = fd_list;
00160 #elif defined(__BEOS__)
00161 #elif HAVE_SELECT
00162     static fd_set set;
00163 #endif
00164 
00165     long starttime, returntime;
00166 
00167     TR(TRACE_IEVENT, ("start twait: %d milliseconds, mode: %d",
00168                     milliseconds, mode));
00169 
00170 #ifdef NCURSES_WGETCH_EVENTS
00171     if (mode & 4) {
00172        int event_delay = _nc_eventlist_timeout(evl);
00173 
00174        if (event_delay >= 0
00175            && (milliseconds >= event_delay || milliseconds < 0)) {
00176            milliseconds = event_delay;
00177            timeout_is_event = 1;
00178        }
00179     }
00180 #endif
00181 
00182 #if PRECISE_GETTIME
00183   retry:
00184 #endif
00185     starttime = _nc_gettime(TRUE);
00186 
00187     count = 0;
00188 
00189 #ifdef NCURSES_WGETCH_EVENTS
00190     if ((mode & 4) && evl)
00191        evl->result_flags = 0;
00192 #endif
00193 
00194 #if USE_FUNC_POLL
00195     memset(fd_list, 0, sizeof(fd_list));
00196 
00197 #ifdef NCURSES_WGETCH_EVENTS
00198     if ((mode & 4) && evl)
00199        fds = typeMalloc(struct pollfd, 2 + evl->count);
00200 #endif
00201 
00202     if (mode & 1) {
00203        fds[count].fd = SP->_ifd;
00204        fds[count].events = POLLIN;
00205        count++;
00206     }
00207     if ((mode & 2)
00208        && (fd = SP->_mouse_fd) >= 0) {
00209        fds[count].fd = fd;
00210        fds[count].events = POLLIN;
00211        count++;
00212     }
00213 #ifdef NCURSES_WGETCH_EVENTS
00214     if ((mode & 4) && evl) {
00215        _nc_event **ev = evl->events;
00216        _nc_event **last = ev + evl->count;
00217 
00218        while (ev < last) {
00219            if ((*ev)->type == _NC_EVENT_FILE
00220               && ((*ev)->data.fev.flags & _NC_EVENT_FILE_READABLE)) {
00221               fds[count].fd = (*ev)->data.fev.fd;
00222               fds[count].events = POLLIN;
00223               count++;
00224            }
00225        }
00226     }
00227 #endif
00228 
00229     result = poll(fds, (unsigned) count, milliseconds);
00230 
00231 #ifdef NCURSES_WGETCH_EVENTS
00232     if ((mode & 4) && evl) {
00233        _nc_event **ev = evl->events;
00234        _nc_event **last = ev + evl->count;
00235        int c;
00236 
00237        if (!result)
00238            count = 0;
00239        while (ev < last) {
00240            if ((*ev)->type == _NC_EVENT_FILE
00241               && ((*ev)->data.fev.flags & _NC_EVENT_FILE_READABLE)) {
00242               (*ev)->data.fev.result = 0;
00243               for (c = 0; c < count; c++)
00244                   if (fds[c].fd == (*ev)->data.fev.fd
00245                      && fds[c].revents & POLLIN) {
00246                      (*ev)->data.fev.result |= _NC_EVENT_FILE_READABLE;
00247                      evl->result_flags |= _NC_EVENT_FILE_READABLE;
00248                   }
00249            } else if ((*ev)->type == _NC_EVENT_TIMEOUT_MSEC
00250                      && !result && timeout_is_event) {
00251               evl->result_flags |= _NC_EVENT_TIMEOUT_MSEC;
00252            }
00253        }
00254     }
00255 
00256     if (fds != fd_list)
00257        free((char *) fds);
00258 
00259 #endif
00260 
00261 #elif defined(__BEOS__)
00262     /*
00263      * BeOS's select() is declared in socket.h, so the configure script does
00264      * not see it.  That's just as well, since that function works only for
00265      * sockets.  This (using snooze and ioctl) was distilled from Be's patch
00266      * for ncurses which uses a separate thread to simulate select().
00267      *
00268      * FIXME: the return values from the ioctl aren't very clear if we get
00269      * interrupted.
00270      *
00271      * FIXME: this assumes mode&1 if milliseconds < 0 (see lib_getch.c).
00272      */
00273     result = 0;
00274     if (mode & 1) {
00275        int step = (milliseconds < 0) ? 0 : 5000;
00276        bigtime_t d;
00277        bigtime_t useconds = milliseconds * 1000;
00278        int n, howmany;
00279 
00280        if (useconds <= 0)   /* we're here to go _through_ the loop */
00281            useconds = 1;
00282 
00283        for (d = 0; d < useconds; d += step) {
00284            n = 0;
00285            howmany = ioctl(0, 'ichr', &n);
00286            if (howmany >= 0 && n > 0) {
00287               result = 1;
00288               break;
00289            }
00290            if (useconds > 1 && step > 0) {
00291               snooze(step);
00292               milliseconds -= (step / 1000);
00293               if (milliseconds <= 0) {
00294                   milliseconds = 0;
00295                   break;
00296               }
00297            }
00298        }
00299     } else if (milliseconds > 0) {
00300        snooze(milliseconds * 1000);
00301        milliseconds = 0;
00302     }
00303 #elif HAVE_SELECT
00304     /*
00305      * select() modifies the fd_set arguments; do this in the
00306      * loop.
00307      */
00308     FD_ZERO(&set);
00309 
00310     if (mode & 1) {
00311        FD_SET(SP->_ifd, &set);
00312        count = SP->_ifd + 1;
00313     }
00314     if ((mode & 2)
00315        && (fd = SP->_mouse_fd) >= 0) {
00316        FD_SET(fd, &set);
00317        count = max(fd, count) + 1;
00318     }
00319 #ifdef NCURSES_WGETCH_EVENTS
00320     if ((mode & 4) && evl) {
00321        _nc_event **ev = evl->events;
00322        _nc_event **last = ev + evl->count;
00323 
00324        while (ev < last) {
00325            if ((*ev)->type == _NC_EVENT_FILE
00326               && ((*ev)->data.fev.flags & _NC_EVENT_FILE_READABLE)) {
00327               FD_SET((*ev)->data.fev.fd, &set);
00328               count = max((*ev)->data.fev.fd + 1, count);
00329            }
00330        }
00331     }
00332 #endif
00333 
00334     if (milliseconds >= 0) {
00335        struct timeval ntimeout;
00336        ntimeout.tv_sec = milliseconds / 1000;
00337        ntimeout.tv_usec = (milliseconds % 1000) * 1000;
00338        result = select(count, &set, NULL, NULL, &ntimeout);
00339     } else {
00340        result = select(count, &set, NULL, NULL, NULL);
00341     }
00342 
00343 #ifdef NCURSES_WGETCH_EVENTS
00344     if ((mode & 4) && evl) {
00345        _nc_event **ev = evl->events;
00346        _nc_event **last = ev + evl->count;
00347 
00348        evl->result_flags = 0;
00349        while (ev < last) {
00350            if ((*ev)->type == _NC_EVENT_FILE
00351               && ((*ev)->data.fev.flags & _NC_EVENT_FILE_READABLE)) {
00352               (*ev)->data.fev.result = 0;
00353               if (FD_ISSET((*ev)->data.fev.fd, &set)) {
00354                   (*ev)->data.fev.result |= _NC_EVENT_FILE_READABLE;
00355                   evl->result_flags |= _NC_EVENT_FILE_READABLE;
00356               }
00357            } else if ((*ev)->type == _NC_EVENT_TIMEOUT_MSEC
00358                      && !result && timeout_is_event)
00359               evl->result_flags |= _NC_EVENT_TIMEOUT_MSEC;
00360        }
00361     }
00362 #endif
00363 
00364 #endif /* USE_FUNC_POLL, etc */
00365 
00366     returntime = _nc_gettime(FALSE);
00367 
00368     if (milliseconds >= 0)
00369        milliseconds -= (returntime - starttime);
00370 
00371 #ifdef NCURSES_WGETCH_EVENTS
00372     if (evl) {
00373        _nc_event **ev = evl->events;
00374        _nc_event **last = ev + evl->count;
00375 
00376        evl->result_flags = 0;
00377        while (ev < last) {
00378            if ((*ev)->type == _NC_EVENT_TIMEOUT_MSEC) {
00379               long diff = (returntime - starttime);
00380               if ((*ev)->data.timeout_msec <= diff)
00381                   (*ev)->data.timeout_msec = 0;
00382               else
00383                   (*ev)->data.timeout_msec -= diff;
00384            }
00385 
00386        }
00387     }
00388 #endif
00389 
00390 #if PRECISE_GETTIME && HAVE_NANOSLEEP
00391     /*
00392      * If the timeout hasn't expired, and we've gotten no data,
00393      * this is probably a system where 'select()' needs to be left
00394      * alone so that it can complete.  Make this process sleep,
00395      * then come back for more.
00396      */
00397     if (result == 0 && milliseconds > 100) {
00398        napms(100);          /* FIXME: this won't be right if I recur! */
00399        milliseconds -= 100;
00400        goto retry;
00401     }
00402 #endif
00403 
00404     /* return approximate time left in milliseconds */
00405     if (timeleft)
00406        *timeleft = milliseconds;
00407 
00408     TR(TRACE_IEVENT, ("end twait: returned %d (%d), remaining time %d msec",
00409                     result, errno, milliseconds));
00410 
00411     /*
00412      * Both 'poll()' and 'select()' return the number of file descriptors
00413      * that are active.  Translate this back to the mask that denotes which
00414      * file-descriptors, so that we don't need all of this system-specific
00415      * code everywhere.
00416      */
00417     if (result != 0) {
00418        if (result > 0) {
00419            result = 0;
00420 #if USE_FUNC_POLL
00421            for (count = 0; count < 2; count++) {
00422               if ((mode & (1 << count))
00423                   && (fds[count].revents & POLLIN)) {
00424                   result |= (1 << count);
00425               }
00426            }
00427 #elif defined(__BEOS__)
00428            result = 1;             /* redundant, but simple */
00429 #elif HAVE_SELECT
00430            if ((mode & 2)
00431               && (fd = SP->_mouse_fd) >= 0
00432               && FD_ISSET(fd, &set))
00433               result |= 2;
00434            if ((mode & 1)
00435               && FD_ISSET(SP->_ifd, &set))
00436               result |= 1;
00437 #endif
00438        } else
00439            result = 0;
00440     }
00441 #ifdef NCURSES_WGETCH_EVENTS
00442     if ((mode & 4) && evl && evl->result_flags)
00443        result |= 4;
00444 #endif
00445 
00446     return (result);
00447 }