www.pudn.com > EXT.rar > key.c


#include "myhead.h"

#define TTY_DEVICE "/dev/tty"
static int tty_fd = -1;
static struct termios save_tty;
static struct sigaction save_sigtstp, save_sigint;

/*
 * NAME:	error()
 * DESCRIPTION:	show an error using proper interaction with message()
 */
static
void error(char const *id, char const *format, ...)
{
  int err;
  va_list args;

  err = errno;

  if (id)
    fprintf(stderr, "%s: ", id);

  va_start(args, format);

  if (*format == ':') {
    if (format[1] == 0) {
      format = va_arg(args, char const *);
      errno = err;
      perror(format);
    }
    else {
      errno = err;
      perror(format + 1);
    }
  }
  else {
    vfprintf(stderr, format, args);
    fputc('\n', stderr);
  }

  va_end(args);
}


/*
 * NAME:	restore_tty()
 * DESCRIPTION:	revert to previous terminal settings
 */
int restore_tty(int interrupt)
{
  struct termios tty;
  struct sigaction action;
  int result = 0;

  if (tcgetattr(tty_fd, &tty) == 0 &&
      tcsetattr(tty_fd, interrupt ? TCSAFLUSH : TCSADRAIN,
		&save_tty) == -1) {
    if (!interrupt)
      error("tty", ":tcsetattr");
    result = -1;
  }

  save_tty = tty;

  if (sigaction(SIGINT, 0, &action) == 0 &&
      sigaction(SIGINT, &save_sigint, 0) == -1) {
    if (!interrupt)
      error("tty", ":sigaction(SIGINT)");
    result = -1;
  }

  save_sigint = action;

  if (sigaction(SIGTSTP, 0, &action) == 0 &&
      sigaction(SIGTSTP, &save_sigtstp, 0) == -1) {
    if (!interrupt)
      error("tty", ":sigaction(SIGTSTP)");
    result = -1;
  }

  save_sigtstp = action;

  if (!interrupt) {
    if (close(tty_fd) == -1) {
      error("tty", ":close");
      result = -1;
    }

    tty_fd = -1;
  }

  return result;
}

static
void signal_handler(int signal)
{
  static struct sigaction save_sigcont;

  /* restore tty state and previous signal actions */

  restore_tty(1);

  /* handle SIGCONT after SIGTSTP */

  switch (signal) {
  case SIGTSTP:
    {
      struct sigaction action;

      sigaction(SIGCONT, 0, &save_sigcont);

      action = save_sigcont;
      action.sa_handler = signal_handler;
      sigemptyset(&action.sa_mask);
      sigaddset(&action.sa_mask, SIGTSTP);
      sigaddset(&action.sa_mask, SIGINT);
      action.sa_flags = 0;

      sigaction(SIGCONT, &action, 0);
    }
    break;

  case SIGCONT:
    sigaction(SIGCONT, &save_sigcont, 0);
    break;
  }

  /* re-send signal, which is currently blocked */

  kill(getpid(), signal);

  /* return to previous thread, which should immediately receive the signal */

  return;
}

/*
 * NAME:	setup_tty()
 * DESCRIPTION:	change terminal parameters and signal handlers
 */
int setup_tty(void)
{
  struct termios tty;
  struct sigaction action;

  /* open controlling terminal */

  tty_fd = open(TTY_DEVICE, O_RDONLY);
  if (tty_fd == -1) {
    error("tty", ":", TTY_DEVICE);
    return -1;
  }

  /* save current terminal and signal settings */

  if (tcgetattr(tty_fd, &save_tty) == -1) {
    error("tty", ":tcgetattr");
    return -1;
  }

  if (sigaction(SIGTSTP, 0, &save_sigtstp) == -1) {
    error("tty", ":sigaction(SIGTSTP)");
    return -1;
  }

  if (sigaction(SIGINT, 0, &save_sigint) == -1) {
    error("tty", ":sigaction(SIGINT)");
    return -1;
  }

  /* catch SIGTSTP and SIGINT so the tty state can be restored */

  action = save_sigtstp;
  action.sa_handler = signal_handler;
  sigemptyset(&action.sa_mask);
  sigaddset(&action.sa_mask, SIGINT);
# if 0  /* on some systems (Mac OS X) this remains masked upon continue (?!) */
  sigaddset(&action.sa_mask, SIGCONT);
# endif
  action.sa_flags = 0;

  if (sigaction(SIGTSTP, &action, 0) == -1) {
    error("tty", ":sigaction(SIGTSTP)");
    goto fail;
  }

  action = save_sigint;
  action.sa_handler = signal_handler;
  sigemptyset(&action.sa_mask);
  sigaddset(&action.sa_mask, SIGTSTP);
  sigaddset(&action.sa_mask, SIGCONT);
  action.sa_flags = 0;

  if (sigaction(SIGINT, &action, 0) == -1) {
    error("tty", ":sigaction(SIGINT)");
    goto fail;
  }

  /* turn off echo and canonical mode */

  tty = save_tty;

  tty.c_lflag &= ~(ECHO | ICANON);

  /* set VMIN = VTIME = 0 so read() always returns immediately */

  tty.c_cc[VMIN]  = 0;
  tty.c_cc[VTIME] = 0;

  if (tcsetattr(tty_fd, TCSAFLUSH, &tty) == -1) {
    error("tty", ":tcsetattr");
    goto fail;
  }

  return 0;

 fail:
  sigaction(SIGINT,  &save_sigint,  0);
  sigaction(SIGTSTP, &save_sigtstp, 0);
  return -1;
}
int readkey(int blocking)
{
	unsigned char key;
	ssize_t count;

	if (!blocking) {
		/* tty_fd should be a tty in noncanonical mode with VMIN = VTIME = 0 */

		count = read(tty_fd, &key, 1);
		if (count == -1 && errno != EINTR) {
			error("tty", ":read");
			return -1;
		}

		return (count == 1) ? key : 0;
	}
	else {
		struct termios tty, save_tty;

		if (tcgetattr(tty_fd, &tty) == -1) {
			error("tty", ":tcgetattr");
			return -1;
		}

		save_tty = tty;

		/* change terminal temporarily to get a blocking read() */

		tty.c_cc[VMIN] = 1;

		if (tcsetattr(tty_fd, TCSANOW, &tty) == -1) {
			error("tty", ":tcsetattr");
			return -1;
		}

		do
			count = read(tty_fd, &key, 1);
		while (count == -1 && errno == EINTR);

		if (count == -1)
			error("tty", ":read");

		if (tcsetattr(tty_fd, TCSANOW, &save_tty) == -1) {
			error("tty", ":tcsetattr");
			return -1;
		}

		if (count == -1)
			return -1;

		return (count == 1) ? key : 0;
	}
	return blocking ? -1 : 0;
}

int foo_main(void)
{
	char key;

	setup_tty();
	while(1){
		if((key=readkey(1))!=-1){
			printf("read key: %c\n", key);
		}
	}
    restore_tty(0);
	printf("Done!\n");
	
	return 0;
}