/*
* 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 <math.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 "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 {
char *owner;
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;
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;
// 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;
void (*cancel)(void *cancel_ctx);
void *cancel_ctx;
void (*wakeup_cb)(void *ctx);
void *wakeup_ctx;
};
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 default_bindings;
int enable_mouse_movements;
int vo_key_input;
int test;
int allow_win_drag;
};
const struct m_sub_options input_config = {
.opts = (const m_option_t[]) {
OPT_STRING("input-conf", config_file, M_OPT_FIXED | M_OPT_FILE),
OPT_INT("input-ar-delay", ar_delay, 0),
OPT_INT("input-ar-rate", ar_rate, 0),
OPT_PRINT("input-keylist", mp_print_key_list),
OPT_PRINT("input-cmdlist", mp_print_cmd_list),
OPT_FLAG("input-default-bindings", default_bindings, 0),
OPT_FLAG("input-test", test, 0),
OPT_INTRANGE("input-doubleclick-time", doubleclick_time, 0, 0, 1000),
OPT_FLAG("input-right-alt-gr", use_alt_gr, 0),
OPT_INTRANGE("input-key-fifo-size", key_fifo_size, 0, 2, 65000),
OPT_FLAG("input-cursor", enable_mouse_movements, 0),
OPT_FLAG("input-vo-keyboard", vo_key_input, 0),
#if HAVE_COCOA
OPT_FLAG("input-appleremote", use_appleremote, 0),
OPT_FLAG("input-media-keys", use_media_keys, 0),
#endif
OPT_FLAG("window-dragging", allow_win_drag, 0),
OPT_REPLACED("input-x11-keyboard", "input-vo-keyboard"),
{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,
#endif
.default_bindings = 1,
.vo_key_input = 1,
.allow_win_drag = 1,
},
.change_flags = UPDATE_INPUT,
};
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-&
|