mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-02-07 12:17:35 +00:00
d2edcad66e
Thanks to Phil Zimmermann for the code and for the license exception we needed to include it. There remains some build system integration work to be done before this code will build properly in the FreeSWITCH tree.
261 lines
4.6 KiB
C
261 lines
4.6 KiB
C
/*
|
|
* 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 */
|