diff options
Diffstat (limited to 'player/javascript/defaults.js')
-rw-r--r-- | player/javascript/defaults.js | 231 |
1 files changed, 211 insertions, 20 deletions
diff --git a/player/javascript/defaults.js b/player/javascript/defaults.js index c91228087d..6493e38680 100644 --- a/player/javascript/defaults.js +++ b/player/javascript/defaults.js @@ -126,10 +126,17 @@ function dispatch_message(ev) { var hooks = []; // array of callbacks, id is index+1 function run_hook(ev) { + var state = 0; // 0:initial, 1:deferred, 2:continued + function do_cont() { return state = 2, mp._hook_continue(ev.hook_id) } + + function err() { return mp.msg.error("hook already continued"), undefined } + function usr_defer() { return state == 2 ? err() : (state = 1, true) } + function usr_cont() { return state == 2 ? err() : do_cont() } + var cb = ev.id > 0 && hooks[ev.id - 1]; if (cb) - cb(); - mp._hook_continue(ev.hook_id); + cb({ defer: usr_defer, cont: usr_cont }); + return state == 0 ? do_cont() : true; } mp.add_hook = function add_hook(name, pri, fn) { @@ -170,6 +177,68 @@ mp.abort_async_command = function abort_async_command(id) { mp._abort_async_command(id); } +// osd-ass +var next_assid = 1; +mp.create_osd_overlay = function create_osd_overlay(format) { + return { + format: format || "ass-events", + id: next_assid++, + data: "", + res_x: 0, + res_y: 720, + z: 0, + + update: function ass_update() { + var cmd = {}; // shallow clone of `this', excluding methods + for (var k in this) { + if (typeof this[k] != "function") + cmd[k] = this[k]; + } + + cmd.name = "osd-overlay"; + cmd.res_x = Math.round(this.res_x); + cmd.res_y = Math.round(this.res_y); + + return mp.command_native(cmd); + }, + + remove: function ass_remove() { + mp.command_native({ + name: "osd-overlay", + id: this.id, + format: "none", + data: "", + }); + return mp.last_error() ? undefined : true; + }, + }; +} + +// osd-ass legacy API +mp.set_osd_ass = function set_osd_ass(res_x, res_y, data) { + if (!mp._legacy_overlay) + mp._legacy_overlay = mp.create_osd_overlay("ass-events"); + + var lo = mp._legacy_overlay; + if (lo.res_x == res_x && lo.res_y == res_y && lo.data == data) + return true; + + mp._legacy_overlay.res_x = res_x; + mp._legacy_overlay.res_y = res_y; + mp._legacy_overlay.data = data; + return mp._legacy_overlay.update(); +} + +// the following return undefined on error, null passthrough, or legacy object +mp.get_osd_size = function get_osd_size() { + var d = mp.get_property_native("osd-dimensions"); + return d && {width: d.w, height: d.h, aspect: d.aspect}; +} +mp.get_osd_margins = function get_osd_margins() { + var d = mp.get_property_native("osd-dimensions"); + return d && {left: d.ml, right: d.mr, top: d.mt, bottom: d.mb}; +} + /********************************************************************** * key bindings *********************************************************************/ @@ -177,16 +246,26 @@ mp.abort_async_command = function abort_async_command(id) { // {cb: fn, forced: bool, maybe input: str, repeatable: bool, complex: bool} var binds = new_cache(); -function dispatch_key_binding(name, state) { +function dispatch_key_binding(name, state, key_name, key_text) { var cb = binds[name] ? binds[name].cb : false; if (cb) // "script-binding [<script_name>/]<name>" command was invoked - cb(state); + cb(state, key_name, key_text); } -function update_input_sections() { +var binds_tid = 0; // flush timer id. actual id's are always true-thy +mp.flush_key_bindings = function flush_key_bindings() { + function prioritized_inputs(arr) { + return arr.sort(function(a, b) { return a.id - b.id }) + .map(function(bind) { return bind.input }); + } + var def = [], forced = []; - for (var n in binds) // Array.join() will later skip undefined .input - (binds[n].forced ? forced : def).push(binds[n].input); + for (var n in binds) + if (binds[n].input) + (binds[n].forced ? forced : def).push(binds[n]); + // newer bindings for the same key override/hide older ones + def = prioritized_inputs(def); + forced = prioritized_inputs(forced); var sect = "input_" + mp.script_name; mp.commandv("define-section", sect, def.join("\n"), "default"); @@ -195,6 +274,14 @@ function update_input_sections() { sect = "input_forced_" + mp.script_name; mp.commandv("define-section", sect, forced.join("\n"), "force"); mp.commandv("enable-section", sect, "allow-hide-cursor+allow-vo-dragging"); + + clearTimeout(binds_tid); // cancel future flush if called directly + binds_tid = 0; +} + +function sched_bindings_flush() { + if (!binds_tid) + binds_tid = setTimeout(mp.flush_key_bindings, 0); // fires on idle } // name/opts maybe omitted. opts: object with optional bool members: repeatable, @@ -204,23 +291,28 @@ function add_binding(forced, key, name, fn, opts) { if (typeof name == "function") { // as if "name" is not part of the args opts = fn; fn = name; - name = "__keybinding" + next_bid++; // new unique binding name + name = false; } var key_data = {forced: forced}; switch (typeof opts) { // merge opts into key_data case "string": key_data[opts] = true; break; case "object": for (var o in opts) key_data[o] = opts[o]; } + key_data.id = next_bid++; + if (!name) + name = "__keybinding" + key_data.id; // new unique binding name if (key_data.complex) { mp.register_script_message(name, function msg_cb() { fn({event: "press", is_mouse: false}); }); var KEY_STATES = { u: "up", d: "down", r: "repeat", p: "press" }; - key_data.cb = function key_cb(state) { + key_data.cb = function key_cb(state, key_name, key_text) { fn({ event: KEY_STATES[state[0]] || "unknown", - is_mouse: state[1] == "m" + is_mouse: state[1] == "m", + key_name: key_name || undefined, + key_text: key_text || undefined }); } } else { @@ -238,7 +330,7 @@ function add_binding(forced, key, name, fn, opts) { if (key) key_data.input = key + " script-binding " + mp.script_name + "/" + name; binds[name] = key_data; // used by user and/or our (key) script-binding - update_input_sections(); + sched_bindings_flush(); } mp.add_key_binding = add_binding.bind(null, false); @@ -247,7 +339,7 @@ mp.add_forced_key_binding = add_binding.bind(null, true); mp.remove_key_binding = function(name) { mp.unregister_script_message(name); delete binds[name]; - update_input_sections(); + sched_bindings_flush(); } /********************************************************************** @@ -372,6 +464,10 @@ function process_timers() { - Module id supports mpv path enhancements, e.g. ~/foo, ~~/bar, ~~desktop/baz *********************************************************************/ +mp.module_paths = []; // global modules search paths +if (mp.script_path !== undefined) // loaded as a directory + mp.module_paths.push(mp.utils.join_path(mp.script_path, "modules")); + // Internal meta top-dirs. Users should not rely on these names. var MODULES_META = "~~modules", SCRIPTDIR_META = "~~scriptdir", // relative script path -> meta absolute id @@ -386,10 +482,14 @@ function resolve_module_file(id) { return mp.utils.join_path(main_script[0], rest); if (base == MODULES_META) { - var path = mp.find_config_file("scripts/modules.js/" + rest); - if (!path) - throw(Error("Cannot find module file '" + rest + "'")); - return path; + for (var i = 0; i < mp.module_paths.length; i++) { + try { + var f = mp.utils.join_path(mp.module_paths[i], rest); + mp.utils.read_file(f, 1); // throws on any error + return f; + } catch (e) {} + } + throw(Error("Cannot find module file '" + rest + "'")); } return id + ".js"; @@ -480,13 +580,13 @@ g.require = new_require(SCRIPTDIR_META + "/" + main_script[1]); /********************************************************************** * mp.options *********************************************************************/ -function read_options(opts, id) { - id = String(typeof id != "undefined" ? id : mp.get_script_name()); +function read_options(opts, id, on_update, conf_override) { + id = String(id ? id : mp.get_script_name()); mp.msg.debug("reading options for " + id); var conf, fname = "~~/script-opts/" + id + ".conf"; try { - conf = mp.utils.read_file(fname); + conf = arguments.length > 3 ? conf_override : mp.utils.read_file(fname); } catch (e) { mp.msg.verbose(fname + " not found."); } @@ -525,18 +625,101 @@ function read_options(opts, id) { else mp.msg.error(info, "Error: can't convert '" + val + "' to " + type); }); + + if (on_update) { + mp.observe_property("options/script-opts", "native", function(_n, _v) { + var saved = JSON.parse(JSON.stringify(opts)); // clone + var changelist = {}, changed = false; + read_options(opts, id, 0, conf); // re-apply orig-file + script-opts + for (var key in opts) { + if (opts[key] != saved[key]) // type always stays the same + changelist[key] = changed = true; + } + if (changed) + on_update(changelist); + }); + } } mp.options = { read_options: read_options }; /********************************************************************** +* input +*********************************************************************/ +function register_event_handler(t) { + mp.register_script_message("input-event", function (type, args) { + if (t[type]) { + args = JSON.parse(args) + var result = t[type](args[0], args[1]); + + if (type == "complete" && result) { + mp.commandv("script-message-to", "console", "complete", + JSON.stringify(result[0]), result[1]); + } + } + + if (type == "closed") + mp.unregister_script_message("input-event"); + }) +} + +mp.input = { + get: function(t) { + mp.commandv("script-message-to", "console", "get-input", mp.script_name, + JSON.stringify({ + prompt: t.prompt, + default_text: t.default_text, + cursor_position: t.cursor_position, + id: t.id, + })); + + register_event_handler(t) + }, + select: function (t) { + mp.commandv("script-message-to", "console", "get-input", mp.script_name, + JSON.stringify({ + prompt: t.prompt, + items: t.items, + default_item: t.default_item, + })); + + register_event_handler(t); + }, + terminate: function () { + mp.commandv("script-message-to", "console", "disable"); + }, + log: function (message, style, terminal_style) { + mp.commandv("script-message-to", "console", "log", JSON.stringify({ + 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 })); + }, + set_log: function (log) { + mp.commandv("script-message-to", "console", "set-log", + JSON.stringify(log)); + } +} + +/********************************************************************** * various *********************************************************************/ g.print = mp.msg.info; // convenient alias mp.get_script_name = function() { return mp.script_name }; mp.get_script_file = function() { return mp.script_file }; +mp.get_script_directory = function() { return mp.script_path }; mp.get_time = function() { return mp.get_time_ms() / 1000 }; mp.utils.getcwd = function() { return mp.get_property("working-directory") }; +mp.utils.getpid = function() { return mp.get_property_number("pid") } +mp.utils.get_user_path = + function(p) { return mp.command_native(["expand-path", String(p)]) }; +mp.get_mouse_pos = function() { return mp.get_property_native("mouse-pos") }; +mp.utils.write_file = mp.utils._write_file.bind(null, false); +mp.utils.append_file = mp.utils._write_file.bind(null, true); mp.dispatch_event = dispatch_event; mp.process_timers = process_timers; mp.notify_idle_observers = notify_idle_observers; @@ -621,7 +804,7 @@ g.mp_event_loop = function mp_event_loop() { wait = 0; // poll the next one } else { wait = process_timers() / 1000; - if (wait != 0) { + if (wait != 0 && iobservers.length) { notify_idle_observers(); // can add timers -> recalculate wait wait = peek_timers_wait() / 1000; } @@ -629,4 +812,12 @@ g.mp_event_loop = function mp_event_loop() { } while (mp.keep_running); }; + +// let the user extend us, e.g. by adding items to mp.module_paths +var initjs = mp.find_config_file("init.js"); // ~~/init.js +if (initjs) + require(initjs.slice(0, -3)); // remove ".js" +else if ((initjs = mp.find_config_file(".init.js"))) + mp.msg.warn("Use init.js instead of .init.js (ignoring " + initjs + ")"); + })(this) |