1877 lines · 58.8 KB · C
RawDownload
/* * 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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t