summaryrefslogtreecommitdiffstats
path: root/player/lua/defaults.lua
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:36:56 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:36:56 +0000
commit51de1d8436100f725f3576aefa24a2bd2057bc28 (patch)
treec6d1d5264b6d40a8d7ca34129f36b7d61e188af3 /player/lua/defaults.lua
parentInitial commit. (diff)
downloadmpv-51de1d8436100f725f3576aefa24a2bd2057bc28.tar.xz
mpv-51de1d8436100f725f3576aefa24a2bd2057bc28.zip
Adding upstream version 0.37.0.upstream/0.37.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'player/lua/defaults.lua')
-rw-r--r--player/lua/defaults.lua836
1 files changed, 836 insertions, 0 deletions
diff --git a/player/lua/defaults.lua b/player/lua/defaults.lua
new file mode 100644
index 0000000..233d1d6
--- /dev/null
+++ b/player/lua/defaults.lua
@@ -0,0 +1,836 @@
+-- Compatibility shim for lua 5.2/5.3
+unpack = unpack or table.unpack
+
+-- these are used internally by lua.c
+mp.UNKNOWN_TYPE.info = "this value is inserted if the C type is not supported"
+mp.UNKNOWN_TYPE.type = "UNKNOWN_TYPE"
+
+mp.ARRAY.info = "native array"
+mp.ARRAY.type = "ARRAY"
+
+mp.MAP.info = "native map"
+mp.MAP.type = "MAP"
+
+function mp.get_script_name()
+ return mp.script_name
+end
+
+function mp.get_opt(key, def)
+ local opts = mp.get_property_native("options/script-opts")
+ local val = opts[key]
+ if val == nil then
+ val = def
+ end
+ return val
+end
+
+function mp.input_define_section(section, contents, flags)
+ if flags == nil or flags == "" then
+ flags = "default"
+ end
+ mp.commandv("define-section", section, contents, flags)
+end
+
+function mp.input_enable_section(section, flags)
+ if flags == nil then
+ flags = ""
+ end
+ mp.commandv("enable-section", section, flags)
+end
+
+function mp.input_disable_section(section)
+ mp.commandv("disable-section", section)
+end
+
+function mp.get_mouse_pos()
+ local m = mp.get_property_native("mouse-pos")
+ return m.x, m.y
+end
+
+-- 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, key_name, key_text)
+ local fn = dispatch_key_bindings[name]
+ if fn then
+ fn(name, state, key_name, key_text)
+ 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
+
+-- Set the list of key bindings. These will override the user's bindings, so
+-- you should use this sparingly.
+-- A call to this function will remove all bindings previously set with this
+-- function. For example, set_key_bindings({}) would remove all script defined
+-- key bindings.
+-- 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_press, callback_down, callback_up}
+-- key is the key string as used in input.conf, like "ctrl+a"
+--
+-- callback can be a string too, in which case the following will be added like
+-- an input.conf line: key .. " " .. callback
+-- (And callback_down is ignored.)
+function mp.set_key_bindings(list, section, flags)
+ local cfg = ""
+ for i = 1, #list do
+ local entry = list[i]
+ local key = entry[1]
+ local cb = entry[2]
+ local cb_down = entry[3]
+ 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
+ return
+ 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
+ cb()
+ end
+ end
+ cfg = cfg .. key .. " script-binding " ..
+ mp.script_name .. "/" .. mangle .. "\n"
+ else
+ cfg = cfg .. key .. " " .. cb .. "\n"
+ end
+ end
+ mp.input_define_section(section or default_section, cfg, flags)
+end
+
+function mp.enable_key_bindings(section, flags)
+ mp.input_enable_section(section or default_section, flags)
+end
+
+function mp.disable_key_bindings(section)
+ mp.input_disable_section(section or default_section)
+end
+
+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
+
+-- "Newer" and more convenient API
+
+local key_bindings = {}
+local key_binding_counter = 0
+local key_bindings_dirty = false
+
+function mp.flush_keybindings()
+ if not key_bindings_dirty then
+ return
+ end
+ key_bindings_dirty = false
+
+ for i = 1, 2 do
+ local section, flags
+ local def = i == 1
+ if def then
+ section = "input_" .. mp.script_name
+ flags = "default"
+ else
+ section = "input_forced_" .. mp.script_name
+ flags = "force"
+ end
+ local bindings = {}
+ for k, v in pairs(key_bindings) do
+ if v.bind and v.forced ~= def then
+ bindings[#bindings + 1] = v
+ end
+ end
+ table.sort(bindings, function(a, b)
+ return a.priority < b.priority
+ end)
+ local cfg = ""
+ for _, v in ipairs(bindings) do
+ cfg = cfg .. v.bind .. "\n"
+ end
+ mp.input_define_section(section, cfg, flags)
+ -- TODO: remove the section if the script is stopped
+ mp.input_enable_section(section, "allow-hide-cursor+allow-vo-dragging")
+ end
+end
+
+local function add_binding(attrs, key, name, fn, rp)
+ if type(name) ~= "string" and name ~= nil then
+ rp = fn
+ fn = name
+ name = nil
+ end
+ rp = rp or ""
+ if name == nil then
+ name = reserve_binding()
+ end
+ local repeatable = rp == "repeatable" or rp["repeatable"]
+ 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, key_name, key_text)
+ if key_text == "" then
+ key_text = nil
+ end
+ fn({
+ event = key_states[state:sub(1, 1)] or "unknown",
+ is_mouse = state:sub(2, 2) == "m",
+ key_name = key_name,
+ key_text = key_text,
+ })
+ 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 event == "r" and not repeatable then
+ return
+ end
+ if is_mouse and (event == "u" or event == "p") then
+ fn()
+ elseif not is_mouse and (event == "d" or event == "r" or event == "p") then
+ fn()
+ end
+ end
+ msg_cb = fn
+ end
+ if key and #key > 0 then
+ attrs.bind = key .. " script-binding " .. mp.script_name .. "/" .. name
+ end
+ attrs.name = name
+ -- new bindings override old ones (but do not overwrite them)
+ key_binding_counter = key_binding_counter + 1
+ attrs.priority = key_binding_counter
+ key_bindings[name] = attrs
+ key_bindings_dirty = true
+ dispatch_key_bindings[name] = key_cb
+ mp.register_script_message(name, msg_cb)
+end
+
+function mp.add_key_binding(...)
+ add_binding({forced=false}, ...)
+end
+
+function mp.add_forced_key_binding(...)
+ add_binding({forced=true}, ...)
+end
+
+function mp.remove_key_binding(name)
+ key_bindings[name] = nil
+ dispatch_key_bindings[name] = nil
+ key_bindings_dirty = true
+ mp.unregister_script_message(name)
+end
+
+local timers = {}
+
+local timer_mt = {}
+timer_mt.__index = timer_mt
+
+function mp.add_timeout(seconds, cb, disabled)
+ local t = mp.add_periodic_timer(seconds, cb, disabled)
+ t.oneshot = true
+ return t
+end
+
+function mp.add_periodic_timer(seconds, cb, disabled)
+ local t = {
+ timeout = seconds,
+ cb = cb,
+ oneshot = false,
+ }
+ setmetatable(t, timer_mt)
+ if not disabled then
+ t:resume()
+ end
+ return t
+end
+
+function timer_mt.stop(t)
+ if timers[t] then
+ timers[t] = nil
+ t.next_deadline = t.next_deadline - mp.get_time()
+ end
+end
+
+function timer_mt.kill(t)
+ timers[t] = nil
+ t.next_deadline = nil
+end
+mp.cancel_timer = timer_mt.kill
+
+function timer_mt.resume(t)
+ if not timers[t] then
+ local timeout = t.next_deadline
+ if timeout == nil then
+ timeout = t.timeout
+ end
+ t.next_deadline = mp.get_time() + timeout
+ timers[t] = t
+ end
+end
+
+function timer_mt.is_enabled(t)
+ return timers[t] ~= nil
+end
+
+-- Return the timer that expires next.
+local function get_next_timer()
+ local best = nil
+ for t, _ in pairs(timers) do
+ if best == nil or t.next_deadline < best.next_deadline then
+ best = t
+ end
+ end
+ return best
+end
+
+function mp.get_next_timeout()
+ local timer = get_next_timer()
+ if not timer then
+ return
+ end
+ local now = mp.get_time()
+ return timer.next_deadline - now
+end
+
+-- Run timers that have met their deadline at the time of invocation.
+-- Return: time>0 in seconds till the next due timer, 0 if there are due timers
+-- (aborted to avoid infinite loop), or nil if no timers
+local function process_timers()
+ local t0 = nil
+ while true do
+ local timer = get_next_timer()
+ if not timer then
+ return
+ end
+ local now = mp.get_time()
+ local wait = timer.next_deadline - now
+ if wait > 0 then
+ return wait
+ else
+ if not t0 then
+ t0 = now -- first due callback: always executes, remember t0
+ elseif timer.next_deadline > t0 then
+ -- don't block forever with slow callbacks and endless timers.
+ -- we'll continue right after checking mpv events.
+ return 0
+ end
+
+ if timer.oneshot then
+ timer:kill()
+ else
+ timer.next_deadline = now + timer.timeout
+ end
+ timer.cb()
+ end
+ end
+end
+
+local messages = {}
+
+function mp.register_script_message(name, fn)
+ messages[name] = fn
+end
+
+function mp.unregister_script_message(name)
+ messages[name] = nil
+end
+
+local function message_dispatch(ev)
+ if #ev.args > 0 then
+ local handler = messages[ev.args[1]]
+ if handler then
+ handler(unpack(ev.args, 2))
+ end
+ end
+end
+
+local property_id = 0
+local properties = {}
+
+function mp.observe_property(name, t, cb)
+ local id = property_id + 1
+ property_id = id
+ properties[id] = cb
+ mp.raw_observe_property(id, name, t)
+end
+
+function mp.unobserve_property(cb)
+ for prop_id, prop_cb in pairs(properties) do
+ if cb == prop_cb then
+ properties[prop_id] = nil
+ mp.raw_unobserve_property(prop_id)
+ end
+ end
+end
+
+local function property_change(ev)
+ local prop = properties[ev.id]
+ if prop then
+ prop(ev.name, ev.data)
+ end
+end
+
+-- used by default event loop (mp_event_loop()) to decide when to quit
+mp.keep_running = true
+
+local event_handlers = {}
+
+function mp.register_event(name, cb)
+ local list = event_handlers[name]
+ if not list then
+ list = {}
+ event_handlers[name] = list
+ end
+ list[#list + 1] = cb
+ return mp.request_event(name, true)
+end
+
+function mp.unregister_event(cb)
+ for name, sub in pairs(event_handlers) do
+ local found = false
+ for i, e in ipairs(sub) do
+ if e == cb then
+ found = true
+ break
+ end
+ end
+ if found then
+ -- create a new array, just in case this function was called
+ -- from an event handler
+ local new = {}
+ for i = 1, #sub do
+ if sub[i] ~= cb then
+ new[#new + 1] = sub[i]
+ end
+ end
+ event_handlers[name] = new
+ if #new == 0 then
+ mp.request_event(name, false)
+ end
+ end
+ end
+end
+
+-- default handlers
+mp.register_event("shutdown", function() mp.keep_running = false end)
+mp.register_event("client-message", message_dispatch)
+mp.register_event("property-change", property_change)
+
+-- called before the event loop goes back to sleep
+local idle_handlers = {}
+
+function mp.register_idle(cb)
+ idle_handlers[#idle_handlers + 1] = cb
+end
+
+function mp.unregister_idle(cb)
+ local new = {}
+ for _, handler in ipairs(idle_handlers) do
+ if handler ~= cb then
+ new[#new + 1] = handler
+ end
+ end
+ idle_handlers = new
+end
+
+-- 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,
+ error = function(...) return mp.log("error", ...) end,
+ warn = function(...) return mp.log("warn", ...) end,
+ info = function(...) return mp.log("info", ...) end,
+ verbose = function(...) return mp.log("v", ...) end,
+ debug = function(...) return mp.log("debug", ...) end,
+ trace = function(...) return mp.log("trace", ...) end,
+}
+
+_G.print = mp.msg.info
+
+package.loaded["mp"] = mp
+package.loaded["mp.msg"] = mp.msg
+
+function mp.wait_event(t)
+ local r = mp.raw_wait_event(t)
+ if r and r.file_error and not r.error then
+ -- compat; deprecated
+ r.error = r.file_error
+ end
+ return r
+end
+
+_G.mp_event_loop = function()
+ mp.dispatch_events(true)
+end
+
+local function call_event_handlers(e)
+ local handlers = event_handlers[e.event]
+ if handlers then
+ for _, handler in ipairs(handlers) do
+ handler(e)
+ end
+ end
+end
+
+mp.use_suspend = false
+
+local suspend_warned = false
+
+function mp.dispatch_events(allow_wait)
+ local more_events = true
+ if mp.use_suspend then
+ if not suspend_warned then
+ mp.msg.error("mp.use_suspend is now ignored.")
+ suspend_warned = true
+ end
+ end
+ while mp.keep_running do
+ local wait = 0
+ if not more_events then
+ wait = process_timers() or 1e20 -- infinity for all practical purposes
+ if wait ~= 0 then
+ local idle_called = nil
+ for _, handler in ipairs(idle_handlers) do
+ idle_called = true
+ handler()
+ end
+ if idle_called then
+ -- handlers don't complete in 0 time, and may modify timers
+ wait = mp.get_next_timeout() or 1e20
+ if wait < 0 then
+ wait = 0
+ end
+ end
+ end
+ if allow_wait ~= true then
+ return
+ end
+ end
+ local e = mp.wait_event(wait)
+ more_events = false
+ if e.event ~= "none" then
+ call_event_handlers(e)
+ more_events = true
+ end
+ end
+end
+
+mp.register_idle(mp.flush_keybindings)
+
+-- additional helpers
+
+function mp.osd_message(text, duration)
+ if not duration then
+ duration = "-1"
+ else
+ duration = tostring(math.floor(duration * 1000))
+ end
+ mp.commandv("show-text", text, duration)
+end
+
+local hook_table = {}
+
+local hook_mt = {}
+hook_mt.__index = hook_mt
+
+function hook_mt.cont(t)
+ if t._id == nil then
+ mp.msg.error("hook already continued")
+ else
+ mp.raw_hook_continue(t._id)
+ t._id = nil
+ end
+end
+
+function hook_mt.defer(t)
+ t._defer = true
+end
+
+mp.register_event("hook", function(ev)
+ local fn = hook_table[tonumber(ev.id)]
+ local hookobj = {
+ _id = ev.hook_id,
+ _defer = false,
+ }
+ setmetatable(hookobj, hook_mt)
+ if fn then
+ fn(hookobj)
+ end
+ if not hookobj._defer and hookobj._id ~= nil then
+ hookobj:cont()
+ end
+end)
+
+function mp.add_hook(name, pri, cb)
+ local id = #hook_table + 1
+ hook_table[id] = cb
+ -- The C API suggests using 0 for a neutral priority, but lua.rst suggests
+ -- 50 (?), so whatever.
+ mp.raw_hook_add(id, name, pri - 50)
+end
+
+local async_call_table = {}
+local async_next_id = 1
+
+function mp.command_native_async(node, cb)
+ local id = async_next_id
+ async_next_id = async_next_id + 1
+ cb = cb or function() end
+ local res, err = mp.raw_command_native_async(id, node)
+ if not res then
+ mp.add_timeout(0, function() cb(false, nil, err) end)
+ return res, err
+ end
+ local t = {cb = cb, id = id}
+ async_call_table[id] = t
+ return t
+end
+
+mp.register_event("command-reply", function(ev)
+ local id = tonumber(ev.id)
+ local t = async_call_table[id]
+ local cb = t.cb
+ t.id = nil
+ async_call_table[id] = nil
+ if ev.error then
+ cb(false, nil, ev.error)
+ else
+ cb(true, ev.result, nil)
+ end
+end)
+
+function mp.abort_async_command(t)
+ if t.id ~= nil then
+ mp.raw_abort_async_command(t.id)
+ end
+end
+
+local overlay_mt = {}
+overlay_mt.__index = overlay_mt
+local overlay_new_id = 0
+
+function mp.create_osd_overlay(format)
+ overlay_new_id = overlay_new_id + 1
+ local overlay = {
+ format = format,
+ id = overlay_new_id,
+ data = "",
+ res_x = 0,
+ res_y = 720,
+ }
+ setmetatable(overlay, overlay_mt)
+ return overlay
+end
+
+function overlay_mt.update(ov)
+ local cmd = {}
+ for k, v in pairs(ov) do
+ cmd[k] = v
+ end
+ cmd.name = "osd-overlay"
+ cmd.res_x = math.floor(cmd.res_x)
+ cmd.res_y = math.floor(cmd.res_y)
+ return mp.command_native(cmd)
+end
+
+function overlay_mt.remove(ov)
+ mp.command_native {
+ name = "osd-overlay",
+ id = ov.id,
+ format = "none",
+ data = "",
+ }
+end
+
+-- legacy API
+function mp.set_osd_ass(res_x, res_y, data)
+ if not mp._legacy_overlay then
+ mp._legacy_overlay = mp.create_osd_overlay("ass-events")
+ end
+ if mp._legacy_overlay.res_x ~= res_x or
+ mp._legacy_overlay.res_y ~= res_y or
+ mp._legacy_overlay.data ~= data
+ then
+ mp._legacy_overlay.res_x = res_x
+ mp._legacy_overlay.res_y = res_y
+ mp._legacy_overlay.data = data
+ mp._legacy_overlay:update()
+ end
+end
+
+function mp.get_osd_size()
+ local prop = mp.get_property_native("osd-dimensions")
+ return prop.w, prop.h, prop.aspect
+end
+
+function mp.get_osd_margins()
+ local prop = mp.get_property_native("osd-dimensions")
+ return prop.ml, prop.mt, prop.mr, prop.mb
+end
+
+local mp_utils = package.loaded["mp.utils"]
+
+function mp_utils.format_table(t, set)
+ if not set then
+ set = { [t] = true }
+ end
+ local res = "{"
+ -- pretty expensive but simple way to distinguish array and map parts of t
+ local keys = {}
+ local vals = {}
+ local arr = 0
+ for i = 1, #t do
+ if t[i] == nil then
+ break
+ end
+ keys[i] = i
+ vals[i] = t[i]
+ arr = i
+ end
+ for k, v in pairs(t) do
+ if not (type(k) == "number" and k >= 1 and k <= arr and keys[k]) then
+ keys[#keys + 1] = k
+ vals[#keys] = v
+ end
+ end
+ for i = 1, #keys do
+ if #res > 1 then
+ res = res .. ", "
+ end
+ if i > arr then
+ res = res .. mp_utils.to_string(keys[i], set) .. " = "
+ end
+ res = res .. mp_utils.to_string(vals[i], set)
+ end
+ res = res .. "}"
+ return res
+end
+
+function mp_utils.to_string(v, set)
+ if type(v) == "string" then
+ return "\"" .. v .. "\""
+ elseif type(v) == "table" then
+ if set then
+ if set[v] then
+ return "[cycle]"
+ end
+ set[v] = true
+ end
+ return mp_utils.format_table(v, set)
+ else
+ return tostring(v)
+ end
+end
+
+function mp_utils.getcwd()
+ return mp.get_property("working-directory")
+end
+
+function mp_utils.getpid()
+ return mp.get_property_number("pid")
+end
+
+function mp_utils.format_bytes_humanized(b)
+ local d = {"Bytes", "KiB", "MiB", "GiB", "TiB", "PiB"}
+ local i = 1
+ while b >= 1024 do
+ b = b / 1024
+ i = i + 1
+ end
+ return string.format("%0.2f %s", b, d[i] and d[i] or "*1024^" .. (i-1))
+end
+
+function mp_utils.subprocess(t)
+ local cmd = {}
+ cmd.name = "subprocess"
+ cmd.capture_stdout = true
+ for k, v in pairs(t) do
+ if k == "cancellable" then
+ k = "playback_only"
+ elseif k == "max_size" then
+ k = "capture_size"
+ end
+ cmd[k] = v
+ end
+ local res, err = mp.command_native(cmd)
+ if res == nil then
+ -- an error usually happens only if parsing failed (or no args passed)
+ res = {error_string = err, status = -1}
+ end
+ if res.error_string ~= "" then
+ res.error = res.error_string
+ end
+ return res
+end
+
+function mp_utils.subprocess_detached(t)
+ mp.commandv("run", unpack(t.args))
+end
+
+function mp_utils.shared_script_property_set(name, value)
+ if value ~= nil then
+ -- no such thing as change-list with mpv_node, so build a string value
+ mp.commandv("change-list", "shared-script-properties", "append",
+ name .. "=" .. value)
+ else
+ mp.commandv("change-list", "shared-script-properties", "remove", name)
+ end
+end
+
+function mp_utils.shared_script_property_get(name)
+ local map = mp.get_property_native("shared-script-properties")
+ return map and map[name]
+end
+
+-- cb(name, value) on change and on init
+function mp_utils.shared_script_property_observe(name, cb)
+ -- it's _very_ wasteful to observe the mpv core "super" property for every
+ -- shared sub-property, but then again you shouldn't use this
+ mp.observe_property("shared-script-properties", "native", function(_, val)
+ cb(name, val and val[name])
+ end)
+end
+
+return {}