summaryrefslogtreecommitdiffstats
path: root/input/input.c
diff options
context:
space:
mode:
Diffstat (limited to 'input/input.c')
-rw-r--r--input/input.c356
1 files changed, 206 insertions, 150 deletions
diff --git a/input/input.c b/input/input.c
index b8ff15a0c7..fe7acbb3e7 100644
--- a/input/input.c
+++ b/input/input.c
@@ -29,6 +29,7 @@
#include <sys/time.h>
#include <fcntl.h>
#include <ctype.h>
+#include <assert.h>
#include "input.h"
#include "mp_fifo.h"
@@ -550,8 +551,6 @@ static const struct cmd_bind def_cmd_binds[] = {
#define MP_MAX_CMD_FD 10
#endif
-#define CMD_QUEUE_SIZE 100
-
struct input_fd {
int fd;
union {
@@ -582,6 +581,13 @@ struct cmd_bind_section {
struct cmd_bind_section *next;
};
+struct cmd_queue {
+ struct mp_cmd *first;
+ struct mp_cmd *last;
+ int num_cmds;
+ int num_abort_cmds;
+};
+
struct input_ctx {
// Autorepeat stuff
short ar_state;
@@ -590,6 +596,9 @@ struct input_ctx {
// Autorepeat config
unsigned int ar_delay;
unsigned int ar_rate;
+ // Maximum number of queued commands from keypresses (limit to avoid
+ // repeated slow commands piling up)
+ int key_fifo_size;
// these are the keys currently down
int key_down[MP_MAX_KEY_DOWN];
@@ -605,14 +614,18 @@ struct input_ctx {
struct cmd_bind *cmd_binds;
struct cmd_bind *cmd_binds_default;
+ // Used to track whether we managed to read something while checking
+ // events sources. If yes, the sources may have more queued.
+ bool got_new_events;
+
struct input_fd key_fds[MP_MAX_KEY_FD];
unsigned int num_key_fd;
struct input_fd cmd_fds[MP_MAX_CMD_FD];
unsigned int num_cmd_fd;
- mp_cmd_t *cmd_queue[CMD_QUEUE_SIZE];
- unsigned int cmd_queue_length, cmd_queue_start, cmd_queue_end;
+ struct cmd_queue key_cmd_queue;
+ struct cmd_queue control_cmd_queue;
};
@@ -685,6 +698,46 @@ static char *get_key_combo_name(int *keys, int max)
return ret;
}
+static bool is_abort_cmd(int cmd_id)
+{
+ switch (cmd_id) {
+ case MP_CMD_QUIT:
+ case MP_CMD_PLAY_TREE_STEP:
+ case MP_CMD_PLAY_TREE_UP_STEP:
+ case MP_CMD_PLAY_ALT_SRC_STEP:
+ return true;
+ }
+ return false;
+}
+
+static void queue_pop(struct cmd_queue *queue)
+{
+ assert(queue->num_cmds > 0);
+ struct mp_cmd *cmd = queue->first;
+ queue->first = cmd->queue_next;
+ queue->num_cmds--;
+ queue->num_abort_cmds -= is_abort_cmd(cmd->id);
+}
+
+static void queue_add(struct cmd_queue *queue, struct mp_cmd *cmd,
+ bool at_head)
+{
+ if (!queue->num_cmds) {
+ queue->first = cmd;
+ queue->last = cmd;
+ } else if (at_head) {
+ queue->first->queue_prev = cmd;
+ cmd->queue_next = queue->first;
+ queue->first = cmd;
+ } else {
+ queue->last->queue_next = cmd;
+ cmd->queue_prev = queue->last;
+ queue->last = cmd;
+ }
+ queue->num_cmds++;
+ queue->num_abort_cmds += is_abort_cmd(cmd->id);
+}
+
int mp_input_add_cmd_fd(struct input_ctx *ictx, int fd, int select,
int read_func(int fd, char *dest, int size),
int close_func(int fd))
@@ -1261,193 +1314,199 @@ static mp_cmd_t *check_autorepeat(struct input_ctx *ictx)
return NULL;
}
+void mp_input_feed_key(struct input_ctx *ictx, int code)
+{
+ ictx->got_new_events = true;
+ if (code == MP_INPUT_RELEASE_ALL) {
+ memset(ictx->key_down, 0, sizeof(ictx->key_down));
+ ictx->num_key_down = 0;
+ ictx->last_key_down = 0;
+ return;
+ }
+ struct mp_cmd *cmd = interpret_key(ictx, code);
+ if (!cmd)
+ return;
+ struct cmd_queue *queue = &ictx->key_cmd_queue;
+ if (queue->num_cmds >= ictx->key_fifo_size &&
+ (!is_abort_cmd(cmd->id) || queue->num_abort_cmds))
+ return;
+ queue_add(queue, cmd, false);
+}
+
+static void read_cmd_fd(struct input_ctx *ictx, struct input_fd *cmd_fd)
+{
+ int r;
+ char *text;
+ while ((r = read_cmd(cmd_fd, &text)) >= 0) {
+ ictx->got_new_events = true;
+ struct mp_cmd *cmd = mp_input_parse_cmd(text);
+ talloc_free(text);
+ if (cmd)
+ queue_add(&ictx->control_cmd_queue, cmd, false);
+ if (!cmd_fd->got_cmd)
+ return;
+ }
+ if (r == MP_INPUT_ERROR)
+ mp_tmsg(MSGT_INPUT, MSGL_ERR, "Error on command file descriptor %d\n",
+ cmd_fd->fd);
+ else if (r == MP_INPUT_DEAD)
+ cmd_fd->dead = true;
+}
+
+static void read_key_fd(struct input_ctx *ictx, struct input_fd *key_fd)
+{
+ int code = key_fd->read_func.key(key_fd->ctx, key_fd->fd);
+ if (code >= 0 || code == MP_INPUT_RELEASE_ALL) {
+ mp_input_feed_key(ictx, code);
+ return;
+ }
+
+ if (code == MP_INPUT_ERROR)
+ mp_tmsg(MSGT_INPUT, MSGL_ERR,
+ "Error on key input file descriptor %d\n", key_fd->fd);
+ else if (code == MP_INPUT_DEAD) {
+ mp_tmsg(MSGT_INPUT, MSGL_ERR,
+ "Dead key input on file descriptor %d\n", key_fd->fd);
+ key_fd->dead = true;
+ }
+}
/**
* \param time time to wait at most for an event in milliseconds
*/
-static mp_cmd_t *read_events(struct input_ctx *ictx, int time)
+static void read_events(struct input_ctx *ictx, int time)
{
- int i;
- int got_cmd = 0;
+ ictx->got_new_events = false;
struct input_fd *key_fds = ictx->key_fds;
struct input_fd *cmd_fds = ictx->cmd_fds;
- for (i = 0; i < ictx->num_key_fd; i++)
+ for (int i = 0; i < ictx->num_key_fd; i++)
if (key_fds[i].dead) {
mp_input_rm_key_fd(ictx, key_fds[i].fd);
i--;
- }
- for (i = 0; i < ictx->num_cmd_fd; i++)
+ } else if (time && key_fds[i].no_select)
+ read_key_fd(ictx, &key_fds[i]);
+ for (int i = 0; i < ictx->num_cmd_fd; i++)
if (cmd_fds[i].dead || cmd_fds[i].eof) {
mp_input_rm_cmd_fd(ictx, cmd_fds[i].fd);
i--;
- } else if (cmd_fds[i].got_cmd)
- got_cmd = 1;
+ } else if (time && cmd_fds[i].no_select)
+ read_cmd_fd(ictx, &cmd_fds[i]);
+ if (ictx->got_new_events)
+ time = 0;
#ifdef HAVE_POSIX_SELECT
fd_set fds;
FD_ZERO(&fds);
- if (!got_cmd) {
- int max_fd = 0;
- for (i = 0; i < ictx->num_key_fd; i++) {
- if (key_fds[i].no_select)
- continue;
- if (key_fds[i].fd > max_fd)
- max_fd = key_fds[i].fd;
- FD_SET(key_fds[i].fd, &fds);
- }
- for (i = 0; i < ictx->num_cmd_fd; i++) {
- if (cmd_fds[i].no_select)
- continue;
- if (cmd_fds[i].fd > max_fd)
- max_fd = cmd_fds[i].fd;
- FD_SET(cmd_fds[i].fd, &fds);
- }
- struct timeval tv, *time_val;
- if (time >= 0) {
- tv.tv_sec = time / 1000;
- tv.tv_usec = (time % 1000) * 1000;
- time_val = &tv;
- } else
- time_val = NULL;
- if (select(max_fd + 1, &fds, NULL, NULL, time_val) < 0) {
- if (errno != EINTR)
- mp_tmsg(MSGT_INPUT, MSGL_ERR, "Select error: %s\n",
- strerror(errno));
- FD_ZERO(&fds);
- }
+ int max_fd = 0;
+ for (int i = 0; i < ictx->num_key_fd; i++) {
+ if (key_fds[i].no_select)
+ continue;
+ if (key_fds[i].fd > max_fd)
+ max_fd = key_fds[i].fd;
+ FD_SET(key_fds[i].fd, &fds);
+ }
+ for (int i = 0; i < ictx->num_cmd_fd; i++) {
+ if (cmd_fds[i].no_select)
+ continue;
+ if (cmd_fds[i].fd > max_fd)
+ max_fd = cmd_fds[i].fd;
+ FD_SET(cmd_fds[i].fd, &fds);
+ }
+ struct timeval tv, *time_val;
+ if (time >= 0) {
+ tv.tv_sec = time / 1000;
+ tv.tv_usec = (time % 1000) * 1000;
+ time_val = &tv;
+ } else
+ time_val = NULL;
+ if (select(max_fd + 1, &fds, NULL, NULL, time_val) < 0) {
+ if (errno != EINTR)
+ mp_tmsg(MSGT_INPUT, MSGL_ERR, "Select error: %s\n",
+ strerror(errno));
+ FD_ZERO(&fds);
}
#else
- if (!got_cmd && time)
+ if (time)
usec_sleep(time * 1000);
#endif
- for (i = 0; i < ictx->num_key_fd; i++) {
+ for (int i = 0; i < ictx->num_key_fd; i++) {
#ifdef HAVE_POSIX_SELECT
if (!key_fds[i].no_select && !FD_ISSET(key_fds[i].fd, &fds))
continue;
#endif
-
- int code;
- while (1) {
- code = key_fds[i].read_func.key(key_fds[i].ctx, key_fds[i].fd);
- if (code < 0) {
- if (code == MP_INPUT_RELEASE_ALL) {
- memset(ictx->key_down, 0, sizeof(ictx->key_down));
- ictx->num_key_down = 0;
- ictx->last_key_down = 0;
- continue;
- }
- break;
- }
- mp_cmd_t *ret = interpret_key(ictx, code);
- if (ret)
- return ret;
- }
- if (code == MP_INPUT_ERROR)
- mp_tmsg(MSGT_INPUT, MSGL_ERR, "Error on key input "
- "file descriptor %d\n", key_fds[i].fd);
- else if (code == MP_INPUT_DEAD) {
- mp_tmsg(MSGT_INPUT, MSGL_ERR, "Dead key input on "
- "file descriptor %d\n", key_fds[i].fd);
- key_fds[i].dead = 1;
- }
+ read_key_fd(ictx, &key_fds[i]);
}
- mp_cmd_t *autorepeat_cmd = check_autorepeat(ictx);
- if (autorepeat_cmd)
- return autorepeat_cmd;
- for (i = 0; i < ictx->num_cmd_fd; i++) {
+ for (int i = 0; i < ictx->num_cmd_fd; i++) {
#ifdef HAVE_POSIX_SELECT
- if (!cmd_fds[i].no_select && !FD_ISSET(cmd_fds[i].fd, &fds) &&
- !cmd_fds[i].got_cmd)
+ if (!cmd_fds[i].no_select && !FD_ISSET(cmd_fds[i].fd, &fds))
continue;
#endif
- char *cmd;
- int r;
- while ((r = read_cmd(&cmd_fds[i], &cmd)) >= 0) {
- mp_cmd_t *ret = mp_input_parse_cmd(cmd);
- talloc_free(cmd);
- if (ret)
- return ret;
- }
- if (r == MP_INPUT_ERROR)
- mp_tmsg(MSGT_INPUT, MSGL_ERR, "Error on command "
- "file descriptor %d\n", cmd_fds[i].fd);
- else if (r == MP_INPUT_DEAD)
- cmd_fds[i].dead = 1;
+ read_cmd_fd(ictx, &cmd_fds[i]);
}
-
- return NULL;
}
+/* To support blocking file descriptors we don't loop the read over
+ * every source until it's known to be empty. Instead we use this wrapper
+ * to run select() again.
+ */
+static void read_all_events(struct input_ctx *ictx, int time)
+{
+ while (1) {
+ read_events(ictx, time);
+ if (!ictx->got_new_events)
+ return;
+ time = 0;
+ }
+}
int mp_input_queue_cmd(struct input_ctx *ictx, mp_cmd_t *cmd)
{
- if (!cmd || ictx->cmd_queue_length >= CMD_QUEUE_SIZE)
+ ictx->got_new_events = true;
+ if (!cmd)
return 0;
- ictx->cmd_queue[ictx->cmd_queue_end] = cmd;
- ictx->cmd_queue_end = (ictx->cmd_queue_end + 1) % CMD_QUEUE_SIZE;
- ictx->cmd_queue_length++;
+ queue_add(&ictx->control_cmd_queue, cmd, true);
return 1;
}
-static mp_cmd_t *get_queued_cmd(struct input_ctx *ictx, int peek_only)
-{
- mp_cmd_t *ret;
-
- if (ictx->cmd_queue_length == 0)
- return NULL;
-
- ret = ictx->cmd_queue[ictx->cmd_queue_start];
-
- if (!peek_only) {
- ictx->cmd_queue_length--;
- ictx->cmd_queue_start = (ictx->cmd_queue_start + 1) % CMD_QUEUE_SIZE;
- }
-
- return ret;
-}
-
/**
* \param peek_only when set, the returned command stays in the queue.
* Do not free the returned cmd whe you set this!
*/
mp_cmd_t *mp_input_get_cmd(struct input_ctx *ictx, int time, int peek_only)
{
- mp_cmd_t *ret = NULL;
- struct cmd_filter *cf;
- int from_queue;
-
if (async_quit_request)
return mp_input_parse_cmd("quit 1");
- while (1) {
- from_queue = 1;
- ret = get_queued_cmd(ictx, peek_only);
- if (ret)
- break;
- from_queue = 0;
- ret = read_events(ictx, time);
- if (!ret) {
- from_queue = 1;
- ret = get_queued_cmd(ictx, peek_only);
- }
- break;
- }
- if (!ret)
- return NULL;
- for (cf = cmd_filters; cf; cf = cf->next) {
+ if (ictx->control_cmd_queue.num_cmds || ictx->key_cmd_queue.num_cmds)
+ time = 0;
+ read_all_events(ictx, time);
+ struct mp_cmd *ret;
+ struct cmd_queue *queue = &ictx->control_cmd_queue;
+ if (!queue->num_cmds)
+ queue = &ictx->key_cmd_queue;
+ if (!queue->num_cmds) {
+ queue = NULL;
+ ret = check_autorepeat(ictx);
+ if (!ret)
+ return NULL;
+ } else
+ ret = queue->first;
+
+ for (struct cmd_filter *cf = cmd_filters; cf; cf = cf->next) {
if (cf->filter(ret, cf->ctx)) {
- if (peek_only && from_queue)
- // The filter ate the cmd, so we remove it from queue
- ret = get_queued_cmd(ictx, 0);
+ // The filter ate the cmd, so remove it from the queue
+ if (queue)
+ queue_pop(queue);
mp_cmd_free(ret);
- return NULL;
+ // Retry with next command
+ return mp_input_get_cmd(ictx, 0, peek_only);
}
}
- if (!from_queue && peek_only)
- mp_input_queue_cmd(ictx, ret);
+ if (!peek_only && queue)
+ queue_pop(queue);
return ret;
}
@@ -1752,6 +1811,7 @@ struct input_ctx *mp_input_init(struct input_conf *input_conf)
{
struct input_ctx *ictx = talloc_ptrtype(NULL, ictx);
*ictx = (struct input_ctx){
+ .key_fifo_size = input_conf->key_fifo_size,
.ar_state = -1,
.ar_delay = input_conf->ar_delay,
.ar_rate = input_conf->ar_rate,
@@ -1917,19 +1977,15 @@ static int print_cmd_list(m_option_t *cfg)
*/
int mp_input_check_interrupt(struct input_ctx *ictx, int time)
{
- mp_cmd_t *cmd;
- if ((cmd = mp_input_get_cmd(ictx, time, 1)) == NULL)
- return 0;
- switch (cmd->id) {
- case MP_CMD_QUIT:
- case MP_CMD_PLAY_TREE_STEP:
- case MP_CMD_PLAY_TREE_UP_STEP:
- case MP_CMD_PLAY_ALT_SRC_STEP:
- // The cmd will be executed when we are back in the main loop
- return 1;
+ for (int i = 0; ; i++) {
+ if (async_quit_request || ictx->key_cmd_queue.num_abort_cmds ||
+ ictx->control_cmd_queue.num_abort_cmds) {
+ mp_tmsg(MSGT_INPUT, MSGL_WARN, "Received command to move to "
+ "another file. Aborting current processing.\n");
+ return true;
+ }
+ if (i)
+ return false;
+ read_all_events(ictx, time);
}
- // remove the cmd from the queue
- cmd = mp_input_get_cmd(ictx, time, 0);
- mp_cmd_free(cmd);
- return 0;
}