From ae5df9be98e4193342321f30285655fcf88e7e63 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 23 Nov 2014 15:08:49 +0100 Subject: input, lua: redo input handling Much of it is the same, but now there's the possibility to distinguish key down/up events in the Lua API. --- player/client.c | 24 +++++++++ player/client.h | 2 + player/command.c | 30 +++++++---- player/lua/defaults.lua | 132 +++++++++++++++++++++++++++++++++++------------- 4 files changed, 145 insertions(+), 43 deletions(-) (limited to 'player') diff --git a/player/client.c b/player/client.c index 97bb215464..d01c611a3d 100644 --- a/player/client.c +++ b/player/client.c @@ -589,9 +589,16 @@ void mp_client_broadcast_event(struct MPContext *mpctx, int event, void *data) pthread_mutex_unlock(&clients->lock); } +// If client_name == NULL, then broadcast and free the event. int mp_client_send_event(struct MPContext *mpctx, const char *client_name, int event, void *data) { + if (!client_name) { + mp_client_broadcast_event(mpctx, event, data); + talloc_free(data); + return 0; + } + struct mp_client_api *clients = mpctx->clients; int r = 0; @@ -615,6 +622,23 @@ int mp_client_send_event(struct MPContext *mpctx, const char *client_name, return r; } +int mp_client_send_event_dup(struct MPContext *mpctx, const char *client_name, + int event, void *data) +{ + if (!client_name) { + mp_client_broadcast_event(mpctx, event, data); + return 0; + } + + struct mpv_event event_data = { + .event_id = event, + .data = data, + }; + + dup_event_data(&event_data); + return mp_client_send_event(mpctx, client_name, event, event_data.data); +} + int mpv_request_event(mpv_handle *ctx, mpv_event_id event, int enable) { if (!mpv_event_name(event) || enable < 0 || enable > 1) diff --git a/player/client.h b/player/client.h index bdecae20ae..a275bb9728 100644 --- a/player/client.h +++ b/player/client.h @@ -23,6 +23,8 @@ bool mp_client_exists(struct MPContext *mpctx, const char *client_name); void mp_client_broadcast_event(struct MPContext *mpctx, int event, void *data); int mp_client_send_event(struct MPContext *mpctx, const char *client_name, int event, void *data); +int mp_client_send_event_dup(struct MPContext *mpctx, const char *client_name, + int event, void *data); bool mp_client_event_is_registered(struct MPContext *mpctx, int event); void mp_client_property_change(struct MPContext *mpctx, const char *name); diff --git a/player/command.c b/player/command.c index 30768e9b71..0042d8dee7 100644 --- a/player/command.c +++ b/player/command.c @@ -4471,17 +4471,29 @@ int run_command(MPContext *mpctx, mp_cmd_t *cmd) return edit_filters_osd(mpctx, STREAM_VIDEO, cmd->args[0].v.s, cmd->args[1].v.s, msg_osd); - case MP_CMD_SCRIPT_DISPATCH: { - mpv_event_script_input_dispatch *event = talloc_ptrtype(NULL, event); - *event = (mpv_event_script_input_dispatch){ - .arg0 = cmd->args[1].v.i, - .type = cmd->key_up_follows ? "keyup_follows" : "press", - }; - if (mp_client_send_event(mpctx, cmd->args[0].v.s, - MPV_EVENT_SCRIPT_INPUT_DISPATCH, event) < 0) + case MP_CMD_SCRIPT_BINDING: { + mpv_event_client_message event = {0}; + char *name = cmd->args[0].v.s; + if (!name || !name[0]) + return -1; + char *sep = strchr(name, '/'); + char *target = NULL; + char space[MAX_CLIENT_NAME]; + if (name) { + snprintf(space, sizeof(space), "%.*s", (int)(sep - name), name); + target = space; + name = sep + 1; + } + char state[3] = {'p', cmd->is_mouse_button ? 'm' : '-'}; + if (cmd->is_up_down) + state[0] = cmd->repeated ? 'r' : (cmd->is_up ? 'u' : 'd'); + event.num_args = 3; + event.args = (const char*[3]){"key-binding", name, state}; + if (mp_client_send_event_dup(mpctx, target, + MPV_EVENT_CLIENT_MESSAGE, &event) < 0) { MP_VERBOSE(mpctx, "Can't find script '%s' when handling input.\n", - cmd->args[0].v.s); + target ? target : "-"); return -1; } break; diff --git a/player/lua/defaults.lua b/player/lua/defaults.lua index 4b3be12ac9..812bb71afe 100644 --- a/player/lua/defaults.lua +++ b/player/lua/defaults.lua @@ -22,7 +22,27 @@ function mp.get_opt(key, def) return val end -local callbacks = {} +-- For dispatching script_binding. This is sent as: +-- script_message_to $script_name $binding_name $keystate +-- The array is indexed by $binding_name, and has functions like this as value: +-- fn($binding_name, $keystate) +local dispatch_key_bindings = {} + +local message_id = 0 +local function reserve_binding() + message_id = message_id + 1 + return "__keybinding" .. tostring(message_id) +end + +local function dispatch_key_binding(name, state) + local fn = dispatch_key_bindings[name] + if fn then + fn(name, state) + end +end + +-- "Old", deprecated API + -- each script has its own section, so that they don't conflict local default_section = "input_dispatch_" .. mp.script_name @@ -34,13 +54,8 @@ local default_section = "input_dispatch_" .. mp.script_name -- Note: the bindings are not active by default. Use enable_key_bindings(). -- -- list is an array of key bindings, where each entry is an array as follow: --- {key, callback} --- {key, callback, callback_down} +-- {key, callback_press, callback_down, callback_up} -- key is the key string as used in input.conf, like "ctrl+a" --- callback is a Lua function that is called when the key binding is used. --- callback_down can be given too, and is called when a mouse button is pressed --- if the key is a mouse button. (The normal callback will be for mouse button --- down.) -- -- callback can be a string too, in which case the following will be added like -- an input.conf line: key .. " " .. callback @@ -52,10 +67,29 @@ function mp.set_key_bindings(list, section, flags) local key = entry[1] local cb = entry[2] local cb_down = entry[3] - if type(cb) == "function" then - callbacks[#callbacks + 1] = {press=cb, before_press=cb_down} - cfg = cfg .. key .. " script_dispatch " .. mp.script_name - .. " " .. #callbacks .. "\n" + local cb_up = entry[4] + if type(cb) ~= "string" then + local mangle = reserve_binding() + dispatch_key_bindings[mangle] = function(name, state) + local event = state:sub(1, 1) + local is_mouse = state:sub(2, 2) == "m" + local def = (is_mouse and "u") or "d" + if event == "r" then + event = "d" + end + if event == "p" and cb then + cb() + elseif event == "d" and cb_down then + cb_down() + elseif event == "u" and cb_up then + cb_up() + elseif event == def and cb then + print("whooo") + cb() + end + end + cfg = cfg .. key .. " script_binding " .. + mp.script_name .. "/" .. mangle .. "\n" else cfg = cfg .. key .. " " .. cb .. "\n" end @@ -75,21 +109,9 @@ function mp.set_mouse_area(x0, y0, x1, y1, section) mp.input_set_section_mouse_area(section or default_section, x0, y0, x1, y1) end -local function script_dispatch(event) - local cb = callbacks[event.arg0] - if cb then - if event.type == "press" and cb.press then - cb.press() - elseif event.type == "keyup_follows" and cb.before_press then - cb.before_press() - end - end -end - -- "Newer" and more convenient API local key_bindings = {} -local message_id = 1 local function update_key_bindings() for i = 1, 2 do @@ -105,9 +127,7 @@ local function update_key_bindings() local cfg = "" for k, v in pairs(key_bindings) do if v.forced ~= def then - local flags = (v.repeatable and " repeatable") or "" - cfg = cfg .. v.key .. " " .. flags .. " script_message_to " - .. mp.script_name .. " " .. v.name .. "\n" + cfg = cfg .. v.bind .. "\n" end end mp.input_define_section(section, cfg, flags) @@ -117,19 +137,61 @@ local function update_key_bindings() end local function add_binding(attrs, key, name, fn, rp) + rp = rp or "" if (type(name) ~= "string") and (not fn) then fn = name - name = "message" .. tostring(message_id) - message_id = message_id + 1 + name = reserve_binding() + end + local bind = key + if rp == "repeatable" or rp["repeatable"] then + bind = bind .. " repeatable" + end + if rp["forced"] then + attrs.forced = true + end + local key_cb, msg_cb + if not fn then + fn = function() end + end + if rp["complex"] then + local key_states = { + ["u"] = "up", + ["d"] = "down", + ["r"] = "repeat", + ["p"] = "press", + } + key_cb = function(name, state) + fn({ + event = key_states[state:sub(1, 1)] or "unknown", + is_mouse = state:sub(2, 2) == "m" + }) + end + msg_cb = function() + fn({event = "press", is_mouse = false}) + end + else + key_cb = function(name, state) + -- Emulate the same semantics as input.c uses for most bindings: + -- For keyboard, "down" runs the command, "up" does nothing; + -- for mouse, "down" does nothing, "up" runs the command. + -- Also, key repeat triggers the binding again. + local event = state:sub(1, 1) + local is_mouse = state:sub(2, 2) == "m" + if is_mouse and event == "u" then + fn() + elseif (not is_mouse) and (event == "d" or event == "r") then + fn() + end + end + msg_cb = fn end - attrs.repeatable = rp == "repeatable" - attrs.key = key + attrs.bind = bind .. " script_binding " .. mp.script_name .. "/" .. name attrs.name = name key_bindings[name] = attrs update_key_bindings() - if fn then - mp.register_script_message(name, fn) - end + dispatch_key_bindings[name] = key_cb + mp.unregister_script_message(name) + mp.register_script_message(name, msg_cb) end function mp.add_key_binding(...) @@ -322,10 +384,12 @@ end -- default handlers mp.register_event("shutdown", function() mp.keep_running = false end) -mp.register_event("script-input-dispatch", script_dispatch) mp.register_event("client-message", message_dispatch) mp.register_event("property-change", property_change) +-- sent by "script_binding" +mp.register_script_message("key-binding", dispatch_key_binding) + mp.msg = { log = mp.log, fatal = function(...) return mp.log("fatal", ...) end, -- cgit v1.2.3