--[[ Name: Metrognome-2.0 Revision: $Rev: 12749 $ Author: Tekkub Stoutwrithe (tekkub@gmail.com) Website: http://wiki.wowace.com/index.php/Metrognome_Embedded_Library Documentation: http://wiki.wowace.com/index.php/Metrognome_Embedded_Library_Methods SVN: svn://svn.wowace.com/root/trunk/Metrognome/Metrognome-2.0 Description: OnUpdate timer managing library Dependencies: AceLibrary, AceOO-2.0, Compost-2.0 (optional) ]] local vmajor, vminor = "Metrognome-2.0", "$Revision: 12749 $" if not AceLibrary then error(vmajor .. " requires AceLibrary.") end if not AceLibrary:IsNewVersion(vmajor, vminor) then return end if not AceLibrary:HasInstance("AceOO-2.0") then error(vmajor .. " requires AceOO-2.0") end local compost = AceLibrary:HasInstance("Compost-2.0") and AceLibrary("Compost-2.0") local AceOO = AceLibrary:GetInstance("AceOO-2.0") local Mixin = AceOO.Mixin local Metrognome = Mixin { "RegisterMetro", "UnregisterMetro", "StartMetro", "StopMetro", "ChangeMetroRate", "ClearMetroStats", "MetroStatus", "MetroProfile", } local function getnewtable() return compost and compost:Acquire() or {} end local function reclaimtable(t) if compost then compost:Reclaim(t) end end -- Sets up a new OnUpdate handler -- name - A unique name, if you only need one handler then your addon's name will suffice here -- func - Function to be called -- rate (optional but highly reccomended) - The rate (in seconds) at which your function should be called -- a1-4 (optional) - A args to be passed to func, this is a great place to pass self -- if a2 is defined then the elapsed time will not be passed to your function! -- Returns true if you've been registered function Metrognome:Register(name, func, rate, a1, a2, a3, a4, a5, a6) Metrognome:argCheck(name, 2, "string") Metrognome:argCheck(func, 3, "function") Metrognome:assert(not Metrognome.var.handlers[name], "A timer with the name "..name.." is already registered") local t = getnewtable() t.name, t.func, t.rate = name, func, rate or 0 t.a1, t.a2, t.a3, t.a4, t.a5, t.a6 = a1, a2, a3, a4, a5, a6 Metrognome.var.handlers[name] = t return true end -- Removes an OnUpdate handler -- name - the hander you want to remove -- Returns true if successful function Metrognome:Unregister(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) Metrognome:argCheck(a1, 2, "string") if not Metrognome.var.handlers[a1] then return end reclaimtable(Metrognome.var.handlers[a1]) Metrognome.var.handlers[a1] = nil if a2 then Metrognome:Unregister(a2,a3,a4,a5,a6,a7,a8,a9,a10) elseif not Metrognome:HasHandlers() then Metrognome.var.frame:Hide() end return true end -- Begins triggering updates -- name - the hander you want to start -- numexec (optional) - Limit the number of times the timer runs -- Returns true if successful function Metrognome:Start(name, numexec) Metrognome:argCheck(name, 2, "string") if not Metrognome.var.handlers[name] then return end Metrognome.var.handlers[name].limit = numexec Metrognome.var.handlers[name].elapsed = 0 Metrognome.var.handlers[name].running = true Metrognome.var.frame:Show() return true end -- Stops triggering updates -- name - the hander you want to stop -- Returns true if successful function Metrognome:Stop(name) Metrognome:argCheck(name, 2, "string") if not Metrognome.var.handlers[name] then return end Metrognome.var.handlers[name].running = nil Metrognome.var.handlers[name].limit = nil if not Metrognome:HasHandlers() then Metrognome.var.frame:Hide() end return true end -- Changes the execution rate of a timer. -- This will also reset the timer's elapsed time to 0 -- name - The timer you wish to change -- newrate (optional)- The new exec rate, in seconds. If nil or 0 default OnUpdate timing will be used -- n#,r# (optional) - Recusivly calls ChangeRate to allow you to set up to 5 rates in one call. -- Returns true if successful function Metrognome:ChangeRate(name, newrate, n2,r2,n3,r3,n4,r4,n5,r5) Metrognome:argCheck(name, 2, "string") if not Metrognome.var.handlers[name] then if n2 then return nil, Metrognome:ChangeRate(n2,r2,n3,r3,n4,r4,n5,r5) else return end end local t = Metrognome.var.handlers[name] t.elapsed = 0 t.rate = newrate or 0 if n2 then return true, Metrognome:ChangeRate(n2,r2,n3,r3,n4,r4,n5,r5) else return true end end -- Resets the profile stats for a timer -- Accepts up to 10 timer names to clear function Metrognome:ClearStats(name, n2, n3, n4, n5, n6, n7, n8, n9, n10) Metrognome:argCheck(name, 2, "string") if not Metrognome.var.handlers[name] then if n2 then return nil, Metrognome:ClearStats(n2,r2,n3,r3,n4,r4,n5,r5) else return end end local t = Metrognome.var.handlers[name] t.count, t.mem, t.time = 0, 0, 0 if n2 then return true, Metrognome:ClearStats(n2,r2,n3,r3,n4,r4,n5,r5) else return true end end -- Query a timer's status -- Args: name - the schedule you wish to look up -- Returns: registered - true if a schedule exists with this name -- rate - the registered rate, if defined -- running - true if this schedule is currently running -- limit - limit of times to repeat this timer -- elapsed - time elapsed this cycle of the timer function Metrognome:Status(name) Metrognome:argCheck(name, 2, "string") if not Metrognome.var.handlers[name] then return end return true, Metrognome.var.handlers[name].rate, Metrognome.var.handlers[name].running, Metrognome.var.handlers[name].limit, Metrognome.var.handlers[name].elapsed end -- Query a timer's profile info -- Args: name - the schedule you wish to look up -- Returns: mem - the total memory consumed by the timer's execution (in KiB) -- time - the total time consumed by the timer's execution (in sec) -- count - the number of times the timer has been triggered -- rate - the rate at which the timer triggers (0 means the default OnUpdate rate) function Metrognome:Profile(name) Metrognome:argCheck(name, 2, "string") if not Metrognome.var.handlers[name] then return end local t = Metrognome.var.handlers[name] return t.mem, t.time, t.count, t.rate end function Metrognome:OnUpdate() for i,v in pairs(this.owner.var.handlers) do if v.running then v.elapsed = v.elapsed + arg1 if v.elapsed >= v.rate then local mem, time = gcinfo(), GetTime() v.func(v.a1 or v.arg, v.a2 or v.elapsed, v.a3, v.a4, v.a5, v.a6) mem, time = gcinfo() - mem, GetTime() - time if mem >= 0 then v.mem, v.time, v.count = (v.mem or 0) + mem, (v.time or 0) + time, (v.count or 0) + 1 end v.elapsed = 0 if v.limit then v.limit = v.limit - 1 end if v.limit and v.limit <= 0 then this.owner:Stop(i) end end end end end function Metrognome:HasHandlers() if next(Metrognome.var.handlers) then return true end end ------------------------------ -- Mixins Methods -- ------------------------------ Metrognome.StartMetro = Metrognome.Start Metrognome.StopMetro = Metrognome.Stop Metrognome.ChangeMetroRate = Metrognome.ChangeRate Metrognome.ClearMetroStats = Metrognome.ClearStats Metrognome.MetroStatus = Metrognome.Status Metrognome.MetroProfile = Metrognome.Profile function Metrognome:RegisterMetro(name, func, rate, a1, a2, a3, a4, a5, a6) if not Metrognome.var.addons[self] then Metrognome.var.addons[self] = getnewtable() end Metrognome.var.addons[self][name] = Metrognome:Register(name, func, rate, a1, a2, a3, a4, a5, a6) end function Metrognome:UnregisterMetro(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) Metrognome:argCheck(a1, 2, "string") if Metrognome.var.addons[self] then Metrognome.var.addons[self][a1] = nil end Metrognome:Unregister(a1) if a2 then self:UnregisterMetro(a2,a3,a4,a5,a6,a7,a8,a9,a10) end end function Metrognome:OnEmbedDisable(target) if self.var.addons[target] then for i in pairs(Metrognome.var.addons[target]) do self:Stop(i) end end end -------------------------------- -- Load this bitch! -- -------------------------------- function Metrognome:activate(oldLib, oldDeactivate) Metrognome = self if oldLib then self.var = oldLib.var else self.var = { -- "Local" variables go here frame = CreateFrame("Frame"), handlers = {}, addons = {}, } self.var.frame:Hide() self.var.frame.name = "Metrognome-2.0 Frame" end if not self.var.addons then self.var.addons = {} end self.var.frame:SetScript("OnUpdate", self.OnUpdate) self.var.frame.owner = self self.super.activate(self, oldLib, oldDeactivate) if oldDeactivate then oldDeactivate(oldLib) end end local function external(self, major, instance) if major == "Compost-2.0" then compost = instance end end AceLibrary:Register(Metrognome, vmajor, vminor, Metrognome.activate, nil, external)