diff libs/AceHook-2.0/AceHook-2.0.lua @ 1:c11ca1d8ed91

Version 0.1
author Flick <flickerstreak@gmail.com>
date Tue, 20 Mar 2007 21:03:57 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/AceHook-2.0/AceHook-2.0.lua	Tue Mar 20 21:03:57 2007 +0000
@@ -0,0 +1,554 @@
+--[[
+Name: AceHook-2.0
+Revision: $Rev: 18708 $
+Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
+Inspired By: Ace 1.x by Turan (turan@gryphon.com)
+Website: http://www.wowace.com/
+Documentation: http://www.wowace.com/index.php/AceHook-2.0
+SVN: http://svn.wowace.com/root/trunk/Ace2/AceHook-2.0
+Description: Mixin to allow for safe hooking of functions, methods, and scripts.
+Dependencies: AceLibrary, AceOO-2.0
+]]
+
+local MAJOR_VERSION = "AceHook-2.0"
+local MINOR_VERSION = "$Revision: 18708 $"
+
+-- This ensures the code is only executed if the libary doesn't already exist, or is a newer version
+if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
+if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
+
+local lua51 = loadstring("return function(...) return ... end") and true or false
+
+if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
+
+--[[---------------------------------------------------------------------------------
+  Create the library object
+----------------------------------------------------------------------------------]]
+
+local AceOO = AceLibrary:GetInstance("AceOO-2.0")
+local AceHook = AceOO.Mixin {
+								"Hook",
+								"Unhook",
+								"UnhookAll",
+								"HookReport",
+								"IsHooked",
+								"HookScript",
+							}
+
+local table_setn = lua51 and function() end or table.setn
+
+if lua51 then
+	AceHook.__deprecated = MAJOR_VERSION .. " is deprecated in WoW 2.0"
+end
+
+--[[---------------------------------------------------------------------------------
+  Library Definitions
+----------------------------------------------------------------------------------]]
+
+local protFuncs = {
+	CameraOrSelectOrMoveStart = true, 	CameraOrSelectOrMoveStop = true,
+	TurnOrActionStart = true,			TurnOrActionStop = true,
+	PitchUpStart = true,				PitchUpStop = true,
+	PitchDownStart = true,				PitchDownStop = true,
+	MoveBackwardStart = true,			MoveBackwardStop = true,
+	MoveForwardStart = true,			MoveForwardStop = true,
+	Jump = true,						StrafeLeftStart = true,
+	StrafeLeftStop = true,				StrafeRightStart = true,
+	StrafeRightStop = true,				ToggleMouseMove = true,
+	ToggleRun = true,					TurnLeftStart = true,
+	TurnLeftStop = true,				TurnRightStart = true,
+	TurnRightStop = true,
+}
+
+local _G = getfenv(0)
+
+local handlers, funcs, scripts, actives
+
+--[[---------------------------------------------------------------------------------
+  Private definitions (Not exposed)
+----------------------------------------------------------------------------------]]
+
+--[[----------------------------------------------------------------------
+	_debug - Internal Method
+-------------------------------------------------------------------------]]		
+local function print(text)
+	DEFAULT_CHAT_FRAME:AddMessage(text)
+end
+
+local function _debug(self, msg)
+	local name = self.hooks.name
+	if name then
+		print(string.format("[%s]: %s", name, msg))
+	else
+		print(msg)
+	end		
+end
+
+local new, del
+do
+	local list = setmetatable({}, {__mode = "k"})
+	function new()
+		local t = next(list)
+		if not t then
+			return {}
+		end
+		list[t] = nil
+		return t
+	end
+	
+	function del(t)
+		setmetatable(t, nil)
+		table_setn(t, 0)
+		for k in pairs(t) do
+			t[k] = nil
+		end
+		list[t] = true
+	end
+end
+
+local origMetatable = {
+	__call = function(self, ...)
+		return self.orig(...)
+	end
+}
+
+--[[----------------------------------------------------------------------
+	AceHook:_getFunctionHook- internal method
+-------------------------------------------------------------------------]]		
+
+local function _getFunctionHook(self, func, handler, orig)
+	if type(handler) == "string" then
+		-- The handler is a method, need to self it
+		return function(...)
+			if actives[orig] then
+				return self[handler](self, ...)
+			else
+				return orig(...)
+			end
+		end
+	else
+		-- The handler is a function, just call it
+		return function(...)
+			if actives[orig] then
+				return handler(...)
+			else
+				return orig(...)
+			end
+		end
+	end
+end
+
+--[[----------------------------------------------------------------------
+	AceHook:_getMethodHook - Internal Method
+-------------------------------------------------------------------------]]		
+local function _getMethodHook(self, object, method, handler, orig, script)
+	if type(handler) == "string" then
+		-- The handler is a method, need to self it
+		if script then
+			return function()
+				if actives[orig] then
+					return self[handler](self, object)
+				else
+					return orig()
+				end
+			end
+		else
+			return function(obj,...)
+				if actives[orig] then
+					return self[handler](self, obj, ...)
+				else
+					return orig(obj, ...)
+				end
+			end
+		end
+	else
+		-- The handler is a function, just call it
+		if script then
+			return function()
+				if actives[orig] then
+					return handler(object)
+				else
+					return orig()
+				end
+			end
+		else
+			return function(obj, ...)
+				if actives[orig] then
+					return handler(obj, ...)
+				else
+					return orig(obj, ...)
+				end
+			end	
+		end
+	end
+end
+
+--[[----------------------------------------------------------------------
+	AceHook:HookFunc - internal method.
+	o You can only hook each function once from each source.
+	o If there is an inactive hook for this func/handler pair, we reactivate it
+	o If there is an inactive hook for another handler, we error out.
+	o Looks for handler as a method of the calling class, error if not available
+	o If handler is a function, it just uses it directly through the wrapper
+-------------------------------------------------------------------------]]		
+local function _hookFunc(self, func, handler)						
+	local orig = _G[func]
+	
+	if not orig or type(orig) ~= "function" then
+		_debug(self, string.format("Attempt to hook a non-existant function %q", func),3)
+		return
+	end
+	
+	if not handler then handler = func end
+
+	if self.hooks[func] then
+		local orig = self.hooks[func].orig
+		-- We have an active hook from this source.  Don't multi-hook
+		if actives[orig] then
+			_debug(self, string.format("%q already has an active hook from this source.", func))
+			return
+		end
+		-- The hook is inactive, so reactivate it
+		if handlers[orig] == handler then
+			actives[orig] = true
+			return
+		else
+			AceHook:error("There is a stale hook for %q can't hook or reactivate.", func)
+		end
+	end	
+	
+	if type(handler) == "string" then
+		if type(self[handler]) ~= "function" then
+			AceHook:error("Could not find the the handler %q when hooking function %q", handler, func)
+		end
+	elseif type(handler) ~= "function" then
+		AceHook:error("Could not find the handler you supplied when hooking %q", func)
+	end
+	
+	local t = setmetatable(new(), origMetatable)
+	self.hooks[func] = t
+	t.orig = orig
+	
+	actives[orig] = true
+	handlers[orig] = handler
+	local newFunc = _getFunctionHook(self, func, handler, orig)
+	funcs[orig] = newFunc
+	
+	_G[func] = newFunc
+end
+
+--[[----------------------------------------------------------------------
+	AceHook:UnhookFunc - internal method
+	o If you attempt to unhook a function that has never been hooked, or to unhook in a
+	  system that has never had a hook before, the system will error with a stack trace
+	o If we own the global function, then put the original back in its place and remove
+	  all references to the Hooks[func] structure.
+	o If we don't own the global function (we've been hooked) we deactivate the hook,
+	  forcing the handler to passthrough.
+-------------------------------------------------------------------------]]		
+local function _unhookFunc(self, func)
+	if not self.hooks[func] or not funcs[self.hooks[func].orig] then
+		_debug(self, string.format("Tried to unhook %q which is not currently hooked.", func))
+		return
+	end
+	
+	local orig = self.hooks[func].orig
+	
+	if actives[orig] then
+		-- See if we own the global function
+		if _G[func] == funcs[orig] then
+			_G[func] = orig
+			self.hooks[func] = del(self.hooks[func])
+			handlers[orig] = nil
+			funcs[orig] = nil
+			scripts[orig] = nil
+			actives[orig] = nil
+			-- Magically all-done
+		else
+			actives[orig] = nil
+		end
+	end
+end
+
+--[[----------------------------------------------------------------------
+	AceHook:HookMeth - Takes an optional fourth argument
+	o script - Signifies whether this is a script hook or not
+-------------------------------------------------------------------------]]		
+
+local function _hookMeth(self, obj, method, handler, script)
+	if not handler then handler = method end
+	if (not obj or type(obj) ~= "table") then
+		AceHook:error("The object you supplied could not be found, or isn't a table.")
+	end
+	
+	if self.hooks[obj] and self.hooks[obj][method] then
+		local orig = self.hooks[obj][method].orig
+		-- We have an active hook from this source.  Don't multi-hook
+		if actives[orig] then
+			_debug(self, string.format("%q already has an active hook from this source.", method))
+			return
+		end
+		-- The hook is inactive, so reactivate it.
+		if handlers[orig] == handler then
+			actives[orig] = true
+			return
+		else
+			AceHook:error("There is a stale hook for %q can't hook or reactivate.", method)
+		end
+	end
+	-- We're clear to try the hook, let's make some checks first
+	if type(handler) == "string" then
+		if type(self[handler]) ~= "function" then
+			AceHook:error("Could not find the handler %q you supplied when hooking method %q", handler, method)
+		end
+	elseif type(handler) ~= "function" then
+		AceHook:error("Could not find the handler you supplied when hooking method %q", method)
+	end
+	-- Handler has been found, so now try to find the method we're trying to hook	
+	local orig
+	-- Script
+	if script then
+		if not obj.GetScript then
+			AceHook:error("The object you supplied does not have a GetScript method.")
+		end
+		if not obj:HasScript(method) then
+			AceHook:error("The object you supplied doesn't allow the %q method.", method)
+		end
+		-- Sometimes there is not a original function for a script.
+		orig = obj:GetScript(method)
+		if not orig then
+			orig = function() end
+		end
+	-- Method
+	else
+		orig = obj[method]
+	end
+	if not orig then
+		AceHook:error("Could not find the method or script %q you are trying to hook.", method)
+	end
+	if not self.hooks[obj] then
+		self.hooks[obj] = new()
+	end
+	local t = setmetatable(new(), origMetatable)
+	self.hooks[obj][method] = t
+	t.orig = orig
+	
+	actives[orig] = true
+	handlers[orig] = handler
+	scripts[orig] = script and true or nil
+	local newFunc = _getMethodHook(self, obj, method, handler, orig, script)
+	funcs[orig] = newFunc
+	
+	if script then
+		obj:SetScript(method, newFunc)
+	else
+		obj[method] = newFunc
+	end
+end	
+
+--[[----------------------------------------------------------------------
+	AceHook:UnhookMeth - Internal method
+	o If you attempt to unhook a method that has never been hooked, or to unhook in a
+	  system that has never had a hook before, the system will error with a stack trace
+	o If we own the global method, then put the original back in its place and remove
+	  all references to the Hooks[obj][method] structure.
+	o If we don't own the global method (we've been hooked) we deactivate the hook,
+	  forcing the handler to passthrough.
+-------------------------------------------------------------------------]]		
+local function _unhookMeth(self, obj, method)
+	if not self.hooks[obj] or not self.hooks[obj][method] or not funcs[self.hooks[obj][method].orig] then
+		_debug(self, string.format("Attempt to unhook a method %q that is not currently hooked.", method))
+		return
+	end
+	
+	local orig = self.hooks[obj][method].orig
+	
+	if actives[orig] then
+		-- If this is a script
+		if scripts[orig] then
+			if obj:GetScript(method) == funcs[orig] then
+				-- We own the script.  Kill it.
+				obj:SetScript(method, orig)
+				self.hooks[obj][method] = del(self.hooks[obj][method])
+				handlers[orig] = nil
+				funcs[orig] = nil
+				scripts[orig] = nil
+				actives[orig] = nil
+			else
+				actives[orig] = nil
+			end
+		else
+			if obj[method] == funcs[orig] then
+				-- We own the method.  Kill it.
+				obj[method] = orig
+				self.hooks[obj][method] = del(self.hooks[obj][method])
+				handlers[orig] = nil
+				funcs[orig] = nil
+				scripts[orig] = nil
+				actives[orig] = nil
+			else
+				actives[orig] = nil
+			end
+		end
+	end
+	if not next(self.hooks[obj]) then
+		-- Spank the table
+		self.hooks[obj] = del(self.hooks[obj])
+	end
+end
+
+function AceHook:OnInstanceInit(object)
+	if not object.hooks then
+		object.hooks = new()
+	end
+	
+	local name
+	
+	if type(rawget(object, 'GetLibraryVersion')) == "function" then
+		name = object:GetLibraryVersion()
+	end
+	if not name and type(object.GetName) == "function" then
+		name = object:GetName()
+	end
+	if not name and type(object.name) == "string" then
+		name = object.name
+	end
+	if not name then
+		for k,v in pairs(_G) do
+			if v == object then
+				name = tostring(k)
+				break
+			end
+		end
+	end
+	
+	object.hooks.name = name
+end
+
+AceHook.OnManualEmbed = AceHook.OnInstanceInit
+
+--[[----------------------------------------------------------------------
+	AceHook:Hook
+		self:Hook("functionName", ["handlerName" | handler])
+		self:Hook(ObjectName, "Method", ["Handler" | handler])
+-------------------------------------------------------------------------]]		
+function AceHook:Hook(arg1, arg2, arg3)
+	if type(arg1)== "string" then
+		if protFuncs[arg1] then
+ 			if self.hooks.name then
+				AceHook:error("%s tried to hook %q, which is a Blizzard protected function.", self.hooks.name, arg1)
+			else
+				_debug(self, string.format("An Addon tried to hook %q, which is a Blizzard protected function.", arg1))
+			end
+		else
+			_hookFunc(self, arg1, arg2)
+		end
+	else
+		_hookMeth(self, arg1, arg2, arg3)
+	end
+end
+
+function AceHook:HookScript(arg1, arg2, arg3)
+	_hookMeth(self, arg1, arg2, arg3, true)
+end
+
+--[[----------------------------------------------------------------------
+	AceHook:IsHooked()
+		self:Hook("functionName")
+		self:Hook(ObjectName, "Method")
+		
+		Returns whether or not the given function is hooked in the current	
+		namespace.  A hooked, but inactive function is considered NOT
+		hooked in this context.
+-------------------------------------------------------------------------]]		
+function AceHook:IsHooked(obj, method)
+	if method and obj then
+		if self.hooks and self.hooks[obj] and self.hooks[obj][method] and actives[self.hooks[obj][method].orig] then
+			return true, handlers[self.hooks[obj][method].orig]
+		end
+	else
+		if self.hooks and self.hooks[obj] and actives[self.hooks[obj].orig] then
+			return true, handlers[self.hooks[obj].orig]
+		end
+	end
+	
+	return false, nil
+end
+
+--[[----------------------------------------------------------------------
+	AceHook:Unhook
+		self:Unhook("functionName")
+		self:Unhook(ObjectName, "Method")
+-------------------------------------------------------------------------]]		
+function AceHook:Unhook(arg1, arg2)
+	if type(arg1) == "string" then
+		_unhookFunc(self, arg1)
+	else
+		_unhookMeth(self, arg1, arg2)
+	end
+end			
+
+--[[----------------------------------------------------------------------
+	AceHook:UnhookAll - Unhooks all active hooks from the calling source
+-------------------------------------------------------------------------]]		
+function AceHook:UnhookAll()
+	for key, value in pairs(self.hooks) do
+		if type(key) == "table" then
+			for method in pairs(value) do
+				self:Unhook(key, method)
+			end
+		else
+			self:Unhook(key)
+		end
+	end
+end
+
+
+function AceHook:OnEmbedDisable(target)
+	self.UnhookAll(target)
+end
+	
+--[[----------------------------------------------------------------------
+	AceHook:HookReport - Lists registered hooks from this source
+-------------------------------------------------------------------------]]		
+
+function AceHook:HookReport()
+	_debug(self, "This is a list of all active hooks for this object:")
+	if not self.hooks then _debug(self, "No registered hooks.") return end
+
+	for key, value in pairs(self.hooks) do
+		if type(value) == "table" then
+			for method in pairs(value) do
+				_debug(self, string.format("key: %s method: %q |cff%s|r", tostring(key), method, self.hooks[key][method].active and "00ff00Active" or "ffff00Inactive"))
+			end
+		else
+			_debug(self, string.format("key: %s value: %q |cff%s|r", tostring(key), tostring(value), self.hooks[key].active and "00ff00Active" or "ffff00Inactive"))
+		end
+	end
+end
+
+--[[---------------------------------------------------------------------------------
+  Stub and Library registration
+----------------------------------------------------------------------------------]]
+
+local function activate(self, oldLib, oldDeactivate)
+	AceHook = self
+	
+	self.handlers = oldLib and oldLib.handlers or {}
+	self.funcs = oldLib and oldLib.funcs or {}
+	self.scripts = oldLib and oldLib.scripts or {}
+	self.actives = oldLib and oldLib.actives or {}
+	
+	handlers = self.handlers
+	funcs = self.funcs
+	scripts = self.scripts
+	actives = self.actives
+	
+	self:activate(oldLib, oldDeactivate)
+	
+	if oldDeactivate then
+		oldDeactivate(oldLib)
+	end
+end
+
+AceLibrary:Register(AceHook, MAJOR_VERSION, MINOR_VERSION, activate)