tio/src/tty.c
Jakub Wilk 05b6a554d3 Completed the ^g to ^t transition
In 72a287f189 the escape key was changed from ^g to ^t, but some
code and comments still referred to the old key.
2016-05-07 21:00:05 +02:00

249 lines
6.7 KiB
C

/*
* tio - the simple TTY terminal I/O application
*
* Copyright (c) 2014-2016 Martin Lund
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <fcntl.h>
#include <termios.h>
#include <stdbool.h>
#include <errno.h>
#include "tio/tty.h"
#include "tio/print.h"
#include "tio/options.h"
#include "tio/time.h"
#include "tio/log.h"
static int connected = false;
struct termios new_stdout, old_stdout, old_tio;
static int fd;
static bool tainted = false;
void wait_for_tty_device(void)
{
int ready, n;
struct stat status;
fd_set rdfs;
struct timeval tv;
char c_stdin[3];
/* Loop until device pops up */
while (true)
{
/* Test for accessible device file */
if (access(option.tty_device, R_OK) == 0)
return;
/* Wait up to 1 second */
tv.tv_sec = 1;
tv.tv_usec = 0;
FD_ZERO(&rdfs);
FD_SET(STDIN_FILENO, &rdfs);
/* Block until input becomes available or timeout */
ready = select(STDIN_FILENO + 1, &rdfs, NULL, NULL, &tv);
if (ready)
{
/* Input from stdin ready */
/* Read one character */
n = read(STDIN_FILENO, &c_stdin[0], 1);
/* Exit upon ctrl-t + q sequence */
c_stdin[2] = c_stdin[1];
c_stdin[1] = c_stdin[0];
if ((c_stdin[1] == KEY_Q) && (c_stdin[2] == KEY_CTRL_T))
exit(EXIT_SUCCESS);
}
}
}
void configure_stdout(void)
{
/* Save current stdout settings */
if (tcgetattr(STDOUT_FILENO, &old_stdout) < 0)
{
printf("Error: Saving current stdio settings failed\n");
exit(EXIT_FAILURE);
}
/* Prepare new stdout settings */
bzero(&new_stdout, sizeof(new_stdout));
/* Control, input, output, local modes for stdout */
new_stdout.c_cflag = 0;
new_stdout.c_iflag = 0;
new_stdout.c_oflag = 0;
new_stdout.c_lflag = 0;
/* Control characters */
new_stdout.c_cc[VTIME] = 0; /* Inter-character timer unused */
new_stdout.c_cc[VMIN] = 1; /* Blocking read until 1 character received */
/* Activate new stdout settings */
tcsetattr(STDOUT_FILENO, TCSANOW, &new_stdout);
tcsetattr(STDOUT_FILENO, TCSAFLUSH, &new_stdout);
}
void restore_stdout(void)
{
tcflush(STDOUT_FILENO, TCIOFLUSH);
tcsetattr(STDOUT_FILENO, TCSANOW, &old_stdout);
tcsetattr(STDOUT_FILENO, TCSAFLUSH, &old_stdout);
}
void disconnect_tty(void)
{
if (tainted)
putchar('\n');
color_printf("[tio %s] Disconnected", current_time());
close(fd);
connected = false;
}
void restore_tty(void)
{
tcsetattr(fd, TCSANOW, &old_tio);
tcsetattr(fd, TCSAFLUSH, &old_tio);
if (connected)
disconnect_tty();
}
int connect_tty(void)
{
fd_set rdfs; /* Read file descriptor set */
int maxfd; /* Maximum file descriptor used */
static bool first = true;
int status;
char c_tty;
char c_stdin[3];
/* Open tty device */
fd = open(option.tty_device, O_RDWR | O_NOCTTY );
if (fd < 0)
return EXIT_FAILURE;
/* Make sure device is of tty type */
if (!isatty(fd))
return EXIT_FAILURE;
/* Flush stale I/O data (if any) */
tcflush(fd, TCIOFLUSH);
/* Print connect status */
color_printf("[tio %s] Connected", current_time());
connected = true;
tainted = false;
bzero(&c_stdin[0], 3);
c_tty = 0;
/* Save current port settings */
if (tcgetattr(fd, &old_tio) < 0)
return EXIT_FAILURE;
/* Make sure we restore tty settings on exit */
if (first)
{
atexit(&restore_tty);
first = false;
}
/* Control, input, output, local modes for tty device */
option.tio.c_cflag |= CLOCAL | CREAD;
option.tio.c_oflag = 0;
option.tio.c_lflag = 0;
/* Control characters */
option.tio.c_cc[VTIME] = 0; /* Inter-character timer unused */
option.tio.c_cc[VMIN] = 1; /* Blocking read until 1 character received */
/* Activate new port settings */
tcsetattr(fd, TCSANOW, &option.tio);
tcsetattr(fd, TCSAFLUSH, &option.tio);
maxfd = MAX(fd, STDIN_FILENO) + 1; /* Maximum bit entry (fd) to test */
/* Input loop */
while (true)
{
FD_ZERO(&rdfs);
FD_SET(fd, &rdfs);
FD_SET(STDIN_FILENO, &rdfs);
/* Block until input becomes available */
select(maxfd, &rdfs, NULL, NULL, NULL);
if (FD_ISSET(fd, &rdfs))
{
/* Input from tty device ready */
if (read(fd, &c_tty, 1) > 0)
{
/* Print received tty character to stdout */
putchar(c_tty);
fflush(stdout);
/* Write to log */
if (option.log)
log_write(c_tty);
if (c_tty != KEY_CTRL_T) // Small trick to avoid ctrl-t echo
tainted = true;
} else
{
/* Error reading - device is likely unplugged */
disconnect_tty();
return EXIT_FAILURE;
}
}
if (FD_ISSET(STDIN_FILENO, &rdfs))
{
/* Input from stdin ready */
status = read(STDIN_FILENO, &c_stdin[0], 1);
if ((c_stdin[0] != KEY_Q) && (c_stdin[0] != KEY_CTRL_T))
tainted = true;
/* Exit upon ctrl-t + q sequence */
c_stdin[2] = c_stdin[1];
c_stdin[1] = c_stdin[0];
if ((c_stdin[1] == KEY_Q) && (c_stdin[2] == KEY_CTRL_T))
exit(EXIT_SUCCESS);
/* Forward input to tty device */
status = write(fd, &c_stdin[0], 1);
/* Write to log */
if (option.log)
log_write(c_stdin[0]);
/* Insert output delay */
if (option.output_delay)
usleep(option.output_delay * 1000);
}
}
return EXIT_SUCCESS;
}