Permalink
Browse files

And so it begins...

  • Loading branch information...
0 parents commit 724f63445421f2b801aac6f2f26c34f48c752ed8 @tekkub tekkub committed May 16, 2008
Showing with 541 additions and 0 deletions.
  1. +239 −0 CallbackHandler-1.0.lua
  2. +111 −0 FPSBlock.lua
  3. +21 −0 FPSBlock.toc
  4. +50 −0 LibDataBroker-1.1/LibDataBroker-1.1.lua
  5. +30 −0 LibStub.lua
  6. +90 −0 tekBlock/tekBlock.lua
@@ -0,0 +1,239 @@
+--[[ $Id: CallbackHandler-1.0.lua 60548 2008-02-07 11:04:06Z nevcairiel $ ]]
+local MAJOR, MINOR = "CallbackHandler-1.0", 3
+local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not CallbackHandler then return end -- No upgrade needed
+
+local meta = {__index = function(tbl, key) tbl[key] = {} return tbl[key] end}
+
+local type = type
+local pcall = pcall
+local pairs = pairs
+local assert = assert
+local concat = table.concat
+local loadstring = loadstring
+local next = next
+local select = select
+local type = type
+local xpcall = xpcall
+
+local function errorhandler(err)
+ return geterrorhandler()(err)
+end
+
+local function CreateDispatcher(argCount)
+ local code = [[
+ local next, xpcall, eh = ...
+
+ local method, ARGS
+ local function call() method(ARGS) end
+
+ local function dispatch(handlers, ...)
+ local index
+ index, method = next(handlers)
+ if not method then return end
+ local OLD_ARGS = ARGS
+ ARGS = ...
+ repeat
+ xpcall(call, eh)
+ index, method = next(handlers, index)
+ until not method
+ ARGS = OLD_ARGS
+ end
+
+ return dispatch
+ ]]
+
+ local ARGS, OLD_ARGS = {}, {}
+ for i = 1, argCount do ARGS[i], OLD_ARGS[i] = "arg"..i, "old_arg"..i end
+ code = code:gsub("OLD_ARGS", concat(OLD_ARGS, ", ")):gsub("ARGS", concat(ARGS, ", "))
+ return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(next, xpcall, errorhandler)
+end
+
+local Dispatchers = setmetatable({}, {__index=function(self, argCount)
+ local dispatcher = CreateDispatcher(argCount)
+ rawset(self, argCount, dispatcher)
+ return dispatcher
+end})
+
+--------------------------------------------------------------------------
+-- CallbackHandler:New
+--
+-- target - target object to embed public APIs in
+-- RegisterName - name of the callback registration API, default "RegisterCallback"
+-- UnregisterName - name of the callback unregistration API, default "UnregisterCallback"
+-- UnregisterAllName - name of the API to unregister all callbacks, default "UnregisterAllCallbacks". false == don't publish this API.
+
+function CallbackHandler:New(target, RegisterName, UnregisterName, UnregisterAllName, OnUsed, OnUnused)
+ -- TODO: Remove this after beta has gone out
+ assert(not OnUsed and not OnUnused, "ACE-80: OnUsed/OnUnused are deprecated. Callbacks are now done to registry.OnUsed and registry.OnUnused")
+
+ RegisterName = RegisterName or "RegisterCallback"
+ UnregisterName = UnregisterName or "UnregisterCallback"
+ if UnregisterAllName==nil then -- false is used to indicate "don't want this method"
+ UnregisterAllName = "UnregisterAllCallbacks"
+ end
+
+ -- we declare all objects and exported APIs inside this closure to quickly gain access
+ -- to e.g. function names, the "target" parameter, etc
+
+
+ -- Create the registry object
+ local events = setmetatable({}, meta)
+ local registry = { recurse=0, events=events }
+
+ -- registry:Fire() - fires the given event/message into the registry
+ function registry:Fire(eventname, ...)
+ if not rawget(events, eventname) or not next(events[eventname]) then return end
+ local oldrecurse = registry.recurse
+ registry.recurse = oldrecurse + 1
+
+ Dispatchers[select('#', ...) + 1](events[eventname], eventname, ...)
+
+ registry.recurse = oldrecurse
+
+ if registry.insertQueue and oldrecurse==0 then
+ -- Something in one of our callbacks wanted to register more callbacks; they got queued
+ for eventname,callbacks in pairs(registry.insertQueue) do
+ local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
+ for self,func in pairs(callbacks) do
+ events[eventname][self] = func
+ -- fire OnUsed callback?
+ if first and registry.OnUsed then
+ registry.OnUsed(registry, target, eventname)
+ first = nil
+ end
+ end
+ end
+ registry.insertQueue = nil
+ end
+ end
+
+ -- Registration of a callback, handles:
+ -- self["method"], leads to self["method"](self, ...)
+ -- self with function ref, leads to functionref(...)
+ -- "addonId" (instead of self) with function ref, leads to functionref(...)
+ -- all with an optional arg, which, if present, gets passed as first argument (after self if present)
+ target[RegisterName] = function(self, eventname, method, ... --[[actually just a single arg]])
+ if type(eventname) ~= "string" then
+ error("Usage: "..RegisterName.."(eventname, method[, arg]): 'eventname' - string expected.", 2)
+ end
+
+ method = method or eventname
+
+ local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
+
+ if type(method) ~= "string" and type(method) ~= "function" then
+ error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - string or function expected.", 2)
+ end
+
+ local regfunc
+
+ if type(method) == "string" then
+ -- self["method"] calling style
+ if type(self) ~= "table" then
+ error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): self was not a table?", 2)
+ elseif self==target then
+ error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): do not use Library:"..RegisterName.."(), use your own 'self'", 2)
+ elseif type(self[method]) ~= "function" then
+ error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - method '"..tostring(method).."' not found on self.", 2)
+ end
+
+ if select("#",...)>=1 then -- this is not the same as testing for arg==nil!
+ local arg=select(1,...)
+ regfunc = function(...) self[method](self,arg,...) end
+ else
+ regfunc = function(...) self[method](self,...) end
+ end
+ else
+ -- function ref with self=object or self="addonId"
+ if type(self)~="table" and type(self)~="string" then
+ error("Usage: "..RegisterName.."(self or \"addonId\", eventname, method): 'self or addonId': table or string expected.", 2)
+ end
+
+ if select("#",...)>=1 then -- this is not the same as testing for arg==nil!
+ local arg=select(1,...)
+ regfunc = function(...) method(arg,...) end
+ else
+ regfunc = method
+ end
+ end
+
+
+ if events[eventname][self] or registry.recurse<1 then
+ -- if registry.recurse<1 then
+ -- we're overwriting an existing entry, or not currently recursing. just set it.
+ events[eventname][self] = regfunc
+ -- fire OnUsed callback?
+ if registry.OnUsed and first then
+ registry.OnUsed(registry, target, eventname)
+ end
+ else
+ -- we're currently processing a callback in this registry, so delay the registration of this new entry!
+ -- yes, we're a bit wasteful on garbage, but this is a fringe case, so we're picking low implementation overhead over garbage efficiency
+ registry.insertQueue = registry.insertQueue or setmetatable({},meta)
+ registry.insertQueue[eventname][self] = regfunc
+ end
+ end
+
+ -- Unregister a callback
+ target[UnregisterName] = function(self, eventname)
+ if not self or self==target then
+ error("Usage: "..UnregisterName.."(eventname): bad 'self'", 2)
+ end
+ if type(eventname) ~= "string" then
+ error("Usage: "..UnregisterName.."(eventname): 'eventname' - string expected.", 2)
+ end
+ if rawget(events, eventname) and events[eventname][self] then
+ events[eventname][self] = nil
+ -- Fire OnUnused callback?
+ if registry.OnUnused and not next(events[eventname]) then
+ registry.OnUnused(registry, target, eventname)
+ end
+ end
+ if registry.insertQueue and rawget(registry.insertQueue, eventname) and registry.insertQueue[eventname][self] then
+ registry.insertQueue[eventname][self] = nil
+ end
+ end
+
+ -- OPTIONAL: Unregister all callbacks for given selfs/addonIds
+ if UnregisterAllName then
+ target[UnregisterAllName] = function(...)
+ if select("#",...)<1 then
+ error("Usage: "..UnregisterAllName.."([whatFor]): missing 'self' or \"addonId\" to unregister events for.", 2)
+ end
+ if select("#",...)==1 and ...==target then
+ error("Usage: "..UnregisterAllName.."([whatFor]): supply a meaningful 'self' or \"addonId\"", 2)
+ end
+
+
+ for i=1,select("#",...) do
+ local self = select(i,...)
+ if registry.insertQueue then
+ for eventname, callbacks in pairs(registry.insertQueue) do
+ if callbacks[self] then
+ callbacks[self] = nil
+ end
+ end
+ end
+ for eventname, callbacks in pairs(events) do
+ if callbacks[self] then
+ callbacks[self] = nil
+ -- Fire OnUnused callback?
+ if registry.OnUnused and not next(callbacks) then
+ registry.OnUnused(registry, target, eventname)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ return registry
+end
+
+
+-- CallbackHandler purposefully does NOT do explicit embedding. Nor does it
+-- try to upgrade old implicit embeds since the system is selfcontained and
+-- relies on closures to work.
+
@@ -0,0 +1,111 @@
+
+------------------------------
+-- Are you local? --
+------------------------------
+
+local UPDATEPERIOD = 0.5
+local prevmem, elapsed, tipshown = collectgarbage("count"), 0.5
+local string_format, math_modf, GetNetStats, GetFramerate, collectgarbage = string.format, math.modf, GetNetStats, GetFramerate, collectgarbage
+
+
+local function ColorGradient(perc, r1, g1, b1, r2, g2, b2, r3, g3, b3)
+ if perc >= 1 then return r3, g3, b3 elseif perc <= 0 then return r1, g1, b1 end
+
+ local segment, relperc = math_modf(perc*2)
+ if segment == 1 then r1, g1, b1, r2, g2, b2 = r2, g2, b2, r3, g3, b3 end
+ return r1 + (r2-r1)*relperc, g1 + (g2-g1)*relperc, b1 + (b2-b1)*relperc
+end
+
+
+-------------------------------------------
+-- Namespace and all that shit --
+-------------------------------------------
+
+local f = CreateFrame("frame")
+f:SetScript("OnEvent", function(self, event, ...) if self[event] then return self[event](self, event, ...) end end)
+f:RegisterEvent("ADDON_LOADED")
+
+local dataobj = LibStub:GetLibrary("LibDataBroker-1.1"):NewDataObject("FPSBlock")
+dataobj.text = "75.0 FPS"
+
+
+---------------------------
+-- Init/Enable --
+---------------------------
+
+function f:ADDON_LOADED(event, addon)
+ if addon ~= "FPSBlock" then return end
+
+ if FPSBlockDB and FPSBlockDB.profiles then FPSBlockDB = nil end
+ FPSBlockDB = FPSBlockDB or {}
+
+ block = LibStub:GetLibrary("tekBlock"):new("FPSBlock", FPSBlockDB)
+
+ f:UnregisterEvent("ADDON_LOADED")
+ f.ADDON_LOADED = nil
+end
+
+
+--------------------------------
+-- OnUpdate Handler --
+--------------------------------
+
+f:SetScript("OnUpdate", function(self, elap)
+ elapsed = elapsed + elap
+ if elapsed < UPDATEPERIOD then return end
+
+ elapsed = 0
+ local fps = GetFramerate()
+ local r, g, b = ColorGradient(fps/75, 1,0,0, 1,1,0, 0,1,0)
+ dataobj.text = string_format("|cff%02x%02x%02x%.1f|r FPS", r*255, g*255, b*255, fps)
+
+ if tipshown then dataobj.OnEnter(tipshown) end
+end)
+
+
+------------------------
+-- Tooltip! --
+------------------------
+
+local function GetTipAnchor(frame)
+ local x,y = frame:GetCenter()
+ if not x or not y then return "TOPLEFT", "BOTTOMLEFT" end
+ local hhalf = (x > UIParent:GetWidth()*2/3) and "RIGHT" or (x < UIParent:GetWidth()/3) and "LEFT" or ""
+ local vhalf = (y > UIParent:GetHeight()/2) and "TOP" or "BOTTOM"
+ return vhalf..hhalf, frame, (vhalf == "TOP" and "BOTTOM" or "TOP")..hhalf
+end
+
+
+function dataobj.OnLeave()
+ GameTooltip:Hide()
+ tipshown = nil
+end
+
+
+function dataobj.OnEnter(self)
+ tipshown = self
+ GameTooltip:SetOwner(self, "ANCHOR_NONE")
+ GameTooltip:SetPoint(GetTipAnchor(self))
+ GameTooltip:ClearLines()
+
+ GameTooltip:AddLine("FPSBlock")
+
+ local fps = GetFramerate()
+ local r, g, b = ColorGradient(fps/75, 1,0,0, 1,1,0, 0,1,0)
+ GameTooltip:AddDoubleLine("FPS:", string_format("%.1f", fps), nil,nil,nil, r,g,b)
+
+ local _, _, lag = GetNetStats()
+ local r, g, b = ColorGradient(lag/1000, 1,0,0, 1,1,0, 0,1,0)
+ GameTooltip:AddDoubleLine("Lag:", lag.. " ms", nil,nil,nil, r,g,b)
+
+ local mem = collectgarbage("count")
+ local deltamem = mem - prevmem
+ prevmem = mem
+ local r, g, b = ColorGradient(mem/(60*1024), 0,1,0, 1,1,0, 1,0,0)
+ GameTooltip:AddDoubleLine("Memory:", string_format("%.2f MiB", mem/1024), nil,nil,nil, r,g,b)
+
+ local r, g, b = ColorGradient(deltamem/15, 0,1,0, 1,1,0, 1,0,0)
+ GameTooltip:AddDoubleLine("Garbage churn:", string_format("%.2f KiB/sec", deltamem), nil,nil,nil, r,g,b)
+
+ GameTooltip:Show()
+end
@@ -0,0 +1,21 @@
+## Interface: 20400
+
+## Title: FPSBlock
+## Notes: Minimalistic FPS display
+## Author: Tekkub Stoutwrithe
+## Version: Alpha
+## X-Website: http://www.tekkub.net/
+## X-Email: tekkub-wow@googlegroups.com
+## X-Credits: kergoth's PerfBlock
+
+## SavedVariables: FPSBlockDB
+
+## LoadManagers: AddonLoader
+## X-LoadOn-Always: delayed
+
+LibStub.lua
+CallbackHandler-1.0.lua
+LibDataBroker-1.1\LibDataBroker-1.1.lua
+tekBlock\tekBlock.lua
+
+FPSBlock.lua
Oops, something went wrong.

0 comments on commit 724f634

Please sign in to comment.