diff --git a/DOCS/man/lua.rst b/DOCS/man/lua.rst index 23c2008050670..3f55da35baa63 100644 --- a/DOCS/man/lua.rst +++ b/DOCS/man/lua.rst @@ -976,19 +976,22 @@ REPL. script name with ``prompt`` appended. ``input.terminate()`` - Close the console. + Closes any currently active input request. This will not close + requests made by other scripts. ``input.log(message, style, terminal_style)`` - Add a line to the log buffer. ``style`` can contain additional ASS tags to - apply to ``message``, and ``terminal_style`` can contain escape sequences - that are used when the console is displayed in the terminal. + Add a line to the log buffer of the latest ``input.get()`` request. + ``style`` can contain additional ASS tags to apply to ``message``, + and ``terminal_style`` can contain escape sequences that are used + when the console is displayed in the terminal. ``input.log_error(message)`` - Helper to add a line to the log buffer with the same color as the one used + Helper to add an error line to the log buffer of the latest ``input.get()`` + request. The line is styled with the same color as the one used for commands that error. Useful when the user submits invalid input. ``input.set_log(log)`` - Replace the entire log buffer. + Replace the entire log buffer of the latest ``input.get()`` request. ``log`` is a table of strings, or tables with ``text``, ``style`` and ``terminal_style`` keys. diff --git a/player/javascript/defaults.js b/player/javascript/defaults.js index ae687febe1014..21d91e7994412 100644 --- a/player/javascript/defaults.js +++ b/player/javascript/defaults.js @@ -653,52 +653,82 @@ mp.options = { read_options: read_options }; /********************************************************************** * input *********************************************************************/ +var input_handle_counter = 0; +var latest_handler_id; +var latest_log_id; + function register_event_handler(t) { - mp.register_script_message("input-event", function (type, args) { + var handler_id = "input-event/" + input_handle_counter++; + latest_handler_id = handler_id; + + mp.register_script_message(handler_id, function (type, args) { + if (latest_handler_id !== handler_id && type !== "closed") + return; + if (t[type]) { args = args ? JSON.parse(args) : []; var result = t[type].apply(null, args); if (type == "complete" && result) { - mp.commandv("script-message-to", "console", "complete", - JSON.stringify(result[0]), result[1], result[2] || ""); + mp.commandv("script-message-to", "console", "complete", JSON.stringify({ + client_name: mp.script_name, + handler_id: handler_id, + list: result[0], + start_pos: result[1], + append: result[2] || "", + })); } } if (type == "closed") - mp.unregister_script_message("input-event"); + mp.unregister_script_message(handler_id); }) + + return handler_id; } -mp.input = { - get: function(t) { - t.has_completions = t.complete !== undefined +function input_request(t) { + t.has_completions = t.complete !== undefined; + t.client_name = mp.script_name; + t.handler_id = register_event_handler(t); - mp.commandv("script-message-to", "console", "get-input", mp.script_name, - JSON.stringify(t)); + mp.commandv("script-message-to", "console", "get-input", JSON.stringify(t)); +} - register_event_handler(t) +mp.input = { + get: function(t) { + t.id = t.id || mp.script_name + (t.prompt || ""); + latest_log_id = t.id; + return input_request(t); }, terminate: function () { - mp.commandv("script-message-to", "console", "disable"); + mp.commandv("script-message-to", "console", "disable", JSON.stringify({ + client_name: mp.script_name, + })); }, log: function (message, style, terminal_style) { mp.commandv("script-message-to", "console", "log", JSON.stringify({ + log_id: latest_log_id, text: message, style: style, terminal_style: terminal_style, })); }, log_error: function (message) { - mp.commandv("script-message-to", "console", "log", - JSON.stringify({ text: message, error: true })); + mp.commandv("script-message-to", "console", "log", JSON.stringify({ + log_id: latest_log_id, + text: message, + error: true, + })); }, set_log: function (log) { - mp.commandv("script-message-to", "console", "set-log", - JSON.stringify(log)); + if (latest_log_id) { + mp.commandv("script-message-to", "console", "set-log", + latest_log_id, JSON.stringify(log)); + } } } -mp.input.select = mp.input.get +mp.input.select = input_request; /********************************************************************** * various diff --git a/player/lua/console.lua b/player/lua/console.lua index 27ab10c974dd0..6603b685a3546 100644 --- a/player/lua/console.lua +++ b/player/lua/console.lua @@ -96,6 +96,7 @@ local key_bindings = {} local dont_bind_up_down = false local global_margins = { t = 0, b = 0 } local input_caller +local input_caller_handler local keep_open = false local completion_buffer = {} @@ -949,7 +950,7 @@ end local function handle_edit() if not selectable_items then handle_cursor_move() - mp.commandv("script-message-to", input_caller, "input-event", "edited", + mp.commandv("script-message-to", input_caller, input_caller_handler, "edited", utils.format_json({line})) return end @@ -1070,7 +1071,7 @@ local function submit() if selectable_items then if #matches > 0 then - mp.commandv("script-message-to", input_caller, "input-event", "submit", + mp.commandv("script-message-to", input_caller, input_caller_handler, "submit", utils.format_json({matches[focused_match].index})) end else @@ -1079,7 +1080,7 @@ local function submit() cycle_through_completions() end - mp.commandv("script-message-to", input_caller, "input-event", "submit", + mp.commandv("script-message-to", input_caller, input_caller_handler, "submit", utils.format_json({line})) history_add(line) @@ -1481,7 +1482,7 @@ end complete = function () completion_old_line = line completion_old_cursor = cursor - mp.commandv("script-message-to", input_caller, "input-event", + mp.commandv("script-message-to", input_caller, input_caller_handler, "complete", utils.format_json({line:sub(1, cursor - 1)})) render() end @@ -1651,25 +1652,30 @@ set_active = function (active) unbind_mouse() mp.set_property_bool("user-data/mpv/console/open", false) mp.set_property_bool("input-ime", ime_active) - mp.commandv("script-message-to", input_caller, "input-event", + mp.commandv("script-message-to", input_caller, input_caller_handler, "closed", utils.format_json({line, cursor})) collectgarbage() end render() end -mp.register_script_message("disable", function() - set_active(false) +mp.register_script_message("disable", function(message) + message = utils.parse_json(message or "") + + if not message or message.client_name == input_caller then + set_active(false) + end end) -mp.register_script_message("get-input", function (script_name, args) - if open and script_name ~= input_caller then - mp.commandv("script-message-to", input_caller, "input-event", +mp.register_script_message("get-input", function (args) + if open then + mp.commandv("script-message-to", input_caller, input_caller_handler, "closed", utils.format_json({line, cursor})) end - input_caller = script_name args = utils.parse_json(args) + input_caller = args.client_name + input_caller_handler = args.handler_id prompt = args.prompt or "" line = args.default_text or "" cursor = tonumber(args.cursor_position) or line:len() + 1 @@ -1704,7 +1710,7 @@ mp.register_script_message("get-input", function (script_name, args) else selectable_items = nil unbind_mouse() - id = args.id or script_name .. prompt + id = args.id log_offset = 0 completion_buffer = {} autoselect_completion = args.autoselect_completion @@ -1721,17 +1727,26 @@ mp.register_script_message("get-input", function (script_name, args) if line ~= "" then complete() + elseif open then + -- This is needed to update the prompt if a new request is + -- received while another is still active. + render() end end set_active(true) - mp.commandv("script-message-to", input_caller, "input-event", "opened") + mp.commandv("script-message-to", input_caller, input_caller_handler, "opened") end) -- Add a line to the log buffer mp.register_script_message("log", function (message) - local log_buffer = log_buffers[id] - message = utils.parse_json(message) + message = utils.parse_json(message or "") + if not message or not message.log_id then + return + end + + local log_buffer = log_buffers[message.log_id] + if not log_buffer then return end log_buffer[#log_buffer + 1] = { text = message.text, @@ -1744,7 +1759,7 @@ mp.register_script_message("log", function (message) table.remove(log_buffer, 1) end - if not open then + if not open or message.log_id ~= id then return end @@ -1760,9 +1775,13 @@ mp.register_script_message("log", function (message) end end) -mp.register_script_message("set-log", function (log) +mp.register_script_message("set-log", function (log_id, log) + if not log_id or not log then + return + end + log = utils.parse_json(log) - log_buffers[id] = {} + log_buffers[log_id] = {} for i = 1, #log do if type(log[i]) == "table" then @@ -1779,20 +1798,25 @@ mp.register_script_message("set-log", function (log) end end - render() + if log_id == id then + render() + end end) -mp.register_script_message("complete", function (list, start_pos, append) - if line ~= completion_old_line or cursor ~= completion_old_cursor then +mp.register_script_message("complete", function (message) + message = utils.parse_json(message) + + if message.client_name ~= input_caller or message.handler_id ~= input_caller_handler + or line ~= completion_old_line or cursor ~= completion_old_cursor then return end completion_buffer = {} selected_completion_index = 0 - local completions = utils.parse_json(list) + local completions = message.list table.sort(completions) - completion_pos = start_pos - completion_append = append + completion_pos = message.start_pos + completion_append = message.append for i, match in ipairs(fuzzy_find(line:sub(completion_pos, cursor - 1), completions)) do completion_buffer[i] = completions[match[1]] diff --git a/player/lua/input.lua b/player/lua/input.lua index 3dded33dd744c..d0fa59c1f77b8 100644 --- a/player/lua/input.lua +++ b/player/lua/input.lua @@ -18,6 +18,10 @@ License along with mpv. If not, see . local utils = require "mp.utils" local input = {} +local handle_counter = 0 +local latest_handler_id +local latest_log_id + local function get_non_callbacks(t) local non_callbacks = {} @@ -31,54 +35,85 @@ local function get_non_callbacks(t) end local function register_event_handler(t) - mp.register_script_message("input-event", function (type, args) + local handler_id = "input-event/"..handle_counter + handle_counter = handle_counter + 1 + latest_handler_id = handler_id + + mp.register_script_message(handler_id, function (type, args) + -- do not process events (other than closed) for an input that has been overwritten + if latest_handler_id ~= handler_id and type ~= "closed" then + return + end + if t[type] then local completions, completion_pos, completion_append = t[type](unpack(utils.parse_json(args or "") or {})) if type == "complete" and completions then - mp.commandv("script-message-to", "console", "complete", - utils.format_json(completions), completion_pos, - completion_append or "") + mp.commandv("script-message-to", "console", "complete", utils.format_json({ + client_name = mp.get_script_name(), + handler_id = handler_id, + list = completions, + start_pos = completion_pos, + append = completion_append or "", + })) end end if type == "closed" then - mp.unregister_script_message("input-event") + mp.unregister_script_message(handler_id) end end) + + return handler_id end -function input.get(t) +local function input_request(t) t.has_completions = t.complete ~= nil + t.client_name = mp.get_script_name() + t.handler_id = register_event_handler(t) mp.commandv("script-message-to", "console", "get-input", - mp.get_script_name(), utils.format_json(get_non_callbacks(t))) + utils.format_json(get_non_callbacks(t))) +end - register_event_handler(t) +function input.get(t) + -- input.select does not support log buffers, so cannot override the latest id. + t.id = t.id or mp.get_script_name()..(t.prompt or "") + latest_log_id = t.id + return input_request(t) end -input.select = input.get +input.select = input_request function input.terminate() - mp.commandv("script-message-to", "console", "disable") + mp.commandv("script-message-to", "console", "disable", utils.format_json({ + client_name = mp.get_script_name(), + })) end function input.log(message, style, terminal_style) mp.commandv("script-message-to", "console", "log", utils.format_json({ - text = message, - style = style, - terminal_style = terminal_style, - })) + log_id = latest_log_id, + text = message, + style = style, + terminal_style = terminal_style, + })) end function input.log_error(message) - mp.commandv("script-message-to", "console", "log", - utils.format_json({ text = message, error = true })) + mp.commandv("script-message-to", "console", "log", utils.format_json({ + log_id = latest_log_id, + text = message, + error = true, + })) end function input.set_log(log) - mp.commandv("script-message-to", "console", "set-log", utils.format_json(log)) + if latest_log_id then + mp.commandv("script-message-to", "console", "set-log", + latest_log_id, utils.format_json(log)) + end end return input