0
0
mirror of https://github.com/mpv-player/mpv.git synced 2024-09-20 20:03:10 +02:00
mpv/input/input.c
Uoti Urpala d6072d7408 input: sleep in event loop even if there are no input fds
The input loop select() call was only run if there was at least one
input file descriptor. However other code uses the input loop to wait;
this could result in the wait becoming a busy loop when running with
-noconsolecontrols (without that there is at least one input fd for
terminal input). Make the input loop call select() to sleep even if
there are no input file descriptors.
2011-04-22 09:34:03 +03:00

1892 lines
57 KiB
C

/*
* This file is part of MPlayer.
*
* MPlayer 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.
*
* MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#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 <ctype.h>
#include "input.h"
#include "mouse.h"
#ifdef MP_DEBUG
#include <assert.h>
#endif
#include "mp_fifo.h"
#include "osdep/keycodes.h"
#include "osdep/timer.h"
#include "libavutil/avstring.h"
#include "mp_msg.h"
#include "m_config.h"
#include "m_option.h"
#include "path.h"
#include "talloc.h"
#include "options.h"
#include "bstr.h"
#include "joystick.h"
#ifdef CONFIG_LIRC
#include "lirc.h"
#endif
#ifdef CONFIG_LIRCC
#include <lirc/lircc.h>
#endif
#include "ar.h"
typedef struct mp_cmd_bind {
int input[MP_MAX_KEY_DOWN+1];
char* cmd;
} mp_cmd_bind_t;
typedef struct mp_key_name {
int key;
char* name;
} mp_key_name_t;
/// This array defines all known commands.
/// The first field is an id used to recognize the command without too many strcmp.
/// The second is obviously the command name.
/// The third is the minimum number of arguments this command needs.
/// Then comes the definition of each argument, terminated with an arg of type -1.
/// A command can take a maximum of MP_CMD_MAX_ARGS-1 arguments (-1 because of
/// the last one) which is actually 9.
/// For the args, the first field is the type (actually int, float or string), the second
/// is the default value wich is used for optional arguments
static const mp_cmd_t mp_cmds[] = {
#ifdef CONFIG_RADIO
{ MP_CMD_RADIO_STEP_CHANNEL, "radio_step_channel", 1, { { MP_CMD_ARG_INT ,{0}}, {-1,{0}} }},
{ MP_CMD_RADIO_SET_CHANNEL, "radio_set_channel", 1, { { MP_CMD_ARG_STRING, {0}}, {-1,{0}} }},
{ MP_CMD_RADIO_SET_FREQ, "radio_set_freq", 1, { {MP_CMD_ARG_FLOAT,{0}}, {-1,{0}} } },
{ MP_CMD_RADIO_STEP_FREQ, "radio_step_freq", 1, { {MP_CMD_ARG_FLOAT,{0}}, {-1,{0}} } },
#endif
{ MP_CMD_SEEK, "seek", 1, { {MP_CMD_ARG_FLOAT,{0}}, {MP_CMD_ARG_INT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_EDL_MARK, "edl_mark", 0, { {-1,{0}} } },
{ MP_CMD_AUDIO_DELAY, "audio_delay", 1, { {MP_CMD_ARG_FLOAT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_SPEED_INCR, "speed_incr", 1, { {MP_CMD_ARG_FLOAT,{0}}, {-1,{0}} } },
{ MP_CMD_SPEED_MULT, "speed_mult", 1, { {MP_CMD_ARG_FLOAT,{0}}, {-1,{0}} } },
{ MP_CMD_SPEED_SET, "speed_set", 1, { {MP_CMD_ARG_FLOAT,{0}}, {-1,{0}} } },
{ MP_CMD_QUIT, "quit", 0, { {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_STOP, "stop", 0, { {-1,{0}} } },
{ MP_CMD_PAUSE, "pause", 0, { {-1,{0}} } },
{ MP_CMD_FRAME_STEP, "frame_step", 0, { {-1,{0}} } },
{ MP_CMD_PLAY_TREE_STEP, "pt_step",1, { { MP_CMD_ARG_INT ,{0}}, { MP_CMD_ARG_INT ,{0}}, {-1,{0}} } },
{ MP_CMD_PLAY_TREE_UP_STEP, "pt_up_step",1, { { MP_CMD_ARG_INT,{0} }, { MP_CMD_ARG_INT ,{0}}, {-1,{0}} } },
{ MP_CMD_PLAY_ALT_SRC_STEP, "alt_src_step",1, { { MP_CMD_ARG_INT,{0} }, {-1,{0}} } },
{ MP_CMD_LOOP, "loop", 1, { {MP_CMD_ARG_INT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_SUB_DELAY, "sub_delay",1, { {MP_CMD_ARG_FLOAT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_SUB_STEP, "sub_step",1, { { MP_CMD_ARG_INT,{0} }, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_OSD, "osd",0, { {MP_CMD_ARG_INT,{-1}}, {-1,{0}} } },
{ MP_CMD_OSD_SHOW_TEXT, "osd_show_text", 1, { {MP_CMD_ARG_STRING, {0}}, {MP_CMD_ARG_INT,{-1}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_OSD_SHOW_PROPERTY_TEXT, "osd_show_property_text",1, { {MP_CMD_ARG_STRING, {0}}, {MP_CMD_ARG_INT,{-1}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_OSD_SHOW_PROGRESSION, "osd_show_progression", 0, { {-1,{0}} } },
{ MP_CMD_VOLUME, "volume", 1, { { MP_CMD_ARG_FLOAT,{0} }, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_BALANCE, "balance", 1, { { MP_CMD_ARG_FLOAT,{0} }, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_MIXER_USEMASTER, "use_master", 0, { {-1,{0}} } },
{ MP_CMD_MUTE, "mute", 0, { {MP_CMD_ARG_INT,{-1}}, {-1,{0}} } },
{ MP_CMD_CONTRAST, "contrast",1, { {MP_CMD_ARG_INT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_GAMMA, "gamma", 1, { {MP_CMD_ARG_INT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_BRIGHTNESS, "brightness",1, { {MP_CMD_ARG_INT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_HUE, "hue",1, { {MP_CMD_ARG_INT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_SATURATION, "saturation",1, { {MP_CMD_ARG_INT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_FRAMEDROPPING, "frame_drop",0, { { MP_CMD_ARG_INT,{-1} }, {-1,{0}} } },
{ MP_CMD_SUB_POS, "sub_pos", 1, { {MP_CMD_ARG_INT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_SUB_ALIGNMENT, "sub_alignment",0, { {MP_CMD_ARG_INT,{-1}}, {-1,{0}} } },
{ MP_CMD_SUB_VISIBILITY, "sub_visibility", 0, { {MP_CMD_ARG_INT,{-1}}, {-1,{0}} } },
{ MP_CMD_SUB_LOAD, "sub_load", 1, { {MP_CMD_ARG_STRING,{0}}, {-1,{0}} } },
{ MP_CMD_SUB_REMOVE, "sub_remove", 0, { {MP_CMD_ARG_INT,{-1}}, {-1,{0}} } },
{ MP_CMD_SUB_SELECT, "vobsub_lang", 0, { { MP_CMD_ARG_INT,{-2} }, {-1,{0}} } }, // for compatibility
{ MP_CMD_SUB_SELECT, "sub_select", 0, { { MP_CMD_ARG_INT,{-2} }, {-1,{0}} } },
{ MP_CMD_SUB_SOURCE, "sub_source", 0, { { MP_CMD_ARG_INT,{-2} }, {-1,{0}} } },
{ MP_CMD_SUB_VOB, "sub_vob", 0, { { MP_CMD_ARG_INT,{-2} }, {-1,{0}} } },
{ MP_CMD_SUB_DEMUX, "sub_demux", 0, { { MP_CMD_ARG_INT,{-2} }, {-1,{0}} } },
{ MP_CMD_SUB_FILE, "sub_file", 0, { { MP_CMD_ARG_INT,{-2} }, {-1,{0}} } },
{ MP_CMD_SUB_LOG, "sub_log", 0, { {-1,{0}} } },
{ MP_CMD_SUB_SCALE, "sub_scale",1, { {MP_CMD_ARG_FLOAT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
#ifdef CONFIG_ASS
{ MP_CMD_ASS_USE_MARGINS, "ass_use_margins", 0, { {MP_CMD_ARG_INT,{-1}}, {-1,{0}} } },
#endif
{ MP_CMD_GET_PERCENT_POS, "get_percent_pos", 0, { {-1,{0}} } },
{ MP_CMD_GET_TIME_POS, "get_time_pos", 0, { {-1,{0}} } },
{ MP_CMD_GET_TIME_LENGTH, "get_time_length", 0, { {-1,{0}} } },
{ MP_CMD_GET_FILENAME, "get_file_name", 0, { {-1,{0}} } },
{ MP_CMD_GET_VIDEO_CODEC, "get_video_codec", 0, { {-1,{0}} } },
{ MP_CMD_GET_VIDEO_BITRATE, "get_video_bitrate", 0, { {-1,{0}} } },
{ MP_CMD_GET_VIDEO_RESOLUTION, "get_video_resolution", 0, { {-1,{0}} } },
{ MP_CMD_GET_AUDIO_CODEC, "get_audio_codec", 0, { {-1,{0}} } },
{ MP_CMD_GET_AUDIO_BITRATE, "get_audio_bitrate", 0, { {-1,{0}} } },
{ MP_CMD_GET_AUDIO_SAMPLES, "get_audio_samples", 0, { {-1,{0}} } },
{ MP_CMD_GET_META_TITLE, "get_meta_title", 0, { {-1,{0}} } },
{ MP_CMD_GET_META_ARTIST, "get_meta_artist", 0, { {-1,{0}} } },
{ MP_CMD_GET_META_ALBUM, "get_meta_album", 0, { {-1,{0}} } },
{ MP_CMD_GET_META_YEAR, "get_meta_year", 0, { {-1,{0}} } },
{ MP_CMD_GET_META_COMMENT, "get_meta_comment", 0, { {-1,{0}} } },
{ MP_CMD_GET_META_TRACK, "get_meta_track", 0, { {-1,{0}} } },
{ MP_CMD_GET_META_GENRE, "get_meta_genre", 0, { {-1,{0}} } },
{ MP_CMD_SWITCH_AUDIO, "switch_audio", 0, { { MP_CMD_ARG_INT,{-1} }, {-1,{0}} } },
{ MP_CMD_SWITCH_ANGLE, "switch_angle", 0, { { MP_CMD_ARG_INT,{-1} }, {-1,{0}} } },
{ MP_CMD_SWITCH_TITLE, "switch_title", 0, { { MP_CMD_ARG_INT,{-1} }, {-1,{0}} } },
#ifdef CONFIG_TV
{ MP_CMD_TV_START_SCAN, "tv_start_scan", 0, { {-1,{0}} }},
{ MP_CMD_TV_STEP_CHANNEL, "tv_step_channel", 1, { { MP_CMD_ARG_INT ,{0}}, {-1,{0}} }},
{ MP_CMD_TV_STEP_NORM, "tv_step_norm",0, { {-1,{0}} } },
{ MP_CMD_TV_STEP_CHANNEL_LIST, "tv_step_chanlist", 0, { {-1,{0}} } },
{ MP_CMD_TV_SET_CHANNEL, "tv_set_channel", 1, { { MP_CMD_ARG_STRING, {0}}, {-1,{0}} }},
{ MP_CMD_TV_LAST_CHANNEL, "tv_last_channel", 0, { {-1,{0}} } },
{ MP_CMD_TV_SET_FREQ, "tv_set_freq", 1, { {MP_CMD_ARG_FLOAT,{0}}, {-1,{0}} } },
{ MP_CMD_TV_STEP_FREQ, "tv_step_freq", 1, { {MP_CMD_ARG_FLOAT,{0}}, {-1,{0}} } },
{ MP_CMD_TV_SET_NORM, "tv_set_norm", 1, { {MP_CMD_ARG_STRING,{0}}, {-1,{0}} } },
{ MP_CMD_TV_SET_BRIGHTNESS, "tv_set_brightness", 1, { { MP_CMD_ARG_INT ,{0}}, { MP_CMD_ARG_INT,{1} }, {-1,{0}} }},
{ MP_CMD_TV_SET_CONTRAST, "tv_set_contrast", 1, { { MP_CMD_ARG_INT ,{0}}, { MP_CMD_ARG_INT,{1} }, {-1,{0}} }},
{ MP_CMD_TV_SET_HUE, "tv_set_hue", 1, { { MP_CMD_ARG_INT ,{0}}, { MP_CMD_ARG_INT,{1} }, {-1,{0}} }},
{ MP_CMD_TV_SET_SATURATION, "tv_set_saturation", 1, { { MP_CMD_ARG_INT ,{0}}, { MP_CMD_ARG_INT,{1} }, {-1,{0}} }},
#endif
{ MP_CMD_SUB_FORCED_ONLY, "forced_subs_only", 0, { {MP_CMD_ARG_INT,{-1}}, {-1,{0}} } },
#ifdef CONFIG_DVBIN
{ MP_CMD_DVB_SET_CHANNEL, "dvb_set_channel", 2, { {MP_CMD_ARG_INT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}}}},
#endif
{ MP_CMD_SWITCH_RATIO, "switch_ratio", 0, { {MP_CMD_ARG_FLOAT,{0}}, {-1,{0}} } },
{ MP_CMD_VO_FULLSCREEN, "vo_fullscreen", 0, { {MP_CMD_ARG_INT,{-1}}, {-1,{0}} } },
{ MP_CMD_VO_ONTOP, "vo_ontop", 0, { {MP_CMD_ARG_INT,{-1}}, {-1,{0}} } },
{ MP_CMD_FILE_FILTER, "file_filter", 1, { { MP_CMD_ARG_INT, {0}}, {-1,{0}}}},
{ MP_CMD_VO_ROOTWIN, "vo_rootwin", 0, { {MP_CMD_ARG_INT,{-1}}, {-1,{0}} } },
{ MP_CMD_VO_BORDER, "vo_border", 0, { {MP_CMD_ARG_INT,{-1}}, {-1,{0}} } },
{ MP_CMD_SCREENSHOT, "screenshot", 0, { {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_PANSCAN, "panscan",1, { {MP_CMD_ARG_FLOAT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_SWITCH_VSYNC, "switch_vsync", 0, { {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_LOADFILE, "loadfile", 1, { {MP_CMD_ARG_STRING, {0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_LOADLIST, "loadlist", 1, { {MP_CMD_ARG_STRING, {0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_RUN, "run", 1, { {MP_CMD_ARG_STRING,{0}}, {-1,{0}} } },
{ MP_CMD_CAPTURING, "capturing", 0, { {-1,{0}} } },
{ MP_CMD_VF_CHANGE_RECTANGLE, "change_rectangle", 2, { {MP_CMD_ARG_INT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}}}},
{ MP_CMD_TV_TELETEXT_ADD_DEC, "teletext_add_dec", 1, { {MP_CMD_ARG_STRING,{0}}, {-1,{0}} } },
{ MP_CMD_TV_TELETEXT_GO_LINK, "teletext_go_link", 1, { {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
#ifdef CONFIG_DVDNAV
{ MP_CMD_DVDNAV, "dvdnav", 1, { {MP_CMD_ARG_STRING, {0}}, {-1,{0}} } },
#endif
#ifdef CONFIG_MENU
{ MP_CMD_MENU, "menu",1, { {MP_CMD_ARG_STRING, {0}}, {-1,{0}} } },
{ MP_CMD_SET_MENU, "set_menu",1, { {MP_CMD_ARG_STRING, {0}}, {MP_CMD_ARG_STRING, {0}}, {-1,{0}} } },
{ MP_CMD_CHELP, "help", 0, { {-1,{0}} } },
{ MP_CMD_CEXIT, "exit", 0, { {-1,{0}} } },
{ MP_CMD_CHIDE, "hide", 0, { {MP_CMD_ARG_INT,{3000}}, {-1,{0}} } },
#endif
{ MP_CMD_GET_VO_FULLSCREEN, "get_vo_fullscreen", 0, { {-1,{0}} } },
{ MP_CMD_GET_SUB_VISIBILITY, "get_sub_visibility", 0, { {-1,{0}} } },
{ MP_CMD_KEYDOWN_EVENTS, "key_down_event", 1, { {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_SET_PROPERTY, "set_property", 2, { {MP_CMD_ARG_STRING, {0}}, {MP_CMD_ARG_STRING, {0}}, {-1,{0}} } },
{ MP_CMD_SET_PROPERTY_OSD, "set_property_osd", 2, { {MP_CMD_ARG_STRING, {0}}, {MP_CMD_ARG_STRING, {0}}, {-1,{0}} } },
{ MP_CMD_GET_PROPERTY, "get_property", 1, { {MP_CMD_ARG_STRING, {0}}, {-1,{0}} } },
{ MP_CMD_STEP_PROPERTY, "step_property", 1, { {MP_CMD_ARG_STRING, {0}}, {MP_CMD_ARG_FLOAT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_STEP_PROPERTY_OSD, "step_property_osd", 1, { {MP_CMD_ARG_STRING, {0}}, {MP_CMD_ARG_FLOAT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_SEEK_CHAPTER, "seek_chapter", 1, { {MP_CMD_ARG_INT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_SET_MOUSE_POS, "set_mouse_pos", 2, { {MP_CMD_ARG_INT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
{ MP_CMD_AF_SWITCH, "af_switch", 1, { {MP_CMD_ARG_STRING, {0}}, {-1,{0}} } },
{ MP_CMD_AF_ADD, "af_add", 1, { {MP_CMD_ARG_STRING, {0}}, {-1,{0}} } },
{ MP_CMD_AF_DEL, "af_del", 1, { {MP_CMD_ARG_STRING, {0}}, {-1,{0}} } },
{ MP_CMD_AF_CLR, "af_clr", 0, { {-1,{0}} } },
{ MP_CMD_AF_CMDLINE, "af_cmdline", 2, { {MP_CMD_ARG_STRING, {0}}, {MP_CMD_ARG_STRING, {0}}, {-1,{0}} } },
{ 0, NULL, 0, {} }
};
/// The names of the keys as used in input.conf
/// If you add some new keys, you also need to add them here
static const mp_key_name_t key_names[] = {
{ ' ', "SPACE" },
{ '#', "SHARP" },
{ KEY_ENTER, "ENTER" },
{ KEY_TAB, "TAB" },
{ KEY_BACKSPACE, "BS" },
{ KEY_DELETE, "DEL" },
{ KEY_INSERT, "INS" },
{ KEY_HOME, "HOME" },
{ KEY_END, "END" },
{ KEY_PAGE_UP, "PGUP" },
{ KEY_PAGE_DOWN, "PGDWN" },
{ KEY_ESC, "ESC" },
{ KEY_RIGHT, "RIGHT" },
{ KEY_LEFT, "LEFT" },
{ KEY_DOWN, "DOWN" },
{ KEY_UP, "UP" },
{ KEY_F+1, "F1" },
{ KEY_F+2, "F2" },
{ KEY_F+3, "F3" },
{ KEY_F+4, "F4" },
{ KEY_F+5, "F5" },
{ KEY_F+6, "F6" },
{ KEY_F+7, "F7" },
{ KEY_F+8, "F8" },
{ KEY_F+9, "F9" },
{ KEY_F+10, "F10" },
{ KEY_F+11, "F11" },
{ KEY_F+12, "F12" },
{ KEY_KP0, "KP0" },
{ KEY_KP1, "KP1" },
{ KEY_KP2, "KP2" },
{ KEY_KP3, "KP3" },
{ KEY_KP4, "KP4" },
{ KEY_KP5, "KP5" },
{ KEY_KP6, "KP6" },
{ KEY_KP7, "KP7" },
{ KEY_KP8, "KP8" },
{ KEY_KP9, "KP9" },
{ KEY_KPDEL, "KP_DEL" },
{ KEY_KPDEC, "KP_DEC" },
{ KEY_KPINS, "KP_INS" },
{ KEY_KPENTER, "KP_ENTER" },
{ MOUSE_BTN0, "MOUSE_BTN0" },
{ MOUSE_BTN1, "MOUSE_BTN1" },
{ MOUSE_BTN2, "MOUSE_BTN2" },
{ MOUSE_BTN3, "MOUSE_BTN3" },
{ MOUSE_BTN4, "MOUSE_BTN4" },
{ MOUSE_BTN5, "MOUSE_BTN5" },
{ MOUSE_BTN6, "MOUSE_BTN6" },
{ MOUSE_BTN7, "MOUSE_BTN7" },
{ MOUSE_BTN8, "MOUSE_BTN8" },
{ MOUSE_BTN9, "MOUSE_BTN9" },
{ MOUSE_BTN0_DBL, "MOUSE_BTN0_DBL" },
{ MOUSE_BTN1_DBL, "MOUSE_BTN1_DBL" },
{ MOUSE_BTN2_DBL, "MOUSE_BTN2_DBL" },
{ MOUSE_BTN3_DBL, "MOUSE_BTN3_DBL" },
{ MOUSE_BTN4_DBL, "MOUSE_BTN4_DBL" },
{ MOUSE_BTN5_DBL, "MOUSE_BTN5_DBL" },
{ MOUSE_BTN6_DBL, "MOUSE_BTN6_DBL" },
{ MOUSE_BTN7_DBL, "MOUSE_BTN7_DBL" },
{ MOUSE_BTN8_DBL, "MOUSE_BTN8_DBL" },
{ MOUSE_BTN9_DBL, "MOUSE_BTN9_DBL" },
{ JOY_AXIS1_MINUS, "JOY_UP" },
{ JOY_AXIS1_PLUS, "JOY_DOWN" },
{ JOY_AXIS0_MINUS, "JOY_LEFT" },
{ JOY_AXIS0_PLUS, "JOY_RIGHT" },
{ JOY_AXIS0_PLUS, "JOY_AXIS0_PLUS" },
{ JOY_AXIS0_MINUS, "JOY_AXIS0_MINUS" },
{ JOY_AXIS1_PLUS, "JOY_AXIS1_PLUS" },
{ JOY_AXIS1_MINUS, "JOY_AXIS1_MINUS" },
{ JOY_AXIS2_PLUS, "JOY_AXIS2_PLUS" },
{ JOY_AXIS2_MINUS, "JOY_AXIS2_MINUS" },
{ JOY_AXIS3_PLUS, "JOY_AXIS3_PLUS" },
{ JOY_AXIS3_MINUS, "JOY_AXIS3_MINUS" },
{ JOY_AXIS4_PLUS, "JOY_AXIS4_PLUS" },
{ JOY_AXIS4_MINUS, "JOY_AXIS4_MINUS" },
{ JOY_AXIS5_PLUS, "JOY_AXIS5_PLUS" },
{ JOY_AXIS5_MINUS, "JOY_AXIS5_MINUS" },
{ JOY_AXIS6_PLUS, "JOY_AXIS6_PLUS" },
{ JOY_AXIS6_MINUS, "JOY_AXIS6_MINUS" },
{ JOY_AXIS7_PLUS, "JOY_AXIS7_PLUS" },
{ JOY_AXIS7_MINUS, "JOY_AXIS7_MINUS" },
{ JOY_AXIS8_PLUS, "JOY_AXIS8_PLUS" },
{ JOY_AXIS8_MINUS, "JOY_AXIS8_MINUS" },
{ JOY_AXIS9_PLUS, "JOY_AXIS9_PLUS" },
{ JOY_AXIS9_MINUS, "JOY_AXIS9_MINUS" },
{ JOY_BTN0, "JOY_BTN0" },
{ JOY_BTN1, "JOY_BTN1" },
{ JOY_BTN2, "JOY_BTN2" },
{ JOY_BTN3, "JOY_BTN3" },
{ JOY_BTN4, "JOY_BTN4" },
{ JOY_BTN5, "JOY_BTN5" },
{ JOY_BTN6, "JOY_BTN6" },
{ JOY_BTN7, "JOY_BTN7" },
{ JOY_BTN8, "JOY_BTN8" },
{ JOY_BTN9, "JOY_BTN9" },
{ AR_PLAY, "AR_PLAY" },
{ AR_PLAY_HOLD, "AR_PLAY_HOLD" },
{ AR_NEXT, "AR_NEXT" },
{ AR_NEXT_HOLD, "AR_NEXT_HOLD" },
{ AR_PREV, "AR_PREV" },
{ AR_PREV_HOLD, "AR_PREV_HOLD" },
{ AR_MENU, "AR_MENU" },
{ AR_MENU_HOLD, "AR_MENU_HOLD" },
{ AR_VUP, "AR_VUP" },
{ AR_VDOWN, "AR_VDOWN" },
{ KEY_POWER, "POWER" },
{ KEY_MENU, "MENU" },
{ KEY_PLAY, "PLAY" },
{ KEY_PAUSE, "PAUSE" },
{ KEY_PLAYPAUSE, "PLAYPAUSE" },
{ KEY_STOP, "STOP" },
{ KEY_FORWARD, "FORWARD" },
{ KEY_REWIND, "REWIND" },
{ KEY_NEXT, "NEXT" },
{ KEY_PREV, "PREV" },
{ KEY_VOLUME_UP, "VOLUME_UP" },
{ KEY_VOLUME_DOWN, "VOLUME_DOWN" },
{ KEY_MUTE, "MUTE" },
// These are kept for backward compatibility
{ KEY_PAUSE, "XF86_PAUSE" },
{ KEY_STOP, "XF86_STOP" },
{ KEY_PREV, "XF86_PREV" },
{ KEY_NEXT, "XF86_NEXT" },
{ KEY_CLOSE_WIN, "CLOSE_WIN" },
{ 0, NULL }
};
struct mp_key_name modifier_names[] = {
{ KEY_MODIFIER_SHIFT, "Shift" },
{ KEY_MODIFIER_CTRL, "Ctrl" },
{ KEY_MODIFIER_ALT, "Alt" },
{ KEY_MODIFIER_META, "Meta" },
{ 0 }
};
#define KEY_MODIFIER_MASK (KEY_MODIFIER_SHIFT | KEY_MODIFIER_CTRL | KEY_MODIFIER_ALT | KEY_MODIFIER_META)
// This is the default binding. The content of input.conf overrides these.
// The first arg is a null terminated array of key codes.
// The second is the command
static const mp_cmd_bind_t def_cmd_binds[] = {
{ { MOUSE_BTN3, 0 }, "seek 10" },
{ { MOUSE_BTN4, 0 }, "seek -10" },
{ { MOUSE_BTN5, 0 }, "volume 1" },
{ { MOUSE_BTN6, 0 }, "volume -1" },
#ifdef CONFIG_DVDNAV
{ { KEY_KP8, 0 }, "dvdnav up" }, // up
{ { KEY_KP2, 0 }, "dvdnav down" }, // down
{ { KEY_KP4, 0 }, "dvdnav left" }, // left
{ { KEY_KP6, 0 }, "dvdnav right" }, // right
{ { KEY_KP5, 0 }, "dvdnav menu" }, // menu
{ { KEY_KPENTER, 0 }, "dvdnav select" }, // select
{ { MOUSE_BTN0, 0 }, "dvdnav mouse" }, //select
{ { KEY_KP7, 0 }, "dvdnav prev" }, // previous menu
#endif
{ { KEY_RIGHT, 0 }, "seek 10" },
{ { KEY_LEFT, 0 }, "seek -10" },
{ { KEY_MODIFIER_SHIFT + KEY_RIGHT, 0 }, "seek 1 0 1" },
{ { KEY_MODIFIER_SHIFT + KEY_LEFT, 0 }, "seek -1 0 1" },
{ { KEY_UP, 0 }, "seek 60" },
{ { KEY_DOWN, 0 }, "seek -60" },
{ { KEY_MODIFIER_SHIFT + KEY_UP, 0 }, "seek 5 0 1" },
{ { KEY_MODIFIER_SHIFT + KEY_DOWN, 0 }, "seek -5 0 1" },
{ { KEY_PAGE_UP, 0 }, "seek 600" },
{ { KEY_PAGE_DOWN, 0 }, "seek -600" },
{ { '+', 0 }, "audio_delay 0.100" },
{ { '-', 0 }, "audio_delay -0.100" },
{ { '[', 0 }, "speed_mult 0.9091" },
{ { ']', 0 }, "speed_mult 1.1" },
{ { '{', 0 }, "speed_mult 0.5" },
{ { '}', 0 }, "speed_mult 2.0" },
{ { KEY_BACKSPACE, 0 }, "speed_set 1.0" },
{ { 'q', 0 }, "quit" },
{ { KEY_ESC, 0 }, "quit" },
{ { 'p', 0 }, "pause" },
{ { ' ', 0 }, "pause" },
{ { '.', 0 }, "frame_step" },
{ { KEY_HOME, 0 }, "pt_up_step 1" },
{ { KEY_END, 0 }, "pt_up_step -1" },
{ { '>', 0 }, "pt_step 1" },
{ { KEY_ENTER, 0 }, "pt_step 1 1" },
{ { '<', 0 }, "pt_step -1" },
{ { KEY_INS, 0 }, "alt_src_step 1" },
{ { KEY_DEL, 0 }, "alt_src_step -1" },
{ { 'o', 0 }, "osd" },
{ { 'I', 0 }, "osd_show_property_text \"${filename}\"" },
{ { 'P', 0 }, "osd_show_progression" },
{ { 'z', 0 }, "sub_delay -0.1" },
{ { 'x', 0 }, "sub_delay +0.1" },
{ { 'g', 0 }, "sub_step -1" },
{ { 'y', 0 }, "sub_step +1" },
{ { '9', 0 }, "volume -1" },
{ { '/', 0 }, "volume -1" },
{ { '0', 0 }, "volume 1" },
{ { '*', 0 }, "volume 1" },
{ { '(', 0 }, "balance -0.1" },
{ { ')', 0 }, "balance 0.1" },
{ { 'm', 0 }, "mute" },
{ { '1', 0 }, "contrast -1" },
{ { '2', 0 }, "contrast 1" },
{ { '3', 0 }, "brightness -1" },
{ { '4', 0 }, "brightness 1" },
{ { '5', 0 }, "hue -1" },
{ { '6', 0 }, "hue 1" },
{ { '7', 0 }, "saturation -1" },
{ { '8', 0 }, "saturation 1" },
{ { 'd', 0 }, "frame_drop" },
{ { 'D', 0 }, "step_property_osd deinterlace" },
{ { 'c', 0 }, "step_property_osd yuv_colorspace" },
{ { 'r', 0 }, "sub_pos -1" },
{ { 't', 0 }, "sub_pos +1" },
{ { 'a', 0 }, "sub_alignment" },
{ { 'v', 0 }, "sub_visibility" },
{ { 'j', 0 }, "sub_select" },
{ { 'J', 0 }, "sub_select -3" },
{ { 'F', 0 }, "forced_subs_only" },
{ { '#', 0 }, "switch_audio" },
{ { '_', 0 }, "step_property switch_video" },
{ { KEY_TAB, 0 }, "step_property switch_program" },
{ { 'i', 0 }, "edl_mark" },
#ifdef CONFIG_TV
{ { 'h', 0 }, "tv_step_channel 1" },
{ { 'k', 0 }, "tv_step_channel -1" },
{ { 'n', 0 }, "tv_step_norm" },
{ { 'u', 0 }, "tv_step_chanlist" },
#endif
{ { 'X', 0 }, "step_property teletext_mode 1" },
{ { 'W', 0 }, "step_property teletext_page 1" },
{ { 'Q', 0 }, "step_property teletext_page -1" },
#ifdef CONFIG_JOYSTICK
{ { JOY_AXIS0_PLUS, 0 }, "seek 10" },
{ { JOY_AXIS0_MINUS, 0 }, "seek -10" },
{ { JOY_AXIS1_MINUS, 0 }, "seek 60" },
{ { JOY_AXIS1_PLUS, 0 }, "seek -60" },
{ { JOY_BTN0, 0 }, "pause" },
{ { JOY_BTN1, 0 }, "osd" },
{ { JOY_BTN2, 0 }, "volume 1"},
{ { JOY_BTN3, 0 }, "volume -1"},
#endif
#ifdef CONFIG_APPLE_REMOTE
{ { AR_PLAY, 0}, "pause" },
{ { AR_PLAY_HOLD, 0}, "quit" },
{ { AR_NEXT, 0 }, "seek 30" },
{ { AR_NEXT_HOLD, 0 }, "seek 120" },
{ { AR_PREV, 0 }, "seek -10" },
{ { AR_PREV_HOLD, 0 }, "seek -120" },
{ { AR_MENU, 0 }, "osd" },
{ { AR_MENU_HOLD, 0 }, "mute" },
{ { AR_VUP, 0 }, "volume 1"},
{ { AR_VDOWN, 0 }, "volume -1"},
#endif
{ { 'T', 0 }, "vo_ontop" },
{ { 'f', 0 }, "vo_fullscreen" },
{ { 'C', 0 }, "step_property_osd capturing" },
{ { 's', 0 }, "screenshot 0" },
{ { 'S', 0 }, "screenshot 1" },
{ { 'w', 0 }, "panscan -0.1" },
{ { 'e', 0 }, "panscan +0.1" },
{ { KEY_POWER, 0 }, "quit" },
{ { KEY_MENU, 0 }, "osd" },
{ { KEY_PLAY, 0 }, "pause" },
{ { KEY_PAUSE, 0 }, "pause" },
{ { KEY_PLAYPAUSE, 0 }, "pause" },
{ { KEY_STOP, 0 }, "quit" },
{ { KEY_FORWARD, 0 }, "seek 60" },
{ { KEY_REWIND, 0 }, "seek -60" },
{ { KEY_NEXT, 0 }, "pt_step 1" },
{ { KEY_PREV, 0 }, "pt_step -1" },
{ { KEY_VOLUME_UP, 0 }, "volume 1" },
{ { KEY_VOLUME_DOWN, 0 }, "volume -1" },
{ { KEY_MUTE, 0 }, "mute" },
{ { KEY_CLOSE_WIN, 0 }, "quit" },
{ { '!', 0 }, "seek_chapter -1" },
{ { '@', 0 }, "seek_chapter 1" },
{ { 'A', 0 }, "switch_angle 1" },
{ { 'U', 0 }, "stop" },
{ { 0 }, NULL }
};
#ifndef MP_MAX_KEY_FD
#define MP_MAX_KEY_FD 10
#endif
#ifndef MP_MAX_CMD_FD
#define MP_MAX_CMD_FD 10
#endif
#define CMD_QUEUE_SIZE 100
typedef struct mp_input_fd {
int fd;
union {
mp_key_func_t key;
mp_cmd_func_t cmd;
} read_func;
mp_close_func_t close_func;
void *ctx;
unsigned eof : 1;
unsigned drop : 1;
unsigned dead : 1;
unsigned got_cmd : 1;
unsigned no_select : 1;
// These fields are for the cmd fds.
char* buffer;
int pos,size;
} mp_input_fd_t;
typedef struct mp_cmd_filter mp_cmd_filter_t;
struct mp_cmd_filter {
mp_input_cmd_filter filter;
void* ctx;
mp_cmd_filter_t* next;
};
typedef struct mp_cmd_bind_section mp_cmd_bind_section_t;
struct mp_cmd_bind_section {
mp_cmd_bind_t* cmd_binds;
char* section;
mp_cmd_bind_section_t* next;
};
struct input_ctx {
// Autorepeat stuff
short ar_state;
mp_cmd_t *ar_cmd;
unsigned int last_ar;
// Autorepeat config
unsigned int ar_delay;
unsigned int ar_rate;
// these are the keys currently down
int key_down[MP_MAX_KEY_DOWN];
unsigned int num_key_down;
unsigned int last_key_down;
bool default_bindings;
// List of command binding sections
mp_cmd_bind_section_t *cmd_bind_sections;
// Name of currently used command section
char *section;
// The command binds of current section
mp_cmd_bind_t *cmd_binds;
mp_cmd_bind_t *cmd_binds_default;
mp_input_fd_t key_fds[MP_MAX_KEY_FD];
unsigned int num_key_fd;
mp_input_fd_t cmd_fds[MP_MAX_CMD_FD];
unsigned int num_cmd_fd;
mp_cmd_t *cmd_queue[CMD_QUEUE_SIZE];
unsigned int cmd_queue_length, cmd_queue_start, cmd_queue_end;
};
static mp_cmd_filter_t* cmd_filters = NULL;
// Callback to allow the menu filter to grab the incoming keys
int (*mp_input_key_cb)(int code) = NULL;
int async_quit_request;
static int print_key_list(m_option_t* cfg);
static int print_cmd_list(m_option_t* cfg);
// Our command line options
static const m_option_t input_conf[] = {
OPT_STRING("conf", input.config_file, CONF_GLOBAL),
OPT_INT("ar-delay",input.ar_delay, CONF_GLOBAL),
OPT_INT("ar-rate", input.ar_rate, CONF_GLOBAL),
{ "keylist", print_key_list, CONF_TYPE_FUNC, CONF_GLOBAL, 0, 0, NULL },
{ "cmdlist", print_cmd_list, CONF_TYPE_FUNC, CONF_GLOBAL, 0, 0, NULL },
OPT_STRING("js-dev", input.js_dev, CONF_GLOBAL),
OPT_STRING("ar-dev", input.ar_dev, CONF_GLOBAL),
OPT_STRING("file", input.in_file, CONF_GLOBAL),
OPT_MAKE_FLAGS("default-bindings", input.default_bindings, CONF_GLOBAL),
{ NULL, NULL, 0, 0, 0, 0, NULL}
};
static const m_option_t mp_input_opts[] = {
{ "input", &input_conf, CONF_TYPE_SUBCONFIG, 0, 0, 0, NULL},
OPT_MAKE_FLAGS("joystick", input.use_joystick, CONF_GLOBAL),
OPT_MAKE_FLAGS("lirc", input.use_lirc, CONF_GLOBAL),
OPT_MAKE_FLAGS("lircc", input.use_lircc, CONF_GLOBAL),
OPT_MAKE_FLAGS("ar", input.use_ar, CONF_GLOBAL),
{ NULL, NULL, 0, 0, 0, 0, NULL}
};
static int default_cmd_func(int fd,char* buf, int l);
static char *get_key_name(int key)
{
char *ret = talloc_strdup(NULL, "");
for (int i = 0; modifier_names[i].name; i++) {
if (modifier_names[i].key & key) {
ret = talloc_asprintf_append_buffer(ret, "%s+",
modifier_names[i].name);
key -= modifier_names[i].key;
}
}
for (int i = 0; key_names[i].name != NULL; i++) {
if (key_names[i].key == key)
return talloc_asprintf_append_buffer(ret, "%s", key_names[i].name);
}
if (isascii(key))
return talloc_asprintf_append_buffer(ret, "%c", key);
// Print the hex key code
return talloc_asprintf_append_buffer(ret, "%#-8x", key);
}
int mp_input_add_cmd_fd(struct input_ctx *ictx, int fd, int select,
mp_cmd_func_t read_func, mp_close_func_t close_func)
{
if (ictx->num_cmd_fd == MP_MAX_CMD_FD) {
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Too many command file descriptors, cannot register file descriptor %d.\n",fd);
return 0;
}
if (select && fd < 0) {
mp_msg(MSGT_INPUT, MSGL_ERR, "Invalid fd %i in mp_input_add_cmd_fd", fd);
return 0;
}
ictx->cmd_fds[ictx->num_cmd_fd] = (struct mp_input_fd){
.fd = fd,
.read_func.cmd = read_func ? read_func : default_cmd_func,
.close_func = close_func,
.no_select = !select
};
ictx->num_cmd_fd++;
return 1;
}
void mp_input_rm_cmd_fd(struct input_ctx *ictx, int fd)
{
struct mp_input_fd *cmd_fds = ictx->cmd_fds;
unsigned int i;
for (i = 0; i < ictx->num_cmd_fd; i++) {
if(cmd_fds[i].fd == fd)
break;
}
if (i == ictx->num_cmd_fd)
return;
if(cmd_fds[i].close_func)
cmd_fds[i].close_func(cmd_fds[i].fd);
talloc_free(cmd_fds[i].buffer);
if (i + 1 < ictx->num_cmd_fd)
memmove(&cmd_fds[i], &cmd_fds[i+1],
(ictx->num_cmd_fd - i - 1) * sizeof(mp_input_fd_t));
ictx->num_cmd_fd--;
}
void mp_input_rm_key_fd(struct input_ctx *ictx, int fd)
{
struct mp_input_fd *key_fds = ictx->key_fds;
unsigned int i;
for (i = 0; i < ictx->num_key_fd; i++) {
if(key_fds[i].fd == fd)
break;
}
if (i == ictx->num_key_fd)
return;
if(key_fds[i].close_func)
key_fds[i].close_func(key_fds[i].fd);
if(i + 1 < ictx->num_key_fd)
memmove(&key_fds[i], &key_fds[i+1],
(ictx->num_key_fd - i - 1) * sizeof(mp_input_fd_t));
ictx->num_key_fd--;
}
int mp_input_add_key_fd(struct input_ctx *ictx, int fd, int select,
mp_key_func_t read_func, mp_close_func_t close_func,
void *ctx)
{
if (ictx->num_key_fd == MP_MAX_KEY_FD) {
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Too many key file descriptors, cannot register file descriptor %d.\n",fd);
return 0;
}
if (select && fd < 0) {
mp_msg(MSGT_INPUT, MSGL_ERR, "Invalid fd %i in mp_input_add_key_fd", fd);
return 0;
}
ictx->key_fds[ictx->num_key_fd] = (struct mp_input_fd){
.fd = fd,
.read_func.key = read_func,
.close_func = close_func,
.no_select = !select,
.ctx = ctx,
};
ictx->num_key_fd++;
return 1;
}
int mp_input_parse_and_queue_cmds(struct input_ctx *ictx, const char *str)
{
int cmd_num = 0;
while (*str == '\n' || *str == '\r' || *str == ' ')
++str;
while (*str) {
mp_cmd_t *cmd;
size_t len = strcspn(str, "\r\n");
char *cmdbuf = talloc_size(NULL, len+1);
av_strlcpy(cmdbuf, str, len+1);
cmd = mp_input_parse_cmd(cmdbuf);
if (cmd) {
mp_input_queue_cmd(ictx, cmd);
++cmd_num;
}
str += len;
while (*str == '\n' || *str == '\r' || *str == ' ')
++str;
talloc_free(cmdbuf);
}
return cmd_num;
}
mp_cmd_t*
mp_input_parse_cmd(char* str) {
int i,l;
int pausing = 0;
char *ptr,*e;
const mp_cmd_t *cmd_def;
#ifdef MP_DEBUG
assert(str != NULL);
#endif
// Ignore heading spaces.
while (str[0] == ' ' || str[0] == '\t')
++str;
if (strncmp(str, "pausing ", 8) == 0) {
pausing = 1;
str = &str[8];
} else if (strncmp(str, "pausing_keep ", 13) == 0) {
pausing = 2;
str = &str[13];
} else if (strncmp(str, "pausing_toggle ", 15) == 0) {
pausing = 3;
str = &str[15];
} else if (strncmp(str, "pausing_keep_force ", 19) == 0) {
pausing = 4;
str = &str[19];
}
for(ptr = str ; ptr[0] != '\0' && ptr[0] != '\t' && ptr[0] != ' ' ; ptr++)
/* NOTHING */;
if(ptr[0] != '\0')
l = ptr-str;
else
l = strlen(str);
if(l == 0)
return NULL;
for(i=0; mp_cmds[i].name != NULL; i++) {
if(strncasecmp(mp_cmds[i].name,str,l) == 0)
break;
}
if(mp_cmds[i].name == NULL)
return NULL;
cmd_def = &mp_cmds[i];
mp_cmd_t *cmd = talloc_ptrtype(NULL, cmd);
*cmd = (mp_cmd_t){
.id = cmd_def->id,
.name = talloc_strdup(cmd, cmd_def->name),
.pausing = pausing,
};
ptr = str;
for(i=0; ptr && i < MP_CMD_MAX_ARGS; i++) {
while(ptr[0] != ' ' && ptr[0] != '\t' && ptr[0] != '\0') ptr++;
if(ptr[0] == '\0') break;
while(ptr[0] == ' ' || ptr[0] == '\t') ptr++;
if(ptr[0] == '\0' || ptr[0] == '#') break;
cmd->args[i].type = cmd_def->args[i].type;
switch(cmd_def->args[i].type) {
case MP_CMD_ARG_INT:
errno = 0;
cmd->args[i].v.i = atoi(ptr);
if(errno != 0) {
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Command %s: argument %d isn't an integer.\n",cmd_def->name,i+1);
ptr = NULL;
}
break;
case MP_CMD_ARG_FLOAT:
errno = 0;
cmd->args[i].v.f = atof(ptr);
if(errno != 0) {
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Command %s: argument %d isn't a float.\n",cmd_def->name,i+1);
ptr = NULL;
}
break;
case MP_CMD_ARG_STRING: {
char term;
char* ptr2 = ptr, *start;
if(ptr[0] == '\'' || ptr[0] == '"') {
term = ptr[0];
ptr2++;
} else
term = ' ';
start = ptr2;
while(1) {
e = strchr(ptr2,term);
if(!e) break;
if(e <= ptr2 || *(e - 1) != '\\') break;
ptr2 = e + 1;
}
if(term != ' ' && (!e || e[0] == '\0')) {
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Command %s: argument %d is unterminated.\n",cmd_def->name,i+1);
ptr = NULL;
break;
} else if(!e) e = ptr+strlen(ptr);
l = e-start;
ptr2 = start;
for(e = strchr(ptr2,'\\') ; e && e<start+l ; e = strchr(ptr2,'\\')) {
memmove(e,e+1,strlen(e));
ptr2 = e + 1;
l--;
}
cmd->args[i].v.s = talloc_strndup(cmd, start, l);
if(term != ' ') ptr += l+2;
} break;
case -1:
ptr = NULL;
break;
default :
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Unknown argument %d\n",i);
}
}
cmd->nargs = i;
if(cmd_def->nargs > cmd->nargs) {
/* mp_msg(MSGT_INPUT,MSGL_ERR,"Got command '%s' but\n",str); */
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Command %s requires at least %d arguments, we found only %d so far.\n",cmd_def->name,cmd_def->nargs,cmd->nargs);
mp_cmd_free(cmd);
return NULL;
}
for( ; i < MP_CMD_MAX_ARGS && cmd_def->args[i].type != -1 ; i++) {
memcpy(&cmd->args[i],&cmd_def->args[i],sizeof(mp_cmd_arg_t));
if(cmd_def->args[i].type == MP_CMD_ARG_STRING && cmd_def->args[i].v.s != NULL)
cmd->args[i].v.s = talloc_strdup(cmd, cmd_def->args[i].v.s);
}
if(i < MP_CMD_MAX_ARGS)
cmd->args[i].type = -1;
return cmd;
}
#define MP_CMD_MAX_SIZE 4096
static int read_cmd(mp_input_fd_t* mp_fd, char** ret)
{
char* end;
(*ret) = NULL;
// Allocate the buffer if it doesn't exist
if(!mp_fd->buffer) {
mp_fd->buffer = talloc_size(NULL, MP_CMD_MAX_SIZE);
mp_fd->pos = 0;
mp_fd->size = MP_CMD_MAX_SIZE;
}
// Get some data if needed/possible
while (!mp_fd->got_cmd && !mp_fd->eof && (mp_fd->size - mp_fd->pos > 1) ) {
int r = mp_fd->read_func.cmd(mp_fd->fd, mp_fd->buffer+mp_fd->pos,
mp_fd->size - 1 - mp_fd->pos);
// Error ?
if(r < 0) {
switch(r) {
case MP_INPUT_ERROR:
case MP_INPUT_DEAD:
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Error while reading command file descriptor %d: %s\n",mp_fd->fd,strerror(errno));
case MP_INPUT_NOTHING:
return r;
case MP_INPUT_RETRY:
continue;
}
// EOF ?
} else if(r == 0) {
mp_fd->eof = 1;
break;
}
mp_fd->pos += r;
break;
}
mp_fd->got_cmd = 0;
while(1) {
int l = 0;
// Find the cmd end
mp_fd->buffer[mp_fd->pos] = '\0';
end = strchr(mp_fd->buffer,'\r');
if (end) *end = '\n';
end = strchr(mp_fd->buffer,'\n');
// No cmd end ?
if(!end) {
// If buffer is full we must drop all until the next \n
if(mp_fd->size - mp_fd->pos <= 1) {
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Command buffer of file descriptor %d is full: dropping content.\n",mp_fd->fd);
mp_fd->pos = 0;
mp_fd->drop = 1;
}
break;
}
// We already have a cmd : set the got_cmd flag
else if((*ret)) {
mp_fd->got_cmd = 1;
break;
}
l = end - mp_fd->buffer;
// Not dropping : put the cmd in ret
if (!mp_fd->drop)
*ret = talloc_strndup(NULL, mp_fd->buffer, l);
else
mp_fd->drop = 0;
mp_fd->pos -= l+1;
memmove(mp_fd->buffer, end+1, mp_fd->pos);
}
if(*ret)
return 1;
else
return MP_INPUT_NOTHING;
}
static int default_cmd_func(int fd,char* buf, int l)
{
while(1) {
int r = read(fd,buf,l);
// Error ?
if(r < 0) {
if(errno == EINTR)
continue;
else if(errno == EAGAIN)
return MP_INPUT_NOTHING;
return MP_INPUT_ERROR;
// EOF ?
}
return r;
}
}
void
mp_input_add_cmd_filter(mp_input_cmd_filter func, void* ctx) {
mp_cmd_filter_t *filter = talloc_ptrtype(NULL, filter);
filter->filter = func;
filter->ctx = ctx;
filter->next = cmd_filters;
cmd_filters = filter;
}
static char *find_bind_for_key(const mp_cmd_bind_t* binds, int n,int* keys)
{
int j;
if (n <= 0) return NULL;
for(j = 0; binds[j].cmd != NULL; j++) {
int found = 1,s;
for(s = 0; s < n && binds[j].input[s] != 0; s++) {
if(binds[j].input[s] != keys[s]) {
found = 0;
break;
}
}
if(found && binds[j].input[s] == 0 && s == n)
break;
}
return binds[j].cmd;
}
static mp_cmd_bind_section_t *get_bind_section(struct input_ctx *ictx,
char *section)
{
mp_cmd_bind_section_t *bind_section = ictx->cmd_bind_sections;
if (section==NULL) section="default";
while (bind_section) {
if(strcmp(section,bind_section->section)==0) return bind_section;
if(bind_section->next==NULL) break;
bind_section=bind_section->next;
}
if(bind_section) {
bind_section->next = talloc_ptrtype(ictx, bind_section->next);
bind_section=bind_section->next;
} else {
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 = talloc_strdup(bind_section, section);
bind_section->next=NULL;
return bind_section;
}
static mp_cmd_t *get_cmd_from_keys(struct input_ctx *ictx, int n, int *keys)
{
char* cmd = NULL;
mp_cmd_t* ret;
if (ictx->cmd_binds)
cmd = find_bind_for_key(ictx->cmd_binds, n, keys);
if (ictx->cmd_binds_default && cmd == NULL)
cmd = find_bind_for_key(ictx->cmd_binds_default, n, keys);
if (ictx->default_bindings && cmd == NULL)
cmd = find_bind_for_key(def_cmd_binds,n,keys);
if(cmd == NULL) {
char *key_buf = get_key_name(keys[0]);
mp_tmsg(MSGT_INPUT,MSGL_WARN,"No bind found for key '%s'.", key_buf);
talloc_free(key_buf);
if(n > 1) {
int s;
for(s=1; s < n; s++) {
key_buf = get_key_name(keys[s]);
mp_msg(MSGT_INPUT,MSGL_WARN,"-%s", key_buf);
talloc_free(key_buf);
}
}
mp_msg(MSGT_INPUT,MSGL_WARN," \n");
return NULL;
}
if (strcmp(cmd, "ignore") == 0) return NULL;
ret = mp_input_parse_cmd(cmd);
if(!ret) {
char *key_buf = get_key_name(ictx->key_down[0]);
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Invalid command for bound key %s", key_buf);
talloc_free(key_buf);
if (ictx->num_key_down > 1) {
unsigned int s;
for(s=1; s < ictx->num_key_down; s++) {
char *key_buf = get_key_name(ictx->key_down[s]);
mp_msg(MSGT_INPUT,MSGL_ERR,"-%s", key_buf);
talloc_free(key_buf);
}
}
mp_msg(MSGT_INPUT,MSGL_ERR," : %s \n",cmd);
}
return ret;
}
static mp_cmd_t* interpret_key(struct input_ctx *ictx, int code)
{
unsigned int j;
mp_cmd_t* ret;
/* 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
* shift modifier is still kept for special keys like arrow keys.
*/
int unmod = code & ~KEY_MODIFIER_MASK;
if (unmod < 256 && unmod != KEY_ENTER && unmod != KEY_TAB)
code &= ~KEY_MODIFIER_SHIFT;
if(mp_input_key_cb) {
if (code & MP_KEY_DOWN)
return NULL;
code &= ~(MP_KEY_DOWN|MP_NO_REPEAT_KEY);
if (mp_input_key_cb(code))
return NULL;
}
if(code & MP_KEY_DOWN) {
if (ictx->num_key_down > MP_MAX_KEY_DOWN) {
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Too many key down events at the same time\n");
return NULL;
}
code &= ~MP_KEY_DOWN;
// Check if we don't already have this key as pushed
for (j = 0; j < ictx->num_key_down; j++) {
if (ictx->key_down[j] == code)
break;
}
if (j != ictx->num_key_down)
return NULL;
ictx->key_down[ictx->num_key_down] = code;
ictx->num_key_down++;
ictx->last_key_down = GetTimer();
ictx->ar_state = 0;
return NULL;
}
// key released
// Check if the key is in the down key, driver which can't send push event
// send only release event
for (j = 0; j < ictx->num_key_down; j++) {
if (ictx->key_down[j] == code)
break;
}
if (j == ictx->num_key_down) { // key was not in the down keys : add it
if (ictx->num_key_down > MP_MAX_KEY_DOWN) {
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Too many key down events at the same time\n");
return NULL;
}
ictx->key_down[ictx->num_key_down] = code;
ictx->num_key_down++;
ictx->last_key_down = 1;
}
// We ignore key from last combination
ret = ictx->last_key_down ?
get_cmd_from_keys(ictx, ictx->num_key_down, ictx->key_down)
: NULL;
// Remove the key
if (j+1 < ictx->num_key_down)
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;
return ret;
}
static mp_cmd_t *check_autorepeat(struct input_ctx *ictx)
{
// No input : autorepeat ?
if (ictx->ar_rate > 0 && ictx->ar_state >=0 && ictx->num_key_down > 0
&& !(ictx->key_down[ictx->num_key_down-1] & MP_NO_REPEAT_KEY)) {
unsigned int t = GetTimer();
// First time : wait delay
if (ictx->ar_state == 0
&& (t - ictx->last_key_down) >= ictx->ar_delay*1000) {
ictx->ar_cmd = get_cmd_from_keys(ictx, ictx->num_key_down,
ictx->key_down);
if (!ictx->ar_cmd) {
ictx->ar_state = -1;
return NULL;
}
ictx->ar_state = 1;
ictx->last_ar = t;
return mp_cmd_clone(ictx->ar_cmd);
// Then send rate / sec event
} else if (ictx->ar_state == 1
&& (t -ictx->last_ar) >= 1000000 / ictx->ar_rate) {
ictx->last_ar = t;
return mp_cmd_clone(ictx->ar_cmd);
}
}
return NULL;
}
/**
* \param time time to wait at most for an event in milliseconds
*/
static mp_cmd_t *read_events(struct input_ctx *ictx, int time)
{
int i;
int got_cmd = 0;
struct mp_input_fd *key_fds = ictx->key_fds;
struct mp_input_fd *cmd_fds = ictx->cmd_fds;
for (i = 0; i < ictx->num_key_fd; i++)
if (key_fds[i].dead) {
mp_input_rm_key_fd(ictx, key_fds[i].fd);
i--;
}
for (i = 0; i < ictx->num_cmd_fd; i++)
if (cmd_fds[i].dead || cmd_fds[i].eof) {
mp_input_rm_cmd_fd(ictx, cmd_fds[i].fd);
i--;
}
else if (cmd_fds[i].got_cmd)
got_cmd = 1;
#ifdef HAVE_POSIX_SELECT
fd_set fds;
FD_ZERO(&fds);
if (!got_cmd) {
int max_fd = 0;
for (i = 0; i < ictx->num_key_fd; i++) {
if (key_fds[i].no_select)
continue;
if (key_fds[i].fd > max_fd)
max_fd = key_fds[i].fd;
FD_SET(key_fds[i].fd, &fds);
}
for (i = 0; i < ictx->num_cmd_fd; i++) {
if (cmd_fds[i].no_select)
continue;
if (cmd_fds[i].fd > max_fd)
max_fd = cmd_fds[i].fd;
FD_SET(cmd_fds[i].fd, &fds);
}
struct timeval tv, *time_val;
if (time >= 0) {
tv.tv_sec = time / 1000;
tv.tv_usec = (time % 1000) * 1000;
time_val = &tv;
} else
time_val = NULL;
if (select(max_fd + 1, &fds, NULL, NULL, time_val) < 0) {
if (errno != EINTR)
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Select error: %s\n",
strerror(errno));
FD_ZERO(&fds);
}
}
#else
if (!got_cmd && time)
usec_sleep(time * 1000);
#endif
for (i = 0; i < ictx->num_key_fd; i++) {
#ifdef HAVE_POSIX_SELECT
if (!key_fds[i].no_select && !FD_ISSET(key_fds[i].fd, &fds))
continue;
#endif
int code = key_fds[i].read_func.key(key_fds[i].ctx, key_fds[i].fd);
if (code >= 0) {
mp_cmd_t *ret = interpret_key(ictx, code);
if (ret)
return ret;
}
else if (code == MP_INPUT_ERROR)
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Error on key input file descriptor %d\n",
key_fds[i].fd);
else if (code == MP_INPUT_DEAD) {
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Dead key input on file descriptor %d\n",
key_fds[i].fd);
key_fds[i].dead = 1;
}
}
mp_cmd_t *autorepeat_cmd = check_autorepeat(ictx);
if (autorepeat_cmd)
return autorepeat_cmd;
for (i = 0; i < ictx->num_cmd_fd; i++) {
#ifdef HAVE_POSIX_SELECT
if (!cmd_fds[i].no_select && !FD_ISSET(cmd_fds[i].fd, &fds) &&
!cmd_fds[i].got_cmd)
continue;
#endif
char *cmd;
int r = read_cmd(&cmd_fds[i], &cmd);
if (r >= 0) {
mp_cmd_t *ret = mp_input_parse_cmd(cmd);
talloc_free(cmd);
if (ret)
return ret;
}
else if (r == MP_INPUT_ERROR)
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Error on command file descriptor %d\n",
cmd_fds[i].fd);
else if (r == MP_INPUT_DEAD)
cmd_fds[i].dead = 1;
}
return NULL;
}
int mp_input_queue_cmd(struct input_ctx *ictx, mp_cmd_t* cmd)
{
if (!cmd || ictx->cmd_queue_length >= CMD_QUEUE_SIZE)
return 0;
ictx->cmd_queue[ictx->cmd_queue_end] = cmd;
ictx->cmd_queue_end = (ictx->cmd_queue_end + 1) % CMD_QUEUE_SIZE;
ictx->cmd_queue_length++;
return 1;
}
static mp_cmd_t *get_queued_cmd(struct input_ctx *ictx, int peek_only)
{
mp_cmd_t* ret;
if (ictx->cmd_queue_length == 0)
return NULL;
ret = ictx->cmd_queue[ictx->cmd_queue_start];
if (!peek_only) {
ictx->cmd_queue_length--;
ictx->cmd_queue_start = (ictx->cmd_queue_start + 1) % CMD_QUEUE_SIZE;
}
return ret;
}
/**
* \param peek_only when set, the returned command stays in the queue.
* Do not free the returned cmd whe you set this!
*/
mp_cmd_t *mp_input_get_cmd(struct input_ctx *ictx, int time, int peek_only)
{
mp_cmd_t* ret = NULL;
mp_cmd_filter_t* cf;
int from_queue;
if (async_quit_request)
return mp_input_parse_cmd("quit 1");
while(1) {
from_queue = 1;
ret = get_queued_cmd(ictx, peek_only);
if(ret) break;
from_queue = 0;
ret = read_events(ictx, time);
if (!ret) {
from_queue = 1;
ret = get_queued_cmd(ictx, peek_only);
}
break;
}
if(!ret) return NULL;
for(cf = cmd_filters ; cf ; cf = cf->next) {
if (cf->filter(ret, cf->ctx)) {
if (peek_only && from_queue)
// The filter ate the cmd, so we remove it from queue
ret = get_queued_cmd(ictx, 0);
mp_cmd_free(ret);
return NULL;
}
}
if (!from_queue && peek_only)
mp_input_queue_cmd(ictx, ret);
return ret;
}
void
mp_cmd_free(mp_cmd_t* cmd) {
talloc_free(cmd);
}
mp_cmd_t*
mp_cmd_clone(mp_cmd_t* cmd) {
mp_cmd_t* ret;
int i;
#ifdef MP_DEBUG
assert(cmd != NULL);
#endif
ret = talloc_memdup(NULL, cmd, sizeof(mp_cmd_t));
ret->name = talloc_strdup(ret, cmd->name);
for(i = 0; i < MP_CMD_MAX_ARGS && cmd->args[i].type != -1; i++) {
if(cmd->args[i].type == MP_CMD_ARG_STRING && cmd->args[i].v.s != NULL)
ret->args[i].v.s = talloc_strdup(ret, cmd->args[i].v.s);
}
return ret;
}
int mp_input_get_key_from_name(const char *name)
{
int modifiers = 0;
const char *p;
while (p = strchr(name, '+')) {
for (struct mp_key_name *m = modifier_names; m->name; m++)
if (!bstrcasecmp(BSTR(m->name), (struct bstr){name, p - name})) {
modifiers |= m->key;
goto found;
}
if (!strcmp(name, "+"))
return '+' + modifiers;
return -1;
found:
name = p + 1;
}
int len = strlen(name);
if (len == 1) // Direct key code
return (unsigned char)name[0] + modifiers;
else if (len > 2 && strncasecmp("0x", name, 2) == 0)
return strtol(name, NULL, 16) + modifiers;
for (int i = 0; key_names[i].name != NULL; i++) {
if (strcasecmp(key_names[i].name, name) == 0)
return key_names[i].key + modifiers;
}
return -1;
}
static int get_input_from_name(char* name,int* keys) {
char *end,*ptr;
int n=0;
ptr = name;
n = 0;
for(end = strchr(ptr,'-') ; ptr != NULL ; end = strchr(ptr,'-')) {
if(end && end[1] != '\0') {
if(end[1] == '-')
end = &end[1];
end[0] = '\0';
}
keys[n] = mp_input_get_key_from_name(ptr);
if(keys[n] < 0) {
return 0;
}
n++;
if(end && end[1] != '\0' && n < MP_MAX_KEY_DOWN)
ptr = &end[1];
else
break;
}
keys[n] = 0;
return 1;
}
#define BS_MAX 256
#define SPACE_CHAR " \n\r\t"
static void bind_keys(struct input_ctx *ictx,
const int keys[MP_MAX_KEY_DOWN+1], char *cmd)
{
int i = 0,j;
mp_cmd_bind_t* bind = NULL;
mp_cmd_bind_section_t* bind_section = NULL;
char *section=NULL, *p;
#ifdef MP_DEBUG
assert(keys != NULL);
assert(cmd != NULL);
#endif
if(*cmd=='{' && (p=strchr(cmd,'}'))) {
*p=0;
section=++cmd;
cmd=++p;
// Jump beginning space
for( ; cmd[0] != '\0' && strchr(SPACE_CHAR,cmd[0]) != NULL ; cmd++)
/* NOTHING */;
}
bind_section = get_bind_section(ictx, section);
if(bind_section->cmd_binds) {
for(i = 0; bind_section->cmd_binds[i].cmd != NULL ; i++) {
for(j = 0 ; bind_section->cmd_binds[i].input[j] == keys[j] && keys[j] != 0 ; j++)
/* NOTHING */;
if(keys[j] == 0 && bind_section->cmd_binds[i].input[j] == 0 ) {
bind = &bind_section->cmd_binds[i];
break;
}
}
}
if(!bind) {
bind_section->cmd_binds = talloc_realloc(bind_section,
bind_section->cmd_binds,
mp_cmd_bind_t, i + 2);
memset(&bind_section->cmd_binds[i],0,2*sizeof(mp_cmd_bind_t));
bind = &bind_section->cmd_binds[i];
}
talloc_free(bind->cmd);
bind->cmd = talloc_strdup(bind_section->cmd_binds, cmd);
memcpy(bind->input,keys,(MP_MAX_KEY_DOWN+1)*sizeof(int));
}
static int parse_config(struct input_ctx *ictx, char *file)
{
int fd;
int bs = 0,r,eof = 0,comments = 0;
char *iter,*end;
char buffer[BS_MAX];
int n_binds = 0, keys[MP_MAX_KEY_DOWN+1] = { 0 };
fd = open(file,O_RDONLY);
if(fd < 0) {
mp_msg(MSGT_INPUT,MSGL_V,"Can't open input config file %s: %s\n",file,strerror(errno));
return 0;
}
mp_msg(MSGT_INPUT,MSGL_V,"Parsing input config file %s\n",file);
while(1) {
if(! eof && bs < BS_MAX-1) {
if(bs > 0) bs--;
r = read(fd,buffer+bs,BS_MAX-1-bs);
if(r < 0) {
if(errno == EINTR)
continue;
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Error while reading input config file %s: %s\n",file,strerror(errno));
close(fd);
return 0;
} else if(r == 0) {
eof = 1;
} else {
bs += r+1;
buffer[bs-1] = '\0';
}
}
// Empty buffer : return
if(bs <= 1) {
mp_msg(MSGT_INPUT,MSGL_V,"Input config file %s parsed: %d binds\n",file,n_binds);
close(fd);
return 1;
}
iter = buffer;
if(comments) {
for( ; iter[0] != '\0' && iter[0] != '\n' ; iter++)
/* NOTHING */;
if(iter[0] == '\0') { // Buffer was full of comment
bs = 0;
continue;
}
iter++;
r = strlen(iter);
memmove(buffer,iter,r+1);
bs = r+1;
comments = 0;
continue;
}
// Find the wanted key
if(keys[0] == 0) {
// Jump beginning space
for( ; iter[0] != '\0' && strchr(SPACE_CHAR,iter[0]) != NULL ; iter++)
/* NOTHING */;
if(iter[0] == '\0') { // Buffer was full of space char
bs = 0;
continue;
}
if(iter[0] == '#') { // Comments
comments = 1;
continue;
}
// Find the end of the key code name
for(end = iter; end[0] != '\0' && strchr(SPACE_CHAR,end[0]) == NULL ; end++)
/*NOTHING */;
if(end[0] == '\0') { // Key name doesn't fit in the buffer
if(buffer == iter) {
if(eof && (buffer-iter) == bs)
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Unfinished binding %s\n",iter);
else
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Buffer is too small for this key name: %s\n",iter);
return 0;
}
memmove(buffer,iter,end-iter);
bs = end-iter;
continue;
}
{
char name[end-iter+1];
strncpy(name,iter,end-iter);
name[end-iter] = '\0';
if (!get_input_from_name(name,keys)) {
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Unknown key '%s'\n",name);
close(fd);
return 0;
}
}
if( bs > (end-buffer))
memmove(buffer,end,bs - (end-buffer));
bs -= end-buffer;
continue;
} else { // Get the command
while(iter[0] == ' ' || iter[0] == '\t') iter++;
// Found new line
if(iter[0] == '\n' || iter[0] == '\r') {
int i;
char *key_buf = get_key_name(keys[0]);
mp_tmsg(MSGT_INPUT,MSGL_ERR,"No command found for key %s", key_buf);
talloc_free(key_buf);
for(i = 1; keys[i] != 0 ; i++) {
char *key_buf = get_key_name(keys[i]);
mp_msg(MSGT_INPUT,MSGL_ERR,"-%s", key_buf);
talloc_free(key_buf);
}
mp_msg(MSGT_INPUT,MSGL_ERR,"\n");
keys[0] = 0;
if(iter > buffer) {
memmove(buffer,iter,bs- (iter-buffer));
bs -= (iter-buffer);
}
continue;
}
for(end = iter ; end[0] != '\n' && end[0] != '\r' && end[0] != '\0' ; end++)
/* NOTHING */;
if(end[0] == '\0' && ! (eof && ((end+1) - buffer) == bs)) {
if(iter == buffer) {
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Buffer is too small for command %s\n",buffer);
close(fd);
return 0;
}
memmove(buffer,iter,end - iter);
bs = end - iter;
continue;
}
{
char cmd[end-iter+1];
strncpy(cmd,iter,end-iter);
cmd[end-iter] = '\0';
//printf("Set bind %d => %s\n",keys[0],cmd);
bind_keys(ictx, keys,cmd);
n_binds++;
}
keys[0] = 0;
end++;
if(bs > (end-buffer))
memmove(buffer,end,bs-(end-buffer));
bs -= (end-buffer);
buffer[bs-1] = '\0';
continue;
}
}
mp_tmsg(MSGT_INPUT,MSGL_ERR,"What are we doing here?\n");
close(fd);
mp_input_set_section(ictx, NULL);
return 0;
}
void mp_input_set_section(struct input_ctx *ictx, char *name)
{
mp_cmd_bind_section_t* bind_section = NULL;
ictx->cmd_binds = NULL;
ictx->cmd_binds_default = NULL;
talloc_free(ictx->section);
if (name)
ictx->section = talloc_strdup(ictx, name);
else
ictx->section = talloc_strdup(ictx, "default");
if ((bind_section = get_bind_section(ictx, ictx->section)))
ictx->cmd_binds = bind_section->cmd_binds;
if (strcmp(ictx->section, "default") == 0)
return;
if ((bind_section = get_bind_section(ictx, NULL)))
ictx->cmd_binds_default = bind_section->cmd_binds;
}
char *mp_input_get_section(struct input_ctx *ictx)
{
return ictx->section;
}
struct input_ctx *mp_input_init(struct input_conf *input_conf)
{
struct input_ctx *ictx = talloc_ptrtype(NULL, ictx);
*ictx = (struct input_ctx){
.ar_state = -1,
.ar_delay = input_conf->ar_delay,
.ar_rate = input_conf->ar_rate,
.default_bindings = input_conf->default_bindings,
};
char* file;
char *config_file = input_conf->config_file;
file = config_file[0] != '/' ? get_path(config_file) : config_file;
if(!file)
return ictx;
if (!parse_config(ictx, file)) {
// free file if it was allocated by get_path(),
// before it gets overwritten
if( file != config_file)
{
free(file);
}
// Try global conf dir
file = MPLAYER_CONFDIR "/input.conf";
if (!parse_config(ictx, file))
mp_msg(MSGT_INPUT,MSGL_V,"Falling back on default (hardcoded) input config\n");
}
else
{
// free file if it was allocated by get_path()
if( file != config_file)
free(file);
}
#ifdef CONFIG_JOYSTICK
if (input_conf->use_joystick) {
int fd = mp_input_joystick_init(input_conf->js_dev);
if(fd < 0)
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Can't init input joystick\n");
else
mp_input_add_key_fd(ictx, fd, 1, mp_input_joystick_read,
(mp_close_func_t)close,NULL);
}
#endif
#ifdef CONFIG_LIRC
if (input_conf->use_lirc) {
int fd = mp_input_lirc_init();
if(fd > 0)
mp_input_add_cmd_fd(ictx, fd, 0, mp_input_lirc_read,
mp_input_lirc_close);
}
#endif
#ifdef CONFIG_LIRCC
if (input_conf->use_lircc) {
int fd = lircc_init("mplayer", NULL);
if(fd >= 0)
mp_input_add_cmd_fd(ictx, fd, 1, NULL, (mp_close_func_t)lircc_cleanup);
}
#endif
#ifdef CONFIG_APPLE_REMOTE
if (input_conf->use_ar) {
if(mp_input_ar_init() < 0)
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Can't init Apple Remote.\n");
else
mp_input_add_key_fd(ictx, -1, 0, mp_input_ar_read, mp_input_ar_close,
NULL);
}
#endif
#ifdef CONFIG_APPLE_IR
if (input_conf->use_ar) {
int fd = mp_input_appleir_init(input_conf->ar_dev);
if(fd < 0)
mp_tmsg(MSGT_INPUT,MSGL_ERR,"Can't init Apple Remote.\n");
else
mp_input_add_key_fd(ictx, fd, 1, mp_input_appleir_read,
(mp_close_func_t)close, NULL);
}
#endif
if (input_conf->in_file) {
struct stat st;
int mode = O_RDONLY;
// Use RDWR for FIFOs to ensure they stay open over multiple accesses.
// Note that on Windows stat may fail for named pipes, but due to how the
// API works, using RDONLY should be ok.
if (stat(input_conf->in_file, &st) == 0 && S_ISFIFO(st.st_mode))
mode = O_RDWR;
int in_file_fd = open(input_conf->in_file, mode);
if(in_file_fd >= 0)
mp_input_add_cmd_fd(ictx, in_file_fd, 1, NULL,
(mp_close_func_t)close);
else
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Can't open %s: %s\n",
input_conf->in_file, strerror(errno));
}
return ictx;
}
void mp_input_uninit(struct input_ctx *ictx)
{
if (!ictx)
return;
unsigned int i;
for (i=0; i < ictx->num_key_fd; i++) {
if (ictx->key_fds[i].close_func)
ictx->key_fds[i].close_func(ictx->key_fds[i].fd);
}
for (i = 0; i < ictx->num_cmd_fd; i++) {
if (ictx->cmd_fds[i].close_func)
ictx->cmd_fds[i].close_func(ictx->cmd_fds[i].fd);
}
talloc_free(ictx);
}
void
mp_input_register_options(m_config_t* cfg) {
m_config_register_options(cfg,mp_input_opts);
}
static int print_key_list(m_option_t* cfg)
{
int i;
printf("\n");
for(i= 0; key_names[i].name != NULL ; i++)
printf("%s\n",key_names[i].name);
exit(0);
}
static int print_cmd_list(m_option_t* cfg)
{
const mp_cmd_t *cmd;
int i,j;
const char* type;
for(i = 0; (cmd = &mp_cmds[i])->name != NULL ; i++) {
printf("%-20.20s",cmd->name);
for(j= 0 ; j < MP_CMD_MAX_ARGS && cmd->args[j].type != -1 ; j++) {
switch(cmd->args[j].type) {
case MP_CMD_ARG_INT:
type = "Integer";
break;
case MP_CMD_ARG_FLOAT:
type = "Float";
break;
case MP_CMD_ARG_STRING:
type = "String";
break;
default:
type = "??";
}
if(j+1 > cmd->nargs)
printf(" [%s]",type);
else
printf(" %s",type);
}
printf("\n");
}
exit(0);
}
/**
* \param time time to wait for an interruption in milliseconds
*/
int mp_input_check_interrupt(struct input_ctx *ictx, int time)
{
mp_cmd_t* cmd;
if ((cmd = mp_input_get_cmd(ictx, time, 1)) == NULL)
return 0;
switch(cmd->id) {
case MP_CMD_QUIT:
case MP_CMD_PLAY_TREE_STEP:
case MP_CMD_PLAY_TREE_UP_STEP:
case MP_CMD_PLAY_ALT_SRC_STEP:
// The cmd will be executed when we are back in the main loop
return 1;
}
// remove the cmd from the queue
cmd = mp_input_get_cmd(ictx, time, 0);
mp_cmd_free(cmd);
return 0;
}