From 28fc13977e740769d11b17165016559505187486 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 9 Sep 2014 20:58:26 +0200 Subject: terminal-unix: move to thread Do terminal input with a thread, instead of using the central select() loop. This also changes some details how SIGTERM is handled. Part of my crusade against mp_input_add_fd(). --- input/input.c | 20 +--------- input/input.h | 2 - osdep/terminal-unix.c | 103 +++++++++++++++++++++++++++++++++++--------------- osdep/terminal-win.c | 4 -- osdep/terminal.h | 3 -- player/playloop.c | 5 --- 6 files changed, 74 insertions(+), 63 deletions(-) diff --git a/input/input.c b/input/input.c index bf032e6ce2..31ce41ff57 100644 --- a/input/input.c +++ b/input/input.c @@ -187,8 +187,6 @@ struct input_ctx { int wakeup_pipe[2]; }; -int async_quit_request; - static int parse_config(struct input_ctx *ictx, bool builtin, bstr data, const char *location, const char *restrict_section); static void close_input_sources(struct input_ctx *ictx); @@ -296,12 +294,6 @@ static void queue_remove(struct cmd_queue *queue, struct mp_cmd *cmd) *p_prev = cmd->queue_next; } -static void queue_add_head(struct cmd_queue *queue, struct mp_cmd *cmd) -{ - cmd->queue_next = queue->first; - queue->first = cmd; -} - static void queue_add_tail(struct cmd_queue *queue, struct mp_cmd *cmd) { struct mp_cmd **p_prev = &queue->first; @@ -1159,11 +1151,6 @@ mp_cmd_t *mp_input_read_cmd(struct input_ctx *ictx) { input_lock(ictx); read_events(ictx, 0); - if (async_quit_request && !queue_has_abort_cmds(&ictx->cmd_queue)) { - struct mp_cmd *cmd = mp_input_parse_cmd(ictx, bstr0("quit"), ""); - queue_add_head(&ictx->cmd_queue, cmd); - async_quit_request = 0; - } struct cmd_queue *queue = &ictx->cmd_queue; if (!queue->first) { struct mp_cmd *repeated = check_autorepeat(ictx); @@ -1664,11 +1651,6 @@ void mp_input_wakeup_nolock(struct input_ctx *ictx) } } -static bool test_abort(struct input_ctx *ictx) -{ - return async_quit_request || queue_has_abort_cmds(&ictx->cmd_queue); -} - void mp_input_set_main_thread(struct input_ctx *ictx) { ictx->mainthread = pthread_self(); @@ -1678,7 +1660,7 @@ void mp_input_set_main_thread(struct input_ctx *ictx) bool mp_input_check_interrupt(struct input_ctx *ictx) { input_lock(ictx); - bool res = test_abort(ictx); + bool res = queue_has_abort_cmds(&ictx->cmd_queue); if (!res && ictx->mainthread_set && pthread_equal(ictx->mainthread, pthread_self())) { diff --git a/input/input.h b/input/input.h index 6ab1392111..1bd1410f66 100644 --- a/input/input.h +++ b/input/input.h @@ -260,6 +260,4 @@ void mp_input_add_pipe(struct input_ctx *ictx, const char *filename); void mp_input_set_main_thread(struct input_ctx *ictx); -extern int async_quit_request; - #endif /* MPLAYER_INPUT_H */ diff --git a/osdep/terminal-unix.c b/osdep/terminal-unix.c index b4890ee30c..a3cb82aa06 100644 --- a/osdep/terminal-unix.c +++ b/osdep/terminal-unix.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #if HAVE_TERMIOS @@ -41,6 +42,9 @@ #endif #include +#include + +#include "osdep/io.h" #include "common/common.h" #include "misc/bstr.h" @@ -185,7 +189,7 @@ static bool getch2(struct input_ctx *input_ctx) /* 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 - * happen if the terminal state change done in getch2_enable() + * happen if the terminal state change done in terminal_init() * works. */ if (retval == 0) @@ -570,7 +574,7 @@ static bool getch2(struct input_ctx *input_ctx) /* 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 - * happen if the terminal state change done in getch2_enable() + * happen if the terminal state change done in terminal_init() * works. */ if (retval == 0) @@ -645,23 +649,9 @@ static bool getch2(struct input_ctx *input_ctx) #endif /* terminfo/termcap */ -static int read_keys(void *ctx, int fd) -{ - if (getch2(ctx)) - return MP_INPUT_NOTHING; - return MP_INPUT_DEAD; -} - static volatile int getch2_active = 0; static volatile int getch2_enabled = 0; -void terminal_setup_getch(struct input_ctx *ictx) -{ - if (!getch2_enabled) - return; - mp_input_add_fd(ictx, 0, 1, NULL, read_keys, NULL, ictx); -} - static void do_activate_getch2(void) { if (getch2_active || !isatty(STDOUT_FILENO)) @@ -721,7 +711,8 @@ static int setsigaction(int signo, void (*handler) (int), return sigaction(signo, &sa, NULL); } -void getch2_poll(void){ +static void getch2_poll(void) +{ if (!getch2_enabled) return; @@ -752,29 +743,62 @@ static void continue_sighandler(int signum) getch2_poll(); } +static pthread_t input_thread; +static struct input_ctx *input_ctx; +static int death_pipe[2]; + static void quit_request_sighandler(int signum) { do_deactivate_getch2(); - async_quit_request = 1; + write(death_pipe[1], &(char){0}, 1); } -static void getch2_enable(void) +static void *terminal_thread(void *ptr) { - assert(!getch2_enabled); + bool stdin_ok = true; // if false, we still wait for SIGTERM + while (1) { + struct pollfd fds[2] = { + {.events = POLLIN, .fd = death_pipe[0]}, + {.events = POLLIN, .fd = STDIN_FILENO}, + }; + // Wait with some timeout, so we can call getch2_poll() frequently. + poll(fds, stdin_ok ? 2 : 1, 1000); + if (fds[0].revents) + break; + if (fds[1].revents) + stdin_ok = getch2(input_ctx); + getch2_poll(); + } + // Important if we received SIGTERM, rather than regular quit. + struct mp_cmd *cmd = mp_input_parse_cmd(input_ctx, bstr0("quit"), ""); + if (cmd) + mp_input_queue_cmd(input_ctx, cmd); + return NULL; +} + +void terminal_setup_getch(struct input_ctx *ictx) +{ + if (!getch2_enabled) + return; + + assert(!input_ctx); // already setup + + if (mp_make_wakeup_pipe(death_pipe) < 0) + return; + + input_ctx = ictx; + + if (pthread_create(&input_thread, NULL, terminal_thread, NULL)) { + input_ctx = NULL; + close(death_pipe[0]); + close(death_pipe[1]); + return; + } - // handlers to fix terminal settings - setsigaction(SIGCONT, continue_sighandler, 0, true); - setsigaction(SIGTSTP, stop_sighandler, SA_RESETHAND, false); setsigaction(SIGINT, quit_request_sighandler, SA_RESETHAND, false); setsigaction(SIGQUIT, quit_request_sighandler, SA_RESETHAND, false); setsigaction(SIGTERM, quit_request_sighandler, SA_RESETHAND, false); - setsigaction(SIGTTIN, SIG_IGN, 0, true); - setsigaction(SIGTTOU, SIG_IGN, 0, true); - - do_activate_getch2(); - - getch2_enabled = 1; } void terminal_uninit(void) @@ -793,6 +817,14 @@ void terminal_uninit(void) do_deactivate_getch2(); + if (input_ctx) { + write(death_pipe[1], &(char){0}, 1); + pthread_join(input_thread, NULL); + close(death_pipe[0]); + close(death_pipe[1]); + input_ctx = NULL; + } + getch2_enabled = 0; } @@ -815,6 +847,17 @@ int terminal_init(void) { if (isatty(STDOUT_FILENO)) load_termcap(); - getch2_enable(); + + assert(!getch2_enabled); + + // handlers to fix terminal settings + setsigaction(SIGCONT, continue_sighandler, 0, true); + setsigaction(SIGTSTP, stop_sighandler, SA_RESETHAND, false); + setsigaction(SIGTTIN, SIG_IGN, 0, true); + setsigaction(SIGTTOU, SIG_IGN, 0, true); + + do_activate_getch2(); + + getch2_enabled = 1; return 0; } diff --git a/osdep/terminal-win.c b/osdep/terminal-win.c index 20077abc7e..9bfb97041b 100644 --- a/osdep/terminal-win.c +++ b/osdep/terminal-win.c @@ -151,10 +151,6 @@ void terminal_setup_getch(struct input_ctx *ictx) } } -void getch2_poll(void) -{ -} - void terminal_uninit(void) { if (running) { diff --git a/osdep/terminal.h b/osdep/terminal.h index b2336b68a8..fc98f0c469 100644 --- a/osdep/terminal.h +++ b/osdep/terminal.h @@ -44,9 +44,6 @@ bool terminal_in_background(void); /* Get terminal-size in columns/rows. */ void terminal_get_size(int *w, int *h); -/* Enable and disable STDIN line-buffering */ -void getch2_poll(void); - // Windows only. void mp_write_console_ansi(void *wstream, char *buf); diff --git a/player/playloop.c b/player/playloop.c index a06165dfce..d6451083ac 100644 --- a/player/playloop.c +++ b/player/playloop.c @@ -961,9 +961,6 @@ void run_playloop(struct MPContext *mpctx) handle_force_window(mpctx, false); execute_queued_seek(mpctx); - - if (mpctx->opts->use_terminal) - getch2_poll(); } // Waiting for the slave master to send us a new file to play. @@ -989,7 +986,5 @@ void idle_loop(struct MPContext *mpctx) mp_process_input(mpctx); mp_wait_events(mpctx, mpctx->sleeptime); mpctx->sleeptime = 100.0; - if (mpctx->opts->use_terminal) - getch2_poll(); } } -- cgit v1.2.3