vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[---------------------------------------------------------------------------------
2 AceHook Library by Cladhaire (cladhaire@gmail.com)
3  
4 This first section is the standard embedded library stub declaration. This code
5 manages the versioning between different instances of the AceHooks library.
6  
7 See http://www.iriel.org/wow/addondev/embedlibrary1.html for more information
8 ----------------------------------------------------------------------------------]]
9  
10 local protFuncs = {
11 CameraOrSelectOrMoveStart = true, CameraOrSelectOrMoveStop = true,
12 TurnOrActionStart = true, TurnOrActionStop = true,
13 PitchUpStart = true, PitchUpStop = true,
14 PitchDownStart = true, PitchDownStop = true,
15 MoveBackwardStart = true, MoveBackwardStop = true,
16 MoveForwardStart = true, MoveForwardStop = true,
17 Jump = true, StrafeLeftStart = true,
18 StrafeLeftStop = true, StrafeRightStart = true,
19 StrafeRightStop = true, ToggleMouseMove = true,
20 ToggleRun = true, TurnLeftStart = true,
21 TurnLeftStop = true, TurnRightStart = true,
22 TurnRightStop = true,
23 }
24  
25 --[[---------------------------------------------------------------------------------
26 AceHooks Library implementation.
27 -----------------------------------------------------------------------------------]]
28  
29 local MAJOR_VERSION = "1.4"
30 local MINOR_VERSION = 2006042401
31 local AceHook = {}
32  
33 -- This is a compatability change for AceHook 1.3 upgrade
34 if AceHookLib and not AceHookLib.NewVersion then
35 function AceHookLib:NewVersion(version, minor)
36 local versionData = self.versions[version]
37 if not versionData or versionData.minor < minor then return true end
38 end
39 end
40  
41 -- Only declare this class if necessary
42 if not AceHookLib or AceHookLib:NewVersion(MAJOR_VERSION, MINOR_VERSION) then
43  
44 function AceHook:GetLibraryVersion()
45 return MAJOR_VERSION, MINOR_VERSION
46 end
47  
48 function AceHook:LibActivate(stub, oldLib, oldList)
49 local maj, min = self:GetLibraryVersion()
50  
51 if oldLib then
52 for i,namespace in pairs(oldLib.embedList or {}) do
53 self:Embed(namespace)
54 end
55 end
56  
57 -- Return nil to force the instance copy.
58 return nil
59 end
60  
61 function AceHook:HookDebug(msg)
62 local maj,min = MAJOR_VERSION, MINOR_VERSION
63 if DEFAULT_CHAT_FRAME then
64 DEFAULT_CHAT_FRAME:AddMessage("[|cffffff00AceHook "..maj.."|r] " .. tostring(msg))
65 else
66 print("[AceHook "..maj.."] " .. tostring(msg))
67 end
68 end
69  
70 --[[----------------------------------------------------------------------
71 AceHook:Hook
72 self:Hook("functionName", ["handlerName" | handler])
73 self:Hook(ObjectName, "Method", ["Handler" | handler])
74 -------------------------------------------------------------------------]]
75 function AceHook:Hook(arg1, arg2, arg3)
76 if not self.Hooks then self.Hooks = {} end
77 if type(arg1)== "string" then
78 if protFuncs[arg1] then
79 local name = self.name
80  
81 if not name then
82 local g = getfenv(0)
83  
84 for k,v in pairs(g) do
85 if v == self then
86 name = k
87 break
88 end
89 end
90 end
91  
92 if name then
93 error(tostring(name) .. " tried to hook " .. arg1 .. ", which is a Blizzard protected function.",3)
94 else
95 self:HookDebug("An Addon tried to hook " .. arg1 .. ", which is a Blizzard protected function.")
96 end
97 else
98 self:HookFunc(arg1, arg2)
99 end
100 else
101 self:HookMeth(arg1, arg2, arg3)
102 end
103 end
104  
105 function AceHook:HookScript(arg1, arg2, arg3)
106 if not self.Hooks then self.Hooks = {} end
107  
108 self:HookMeth(arg1, arg2, arg3, true)
109 end
110  
111 --[[----------------------------------------------------------------------
112 AceHook:IsHooked()
113 self:Hook("functionName")
114 self:Hook(ObjectName, "Method")
115  
116 Returns whether or not the given function is hooked in the current
117 namespace. A hooked, but inactive function is considered NOT
118 hooked in this context.
119 -------------------------------------------------------------------------]]
120 function AceHook:IsHooked(obj, method)
121 if method and obj then
122 if self.Hooks and self.Hooks[obj] and self.Hooks[obj][method] and self.Hooks[obj][method].active then
123 return true
124 end
125 else
126 if self.Hooks and self.Hooks[obj] and self.Hooks[obj].active then
127 return true
128 end
129 end
130  
131 return false
132 end
133  
134 --[[----------------------------------------------------------------------
135 AceHook:Unhook
136 self:Unhook("functionName")
137 self:Unhook(ObjectName, "Method")
138 -------------------------------------------------------------------------]]
139 function AceHook:Unhook(arg1, arg2)
140 if type(arg1) == "string" then
141 self:UnhookFunc(arg1)
142 else
143 self:UnhookMeth(arg1, arg2)
144 end
145 end
146  
147 --[[----------------------------------------------------------------------
148 AceHook:UnhookAll - Unhooks all active hooks from the calling source
149 -------------------------------------------------------------------------]]
150 function AceHook:UnhookAll(script)
151 if not self.Hooks then return end
152 for key, value in pairs(self.Hooks) do
153 if type(key) == "table" then
154 for method in pairs(value) do
155 if (self.Hooks[key][method].script == script) then
156 self:Unhook(key, method)
157 end
158 end
159 else
160 self:Unhook(key)
161 end
162 end
163 end
164  
165 function AceHook:UnhookAllScripts()
166 self:UnhookAll(true)
167 end
168  
169 --[[----------------------------------------------------------------------
170 AceHook:Print - Utility function for HookReport, for embedding
171 AceHook:HookReport - Lists registered hooks from this source
172 -------------------------------------------------------------------------]]
173  
174 function AceHook:HookReport()
175 self:HookDebug("This is a list of all active hooks for this object:")
176 if not self.Hooks then self:HookDebug("No registered hooks.") return end
177  
178 for key, value in pairs(self.Hooks) do
179 if type(key) == "table" then
180 for method in pairs(value) do
181 self:HookDebug("key: "..tostring(key).." method: "..tostring(method)..((self.Hooks[key][method].active and "|cff00ff00 Active" or "|cffffff00 Inactive")))
182 end
183 else
184 self:HookDebug("key: " .. tostring(key) .. " value: " .. tostring(value) .. ((self.Hooks[key].active and "|cff00ff00 Active" or "|cffffff00 Inactive")))
185 end
186 end
187 end
188  
189 --[[----------------------------------------------------------------------
190 AceHook:CallHook("functionName" [, arg1, arg2, arg3, ...])
191 AceHook:CallHook(ObjectName, "Method" [, arg1, arg2, arg3, ...])
192  
193 DEPRECATED: This function has been deprecated and replaced by a direct
194 call from the self.Hooks table. This avoids function calls and decreases
195 the overhead from a CallHook. The new wrapper functions limit any
196 CallHook to 20 arguments. The new forms are as follows:
197  
198 self.Hooks.functionName.orig(arg1, arg2, arg3)
199 self.Hooks[ObjectName].MethodName.orig(ObjectName, arg1, arg2, arg3)
200 -------------------------------------------------------------------------]]
201  
202 function AceHook:CallHook(obj,meth,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
203 if type(obj) == "string" then
204 -- Function Call
205 if not self.Hooks or not self.Hooks[obj] then
206 self:HookDebug( "Attempt to CallHook when no hook exists for " .. obj)
207 return
208 else
209 local func = obj
210 return self.Hooks[func].orig(meth,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
211 end
212 else
213 if not self.Hooks[obj] or not self.Hooks[obj][meth] then
214 self:HookDebug( "Attempt to CallHook when no hook exists for " .. tostring(meth))
215 return
216 else
217 return self.Hooks[obj][meth].orig(obj,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
218 end
219 end
220 end
221  
222 --[[----------------------------------------------------------------------
223 AceHook:HookFunc - internal method.
224 o You can only hook each function once from each source.
225 o If there is an inactive hook for this func/handler pair, we reactivate it
226 o If there is an inactive hook for another handler, we error out.
227 o Looks for handler as a method of the calling class, error if not available
228 o If handler is a function, it just uses it directly through the wrapper
229 -------------------------------------------------------------------------]]
230 function AceHook:HookFunc(func, handler)
231 local f = getglobal(func)
232  
233 if not f or type(f) ~= "function" then
234 self:HookDebug("Attempt to hook a non-existant function '"..func.."'.",3)
235 return
236 end
237  
238 if not handler then handler = func end
239  
240 if self.Hooks[func] then
241 -- We have an active hook from this source. Don't multi-hook
242 if self.Hooks[func].active then
243 self:HookDebug( func .. " already has an active hook from this source.")
244 return
245 end
246 -- The hook is inactive, so reactivate it
247 if self.Hooks[func].handler == handler then
248 self.Hooks[func].active = true
249 return
250 else
251 error( "There is a stale hook for " .. func .. " can't hook or reactivate.",3)
252 end
253 end
254  
255 local methodHandler
256  
257 if type(handler) == "string" then
258 if self[handler] then
259 methodHandler = true
260 else
261 error( "Could not find the the handler " ..handler.." when hooking function " .. func,3)
262 end
263 elseif type(handler) ~= "function" then
264 error( "Could not find the handler you supplied when hooking " .. func,3)
265 end
266  
267 self.Hooks[func] = {}
268 self.Hooks[func].orig = f
269 self.Hooks[func].active = true
270 self.Hooks[func].handler = handler
271 self.Hooks[func].func = self:_getFunctionHook(func, handler, methodHandler)
272 setglobal(func, self.Hooks[func].func)
273 end
274  
275 --[[----------------------------------------------------------------------
276 AceHook:UnhookFunc - internal method
277 o If you attempt to unhook a function that has never been hooked, or to unhook in a
278 system that has never had a hook before, the system will error with a stack trace
279 o If we own the global function, then put the original back in its place and remove
280 all references to the Hooks[func] structure.
281 o If we don't own the global function (we've been hooked) we deactivate the hook,
282 forcing the handler to passthrough.
283 -------------------------------------------------------------------------]]
284 function AceHook:UnhookFunc(func)
285 if not self.Hooks or not self.Hooks[func] then
286 self:HookDebug("Tried to unhook '" ..func .. "' which is not currently hooked.")
287 return
288 end
289 if self.Hooks[func].active then
290 -- See if we own the global function
291 if getglobal(func) == self.Hooks[func].func then
292 setglobal(func, self.Hooks[func].orig)
293 self.Hooks[func] = nil
294 -- Magically all-done
295 else
296 self.Hooks[func].active = nil
297 end
298 end
299 end
300  
301 --[[----------------------------------------------------------------------
302 AceHook:_getFunctionHook- internal method
303 -------------------------------------------------------------------------]]
304  
305 function AceHook:_getFunctionHook(func, handler, methodHandler)
306 if methodHandler then
307 -- The handler is a method, need to self it
308 return
309 function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
310 if self.Hooks[func].active then
311 return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
312 else
313 return self.Hooks[func].orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
314 end
315 end
316 else
317 -- The handler is a function, just call it
318 return
319 function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
320 if self.Hooks[func].active then
321 return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
322 else
323 return self.Hooks[func].orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
324 end
325 end
326 end
327 end
328  
329 --[[----------------------------------------------------------------------
330 AceHook:HookMeth - Takes an optional fourth argument
331 o script - Signifies whether this is a script hook or not
332 -------------------------------------------------------------------------]]
333  
334 function AceHook:HookMeth(obj, method, handler, script)
335 if not handler then handler = method end
336 if (not obj or type(obj) ~= "table") then
337 error("The object you supplied could not be found, or isn't a table.", 3)
338 end
339  
340 if self.Hooks[obj] and self.Hooks[obj][method] then
341 -- We have an active hook from this source. Don't multi-hook
342 if self.Hooks[obj][method].active then
343 self:HookDebug( method .. " already has an active hook from this source.")
344 return
345 end
346 -- The hook is inactive, so reactivate it.
347 if self.Hooks[obj][method].handler == handler then
348 self.Hooks[obj][method].active = true
349 return
350 else
351 error( "There is a stale hook for " .. method .. " can't hook or reactivate.",3)
352 end
353 end
354 -- We're clear to try the hook, let's make some checks first
355 local methodHandler
356 if type(handler) == "string" then
357 if self[handler] then
358 methodHandler = true
359 else
360 error( "Could not find the handler ("..handler..") you supplied when hooking method " .. method,3)
361 end
362 elseif type(handler) ~= "function" then
363 error( "Could not find the handler you supplied when hooking method " .. method,3)
364 end
365 -- Handler has been found, so now try to find the method we're trying to hook
366 local orig, setscript
367 -- Script
368 if script then
369 if not obj.GetScript then
370 error("The object you supplied does not have a GetScript method.", 3)
371 end
372 if not obj:HasScript(method) then
373 error("The object you supplied doesn't allows the " .. method .. " method.", 3)
374 end
375 -- Sometimes there is not a original function for a script.
376 orig = obj:GetScript(method) or function () end
377 -- Method
378 else
379 orig = obj[method]
380 end
381 if not orig then
382 error("Could not find the method or script ("..method..") you are trying to hook.",3)
383 end
384 if not self.Hooks[obj] then self.Hooks[obj] = {} end
385 self.Hooks[obj][method] = {}
386 self.Hooks[obj][method].orig = orig
387 self.Hooks[obj][method].active = true
388 self.Hooks[obj][method].handler = handler
389 self.Hooks[obj][method].script = script
390 self.Hooks[obj][method].func = self:_getMethodHook(obj, method, handler, methodHandler)
391 if script then
392 obj:SetScript(method, self.Hooks[obj][method].func)
393 else
394 obj[method] = self.Hooks[obj][method].func
395 end
396 end
397  
398 --[[----------------------------------------------------------------------
399 AceHook:UnhookMeth - Internal method
400 o If you attempt to unhook a method that has never been hooked, or to unhook in a
401 system that has never had a hook before, the system will error with a stack trace
402 o If we own the global method, then put the original back in its place and remove
403 all references to the Hooks[obj][method] structure.
404 o If we don't own the global method (we've been hooked) we deactivate the hook,
405 forcing the handler to passthrough.
406 -------------------------------------------------------------------------]]
407 function AceHook:UnhookMeth(obj, method)
408 if not self.Hooks or not self.Hooks[obj] or not self.Hooks[obj][method] then
409 self:HookDebug("Attempt to unhook a method ("..method..") that is not currently hooked.")
410 return
411 end
412 if self.Hooks[obj][method].active then
413 -- If this is a script
414 if self.Hooks[obj][method].script then
415 if obj:GetScript(method) == self.Hooks[obj][method].func then
416 -- We own the global function. Kill it.
417 obj:SetScript(method, self.Hooks[obj][method].orig)
418 self.Hooks[obj][method] = nil
419 return
420 else
421 self.Hooks[obj][method].active = nil
422 end
423 else
424 if obj[method] == self.Hooks[obj][method].func then
425 -- We own the global function. Kill it.
426 obj[method] = self.Hooks[obj][method].orig
427 self.Hooks[obj][method] = nil
428 return
429 else
430 self.Hooks[obj][method].active = nil
431 end
432 end
433 end
434 if not next(self.Hooks[obj]) then
435 -- Spank the table
436 self.Hooks[obj] = nil
437 end
438 end
439  
440 --[[----------------------------------------------------------------------
441 AceHook:_getMethodHook - Internal Method
442 -------------------------------------------------------------------------]]
443 function AceHook:_getMethodHook(obj, method, handler, methodHook)
444 if methodHook then
445 -- The handler is a method, need to self it
446 return
447 function(o,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
448 if self.Hooks[obj][method].active then
449 return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
450 elseif self.Hooks[obj][method].orig then
451 return self.Hooks[obj][method].orig(obj, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
452 end
453 end
454 else
455 -- The handler is a function, just call it
456 return
457 function(o,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
458 if self.Hooks[obj][method].active then
459 return handler(o,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
460 elseif self.Hooks[obj][method].orig then
461 return self.Hooks[obj][method].orig(obj,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
462 end
463 end
464 end
465 end
466  
467 --[[----------------------------------------------------------------------
468 AceHook:Embed - Called to embed the vital methods into another object
469 -------------------------------------------------------------------------]]
470 function AceHook:Embed(object)
471 object.Hook = self.Hook
472 object.Unhook = self.Unhook
473 object.UnhookAll = self.UnhookAll
474 object.CallHook = self.CallHook
475 object.HookFunc = self.HookFunc
476 object.HookMeth = self.HookMeth
477 object.UnhookFunc = self.UnhookFunc
478 object.UnhookMeth = self.UnhookMeth
479 object._getFunctionHook = self._getFunctionHook
480 object._getMethodHook = self._getMethodHook
481 object.HookReport = self.HookReport
482 object.IsHooked = self.IsHooked
483  
484 object.HookScript = self.HookScript
485 object.UnhookScript = self.Unhook
486 object.UnhookAllScripts = self.UnhookAllScripts
487 object.CallScript = self.CallHook
488  
489 if object.debug then
490 object.HookDebug = object.debug
491 else
492 object.HookDebug = self.HookDebug
493 end
494 end
495 end -- End conditional declaration
496  
497 --[[---------------------------------------------------------------------------------
498 This is the stub declaration for non-Ace environments
499 ----------------------------------------------------------------------------------]]
500  
501 if not AceLibStub then
502 AceLibStub = {}
503  
504 -- Instance replacement method. Handles "upgrading" to a new version
505 function AceLibStub:ReplaceInstance(old,new)
506 for k,v in pairs(old) do old[k]=nil end
507 for k,v in pairs(new) do old[k]=v end
508 end
509  
510 -- Returns a new stub. This allows multiple libraries to use the same stub
511 function AceLibStub:NewStub(name)
512 local newStub = {}
513 self:ReplaceInstance(newStub, self)
514 newStub.versions = {}
515 newStub.libName = name
516 return newStub
517 end
518  
519 -- Returns true if the supplied version would be an upgrade to the current version
520 -- This allows for bypass code in library declaration.
521  
522 function AceLibStub:NewVersion(version, minor)
523 local versionData = self.versions[version]
524 if not versionData or versionData.minor < minor then return true end
525 end
526  
527 -- Handles referencing methods in the calling namespace itself, to accomplish
528 -- inheritance instead of composition. This will not be used in every library
529 -- but its important to track what namespaces have used :Embed() since
530 -- :ReplaceInstance() won't update those references.
531  
532 function AceLibStub:Embed(namespace, version)
533 local lib = self:GetInstance(version)
534 if not lib.Embed then return end
535  
536 lib:Embed(namespace)
537  
538 if not lib.embedList then
539 lib.embedList = setmetatable({}, {__mode="v"})
540 end
541  
542 table.insert(lib.embedList, namespace)
543 lib = nil
544 end
545  
546 -- Returns the most recent version of the given major library
547 function AceLibStub:GetInstance(version)
548 if not version then
549 error("You must specify a version when requesting a library instance" ,2)
550 end
551  
552 local versionData = self.versions[version]
553 if not versionData then
554 error("Cannot find " .. self.libName .. " instance with version '" .. version .. "'",2)
555 return
556 end
557 return versionData.instance
558 end
559  
560 -- Registers a new version of a given library. Takes the library object.
561 function AceLibStub:Register(newInstance)
562 if not newInstance.GetLibraryVersion then return end
563 local version,minor = newInstance:GetLibraryVersion()
564 local versionData = self.versions[version]
565 if not versionData then
566 -- This is new
567 versionData = { instance=newInstance, minor=minor, old={}}
568 self.versions[version] = versionData
569 newInstance:LibActivate(self)
570 return newInstance
571 end
572 if minor <= versionData.minor then
573 -- This one is already obsolete
574 if newInstance.LibDiscard then
575 newInstance:LibDiscard()
576 end
577 return versionData.instance
578 end
579  
580 -- This is an update
581 local oldInstance = versionData.instance
582 local oldList = versionData.old
583 versionData.instance = newInstance
584 versionData.minor = minor
585 local skipCopy = newInstance:LibActivate(self, oldInstance, oldList)
586 table.insert(oldList, oldInstance)
587 if not skipCopy then
588 for i,old in ipairs(oldList) do
589 self:ReplaceInstance(old, newInstance)
590 end
591 end
592 return newInstance
593 end
594 end
595  
596 --[[---------------------------------------------------------------------------------
597 Stub and Library registration
598 ----------------------------------------------------------------------------------]]
599  
600 -- Bind a stub in the global namespace if it doesn't already exist
601 if not AceHookLib then
602 AceHookLib = AceLibStub:NewStub("AceHook")
603 end
604  
605 -- Register this library version with the global stub
606 AceHookLib:Register(AceHook)
607 AceHook = nil