2013-09-25 22:56:41 +00:00
|
|
|
|
-- osc.lua
|
|
|
|
|
|
|
|
|
|
local assdraw = require 'mp.assdraw'
|
|
|
|
|
local msg = require 'mp.msg'
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
|
-- Parameters
|
|
|
|
|
--
|
|
|
|
|
|
2013-09-26 16:27:11 +00:00
|
|
|
|
-- default user option values
|
|
|
|
|
-- do not touch, change them in plugin_osc.conf
|
2013-09-25 22:56:41 +00:00
|
|
|
|
local user_opts = {
|
2013-09-27 18:12:13 +00:00
|
|
|
|
showWindowed = true, -- show OSC when windowed?
|
|
|
|
|
showFullscreen = true, -- show OSC when fullscreen?
|
2013-09-25 22:56:41 +00:00
|
|
|
|
scaleWindowed = 1, -- scaling of the controller when windowed
|
|
|
|
|
scaleFullscreen = 1, -- scaling of the controller when fullscreen
|
2013-10-06 21:44:46 +00:00
|
|
|
|
scaleForcedWindow = 2, -- scaling of the controller when rendered on a forced (dummy) window
|
2013-09-25 22:56:41 +00:00
|
|
|
|
vidscale = true, -- scale the controller with the video?
|
|
|
|
|
valign = 0.8, -- vertical alignment, -1 (top) to 1 (bottom)
|
|
|
|
|
halign = 0, -- horizontal alignment, -1 (left) to 1 (right)
|
2013-10-13 01:51:25 +00:00
|
|
|
|
hidetimeout = 500, -- duration in ms until the OSC hides if no mouse movement
|
2013-09-25 22:56:41 +00:00
|
|
|
|
fadeduration = 200, -- duration of fade out in ms, 0 = no fade
|
2013-10-13 01:51:25 +00:00
|
|
|
|
deadzonesize = 0, -- distance between OSC and deadzone
|
|
|
|
|
minmousemove = 1, -- minimum amount of pixeles the mouse has to move between ticks to make the OSC show up
|
2013-10-06 22:04:35 +00:00
|
|
|
|
iAmAProgrammer = false, -- use native mpv values and disable OSC internal playlist management (and some functions that depend on it)
|
2013-09-25 22:56:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local osc_param = {
|
|
|
|
|
osc_w = 550, -- width, height, corner-radius, padding of the OSC box
|
|
|
|
|
osc_h = 138,
|
|
|
|
|
osc_r = 10,
|
|
|
|
|
osc_p = 15,
|
|
|
|
|
|
|
|
|
|
-- calculated by osc_init()
|
|
|
|
|
playresy = 0, -- canvas size Y
|
|
|
|
|
playresx = 0, -- canvas size X
|
|
|
|
|
posX, posY = 0,0, -- position of the controler
|
|
|
|
|
pos_offsetX, pos_offsetY = 0,0, -- vertical/horizontal position offset for contents aligned at the borders of the box
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local osc_styles = {
|
2013-09-26 16:27:11 +00:00
|
|
|
|
bigButtons = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs50\\fnmpv-osd-symbols}",
|
2013-09-27 18:12:13 +00:00
|
|
|
|
smallButtonsL = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs20\\fnmpv-osd-symbols}",
|
2013-09-25 22:56:41 +00:00
|
|
|
|
smallButtonsLlabel = "{\\fs17\\fn" .. mp.property_get("options/osd-font") .. "}",
|
2013-09-26 16:27:11 +00:00
|
|
|
|
smallButtonsR = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs30\\fnmpv-osd-symbols}",
|
2013-09-25 22:56:41 +00:00
|
|
|
|
|
|
|
|
|
elementDown = "{\\1c&H999999}",
|
2013-09-26 16:27:11 +00:00
|
|
|
|
timecodes = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs20}",
|
|
|
|
|
vidtitle = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs12}",
|
|
|
|
|
box = "{\\rDefault\\blur0\\bord1\\1c&H000000\\3c&HFFFFFF}",
|
2013-09-25 22:56:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-- internal states, do not touch
|
|
|
|
|
local state = {
|
2013-10-13 01:51:25 +00:00
|
|
|
|
showtime, -- time of last invokation (last mouse move)
|
2013-09-25 22:56:41 +00:00
|
|
|
|
osc_visible = false,
|
|
|
|
|
anistart, -- time when the animation started
|
|
|
|
|
anitype, -- current type of animation
|
|
|
|
|
animation, -- current animation alpha
|
|
|
|
|
mouse_down_counter = 0, -- used for softrepeat
|
|
|
|
|
active_element = nil, -- nil = none, 0 = background, 1+ = see elements[]
|
|
|
|
|
active_event_source = nil, -- the "button" that issued the current event
|
|
|
|
|
rightTC_trem = true, -- if the right timcode should display total or remaining time
|
|
|
|
|
tc_ms = false, -- Should the timecodes display their time with milliseconds
|
|
|
|
|
mp_screen_sizeX, mp_screen_sizeY, -- last screen-resolution, to detect resolution changes to issue reINITs
|
|
|
|
|
initREQ = false, -- is a re-init request pending?
|
|
|
|
|
last_seek, -- last seek position, to avoid deadlocks by repeatedly seeking to the same position
|
2013-10-13 01:51:25 +00:00
|
|
|
|
last_mouseX, last_mouseY, -- last mouse position, to detect siginificant mouse movement
|
2013-09-25 22:56:41 +00:00
|
|
|
|
message_text,
|
|
|
|
|
message_timeout,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
|
-- User Settings Management
|
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
function val2str(val)
|
|
|
|
|
local strval = val
|
|
|
|
|
if type(val) == "boolean" then
|
|
|
|
|
if val then strval = "yes" else strval = "no" end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return strval
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- converts val to type of desttypeval
|
|
|
|
|
function typeconv(desttypeval, val)
|
|
|
|
|
if type(desttypeval) == "boolean" then
|
|
|
|
|
if val == "yes" then
|
|
|
|
|
val = true
|
|
|
|
|
elseif val == "no" then
|
|
|
|
|
val = false
|
|
|
|
|
else
|
2013-10-06 21:44:46 +00:00
|
|
|
|
msg.error("Error: Can't convert " .. val .. " to boolean!")
|
2013-09-25 22:56:41 +00:00
|
|
|
|
val = nil
|
|
|
|
|
end
|
|
|
|
|
elseif type(desttypeval) == "number" then
|
|
|
|
|
if not (tonumber(val) == nil) then
|
|
|
|
|
val = tonumber(val)
|
|
|
|
|
else
|
2013-10-06 21:44:46 +00:00
|
|
|
|
msg.error("Error: Can't convert " .. val .. " to number!")
|
2013-09-25 22:56:41 +00:00
|
|
|
|
val = nil
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return val
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- Automagical config handling
|
|
|
|
|
-- options: A table with options setable via config with assigned default values. The type of the default values is important for
|
|
|
|
|
-- converting the values read from the config file back. Do not use "nil" as a default value!
|
|
|
|
|
-- identifier: A simple indentifier string for the config file. Make sure this doesn't collide with other scripts.
|
|
|
|
|
|
|
|
|
|
-- How does it work:
|
|
|
|
|
-- Existance of the configfile will be checked, if it doesn't exist, the default values from the options table will be written in a new
|
|
|
|
|
-- file, commented out. If it exits, the key/value pairs will be read, and values of keys that exist in the options table will overwrite
|
|
|
|
|
-- their value. Keys that don't exist in the options table will be ignored, keys that don't exits in the config will keep their default
|
|
|
|
|
-- value. The value's types will automatically be converted to the type used in the options table.
|
|
|
|
|
function read_config(options, identifier)
|
|
|
|
|
|
|
|
|
|
local conffilename = "plugin_" .. identifier .. ".conf"
|
|
|
|
|
local conffile = mp.find_config_file(conffilename)
|
|
|
|
|
local f = io.open(conffile,"r")
|
|
|
|
|
if f == nil then
|
|
|
|
|
msg.warn(conffile.." does not exist, creating it ...")
|
|
|
|
|
-- so create it, write default options
|
|
|
|
|
local f = io.open(conffile,"w+")
|
|
|
|
|
f:write("# Config file for "..identifier.."\n# <-- works only at beginning of line.\n# Do not have any spare spaces flying around.\n\n")
|
|
|
|
|
-- iterate over the options table
|
|
|
|
|
for key, value in pairs(options) do
|
|
|
|
|
f:write("#" .. key .. "=" .. val2str(value) .. "\n")
|
|
|
|
|
end
|
|
|
|
|
io.close(f)
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
-- config exists, read values
|
|
|
|
|
local linecounter = 1
|
|
|
|
|
for line in f:lines() do
|
|
|
|
|
if string.find(line, "#") == 1 then
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
local eqpos = string.find(line, "=")
|
|
|
|
|
if eqpos == nil then
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
local key = string.sub(line, 1, eqpos-1)
|
|
|
|
|
local val = string.sub(line, eqpos+1)
|
|
|
|
|
|
|
|
|
|
-- match found values with defaults
|
|
|
|
|
if options[key] == nil then
|
|
|
|
|
msg.warn(conffilename..":"..linecounter.." unknown key " .. key .. ", ignoring")
|
|
|
|
|
else
|
|
|
|
|
local convval = typeconv(options[key], val)
|
|
|
|
|
if convval == nil then
|
|
|
|
|
msg.error(conffilename..":"..linecounter.." error converting value '" .. val .. "' for key '" .. key .. "'")
|
|
|
|
|
else
|
|
|
|
|
options[key] = convval
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
linecounter = linecounter + 1
|
|
|
|
|
end
|
|
|
|
|
io.close(f)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- read configfile
|
|
|
|
|
read_config(user_opts, "osc")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
|
-- Helperfunctions
|
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
function scale_value(x0, x1, y0, y1, val)
|
|
|
|
|
local m = (y1 - y0) / (x1 - x0)
|
|
|
|
|
local b = y0 - (m * x0)
|
|
|
|
|
return (m * val) + b
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- returns hitbox spanning coordinates (top left, bottom right corner) according to alignment
|
|
|
|
|
function get_hitbox_coords(x, y, an, w, h)
|
|
|
|
|
|
|
|
|
|
local alignments = {
|
|
|
|
|
[1] = function () return x, y-h, x+w, y end,
|
|
|
|
|
[2] = function () return x-(w/2), y-h, x+(w/2), y end,
|
|
|
|
|
[3] = function () return x-w, y-h, x, y end,
|
|
|
|
|
|
|
|
|
|
[4] = function () return x, y-(h/2), x+w, y+(h/2) end,
|
|
|
|
|
[5] = function () return x-(w/2), y-(h/2), x+(w/2), y+(h/2) end,
|
|
|
|
|
[6] = function () return x-w, y-(h/2), x, y+(h/2) end,
|
|
|
|
|
|
|
|
|
|
[7] = function () return x, y, x+w, y+h end,
|
|
|
|
|
[8] = function () return x-(w/2), y, x+(w/2), y+h end,
|
|
|
|
|
[9] = function () return x-w, y, x, y+h end,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return alignments[an]()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function get_element_hitbox(element)
|
|
|
|
|
return element.hitbox.x1, element.hitbox.y1, element.hitbox.x2, element.hitbox.y2
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function mouse_hit(element)
|
|
|
|
|
local mX, mY = mp.get_mouse_pos()
|
|
|
|
|
local bX1, bY1, bX2, bY2 = get_element_hitbox(element)
|
|
|
|
|
|
|
|
|
|
return (mX >= bX1 and mX <= bX2 and mY >= bY1 and mY <= bY2)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function limit_range(min, max, val)
|
|
|
|
|
if val > max then
|
|
|
|
|
val = max
|
|
|
|
|
elseif val < min then
|
|
|
|
|
val = min
|
|
|
|
|
end
|
|
|
|
|
return val
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function get_slider_value(element)
|
|
|
|
|
local fill_offsetV = element.metainfo.slider.border + element.metainfo.slider.gap
|
|
|
|
|
local paddingH = (element.h - (2*fill_offsetV)) / 2
|
|
|
|
|
|
|
|
|
|
local b_x1, b_x2 = element.hitbox.x1 + paddingH, element.hitbox.x2 - paddingH
|
|
|
|
|
local s_min, s_max = element.metainfo.slider.min, element.metainfo.slider.max
|
|
|
|
|
|
|
|
|
|
local pos = scale_value(b_x1, b_x2, s_min, s_max, mp.get_mouse_pos())
|
|
|
|
|
|
|
|
|
|
return limit_range(s_min, s_max, pos)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function countone(val)
|
|
|
|
|
if not (user_opts.iAmAProgrammer) then
|
|
|
|
|
val = val + 1
|
|
|
|
|
end
|
|
|
|
|
return val
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- align: -1 .. +1
|
|
|
|
|
-- frame: size of the containing area
|
|
|
|
|
-- obj: size of the object that should be positioned inside the area
|
|
|
|
|
-- margin: min. distance from object to frame (as long as -1 <= align <= +1)
|
|
|
|
|
function get_align(align, frame, obj, margin)
|
|
|
|
|
return (frame / 2) + (((frame / 2) - margin - (obj / 2)) * align)
|
|
|
|
|
end
|
|
|
|
|
|
2013-09-26 16:27:11 +00:00
|
|
|
|
-- multiplies two alpha values, formular can probably be improved
|
|
|
|
|
function mult_alpha(alphaA, alphaB)
|
|
|
|
|
return 255 - (((1-(alphaA/255)) * (1-(alphaB/255))) * 255)
|
|
|
|
|
end
|
|
|
|
|
|
2013-09-25 22:56:41 +00:00
|
|
|
|
--
|
|
|
|
|
-- Tracklist Management
|
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
local nicetypes = {video = "Video", audio = "Audio", sub = "Subtitle"}
|
|
|
|
|
|
|
|
|
|
-- updates the OSC internal playlists, should be run each time the track-layout changes
|
|
|
|
|
function update_tracklist()
|
|
|
|
|
local tracktable = mp.get_track_list()
|
|
|
|
|
|
|
|
|
|
-- by osc_id
|
|
|
|
|
tracks_osc = {}
|
|
|
|
|
tracks_osc.video, tracks_osc.audio, tracks_osc.sub = {}, {}, {}
|
|
|
|
|
-- by mpv_id
|
|
|
|
|
tracks_mpv = {}
|
|
|
|
|
tracks_mpv.video, tracks_mpv.audio, tracks_mpv.sub = {}, {}, {}
|
|
|
|
|
for n = 1, #tracktable do
|
|
|
|
|
if not (tracktable[n].type == "unkown") then
|
|
|
|
|
local type = tracktable[n].type
|
|
|
|
|
local mpv_id = tonumber(tracktable[n].id)
|
|
|
|
|
|
|
|
|
|
-- by osc_id
|
|
|
|
|
table.insert(tracks_osc[type], tracktable[n])
|
|
|
|
|
|
|
|
|
|
-- by mpv_id
|
|
|
|
|
tracks_mpv[type][mpv_id] = tracktable[n]
|
|
|
|
|
tracks_mpv[type][mpv_id].osc_id = #tracks_osc[type]
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- return a nice list of tracks of the given type (video, audio, sub)
|
|
|
|
|
function get_tracklist(type)
|
|
|
|
|
local msg = "Available " .. nicetypes[type] .. " Tracks: "
|
|
|
|
|
local select_scale = 100
|
|
|
|
|
if #tracks_osc[type] == 0 then
|
|
|
|
|
msg = msg .. "none"
|
|
|
|
|
else
|
|
|
|
|
for n = 1, #tracks_osc[type] do
|
|
|
|
|
local track = tracks_osc[type][n]
|
|
|
|
|
local lang, title, selected = "unkown", "", "{\\fscx" .. select_scale .. "\\fscy" .. select_scale .. "}○{\\fscx100\\fscy100}"
|
|
|
|
|
if not(track.language == nil) then lang = track.language end
|
|
|
|
|
if not(track.title == nil) then title = track.title end
|
|
|
|
|
if (track.id == tonumber(mp.property_get(type))) then
|
|
|
|
|
selected = "{\\fscx" .. select_scale .. "\\fscy" .. select_scale .. "}●{\\fscx100\\fscy100}"
|
|
|
|
|
end
|
|
|
|
|
msg = msg .. "\n" .. selected .. " " .. n .. ": [" .. lang .. "] " .. title
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return msg
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- relatively change the track of given <type> by <next> tracks (+1 -> next, -1 -> previous)
|
|
|
|
|
function set_track(type, next)
|
|
|
|
|
local current_track_mpv, current_track_osc
|
|
|
|
|
if (mp.property_get(type) == "no") then
|
|
|
|
|
current_track_osc = 0
|
|
|
|
|
else
|
|
|
|
|
current_track_mpv = tonumber(mp.property_get(type))
|
|
|
|
|
current_track_osc = tracks_mpv[type][current_track_mpv].osc_id
|
|
|
|
|
end
|
|
|
|
|
local new_track_osc = (current_track_osc + next) % (#tracks_osc[type] + 1)
|
|
|
|
|
local new_track_mpv
|
|
|
|
|
if new_track_osc == 0 then
|
|
|
|
|
new_track_mpv = "no"
|
|
|
|
|
else
|
|
|
|
|
new_track_mpv = tracks_osc[type][new_track_osc].id
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
mp.send_command("no-osd set " .. type .. " " .. new_track_mpv)
|
|
|
|
|
|
|
|
|
|
if (new_track_osc == 0) then
|
|
|
|
|
show_message(nicetypes[type] .. " Track: none")
|
|
|
|
|
else
|
|
|
|
|
show_message(nicetypes[type] .. " Track: " .. new_track_osc .. "/" .. #tracks_osc[type]
|
|
|
|
|
.. " [" .. (tracks_osc[type][new_track_osc].language or "unkown") .. "] " .. (tracks_osc[type][new_track_osc].title or ""))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- get the currently selected track of <type>, OSC-style counted
|
|
|
|
|
function get_track(type)
|
|
|
|
|
local track = mp.property_get(type)
|
|
|
|
|
if (track == "no" or track == nil) then
|
|
|
|
|
return 0
|
|
|
|
|
else
|
|
|
|
|
return tracks_mpv[type][tonumber(track)].osc_id
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
|
-- Element Management
|
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
-- do not use this function, use the wrappers below
|
|
|
|
|
function register_element(type, x, y, an, w, h, style, content, eventresponder, metainfo2)
|
|
|
|
|
-- type button, slider or box
|
|
|
|
|
-- x, y position
|
|
|
|
|
-- an alignment (see ASS standard)
|
|
|
|
|
-- w, h size of hitbox
|
|
|
|
|
-- style main style
|
|
|
|
|
-- content what the element should display, can be a string or a function(ass)
|
|
|
|
|
-- eventresponder A table containing functions mapped to events that shall be run on those events
|
|
|
|
|
-- metainfo A table containing additional parameters for the element
|
|
|
|
|
|
|
|
|
|
-- set default metainfo
|
|
|
|
|
local metainfo = {}
|
|
|
|
|
if not (metainfo2 == nil) then metainfo = metainfo2 end
|
|
|
|
|
if metainfo.visible == nil then metainfo.visible = true end -- element visible at all?
|
|
|
|
|
if metainfo.enabled == nil then metainfo.enabled = true end -- element clickable?
|
|
|
|
|
if metainfo.styledown == nil then metainfo.styledown = true end -- should the element be styled with the elementDown style when clicked?
|
|
|
|
|
if metainfo.softrepeat == nil then metainfo.softrepeat = false end -- should the *_down event be executed with "hold for repeat" behaviour?
|
2013-09-26 16:27:11 +00:00
|
|
|
|
if metainfo.alpha1 == nil then metainfo.alpha1 = 0 end -- alpha1 of the element, 0 = opaque, 255 = transparent (primary fill alpha)
|
|
|
|
|
if metainfo.alpha2 == nil then metainfo.alpha2 = 255 end -- alpha1 of the element, 0 = opaque, 255 = transparent (secondary fill alpha)
|
|
|
|
|
if metainfo.alpha3 == nil then metainfo.alpha3 = 255 end -- alpha1 of the element, 0 = opaque, 255 = transparent (border alpha)
|
|
|
|
|
if metainfo.alpha4 == nil then metainfo.alpha4 = 255 end -- alpha1 of the element, 0 = opaque, 255 = transparent (shadow alpha)
|
2013-09-25 22:56:41 +00:00
|
|
|
|
|
|
|
|
|
if metainfo.visible then
|
|
|
|
|
local ass = assdraw.ass_new()
|
|
|
|
|
|
|
|
|
|
ass:append("{}") -- shitty hack to troll the new_event function into inserting a \n
|
|
|
|
|
ass:new_event()
|
|
|
|
|
ass:pos(x, y) -- positioning
|
|
|
|
|
ass:an(an)
|
|
|
|
|
ass:append(style) -- styling
|
|
|
|
|
|
|
|
|
|
-- if the element is supposed to be disabled, style it accordingly and kill the eventresponders
|
|
|
|
|
if metainfo.enabled == false then
|
2013-09-26 16:27:11 +00:00
|
|
|
|
metainfo.alpha1 = 136
|
2013-09-25 22:56:41 +00:00
|
|
|
|
eventresponder = nil
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- Calculate the hitbox
|
|
|
|
|
local bX1, bY1, bX2, bY2 = get_hitbox_coords(x, y, an, w, h)
|
|
|
|
|
local hitbox
|
|
|
|
|
if type == "slider" then
|
|
|
|
|
-- if it's a slider, cut the border and gap off, as those aren't of interest for eventhandling
|
|
|
|
|
local fill_offset = metainfo.slider.border + metainfo.slider.gap
|
|
|
|
|
hitbox = {x1 = bX1 + fill_offset, y1 = bY1 + fill_offset, x2 = bX2 - fill_offset, y2 = bY2 - fill_offset}
|
|
|
|
|
else
|
|
|
|
|
hitbox = {x1 = bX1, y1 = bY1, x2 = bX2, y2 = bY2}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local element = {
|
|
|
|
|
type = type,
|
|
|
|
|
elem_ass = ass,
|
|
|
|
|
hitbox = hitbox,
|
|
|
|
|
w = w,
|
|
|
|
|
h = h,
|
|
|
|
|
content = content,
|
|
|
|
|
eventresponder = eventresponder,
|
|
|
|
|
metainfo = metainfo,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table.insert(elements, element)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function register_button(x, y, an, w, h, style, content, eventresponder, metainfo)
|
|
|
|
|
register_element("button", x, y, an, w, h, style, content, eventresponder, metainfo)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function register_box(x, y, an, w, h, r, style, metainfo2)
|
|
|
|
|
local ass = assdraw.ass_new()
|
|
|
|
|
ass:draw_start()
|
|
|
|
|
ass:round_rect_cw(0, 0, w, h, r)
|
|
|
|
|
ass:draw_stop()
|
|
|
|
|
|
|
|
|
|
local metainfo = {}
|
|
|
|
|
if not (metainfo2 == nil) then metainfo = metainfo2 end
|
|
|
|
|
|
|
|
|
|
metainfo.styledown = false
|
|
|
|
|
|
|
|
|
|
register_element("box", x, y, an, w, h, style, ass, nil, metainfo)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function register_slider(x, y, an, w, h, style, min, max, markerF, posF, eventresponder, metainfo2)
|
|
|
|
|
local metainfo = {}
|
|
|
|
|
if not (metainfo2 == nil) then metainfo = metainfo2 end
|
|
|
|
|
local slider1 = {}
|
|
|
|
|
if (metainfo.slider == nil) then metainfo.slider = slider1 end
|
|
|
|
|
|
|
|
|
|
-- defaults
|
|
|
|
|
if min == nil then metainfo.slider.min = 0 else metainfo.slider.min = min end
|
|
|
|
|
if max == nil then metainfo.slider.max = 100 else metainfo.slider.max = max end
|
|
|
|
|
if metainfo.slider.border == nil then metainfo.slider.border = 1 end
|
|
|
|
|
if metainfo.slider.gap == nil then metainfo.slider.gap = 2 end
|
|
|
|
|
if metainfo.slider.type == nil then metainfo.slider.type = "slider" end
|
|
|
|
|
|
|
|
|
|
metainfo.slider.markerF = markerF
|
|
|
|
|
metainfo.slider.posF = posF
|
|
|
|
|
|
|
|
|
|
-- prepare the box with markers
|
|
|
|
|
local ass = assdraw.ass_new()
|
|
|
|
|
local border, gap = metainfo.slider.border, metainfo.slider.gap
|
|
|
|
|
local fill_offsetV = border + gap -- Vertical offset between element outline and drag-area
|
|
|
|
|
local fill_offsetH = h / 2 -- Horizontal offset between element outline and drag-area
|
|
|
|
|
|
|
|
|
|
ass:draw_start()
|
|
|
|
|
|
|
|
|
|
-- the box
|
|
|
|
|
ass:rect_cw(0, 0, w, h);
|
|
|
|
|
|
|
|
|
|
-- the "hole"
|
|
|
|
|
ass:rect_ccw(border, border, w - border, h - border)
|
|
|
|
|
|
|
|
|
|
-- marker nibbles
|
|
|
|
|
if not (markerF == nil) and gap > 0 then
|
|
|
|
|
local markers = markerF()
|
|
|
|
|
for n = 1, #markers do
|
|
|
|
|
if (markers[n] > min) and (markers[n] < max) then
|
|
|
|
|
|
|
|
|
|
local coordL, coordR = fill_offsetH, (w - fill_offsetH)
|
|
|
|
|
|
|
|
|
|
local s = scale_value(min, max, coordL, coordR, markers[n])
|
|
|
|
|
|
|
|
|
|
if gap > 1 then
|
|
|
|
|
-- draw triangles
|
|
|
|
|
local a = gap / 0.5 --0.866
|
|
|
|
|
--top
|
|
|
|
|
ass:move_to(s - (a/2), border)
|
|
|
|
|
ass:line_to(s + (a/2), border)
|
|
|
|
|
ass:line_to(s, border + gap)
|
|
|
|
|
|
|
|
|
|
--bottom
|
|
|
|
|
ass:move_to(s - (a/2), h - border)
|
|
|
|
|
ass:line_to(s, h - border - gap)
|
|
|
|
|
ass:line_to(s + (a/2), h - border)
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
-- draw 1px nibbles
|
|
|
|
|
ass:rect_cw(s - 0.5, border, s + 0.5, border*2);
|
|
|
|
|
ass:rect_cw(s - 0.5, h - border*2, s + 0.5, h - border);
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
register_element("slider", x, y, an, w, h, style, ass, eventresponder, metainfo)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
|
-- Element Rendering
|
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
function render_elements(master_ass)
|
|
|
|
|
|
|
|
|
|
for n = 1, #elements do
|
|
|
|
|
|
|
|
|
|
local element = elements[n]
|
|
|
|
|
local elem_ass = assdraw.ass_new()
|
|
|
|
|
local elem_ass1 = element.elem_ass
|
|
|
|
|
elem_ass:merge(elem_ass1)
|
|
|
|
|
|
|
|
|
|
--alpha
|
2013-09-26 16:27:11 +00:00
|
|
|
|
local alpha1 = element.metainfo.alpha1
|
|
|
|
|
local alpha2 = element.metainfo.alpha2
|
|
|
|
|
local alpha3 = element.metainfo.alpha3
|
|
|
|
|
local alpha4 = element.metainfo.alpha4
|
2013-09-25 22:56:41 +00:00
|
|
|
|
|
|
|
|
|
if not(state.animation == nil) then
|
2013-09-26 16:27:11 +00:00
|
|
|
|
alpha1 = mult_alpha(element.metainfo.alpha1, state.animation)
|
|
|
|
|
alpha2 = mult_alpha(element.metainfo.alpha2, state.animation)
|
|
|
|
|
alpha3 = mult_alpha(element.metainfo.alpha3, state.animation)
|
|
|
|
|
alpha4 = mult_alpha(element.metainfo.alpha4, state.animation)
|
2013-09-25 22:56:41 +00:00
|
|
|
|
end
|
|
|
|
|
|
2013-09-26 16:27:11 +00:00
|
|
|
|
elem_ass:append(string.format("{\\1a&H%X&\\2a&H%X&\\3a&H%X&\\4a&H%X&}", alpha1, alpha2, alpha3, alpha4))
|
2013-09-25 22:56:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if state.active_element == n then
|
|
|
|
|
|
|
|
|
|
-- run render event functions
|
|
|
|
|
if not (element.eventresponder.render == nil) then
|
|
|
|
|
element.eventresponder.render(element)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if mouse_hit(element) then
|
|
|
|
|
-- mouse down styling
|
|
|
|
|
if element.metainfo.styledown then
|
|
|
|
|
elem_ass:append(osc_styles.elementDown)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if (element.metainfo.softrepeat == true) and (state.mouse_down_counter >= 15 and state.mouse_down_counter % 5 == 0) then
|
|
|
|
|
element.eventresponder[state.active_event_source .. "_down"](element)
|
|
|
|
|
end
|
|
|
|
|
state.mouse_down_counter = state.mouse_down_counter + 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if element.type == "slider" then
|
|
|
|
|
|
|
|
|
|
elem_ass:merge(element.content) -- ASS objects
|
|
|
|
|
-- draw pos marker
|
|
|
|
|
|
|
|
|
|
local pos = element.metainfo.slider.posF()
|
|
|
|
|
|
|
|
|
|
if not (pos == nil) then
|
|
|
|
|
|
|
|
|
|
if pos > element.metainfo.slider.max then
|
|
|
|
|
pos = element.metainfo.slider.max
|
|
|
|
|
elseif pos < element.metainfo.slider.min then
|
|
|
|
|
pos = element.metainfo.slider.min
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local fill_offsetV = element.metainfo.slider.border + element.metainfo.slider.gap
|
|
|
|
|
local fill_offsetH = element.h/2
|
|
|
|
|
|
|
|
|
|
local coordL, coordR = fill_offsetH, (element.w - fill_offsetH)
|
|
|
|
|
|
|
|
|
|
local xp = scale_value(element.metainfo.slider.min, element.metainfo.slider.max, coordL, coordR, pos)
|
|
|
|
|
|
|
|
|
|
-- the filling, draw it only if positive
|
|
|
|
|
local innerH = element.h - (2*fill_offsetV)
|
|
|
|
|
|
|
|
|
|
if element.metainfo.slider.type == "bar" then
|
|
|
|
|
elem_ass:rect_cw(fill_offsetV, fill_offsetV, xp, element.h - fill_offsetV)
|
|
|
|
|
else
|
|
|
|
|
elem_ass:move_to(xp, fill_offsetV)
|
|
|
|
|
elem_ass:line_to(xp+(innerH/2), (innerH/2)+fill_offsetV)
|
|
|
|
|
elem_ass:line_to(xp, (innerH)+fill_offsetV)
|
|
|
|
|
elem_ass:line_to(xp-(innerH/2), (innerH/2)+fill_offsetV)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
elem_ass:draw_stop()
|
|
|
|
|
|
|
|
|
|
elseif element.type == "box" then
|
|
|
|
|
elem_ass:merge(element.content) -- ASS objects
|
|
|
|
|
elseif type(element.content) == "function" then
|
|
|
|
|
element.content(elem_ass) -- function objects
|
|
|
|
|
else
|
|
|
|
|
elem_ass:append(element.content) -- text objects
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
master_ass:merge(elem_ass)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
|
-- Message display
|
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
function show_message(text, duration)
|
|
|
|
|
|
|
|
|
|
if duration == nil then
|
|
|
|
|
duration = tonumber(mp.property_get("options/osd-duration")) / 1000
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- cut the text short, otherwise the following functions may slow down massively on huge input
|
|
|
|
|
text = string.sub(text, 0, 4000)
|
|
|
|
|
|
|
|
|
|
-- replace actual linebreaks with ASS linebreaks and get the amount of lines along the way
|
|
|
|
|
local lines
|
|
|
|
|
text, lines = string.gsub(text, "\n", "\\N")
|
|
|
|
|
|
|
|
|
|
-- append a Zero-Width-Space to . and _ to enable linebreaking of long filenames
|
|
|
|
|
text = string.gsub(text, "%.", ".\226\128\139")
|
|
|
|
|
text = string.gsub(text, "_", "_\226\128\139")
|
|
|
|
|
|
|
|
|
|
-- scale the fontsize for longer multi-line output
|
|
|
|
|
local fontsize, outline = tonumber(mp.property_get("options/osd-font-size")), tonumber(mp.property_get("options/osd-border-size"))
|
|
|
|
|
if lines > 12 then
|
|
|
|
|
fontsize, outline = fontsize / 2, outline / 1.5
|
|
|
|
|
elseif lines > 8 then
|
|
|
|
|
fontsize, outline = fontsize / 1.5, outline / 1.25
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local style = "{\\bord" .. outline .. "\\fs" .. fontsize .. "}"
|
|
|
|
|
|
|
|
|
|
state.message_text = style .. text
|
|
|
|
|
state.message_timeout = mp.get_timer() + duration
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function render_message(ass)
|
|
|
|
|
if not(state.message_timeout == nil) and not(state.message_text == nil) and state.message_timeout > mp.get_timer() then
|
|
|
|
|
ass:new_event()
|
|
|
|
|
ass:append(state.message_text)
|
|
|
|
|
else
|
|
|
|
|
state.message_text = nil
|
|
|
|
|
state.message_timeout = nil
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
|
-- Initialisation and Layout
|
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
-- OSC INIT
|
|
|
|
|
function osc_init()
|
|
|
|
|
-- kill old Elements
|
|
|
|
|
elements = {}
|
|
|
|
|
|
|
|
|
|
-- set canvas resolution acording to display aspect and scaling setting
|
|
|
|
|
local baseResY = 720
|
|
|
|
|
local display_w, display_h, display_aspect = mp.get_screen_size()
|
|
|
|
|
local scale = 1
|
|
|
|
|
|
2013-10-06 21:44:46 +00:00
|
|
|
|
if (mp.property_get("video") == "no") then -- dummy/forced window
|
|
|
|
|
scale = user_opts.scaleForcedWindow
|
|
|
|
|
elseif (mp.property_get("fullscreen") == "yes") then
|
2013-09-25 22:56:41 +00:00
|
|
|
|
scale = user_opts.scaleFullscreen
|
|
|
|
|
else
|
|
|
|
|
scale = user_opts.scaleWindowed
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if user_opts.vidscale == true then
|
|
|
|
|
osc_param.playresy = baseResY / scale
|
|
|
|
|
else
|
|
|
|
|
osc_param.playresy = display_h / scale
|
|
|
|
|
end
|
|
|
|
|
osc_param.playresx = osc_param.playresy * display_aspect
|
|
|
|
|
|
|
|
|
|
-- position of the controller according to video aspect and valignment
|
|
|
|
|
osc_param.posX = math.floor(get_align(user_opts.halign, osc_param.playresx, osc_param.osc_w, 0))
|
|
|
|
|
osc_param.posY = math.floor(get_align(user_opts.valign, osc_param.playresy, osc_param.osc_h, 0))
|
|
|
|
|
|
|
|
|
|
-- Some calculations on stuff we'll need
|
|
|
|
|
-- vertical/horizontal position offset for contents aligned at the borders of the box
|
|
|
|
|
osc_param.pos_offsetX, osc_param.pos_offsetY = (osc_param.osc_w - (2*osc_param.osc_p)) / 2, (osc_param.osc_h - (2*osc_param.osc_p)) / 2
|
|
|
|
|
|
|
|
|
|
-- fetch values
|
|
|
|
|
local osc_w, osc_h, osc_r, osc_p = osc_param.osc_w, osc_param.osc_h, osc_param.osc_r, osc_param.osc_p
|
|
|
|
|
local pos_offsetX, pos_offsetY = osc_param.pos_offsetX, osc_param.pos_offsetY
|
|
|
|
|
local posX, posY = osc_param.posX, osc_param.posY
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
|
-- Backround box
|
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
local metainfo = {}
|
2013-09-26 16:27:11 +00:00
|
|
|
|
metainfo.alpha1 = 80
|
|
|
|
|
metainfo.alpha3 = 80
|
2013-09-25 22:56:41 +00:00
|
|
|
|
register_box(posX, posY, 5, osc_w, osc_h, osc_r, osc_styles.box, metainfo)
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
|
-- Title row
|
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
local titlerowY = posY - pos_offsetY - 10
|
|
|
|
|
|
|
|
|
|
-- title
|
|
|
|
|
local contentF = function (ass)
|
|
|
|
|
local title = mp.property_get_string("media-title")
|
|
|
|
|
if not (title == nil) then
|
|
|
|
|
|
|
|
|
|
if #title > 80 then
|
|
|
|
|
title = string.format("{\\fscx%f}", (80 / #title) * 100) .. title
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
ass:append(title)
|
|
|
|
|
else
|
|
|
|
|
ass:append("mpv")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local eventresponder = {}
|
|
|
|
|
eventresponder.mouse_btn0_up = function ()
|
|
|
|
|
|
|
|
|
|
local title = mp.property_get("media-title")
|
|
|
|
|
local pl_count = tonumber(mp.property_get("playlist-count"))
|
|
|
|
|
|
|
|
|
|
if pl_count > 1 then
|
|
|
|
|
local playlist_pos = countone(tonumber(mp.property_get("playlist-pos")))
|
|
|
|
|
title = "[" .. playlist_pos .. "/" .. pl_count .. "] " .. title
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
show_message(title)
|
|
|
|
|
end
|
|
|
|
|
eventresponder.mouse_btn2_up = function () show_message(mp.property_get("filename")) end
|
|
|
|
|
|
|
|
|
|
register_button(posX, titlerowY, 8, 496, 12, osc_styles.vidtitle, contentF, eventresponder, nil)
|
|
|
|
|
|
|
|
|
|
-- If we have more than one playlist entry, render playlist navigation buttons
|
|
|
|
|
local metainfo = {}
|
|
|
|
|
metainfo.visible = (tonumber(mp.property_get("playlist-count")) > 1)
|
|
|
|
|
|
|
|
|
|
-- playlist prev
|
|
|
|
|
local eventresponder = {}
|
|
|
|
|
eventresponder.mouse_btn0_up = function () mp.send_command("playlist_prev weak") end
|
|
|
|
|
eventresponder["shift+mouse_btn0_up"] = function () show_message(mp.property_get("playlist"), 3) end
|
|
|
|
|
register_button(posX - pos_offsetX, titlerowY, 7, 12, 12, osc_styles.vidtitle, "◀", eventresponder, metainfo)
|
|
|
|
|
|
|
|
|
|
-- playlist next
|
|
|
|
|
local eventresponder = {}
|
|
|
|
|
eventresponder.mouse_btn0_up = function () mp.send_command("playlist_next weak") end
|
|
|
|
|
eventresponder["shift+mouse_btn0_up"] = function () show_message(mp.property_get("playlist"), 3) end
|
|
|
|
|
register_button(posX + pos_offsetX, titlerowY, 9, 12, 12, osc_styles.vidtitle, "▶", eventresponder, metainfo)
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
|
-- Big buttons
|
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
local bigbuttonrowY = posY - pos_offsetY + 35
|
|
|
|
|
local bigbuttondistance = 60
|
|
|
|
|
|
|
|
|
|
--play/pause
|
|
|
|
|
local contentF = function (ass)
|
|
|
|
|
if mp.property_get("pause") == "yes" then
|
|
|
|
|
ass:append("\238\132\129")
|
|
|
|
|
else
|
|
|
|
|
ass:append("\238\128\130")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
local eventresponder = {}
|
|
|
|
|
eventresponder.mouse_btn0_up = function () mp.send_command("no-osd cycle pause") end
|
|
|
|
|
register_button(posX, bigbuttonrowY, 5, 40, 40, osc_styles.bigButtons, contentF, eventresponder, nil)
|
|
|
|
|
|
|
|
|
|
--skipback
|
|
|
|
|
local metainfo = {}
|
|
|
|
|
metainfo.softrepeat = true
|
|
|
|
|
|
|
|
|
|
local eventresponder = {}
|
|
|
|
|
eventresponder.mouse_btn0_down = function () mp.send_command("no-osd seek -5 relative keyframes") end
|
|
|
|
|
eventresponder["shift+mouse_btn0_down"] = function () mp.send_command("no-osd frame_back_step") end
|
|
|
|
|
eventresponder.mouse_btn2_down = function () mp.send_command("no-osd seek -30 relative keyframes") end
|
|
|
|
|
register_button(posX - bigbuttondistance, bigbuttonrowY, 5, 40, 40, osc_styles.bigButtons, "\238\128\132", eventresponder, metainfo)
|
|
|
|
|
|
|
|
|
|
--skipfrwd
|
|
|
|
|
local eventresponder = {}
|
|
|
|
|
eventresponder.mouse_btn0_down = function () mp.send_command("no-osd seek 10 relative keyframes") end
|
|
|
|
|
eventresponder["shift+mouse_btn0_down"] = function () mp.send_command("no-osd frame_step") end
|
|
|
|
|
eventresponder.mouse_btn2_down = function () mp.send_command("no-osd seek 60 relative keyframes") end
|
|
|
|
|
register_button(posX + bigbuttondistance, bigbuttonrowY, 5, 40, 40, osc_styles.bigButtons, "\238\128\133", eventresponder, metainfo)
|
|
|
|
|
|
|
|
|
|
--chapters
|
|
|
|
|
-- do we have any?
|
|
|
|
|
local metainfo = {}
|
|
|
|
|
metainfo.enabled = ((#mp.get_chapter_list()) > 0)
|
|
|
|
|
|
|
|
|
|
--prev
|
|
|
|
|
local eventresponder = {}
|
|
|
|
|
eventresponder.mouse_btn0_up = function () mp.send_command("osd-msg add chapter -1") end
|
|
|
|
|
eventresponder["shift+mouse_btn0_up"] = function () show_message(mp.property_get("chapter-list"), 3) end
|
|
|
|
|
register_button(posX - (bigbuttondistance * 2), bigbuttonrowY, 5, 40, 40, osc_styles.bigButtons, "\238\132\132", eventresponder, metainfo)
|
|
|
|
|
|
|
|
|
|
--next
|
|
|
|
|
local eventresponder = {}
|
|
|
|
|
eventresponder.mouse_btn0_up = function () mp.send_command("osd-msg add chapter 1") end
|
|
|
|
|
eventresponder["shift+mouse_btn0_up"] = function () show_message(mp.property_get("chapter-list"), 3) end
|
|
|
|
|
register_button(posX + (bigbuttondistance * 2), bigbuttonrowY, 5, 40, 40, osc_styles.bigButtons, "\238\132\133", eventresponder, metainfo)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
|
-- Smaller buttons
|
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
if not (user_opts.iAmAProgrammer) then
|
|
|
|
|
update_tracklist()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--cycle audio tracks
|
|
|
|
|
|
|
|
|
|
local metainfo = {}
|
|
|
|
|
local eventresponder = {}
|
|
|
|
|
local contentF
|
|
|
|
|
|
|
|
|
|
if not (user_opts.iAmAProgrammer) then
|
|
|
|
|
metainfo.enabled = (#tracks_osc.audio > 0)
|
|
|
|
|
|
|
|
|
|
contentF = function (ass)
|
|
|
|
|
local aid = "–"
|
|
|
|
|
if not (get_track("audio") == 0) then
|
|
|
|
|
aid = get_track("audio")
|
|
|
|
|
end
|
|
|
|
|
ass:append("\238\132\134" .. osc_styles.smallButtonsLlabel .. " " .. aid .. "/" .. #tracks_osc.audio)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
eventresponder.mouse_btn0_up = function () set_track("audio", 1) end
|
|
|
|
|
eventresponder.mouse_btn2_up = function () set_track("audio", -1) end
|
|
|
|
|
eventresponder["shift+mouse_btn0_down"] = function ()
|
|
|
|
|
show_message(get_tracklist("audio"), 2)
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
metainfo.enabled = true
|
|
|
|
|
contentF = function (ass)
|
|
|
|
|
local aid = mp.property_get("audio")
|
|
|
|
|
|
|
|
|
|
ass:append("\238\132\134" .. osc_styles.smallButtonsLlabel .. " " .. aid)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
eventresponder.mouse_btn0_up = function () mp.send_command("osd-msg add audio 1") end
|
|
|
|
|
eventresponder.mouse_btn2_up = function () mp.send_command("osd-msg add audio -1") end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
register_button(posX - pos_offsetX, bigbuttonrowY, 1, 70, 18, osc_styles.smallButtonsL, contentF, eventresponder, metainfo)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--cycle sub tracks
|
|
|
|
|
|
|
|
|
|
local metainfo = {}
|
|
|
|
|
local eventresponder = {}
|
|
|
|
|
local contentF
|
|
|
|
|
|
|
|
|
|
if not (user_opts.iAmAProgrammer) then
|
|
|
|
|
metainfo.enabled = (#tracks_osc.sub > 0)
|
|
|
|
|
|
|
|
|
|
contentF = function (ass)
|
|
|
|
|
local sid = "–"
|
|
|
|
|
if not (get_track("sub") == 0) then
|
|
|
|
|
sid = get_track("sub")
|
|
|
|
|
end
|
|
|
|
|
ass:append("\238\132\135" .. osc_styles.smallButtonsLlabel .. " " .. sid .. "/" .. #tracks_osc.sub)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
eventresponder.mouse_btn0_up = function () set_track("sub", 1) end
|
|
|
|
|
eventresponder.mouse_btn2_up = function () set_track("sub", -1) end
|
|
|
|
|
eventresponder["shift+mouse_btn0_down"] = function ()
|
|
|
|
|
show_message(get_tracklist("sub"), 2)
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
metainfo.enabled = true
|
|
|
|
|
contentF = function (ass)
|
|
|
|
|
local sid = mp.property_get("sub")
|
|
|
|
|
|
|
|
|
|
ass:append("\238\132\135" .. osc_styles.smallButtonsLlabel .. " " .. sid)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
eventresponder.mouse_btn0_up = function () mp.send_command("osd-msg add sub 1") end
|
|
|
|
|
eventresponder.mouse_btn2_up = function () mp.send_command("osd-msg add sub -1") end
|
|
|
|
|
end
|
|
|
|
|
register_button(posX - pos_offsetX, bigbuttonrowY, 7, 70, 18, osc_styles.smallButtonsL, contentF, eventresponder, metainfo)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--toggle FS
|
|
|
|
|
local contentF = function (ass)
|
|
|
|
|
if mp.property_get("fullscreen") == "yes" then
|
|
|
|
|
ass:append("\238\132\137")
|
|
|
|
|
else
|
|
|
|
|
ass:append("\238\132\136")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
local eventresponder = {}
|
|
|
|
|
eventresponder.mouse_btn0_up = function () mp.send_command("no-osd cycle fullscreen") end
|
|
|
|
|
register_button(posX+pos_offsetX, bigbuttonrowY, 6, 25, 25, osc_styles.smallButtonsR, contentF, eventresponder, nil)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
|
-- Seekbar
|
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
local markerF = function ()
|
|
|
|
|
local duration = 0
|
|
|
|
|
if not (mp.property_get("length") == nil) then
|
|
|
|
|
duration = tonumber(mp.property_get("length"))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local chapters = mp.get_chapter_list()
|
|
|
|
|
local markers = {}
|
|
|
|
|
for n = 1, #chapters do
|
|
|
|
|
markers[n] = (chapters[n].time / duration * 100)
|
|
|
|
|
end
|
|
|
|
|
return markers
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local posF = function ()
|
|
|
|
|
if mp.property_get("length") == nil then
|
|
|
|
|
return nil
|
|
|
|
|
else
|
|
|
|
|
return tonumber(mp.property_get("percent-pos"))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local metainfo = {}
|
|
|
|
|
metainfo.enabled = (not (mp.property_get("length") == nil)) and (tonumber(mp.property_get("length")) > 0)
|
|
|
|
|
metainfo.styledown = false
|
|
|
|
|
metainfo.slider = {}
|
|
|
|
|
metainfo.slider.border = 1
|
|
|
|
|
metainfo.slider.gap = 1 -- >1 will draw triangle markers
|
|
|
|
|
metainfo.slider.type = "slider" -- "bar" for old bar-style filling
|
|
|
|
|
|
|
|
|
|
local eventresponder = {}
|
|
|
|
|
local sliderF = function (element)
|
|
|
|
|
local seek_to = get_slider_value(element)
|
|
|
|
|
-- ignore identical seeks
|
|
|
|
|
if not(state.last_seek == seek_to) then
|
|
|
|
|
mp.send_command(string.format("no-osd seek %f absolute-percent keyframes", seek_to))
|
|
|
|
|
state.last_seek = seek_to
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
eventresponder.render = sliderF
|
|
|
|
|
eventresponder.mouse_btn0_down = sliderF
|
|
|
|
|
register_slider(posX, posY+pos_offsetY-22, 2, pos_offsetX*2, 15, osc_styles.timecodes, 0, 100, markerF, posF, eventresponder, metainfo)
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
|
-- Timecodes + Volume
|
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
local bottomrowY = posY + pos_offsetY - 5
|
|
|
|
|
|
|
|
|
|
-- left (current pos)
|
|
|
|
|
local metainfo = {}
|
|
|
|
|
local eventresponder = {}
|
|
|
|
|
|
|
|
|
|
local contentF = function (ass)
|
|
|
|
|
if state.tc_ms then
|
|
|
|
|
ass:append(mp.property_get_string("time-pos/full"))
|
|
|
|
|
else
|
|
|
|
|
ass:append(mp.property_get_string("time-pos"))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
eventresponder.mouse_btn0_up = function () state.tc_ms = not state.tc_ms end
|
|
|
|
|
register_button(posX - pos_offsetX, bottomrowY, 4, 110, 18, osc_styles.timecodes, contentF, eventresponder, metainfo)
|
|
|
|
|
|
2013-10-06 22:04:35 +00:00
|
|
|
|
-- center (Cache)
|
|
|
|
|
local metainfo = {}
|
|
|
|
|
local eventresponder = {}
|
|
|
|
|
|
|
|
|
|
local contentF = function (ass)
|
|
|
|
|
local cache = mp.property_get("cache")
|
|
|
|
|
if not (cache == nil) then
|
|
|
|
|
cache = tonumber(mp.property_get("cache"))
|
|
|
|
|
if (cache < 48) then
|
2013-10-06 22:56:11 +00:00
|
|
|
|
ass:append("Cache: " .. (cache) .."%")
|
2013-10-06 22:04:35 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
register_button(posX, bottomrowY, 5, 110, 18, osc_styles.timecodes, contentF, eventresponder, metainfo)
|
|
|
|
|
|
|
|
|
|
|
2013-09-25 22:56:41 +00:00
|
|
|
|
-- right (total/remaining time)
|
|
|
|
|
-- do we have a usuable duration?
|
|
|
|
|
local metainfo = {}
|
|
|
|
|
metainfo.visible = (not (mp.property_get("length") == nil)) and (tonumber(mp.property_get("length")) > 0)
|
|
|
|
|
|
|
|
|
|
local contentF = function (ass)
|
|
|
|
|
if state.rightTC_trem == true then
|
|
|
|
|
if state.tc_ms then
|
|
|
|
|
ass:append("-" .. mp.property_get_string("time-remaining/full"))
|
|
|
|
|
else
|
|
|
|
|
ass:append("-" .. mp.property_get_string("time-remaining"))
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
if state.tc_ms then
|
|
|
|
|
ass:append(mp.property_get_string("length/full"))
|
|
|
|
|
else
|
|
|
|
|
ass:append(mp.property_get_string("length"))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
local eventresponder = {}
|
|
|
|
|
eventresponder.mouse_btn0_up = function () state.rightTC_trem = not state.rightTC_trem end
|
|
|
|
|
|
|
|
|
|
register_button(posX + pos_offsetX, bottomrowY, 6, 110, 18, osc_styles.timecodes, contentF, eventresponder, metainfo)
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
|
-- Other important stuff
|
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function show_osc()
|
|
|
|
|
|
2013-10-13 01:51:25 +00:00
|
|
|
|
--remember last time of invokation (mouse move)
|
|
|
|
|
state.showtime = mp.get_timer()
|
|
|
|
|
|
2013-09-25 22:56:41 +00:00
|
|
|
|
state.osc_visible = true
|
|
|
|
|
|
|
|
|
|
if (user_opts.fadeduration > 0) then
|
|
|
|
|
state.anitype = nil
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function hide_osc()
|
|
|
|
|
if (user_opts.fadeduration > 0) then
|
|
|
|
|
if not(state.osc_visible == false) then
|
|
|
|
|
state.anitype = "out"
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
state.osc_visible = false
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function mouse_leave()
|
|
|
|
|
hide_osc()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function request_init()
|
|
|
|
|
state.initREQ = true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function render()
|
|
|
|
|
local current_screen_sizeX, current_screen_sizeY = mp.get_screen_size()
|
2013-10-13 01:51:25 +00:00
|
|
|
|
local mouseX, mouseY = mp.get_mouse_pos()
|
|
|
|
|
local now = mp.get_timer()
|
2013-09-25 22:56:41 +00:00
|
|
|
|
|
|
|
|
|
if not (state.mp_screen_sizeX == current_screen_sizeX and state.mp_screen_sizeY == current_screen_sizeY) then
|
|
|
|
|
-- display changed, reinit everything
|
|
|
|
|
request_init()
|
|
|
|
|
state.mp_screen_sizeX, state.mp_screen_sizeY = current_screen_sizeX, current_screen_sizeY
|
|
|
|
|
end
|
|
|
|
|
|
2013-10-13 01:51:25 +00:00
|
|
|
|
|
2013-09-25 22:56:41 +00:00
|
|
|
|
if state.initREQ then
|
|
|
|
|
osc_init()
|
|
|
|
|
state.initREQ = false
|
2013-10-13 01:51:25 +00:00
|
|
|
|
|
|
|
|
|
-- store initial mouse position
|
|
|
|
|
if (state.last_mouseX == nil or state.last_mouseY == nil) and not (mouseX == nil or mouseY == nil) then
|
|
|
|
|
state.last_mouseX, state.last_mouseY = mouseX, mouseY
|
|
|
|
|
end
|
2013-09-25 22:56:41 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
2013-10-13 01:51:25 +00:00
|
|
|
|
|
|
|
|
|
if not (state.showtime == nil) and (state.showtime + (user_opts.hidetimeout/1000) < now) and (state.active_element == nil)
|
|
|
|
|
and not (mouseX >= osc_param.posX - (osc_param.osc_w / 2) and mouseX <= osc_param.posX + (osc_param.osc_w / 2)
|
|
|
|
|
and mouseY >= osc_param.posY - (osc_param.osc_h / 2) and mouseY <= osc_param.posY + (osc_param.osc_h / 2)) then
|
|
|
|
|
hide_osc()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if not(state.anitype == nil) then
|
2013-09-25 22:56:41 +00:00
|
|
|
|
|
|
|
|
|
if (state.anistart == nil) then
|
|
|
|
|
state.anistart = now
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if (now < state.anistart + (user_opts.fadeduration/1000) ) then
|
|
|
|
|
|
|
|
|
|
if (state.anitype == "in") then --fade in
|
|
|
|
|
state.osc_visible = true
|
|
|
|
|
state.animation = scale_value(state.anistart, (state.anistart + (user_opts.fadeduration/1000)), 255, 0, now)
|
|
|
|
|
elseif (state.anitype == "out") then --fade in
|
|
|
|
|
state.animation = scale_value(state.anistart, (state.anistart + (user_opts.fadeduration/1000)), 0, 255, now)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
if (state.anitype == "out") then state.osc_visible = false end
|
|
|
|
|
state.anistart = nil
|
|
|
|
|
state.animation = nil
|
|
|
|
|
state.anitype = nil
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
state.anistart = nil
|
|
|
|
|
state.animation = nil
|
|
|
|
|
state.anitype = nil
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local ass = assdraw.ass_new()
|
|
|
|
|
|
|
|
|
|
render_message(ass)
|
|
|
|
|
|
|
|
|
|
if state.osc_visible then
|
|
|
|
|
render_elements(ass)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local w, h, aspect = mp.get_screen_size()
|
|
|
|
|
mp.set_osd_ass(osc_param.playresy * aspect, osc_param.playresy, ass.text)
|
|
|
|
|
|
|
|
|
|
-- set mouse area
|
|
|
|
|
local area_y0, area_y1
|
|
|
|
|
if user_opts.valign > 0 then
|
|
|
|
|
-- deadzone above OSC
|
2013-10-13 01:51:25 +00:00
|
|
|
|
area_y0 = get_align(-1 + (2*user_opts.deadzonesize), osc_param.posY - (osc_param.osc_h / 2), 0, 0)
|
2013-09-25 22:56:41 +00:00
|
|
|
|
area_y1 = osc_param.playresy
|
|
|
|
|
else
|
|
|
|
|
-- deadzone below OSC
|
|
|
|
|
area_y0 = 0
|
|
|
|
|
area_y1 = (osc_param.posY + (osc_param.osc_h / 2))
|
2013-10-13 01:51:25 +00:00
|
|
|
|
+ get_align(1 - (2*user_opts.deadzonesize), osc_param.playresy - (osc_param.posY + (osc_param.osc_h / 2)), 0, 0)
|
2013-09-25 22:56:41 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--mouse show/hide area
|
|
|
|
|
mp.set_mouse_area(0, area_y0, osc_param.playresx, area_y1, "showhide")
|
|
|
|
|
|
|
|
|
|
--mouse input area
|
|
|
|
|
mp.set_mouse_area(
|
|
|
|
|
osc_param.posX - (osc_param.osc_w / 2), osc_param.posY - (osc_param.osc_h / 2),
|
|
|
|
|
osc_param.posX + (osc_param.osc_w / 2), osc_param.posY + (osc_param.osc_h / 2),
|
|
|
|
|
"input")
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
|
-- Eventhandling
|
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
function process_event(source, what)
|
|
|
|
|
|
|
|
|
|
if what == "down" then
|
|
|
|
|
|
|
|
|
|
for n = 1, #elements do
|
|
|
|
|
|
|
|
|
|
if not (elements[n].eventresponder == nil) then
|
|
|
|
|
if not (elements[n].eventresponder[source .. "_up"] == nil) or not (elements[n].eventresponder[source .. "_down"] == nil) then
|
|
|
|
|
|
|
|
|
|
if mouse_hit(elements[n]) then
|
|
|
|
|
state.active_element = n
|
|
|
|
|
state.active_event_source = source
|
|
|
|
|
-- fire the down event if the element has one
|
|
|
|
|
if not (elements[n].eventresponder[source .. "_" .. what] == nil) then
|
|
|
|
|
elements[n].eventresponder[source .. "_" .. what](elements[n])
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
elseif what == "up" then
|
|
|
|
|
|
|
|
|
|
if not (state.active_element == nil) then
|
|
|
|
|
|
|
|
|
|
local n = state.active_element
|
|
|
|
|
|
|
|
|
|
if n == 0 then
|
|
|
|
|
--click on background (does not work)
|
|
|
|
|
elseif n > 0 and not (elements[n].eventresponder[source .. "_" .. what] == nil) then
|
|
|
|
|
|
|
|
|
|
if mouse_hit(elements[n]) then
|
|
|
|
|
elements[n].eventresponder[source .. "_" .. what](elements[n])
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
state.active_element = nil
|
|
|
|
|
state.mouse_down_counter = 0
|
|
|
|
|
state.last_seek = nil
|
|
|
|
|
|
|
|
|
|
elseif source == "mouse_move" then
|
2013-10-13 01:51:25 +00:00
|
|
|
|
local mouseX, mouseY = mp.get_mouse_pos()
|
|
|
|
|
if (math.abs(mouseX - state.last_mouseX) >= user_opts.minmousemove) or (math.abs(mouseY - state.last_mouseY) >= user_opts.minmousemove) then
|
|
|
|
|
show_osc()
|
|
|
|
|
end
|
|
|
|
|
state.last_mouseX, state.last_mouseY = mouseX, mouseY
|
2013-09-25 22:56:41 +00:00
|
|
|
|
|
|
|
|
|
if not (state.active_element == nil) then
|
|
|
|
|
|
|
|
|
|
local n = state.active_element
|
|
|
|
|
|
|
|
|
|
if not (elements[n].eventresponder == nil) then
|
|
|
|
|
if not (elements[n].eventresponder[source] == nil) then
|
|
|
|
|
elements[n].eventresponder[source](elements[n])
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2013-10-13 01:51:25 +00:00
|
|
|
|
-- called by mpv on every frame
|
|
|
|
|
|
2013-09-27 18:12:13 +00:00
|
|
|
|
function tick()
|
|
|
|
|
if (mp.property_get("fullscreen") == "yes" and user_opts.showFullscreen) or (mp.property_get("fullscreen") == "no" and user_opts.showWindowed) then
|
|
|
|
|
render()
|
|
|
|
|
else
|
|
|
|
|
mp.set_osd_ass(osc_param.playresy, osc_param.playresy, "")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2013-09-25 22:56:41 +00:00
|
|
|
|
function mp_event(name, arg)
|
|
|
|
|
if name == "tick" then
|
2013-09-27 18:12:13 +00:00
|
|
|
|
tick()
|
2013-09-25 22:56:41 +00:00
|
|
|
|
elseif name == "start" or name == "track-layout" then
|
|
|
|
|
request_init()
|
|
|
|
|
elseif name == "end" then
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- mouse show/hide bindings
|
|
|
|
|
mp.set_key_bindings({
|
|
|
|
|
{"mouse_move", function(e) process_event("mouse_move", nil) end},
|
|
|
|
|
{"mouse_leave", mouse_leave},
|
|
|
|
|
}, "showhide")
|
|
|
|
|
mp.enable_key_bindings("showhide", "allow-vo-dragging")
|
|
|
|
|
|
|
|
|
|
--mouse input bindings
|
|
|
|
|
mp.set_key_bindings({
|
|
|
|
|
{"mouse_btn0", function(e) process_event("mouse_btn0", "up") end,
|
|
|
|
|
function(e) process_event("mouse_btn0", "down") end},
|
|
|
|
|
{"shift+mouse_btn0", function(e) process_event("shift+mouse_btn0", "up") end,
|
|
|
|
|
function(e) process_event("shift+mouse_btn0", "down") end},
|
|
|
|
|
{"mouse_btn2", function(e) process_event("mouse_btn2", "up") end,
|
|
|
|
|
function(e) process_event("mouse_btn2", "down") end},
|
|
|
|
|
{"mouse_btn0_dbl", "ignore"},
|
|
|
|
|
{"shift+mouse_btn0_dbl", "ignore"},
|
|
|
|
|
{"mouse_btn2_dbl", "ignore"},
|
|
|
|
|
}, "input")
|
|
|
|
|
mp.enable_key_bindings("input")
|