summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-04-26 02:13:30 +0200
committerwm4 <wm4@nowhere>2013-06-29 22:58:13 +0200
commit5b38a522f17aa24d89b807846c2f76760923351f (patch)
tree44fc0bf235cb6df8e8bdf3a4dc19fdd616553081 /core
parentd4680aaecdc8711a878d973c74e223d49cdf9da3 (diff)
downloadmpv-5b38a522f17aa24d89b807846c2f76760923351f.tar.bz2
mpv-5b38a522f17aa24d89b807846c2f76760923351f.tar.xz
input: handle mouse movement differently
Before this commit, mouse movement events emitted a special command ("set_mouse_pos"), which was specially handled in command.c. This was once special-cased to the dvdnav and menu code, and did nothing after libmenu and dvdnav were removed. Change it so that mouse movement triggers a pseudo-key ("MOUSE_MOVE"), which then can be bound to an arbitrary command. The mouse position is now managed in input.c. A command which actually needs the mouse position can use either mp_input_get_mouse_pos() or mp_get_osd_mouse_pos() to query it. The former returns raw window-space coordinates, while the latter returns coordinates transformed to OSD- space. (Both are the same for most VOs, except vo_xv and vo_x11, which can't render OSD in window-space. These require extra code for mapping mouse position.) As of this commit, there is still nothing that uses mouse movement, so MOUSE_MOVE is mapped to "ignore" to silence warnings when moving the mouse (much like MOUSE_BTN0). Extend the concept of input sections. Allow multiple sections to be active at once, and organize them as stack. Bindings from the top of the stack are preferred to lower ones. Each section has a mouse input section associated, inside which mouse events are associated with the bindings. If the mouse pointer is outside of a section's mouse area, mouse events will be dispatched to an input section lower on the stack of active sections. This is intended for scripting, which is to be added later. Two scripts could occupy different areas of the screen without conflicting with each other. (If it turns out that this mechanism is useless, we'll just remove it again.)
Diffstat (limited to 'core')
-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
6 files changed, 328 insertions, 145 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(&