/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU 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 <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <pthread.h>
#include <assert.h>
#include <libavutil/avstring.h>
#include <libavutil/common.h>
#include "osdep/io.h"
#include "osdep/semaphore.h"
#include "misc/rendezvous.h"
#include "input.h"
#include "keycodes.h"
#include "cmd_list.h"
#include "cmd_parse.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 "stream/stream.h"
#include "common/common.h"
#if HAVE_COCOA
#include "osdep/macosx_events.h"
#endif
#define input_lock(ictx) pthread_mutex_lock(&ictx->mutex)
#define input_unlock(ictx) pthread_mutex_unlock(&ictx->mutex)
#define MP_MAX_KEY_DOWN 4
struct cmd_bind {
int keys[MP_MAX_KEY_DOWN];
int num_keys;
char *cmd;
char *location; // filename/line number of definition
bool is_builtin;
struct cmd_bind_section *owner;
};
struct cmd_bind_section {
struct cmd_bind *binds;
int num_binds;
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 MP_MAX_SOURCES 10
#define MAX_ACTIVE_SECTIONS 50
struct active_section {
char *name;
int flags;
};
struct cmd_queue {
struct mp_cmd *first;
};
struct input_ctx {
pthread_mutex_t mutex;
sem_t wakeup;
struct mp_log *log;
struct mpv_global *global;
struct input_opts *opts;
bool using_alt_gr;
bool using_ar;
bool using_cocoa_media_keys;
bool win_drag;
// 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;
// Mouse position on the consumer side (as command.c sees it)
int mouse_x, mouse_y;
char *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;
// List of command binding sections
struct cmd_bind_section *cmd_bind_sections;
// List currently active command sections
struct active_section active_sections[MAX_ACTIVE_SECTIONS];
int num_active_sections;
unsigned int mouse_event_counter;
struct mp_input_src *sources[MP_MAX_SOURCES];
int num_sources;
struct cmd_queue cmd_queue;
struct mp_cancel *cancel;
};
static int parse_config(struct input_ctx *ictx, bool builtin, bstr data,
const char *location, const char *restrict_section);
static void close_input_sources(struct input_ctx *ictx);
#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 use_alt_gr;
int use_appleremote;
int use_media_keys;
int use_app_events;
int default_bindings;
int enable_mouse_movements;
int vo_key_input;
int test;
};
const struct m_sub_options input_config = {
.opts = (const m_option_t[]) {
OPT_STRING("conf", config_file, CONF_GLOBAL | M_OPT_FILE),
OPT_INT("ar-delay", ar_delay, CONF_GLOBAL),
OPT_INT("ar-rate", ar_rate, CONF_GLOBAL),
OPT_PRINT("keylist", mp_print_key_list),
OPT_PRINT("cmdlist", mp_print_cmd_list),
OPT_FLAG("default-bindings", default_bindings, CONF_GLOBAL),
OPT_FLAG("test", test, CONF_GLOBAL),
OPT_INTRANGE("doubleclick-time", doubleclick_time, 0, 0, 1000),
OPT_FLAG("right-alt-gr", use_alt_gr, CONF_GLOBAL),
OPT_INTRANGE("key-fifo-size", key_fifo_size, CONF_GLOBAL, 2, 65000),
OPT_FLAG("cursor", enable_mouse_movements, CONF_GLOBAL),
OPT_FLAG("vo-keyboard", vo_key_input, CONF_GLOBAL),
OPT_FLAG("x11-keyboard", vo_key_input, CONF_GLOBAL), // old alias
#if HAVE_COCOA
OPT_FLAG("appleremote", use_appleremote, CONF_GLOBAL),
OPT_FLAG("media-keys", use_media_keys, CONF_GLOBAL),
OPT_FLAG("app-events", use_app_events, CONF_GLOBAL),
#endif
{0}
},
.size = sizeof(struct input_opts),
.defaults = &(const struct input_opts){
.key_fifo_size = 7,
.doubleclick_time = 300,
.ar_delay = 200,
.ar_rate = 40,
.use_alt_gr = 1,
.enable_mouse_movements = 1,
#if HAVE_COCOA
.use_appleremote = 1,
.use_media_keys = 1,
.use_app_events = 1,
#endif
.default_bindings = 1,
.vo_key_input = 1,
},
};
static const char builtin_input_conf[] =
#include "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 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 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);
bstr stripped = cmd ? cmd->original : bstr0(bind->cmd);
msg = talloc_asprintf_append(msg, " '%.*s'", BSTR_P(stripped));
if (!cmd)
msg = talloc_asprintf_append(msg, " (invalid)");
if (strcmp(bind->owner->section, "default") != 0)
msg = talloc_asprintf_append(msg, " in section {%s}",
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 to
|