Use /dev/tty instead of stdin for terminal input

Fixes #4190

This allows you to use terminal input even if you've piped something
into mpv.
This commit is contained in:
Drew DeVault 2017-07-18 11:53:37 -04:00 committed by Jan Ekström
parent 665173d8b2
commit 8977fe5ed9
1 changed files with 43 additions and 18 deletions

View File

@ -25,12 +25,13 @@
#include <signal.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <pthread.h>
#include <time.h>
#include <assert.h>
#include <termios.h>
#include <unistd.h>
#include <poll.h>
#include "osdep/io.h"
#include "osdep/threads.h"
@ -45,6 +46,8 @@
static volatile struct termios tio_orig;
static volatile int tio_orig_set;
static int tty_in, tty_out;
struct key_entry {
const char *seq;
int mpkey;
@ -171,7 +174,7 @@ static struct termbuf buf;
static bool getch2(struct input_ctx *input_ctx)
{
int retval = read(0, &buf.b[buf.len], BUF_LEN - buf.len);
int retval = read(tty_in, &buf.b[buf.len], BUF_LEN - buf.len);
/* Return false on EOF to stop running select() on the FD, as it'd
* trigger all the time. Note that it's possible to get temporary
* EOF on terminal if the user presses ctrl-d, but that shouldn't
@ -259,9 +262,9 @@ static void enable_kx(bool enable)
// tty. Note that stderr being redirected away has no influence over mpv's
// I/O handling except for disabling the terminal OSD, and thus stderr
// shouldn't be relied on here either.
if (isatty(STDOUT_FILENO)) {
if (isatty(tty_out)) {
char *cmd = enable ? "\033=" : "\033>";
(void)write(STDOUT_FILENO, cmd, strlen(cmd));
(void)write(tty_out, cmd, strlen(cmd));
}
}
@ -273,7 +276,7 @@ static void do_activate_getch2(void)
enable_kx(true);
struct termios tio_new;
tcgetattr(0,&tio_new);
tcgetattr(tty_in,&tio_new);
if (!tio_orig_set) {
tio_orig = tio_new;
@ -283,7 +286,7 @@ static void do_activate_getch2(void)
tio_new.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
tio_new.c_cc[VMIN] = 1;
tio_new.c_cc[VTIME] = 0;
tcsetattr(0,TCSANOW,&tio_new);
tcsetattr(tty_in,TCSANOW,&tio_new);
getch2_active = 1;
}
@ -298,7 +301,7 @@ static void do_deactivate_getch2(void)
if (tio_orig_set) {
// once set, it will never be set again
// so we can cast away volatile here
tcsetattr(0, TCSANOW, (const struct termios *) &tio_orig);
tcsetattr(tty_in, TCSANOW, (const struct termios *) &tio_orig);
}
getch2_active = 0;
@ -326,7 +329,7 @@ static void getch2_poll(void)
return;
// check if stdin is in the foreground process group
int newstatus = (tcgetpgrp(0) == getpgrp());
int newstatus = (tcgetpgrp(tty_in) == getpgrp());
// and activate getch2 if it is, deactivate otherwise
if (newstatus)
@ -365,6 +368,14 @@ static void close_death_pipe(void)
}
}
static void close_tty(void)
{
if (tty_in >= 0 && tty_in != STDIN_FILENO)
close(tty_in);
tty_in = tty_out = -1;
}
static void quit_request_sighandler(int signum)
{
do_deactivate_getch2();
@ -376,17 +387,23 @@ static void *terminal_thread(void *ptr)
{
mpthread_set_name("terminal");
bool stdin_ok = read_terminal; // if false, we still wait for SIGTERM
fd_set readfds;
int max = death_pipe[0] > tty_in ? death_pipe[0] : tty_in;
struct timeval timeout = { .tv_sec = 0, .tv_usec = 100000 };
while (1) {
FD_ZERO(&readfds);
FD_SET(death_pipe[0], &readfds);
FD_SET(tty_in, &readfds);
getch2_poll();
struct pollfd fds[2] = {
{.events = POLLIN, .fd = death_pipe[0]},
{.events = POLLIN, .fd = STDIN_FILENO},
};
poll(fds, stdin_ok ? 2 : 1, -1);
if (fds[0].revents)
int s = select(max + 1, &readfds, NULL, NULL, &timeout);
if (s == -1) {
break;
if (fds[1].revents)
stdin_ok = getch2(input_ctx);
} else if (s != 0) {
if (FD_ISSET(death_pipe[0], &readfds))
break;
if (stdin_ok && FD_ISSET(tty_in, &readfds))
stdin_ok = getch2(input_ctx);
}
}
char c;
bool quit = read(death_pipe[0], &c, 1) == 1 && c == 1;
@ -407,16 +424,23 @@ void terminal_setup_getch(struct input_ctx *ictx)
if (mp_make_wakeup_pipe(death_pipe) < 0)
return;
tty_in = tty_out = open("/dev/tty", O_RDWR | O_CLOEXEC);
if (tty_in < 0) {
tty_in = STDIN_FILENO;
tty_out = STDOUT_FILENO;
}
// Disable reading from the terminal even if stdout is not a tty, to make
// mpv ... | less
// do the right thing.
read_terminal = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO);
read_terminal = isatty(tty_in) && isatty(STDOUT_FILENO);
input_ctx = ictx;
if (pthread_create(&input_thread, NULL, terminal_thread, NULL)) {
input_ctx = NULL;
close_death_pipe();
close_tty();
return;
}
@ -445,6 +469,7 @@ void terminal_uninit(void)
(void)write(death_pipe[1], &(char){0}, 1);
pthread_join(input_thread, NULL);
close_death_pipe();
close_tty();
input_ctx = NULL;
}
@ -460,7 +485,7 @@ bool terminal_in_background(void)
void terminal_get_size(int *w, int *h)
{
struct winsize ws;
if (ioctl(0, TIOCGWINSZ, &ws) < 0 || !ws.ws_row || !ws.ws_col)
if (ioctl(tty_in, TIOCGWINSZ, &ws) < 0 || !ws.ws_row || !ws.ws_col)
return;
*w = ws.ws_col;