diff options
Diffstat (limited to 'core/input/input.c')
-rw-r--r-- | core/input/input.c | 319 |
1 files changed, 238 insertions, 81 deletions
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); } |