123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- /*
- * Copyright (c) 1995 Colin Plumb. All rights reserved.
- * For licensing and other legal details, see the file legal.c.
- *
- * kbunix.c - Unix keyboard input routines.
- */
- /*
- * Define NOTERMIO if you don't have the termios stuff
- */
- #include "first.h"
- #include <fcntl.h>
- #include <signal.h>
- #include <stdio.h>
- #include <stdlib.h> /* For exit() */
- #include <sys/types.h>
- /* How to get cbreak mode */
- #if defined(NOTERMIO)
- #include <sgtty.h> /* No termio: Use ioctl() TIOCGETP and TIOCSETP */
- #elif defined(SVR2)
- #include <termio.h> /* SVR2: Use ioctl() TCGETA and TCSETAF */
- #else /* Usual case */
- #include <termios.h> /* Posix: use tcgetattr/tcsetattr */
- #endif
- #ifdef sun /* including ioctl.h and termios.h gives a lot of warnings on sun */
- #include <sys/filio.h>
- #else
- #include <sys/ioctl.h> /* for FIONREAD */
- #endif /* sun */
- #ifndef FIONREAD
- #define FIONREAD TIOCINQ
- #endif
- #include "posix.h" /* For read(), sleep() */
- #include "kb.h"
- #if UNITTTEST
- #define randEvent(c) (void)c
- #else
- #include "random.h"
- #endif
- #include "kludge.h"
- /* The structure to hold the keyuboard's state */
- #if defined(NOTERMIO)
- static struct sgttyb kbState0, kbState1;
- #elif defined(SVR2)
- static struct termio kbState0, kbState1;
- #else
- static struct termios kbState0, kbState1;
- #endif
- #ifndef CBREAK
- #define CBREAK RAW
- #endif
- /* The basic task of getting the terminal into CBREAK mode. */
- static void
- kbInternalCbreak(int fd)
- {
- #ifdef NOTERMIO
- if (ioctl(fd, TIOCGETP, &kbState0) < 0) {
- fprintf (stderr, "\nUnable to get terminal characteristics: ");
- perror("ioctl");
- exit(1);
- }
- kbState1 = kbState0;
- kbState1.sg_flags |= CBREAK;
- kbState1.sg_flags &= ~ECHO;
- ioctl(fd, TIOCSETP, &kbState1);
- #else /* !NOTERMIO - the usual case */
- #ifdef SVR2
- if (ioctl(fd, TCGETA, &kbState0) < 0)
- #else
- if (tcgetattr(fd, &kbState0) < 0)
- #endif
- {
- fprintf (stderr, "\nUnable to get terminal characteristics: ");
- perror("ioctl");
- exit(1);
- }
- kbState1 = kbState0;
- kbState1.c_cc[VMIN] = 1;
- kbState1.c_cc[VTIME] = 0;
- kbState1.c_lflag &= ~(ECHO|ICANON);
- #ifdef SVR2
- ioctl(fd, TCSETAF, &kbState1);
- #else
- tcsetattr(fd, TCSAFLUSH, &kbState1);
- #endif /* not SVR2 */
- #endif /* !NOTERMIO */
- }
- /* Restore the terminal to normal operation */
- static void
- kbInternalNorm(int fd)
- {
- #if defined(NOTERMIO)
- ioctl(fd, TIOCSETP, &kbState0);
- #elif defined(SVR2)
- ioctl(fd, TCSETAF, &kbState0);
- #else /* Usual case */
- tcsetattr (fd, TCSAFLUSH, &kbState0);
- #endif
- }
- /* State variables */
- static volatile int kbCbreakFlag = 0;
- static int kbFd = -1;
- #ifdef SVR2
- static int (*savesig)(int);
- #else
- static void (*savesig)(int);
- #endif
- /* A wrapper around SIGINT and SIGCONT to restore the terminal modes. */
- static void
- kbSig1(int sig)
- {
- if (kbCbreakFlag)
- kbInternalNorm(kbFd);
- if (sig == SIGINT)
- signal(sig, savesig);
- else
- signal(sig, SIG_DFL);
- raise(sig); /* Re-send the signal */
- }
- static void
- kbAddSigs(void);
- /* Resume cbreak after SIGCONT */
- static void
- kbSig2(int sig)
- {
- (void)sig;
- if (kbCbreakFlag)
- kbInternalCbreak(kbFd);
- else
- kbAddSigs();
- }
- static void
- kbAddSigs(void)
- {
- savesig = signal (SIGINT, kbSig1);
- #ifdef SIGTSTP
- signal (SIGCONT, kbSig2);
- signal (SIGTSTP, kbSig1);
- #endif
- }
- static void
- kbRemoveSigs(void)
- {
- signal (SIGINT, savesig);
- #ifdef SIGTSTP
- signal (SIGCONT, SIG_DFL);
- signal (SIGTSTP, SIG_DFL);
- #endif
- }
- /* Now, at last, the externally callable functions */
- void
- kbCbreak(void)
- {
- if (kbFd < 0) {
- kbFd = open("/dev/tty", O_RDWR);
- if (kbFd < 0) {
- fputs("Can't open tty; using stdin\n", stderr);
- kbFd = STDIN_FILENO;
- }
- }
- kbAddSigs();
- kbCbreakFlag = 1;
- kbInternalCbreak(kbFd);
- }
- void
- kbNorm(void)
- {
- kbInternalNorm(kbFd);
- kbCbreakFlag = 0;
- kbRemoveSigs();
- }
- int
- kbGet(void)
- {
- int i;
- char c;
- i = read(kbFd, &c, 1);
- if (i < 1)
- return -1;
- randEvent(c);
- return c;
- }
- /*
- * Flush any pending input. If "thorough" is set, tries to be more
- * thorough about it. Ideally, wait for 1 second of quiet, but we
- * may do something more primitive.
- *
- * kbCbreak() has the side effect of flushing the inout queue, so this
- * is not too critical.
- */
- void
- kbFlush(int thorough)
- {
- if (thorough)
- sleep(1);
- #if defined(TCIFLUSH)
- tcflush(kbFd, TCIFLUSH);
- #elif defined(TIOCFLUSH)
- #ifndef FREAD
- #define FREAD 1 /* The usual value */
- #endif
- ioctl(kbFd, TIOCFLUSH, FREAD);
- #endif
- }
- #if UNITTEST /* Self-contained test driver */
- #include <ctype.h>
- int
- main(void)
- {
- int c;
- puts("Going to cbreak mode...");
- kbCbreak();
- puts("In cbreak mode. Please type.");
- for (;;) {
- c = kbGet();
- if (c == '\n' || c == '\r')
- break;
- printf("c = %d = '%c'\n", c, c);
- kbFlush(isupper(c));
- }
- puts("Returning to normal mode...");
- kbNorm();
- puts("Done.");
- return 0;
- }
- #endif /* UNITTEST */
|