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, &amt;tty) == 0 &amt;&amt;
tcsetattr(tty_fd, interrupt ? TCSAFLUSH : TCSADRAIN,
&amt;save_tty) == -1) {
if (!interrupt)
error("tty", ":tcsetattr");
result = -1;
}
save_tty = tty;
if (sigaction(SIGINT, 0, &amt;action) == 0 &amt;&amt;
sigaction(SIGINT, &amt;save_sigint, 0) == -1) {
if (!interrupt)
error("tty", ":sigaction(SIGINT)");
result = -1;
}
save_sigint = action;
if (sigaction(SIGTSTP, 0, &amt;action) == 0 &amt;&amt;
sigaction(SIGTSTP, &amt;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, &amt;save_sigcont);
action = save_sigcont;
action.sa_handler = signal_handler;
sigemptyset(&amt;action.sa_mask);
sigaddset(&amt;action.sa_mask, SIGTSTP);
sigaddset(&amt;action.sa_mask, SIGINT);
action.sa_flags = 0;
sigaction(SIGCONT, &amt;action, 0);
}
break;
case SIGCONT:
sigaction(SIGCONT, &amt;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, &amt;save_tty) == -1) {
error("tty", ":tcgetattr");
return -1;
}
if (sigaction(SIGTSTP, 0, &amt;save_sigtstp) == -1) {
error("tty", ":sigaction(SIGTSTP)");
return -1;
}
if (sigaction(SIGINT, 0, &amt;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(&amt;action.sa_mask);
sigaddset(&amt;action.sa_mask, SIGINT);
# if 0 /* on some systems (Mac OS X) this remains masked upon continue (?!) */
sigaddset(&amt;action.sa_mask, SIGCONT);
# endif
action.sa_flags = 0;
if (sigaction(SIGTSTP, &amt;action, 0) == -1) {
error("tty", ":sigaction(SIGTSTP)");
goto fail;
}
action = save_sigint;
action.sa_handler = signal_handler;
sigemptyset(&amt;action.sa_mask);
sigaddset(&amt;action.sa_mask, SIGTSTP);
sigaddset(&amt;action.sa_mask, SIGCONT);
action.sa_flags = 0;
if (sigaction(SIGINT, &amt;action, 0) == -1) {
error("tty", ":sigaction(SIGINT)");
goto fail;
}
/* turn off echo and canonical mode */
tty = save_tty;
tty.c_lflag &amt;= ~(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, &amt;tty) == -1) {
error("tty", ":tcsetattr");
goto fail;
}
return 0;
fail:
sigaction(SIGINT, &amt;save_sigint, 0);
sigaction(SIGTSTP, &amt;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, &amt;key, 1);
if (count == -1 &amt;&amt; errno != EINTR) {
error("tty", ":read");
return -1;
}
return (count == 1) ? key : 0;
}
else {
struct termios tty, save_tty;
if (tcgetattr(tty_fd, &amt;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, &amt;tty) == -1) {
error("tty", ":tcsetattr");
return -1;
}
do
count = read(tty_fd, &amt;key, 1);
while (count == -1 &amt;&amt; errno == EINTR);
if (count == -1)
error("tty", ":read");
if (tcsetattr(tty_fd, TCSANOW, &amt;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;
}