From 364e06261a2ffd39afea1d029d4dcc87ef1d6144 Mon Sep 17 00:00:00 2001 From: ChrisK2 Date: Fri, 15 Aug 2014 15:24:22 +0200 Subject: osc: Overhaul (wip) Code reorganized to make layouts exchangeable alternative test layout can be tested with layout=slimbox in the OSC config timers are now used to properly animate the fade out when the player is paused duplicate seeks are discarded again --- player/lua/osc.lua | 1350 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 848 insertions(+), 502 deletions(-) diff --git a/player/lua/osc.lua b/player/lua/osc.lua index f688d0f55a..06974239c7 100644 --- a/player/lua/osc.lua +++ b/player/lua/osc.lua @@ -24,22 +24,17 @@ local user_opts = { minmousemove = 3, -- minimum amount of pixels the mouse has to move between ticks to make the OSC show up seektooltip = true, -- display tooltip over the seekbar indicating time at mouse position iamaprogrammer = false, -- use native mpv values and disable OSC internal playlist management (and some functions that depend on it) + layout = "box", } -- read options from config and command-line read_options(user_opts, "osc") -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() +local osc_param = { -- 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 + display_aspect = 1, + areas = {}, } local osc_styles = { @@ -72,6 +67,8 @@ local state = { message_text, message_timeout, fullscreen = false, + timer = nil, + cache_idle = false, } @@ -87,7 +84,8 @@ function scale_value(x0, x1, y0, y1, val) return (m * val) + b end --- returns hitbox spanning coordinates (top left, bottom right corner) according to alignment +-- returns hitbox spanning coordinates (top left, bottom right corner) +-- according to alignment function get_hitbox_coords(x, y, an, w, h) local alignments = { @@ -107,14 +105,22 @@ function get_hitbox_coords(x, y, an, w, h) return alignments[an]() end +function get_hitbox_coords_geo(geometry) + return get_hitbox_coords(geometry.x, geometry.y, geometry.an, + geometry.w, geometry.h) +end + function get_element_hitbox(element) - return element.hitbox.x1, element.hitbox.y1, element.hitbox.x2, element.hitbox.y2 + 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 mouse_hit_coords(get_element_hitbox(element)) +end +function mouse_hit_coords(bX1, bY1, bX2, bY2) + local mX, mY = mp.get_mouse_pos() return (mX >= bX1 and mX <= bX2 and mY >= bY1 and mY <= bY2) end @@ -128,11 +134,11 @@ function limit_range(min, max, val) end function get_slider_value(element) - local fill_offsetV = element.metainfo.slider.border - local paddingH = (element.h - (2*fill_offsetV)) / 2 + local foV = element.layout.slider.border + local pH = (element.layout.geometry.h - (2*foV)) / 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 b_x1, b_x2 = element.hitbox.x1 + pH, element.hitbox.x2 - pH + local s_min, s_max = element.slider.min, element.slider.max local pos = scale_value(b_x1, b_x2, s_min, s_max, mp.get_mouse_pos()) @@ -159,6 +165,15 @@ function mult_alpha(alphaA, alphaB) return 255 - (((1-(alphaA/255)) * (1-(alphaB/255))) * 255) end +function add_area(name, x1, y1, x2, y2) + -- create area if needed + if (osc_param.areas[name] == nil) then + osc_param.areas[name] = {} + end + table.insert(osc_param.areas[name], {x1=x1, y1=y1, x2=x2, y2=y2}) +end + + -- -- Tracklist Management -- @@ -253,187 +268,175 @@ 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? - 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) - - 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 - metainfo.alpha1 = 136 - eventresponder = nil - end +local elements = {} - -- 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 - 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 +function prepare_elements() - local element = { - type = type, - elem_ass = ass, - hitbox = hitbox, - w = w, - h = h, - x = x, - y = y, - content = content, - eventresponder = eventresponder, - metainfo = metainfo, - state = {}, - } - - table.insert(elements, element) + -- remove elements without layout or invisble + local elements2 = {} + for n, element in pairs(elements) do + if not (element.layout == nil) and (element.visible) then + table.insert(elements2, element) + end end -end + elements = elements2 -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() + function elem_compare (a, b) + return a.layout.layer < b.layout.layer + end - local metainfo = {} - if not (metainfo2 == nil) then metainfo = metainfo2 end + table.sort(elements, elem_compare) - metainfo.styledown = false - register_element("box", x, y, an, w, h, style, ass, nil, metainfo) -end + for _,element in pairs(elements) do -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 + local elem_geo = element.layout.geometry + local style_ass = assdraw.ass_new() - -- 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 + -- prepare static elements + style_ass:append("{}") -- hack to troll new_event into inserting a \n + style_ass:new_event() + style_ass:pos(elem_geo.x, elem_geo.y) + style_ass:an(elem_geo.an) + style_ass:append(element.layout.style) - metainfo.slider.markerF = markerF - metainfo.slider.posF = posF + element.style_ass = style_ass - -- 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 + local static_ass = assdraw.ass_new() - ass:draw_start() - -- the box - ass:rect_cw(0, 0, w, h); + if (element.type == "box") then + --draw box + static_ass:draw_start() + static_ass:round_rect_cw(0, 0, elem_geo.w, elem_geo.h, + element.layout.box.radius) + static_ass:draw_stop() - -- 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 + elseif (element.type == "slider") then + --draw static slider parts - local coordL, coordR = fill_offsetH, (w - fill_offsetH) + local slider_lo = element.layout.slider + -- offset between element outline and drag-area + local foV = slider_lo.border + slider_lo.gap + local foH = 0 + if (slider_lo.stype == "slider") then + foH = elem_geo.h / 2 + elseif (slider_lo.stype == "bar") then + foH = slider_lo.border + slider_lo.gap + end - local s = scale_value(min, max, coordL, coordR, markers[n]) + static_ass:draw_start() + + -- the box + static_ass:rect_cw(0, 0, elem_geo.w, elem_geo.h); + + -- the "hole" + static_ass:rect_ccw(slider_lo.border, slider_lo.border, + elem_geo.w - slider_lo.border, elem_geo.h - slider_lo.border) + + -- marker nibbles + if not (element.slider.markerF == nil) and (slider_lo.gap > 0) then + local markers = element.slider.markerF() + for _,marker in pairs(markers) do + if (marker > element.slider.min) and + (marker < element.slider.max) then + + local s = scale_value(element.slider.min, + element.slider.max, foH, (elem_geo.w - foH), marker) + + if (slider_lo.gap > 1) then -- draw triangles + + local a = slider_lo.gap / 0.5 --0.866 + + --top + if (slider_lo.nibbles_top) then + static_ass:move_to(s - (a/2), slider_lo.border) + static_ass:line_to(s + (a/2), slider_lo.border) + static_ass:line_to(s, foV) + end + + --bottom + if (slider_lo.nibbles_bottom) then + static_ass:move_to(s - (a/2), + elem_geo.h - slider_lo.border) + static_ass:line_to(s, + elem_geo.h - foV) + static_ass:line_to(s + (a/2), + elem_geo.h - slider_lo.border) + end + + else -- draw 1px nibbles + + --top + if (slider_lo.nibbles_top) then + static_ass:rect_cw(s - 0.5, slider_lo.gap, + s + 0.5, slider_lo.gap*2); + end + + --bottom + if (slider_lo.nibbles_bottom) then + static_ass:rect_cw(s - 0.5, + elem_geo.h - slider_lo.gap*2, + s + 0.5, + elem_geo.h - slider_lo.gap); + end + end + end + end + end + end - 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) + element.static_ass = static_ass - --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 + -- if the element is supposed to be disabled, + -- style it accordingly and kill the eventresponders + if not (element.enabled) then + element.layout.alpha[1] = 136 + element.eventresponder = nil + end - end + -- Calculate the hitbox + local bX1, bY1, bX2, bY2 = get_hitbox_coords_geo(elem_geo) + if type == "slider" then + -- if it's a slider, cut the border off, + -- as it is not of interest for eventhandling + element.hitbox = { + x1 = bX1 + slider.border, y1 = bY1 + slider.border, + x2 = bX2 - slider.border, y2 = bY2 - slider.border} + else + element.hitbox = {x1 = bX1, y1 = bY1, x2 = bX2, y2 = bY2} 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 - + 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) + + local style_ass = assdraw.ass_new() + style_ass:merge(element.style_ass) --alpha - local alpha1 = element.metainfo.alpha1 - local alpha2 = element.metainfo.alpha2 - local alpha3 = element.metainfo.alpha3 - local alpha4 = element.metainfo.alpha4 - - if not(state.animation == nil) then - 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) + local ar = element.layout.alpha + if not (state.animation == nil) then + ar = {} + for ai, av in pairs(element.layout.alpha) do + ar[ai] = mult_alpha(av, state.animation) + end end - elem_ass:append(string.format("{\\1a&H%X&\\2a&H%X&\\3a&H%X&\\4a&H%X&}", alpha1, alpha2, alpha3, alpha4)) - + style_ass:append(string.format("{\\1a&H%X&\\2a&H%X&\\3a&H%X&\\4a&H%X&}", + ar[1], ar[2], ar[3], ar[4])) - if state.active_element == n then + if (state.active_element == n) then -- run render event functions if not (element.eventresponder.render == nil) then @@ -442,11 +445,11 @@ function render_elements(master_ass) if mouse_hit(element) then -- mouse down styling - if element.metainfo.styledown then - elem_ass:append(osc_styles.elementDown) + if (element.styledown) then + style_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 + if (element.softrepeat) 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 @@ -454,70 +457,84 @@ function render_elements(master_ass) end - if element.type == "slider" then + local elem_ass = assdraw.ass_new() + + elem_ass:merge(style_ass) + + if not (element.type == "button") then + elem_ass:merge(element.static_ass) + end - elem_ass:merge(element.content) -- ASS objects + + + if (element.type == "slider") then + + local slider_lo = element.layout.slider + local elem_geo = element.layout.geometry + local s_min, s_max = element.slider.min, element.slider.max -- draw pos marker - local pos = element.metainfo.slider.posF() + local pos = element.slider.posF() if not (pos == nil) then - pos = limit_range(element.metainfo.slider.min, element.metainfo.slider.max, pos) - - local fill_offsetV = element.metainfo.slider.border + element.metainfo.slider.gap - local fill_offsetH = element.h/2 + local foV = slider_lo.border + slider_lo.gap + local foH = 0 + if (slider_lo.stype == "slider") then + foH = elem_geo.h / 2 + elseif (slider_lo.stype == "bar") then + foH = slider_lo.border + slider_lo.gap + end - local coordL, coordR = fill_offsetH, (element.w - fill_offsetH) + pos = limit_range(s_min, s_max, pos) - local xp = scale_value(element.metainfo.slider.min, element.metainfo.slider.max, coordL, coordR, pos) + local xp = scale_value(s_min, s_max, + foH, (elem_geo.w - foH), 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) + local innerH = elem_geo.h - (2*foV) + + if (slider_lo.stype == "bar") then + elem_ass:rect_cw(foH, foV, xp, elem_geo.h - foV) + elseif (slider_lo.stype == "slider") then + elem_ass:move_to(xp, foV) + elem_ass:line_to(xp+(innerH/2), (innerH/2)+foV) + elem_ass:line_to(xp, (innerH)+foV) + elem_ass:line_to(xp-(innerH/2), (innerH/2)+foV) end end elem_ass:draw_stop() -- add tooltip - if not (element.metainfo.slider.tooltipF == nil) then + if not (element.slider.tooltipF == nil) then if mouse_hit(element) then local sliderpos = get_slider_value(element) - local tooltiplabel = element.metainfo.slider.tooltipF(sliderpos) - local s_min, s_max = element.metainfo.slider.min, element.metainfo.slider.max + local tooltiplabel = element.slider.tooltipF(sliderpos) local an = 2 - if (sliderpos < (s_min + 5)) then - an = 1 - elseif (sliderpos > (s_max - 5)) then - an = 3 + if (slider_lo.adjust_tooltip) then + if (sliderpos < (s_min + 5)) then + an = 1 + elseif (sliderpos > (s_max - 5)) then + an = 3 + end end elem_ass:new_event() - elem_ass:pos(mp.get_mouse_pos(), element.y - (element.h) - 0) -- positioning + elem_ass:pos(mp.get_mouse_pos(), + element.hitbox.y1 - slider_lo.border) elem_ass:an(an) - elem_ass:append(osc_styles.vidtitle) -- styling + elem_ass:append(slider_lo.tooltip_style) elem_ass:append(tooltiplabel) end end - - - 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 + elseif not (element.content == nil) then elem_ass:append(element.content) -- text objects end @@ -574,61 +591,128 @@ end -- Initialisation and Layout -- --- OSC INIT -function osc_init() - msg.debug("osc_init") +function new_element(name, type) + elements[name] = {} + elements[name].type = type - -- kill old Elements - elements = {} + -- add default stuff + elements[name].eventresponder = {} + elements[name].visible = true + elements[name].enabled = true + elements[name].softrepeat = false + elements[name].styledown = (type == "button") + elements[name].state = {} - -- set canvas resolution according to display aspect and scaling setting - local baseResY = 720 - local display_w, display_h, display_aspect = mp.get_screen_size() - local scale = 1 - - if (mp.get_property("video") == "no") then -- dummy/forced window - scale = user_opts.scaleforcedwindow - elseif state.fullscreen then - scale = user_opts.scalefullscreen - else - scale = user_opts.scalewindowed + if (type == "slider") then + elements[name].slider = {min = 0, max = 100} end - if user_opts.vidscale then - osc_param.playresy = baseResY / scale + return elements[name] +end + +function add_layout(name) + if not (elements[name] == nil) then + -- new layout + elements[name].layout = {} + + -- set layout defaults + elements[name].layout.layer = 50 + elements[name].layout.alpha = {[1] = 0, [2] = 255, [3] = 255, [4] = 255} + + if (elements[name].type == "slider") then + -- slider defaults + elements[name].layout.slider = { + border = 1, + gap = 1, + nibbles_top = true, + nibbles_bottom = true, + stype = "slider", + adjust_tooltip = true, + tooltip_style = "", + } + elseif (elements[name].type == "box") then + elements[name].layout.box = {radius = 0} + end + + return elements[name].layout else - osc_param.playresy = display_h / scale + msg.error("Can't add_layout to element \""..name.."\", doesn't exist.") end - osc_param.playresx = osc_param.playresy * display_aspect +end + +-- +-- Layouts +-- + +local layouts = {} + +-- Classic box layout +layouts["box"] = function () + + local osc_geo = { + w = 550, -- width + h = 138, -- height + r = 10, -- corner-radius + p = 15, -- padding + } -- make sure the OSC actually fits into the video - if (osc_param.playresx < (osc_param.osc_w + (2 * osc_param.osc_p))) then - osc_param.playresy = (osc_param.osc_w + (2 * osc_param.osc_p)) / display_aspect - osc_param.playresx = osc_param.playresy * display_aspect + if (osc_param.playresx < (osc_geo.w + (2 * osc_geo.p))) then + osc_param.playresy = (osc_geo.w+(2*osc_geo.p))/osc_param.display_aspect + osc_param.playresx = osc_param.playresy * osc_param.display_aspect end -- 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)) + local posX = math.floor(get_align(user_opts.halign, osc_param.playresx, + osc_geo.w, 0)) + local posY = math.floor(get_align(user_opts.valign, osc_param.playresy, + osc_geo.h, 0)) + + -- position offset for contents aligned at the borders of the box + local pos_offsetX = (osc_geo.w - (2*osc_geo.p)) / 2 + local pos_offsetY = (osc_geo.h - (2*osc_geo.p)) / 2 - -- 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 + osc_param.areas = {} -- delete areas + + -- area for active mouse input + add_area("input", get_hitbox_coords(posX, posY, 5, osc_geo.w, osc_geo.h)) + + -- area for show/hide + local sh_area_y0, sh_area_y1 + if user_opts.valign > 0 then + -- deadzone above OSC + sh_area_y0 = get_align(-1 + (2*user_opts.deadzonesize), + posY - (osc_geo.h / 2), 0, 0) + sh_area_y1 = osc_param.playresy + else + -- deadzone below OSC + sh_area_y0 = 0 + sh_area_y1 = (posY + (osc_geo.h / 2)) + + get_align(1 - (2*user_opts.deadzonesize), + osc_param.playresy - (posY + (osc_geo.h / 2)), 0, 0) + end + add_area("showhide", 0, sh_area_y0, osc_param.playresx, sh_area_y1) -- 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 + local osc_w, osc_h, osc_r, osc_p = + osc_geo.w, osc_geo.h, osc_geo.r, osc_geo.p + + local lo -- - -- Backround box + -- Background box -- - local metainfo = {} - metainfo.alpha1 = user_opts.boxalpha - metainfo.alpha3 = user_opts.boxalpha - register_box(posX, posY, 5, osc_w, osc_h, osc_r, osc_styles.box, metainfo) + new_element("bgbox", "box") + lo = add_layout("bgbox") + + lo.geometry = {x = posX, y = posY, an = 5, w = osc_w, h = osc_h} + lo.layer = 10 + lo.style = osc_styles.box + lo.alpha[1] = user_opts.boxalpha + lo.alpha[3] = user_opts.boxalpha + lo.box.radius = osc_r -- -- Title row @@ -636,312 +720,490 @@ function osc_init() local titlerowY = posY - pos_offsetY - 10 - -- title - local contentF = function (ass) - local title = mp.get_property_osd("media-title") - if not (title == nil) then + lo = add_layout("title") + lo.geometry = {x = posX, y = titlerowY, an = 8, w = 496, h = 12} + lo.style = osc_styles.vidtitle - if #title > 80 then - title = string.format("{\\fscx%f}", (80 / #title) * 100) .. title - end + lo = add_layout("pl_prev") + lo.geometry = + {x = (posX - pos_offsetX), y = titlerowY, an = 7, w = 12, h = 12} + lo.style = osc_styles.vidtitle - ass:append(title) - else - ass:append("mpv") - end + lo = add_layout("pl_next") + lo.geometry = + {x = (posX + pos_offsetX), y = titlerowY, an = 9, w = 12, h = 12} + lo.style = osc_styles.vidtitle + + -- + -- Big buttons + -- + + local bigbtnrowY = posY - pos_offsetY + 35 + local bigbtndist = 60 + + lo = add_layout("playpause") + lo.geometry = + {x = posX, y = bigbtnrowY, an = 5, w = 40, h = 40} + lo.style = osc_styles.bigButtons + + lo = add_layout("skipback") + lo.geometry = + {x = posX - bigbtndist, y = bigbtnrowY, an = 5, w = 40, h = 40} + lo.style = osc_styles.bigButtons + + lo = add_layout("skipfrwd") + lo.geometry = + {x = posX + bigbtndist, y = bigbtnrowY, an = 5, w = 40, h = 40} + lo.style = osc_styles.bigButtons + + lo = add_layout("ch_prev") + lo.geometry = + {x = posX - (bigbtndist * 2), y = bigbtnrowY, an = 5, w = 40, h = 40} + lo.style = osc_styles.bigButtons + + lo = add_layout("ch_next") + lo.geometry = + {x = posX + (bigbtndist * 2), y = bigbtnrowY, an = 5, w = 40, h = 40} + lo.style = osc_styles.bigButtons + + lo = add_layout("cy_audio") + lo.geometry = + {x = posX - pos_offsetX, y = bigbtnrowY, an = 1, w = 70, h = 18} + lo.style = osc_styles.smallButtonsL + + lo = add_layout("cy_sub") + lo.geometry = + {x = posX - pos_offsetX, y = bigbtnrowY, an = 7, w = 70, h = 18} + lo.style = osc_styles.smallButtonsL + + lo = add_layout("tog_fs") + lo.geometry = + {x = posX+pos_offsetX, y = bigbtnrowY, an = 6, w = 25, h = 25} + lo.style = osc_styles.smallButtonsR + + -- + -- Seekbar + -- + + lo = add_layout("seekbar") + lo.geometry = + {x = posX, y = posY+pos_offsetY-22, an = 2, w = pos_offsetX*2, h = 15} + lo.style = osc_styles.timecodes + lo.slider.tooltip_style = osc_styles.vidtitle + + -- + -- Timecodes + Cache + -- + + local bottomrowY = posY + pos_offsetY - 5 + + lo = add_layout("tc_left") + lo.geometry = + {x = posX - pos_offsetX, y = bottomrowY, an = 4, w = 110, h = 18} + lo.style = osc_styles.timecodes + + lo = add_layout("tc_right") + lo.geometry = + {x = posX + pos_offsetX, y = bottomrowY, an = 6, w = 110, h = 18} + lo.style = osc_styles.timecodes + + lo = add_layout("cache") + lo.geometry = + {x = posX, y = bottomrowY, an = 5, w = 110, h = 18} + lo.style = osc_styles.timecodes + +end + +-- slim box layout +layouts["slimbox"] = function () + + local osc_geo = { + w = 600, -- width + h = 20, -- height + r = 20, -- corner-radius + p = 20, -- padding + } + + -- make sure the OSC actually fits into the video + if (osc_param.playresx < (osc_geo.w + (2 * osc_geo.p))) then + osc_param.playresy = (osc_geo.w+(2*osc_geo.p))/osc_param.display_aspect + osc_param.playresx = osc_param.playresy * osc_param.display_aspect end - local eventresponder = {} - eventresponder.mouse_btn0_up = function () + -- position of the controller according to video aspect and valignment + local posX = math.floor(get_align(user_opts.halign, osc_param.playresx, + osc_geo.w, 0)) + local posY = math.floor(get_align(user_opts.valign, osc_param.playresy, + osc_geo.h, 0)) - local title = mp.get_property("media-title") - local pl_count = tonumber(mp.get_property("playlist-count")) + -- position offset for contents aligned at the borders of the box + local pos_offsetX = (osc_geo.w - (2*osc_geo.p)) / 2 + local pos_offsetY = (osc_geo.h - (2*osc_geo.p)) / 2 - if pl_count > 1 then - local playlist_pos = countone(tonumber(mp.get_property("playlist-pos"))) - title = "[" .. playlist_pos .. "/" .. pl_count .. "] " .. title - end + osc_param.areas = {} -- delete areas - show_message(title) + -- area for active mouse input + add_area("input", get_hitbox_coords(posX, posY, 5, osc_geo.w, osc_geo.h)) + + -- area for show/hide + local sh_area_y0, sh_area_y1 + if user_opts.valign > 0 then + -- deadzone above OSC + sh_area_y0 = get_align(-1 + (2*user_opts.deadzonesize), + posY - (osc_geo.h / 2), 0, 0) + sh_area_y1 = osc_param.playresy + else + -- deadzone below OSC + sh_area_y0 = 0 + sh_area_y1 = (posY + (osc_geo.h / 2)) + + get_align(1 - (2*user_opts.deadzonesize), + osc_param.playresy - (posY + (osc_geo.h / 2)), 0, 0) end - eventresponder.mouse_btn2_up = function () show_message(mp.get_property("filename")) end + add_area("showhide", 0, sh_area_y0, osc_param.playresx, sh_area_y1) - register_button(posX, titlerowY, 8, 496, 12, osc_styles.vidtitle, contentF, eventresponder, nil) + -- fetch values + local osc_w, osc_h, osc_r, osc_p = + osc_geo.w, osc_geo.h, osc_geo.r, osc_geo.p - -- If we have more than one playlist entry, render playlist navigation buttons - local metainfo = {} - metainfo.visible = (tonumber(mp.get_property("playlist-count")) > 1) + local lo - -- playlist prev - local eventresponder = {} - eventresponder.mouse_btn0_up = function () mp.commandv("playlist_prev", "weak") end - eventresponder["shift+mouse_btn0_up"] = function () show_message(mp.get_property_osd("playlist"), 3) end - register_button(posX - pos_offsetX, titlerowY, 7, 12, 12, osc_styles.vidtitle, "◀", eventresponder, metainfo) + local tc_w = 70 - -- playlist next - local eventresponder = {} - eventresponder.mouse_btn0_up = function () mp.commandv("playlist_next", "weak") end - eventresponder["shift+mouse_btn0_up"] = function () show_message(mp.get_property_osd("playlist"), 3) end - register_button(posX + pos_offsetX, titlerowY, 9, 12, 12, osc_styles.vidtitle, "▶", eventresponder, metainfo) - -- - -- Big buttons - -- + new_element("bgbox", "box") + lo = add_layout("bgbox") - local bigbuttonrowY = posY - pos_offsetY + 35 - local bigbuttondistance = 60 + lo.geometry = {x = posX, y = posY, an = 5, w = osc_w, h = osc_h} + lo.layer = 10 + lo.style = osc_styles.box + lo.alpha[1] = user_opts.boxalpha + lo.box.radius = osc_r - --play/pause - local contentF = function (ass) - if mp.get_property("pause") == "yes" then - ass:append("\238\132\129") - else - ass:append("\238\128\130") - end - end - local eventresponder = {} - eventresponder.mouse_btn0_up = function () mp.commandv("cycle", "pause") end - register_button(posX, bigbuttonrowY, 5, 40, 40, osc_styles.bigButtons, contentF, eventresponder, nil) - --skipback - local metainfo = {} - metainfo.softrepeat = true + new_element("bgbar1", "box") + lo = add_layout("bgbar1") - local eventresponder = {} - eventresponder.mouse_btn0_down = function () mp.commandv("seek", -5, "relative", "keyframes") end - eventresponder["shift+mouse_btn0_down"] = function () mp.commandv("frame_back_step") end - eventresponder.mouse_btn2_down = function () mp.commandv("seek", -30, "relative", "keyframes") end - register_button(posX - bigbuttondistance, bigbuttonrowY, 5, 40, 40, osc_styles.bigButtons, "\238\128\132", eventresponder, metainfo) + lo.geometry = + {x = posX - (osc_w/2) + tc_w, y = posY, an = 5, w = 1, h = osc_h} + lo.layer = 20 + lo.style = osc_styles.timecodes + lo.alpha[1] = user_opts.boxalpha - --skipfrwd - local eventresponder = {} - eventresponder.mouse_btn0_down = function () mp.commandv("seek", 10, "relative", "keyframes") end - eventresponder["shift+mouse_btn0_down"] = function () mp.commandv("frame_step") end - eventresponder.mouse_btn2_down = function () mp.commandv("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_property_native("chapter-list", {})) > 0) - - --prev - local eventresponder = {} - eventresponder.mouse_btn0_up = function () mp.commandv("osd-msg", "add", "chapter", -1) end - eventresponder["shift+mouse_btn0_up"] = function () show_message(mp.get_property_osd("chapter-list"), 3) end - register_button(posX - (bigbuttondistance * 2), bigbuttonrowY, 5, 40, 40, osc_styles.bigButtons, "\238\132\132", eventresponder, metainfo) + new_element("bgbar2", "box") + lo = add_layout("bgbar2") - --next - local eventresponder = {} - eventresponder.mouse_btn0_up = function () mp.commandv("osd-msg", "add", "chapter", 1) end - eventresponder["shift+mouse_btn0_up"] = function () show_message(mp.get_property_osd("chapter-list"), 3) end - register_button(posX + (bigbuttondistance * 2), bigbuttonrowY, 5, 40, 40, osc_styles.bigButtons, "\238\132\133", eventresponder, metainfo) + lo.geometry = + {x = posX + (osc_w/2) - tc_w, y = posY, an = 5, w = 1, h = osc_h} + lo.layer = 20 + lo.style = osc_styles.timecodes + lo.alpha[1] = user_opts.boxalpha + lo = add_layout("seekbar") + lo.geometry = + {x = posX, y = posY, an = 5, w = osc_w - (tc_w*2), h = osc_h} + lo.style = osc_styles.timecodes + lo.slider.border = 0 + lo.slider.gap = 1.5 + lo.slider.tooltip_style = osc_styles.vidtitle + lo.slider.adjust_tooltip = false + -- - -- Smaller buttons + -- Timecodes -- - if not (user_opts.iamaprogrammer) then - update_tracklist() - end + local bottomrowY = posY + pos_offsetY - 5 - --cycle audio tracks + lo = add_layout("tc_left") + lo.geometry = + {x = posX - (osc_w/2) + tc_w - 5, y = posY+0.5, + an = 6, w = tc_w, h = osc_h} + lo.style = osc_styles.timecodes - local metainfo = {} - local eventresponder = {} - local contentF + lo = add_layout("tc_right") + lo.geometry = + {x = posX + (osc_w/2) - tc_w + 5, y = posY+0.5, + an = 4, w = tc_w, h = osc_h} + lo.style = osc_styles.timecodes - 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 +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.get_property("audio") - ass:append("\238\132\134" .. osc_styles.smallButtonsLlabel .. " " .. aid) - end +-- OSC INIT +function osc_init() + msg.debug("osc_init") - eventresponder.mouse_btn0_up = function () mp.commandv("osd-msg", "add", "audio", 1) end - eventresponder.mouse_btn2_up = function () mp.commandv("osd-msg", "add", "audio", -1) end + -- set canvas resolution according to display aspect and scaling setting + local baseResY = 720 + local display_w, display_h, display_aspect = mp.get_screen_size() + local scale = 1 + + if (mp.get_property("video") == "no") then -- dummy/forced window + scale = user_opts.scaleforcedwindow + elseif state.fullscreen then + scale = user_opts.scalefullscreen + else + scale = user_opts.scalewindowed end - register_button(posX - pos_offsetX, bigbuttonrowY, 1, 70, 18, osc_styles.smallButtonsL, contentF, eventresponder, metainfo) + if user_opts.vidscale then + osc_param.playresy = baseResY / scale + else + osc_param.playresy = display_h / scale + end + osc_param.playresx = osc_param.playresy * display_aspect + osc_param.display_aspect = display_aspect - --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") + elements = {} + + -- some often needed stuff + local pl_count = mp.get_property_number("playlist-count") + local have_pl = (pl_count > 1) + local have_ch = (mp.get_property_number("chapters", 0) > 0) + + local ne + + -- title + ne = new_element("title", "button") + + ne.content = function (ass) + local title = mp.get_property_osd("media-title") + if not (title == nil) then + if (#title > 80) then + title = string.format("{\\fscx%f}", (80/#title)*100) .. title end - ass:append("\238\132\135" .. osc_styles.smallButtonsLlabel .. " " .. sid .. "/" .. #tracks_osc.sub) + ass:append(title) + else + ass:append("mpv") end + 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) + ne.eventresponder["mouse_btn0_up"] = function () + local title = mp.get_property_osd("media-title") + if (have_pl) then + local pl_pos = countone(mp.get_property_number("playlist-pos")) + title = "[" .. pl_pos .. "/" .. pl_count .. "] " .. title end - else - metainfo.enabled = true - contentF = function (ass) - local sid = mp.get_property("sub") + show_message(title) + end - ass:append("\238\132\135" .. osc_styles.smallButtonsLlabel .. " " .. sid) - end + ne.eventresponder["mouse_btn2_up"] = + function () show_message(mp.get_property_osd("filename")) end - eventresponder.mouse_btn0_up = function () mp.commandv("osd-msg", "add", "sub", 1) end - eventresponder.mouse_btn2_up = function () mp.commandv("osd-msg", "add", "sub", -1) end - end - register_button(posX - pos_offsetX, bigbuttonrowY, 7, 70, 18, osc_styles.smallButtonsL, contentF, eventresponder, metainfo) + -- playlist buttons + -- prev + ne = new_element("pl_prev", "button") - --toggle FS - local contentF = function (ass) - if state.fullscreen then - ass:append("\238\132\137") + ne.content = "◀" + ne.visible = have_pl + ne.eventresponder["mouse_btn0_up"] = + function () mp.commandv("playlist_prev", "weak") end + ne.eventresponder["shift+mouse_btn0_up"] = + function () show_message(mp.get_property_osd("playlist"), 3) end + + --next + ne = new_element("pl_next", "button") + + ne.content = "▶" + ne.visible = have_pl + ne.eventresponder["mouse_btn0_up"] = + function () mp.commandv("playlist_next", "weak") end + ne.eventresponder["shift+mouse_btn0_up"] = + function () show_message(mp.get_property_osd("playlist"), 3) end + + + -- big buttons + + --playpause + ne = new_element("playpause", "button") + + ne.content = function (ass) + if mp.get_property("pause") == "yes" then + ass:append("\238\132\129") else - ass:append("\238\132\136") + ass:append("\238\128\130") end end - local eventresponder = {} - eventresponder.mouse_btn0_up = function () mp.commandv("cycle", "fullscreen") end - register_button(posX+pos_offsetX, bigbuttonrowY, 6, 25, 25, osc_styles.smallButtonsR, contentF, eventresponder, nil) + ne.eventresponder["mouse_btn0_up"] = + function () mp.commandv("cycle", "pause") end + --skipback + ne = new_element("skipback", "button") + + ne.softrepeat = true + ne.content = "\238\128\132" + ne.eventresponder["mouse_btn0_down"] = + function () mp.commandv("seek", -5, "relative", "keyframes") end + ne.eventresponder["shift+mouse_btn0_down"] = + function () mp.commandv("frame_back_step") end + ne.eventresponder["mouse_btn2_down"] = + function () mp.commandv("seek", -30, "relative", "keyframes") end + + --skipfrwd + ne = new_element("skipfrwd", "button") + + ne.softrepeat = true + ne.content = "\238\128\133" + ne.eventresponder["mouse_btn0_down"] = + function () mp.commandv("seek", 10, "relative", "keyframes") end + ne.eventresponder["shift+mouse_btn0_down"] = + function () mp.commandv("frame_step") end + ne.eventresponder["mouse_btn2_down"] = + function () mp.commandv("seek", 60, "relative", "keyframes") end + + --ch_prev + ne = new_element("ch_prev", "button") + + ne.enabled = have_ch + ne.content = "\238\132\132" + ne.eventresponder["mouse_btn0_up"] = + function () mp.commandv("osd-msg", "add", "chapter", -1) end + ne.eventresponder["shift+mouse_btn0_up"] = + function () show_message(mp.get_property_osd("chapter-list"), 3) end + + --ch_next + ne = new_element("ch_next", "button") + + ne.enabled = have_ch + ne.content = "\238\132\133" + ne.eventresponder["mouse_btn0_up"] = + function () mp.commandv("osd-msg", "add", "chapter", 1) end + ne.eventresponder["shift+mouse_btn0_up"] = + function () show_message(mp.get_property_osd("chapter-list"), 3) end -- - -- Seekbar - -- + update_tracklist() - local markerF = function () - local duration = 0 - if not (mp.get_property("length") == nil) then - duration = tonumber(mp.get_property("length")) - end + --cy_audio + ne = new_element("cy_audio", "button") - local chapters = mp.get_property_native("chapter-list", {}) - local markers = {} - for n = 1, #chapters do - markers[n] = (chapters[n].time / duration * 100) + ne.enabled = (#tracks_osc.audio > 0) + ne.content = function (ass) + local aid = "–" + if not (get_track("audio") == 0) then + aid = get_track("audio") end - return markers + ass:append("\238\132\134" .. osc_styles.smallButtonsLlabel + .. " " .. aid .. "/" .. #tracks_osc.audio) end - - local posF = function () - if mp.get_property("percent-pos") == nil then - return nil - else - return tonumber(mp.get_property("percent-pos")) + ne.eventresponder["mouse_btn0_up"] = + function () set_track("audio", 1) end + ne.eventresponder["mouse_btn2_up"] = + function () set_track("audio", -1) end + ne.eventresponder["shift+mouse_btn0_down"] = + function () show_message(get_tracklist("audio"), 2) end + + --cy_sub + ne = new_element("cy_sub", "button") + + ne.enabled = (#tracks_osc.sub > 0) + ne.content = 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 - - local tooltipF = function (pos) - if not (mp.get_property("length") == nil) then - duration = tonumber(mp.get_property("length")) - possec = duration * (pos / 100) - return mp.format_time(possec) + ne.eventresponder["mouse_btn0_up"] = + function () set_track("sub", 1) end + ne.eventresponder["mouse_btn2_up"] = + function () set_track("sub", -1) end + ne.eventresponder["shift+mouse_btn0_down"] = + function () show_message(get_tracklist("sub"), 2) end + + --tog_fs + ne = new_element("tog_fs", "button") + ne.content = function (ass) + if (state.fullscreen) then + ass:append("\238\132\137") else - return "" + ass:append("\238\132\136") end end + ne.eventresponder["mouse_btn0_up"] = + function () mp.commandv("cycle", "fullscreen") end - local metainfo = {} - - - metainfo.enabled = not (mp.get_property("percent-pos") == nil) - 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 - if (user_opts.seektooltip) and (not (mp.get_property("length") == nil)) then - metainfo.slider.tooltipF = tooltipF - end - local eventresponder = {} + --seekbar + ne = new_element("seekbar", "slider") - -- Do keyframe seeking when mouse is dragged - local sliderFfast = function (element) - mp.commandv("seek", get_slider_value(element), "absolute-percent", "keyframes") + ne.enabled = not (mp.get_property("percent-pos") == nil) + ne.slider.markerF = function () + local duration = mp.get_property_number("length", nil) + if not (duration == nil) then + local chapters = mp.get_property_native("chapter-list", {}) + local markers = {} + for n = 1, #chapters do + markers[n] = (chapters[n].time / duration * 100) + end + return markers + else + return {} + end end - eventresponder["mouse_move"] = sliderFfast - - -- Do exact seeks on single clicks - local sliderFexact = function (element) - mp.commandv("seek", get_slider_value(element), "absolute-percent", "exact") + ne.slider.posF = + function () return mp.get_property_number("percent-pos", nil) end + ne.slider.tooltipF = function (pos) + local duration = mp.get_property_number("length", nil) + if not ((duration == nil) or (pos == nil)) then + possec = duration * (pos / 100) + return mp.format_time(possec) + else + return "" + end end - eventresponder.mouse_btn0_down = sliderFexact + ne.eventresponder["mouse_move"] = --keyframe seeking when mouse is dragged + function (element) + -- mouse move events may pile up during seeking and may still get + -- sent when the user is done seeking, so we need to throw away + -- identical seeks + local seekto = get_slider_value(element) + if (element.state.lastseek == nil) or + (not (element.state.lastseek == seekto)) then + mp.commandv("seek", seekto, + "absolute-percent", "keyframes") + element.state.lastseek = seekto + end - register_slider(posX, posY+pos_offsetY-22, 2, pos_offsetX*2, 15, osc_styles.timecodes, 0, 100, markerF, posF, eventresponder, metainfo) + end + ne.eventresponder["mouse_btn0_down"] = --exact seeks on single clicks + function (element) mp.commandv("seek", get_slider_value(element), + "absolute-percent", "exact") end + ne.eventresponder["reset"] = + function (element) element.state.lastseek = nil end - -- - -- Timecodes + Volume - -- - local bottomrowY = posY + pos_offsetY - 5 + -- tc_left (current pos) + ne = new_element("tc_left", "button") - -- left (current pos) - local metainfo = {} - local eventresponder = {} - - local contentF = function (ass) - if state.tc_ms then + ne.content = function (ass) + if (state.tc_ms) then ass:append(mp.get_property_osd("playback-time/full")) else ass:append(mp.get_property_osd("playback-time")) end end + ne.eventresponder["mouse_btn0_up"] = + function () state.tc_ms = not state.tc_ms 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) - - -- center (Cache) - local metainfo = {} - local eventresponder = {} + -- tc_right (total/remaining time) + ne = new_element("tc_right", "button") - local contentF = function (ass) - local cache = mp.get_property_number("cache") - if not (cache == nil) and (cache < 45) then - ass:append("Cache: " .. (math.floor(cache)) .."%") - end - end - register_button(posX, bottomrowY, 5, 110, 18, osc_styles.timecodes, contentF, eventresponder, metainfo) - - - -- right (total/remaining time) - -- do we have a usuable duration? - local metainfo = {} - metainfo.visible = (not (mp.get_property("length") == nil)) and (tonumber(mp.get_property("length")) > 0) - - local contentF = function (ass) - if state.rightTC_trem == true then + ne.visible = (not (mp.get_property("length") == nil)) + and (mp.get_property_number("length") > 0) + ne.content = function (ass) + if (state.rightTC_trem) then if state.tc_ms then - ass:append("-" .. mp.get_property_osd("playtime-remaining/full")) + ass:append("-"..mp.get_property_osd("playtime-remaining/full")) else - ass:append("-" .. mp.get_property_osd("playtime-remaining")) + ass:append("-"..mp.get_property_osd("playtime-remaining")) end else if state.tc_ms then @@ -951,13 +1213,31 @@ function osc_init() end end end - local eventresponder = {} - eventresponder.mouse_btn0_up = function () state.rightTC_trem = not state.rightTC_trem end + ne.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) + -- cache + ne = new_element("cache", "button") + + ne.content = function (ass) + local cache = mp.get_property_number("cache") + if not (cache == nil) and (cache < 45) then + ass:append("Cache: " .. (math.floor(cache)) .."%") + end + end + + + + -- load layout + layouts[user_opts.layout]() + + --do something with the elements + prepare_elements() end + + -- -- Other important stuff -- @@ -968,7 +1248,7 @@ function show_osc() --remember last time of invocation (mouse move) state.showtime = mp.get_time() - state.osc_visible = true + osc_visible(true) if (user_opts.fadeduration > 0) then state.anitype = nil @@ -981,12 +1261,69 @@ function hide_osc() if (user_opts.fadeduration > 0) then if not(state.osc_visible == false) then state.anitype = "out" + control_timer() end else - state.osc_visible = false + osc_visible(false) + end +end + +function osc_visible(visible) + state.osc_visible = visible + control_timer() +end + +function pause_state(name, enabled) + state.paused = enabled + control_timer() +end + +function cache_state(name, idle) + state.cache_idle = idle + control_timer() +end + +function control_timer() + if (state.paused) and (state.osc_visible) and + ( not(state.cache_idle) or not (state.anitype == nil) ) then + + timer_start() + else + timer_stop() + end +end + +function timer_start() + if not (state.timer_active) then + msg.debug("timer start") + + if (state.timer == nil) then + -- create new timer + state.timer = mp.add_periodic_timer(0.03, tick) + else + -- resume existing one + state.timer:resume() + end + + state.timer_active = true end end +function timer_stop() + if (state.timer_active) then + msg.debug("timer stop") + + if not (state.timer == nil) then + -- kill timer + state.timer:kill() + end + + state.timer_active = false + end +end + + + function mouse_leave() hide_osc() -- reset mouse position @@ -999,14 +1336,18 @@ end function render() msg.debug("rendering") - local current_screen_sizeX, current_screen_sizeY = mp.get_screen_size() + local current_screen_sizeX, current_screen_sizeY, aspect = mp.get_screen_size() local mouseX, mouseY = mp.get_mouse_pos() local now = mp.get_time() -- check if display changed, if so request reinit - if not (state.mp_screen_sizeX == current_screen_sizeX and state.mp_screen_sizeY == current_screen_sizeY) then + if not (state.mp_screen_sizeX == current_screen_sizeX + and state.mp_screen_sizeY == current_screen_sizeY) then + request_init() - state.mp_screen_sizeX, state.mp_screen_sizeY = current_screen_sizeX, current_screen_sizeY + + state.mp_screen_sizeX = current_screen_sizeX + state.mp_screen_sizeY = current_screen_sizeY end -- init management @@ -1015,17 +1356,13 @@ function render() state.initREQ = false -- store initial mouse position - if (state.last_mouseX == nil or state.last_mouseY == nil) and not (mouseX == nil or mouseY == nil) then + 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 end - -- autohide - if not (state.showtime == nil) and (user_opts.hidetimeout >= 0) 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 -- fade animation if not(state.anitype == nil) then @@ -1037,14 +1374,16 @@ function render() if (now < state.anistart + (user_opts.fadeduration/1000)) then if (state.anitype == "in") then --fade in - state.osc_visible = true + 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 + elseif (state.anitype == "out") then --fade out 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 + if (state.anitype == "out") then + osc_visible(false) + end state.anistart = nil state.animation = nil state.anitype = nil @@ -1055,6 +1394,36 @@ function render() state.anitype = nil end + --mouse show/hide area + for k,cords in pairs(osc_param.areas["showhide"]) do + mp.set_mouse_area(cords.x1, cords.y1, cords.x2, cords.y2, "showhide") + end + + --mouse input area + local mouse_over_osc = false + + for _,cords in ipairs(osc_param.areas["input"]) do + if state.osc_visible then -- activate only when OSC is actually visible + mp.set_mouse_area(cords.x1, cords.y1, cords.x2, cords.y2, "input") + mp.enable_key_bindings("input") + else + mp.disable_key_bindings("input") + end + + if (mouse_hit_coords(cords.x1, cords.y1, cords.x2, cords.y2)) then + mouse_over_osc = true + end + end + + -- autohide + if not (state.showtime == nil) and (user_opts.hidetimeout >= 0) + and (state.showtime + (user_opts.hidetimeout/1000) < now) + and (state.active_element == nil) and not (mouse_over_osc) then + + hide_osc() + end + + -- actual rendering local ass = assdraw.ass_new() @@ -1067,35 +1436,10 @@ function render() end -- submit - 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 - area_y0 = get_align(-1 + (2*user_opts.deadzonesize), osc_param.posY - (osc_param.osc_h / 2), 0, 0) - area_y1 = osc_param.playresy - else - -- deadzone below OSC - area_y0 = 0 - area_y1 = (osc_param.posY + (osc_param.osc_h / 2)) - + get_align(1 - (2*user_opts.deadzonesize), osc_param.playresy - (osc_param.posY + (osc_param.osc_h / 2)), 0, 0) - end - --mouse show/hide area - mp.set_mouse_area(0, area_y0, osc_param.playresx, area_y1, "showhide") - --mouse input area - if state.osc_visible then -- activate only when OSC is actually visible - 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") - mp.enable_key_bindings("input") - else - mp.disable_key_bindings("input") - end end @@ -1202,6 +1546,8 @@ mp.register_script_message("enable-osc", function() enable_osc(true) end) mp.register_script_message("disable-osc", function() enable_osc(false) end) mp.observe_property("fullscreen", "bool", function(name, val) state.fullscreen = val end) +mp.observe_property("pause", "bool", pause_state) +mp.observe_property("cache-idle", "bool", cache_state) -- mouse show/hide bindings mp.set_key_bindings({ -- cgit v1.2.3