summaryrefslogtreecommitdiffstats
path: root/input
diff options
context:
space:
mode:
Diffstat (limited to 'input')
-rw-r--r--input/input.c15
-rw-r--r--input/input.h24
-rw-r--r--input/pipe-unix.c59
-rw-r--r--input/pipe-win32.c91
-rw-r--r--input/pipe.c133
5 files changed, 175 insertions, 147 deletions
diff --git a/input/input.c b/input/input.c
index 02589e1941..ea0a4c375a 100644
--- a/input/input.c
+++ b/input/input.c
@@ -1273,8 +1273,13 @@ struct input_ctx *mp_input_init(struct mpv_global *global)
ictx->win_drag = global->opts->allow_win_drag;
- if (input_conf->in_file && input_conf->in_file[0])
- mp_input_add_pipe(ictx, input_conf->in_file);
+ if (input_conf->in_file && input_conf->in_file[0]) {
+#if !defined(__MINGW32__) || HAVE_WAIO
+ mp_input_pipe_add(ictx, input_conf->in_file);
+#else
+ MP_ERR(ictx, "Pipes not available.\n");
+#endif
+ }
return ictx;
}
@@ -1399,10 +1404,12 @@ void mp_input_src_kill(struct mp_input_src *src)
MP_TARRAY_REMOVE_AT(ictx->sources, ictx->num_sources, n);
input_unlock(ictx);
write(src->in->wakeup[1], &(char){0}, 1);
- if (src->close)
- src->close(src);
+ if (src->cancel)
+ src->cancel(src);
if (src->in->thread_running)
pthread_join(src->in->thread, NULL);
+ if (src->uninit)
+ src->uninit(src);
talloc_free(src);
return;
}
diff --git a/input/input.h b/input/input.h
index 7096ae62cb..ef8861eaf3 100644
--- a/input/input.h
+++ b/input/input.h
@@ -91,34 +91,38 @@ struct mp_input_src {
struct mp_input_src_internal *in;
- // If not-NULL: called before destroying the input_src. Should close the
- // underlying device, and free all memory.
- void (*close)(struct mp_input_src *src);
+ // If not-NULL: called before destroying the input_src. Should unblock the
+ // reader loop, and make it exit. (Use with mp_input_add_thread_src().)
+ void (*cancel)(struct mp_input_src *src);
+ // Called after the reader thread returns, and cancel() won't be called
+ // again. This should make sure that nothing after this call accesses src.
+ void (*uninit)(struct mp_input_src *src);
// For free use by the implementer.
void *priv;
};
-/* Add a new input source. The input code can create a new thread, which feeds
- * keys or commands to input_ctx. mp_input_src.close must be set.
- */
+// Add a new input source. The input code can create a new thread, which feeds
+// keys or commands to input_ctx. mp_input_src.uninit must be set.
+// mp_input_src_kill() must not be called by anything after init.
struct mp_input_src *mp_input_add_src(struct input_ctx *ictx);
// Add an input source that runs on a thread. The source is automatically
// removed if the thread loop exits.
// ctx: this is passed to loop_fn.
// loop_fn: this is called once inside of a new thread, and should not return
-// until all input is read, or src->close is called by another thread.
+// until all input is read, or src->cancel is called by another thread.
// You must call mp_input_src_init_done(src) early during init to signal
-// success (then src->close may be called at a later point); on failure,
+// success (then src->cancel may be called at a later point); on failure,
// return from loop_fn immediately.
// Returns >=0 on success, <0 on failure to allocate resources.
-// Do not set src->close after mp_input_src_init_done() has been called.
+// Do not set src->cancel after mp_input_src_init_done() has been called.
int mp_input_add_thread_src(struct input_ctx *ictx, void *ctx,
void (*loop_fn)(struct mp_input_src *src, void *ctx));
// Signal successful init.
// Must be called on the same thread as loop_fn (see mp_input_add_thread_src()).
+// Set src->cancel and src->uninit (if needed) before calling this.
void mp_input_src_init_done(struct mp_input_src *src);
// Currently only with mp_input_add_thread_src().
@@ -241,7 +245,7 @@ bool mp_input_use_alt_gr(struct input_ctx *ictx);
void mp_input_run_cmd(struct input_ctx *ictx, int def_flags, const char **cmd,
const char *location);
-void mp_input_add_pipe(struct input_ctx *ictx, const char *filename);
+void mp_input_pipe_add(struct input_ctx *ictx, const char *filename);
void mp_input_joystick_add(struct input_ctx *ictx, char *dev);
void mp_input_lirc_add(struct input_ctx *ictx, char *lirc_configfile);
diff --git a/input/pipe-unix.c b/input/pipe-unix.c
new file mode 100644
index 0000000000..51eb315d6b
--- /dev/null
+++ b/input/pipe-unix.c
@@ -0,0 +1,59 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <poll.h>
+
+#include "common/msg.h"
+#include "osdep/io.h"
+#include "input.h"
+#include "cmd_parse.h"
+
+static void read_pipe_thread(struct mp_input_src *src, void *param)
+{
+ void *tmp = talloc_new(NULL);
+ char *filename = talloc_strdup(tmp, param); // param deallocates after init
+ int wakeup_fd = mp_input_src_get_wakeup_fd(src);
+ int fd = -1;
+
+ struct mp_log *log = src->log;
+
+ int mode = O_RDONLY;
+ // Use RDWR for FIFOs to ensure they stay open over multiple accesses.
+ struct stat st;
+ if (stat(filename, &st) == 0 && S_ISFIFO(st.st_mode))
+ mode = O_RDWR;
+ fd = open(filename, mode);
+ if (fd < 0) {
+ mp_err(log, "Can't open %s.\n", filename);
+ goto done;
+ }
+
+ mp_input_src_init_done(src);
+
+ while (1) {
+ struct pollfd fds[2] = {
+ { .fd = fd, .events = POLLIN },
+ { .fd = wakeup_fd, .events = POLLIN },
+ };
+ poll(fds, 2, -1);
+ if (!(fds[0].revents & POLLIN))
+ break;
+ char buffer[128];
+ int r = read(fd, buffer, sizeof(buffer));
+ if (r <= 0)
+ break;
+ mp_input_src_feed_cmd_text(src, buffer, r);
+ }
+
+done:
+ close(fd);
+ talloc_free(tmp);
+}
+
+void mp_input_pipe_add(struct input_ctx *ictx, const char *filename)
+{
+ mp_input_add_thread_src(ictx, (void *)filename, read_pipe_thread);
+}
diff --git a/input/pipe-win32.c b/input/pipe-win32.c
new file mode 100644
index 0000000000..816bdcffed
--- /dev/null
+++ b/input/pipe-win32.c
@@ -0,0 +1,91 @@
+#include <pthread.h>
+#include <stdio.h>
+#include <windows.h>
+#include <io.h>
+
+#include <stdint.h>
+#include <waio/waio.h>
+
+#include "common/msg.h"
+#include "osdep/io.h"
+#include "input.h"
+
+static void request_cancel(struct mp_input_src *src)
+{
+ HANDLE terminate = src->priv;
+
+ MP_VERBOSE(src, "Exiting...\n");
+ SetEvent(terminate);
+}
+
+static void uninit(struct mp_input_src *src)
+{
+ HANDLE terminate = src->priv;
+
+ CloseHandle(terminate);
+ MP_VERBOSE(src, "Exited.\n");
+}
+
+static void read_pipe_thread(struct mp_input_src *src, void *param)
+{
+ char *filename = talloc_strdup(src, param);
+
+ struct waio_cx_interface *waio = NULL;
+ int mode = O_RDONLY;
+ int fd = -1;
+ bool close_fd = true;
+ if (strcmp(filename, "/dev/stdin") == 0) { // for symmetry with unix
+ fd = STDIN_FILENO;
+ close_fd = false;
+ }
+ if (fd < 0)
+ fd = open(filename, mode);
+ if (fd < 0) {
+ MP_ERR(src, "Can't open %s.\n", filename);
+ goto done;
+ }
+
+ waio = waio_alloc((void *)_get_osfhandle(fd), 0, NULL, NULL);
+ if (!waio) {
+ MP_ERR(src, "Can't initialize win32 file reader.\n");
+ goto done;
+ }
+
+ HANDLE terminate = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!terminate)
+ goto done;
+
+ src->priv = terminate;
+ src->cancel = request_cancel;
+ src->uninit = uninit;
+ mp_input_src_init_done(src);
+
+ char buffer[128];
+ struct waio_aiocb cb = {
+ .aio_buf = buffer,
+ .aio_nbytes = sizeof(buffer),
+ .hsignal = terminate,
+ };
+ while (1) {
+ if (waio_read(waio, &cb)) {
+ MP_ERR(src, "Read operation failed.\n");
+ break;
+ }
+ if (waio_suspend(waio, (const struct waio_aiocb *[]){&cb}, 1, NULL))
+ break;
+ ssize_t r = waio_return(waio, &cb);
+ if (r <= 0)
+ break; // EOF or error
+ mp_input_src_feed_cmd_text(src, buffer, r);
+ }
+
+done:
+ waio_free(waio);
+ if (close_fd)
+ close(fd);
+}
+
+void mp_input_pipe_add(struct input_ctx *ictx, const char *filename)
+{
+ mp_input_add_thread_src(ictx, (void *)filename, read_pipe_thread);
+}
diff --git a/input/pipe.c b/input/pipe.c
deleted file mode 100644
index 12922f590c..0000000000
--- a/input/pipe.c
+++ /dev/null
@@ -1,133 +0,0 @@
-#include <pthread.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#ifndef __MINGW32__
-#include <poll.h>
-#endif
-
-#include "common/msg.h"
-#include "misc/bstr.h"
-#include "osdep/io.h"
-#include "input.h"
-#include "cmd_parse.h"
-
-static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
-
-struct priv {
- struct mp_log *log;
- char *filename;
- struct mp_input_src *src;
- int wakeup_pipe[2];
-};
-
-static void *reader_thread(void *ctx)
-{
- struct priv *p = ctx;
- pthread_detach(pthread_self());
-
- int mode = O_RDONLY;
-#ifndef __MINGW32__
- // Use RDWR for FIFOs to ensure they stay open over multiple accesses.
- // Note that on Windows due to how the API works, using RDONLY should
- // be ok.
- struct stat st;
- if (stat(p->filename, &st) == 0 && S_ISFIFO(st.st_mode))
- mode = O_RDWR;
-#endif
- int fd = -1;
- bool close_fd = true;
- if (strcmp(p->filename, "/dev/stdin") == 0) { // mainly for win32
- fd = STDIN_FILENO;
- close_fd = false;
- }
- if (fd < 0)
- fd = open(p->filename, mode);
- if (fd < 0) {
- MP_ERR(p, "Can't open %s.\n", p->filename);
- goto done;
- }
-
- while (1) {
-#ifndef __MINGW32__
- struct pollfd fds[2] = {
- { .fd = fd, .events = POLLIN },
- { .fd = p->wakeup_pipe[0], .events = POLLIN },
- };
- poll(fds, 2, -1);
- if (!(fds[0].revents & POLLIN))
- break;
-#endif
- char buffer[128];
- int r = read(fd, buffer, sizeof(buffer));
- if (r <= 0)
- break;
-
- pthread_mutex_lock(&lock);
- if (!p->src) {
- pthread_mutex_unlock(&lock);
- break;
- }
- mp_input_src_feed_cmd_text(p->src, buffer, r);
- pthread_mutex_unlock(&lock);
- }
-
- if (close_fd)
- close(fd);
-
-done:
- pthread_mutex_lock(&lock);
- if (p->src)
- p->src->priv = NULL;
- pthread_mutex_unlock(&lock);
- close(p->wakeup_pipe[0]);
- close(p->wakeup_pipe[1]);
- talloc_free(p);
- return NULL;
-}
-
-static void close_pipe(struct mp_input_src *src)
-{
- pthread_mutex_lock(&lock);
- struct priv *p = src->priv;
- // Windows pipe have a severe problem: they can't be made non-blocking (not
- // after creation), and you can't wait on them. The only things that work
- // are cancellation (Vista+, broken in wine) or forceful thread termination.
- // So don't bother with "correct" termination, and just abandon the reader
- // thread.
- // On Unix, we interrupt it using the wakeup pipe.
- if (p) {
-#ifndef __MINGW32__
- write(p->wakeup_pipe[1], &(char){0}, 1);
-#endif
- p->src = NULL;
- }
- pthread_mutex_unlock(&lock);
-}
-
-void mp_input_add_pipe(struct input_ctx *ictx, const char *filename)
-{
- struct mp_input_src *src = mp_input_add_src(ictx);
- if (!src)
- return;
-
- struct priv *p = talloc_zero(NULL, struct priv);
- src->priv = p;
- p->filename = talloc_strdup(p, filename);
- p->src = src;
- p->log = mp_log_new(p, src->log, NULL);
- mp_make_wakeup_pipe(p->wakeup_pipe);
-
- pthread_t thread;
- if (pthread_create(&thread, NULL, reader_thread, p)) {
- close(p->wakeup_pipe[0]);
- close(p->wakeup_pipe[1]);
- talloc_free(p);
- mp_input_src_kill(src);
- } else {
- src->close = close_pipe;
- }
-}