/* * This file is part of mpv. * * mpv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * mpv is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with mpv. If not, see <http://www.gnu.org/licenses/>. */ #include "config.h" #include <stdlib.h>#include <string.h>#include <stdio.h>#include <stdbool.h>#include <math.h>#include <errno.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <assert.h> #include "osdep/io.h"#include "misc/rendezvous.h" #include "input.h"#include "keycodes.h"#include "osdep/threads.h"#include "osdep/timer.h"#include "common/msg.h"#include "common/global.h"#include "options/m_config.h"#include "options/m_option.h"#include "options/path.h"#include "mpv_talloc.h"#include "options/options.h"#include "misc/bstr.h"#include "misc/node.h"#include "stream/stream.h"#include "common/common.h" #if HAVE_COCOA#include "osdep/mac/app_bridge.h"#endif #define input_lock(ictx) mp_mutex_lock(&ictx->mutex)#define input_unlock(ictx) mp_mutex_unlock(&ictx->mutex) #define MP_MAX_KEY_DOWN 16 struct cmd_bind { int keys[MP_MAX_KEY_DOWN]; int num_keys; char *cmd; char *location; // filename/line number of definition char *desc; // human readable description bool is_builtin; struct cmd_bind_section *owner;}; struct cmd_bind_section { char *owner; struct cmd_bind *binds; int num_binds; bstr section; struct mp_rect mouse_area; // set at runtime, if at all bool mouse_area_set; // mouse_area is valid and should be tested}; #define MP_MAX_SOURCES 10 struct active_section { bstr name; int flags;}; struct cmd_queue { struct mp_cmd *first;}; struct wheel_state { double dead_zone_accum; double unit_accum;}; struct touch_point { int id; int x, y;}; struct input_ctx { mp_mutex mutex; struct mp_log *log; struct mpv_global *global; struct m_config_cache *opts_cache; struct input_opts *opts; bool using_ar; bool using_cocoa_media_keys; // Autorepeat stuff short ar_state; int64_t last_ar; // history of key downs - the newest is in position 0 int key_history[MP_MAX_KEY_DOWN]; // key code of the last key that triggered MP_KEY_STATE_DOWN int last_key_down; int64_t last_key_down_time; struct mp_cmd *current_down_cmd; int last_doubleclick_key_down; double last_doubleclick_time; // VO dragging state bool dragging_button_down; int mouse_drag_x, mouse_drag_y; // Raw mouse position before transform int mouse_raw_x, mouse_raw_y; // Mouse position on the consumer side (as command.c sees it) int mouse_x, mouse_y; int mouse_hover; // updated on mouse-enter/leave bstr mouse_section; // last section to receive mouse event // 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 mouse_mangle, mouse_src_mangle; struct mp_rect mouse_src, mouse_dst; // Wheel state (MP_WHEEL_*) struct wheel_state wheel_state_y; // MP_WHEEL_UP/MP_WHEEL_DOWN struct wheel_state wheel_state_x; // MP_WHEEL_LEFT/MP_WHEEL_RIGHT struct wheel_state *wheel_current; // The direction currently being scrolled double last_wheel_time; // mp_time_sec() of the last wheel event // List of command binding sections struct cmd_bind_section **sections; int num_sections; // List currently active command sections struct active_section *active_sections; int num_active_sections; // List currently active touch points struct touch_point *touch_points; int num_touch_points; unsigned int mouse_event_counter; struct mp_input_src *sources[MP_MAX_SOURCES]; int num_sources; struct cmd_queue cmd_queue; void (*wakeup_cb)(void *ctx); void *wakeup_ctx;}; static int parse_config(struct input_ctx *ictx, bool builtin, bstr data, const char *location, const bstr restrict_section);static void close_input_sources(struct input_ctx *ictx);static bool test_mouse(struct input_ctx *ictx, int x, int y, int rej_flags); #define OPT_BASE_STRUCT struct input_optsstruct input_opts { char *config_file; int doubleclick_time; // Maximum number of queued commands from keypresses (limit to avoid // repeated slow commands piling up) int key_fifo_size; // Autorepeat config (be aware of mp_input_set_repeat_info()) int ar_delay; int ar_rate; int dragging_deadzone; bool use_alt_gr; bool use_gamepad; bool use_media_keys; bool default_bindings; bool builtin_bindings; bool builtin_dragging; bool enable_mouse_movements; bool vo_key_input; bool test; bool allow_win_drag; bool preprocess_wheel; bool touch_emulate_mouse;}; const struct m_sub_options input_config = { .opts = (const m_option_t[]) { {"input-conf", OPT_STRING(config_file), .flags = M_OPT_FILE}, {"input-ar-delay", OPT_INT(ar_delay)}, {"input-ar-rate", OPT_INT(ar_rate)}, {"input-keylist", OPT_PRINT(mp_print_key_list)}, {"input-cmdlist", OPT_PRINT(mp_print_cmd_list)}, {"input-default-bindings", OPT_BOOL(default_bindings)}, {"input-builtin-bindings", OPT_BOOL(builtin_bindings)}, {"input-builtin-dragging", OPT_BOOL(builtin_dragging)}, {"input-test", OPT_BOOL(test)}, {"input-doubleclick-time", OPT_INT(doubleclick_time), M_RANGE(0, 1000)}, {"input-right-alt-gr", OPT_BOOL(use_alt_gr)}, {"input-key-fifo-size", OPT_INT(key_fifo_size), M_RANGE(2, 65000)}, {"input-cursor", OPT_BOOL(enable_mouse_movements)}, {"input-vo-keyboard", OPT_BOOL(vo_key_input)}, {"input-media-keys", OPT_BOOL(use_media_keys)}, {"input-preprocess-wheel", OPT_BOOL(preprocess_wheel)}, {"input-touch-emulate-mouse", OPT_BOOL(touch_emulate_mouse)}, {"input-dragging-deadzone", OPT_INT(dragging_deadzone)},#if HAVE_SDL2_GAMEPAD {"input-gamepad", OPT_BOOL(use_gamepad)},#endif {"window-dragging", OPT_BOOL(allow_win_drag)}, {0} }, .size = sizeof(struct input_opts), .defaults = &(const struct input_opts){ .key_fifo_size = 7, .doubleclick_time = 300, .ar_delay = 200, .ar_rate = 40, .dragging_deadzone = 3, .use_alt_gr = true, .enable_mouse_movements = true, .use_media_keys = true, .default_bindings = true, .builtin_bindings = true, .builtin_dragging = true, .vo_key_input = true, .allow_win_drag = true, .preprocess_wheel = true, .touch_emulate_mouse = true, }, .change_flags = UPDATE_INPUT,}; static const char builtin_input_conf[] =#include "etc/input.conf.inc"; 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 int queue_count_cmds(struct cmd_queue *queue){ int res = 0; for (struct mp_cmd *cmd = queue->first; cmd; cmd = cmd->queue_next) res++; return res;} static void queue_remove(struct cmd_queue *queue, struct mp_cmd *cmd){ struct mp_cmd **p_prev = &queue->first; while (*p_prev != cmd) { p_prev = &(*p_prev)->queue_next; } // if this fails, cmd was not in the queue assert(*p_prev == cmd); *p_prev = cmd->queue_next;} static struct mp_cmd *queue_remove_head(struct cmd_queue *queue){ struct mp_cmd *ret = queue->first; if (ret) queue_remove(queue, ret); return ret;} static void queue_add_tail(struct cmd_queue *queue, struct mp_cmd *cmd){ struct mp_cmd **p_prev = &queue->first; while (*p_prev) p_prev = &(*p_prev)->queue_next; *p_prev = cmd; cmd->queue_next = NULL;} static struct mp_cmd *queue_peek_tail(struct cmd_queue *queue){ struct mp_cmd *cur = queue->first; while (cur && cur->queue_next) cur = cur->queue_next; return cur;} static void queue_cmd(struct input_ctx *ictx, mp_cmd_t *cmd){ if (!cmd) return; queue_add_tail(&ictx->cmd_queue, cmd); mp_input_wakeup(ictx);} static void append_bind_info(struct input_ctx *ictx, char **pmsg, struct cmd_bind *bind){ char *msg = *pmsg; struct mp_cmd *cmd = mp_input_parse_cmd(ictx, bstr0(bind->cmd), bind->location); char *stripped = cmd ? cmd->original : bind->cmd; msg = talloc_asprintf_append(msg, " '%s'", stripped); if (!cmd) msg = talloc_asprintf_append(msg, " (invalid)"); if (!bstr_equals0(bind->owner->section, "default")) msg = talloc_asprintf_append(msg, " in section {%.*s}", BSTR_P(bind->owner->section)); msg = talloc_asprintf_append(msg, " in %s", bind->location); if (bind->is_builtin) msg = talloc_asprintf_append(msg, " (default)"); talloc_free(cmd); *pmsg = msg;} static mp_cmd_t *handle_test(struct input_ctx *ictx, int code){ if (code == MP_KEY_CLOSE_WIN) { MP_WARN(ictx, "CLOSE_WIN was received. This pseudo key can be remapped too,\n" "but --input-test will always quit when receiving it.\n"); const char *args[] = {"quit", NULL}; mp_cmd_t *res = mp_input_parse_cmd_strv(ictx->log, args); return res; } char *key_buf = mp_input_get_key_combo_name(&code, 1); char *msg = talloc_asprintf(NULL, "Key %s is bound to:\n", key_buf); talloc_free(key_buf); int count = 0; for (int n = 0; n < ictx->num_sections; n++) { struct cmd_bind_section *bs = ictx->sections[n]; for (int i = 0; i < bs->num_binds; i++) { if (bs->binds[i].num_keys && bs->binds[i].keys[0] == code) { count++; if (count > 1) msg = talloc_asprintf_append(msg, "\n"); msg = talloc_asprintf_append(msg, "%d. ", count); append_bind_info(ictx, &msg, &bs->binds[i]); } } } if (!count) msg = talloc_asprintf_append(msg, "(nothing)"); MP_INFO(ictx, "%s\n", msg); const char *args[] = {"show-text", msg, NULL}; mp_cmd_t *res = mp_input_parse_cmd_strv(ictx->log, args); talloc_free(msg); return res;} static struct cmd_bind_section *find_section(struct input_ctx *ictx, bstr section){ for (int n = 0; n < ictx->num_sections; n++) { struct cmd_bind_section *bs = ictx->sections[n]; if (bstr_equals(section, bs->section)) return bs; } return NULL;} static struct cmd_bind_section *get_bind_section(struct input_ctx *ictx, bstr section){ if (section.len == 0) section = bstr0("default"); struct cmd_bind_section *bind_section = find_section(ictx, section); if (bind_section) return bind_section; bind_section = talloc_ptrtype(ictx, bind_section); *bind_section = (struct cmd_bind_section) { .section = bstrdup(bind_section, section), .mouse_area = {INT_MIN, INT_MIN, INT_MAX, INT_MAX}, .mouse_area_set = true, }; MP_TARRAY_APPEND(ictx, ictx->sections, ictx->num_sections, bind_section); return bind_section;} static void key_buf_add(int *buf, int code){ for (int n = MP_MAX_KEY_DOWN - 1; n > 0; n--) buf[n] = buf[n - 1]; buf[0] = code;} static struct cmd_bind *find_bind_for_key_section(struct input_ctx *ictx, bstr section, int code){ struct cmd_bind_section *bs = get_bind_section(ictx, section); if (!bs->num_binds) return NULL; int keys[MP_MAX_KEY_DOWN]; memcpy(keys, ictx->key_history, sizeof(keys)); key_buf_add(keys, code); struct cmd_bind *best = NULL; // Prefer user-defined keys over builtin bindings for (int builtin = 0; builtin < 2; builtin++) { if (builtin && !ictx->opts->default_bindings) break; for (int n = 0; n < bs->num_binds; n++) { if (bs->binds[n].is_builtin == (bool)builtin) { struct cmd_bind *b = &bs->binds[n]; // we have: keys=[key2 key1 keyX ...] // and: b->keys=[key1 key2] (and may be just a prefix) for (int i = 0; i < b->num_keys; i++) { if (b->keys[i] != keys[b->num_keys - 1 - i]) goto skip; } if (!best || b->num_keys > best->num_keys) best = b; skip: ; } } } return best;} static struct cmd_bind *find_any_bind_for_key(struct input_ctx *ictx, bstr force_section, int code){ if (force_section.len) return find_bind_for_key_section(ictx, force_section, code); bool use_mouse = MP_KEY_DEPENDS_ON_MOUSE_POS(code); // First look whether a mouse section is capturing all mouse input // exclusively (regardless of the active section stack order). if (use_mouse && MP_KEY_IS_MOUSE_BTN_SINGLE(ictx->last_key_down) && !MP_KEY_IS_MOUSE_BTN_DBL(code)) { struct cmd_bind *bind = find_bind_for_key_section(ictx, ictx->mouse_section, code); if (bind) return bind; } struct cmd_bind *best_bind = NULL; for (int i = ictx->num_active_sections - 1; i >= 0; i--) { struct active_section *s = &ictx->active_sections[i]; struct cmd_bind *bind = find_bind_for_key_section(ictx, s->name, code); if (bind) { struct cmd_bind_section *bs = bind->owner; if (!use_mouse || (bs->mouse_area_set && test_rect(&bs->mouse_area, ictx->mouse_vo_x, ictx->mouse_vo_y))) { if (!best_bind || bind->num_keys > best_bind->num_keys || (best_bind->is_builtin && !bind->is_builtin && bind->num_keys == best_bind->num_keys)) { best_bind = bind; } } } if (s->flags & MP_INPUT_EXCLUSIVE) break; if (best_bind && (s->flags & MP_INPUT_ON_TOP)) break; } return best_bind;} static mp_cmd_t *get_cmd_from_keys(struct input_ctx *ictx, bstr force_section, int code){ if (ictx->opts->test) return handle_test(ictx, code); struct cmd_bind *cmd = NULL; if (MP_KEY_IS_UNICODE(code)) cmd = find_any_bind_for_key(ictx, force_section, MP_KEY_ANY_UNICODE); if (!cmd) cmd = find_any_bind_for_key(ictx, force_section, code); if (!cmd) cmd = find_any_bind_for_key(ictx, force_section, MP_KEY_UNMAPPED);Lines 1–500 of 1877. Load more
/* * This file is part of mpv. * * mpv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * mpv is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with mpv. If not, see <http://www.gnu.org/licenses/>. */ #include "config.h" #include <stdlib.h> #include <string.h> #include <stdio.h> #include <stdbool.h> #include <math.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <assert.h> #include "osdep/io.h" #include "misc/rendezvous.h" #include "input.h" #include "keycodes.h" #include "osdep/threads.h" #include "osdep/timer.h" #include "common/msg.h" #include "common/global.h" #include "options/m_config.h" #include "options/m_option.h" #include "options/path.h" #include "mpv_talloc.h" #include "options/options.h" #include "misc/bstr.h" #include "misc/node.h" #include "stream/stream.h" #include "common/common.h" #if HAVE_COCOA #include "osdep/mac/app_bridge.h" #endif #define input_lock(ictx) mp_mutex_lock(&ictx->mutex) #define input_unlock(ictx) mp_mutex_unlock(&ictx->mutex) #define MP_MAX_KEY_DOWN 16 struct cmd_bind { int keys[MP_MAX_KEY_DOWN]; int num_keys; char *cmd; char *location; // filename/line number of definition char *desc; // human readable description bool is_builtin; struct cmd_bind_section *owner; }; struct cmd_bind_section { char *owner; struct cmd_bind *binds; int num_binds; bstr section; struct mp_rect mouse_area; // set at runtime, if at all bool mouse_area_set; // mouse_area is valid and should be tested }; #define MP_MAX_SOURCES 10 struct active_section { bstr name; int flags; }; struct cmd_queue { struct mp_cmd *first; }; struct wheel_state { double dead_zone_accum; double unit_accum; }; struct touch_point { int id; int x, y; }; struct input_ctx { mp_mutex mutex; struct mp_log *log; struct mpv_global *global; struct m_config_cache *opts_cache; struct input_opts *opts; bool using_ar; bool using_cocoa_media_keys; // Autorepeat stuff short ar_state; int64_t last_ar; // history of key downs - the newest is in position 0 int key_history[MP_MAX_KEY_DOWN]; // key code of the last key that triggered MP_KEY_STATE_DOWN int last_key_down; int64_t last_key_down_time; struct mp_cmd *current_down_cmd; int last_doubleclick_key_down; double last_doubleclick_time; // VO dragging state bool dragging_button_down; int mouse_drag_x, mouse_drag_y; // Raw mouse position before transform int mouse_raw_x, mouse_raw_y; // Mouse position on the consumer side (as command.c sees it) int mouse_x, mouse_y; int mouse_hover; // updated on mouse-enter/leave bstr mouse_section; // last section to receive mouse event // 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 mouse_mangle, mouse_src_mangle; struct mp_rect mouse_src, mouse_dst; // Wheel state (MP_WHEEL_*) struct wheel_state wheel_state_y; // MP_WHEEL_UP/MP_WHEEL_DOWN struct wheel_state wheel_state_x; // MP_WHEEL_LEFT/MP_WHEEL_RIGHT struct wheel_state *wheel_current; // The direction currently being scrolled double last_wheel_time; // mp_time_sec() of the last wheel event // List of command binding sections struct cmd_bind_section **sections; int num_sections; // List currently active command sections struct active_section *active_sections; int num_active_sections; // List currently active touch points struct touch_point *touch_points; int num_touch_points; unsigned int mouse_event_counter; struct mp_input_src *sources[MP_MAX_SOURCES]; int num_sources; struct cmd_queue cmd_queue; void (*wakeup_cb)(void *ctx); void *wakeup_ctx; }; static int parse_config(struct input_ctx *ictx, bool builtin, bstr data, const char *location, const bstr restrict_section); static void close_input_sources(struct input_ctx *ictx); static bool test_mouse(struct input_ctx *ictx, int x, int y, int rej_flags); #define OPT_BASE_STRUCT struct input_opts struct input_opts { char *config_file; int doubleclick_time; // Maximum number of queued commands from keypresses (limit to avoid // repeated slow commands piling up) int key_fifo_size; // Autorepeat config (be aware of mp_input_set_repeat_info()) int ar_delay; int ar_rate; int dragging_deadzone; bool use_alt_gr; bool use_gamepad; bool use_media_keys; bool default_bindings; bool builtin_bindings; bool builtin_dragging; bool enable_mouse_movements; bool vo_key_input; bool test; bool allow_win_drag; bool preprocess_wheel; bool touch_emulate_mouse; }; const struct m_sub_options input_config = { .opts = (const m_option_t[]) { {"input-conf", OPT_STRING(config_file), .flags = M_OPT_FILE}, {"input-ar-delay", OPT_INT(ar_delay)}, {"input-ar-rate", OPT_INT(ar_rate)}, {"input-keylist", OPT_PRINT(mp_print_key_list)}, {"input-cmdlist", OPT_PRINT(mp_print_cmd_list)}, {"input-default-bindings", OPT_BOOL(default_bindings)}, {"input-builtin-bindings", OPT_BOOL(builtin_bindings)}, {"input-builtin-dragging", OPT_BOOL(builtin_dragging)}, {"input-test", OPT_BOOL(test)}, {"input-doubleclick-time", OPT_INT(doubleclick_time), M_RANGE(0, 1000)}, {"input-right-alt-gr", OPT_BOOL(use_alt_gr)}, {"input-key-fifo-size", OPT_INT(key_fifo_size), M_RANGE(2, 65000)}, {"input-cursor", OPT_BOOL(enable_mouse_movements)}, {"input-vo-keyboard", OPT_BOOL(vo_key_input)}, {"input-media-keys", OPT_BOOL(use_media_keys)}, {"input-preprocess-wheel", OPT_BOOL(preprocess_wheel)}, {"input-touch-emulate-mouse", OPT_BOOL(touch_emulate_mouse)}, {"input-dragging-deadzone", OPT_INT(dragging_deadzone)}, #if HAVE_SDL2_GAMEPAD {"input-gamepad", OPT_BOOL(use_gamepad)}, #endif {"window-dragging", OPT_BOOL(allow_win_drag)}, {0} }, .size = sizeof(struct input_opts), .defaults = &(const struct input_opts){ .key_fifo_size = 7, .doubleclick_time = 300, .ar_delay = 200, .ar_rate = 40, .dragging_deadzone = 3, .use_alt_gr = true, .enable_mouse_movements = true, .use_media_keys = true, .default_bindings = true, .builtin_bindings = true, .builtin_dragging = true, .vo_key_input = true, .allow_win_drag = true, .preprocess_wheel = true, .touch_emulate_mouse = true, }, .change_flags = UPDATE_INPUT, }; static const char builtin_input_conf[] = #include "etc/input.conf.inc" ; 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 int queue_count_cmds(struct cmd_queue *queue) { int res = 0; for (struct mp_cmd *cmd = queue->first; cmd; cmd = cmd->queue_next) res++; return res; } static void queue_remove(struct cmd_queue *queue, struct mp_cmd *cmd) { struct mp_cmd **p_prev = &queue->first; while (*p_prev != cmd) { p_prev = &(*p_prev)->queue_next; } // if this fails, cmd was not in the queue assert(*p_prev == cmd); *p_prev = cmd->queue_next; } static struct mp_cmd *queue_remove_head(struct cmd_queue *queue) { struct mp_cmd *ret = queue->first; if (ret) queue_remove(queue, ret); return ret; } static void queue_add_tail(struct cmd_queue *queue, struct mp_cmd *cmd) { struct mp_cmd **p_prev = &queue->first; while (*p_prev) p_prev = &(*p_prev)->queue_next; *p_prev = cmd; cmd->queue_next = NULL; } static struct mp_cmd *queue_peek_tail(struct cmd_queue *queue) { struct mp_cmd *cur = queue->first; while (cur && cur->queue_next) cur = cur->queue_next; return cur; } static void queue_cmd(struct input_ctx *ictx, mp_cmd_t *cmd) { if (!cmd) return; queue_add_tail(&ictx->cmd_queue, cmd); mp_input_wakeup(ictx); } static void append_bind_info(struct input_ctx *ictx, char **pmsg, struct cmd_bind *bind) { char *msg = *pmsg; struct mp_cmd *cmd = mp_input_parse_cmd(ictx, bstr0(bind->cmd), bind->location); char *stripped = cmd ? cmd->original : bind->cmd; msg = talloc_asprintf_append(msg, " '%s'", stripped); if (!cmd) msg = talloc_asprintf_append(msg, " (invalid)"); if (!bstr_equals0(bind->owner->section, "default")) msg = talloc_asprintf_append(msg, " in section {%.*s}", BSTR_P(bind->owner->section)); msg = talloc_asprintf_append(msg, " in %s", bind->location); if (bind->is_builtin) msg = talloc_asprintf_append(msg, " (default)"); talloc_free(cmd); *pmsg = msg; } static mp_cmd_t *handle_test(struct input_ctx *ictx, int code) { if (code == MP_KEY_CLOSE_WIN) { MP_WARN(ictx, "CLOSE_WIN was received. This pseudo key can be remapped too,\n" "but --input-test will always quit when receiving it.\n"); const char *args[] = {"quit", NULL}; mp_cmd_t *res = mp_input_parse_cmd_strv(ictx->log, args); return res; } char *key_buf = mp_input_get_key_combo_name(&code, 1); char *msg = talloc_asprintf(NULL, "Key %s is bound to:\n", key_buf); talloc_free(key_buf); int count = 0; for (int n = 0; n < ictx->num_sections; n++) { struct cmd_bind_section *bs = ictx->sections[n]; for (int i = 0; i < bs->num_binds; i++) { if (bs->binds[i].num_keys && bs->binds[i].keys[0] == code) { count++; if (count > 1) msg = talloc_asprintf_append(msg, "\n"); msg = talloc_asprintf_append(msg, "%d. ", count); append_bind_info(ictx, &msg, &bs->binds[i]); } } } if (!count) msg = talloc_asprintf_append(msg, "(nothing)"); MP_INFO(ictx, "%s\n", msg); const char *args[] = {"show-text", msg, NULL}; mp_cmd_t *res = mp_input_parse_cmd_strv(ictx->log, args); talloc_free(msg); return res; } static struct cmd_bind_section *find_section(struct input_ctx *ictx, bstr section) { for (int n = 0; n < ictx->num_sections; n++) { struct cmd_bind_section *bs = ictx->sections[n]; if (bstr_equals(section, bs->section)) return bs; } return NULL; } static struct cmd_bind_section *get_bind_section(struct input_ctx *ictx, bstr section) { if (section.len == 0) section = bstr0("default"); struct cmd_bind_section *bind_section = find_section(ictx, section); if (bind_section) return bind_section; bind_section = talloc_ptrtype(ictx, bind_section); *bind_section = (struct cmd_bind_section) { .section = bstrdup(bind_section, section), .mouse_area = {INT_MIN, INT_MIN, INT_MAX, INT_MAX}, .mouse_area_set = true, }; MP_TARRAY_APPEND(ictx, ictx->sections, ictx->num_sections, bind_section); return bind_section; } static void key_buf_add(int *buf, int code) { for (int n = MP_MAX_KEY_DOWN - 1; n > 0; n--) buf[n] = buf[n - 1]; buf[0] = code; } static struct cmd_bind *find_bind_for_key_section(struct input_ctx *ictx, bstr section, int code) { struct cmd_bind_section *bs = get_bind_section(ictx, section); if (!bs->num_binds) return NULL; int keys[MP_MAX_KEY_DOWN]; memcpy(keys, ictx->key_history, sizeof(keys)); key_buf_add(keys, code); struct cmd_bind *best = NULL; // Prefer user-defined keys over builtin bindings for (int builtin = 0; builtin < 2; builtin++) { if (builtin && !ictx->opts->default_bindings) break; for (int n = 0; n < bs->num_binds; n++) { if (bs->binds[n].is_builtin == (bool)builtin) { struct cmd_bind *b = &bs->binds[n]; // we have: keys=[key2 key1 keyX ...] // and: b->keys=[key1 key2] (and may be just a prefix) for (int i = 0; i < b->num_keys; i++) { if (b->keys[i] != keys[b->num_keys - 1 - i]) goto skip; } if (!best || b->num_keys > best->num_keys) best = b; skip: ; } } } return best; } static struct cmd_bind *find_any_bind_for_key(struct input_ctx *ictx, bstr force_section, int code) { if (force_section.len) return find_bind_for_key_section(ictx, force_section, code); bool use_mouse = MP_KEY_DEPENDS_ON_MOUSE_POS(code); // First look whether a mouse section is capturing all mouse input // exclusively (regardless of the active section stack order). if (use_mouse && MP_KEY_IS_MOUSE_BTN_SINGLE(ictx->last_key_down) && !MP_KEY_IS_MOUSE_BTN_DBL(code)) { struct cmd_bind *bind = find_bind_for_key_section(ictx, ictx->mouse_section, code); if (bind) return bind; } struct cmd_bind *best_bind = NULL; for (int i = ictx->num_active_sections - 1; i >= 0; i--) { struct active_section *s = &ictx->active_sections[i]; struct cmd_bind *bind = find_bind_for_key_section(ictx, s->name, code); if (bind) { struct cmd_bind_section *bs = bind->owner; if (!use_mouse || (bs->mouse_area_set && test_rect(&bs->mouse_area, ictx->mouse_vo_x, ictx->mouse_vo_y))) { if (!best_bind || bind->num_keys > best_bind->num_keys || (best_bind->is_builtin && !bind->is_builtin && bind->num_keys == best_bind->num_keys)) { best_bind = bind; } } } if (s->flags & MP_INPUT_EXCLUSIVE) break; if (best_bind && (s->flags & MP_INPUT_ON_TOP)) break; } return best_bind; } static mp_cmd_t *get_cmd_from_keys(struct input_ctx *ictx, bstr force_section, int code) { if (ictx->opts->test) return handle_test(ictx, code); struct cmd_bind *cmd = NULL; if (MP_KEY_IS_UNICODE(code)) cmd = find_any_bind_for_key(ictx, force_section, MP_KEY_ANY_UNICODE); if (!cmd) cmd = find_any_bind_for_key(ictx, force_section, code); if (!cmd) cmd = find_any_bind_for_key(ictx, force_section, MP_KEY_UNMAPPED);