From 8977fe5ed9d73dd56441d5071a0c2732636e7be3 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 18 Jul 2017 11:53:37 -0400 Subject: 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. --- osdep/terminal-unix.c | 61 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/osdep/terminal-unix.c b/osdep/terminal-unix.c index da6ccfe7f2..c7309fbf50 100644 --- a/osdep/terminal-unix.c +++ b/osdep/terminal-unix.c @@ -25,12 +25,13 @@ #include #include #include +#include #include +#include #include #include #include -#include #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; -- cgit v1.2.3