/* * 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); }