V10/ncurses/screen/naps.c
/*
* Code for various kinds of delays. Most of this is nonportable and
* requires various enhancements to the operating system, so it won't
* work on all systems. It is included in curses to provide a portable
* interface, and so curses itself can use it for function keys.
*/
/* @(#) naps.c: 1.1 10/15/83 (1.10 3/6/83) */
#include "curses.ext"
#include <signal.h>
#define NAPINTERVAL 100
#define HZ 60
/* From early specs - this may change by 4.2BSD */
struct _timeval {
long tv_sec;
long tv_usec;
};
/*
* Delay the output for ms milliseconds.
* Note that this is NOT the same as a high resolution sleep. It will
* cause a delay in the output but will not necessarily suspend the
* processor. For applications needing to sleep for 1/10th second,
* this is not a usable substitute. It causes a pause in the displayed
* output, for example, for the eye wink in snake. It is disrecommended
* for "delay" to be much more than 1/2 second, especially at high
* baud rates, because of all the characters it will output. Note
* that due to system delays, the actual pause could be even more.
* Some games won't work decently with this routine.
*/
delay_output(ms)
int ms;
{
extern int _outchar(); /* it's in putp.c */
return _delay(ms*10, _outchar);
}
/*
* napms. Sleep for ms milliseconds. We don't expect a particularly good
* resolution - 60ths of a second is normal, 10ths might even be good enough,
* but the rest of the program thinks in ms because the unit of resolution
* varies from system to system. (In some countries, it's 50ths, for example.)
*
* Here are some reasonable ways to get a good nap.
*
* (1) Use the select system call in Berkeley 4.2BSD.
*
* (2) Use the 1/10th second resolution wait in the UNIX 3.0 tty driver.
* It turns out this is hard to do - you need a tty line that is
* always unused that you have read permission on to sleep on.
*
* (3) Install the ft (fast timer) device in your kernel.
* This is a psuedo-device to which an ioctl will wait n ticks
* and then send you an alarm.
*
* (4) Install the nap system call in your kernel.
* This system call does a timeout for the requested number of ticks.
*
* (5) Write a routine that busy waits checking the time with ftime.
* Ftime is not present on USG systems, and since this busy waits,
* it will drag down response on your system. But it works.
*/
#ifdef TIOCREMOTE
/* on 4.2BSD, use select */
napms(ms)
int ms;
{
struct _timeval t;
/*
* If your 4.2BSD select still rounds up to the next higher second,
* you should remove this code and install the ft driver.
* This routine was written under the assumption that the problem
* would be corrected by 4.2BSD.
*/
t.sec = ms/1000;
t.usec = 1000 * (ms % 1000);
select(0, 0, 0, 0, &t);
return OK;
}
#else
/*
* Pause for ms milliseconds. Convert to ticks and wait that long.
* Call nap, which is either defined below or a system call.
*/
napms(ms)
int ms;
{
int ticks;
int rv;
ticks = ms / (1000 / HZ);
if (ticks <= 0)
ticks = 1;
rv = nap(ticks); /* call either the code below or nap system call */
return rv;
}
#endif
#ifdef FTIOCSET
#define HASNAP
/*
* The following code is adapted from the sleep code in libc.
* It uses the "fast timer" device posted to USENET in Feb 1982.
* nap is like sleep but the units are ticks (e.g. 1/60ths of
* seconds in the USA).
*/
#include <setjmp.h>
static jmp_buf jmp;
static int ftfd;
/* don't call nap directly, you should call napms instead */
static int
nap(n)
unsigned n;
{
int napx();
unsigned altime;
int (*alsig)() = SIG_DFL;
char *ftname;
struct requestbuf {
short time;
short signo;
} rb;
if (n==0)
return OK;
if (ftfd <= 0) {
ftname = "/dev/ft0";
while (ftfd <= 0 && ftname[7] <= '~') {
ftfd = open(ftname, 0);
if (ftfd <= 0)
ftname[7] ++;
}
}
if (ftfd <= 0) { /* Couldn't open a /dev/ft? */
sleepnap(n);
return ERR;
}
altime = alarm(1000); /* time to maneuver */
if (setjmp(jmp)) {
signal(SIGALRM, alsig);
alarm(altime);
return OK;
}
if (altime) {
if (altime > n)
altime -= n;
else {
n = altime;
altime = 1;
}
}
alsig = signal(SIGALRM, napx);
rb.time = n;
rb.signo = SIGALRM;
ioctl(ftfd, FTIOCSET, &rb);
for(;;)
pause();
/*NOTREACHED*/
}
static
napx()
{
longjmp(jmp, 1);
}
#endif
#ifdef USG
#ifndef HASNAP
#define HASNAP
#define IDLETTY "/dev/idletty"
/*
* Do it with the timer in the tty driver. Resolution is only 1/10th
* of a second. Problem is, if the user types something while we're
* sleeping, we wake up immediately, and have no way to tell how long
* we should sleep again. So we're sneaky and use a tty which we are
* pretty sure nobody is using.
*
* Note that we should be able to do this by setting VMIN to 100 and VTIME
* to the proper number of ticks. But due to a bug in the USG tty driver
* (this bug was still there in 5.0) this hangs until VMIN chars are typed
* no matter how much time elapses.
*
* This requires some care. If you choose a tty that is a dialup or
* which otherwise can show carrier, it will hang and you won't get
* any response from the keyboard. You can use /dev/tty if you have
* no such tty, but response will feel funny as described above.
* To find a suitable tty, try "stty > /dev/ttyxx" for various ttyxx's
* that look unused. If it hangs, you can't use it. You might try
* connecting a cable to your port that raises carrier to keep it from hanging.
*
* To use this feature on USG, you must
* ln /dev/ttyxx /dev/idletty,
* where /dev/ttyxx is one of your tty lines that is never used but
* won't hang on open. Otherwise we always return ERR.
*
* THIS USG CODE IS UNSUPPORTED AND ON A USE-AT-YOUR-OWN-RISK BASIS.
*/
static int
nap(ticks)
int ticks;
{
struct termio t, ot;
static int ttyfd;
int n, tenths;
char c;
if (ttyfd == 0)
ttyfd = open(IDLETTY, 2);
if (ttyfd < 0) {
sleepnap(ticks);
return ERR;
}
tenths = (ticks+(HZ/10)/2) / (HZ/10); /* Round to nearest 10th second */
ioctl(ttyfd, TCGETA, &t);
ot = t;
t.c_lflag &= ~ICANON;
t.c_cc[VMIN] = 0;
t.c_cc[VTIME] = tenths;
ioctl(ttyfd, TCSETAW, &t);
n = read(ttyfd, &c, 1);
ioctl(ttyfd, TCSETAW, &ot);
/*
* Now we potentially have a character in c that somebody's going
* to want. We just hope and pray they use getch, because there
* is no reasonable way to push it back onto the tty.
*/
if (n > 0) {
for (n=0; SP->input_queue[n] >= 0; n++)
;
SP->input_queue[n++] = c;
SP->input_queue[n++] = -1;
}
return OK;
}
#endif
#endif
/* If you have some other externally supplied nap(), add -DHASNAP to cflags */
#ifndef HASNAP
static int
nap(ms)
int ms;
{
sleep((ms+999)/1000);
return ERR;
}
#endif
/*
* Nothing better around, so we have to simulate nap with sleep.
*/
static
sleepnap(ticks)
{
sleep((ticks+(HZ-1))/HZ);
}