summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/command.c49
-rw-r--r--core/command.h2
-rw-r--r--core/input/input.c319
-rw-r--r--core/input/input.h60
-rw-r--r--core/input/keycodes.h31
-rw-r--r--core/mplayer.c12
-rw-r--r--etc/input.conf3
-rw-r--r--video/out/vo.c9
-rw-r--r--video/out/vo.h1
-rw-r--r--video/out/vo_x11.c9
-rw-r--r--video/out/vo_xv.c10
11 files changed, 354 insertions, 151 deletions
diff --git a/core/command.c b/core/command.c
index 828bf21a08..9e77436a95 100644
--- a/core/command.c
+++ b/core/command.c
@@ -84,37 +84,17 @@ static char *format_delay(double time)
return talloc_asprintf(NULL, "%d ms", ROUND(time * 1000));
}
-static void rescale_input_coordinates(struct MPContext *mpctx, int ix, int iy,
- double *dx, double *dy)
+// Get current mouse position in OSD coordinate space.
+void mp_get_osd_mouse_pos(struct MPContext *mpctx, float *x, float *y)
{
- struct MPOpts *opts = &mpctx->opts;
- struct vo *vo = mpctx->video_out;
- //remove the borders, if any, and rescale to the range [0,1],[0,1]
- if (opts->vo.fs) { //we are in full-screen mode
- if (opts->vo.screenwidth > vo->dwidth)
- // there are borders along the x axis
- ix -= (opts->vo.screenwidth - vo->dwidth) / 2;
- if (opts->vo.screenheight > vo->dheight)
- // there are borders along the y axis (usual way)
- iy -= (opts->vo.screenheight - vo->dheight) / 2;
-
- if (ix < 0 || ix > vo->dwidth) {
- *dx = *dy = -1.0;
- return;
- } //we are on one of the borders
- if (iy < 0 || iy > vo->dheight) {
- *dx = *dy = -1.0;
- return;
- } //we are on one of the borders
- }
-
- *dx = (double) ix / (double) vo->dwidth;
- *dy = (double) iy / (double) vo->dheight;
-
- mp_msg(MSGT_CPLAYER, MSGL_V,
- "\r\nrescaled coordinates: %.3f, %.3f, screen (%d x %d), vodisplay: (%d, %d), fullscreen: %d\r\n",
- *dx, *dy, opts->vo.screenwidth, opts->vo.screenheight, vo->dwidth,
- vo->dheight, opts->vo.fs);
+ int wx, wy;
+ mp_input_get_mouse_pos(mpctx->input, &wx, &wy);
+ float p[2] = {wx, wy};
+ // Raw window coordinates (VO mouse events) to OSD resolution.
+ if (mpctx->video_out)
+ vo_control(mpctx->video_out, VOCTRL_WINDOW_TO_OSD_COORDS, p);
+ *x = p[0];
+ *y = p[1];
}
// Property-option bridge.
@@ -2432,15 +2412,6 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
mplayer_put_key(mpctx->key_fifo, cmd->args[0].v.i);
break;
- case MP_CMD_SET_MOUSE_POS: {
- int pointer_x, pointer_y;
- double dx, dy;
- pointer_x = cmd->args[0].v.i;
- pointer_y = cmd->args[1].v.i;
- rescale_input_coordinates(mpctx, pointer_x, pointer_y, &dx, &dy);
- break;
- }
-
case MP_CMD_VO_CMDLINE:
if (mpctx->video_out) {
char *s = cmd->args[0].v.s;
diff --git a/core/command.h b/core/command.h
index 2c2de03585..dbe1f638e2 100644
--- a/core/command.h
+++ b/core/command.h
@@ -22,6 +22,8 @@
struct MPContext;
struct mp_cmd;
+void mp_get_osd_mouse_pos(struct MPContext *mpctx, float *x, float *y);
+
void run_command(struct MPContext *mpctx, struct mp_cmd *cmd);
char *mp_property_expand_string(struct MPContext *mpctx, char *str);
void property_print_help(void);
diff --git a/core/input/input.c b/core/input/input.c
index dfa7d1e5b4..b883974409 100644
--- a/core/input/input.c
+++ b/core/input/input.c
@@ -194,8 +194,6 @@ static const mp_cmd_t mp_cmds[] = {
.v.f = 1 },
}},
- { MP_CMD_SET_MOUSE_POS, "set_mouse_pos", { ARG_INT, ARG_INT } },
-
{ MP_CMD_AF_SWITCH, "af_switch", { ARG_STRING } },
{ MP_CMD_AF_ADD, "af_add", { ARG_STRING } },
{ MP_CMD_AF_DEL, "af_del", { ARG_STRING } },
@@ -433,7 +431,8 @@ static const struct key_name key_names[] = {
{ MP_KEY_PREV, "XF86_PREV" },
{ MP_KEY_NEXT, "XF86_NEXT" },
- { MP_KEY_CLOSE_WIN, "CLOSE_WIN" },
+ { MP_KEY_CLOSE_WIN, "CLOSE_WIN" },
+ { MP_KEY_MOUSE_MOVE, "MOUSE_MOVE" },
{ 0, NULL }
};
@@ -476,9 +475,18 @@ struct cmd_bind_section {
struct cmd_bind *cmd_binds;
bool is_builtin;
char *section;
+ struct mp_rect mouse_area; // set at runtime, if at all
+ bool mouse_area_set; // mouse_area is valid and should be tested
struct cmd_bind_section *next;
};
+#define MAX_ACTIVE_SECTIONS 5
+
+struct active_section {
+ char *name;
+ int flags;
+};
+
struct cmd_queue {
struct mp_cmd *first;
};
@@ -486,7 +494,6 @@ struct cmd_queue {
struct input_ctx {
// Autorepeat stuff
short ar_state;
- mp_cmd_t *ar_cmd;
int64_t last_ar;
// Autorepeat config
unsigned int ar_delay;
@@ -499,16 +506,24 @@ struct input_ctx {
int key_down[MP_MAX_KEY_DOWN];
unsigned int num_key_down;
int64_t last_key_down;
+ struct mp_cmd *current_down_cmd;
+
+ // Mouse position on the consumer side (as command.c sees it)
+ int mouse_x, mouse_y;
+
+ // Mouse position on the producer side (as the VO sees it)
+ // Unlike mouse_x/y, this can be used to resolve mouse click bindings.
+ int mouse_vo_x, mouse_vo_y;
bool test;
bool default_bindings;
// List of command binding sections
struct cmd_bind_section *cmd_bind_sections;
- // Name of currently used command section
- char *section;
- // Bitfield of mp_input_section_flags
- int section_flags;
+
+ // List currently active command sections
+ struct active_section active_sections[MAX_ACTIVE_SECTIONS];
+ int num_active_sections;
// Used to track whether we managed to read something while checking
// events sources. If yes, the sources may have more queued.
@@ -568,6 +583,11 @@ static const char builtin_input_conf[] =
#include "core/input/input_conf.h"
;
+static bool test_rect(struct mp_rect *rc, int x, int y)
+{
+ return x >= rc->x0 && y >= rc->y0 && x < rc->x1 && y < rc->y1;
+}
+
static char *get_key_name(int key, char *ret)
{
for (int i = 0; modifier_names[i].name; i++) {
@@ -1081,11 +1101,10 @@ static void append_bind_info(char **pmsg, struct cmd_bind *bind)
if (strcmp(bind->owner->section, "default") != 0)
msg = talloc_asprintf_append(msg, " in section {%s}",
bind->owner->section);
- if (bind->owner->is_builtin) {
- msg = talloc_asprintf_append(msg, " (default binding)");
- } else {
- msg = talloc_asprintf_append(msg, " in %s", bind->location);
- }
+ msg = talloc_asprintf_append(msg, " in %s", bind->location);
+ if (bind->owner->is_builtin)
+ msg = talloc_asprintf_append(msg, " (default)");
+ talloc_free(cmd);
*pmsg = msg;
}
@@ -1100,7 +1119,7 @@ static mp_cmd_t *handle_test(struct input_ctx *ictx, int n, int *keys)
for (struct cmd_bind_section *bs = ictx->cmd_bind_sections;
bs; bs = bs->next)
{
- for (struct cmd_bind *bind = bs->cmd_binds; bind->cmd; bind++) {
+ for (struct cmd_bind *bind = bs->cmd_binds; bind && bind->cmd; bind++) {
if (bind_matches_key(bind, n, keys)) {
count++;
msg = talloc_asprintf_append(msg, "%d. ", count);
@@ -1113,6 +1132,8 @@ static mp_cmd_t *handle_test(struct input_ctx *ictx, int n, int *keys)
if (!count)
msg = talloc_asprintf_append(msg, "(nothing)");
+ mp_msg(MSGT_INPUT, MSGL_V, "[input] %s\n", msg);
+
mp_cmd_t *res = mp_input_parse_cmd(bstr0("show_text \"\""), "");
res->args[0].v.s = talloc_steal(res, msg);
return res;
@@ -1135,7 +1156,7 @@ static struct cmd_bind *find_bind_for_key(struct cmd_bind *binds, int n,
{
int j;
- if (n <= 0)
+ if (n <= 0 || !binds)
return NULL;
for (j = 0; binds[j].cmd != NULL; j++) {
if (bind_matches_key(&binds[j], n, keys))
@@ -1166,36 +1187,39 @@ static struct cmd_bind_section *get_bind_section(struct input_ctx *ictx,
ictx->cmd_bind_sections = talloc_ptrtype(ictx, ictx->cmd_bind_sections);
bind_section = ictx->cmd_bind_sections;
}
- bind_section->cmd_binds = NULL;
- bind_section->section = bstrdup0(bind_section, section);
- bind_section->is_builtin = builtin;
- bind_section->next = NULL;
+ *bind_section = (struct cmd_bind_section) {
+ .section = bstrdup0(bind_section, section),
+ .is_builtin = builtin,
+ };
return bind_section;
}
-static struct cmd_bind *section_find_bind_for_key(struct input_ctx *ictx,
- bool builtin, char *section,
- int n, int *keys)
-{
- struct cmd_bind_section *bs = get_bind_section(ictx, builtin,
- bstr0(section));
- return bs->cmd_binds ? find_bind_for_key(bs->cmd_binds, n, keys) : NULL;
-}
-
static struct cmd_bind *find_any_bind_for_key(struct input_ctx *ictx,
int n, int *keys)
{
- struct cmd_bind *cmd
- = section_find_bind_for_key(ictx, false, ictx->section, n, keys);
- if (ictx->default_bindings && cmd == NULL)
- cmd = section_find_bind_for_key(ictx, true, ictx->section, n, keys);
- if (!(ictx->section_flags & MP_INPUT_NO_DEFAULT_SECTION)) {
- if (cmd == NULL)
- cmd = section_find_bind_for_key(ictx, false, "default", n, keys);
- if (ictx->default_bindings && cmd == NULL)
- cmd = section_find_bind_for_key(ictx, true, "default", n, keys);
+ for (int i = ictx->num_active_sections - 1; i >= 0; i--) {
+ struct active_section *as = &ictx->active_sections[i];
+ bstr name = bstr0(as->name);
+ for (int b = 0; b < 2; b++) {
+ bool builtin = !!b;
+ struct cmd_bind_section *bs = get_bind_section(ictx, builtin, name);
+ for (int i = 0; i < n; i++) {
+ if (MP_KEY_DEPENDS_ON_MOUSE_POS(keys[i]) &&
+ bs->mouse_area_set &&
+ !test_rect(&bs->mouse_area,
+ ictx->mouse_vo_x,
+ ictx->mouse_vo_y))
+ goto skip;
+ }
+ struct cmd_bind *cmd = find_bind_for_key(bs->cmd_binds, n, keys);
+ if (cmd)
+ return cmd;
+ skip: ;
+ }
+ if (as->flags & MP_INPUT_EXCLUSIVE)
+ break;
}
- return cmd;
+ return NULL;
}
static mp_cmd_t *get_cmd_from_keys(struct input_ctx *ictx, int n, int *keys)
@@ -1227,12 +1251,27 @@ static mp_cmd_t *get_cmd_from_keys(struct input_ctx *ictx, int n, int *keys)
return ret;
}
+static void release_down_cmd(struct input_ctx *ictx)
+{
+ if (ictx->current_down_cmd && ictx->current_down_cmd->key_up_follows) {
+ ictx->current_down_cmd->key_up_follows = false;
+ queue_add(&ictx->key_cmd_queue, ictx->current_down_cmd, false);
+ } else {
+ talloc_free(ictx->current_down_cmd);
+ }
+ ictx->current_down_cmd = NULL;
+ ictx->last_key_down = 0;
+ ictx->ar_state = -1;
+}
static mp_cmd_t *interpret_key(struct input_ctx *ictx, int code)
{
unsigned int j;
mp_cmd_t *ret;
+ if (code == MP_KEY_MOUSE_MOVE)
+ return get_cmd_from_keys(ictx, 1, (int[]){code});
+
/* On normal keyboards shift changes the character code of non-special
* keys, so don't count the modifier separately for those. In other words
* we want to have "a" and "A" instead of "a" and "Shift+A"; but a separate
@@ -1256,21 +1295,23 @@ static mp_cmd_t *interpret_key(struct input_ctx *ictx, int code)
}
if (j != ictx->num_key_down)
return NULL;
+ release_down_cmd(ictx);
ictx->key_down[ictx->num_key_down] = code;
ictx->num_key_down++;
ictx->last_key_down = mp_time_us();
ictx->ar_state = 0;
- ret = NULL;
- if (!(code & MP_NO_REPEAT_KEY))
- ret = get_cmd_from_keys(ictx, ictx->num_key_down, ictx->key_down);
- return ret;
+ ictx->current_down_cmd = get_cmd_from_keys(ictx, ictx->num_key_down,
+ ictx->key_down);
+ if (ictx->current_down_cmd && (code & MP_KEY_EMIT_ON_UP))
+ ictx->current_down_cmd->key_up_follows = true;
+ return mp_cmd_clone(ictx->current_down_cmd);
}
// button released or press of key with no separate down/up events
for (j = 0; j < ictx->num_key_down; j++) {
if (ictx->key_down[j] == code)
break;
}
- bool doubleclick = code >= MP_MOUSE_BTN0_DBL && code < MP_MOUSE_BTN_DBL_END;
+ bool doubleclick = MP_KEY_IS_MOUSE_BTN_DBL(code);
if (doubleclick) {
int btn = code - MP_MOUSE_BTN0_DBL + MP_MOUSE_BTN0;
if (!ictx->num_key_down
@@ -1279,7 +1320,7 @@ static mp_cmd_t *interpret_key(struct input_ctx *ictx, int code)
j = ictx->num_key_down - 1;
ictx->key_down[j] = code;
}
- bool emit_key = ictx->last_key_down && (code & MP_NO_REPEAT_KEY);
+ bool emit_key = ictx->last_key_down;
if (j == ictx->num_key_down) { // was not already down; add temporarily
if (ictx->num_key_down > MP_MAX_KEY_DOWN) {
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Too many key down events "
@@ -1290,6 +1331,9 @@ static mp_cmd_t *interpret_key(struct input_ctx *ictx, int code)
ictx->num_key_down++;
emit_key = true;
}
+ // This is a key up event, but the key up command is added by
+ // release_down_cmd(), not by this code.
+ emit_key &= !(code & MP_KEY_EMIT_ON_UP);
// Interpret only maximal point of multibutton event
ret = NULL;
if (emit_key)
@@ -1303,10 +1347,7 @@ static mp_cmd_t *interpret_key(struct input_ctx *ictx, int code)
memmove(&ictx->key_down[j], &ictx->key_down[j + 1],
(ictx->num_key_down - (j + 1)) * sizeof(int));
ictx->num_key_down--;
- ictx->last_key_down = 0;
- ictx->ar_state = -1;
- mp_cmd_free(ictx->ar_cmd);
- ictx->ar_cmd = NULL;
+ release_down_cmd(ictx);
return ret;
}
@@ -1322,51 +1363,83 @@ static mp_cmd_t *check_autorepeat(struct input_ctx *ictx)
if (ictx->ar_state == 0
&& (t - ictx->last_key_down) >= ictx->ar_delay * 1000)
{
- talloc_free(ictx->ar_cmd);
- ictx->ar_cmd = get_cmd_from_keys(ictx, ictx->num_key_down,
- ictx->key_down);
- if (!ictx->ar_cmd) {
+ if (!ictx->current_down_cmd) {
ictx->ar_state = -1;
return NULL;
}
ictx->ar_state = 1;
ictx->last_ar = ictx->last_key_down + ictx->ar_delay * 1000;
- return mp_cmd_clone(ictx->ar_cmd);
+ return mp_cmd_clone(ictx->current_down_cmd);
// Then send rate / sec event
} else if (ictx->ar_state == 1
&& (t - ictx->last_ar) >= 1000000 / ictx->ar_rate) {
ictx->last_ar += 1000000 / ictx->ar_rate;
- return mp_cmd_clone(ictx->ar_cmd);
+ return mp_cmd_clone(ictx->current_down_cmd);
}
}
return NULL;
}
+static void add_key_cmd(struct input_ctx *ictx, struct mp_cmd *cmd)
+{
+ struct cmd_queue *queue = &ictx->key_cmd_queue;
+ if (queue_count_cmds(queue) >= ictx->key_fifo_size &&
+ (!mp_input_is_abort_cmd(cmd->id) || queue_has_abort_cmds(queue)))
+ {
+ talloc_free(cmd);
+ return;
+ }
+ queue_add(queue, cmd, false);
+}
+
+// Whether a command can deal with redundant key up events.
+static bool key_updown_ok(enum mp_command_type cmd)
+{
+ switch (cmd) {
+ default:
+ return false;
+ }
+}
+
void mp_input_feed_key(struct input_ctx *ictx, int code)
{
ictx->got_new_events = true;
int unmod = code & ~(MP_KEY_MODIFIER_MASK | MP_KEY_STATE_DOWN);
- if (unmod >= MP_MOUSE_BASE && unmod <= MP_MOUSE_BTN_END)
+ if (MP_KEY_DEPENDS_ON_MOUSE_POS(unmod))
ictx->mouse_event_counter++;
if (code == MP_INPUT_RELEASE_ALL) {
mp_msg(MSGT_INPUT, MSGL_V, "input: release all\n");
memset(ictx->key_down, 0, sizeof(ictx->key_down));
ictx->num_key_down = 0;
- ictx->last_key_down = 0;
+ release_down_cmd(ictx);
return;
}
mp_msg(MSGT_INPUT, MSGL_V, "input: key code=%#x\n", code);
struct mp_cmd *cmd = interpret_key(ictx, code);
if (!cmd)
return;
- struct cmd_queue *queue = &ictx->key_cmd_queue;
- if (queue_count_cmds(queue) >= ictx->key_fifo_size &&
- (!mp_input_is_abort_cmd(cmd->id) || queue_has_abort_cmds(queue)))
- {
+ // Prevent redundant key-down events from being added to the queue. In some
+ // cases (like MP_CMD_SEEK commands), duplicated events might severely
+ // confuse the frontend.
+ if (cmd->key_up_follows && !key_updown_ok(cmd->id)) {
talloc_free(cmd);
return;
}
- queue_add(queue, cmd, false);
+ add_key_cmd(ictx, cmd);
+}
+
+void mp_input_set_mouse_pos(struct input_ctx *ictx, int x, int y)
+{
+ ictx->mouse_event_counter++;
+ ictx->mouse_vo_x = x;
+ ictx->mouse_vo_y = y;
+ struct mp_cmd *cmd = interpret_key(ictx, MP_KEY_MOUSE_MOVE);
+ if (!cmd)
+ return;
+ cmd->mouse_move = true;
+ cmd->mouse_x = x;
+ cmd->mouse_y = y;
+ add_key_cmd(ictx, cmd);
}
static void read_cmd_fd(struct input_ctx *ictx, struct input_fd *cmd_fd)
@@ -1513,8 +1586,6 @@ int mp_input_queue_cmd(struct input_ctx *ictx, mp_cmd_t *cmd)
ictx->got_new_events = true;
if (!cmd)
return 0;
- if (cmd->id == MP_CMD_SET_MOUSE_POS)
- ictx->mouse_event_counter++;
queue_add(&ictx->control_cmd_queue, cmd, false);
return 1;
}
@@ -1545,12 +1616,23 @@ mp_cmd_t *mp_input_get_cmd(struct input_ctx *ictx, int time, int peek_only)
if (!ret)
return NULL;
- if (!peek_only)
+ if (!peek_only) {
queue_remove(queue, ret);
+ if (ret->mouse_move) {
+ ictx->mouse_x = ret->mouse_x;
+ ictx->mouse_y = ret->mouse_y;
+ }
+ }
return ret;
}
+void mp_input_get_mouse_pos(struct input_ctx *ictx, int *x, int *y)
+{
+ *x = ictx->mouse_x;
+ *y = ictx->mouse_y;
+}
+
void mp_cmd_free(mp_cmd_t *cmd)
{
talloc_free(cmd);
@@ -1561,6 +1643,9 @@ mp_cmd_t *mp_cmd_clone(mp_cmd_t *cmd)
mp_cmd_t *ret;
int i;
+ if (!cmd)
+ return NULL;
+
ret = talloc_memdup(NULL, cmd, sizeof(mp_cmd_t));
ret->name = talloc_strdup(ret, cmd->name);
for (i = 0; i < MP_CMD_MAX_ARGS; i++) {
@@ -1668,8 +1753,10 @@ static void bind_keys(struct input_ctx *ictx, bool builtin, bstr section,
memcpy(bind->input, keys, (MP_MAX_KEY_DOWN + 1) * sizeof(int));
}
+// restrict_section: every entry is forced to this section name
+// if NULL, load normally and allow any sections
static int parse_config(struct input_ctx *ictx, bool builtin, bstr data,
- const char *location)
+ const char *location, const char *restrict_section)
{
int n_binds = 0, keys[MP_MAX_KEY_DOWN + 1];
int line_no = 0;
@@ -1704,12 +1791,14 @@ static int parse_config(struct input_ctx *ictx, bool builtin, bstr data,
}
talloc_free(name);
- bstr section = {0};
- if (bstr_startswith0(command, "{")) {
- int p = bstrchr(command, '}');
- if (p != -1) {
- section = bstr_strip(bstr_splice(command, 1, p));
- command = bstr_lstrip(bstr_cut(command, p + 1));
+ bstr section = bstr0(restrict_section);
+ if (!section.len) {
+ if (bstr_startswith0(command, "{")) {
+ int p = bstrchr(command, '}');
+ if (p != -1) {
+ section = bstr_strip(bstr_splice(command, 1, p));
+ command = bstr_lstrip(bstr_cut(command, p + 1));
+ }
}
}
@@ -1740,23 +1829,91 @@ static int parse_config_file(struct input_ctx *ictx, char *file, bool warn)
bstr res = stream_read_complete(s, NULL, 1000000);
free_stream(s);
mp_msg(MSGT_INPUT, MSGL_V, "Parsing input config file %s\n", file);
- int n_binds = parse_config(ictx, false, res, file);
+ int n_binds = parse_config(ictx, false, res, file, NULL);
talloc_free(res.start);
mp_msg(MSGT_INPUT, MSGL_V, "Input config file %s parsed: %d binds\n",
file, n_binds);
return 1;
}
-void mp_input_set_section(struct input_ctx *ictx, char *name, int flags)
+void mp_input_disable_section(struct input_ctx *ictx, char *name)
+{
+ struct cmd_bind_section *s = get_bind_section(ictx, false, bstr0(name));
+ name = s->section; // get allocated name, reduce NULL to "default"
+
+ // Remove old section, or make sure it's on top if re-enabled
+ for (int i = ictx->num_active_sections - 1; i >= 0; i--) {
+ struct active_section *as = &ictx->active_sections[i];
+ if (strcmp(as->name, name) == 0) {
+ for (int x = i; i < ictx->num_active_sections - 1; i++)
+ ictx->active_sections[x] = ictx->active_sections[x + 1];
+ ictx->num_active_sections--;
+ }
+ }
+}
+
+void mp_input_enable_section(struct input_ctx *ictx, char *name, int flags)
+{
+ struct cmd_bind_section *s = get_bind_section(ictx, false, bstr0(name));
+ name = s->section; // get allocated name, reduce NULL to "default"
+
+ mp_input_disable_section(ictx, name);
+
+ if (ictx->num_active_sections < MAX_ACTIVE_SECTIONS) {
+ ictx->active_sections[ictx->num_active_sections++] =
+ (struct active_section) {name, flags};
+ }
+}
+
+void mp_input_disable_all_sections(struct input_ctx *ictx)
+{
+ ictx->num_active_sections = 0;
+}
+
+void mp_input_set_section_mouse_area(struct input_ctx *ictx, char *name,
+ int x0, int y0, int x1, int y1)
{
- talloc_free(ictx->section);
- ictx->section = talloc_strdup(ictx, name ? name : "default");
- ictx->section_flags = flags;
+ for (int b = 0; b < 2; b++) {
+ struct cmd_bind_section *s = get_bind_section(ictx, !!b, bstr0(name));
+ s->mouse_area = (struct mp_rect){x0, y0, x1, y1};
+ s->mouse_area_set = x0 != x1 && y0 != y1;
+ }
+}
+
+bool mp_input_test_mouse_active(struct input_ctx *ictx, int x, int y)
+{
+ for (int i = 0; i < ictx->num_active_sections; i++) {
+ char *name = ictx->active_sections[i].name;
+ for (int b = 0; b < 2; b++) {
+ struct cmd_bind_section *s = get_bind_section(ictx, !!b, bstr0(name));
+ if (s->mouse_area_set && test_rect(&s->mouse_area, x, y))
+ return true;
+ }
+ }
+ return false;
+}
+
+bool mp_input_test_dragging(struct input_ctx *ictx, int x, int y)
+{
+ return mp_input_test_mouse_active(ictx, x, y);
}
-char *mp_input_get_section(struct input_ctx *ictx)
+void mp_input_define_section(struct input_ctx *ictx, char *name, char *location,
+ char *contents, bool builtin)
{
- return ictx->section;
+ if (!name || !name[0])
+ return; // parse_config() changes semantics with restrict_section==empty
+ if (contents) {
+ parse_config(ictx, builtin, bstr0(contents), location, name);
+ } else {
+ // Disable:
+ mp_input_disable_section(ictx, name);
+ // Delete:
+ struct cmd_bind_section *s = get_bind_section(ictx, builtin, bstr0(name));
+ talloc_free(s->cmd_binds);
+ s->cmd_binds = NULL;
+ // Could remove the section itself too, but that's not really necessary.
+ }
}
struct input_ctx *mp_input_init(struct input_conf *input_conf,
@@ -1772,9 +1929,9 @@ struct input_ctx *mp_input_init(struct input_conf *input_conf,
.test = input_conf->test,
.wakeup_pipe = {-1, -1},
};
- ictx->section = talloc_strdup(ictx, "default");
+ mp_input_enable_section(ictx, NULL, 0);
- parse_config(ictx, true, bstr0(builtin_input_conf), "<default>");
+ parse_config(ictx, true, bstr0(builtin_input_conf), "<builtin>", NULL);
#ifndef __MINGW32__
long ret = pipe(ictx->wakeup_pipe);
@@ -1904,7 +2061,7 @@ void mp_input_uninit(struct input_ctx *ictx, struct input_conf *input_conf)
}
clear_queue(&ictx->key_cmd_queue);
clear_queue(&ictx->control_cmd_queue);
- talloc_free(ictx->ar_cmd);
+ talloc_free(ictx->current_down_cmd);
talloc_free(ictx);
}
diff --git a/core/input/input.h b/core/input/input.h
index b9bc295646..26b529bfb9 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -60,7 +60,6 @@ enum mp_command_type {
MP_CMD_RADIO_STEP_CHANNEL,
MP_CMD_RADIO_SET_CHANNEL,
MP_CMD_RADIO_SET_FREQ,
- MP_CMD_SET_MOUSE_POS,
MP_CMD_ADD,
MP_CMD_CYCLE,
MP_CMD_RADIO_STEP_FREQ,
@@ -108,10 +107,10 @@ enum mp_on_osd {
};
enum mp_input_section_flags {
- // If a key binding is not defined in the current section, search the
- // default section for it ("default" refers to bindings with no section
- // specified, not to the default input.conf aka builtin key bindings)
- MP_INPUT_NO_DEFAULT_SECTION = 1,
+ // If a key binding is not defined in the current section, do not search the
+ // other sections for it (like the default section). Instead, an unbound
+ // key warning will be printed.
+ MP_INPUT_EXCLUSIVE = 1,
};
struct input_ctx;
@@ -136,6 +135,9 @@ typedef struct mp_cmd {
bool raw_args;
enum mp_on_osd on_osd;
bstr original;
+ bool key_up_follows;
+ bool mouse_move;
+ int mouse_x, mouse_y;
struct mp_cmd *queue_next;
} mp_cmd_t;
@@ -170,6 +172,11 @@ int mp_input_add_key_fd(struct input_ctx *ictx, int fd, int select,
// Feed a keypress (alternative to being returned from read_func above)
void mp_input_feed_key(struct input_ctx *ictx, int code);
+// Update mouse position (in window coordinates).
+void mp_input_set_mouse_pos(struct input_ctx *ictx, int x, int y);
+
+void mp_input_get_mouse_pos(struct input_ctx *ictx, int *x, int *y);
+
// As for the cmd one you usually don't need this function.
void mp_input_rm_key_fd(struct input_ctx *ictx, int fd);
@@ -197,16 +204,49 @@ void mp_cmd_free(struct mp_cmd *cmd);
// This creates a copy of a command (used by the auto repeat stuff).
struct mp_cmd *mp_cmd_clone(struct mp_cmd *cmd);
-// Set current input section
+// Set current input section. The section is appended on top of the list of
+// active sections, so its bindings are considered first. If the section was
+// already active, it's moved to the top as well.
+// name==NULL will behave as if name=="default"
// flags is a bitfield of enum mp_input_section_flags values
-void mp_input_set_section(struct input_ctx *ictx, char *name, int flags);
-
-// Get current input section
-char *mp_input_get_section(struct input_ctx *ictx);
+void mp_input_enable_section(struct input_ctx *ictx, char *name, int flags);
+
+// Undo mp_input_enable_section().
+// name==NULL will behave as if name=="default"
+void mp_input_disable_section(struct input_ctx *ictx, char *name);
+
+// Like mp_input_set_section(ictx, ..., 0) for all sections.
+void mp_input_disable_all_sections(struct input_ctx *ictx);
+
+// Set the contents of an input section.
+// name: name of the section, for mp_input_set_section() etc.
+// location: location string (like filename) for error reporting
+// contents: list of keybindings, like input.conf
+// a value of NULL deletes the section
+// builtin: create as builtin section; this means if the user defines bindings
+// using "{name}", they won't be ignored or overwritten - instead,
+// they are preferred to the bindings defined with this call
+// If the section already exists, its bindings are removed and replaced.
+void mp_input_define_section(struct input_ctx *ictx, char *name, char *location,
+ char *contents, bool builtin);
+
+// Define where on the screen the named input section should receive.
+// Setting a rectangle of size 0 unsets the mouse area.
+// A rectangle with negative size disables mouse input for this section.
+void mp_input_set_section_mouse_area(struct input_ctx *ictx, char *name,
+ int x0, int y0, int x1, int y1);
// Used to detect mouse movement.
unsigned int mp_input_get_mouse_event_counter(struct input_ctx *ictx);
+// Test whether there is any input section which wants to receive events.
+// Note that the mouse event is always delivered, even if this returns false.
+bool mp_input_test_mouse_active(struct input_ctx *ictx, int x, int y);
+
+// Whether input.c wants mouse drag events at this mouse position. If this
+// returns false, some VOs will initiate window dragging.
+bool mp_input_test_dragging(struct input_ctx *ictx, int x, int y);
+
// Initialize the input system
struct input_conf;
struct input_ctx *mp_input_init(struct input_conf *input_conf,
diff --git a/core/input/keycodes.h b/core/input/keycodes.h
index 92608aa6fa..b086d82809 100644
--- a/core/input/keycodes.h
+++ b/core/input/keycodes.h
@@ -126,7 +126,7 @@
// Mouse events from VOs
-#define MP_MOUSE_BASE ((MP_KEY_BASE+0xA0)|MP_NO_REPEAT_KEY)
+#define MP_MOUSE_BASE ((MP_KEY_BASE+0xA0)|MP_NO_REPEAT_KEY|MP_KEY_EMIT_ON_UP)
#define MP_MOUSE_BTN0 (MP_MOUSE_BASE+0)
#define MP_MOUSE_BTN1 (MP_MOUSE_BASE+1)
#define MP_MOUSE_BTN2 (MP_MOUSE_BASE+2)
@@ -149,6 +149,9 @@
#define MP_MOUSE_BTN19 (MP_MOUSE_BASE+19)
#define MP_MOUSE_BTN_END (MP_MOUSE_BASE+20)
+#define MP_KEY_IS_MOUSE_BTN_SINGLE(code) \
+ ((code) >= MP_MOUSE_BASE && (code) < MP_MOUSE_BTN_END)
+
#define MP_MOUSE_BASE_DBL ((MP_KEY_BASE+0xC0)|MP_NO_REPEAT_KEY)
#define MP_MOUSE_BTN0_DBL (MP_MOUSE_BASE_DBL+0)
#define MP_MOUSE_BTN1_DBL (MP_MOUSE_BASE_DBL+1)
@@ -172,6 +175,9 @@
#define MP_MOUSE_BTN19_DBL (MP_MOUSE_BASE_DBL+19)
#define MP_MOUSE_BTN_DBL_END (MP_MOUSE_BASE_DBL+20)
+#define MP_KEY_IS_MOUSE_BTN_DBL(code) \
+ ((code) >= MP_MOUSE_BTN0_DBL && (code) < MP_MOUSE_BTN_DBL_END)
+
// Apple Remote input module
#define MP_AR_BASE (MP_KEY_BASE+0xE0)
#define MP_AR_PLAY (MP_AR_BASE + 0)
@@ -198,6 +204,13 @@
/* Special keys */
#define MP_KEY_INTERN (MP_KEY_BASE+0x1000)
#define MP_KEY_CLOSE_WIN (MP_KEY_INTERN+0)
+// Generated by input.c (VOs use mp_input_set_mouse_pos())
+#define MP_KEY_MOUSE_MOVE ((MP_KEY_INTERN+1)|MP_NO_REPEAT_KEY)
+
+
+#define MP_KEY_DEPENDS_ON_MOUSE_POS(code) \
+ (MP_KEY_IS_MOUSE_BTN_SINGLE(code) || MP_KEY_IS_MOUSE_BTN_DBL(code) || \
+ (code) == MP_KEY_MOUSE_MOVE)
/* Modifiers added to individual keys */
#define MP_KEY_MODIFIER_SHIFT (1<<22)
@@ -208,13 +221,19 @@
#define MP_KEY_MODIFIER_MASK (MP_KEY_MODIFIER_SHIFT | MP_KEY_MODIFIER_CTRL | \
MP_KEY_MODIFIER_ALT | MP_KEY_MODIFIER_META)
-// Use this when the key shouldn't be auto-repeated (like mouse buttons)
-// This is not a modifier, but is part of the keycode itself.
-#define MP_NO_REPEAT_KEY (1<<28)
-
// Flag for key events. Multiple down events are idempotent. Release keys by
// sending the key code without this flag, or by sending MP_INPUT_RELEASE_ALL
// as key code.
-#define MP_KEY_STATE_DOWN (1<<29)
+#define MP_KEY_STATE_DOWN (1<<26)
+
+// The following flags are not modifiers, but are part of the keycode itself.
+
+// Emit a command even on key-up (normally key-up is ignored). The command
+// handling code has to ignore unwanted commands specifically.
+#define MP_KEY_EMIT_ON_UP (1<<27)
+
+// Use this when the key shouldn't be auto-repeated (like mouse buttons)
+// Also means both key-down key-up events produce emit bound commands.
+#define MP_NO_REPEAT_KEY (1<<28)
#endif /* MPLAYER_KEYCODES_H */
diff --git a/core/mplayer.c b/core/mplayer.c
index f12560b589..567e38e285 100644
--- a/core/mplayer.c
+++ b/core/mplayer.c
@@ -4333,13 +4333,6 @@ goto_reopen_demuxer: ;
mpctx->sh_video->fps, mpctx->sh_video->frametime);
}
- mp_input_set_section(mpctx->input, NULL, 0);
- //TODO: add desired (stream-based) sections here
- if (mpctx->master_demuxer->type == DEMUXER_TYPE_TV)
- mp_input_set_section(mpctx->input, "tv", 0);
- if (mpctx->encode_lavc_ctx)
- mp_input_set_section(mpctx->input, "encode", MP_INPUT_NO_DEFAULT_SECTION);
-
//==================== START PLAYING =======================
if (!mpctx->sh_video && !mpctx->sh_audio) {
@@ -4702,6 +4695,8 @@ static int mpv_main(int argc, char *argv[])
set_priority();
#endif
+ init_input(mpctx);
+
#ifdef CONFIG_ENCODING
if (opts->encode_output.file && *opts->encode_output.file) {
mpctx->encode_lavc_ctx = encode_lavc_init(&opts->encode_output);
@@ -4713,6 +4708,7 @@ static int mpv_main(int argc, char *argv[])
m_config_set_option0(mpctx->mconfig, "ao", "lavc");
m_config_set_option0(mpctx->mconfig, "fixed-vo", "yes");
m_config_set_option0(mpctx->mconfig, "gapless-audio", "yes");
+ mp_input_enable_section(mpctx->input, "encode", MP_INPUT_EXCLUSIVE);
}
#endif
@@ -4722,8 +4718,6 @@ static int mpv_main(int argc, char *argv[])
mpctx->osd = osd_create(opts, mpctx->ass_library);
- init_input(mpctx);
-
mpctx->playlist->current = mpctx->playlist->first;
play_files(mpctx);
diff --git a/etc/input.conf b/etc/input.conf
index f6a5b2a078..d2a39aca30 100644
--- a/etc/input.conf
+++ b/etc/input.conf
@@ -174,6 +174,9 @@ JOY_BTN1 cyc