mirror of
https://github.com/parnic/LibDogTag-3.0.git
synced 2025-06-16 12:10:13 -05:00
880 lines
26 KiB
Lua
880 lines
26 KiB
Lua
local MAJOR_VERSION = "LibDogTag-3.0"
|
|
local MINOR_VERSION = ((tonumber(("@project-date-integer@"):match("%d+")) or 33333333333333))
|
|
|
|
if MINOR_VERSION > _G.DogTag_MINOR_VERSION then
|
|
_G.DogTag_MINOR_VERSION = MINOR_VERSION
|
|
end
|
|
|
|
local type, error, math, next, pairs, ipairs, select, rawget, setmetatable, _G, assert =
|
|
type, error, math, next, pairs, ipairs, select, rawget, setmetatable, _G, assert
|
|
|
|
-- #AUTODOC_NAMESPACE DogTag
|
|
|
|
DogTag_funcs[#DogTag_funcs+1] = function(DogTag)
|
|
|
|
local newList, del, deepCopy = DogTag.newList, DogTag.del, DogTag.deepCopy
|
|
local fixNamespaceList = DogTag.fixNamespaceList
|
|
local memoizeTable = DogTag.memoizeTable
|
|
local select2 = DogTag.select2
|
|
local kwargsToKwargTypes = DogTag.kwargsToKwargTypes
|
|
local kwargsToKwargTypesWithTableCache = DogTag.kwargsToKwargTypesWithTableCache
|
|
local codeToFunction, codeEvaluationTime, evaluate, fsToKwargs, fsToFrame, fsToNSList, fsToCode, updateFontString, updateFontStrings
|
|
local fsNeedUpdate, fsNeedQuickUpdate
|
|
local _clearCodes
|
|
DogTag_funcs[#DogTag_funcs+1] = function()
|
|
codeToFunction = DogTag.codeToFunction
|
|
codeEvaluationTime = DogTag.codeEvaluationTime
|
|
evaluate = DogTag.evaluate
|
|
fsToFrame = DogTag.fsToFrame
|
|
fsToKwargs = DogTag.fsToKwargs
|
|
fsToNSList = DogTag.fsToNSList
|
|
fsToCode = DogTag.fsToCode
|
|
updateFontString = DogTag.updateFontString
|
|
updateFontStrings = DogTag.updateFontStrings
|
|
for fs in pairs(fsToFrame) do
|
|
fsNeedQuickUpdate[fs] = true
|
|
end
|
|
_clearCodes = DogTag._clearCodes
|
|
end
|
|
|
|
local EventHandlers, TimerHandlers
|
|
|
|
if DogTag.oldLib then
|
|
fsNeedUpdate = DogTag.oldLib.fsNeedUpdate
|
|
for k in pairs(fsNeedUpdate) do
|
|
fsNeedUpdate[k] = nil
|
|
end
|
|
fsNeedQuickUpdate = DogTag.oldLib.fsNeedQuickUpdate
|
|
for k in pairs(fsNeedQuickUpdate) do
|
|
fsNeedQuickUpdate[k] = nil
|
|
end
|
|
EventHandlers = DogTag.oldLib.EventHandlers or {}
|
|
TimerHandlers = DogTag.oldLib.TimerHandlers or {}
|
|
else
|
|
fsNeedUpdate = {}
|
|
fsNeedQuickUpdate = {}
|
|
EventHandlers = {}
|
|
TimerHandlers = {}
|
|
end
|
|
DogTag.fsNeedUpdate = fsNeedUpdate
|
|
DogTag.fsNeedQuickUpdate = fsNeedQuickUpdate
|
|
DogTag.EventHandlers = EventHandlers
|
|
DogTag.TimerHandlers = TimerHandlers
|
|
|
|
local frame
|
|
if DogTag.oldLib then
|
|
frame = DogTag.oldLib.frame
|
|
frame:SetScript("OnEvent", nil)
|
|
frame:SetScript("OnUpdate", nil)
|
|
frame:Show()
|
|
frame:UnregisterAllEvents()
|
|
if DogTag.oldLib.UnregisterCustomClassColors then
|
|
DogTag.oldLib:UnregisterCustomClassColors()
|
|
end
|
|
else
|
|
frame = CreateFrame("Frame")
|
|
end
|
|
DogTag.frame = frame
|
|
frame:RegisterEvent("ADDON_LOADED")
|
|
frame:RegisterEvent("PLAYER_LOGIN")
|
|
|
|
-- Keep track of which events we've attempted to register
|
|
-- so we aren't constantly trying to re-register them.
|
|
local usedEvents = {}
|
|
DogTag.usedEvents = usedEvents
|
|
|
|
-- Declare that a particular event is being listened to through DogTag
|
|
-- and should therefore be registered with the main event handler frame
|
|
-- (if it is a WoW event).
|
|
-- Also notifies sublibraries of the event listner request via the
|
|
-- EventRequested event. Events all the way down.
|
|
function DogTag.eventUsed(event)
|
|
-- Always notify of the event request
|
|
-- because sublibraries may have since upgraded,
|
|
-- or new sublibraries loaded:
|
|
DogTag:FireEvent("EventRequested", event)
|
|
|
|
if not usedEvents[event] then
|
|
usedEvents[event] = true
|
|
pcall(frame.RegisterEvent, frame, event)
|
|
end
|
|
end
|
|
|
|
local codeToEventList
|
|
do
|
|
local codeToEventList_mt = {__index = function(self, kwargTypes)
|
|
local t = newList()
|
|
t[""] = false
|
|
self[kwargTypes] = t
|
|
return t
|
|
end}
|
|
codeToEventList = setmetatable({}, {__index = function(self, nsList)
|
|
local t = setmetatable(newList(), codeToEventList_mt)
|
|
self[nsList] = t
|
|
return t
|
|
end})
|
|
end
|
|
DogTag.codeToEventList = codeToEventList
|
|
|
|
DogTag.callback_num = 0
|
|
local callbackToNSList, callbackToKwargs, callbackToFunction, callbackToCode, callbackToExtraArg
|
|
if DogTag.oldLib and DogTag.oldLib.callbackToNSList then
|
|
local oldLib = DogTag.oldLib
|
|
DogTag.callback_num = oldLib.callback_num
|
|
callbackToNSList = oldLib.callbackToNSList
|
|
callbackToKwargs = {}
|
|
for uid, kwargs in pairs(oldLib.callbackToKwargs) do
|
|
callbackToKwargs[uid] = memoizeTable(deepCopy(kwargs))
|
|
end
|
|
callbackToFunction = oldLib.callbackToFunction
|
|
callbackToCode = oldLib.callbackToCode
|
|
callbackToExtraArg = oldLib.callbackToExtraArg
|
|
else
|
|
callbackToNSList = {}
|
|
callbackToKwargs = {}
|
|
callbackToFunction = {}
|
|
callbackToCode = {}
|
|
callbackToExtraArg = {}
|
|
if DogTag.oldLib and DogTag.oldLib.callbacks then
|
|
for nsList, callbacks_nsList in pairs(DogTag.oldLib.callbacks) do
|
|
for kwargTypes, callbacks_nsList_kwargTypes in pairs(callbacks_nsList) do
|
|
for kwargs, callbacks_nsList_kwargTypes_kwargs in pairs(callbacks_nsList_kwargTypes) do
|
|
for code, callbacks_nsList_kwargTypes_kwargs_code in pairs(callbacks_nsList_kwargTypes_kwargs) do
|
|
if type(callbacks_nsList_kwargTypes_kwargs_code) == "function" then
|
|
local uid = DogTag.callback_num + 1
|
|
DogTag.callback_num = uid
|
|
callbackToNSList[uid] = nsList
|
|
callbackToKwargs[uid] = memoizeTable(deepCopy(kwargs))
|
|
callbackToFunction[uid] = callbacks_nsList_kwargTypes_kwargs_code
|
|
callbackToCode[uid] = code
|
|
else -- table
|
|
for k in pairs(callbacks_nsList_kwargTypes_kwargs_code) do
|
|
local uid = DogTag.callback_num + 1
|
|
DogTag.callback_num = uid
|
|
callbackToNSList[uid] = nsList
|
|
callbackToKwargs[uid] = memoizeTable(deepCopy(kwargs))
|
|
callbackToFunction[uid] = k
|
|
callbackToCode[uid] = code
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local callbackToKwargTypes = {}
|
|
for uid, kwargs in pairs(callbackToKwargs) do
|
|
callbackToKwargTypes[uid] = kwargsToKwargTypesWithTableCache[kwargs]
|
|
end
|
|
|
|
DogTag.callbackToNSList = callbackToNSList
|
|
DogTag.callbackToKwargs = callbackToKwargs
|
|
DogTag.callbackToFunction = callbackToFunction
|
|
DogTag.callbackToCode = callbackToCode
|
|
DogTag.callbackToKwargTypes = callbackToKwargTypes
|
|
DogTag.callbackToExtraArg = callbackToExtraArg
|
|
|
|
local eventData = setmetatable({}, {__index = function(self, key)
|
|
local t = newList()
|
|
self[key] = t
|
|
return t
|
|
end})
|
|
DogTag.eventData = eventData
|
|
|
|
function DogTag.hasEvent(event)
|
|
local hasEvent = not not rawget(eventData, event)
|
|
if hasEvent then
|
|
return true
|
|
end
|
|
|
|
for uid, nsList in pairs(callbackToNSList) do
|
|
local kwargTypes = callbackToKwargTypes[uid]
|
|
local code = callbackToCode[uid]
|
|
local eventList = codeToEventList[nsList][kwargTypes][code]
|
|
if eventList then
|
|
local eventList_event = eventList[event]
|
|
if eventList_event then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
--[[
|
|
Notes:
|
|
Adds a callback that will be called if the code in question is to be updated.
|
|
Arguments:
|
|
string - the tag sequence
|
|
function - the function to be called
|
|
[optional] string - a semicolon-separated list of namespaces. Base is implied
|
|
[optional] table - a dictionary of default kwargs for all tags in the code to receive
|
|
[optional] value - a value that will be passed into the callback
|
|
Example:
|
|
LibStub("LibDogTag-3.0"):AddCallback("[Name]", function(code, kwargs)
|
|
-- do something here
|
|
end, "Unit", { unit = 'player' })
|
|
]]
|
|
function DogTag:AddCallback(code, callback, nsList, kwargs, extraArg)
|
|
if type(code) ~= "string" then
|
|
error(("Bad argument #2 to `AddCallback'. Expected %q, got %q."):format("string", type(code)), 2)
|
|
elseif type(callback) ~= "function" then
|
|
error(("Bad argument #3 to `AddCallback'. Expected %q, got %q."):format("function", type(callback)), 2)
|
|
elseif nsList and type(nsList) ~= "string" then
|
|
error(("Bad argument #4 to `AddCallback'. Expected %q, got %q."):format("string", type(nsList)), 2)
|
|
elseif kwargs and type(kwargs) ~= "table" then
|
|
error(("Bad argument #5 to `AddCallback'. Expected %q, got %q."):format("table", type(kwargs)), 2)
|
|
end
|
|
|
|
kwargs = memoizeTable(deepCopy(kwargs or false))
|
|
|
|
nsList = fixNamespaceList[nsList]
|
|
local kwargTypes = kwargsToKwargTypesWithTableCache[kwargs]
|
|
local codeToEventList_nsList_kwargTypes = codeToEventList[nsList][kwargTypes]
|
|
local eventList = codeToEventList_nsList_kwargTypes[code]
|
|
if eventList == nil then
|
|
local _ = codeToFunction[nsList][kwargTypes][code]
|
|
eventList = codeToEventList_nsList_kwargTypes[code]
|
|
assert(eventList ~= nil)
|
|
end
|
|
|
|
for event in pairs(eventList) do
|
|
DogTag.eventUsed(event)
|
|
end
|
|
|
|
local uid = DogTag.callback_num + 1
|
|
DogTag.callback_num = uid
|
|
|
|
callbackToNSList[uid] = nsList
|
|
callbackToKwargs[uid] = kwargs
|
|
callbackToCode[uid] = code
|
|
callbackToFunction[uid] = callback
|
|
callbackToKwargTypes[uid] = kwargTypes
|
|
callbackToExtraArg[uid] = extraArg
|
|
end
|
|
|
|
--[[
|
|
Notes:
|
|
Remove a callback that has been previously added
|
|
Arguments:
|
|
string - the tag sequence
|
|
function - the function to be called
|
|
[optional] string - a semicolon-separated list of namespaces. Base is implied
|
|
[optional] table - a dictionary of default kwargs for all tags in the code to receive
|
|
Example:
|
|
LibStub("LibDogTag-3.0"):RemoveCallback("[Name]", func, "Unit", { unit = 'player' })
|
|
]]
|
|
function DogTag:RemoveCallback(code, callback, nsList, kwargs, extraArg)
|
|
if type(code) ~= "string" then
|
|
error(("Bad argument #2 to `RemoveCallback'. Expected %q, got %q."):format("string", type(code)), 2)
|
|
elseif type(callback) ~= "function" then
|
|
error(("Bad argument #3 to `RemoveCallback'. Expected %q, got %q."):format("function", type(callback)), 2)
|
|
elseif nsList and type(nsList) ~= "string" then
|
|
error(("Bad argument #4 to `RemoveCallback'. Expected %q, got %q."):format("string", type(nsList)), 2)
|
|
elseif kwargs and type(kwargs) ~= "table" then
|
|
error(("Bad argument #5 to `RemoveCallback'. Expected %q, got %q."):format("table", type(kwargs)), 2)
|
|
end
|
|
nsList = fixNamespaceList[nsList]
|
|
kwargs = memoizeTable(deepCopy(kwargs or false))
|
|
|
|
for uid, n in pairs(callbackToNSList) do
|
|
if n == nsList and callbackToKwargs[uid] == kwargs and callbackToCode[uid] == code and callbackToFunction[uid] == callback and callbackToExtraArg[uid] == extraArg then
|
|
callbackToNSList[uid] = nil
|
|
callbackToCode[uid] = nil
|
|
callbackToKwargs[uid] = nil
|
|
callbackToKwargTypes[uid] = nil
|
|
callbackToFunction[uid] = nil
|
|
callbackToExtraArg[uid] = nil
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
local function OnEvent(this, event, ...)
|
|
if DogTag[event] then
|
|
DogTag[event](DogTag, event, ...)
|
|
end
|
|
for namespace, data in pairs(EventHandlers) do
|
|
if data[event] then
|
|
for func in pairs(data[event]) do
|
|
func(event, ...)
|
|
end
|
|
end
|
|
end
|
|
local arg1 = (...)
|
|
for uid, nsList in pairs(callbackToNSList) do
|
|
local kwargTypes = callbackToKwargTypes[uid]
|
|
local code = callbackToCode[uid]
|
|
local eventList = codeToEventList[nsList][kwargTypes][code]
|
|
if eventList then
|
|
local eventList_event = eventList[event]
|
|
if eventList_event then
|
|
local good = false
|
|
local checkKwargs = false
|
|
local mustEvaluate = false
|
|
local checkTable = false
|
|
local multiArg = false
|
|
if eventList_event == true then
|
|
good = true
|
|
elseif type(eventList_event) == "table" then
|
|
good = true
|
|
checkTable = true
|
|
else
|
|
local tab = newList(("#"):split(eventList_event))
|
|
if #tab == 1 then
|
|
if eventList_event == arg1 then
|
|
good = true
|
|
elseif eventList_event:match("^%$") then
|
|
good = true
|
|
checkKwargs = eventList_event:sub(2)
|
|
elseif eventList_event:match("^%[.*%]$") then
|
|
good = true
|
|
mustEvaluate = eventList_event
|
|
end
|
|
tab = del(tab)
|
|
else
|
|
good = true
|
|
multiArg = tab
|
|
end
|
|
end
|
|
if good then
|
|
local kwargs = callbackToKwargs[uid]
|
|
good = true
|
|
if multiArg then
|
|
good = false
|
|
for i, v in ipairs(multiArg) do
|
|
local arg = select(i, ...)
|
|
if not arg then
|
|
good = false
|
|
elseif v == arg then
|
|
good = true
|
|
elseif v:match("^%$") then
|
|
good = kwargs[v:sub(2)] == arg
|
|
elseif v:match("^%[.*%]$") then
|
|
good = evaluate(v, nsList, kwargs) == arg
|
|
else
|
|
good = false
|
|
end
|
|
if not good then
|
|
break
|
|
end
|
|
end
|
|
multiArg = del(multiArg)
|
|
elseif checkTable then
|
|
good = false
|
|
for k in pairs(eventList_event) do
|
|
if k == arg1 then
|
|
good = true
|
|
else
|
|
local multiArg = newList(("#"):split(k))
|
|
for i, v in ipairs(multiArg) do
|
|
local arg = select(i, ...)
|
|
if not arg then
|
|
good = false
|
|
elseif v == arg then
|
|
good = true
|
|
elseif v:match("^%$") then
|
|
good = kwargs[v:sub(2)] == arg
|
|
elseif v:match("^%[.*%]$") then
|
|
good = evaluate(v, nsList, kwargs) == arg
|
|
else
|
|
good = false
|
|
end
|
|
if not good then
|
|
break
|
|
end
|
|
end
|
|
multiArg = del(multiArg)
|
|
end
|
|
if good then
|
|
break
|
|
end
|
|
end
|
|
elseif mustEvaluate then
|
|
good = evaluate(mustEvaluate, nsList, kwargs) == arg1
|
|
elseif checkKwargs then
|
|
good = kwargs[checkKwargs] == arg1
|
|
end
|
|
if good then
|
|
local func = callbackToFunction[uid]
|
|
local extraArg = callbackToExtraArg[uid]
|
|
if extraArg ~= nil then
|
|
func(extraArg, code, nsList, kwargs or nil)
|
|
else
|
|
func(code, nsList, kwargs or nil)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local eventData_event = eventData[event]
|
|
for fs, param in pairs(eventData_event) do
|
|
local kwargs = fsToKwargs[fs]
|
|
local nsList = fsToNSList[fs]
|
|
local good = false
|
|
local checkKwargs = false
|
|
local mustEvaluate = false
|
|
local checkTable = false
|
|
local multiArg = false
|
|
if param == true then
|
|
good = true
|
|
elseif type(param) == "table" then
|
|
good = true
|
|
checkTable = true
|
|
else
|
|
local tab = newList(("#"):split(param))
|
|
if #tab == 1 then
|
|
if param == arg1 then
|
|
good = true
|
|
elseif type(param) == "string" then
|
|
if param:match("^%$") then
|
|
good = true
|
|
checkKwargs = param:sub(2)
|
|
elseif param:match("^%[.*%]$") then
|
|
good = true
|
|
mustEvaluate = param
|
|
end
|
|
end
|
|
tab = del(tab)
|
|
else
|
|
good = true
|
|
multiArg = tab
|
|
end
|
|
end
|
|
if good then
|
|
if multiArg then
|
|
good = false
|
|
for i, v in ipairs(multiArg) do
|
|
local arg = select(i, ...)
|
|
if not arg then
|
|
good = false
|
|
elseif v == arg then
|
|
good = true
|
|
elseif tonumber(v) and type(arg) == "number" then
|
|
good = tonumber(v) == arg
|
|
elseif v:match("^%$") then
|
|
good = kwargs[v:sub(2)] == arg
|
|
elseif v:match("^%[.*%]$") then
|
|
good = evaluate(v, nsList, kwargs) == arg
|
|
else
|
|
good = false
|
|
end
|
|
if not good then
|
|
break
|
|
end
|
|
end
|
|
multiArg = del(multiArg)
|
|
elseif checkTable then
|
|
good = false
|
|
for k in pairs(param) do
|
|
if k == arg1 then
|
|
good = true
|
|
else
|
|
local multiArg = newList(("#"):split(k))
|
|
for i, v in ipairs(multiArg) do
|
|
local arg = select(i, ...)
|
|
if not arg then
|
|
good = false
|
|
elseif v == arg then
|
|
good = true
|
|
elseif tonumber(v) and type(arg) == "number" then
|
|
good = tonumber(v) == arg
|
|
elseif v:match("^%$") then
|
|
good = kwargs[v:sub(2)] == arg
|
|
elseif v:match("^%[.*%]$") then
|
|
good = evaluate(v, nsList, kwargs) == arg
|
|
else
|
|
good = false
|
|
end
|
|
if not good then
|
|
break
|
|
end
|
|
end
|
|
multiArg = del(multiArg)
|
|
end
|
|
if good then
|
|
break
|
|
end
|
|
end
|
|
elseif mustEvaluate then
|
|
good = evaluate(mustEvaluate, nsList, kwargs) == arg1
|
|
elseif checkKwargs then
|
|
good = kwargs[checkKwargs] == arg1
|
|
end
|
|
if good then
|
|
fsNeedUpdate[fs] = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
frame:SetScript("OnEvent", OnEvent)
|
|
|
|
local GetTime = _G.GetTime
|
|
--[[
|
|
-- I'm sorry, but this shit is just stupidly wasteful of CPU time.
|
|
local GetMilliseconds
|
|
if DogTag_DEBUG then
|
|
function GetMilliseconds()
|
|
return math.floor(GetTime() * 1000 + 0.5)
|
|
end
|
|
else
|
|
function GetMilliseconds()
|
|
return GetTime() * 1000
|
|
end
|
|
end]]
|
|
|
|
do -- OnUpdate
|
|
local nextTime = 0
|
|
local nextUpdateTime = 0
|
|
local nextSlowUpdateTime = 0
|
|
local nextCacheInvalidationTime = 0
|
|
local num = 0
|
|
|
|
local start -- start time of each cycle
|
|
|
|
-- Limit in milliseconds for each OnUpdate cycle.
|
|
-- Under max load, this will let FPS drop no lower than 33 FPS (1000/30)
|
|
-- as a result of the code in this function.
|
|
local CoroutineLimit = 30
|
|
|
|
-- Checks to see if we need to yield because the CoroutineLimit was exceeded.
|
|
-- Call this after every significant call
|
|
-- (especially external calls that will take an unknown amount of time)
|
|
-- You can also call this externally through DogTag.checkYield()
|
|
-- if you have an event handler that might take particularly long.
|
|
local function checkYield()
|
|
if running and InCombatLockdown() and debugprofilestop() - start > CoroutineLimit then
|
|
coroutine.yield()
|
|
end
|
|
end
|
|
DogTag.checkYield = checkYield
|
|
|
|
local GetMouseFocus = GetMouseFocus
|
|
if not GetMouseFocus and GetMouseFoci then
|
|
local GetMouseFoci = GetMouseFoci
|
|
GetMouseFocus = function()
|
|
return GetMouseFoci()[1]
|
|
end
|
|
end
|
|
|
|
local function OnUpdate_Coroutine()
|
|
while true do
|
|
running = true
|
|
|
|
_clearCodes()
|
|
num = num + 1
|
|
|
|
local currentTime = GetTime()
|
|
|
|
local oldMouseover = DogTag.__lastMouseover
|
|
local newMouseover = GetMouseFocus()
|
|
DogTag.__lastMouseover = newMouseover
|
|
if oldMouseover ~= DogTag.__lastMouseover then
|
|
for fs, frame in pairs(fsToFrame) do
|
|
if frame == oldMouseover or frame == newMouseover then
|
|
-- TODO: only update if has a mouseover event
|
|
fsNeedQuickUpdate[fs] = true
|
|
end
|
|
end
|
|
end
|
|
|
|
if currentTime >= nextTime then
|
|
DogTag:FireEvent("FastUpdate")
|
|
checkYield()
|
|
|
|
if currentTime >= nextUpdateTime then
|
|
nextUpdateTime = currentTime + 0.15
|
|
DogTag:FireEvent("Update")
|
|
checkYield()
|
|
end
|
|
|
|
if currentTime >= nextSlowUpdateTime then
|
|
nextSlowUpdateTime = currentTime + 10
|
|
DogTag:FireEvent("SlowUpdate")
|
|
checkYield()
|
|
end
|
|
|
|
if currentTime >= nextCacheInvalidationTime then
|
|
nextCacheInvalidationTime = currentTime + 15
|
|
|
|
-- The following code only happens out of combat,
|
|
-- so there is no need to check for yields here.
|
|
if not InCombatLockdown() then
|
|
local oldTime = currentTime - 180
|
|
for nsList, codeToFunction_nsList in pairs(codeToFunction) do
|
|
for kwargTypes, codeToFunction_nsList_kwargTypes in pairs(codeToFunction_nsList) do
|
|
if kwargTypes ~= 1 then
|
|
for code in pairs(codeToFunction_nsList_kwargTypes) do
|
|
if code ~= 1 and code ~= 2 then
|
|
local x = codeEvaluationTime[nsList][kwargTypes][code]
|
|
local good = false
|
|
if x and x > oldTime then
|
|
good = true
|
|
else
|
|
for fs, c in pairs(fsToCode) do
|
|
if c == code and fsToNSList[fs] == nsList then
|
|
good = true
|
|
break
|
|
end
|
|
end
|
|
if not good then
|
|
for uid, c in pairs(callbackToCode) do
|
|
if c == code and callbackToNSList[uid] == nsList then
|
|
good = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if not good then
|
|
codeToFunction_nsList_kwargTypes[code] = nil
|
|
codeToEventList[nsList][kwargTypes][code] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
nextTime = currentTime + 0.05
|
|
|
|
for i = 1, 9 do
|
|
for ns, data in pairs(TimerHandlers) do
|
|
local data_i = data[i]
|
|
if data_i then
|
|
for func in pairs(data_i) do
|
|
func(num, currentTime)
|
|
|
|
checkYield()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
for fs in pairs(fsNeedUpdate) do
|
|
fsNeedQuickUpdate[fs] = true
|
|
fsNeedUpdate[fs] = nil
|
|
end
|
|
end
|
|
|
|
-- debugprofilestop is used now instead of GetTime because
|
|
-- GetTime isn't updated until each frame is drawn. (recent change, WoW 4.3.0 i think?)
|
|
-- Since this whole process takes place within one frame,
|
|
-- GetTime will have the same value throughout.
|
|
-- debugprofilestop is always updated (unless some jerkface resets it with debugprofilestart), so we have to use it instead
|
|
|
|
-- Addendum to this explanation 8/1/2013 (r251):
|
|
-- This part of the code now yields out of the coroutine instead of breaking the loop
|
|
-- It still uses its own execution cap because the time spent updating font strings should be more tightly limited
|
|
-- By yielding out instead of breaking the loop,
|
|
-- we prevent the problem of only having the front strings that are in the top of the table get updated
|
|
-- while some sit in the bottom and never get seen.
|
|
|
|
|
|
-- Don't define this until we loop through at least one frame
|
|
-- so that we don't call debugprofilestop unless we need to:
|
|
local finish_time --= debugprofilestop() + 10
|
|
local n = 0
|
|
|
|
for fs in pairs(fsNeedQuickUpdate) do
|
|
n = n + 1
|
|
if not finish_time then
|
|
finish_time = debugprofilestop() + 10 -- 10 as in 10 miliseconds
|
|
|
|
elseif n%20 == 0 and debugprofilestop() >= finish_time then
|
|
|
|
-- Set finish_time to nil so that it will be recalculated when we resume.
|
|
finish_time = nil
|
|
|
|
coroutine.yield()
|
|
end
|
|
updateFontString(fs)
|
|
end
|
|
|
|
running = false
|
|
|
|
-- Since this function is one continual while(true) loop,
|
|
-- yield at the end each time and wait for the next resume
|
|
-- triggered by the script handler.
|
|
coroutine.yield()
|
|
|
|
end
|
|
end
|
|
|
|
local Coroutine
|
|
local function OnUpdate()
|
|
start = debugprofilestop()
|
|
|
|
-- Recreate the coroutine if it is dead (or create it if we haven't yet)
|
|
-- (It will die if there was an error thrown in the middle of the last cycle).
|
|
if not Coroutine or coroutine.status(Coroutine) == "dead" then
|
|
Coroutine = coroutine.create(OnUpdate_Coroutine)
|
|
end
|
|
|
|
assert(coroutine.resume(Coroutine))
|
|
end
|
|
|
|
frame:SetScript("OnUpdate", OnUpdate)
|
|
end
|
|
|
|
--[[
|
|
Notes:
|
|
Register a function to be called when the event is fired
|
|
This should only be called by sublibraries
|
|
Arguments:
|
|
string - the namespace to mark ownership with
|
|
string - the name of the event
|
|
function - the function to be called
|
|
Example:
|
|
LibStub("LibDogTag-3.0"):AddEventHandler("MyNamespace", "PLAYER_LOGIN", function(event, ...)
|
|
-- do something here.
|
|
end)
|
|
]]
|
|
function DogTag:AddEventHandler(namespace, event, func)
|
|
if type(namespace) ~= "string" then
|
|
error(("Bad argument #2 to `AddEventHandler'. Expected %q, got %q"):format("string", type(namespace)), 2)
|
|
end
|
|
if type(event) ~= "string" then
|
|
error(("Bad argument #3 to `AddEventHandler'. Expected %q, got %q"):format("string", type(event)), 2)
|
|
end
|
|
if type(func) ~= "function" then
|
|
error(("Bad argument #4 to `AddEventHandler'. Expected %q, got %q"):format("function", type(func)), 2)
|
|
end
|
|
if not EventHandlers[namespace] then
|
|
EventHandlers[namespace] = newList()
|
|
end
|
|
if not EventHandlers[namespace][event] then
|
|
EventHandlers[namespace][event] = newList()
|
|
end
|
|
EventHandlers[namespace][event][func] = true
|
|
DogTag.eventUsed(event)
|
|
end
|
|
|
|
--[[
|
|
Notes:
|
|
Remove an event handler that has been previously added
|
|
This should only be called by sublibraries
|
|
Arguments:
|
|
string - the namespace to mark ownership with
|
|
string - the name of the event
|
|
function - the function to be called
|
|
Example:
|
|
LibStub("LibDogTag-3.0"):RemoveEventHandler("MyNamespace", "PLAYER_LOGIN", func)
|
|
]]
|
|
function DogTag:RemoveEventHandler(namespace, event, func)
|
|
if type(namespace) ~= "string" then
|
|
error(("Bad argument #2 to `RemoveEventHandler'. Expected %q, got %q"):format("string", type(namespace)), 2)
|
|
end
|
|
if type(event) ~= "string" then
|
|
error(("Bad argument #3 to `RemoveEventHandler'. Expected %q, got %q"):format("string", type(event)), 2)
|
|
end
|
|
if type(func) ~= "function" then
|
|
error(("Bad argument #4 to `RemoveEventHandler'. Expected %q, got %q"):format("function", type(func)), 2)
|
|
end
|
|
local EventHandlers_namespace = EventHandlers[namespace]
|
|
if not EventHandlers_namespace then
|
|
return
|
|
end
|
|
local EventHandlers_namespace_event = EventHandlers_namespace[event]
|
|
if not EventHandlers_namespace_event then
|
|
return
|
|
end
|
|
EventHandlers_namespace_event[func] = nil
|
|
if not next(EventHandlers_namespace_event) then
|
|
EventHandlers_namespace[event] = del(EventHandlers_namespace_event)
|
|
end
|
|
if not next(EventHandlers_namespace) then
|
|
EventHandlers[namespace] = del(EventHandlers_namespace)
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Notes:
|
|
Fire an event that any tags, handlers, or callbacks will see.
|
|
Arguments:
|
|
string - name of the event
|
|
tuple - a tuple of arguments
|
|
Example:
|
|
LibStub("LibDogTag-3.0"):FireEvent("MyEvent", "Data", "goes", "here", 52)
|
|
]]
|
|
function DogTag:FireEvent(event, ...)
|
|
OnEvent(frame, event, ...)
|
|
end
|
|
|
|
--[[
|
|
Notes:
|
|
Register a function to be called roughly every 0.05 seconds
|
|
This should only be called by sublibraries
|
|
Arguments:
|
|
string - the namespace to mark ownership with
|
|
function - the function to be called
|
|
[optional] number - a number from 1 to 9 specifying the priority it will be called compared to other timers. 1 being called first and 9 being called last. Is 5 by default.
|
|
Example:
|
|
LibStub("LibDogTag-3.0"):AddTimerHandler("MyNamespace", function(num, currentTime)
|
|
-- do something here.
|
|
end)
|
|
]]
|
|
function DogTag:AddTimerHandler(namespace, func, priority)
|
|
if type(namespace) ~= "string" then
|
|
error(("Bad argument #2 to `AddTimerHandler'. Expected %q, got %q"):format("string", type(namespace)), 2)
|
|
end
|
|
if type(func) ~= "function" then
|
|
error(("Bad argument #3 to `AddTimerHandler'. Expected %q, got %q"):format("function", type(func)), 2)
|
|
end
|
|
if not priority then
|
|
priority = 5
|
|
elseif type(priority) ~= "number" then
|
|
error(("Bad argument #4 to `AddTimerHandler'. Expected %q, got %q"):format("number", type(priority)), 2)
|
|
elseif math.floor(priority) ~= priority then
|
|
error("Bad argument #4 to `AddTimerHandler'. Expected integer, got number", 2)
|
|
elseif priority < 1 or priority > 9 then
|
|
error(("Bad argument #4 to `AddTimerHandler'. Expected [1, 9], got %d"):format(priority), 2)
|
|
end
|
|
self:RemoveTimerHandler(namespace, func)
|
|
if not TimerHandlers[namespace] then
|
|
TimerHandlers[namespace] = newList()
|
|
end
|
|
if not TimerHandlers[namespace][priority] then
|
|
TimerHandlers[namespace][priority] = newList()
|
|
end
|
|
TimerHandlers[namespace][priority][func] = true
|
|
end
|
|
|
|
--[[
|
|
Notes:
|
|
Remove a timer handler that has previously been added
|
|
This should only be called by sublibraries
|
|
Arguments:
|
|
string - the namespace to mark ownership with
|
|
function - the function to be called
|
|
Example:
|
|
LibStub("LibDogTag-3.0"):RemoveTimerHandler("MyNamespace", func)
|
|
]]
|
|
function DogTag:RemoveTimerHandler(namespace, func)
|
|
if type(namespace) ~= "string" then
|
|
error(("Bad argument #2 to `RemoveTimerHandler'. Expected %q, got %q"):format("string", type(namespace)), 2)
|
|
end
|
|
if type(func) ~= "function" then
|
|
error(("Bad argument #3 to `RemoveTimerHandler'. Expected %q, got %q"):format("function", type(func)), 2)
|
|
end
|
|
if not TimerHandlers[namespace] then
|
|
return
|
|
end
|
|
for k, v in pairs(TimerHandlers[namespace]) do
|
|
v[func] = nil
|
|
if not next(v) then
|
|
TimerHandlers[namespace][k] = del(v)
|
|
end
|
|
end
|
|
if not next(TimerHandlers[namespace]) then
|
|
TimerHandlers[namespace] = del(TimerHandlers[namespace])
|
|
end
|
|
end
|
|
|
|
end
|