mirror of
https://github.com/mpv-player/mpv
synced 2025-02-17 21:27:08 +00:00
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:
parent
665173d8b2
commit
8977fe5ed9
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user