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;
}