vanilla-wow-addons – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | --[[--------------------------------------------------------------------------------- |
2 | AceHook Library - Embeddable (standalone) or for use through Ace |
||
3 | ------------------------------------------------------------------------------------]] |
||
4 | |||
5 | local VERSION = 1.0 |
||
6 | |||
7 | if not AceHook or AceHook.VERSION < VERSION then |
||
8 | -- We need to load this version of the library |
||
9 | |||
10 | if AceCore then |
||
11 | if AceHook then |
||
12 | ace:print("|cffff0000[AceHook] REPLACING THE SUPPLIED ACEHOOK WITH AN EMBEDDED VERSION ("..VERSION..")."); |
||
13 | end |
||
14 | |||
15 | AceHook = AceCore:new() |
||
16 | else |
||
17 | AceHook = {} |
||
18 | end |
||
19 | |||
20 | --[[---------------------------------------------------------------------- |
||
21 | AceHook:Hook |
||
22 | self:Hook("functionName", ["handlerName" | handler]) |
||
23 | self:Hook(ObjectName, "Method", ["Handler" | handler]) |
||
24 | -------------------------------------------------------------------------]] |
||
25 | function AceHook:Hook(arg1, arg2, arg3) |
||
26 | if not self.Hooks then self.Hooks = {} end |
||
27 | if type(arg1) == "string" then |
||
28 | self:HookFunc(arg1, arg2) |
||
29 | else |
||
30 | self:HookMeth(arg1, arg2, arg3) |
||
31 | end |
||
32 | end |
||
33 | |||
34 | function AceHook:HookScript(arg1, arg2, arg3) |
||
35 | if not self.Hooks then self.Hooks = {} end |
||
36 | |||
37 | self:HookMeth(arg1, arg2, arg3, true) |
||
38 | end |
||
39 | |||
40 | --[[---------------------------------------------------------------------- |
||
41 | AceHook:Unhook |
||
42 | self:Unhook("functionName") |
||
43 | self:Unhook(ObjectName, "Method") |
||
44 | -------------------------------------------------------------------------]] |
||
45 | function AceHook:Unhook(arg1, arg2) |
||
46 | if type(arg1) == "string" then |
||
47 | self:UnhookFunc(arg1) |
||
48 | else |
||
49 | self:UnhookMeth(arg1, arg2) |
||
50 | end |
||
51 | end |
||
52 | |||
53 | --[[---------------------------------------------------------------------- |
||
54 | AceHook:UnhookAll - Unhooks all active hooks from the calling source |
||
55 | -------------------------------------------------------------------------]] |
||
56 | function AceHook:UnhookAll(script) |
||
57 | if not self.Hooks then return end |
||
58 | for key, value in pairs(self.Hooks) do |
||
59 | if type(key) == "table" then |
||
60 | for method in pairs(value) do |
||
61 | if (self.Hooks[key][method].script == script) then |
||
62 | self:Unhook(key, method) |
||
63 | end |
||
64 | end |
||
65 | else |
||
66 | self:Unhook(key) |
||
67 | end |
||
68 | end |
||
69 | end |
||
70 | |||
71 | function AceHook:UnhookAllScripts() |
||
72 | self:UnhookAll(true) |
||
73 | end |
||
74 | |||
75 | --[[---------------------------------------------------------------------- |
||
76 | AceHook:Print - Utility function for HookReport, for embedding |
||
77 | AceHook:HookReport - Lists registered hooks from this source |
||
78 | -------------------------------------------------------------------------]] |
||
79 | function AceHook:Print(message) |
||
80 | if AceCore then |
||
81 | ace:print(message) |
||
82 | else |
||
83 | DEFAULT_CHAT_FRAME:AddMessage(message) |
||
84 | end |
||
85 | end |
||
86 | |||
87 | function AceHook:HookReport() |
||
88 | AceHook:Print("|cffffff00 AceHook: HookReport()") |
||
89 | if not self.Hooks then AceHook:Print("No registered hooks.") return end |
||
90 | |||
91 | for key, value in pairs(self.Hooks) do |
||
92 | if type(key) == "table" then |
||
93 | for method in pairs(value) do |
||
94 | AceHook:Print("AceHook: key: "..tostring(key).." method: "..tostring(method)..((self.Hooks[key][method].active and "|cff00ff00 Active" or "|cffffff00 Inactive"))) |
||
95 | end |
||
96 | else |
||
97 | AceHook:Print("AceHook: key: " .. tostring(key) .. " value: " .. tostring(value) .. ((self.Hooks[key].active and "|cff00ff00 Active" or "|cffffff00 Inactive"))) |
||
98 | end |
||
99 | end |
||
100 | end |
||
101 | |||
102 | --[[---------------------------------------------------------------------- |
||
103 | AceHook:CallHook("functionName" [, arg1, arg2, arg3, ...]) |
||
104 | AceHook:CallHook(ObjectName, "Method" [, arg1, arg2, arg3, ...]) |
||
105 | |||
106 | DEPRECATED: This function has been deprecated and replaced by a direct |
||
107 | call from the self.Hooks table. This avoids function calls and decreases |
||
108 | the overhead from a CallHook. The new wrapper functions limit any |
||
109 | CallHook to 20 arguments. The new forms are as follows: |
||
110 | |||
111 | self.Hooks.functionName.orig(arg1, arg2, arg3) |
||
112 | self.Hooks[ObjectName].MethodName.orig(ObjectName, arg1, arg2, arg3) |
||
113 | -------------------------------------------------------------------------]] |
||
114 | |||
115 | 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) |
||
116 | if type(obj) == "string" then |
||
117 | -- Function Call |
||
118 | if not self.Hooks or not self.Hooks[obj] then |
||
119 | if self.debug then self.debug( "Attempt to CallHook when no hook exists for " .. obj); return; end |
||
120 | else |
||
121 | local func = obj |
||
122 | 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) |
||
123 | end |
||
124 | else |
||
125 | if not self.Hooks[obj] or not self.Hooks[obj][meth] then |
||
126 | if self.debug then self.debug( "Attempt to CallHook when no hook exists for " .. tostring(meth)); return; end |
||
127 | else |
||
128 | 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) |
||
129 | end |
||
130 | end |
||
131 | end |
||
132 | |||
133 | --[[---------------------------------------------------------------------- |
||
134 | AceHook:HookFunc - internal method. |
||
135 | o You can only hook each function once from each source. |
||
136 | o If there is an inactive hook for this func/handler pair, we reactivate it |
||
137 | o If there is an inactive hook for another handler, we error out. |
||
138 | o Looks for handler as a method of the calling class, error if not available |
||
139 | o If handler is a function, it just uses it directly through the wrapper |
||
140 | -------------------------------------------------------------------------]] |
||
141 | function AceHook:HookFunc(func, handler) |
||
142 | if not handler then handler = func end |
||
143 | if self.Hooks[func] then |
||
144 | -- We have an active hook from this source. Don't multi-hook |
||
145 | if self.Hooks[func].active then |
||
146 | if self.debug then self:debug( func .. " already has an active hook from this source.") end |
||
147 | return |
||
148 | end |
||
149 | -- The hook is inactive, so reactivate it |
||
150 | if self.Hooks[func].handler == handler then |
||
151 | self.Hooks[func].active = true |
||
152 | return |
||
153 | else |
||
154 | error( "There is a stale hook for " .. func .. " can't hook or reactivate.",3) |
||
155 | end |
||
156 | end |
||
157 | |||
158 | local methodHandler |
||
159 | |||
160 | if type(handler) == "string" then |
||
161 | if self[handler] then |
||
162 | methodHandler = true |
||
163 | else |
||
164 | error( "Could not find the the handler " ..handler.." when hooking function " .. func,3) |
||
165 | end |
||
166 | elseif type(handler) ~= "function" then |
||
167 | error( "Could not find the handler you supplied when hooking " .. func,3) |
||
168 | end |
||
169 | |||
170 | self.Hooks[func] = {} |
||
171 | self.Hooks[func].orig = getglobal(func) |
||
172 | self.Hooks[func].active = true |
||
173 | self.Hooks[func].handler = handler |
||
174 | self.Hooks[func].func = self:_getFunctionHook(func, handler, methodHandler) |
||
175 | setglobal(func, self.Hooks[func].func) |
||
176 | end |
||
177 | |||
178 | --[[---------------------------------------------------------------------- |
||
179 | AceHook:UnhookFunc - internal method |
||
180 | o If you attempt to unhook a function that has never been hooked, or to unhook in a |
||
181 | system that has never had a hook before, the system will error with a stack trace |
||
182 | o If we own the global function, then put the original back in its place and remove |
||
183 | all references to the Hooks[func] structure. |
||
184 | o If we don't own the global function (we've been hooked) we deactivate the hook, |
||
185 | forcing the handler to passthrough. |
||
186 | -------------------------------------------------------------------------]] |
||
187 | function AceHook:UnhookFunc(func) |
||
188 | if not self.Hooks or not self.Hooks[func] then |
||
189 | if self.debug then self:debug("Attempt to unhook a function that is not currently hooked.") end |
||
190 | return |
||
191 | end |
||
192 | if self.Hooks[func].active then |
||
193 | -- See if we own the global function |
||
194 | if getglobal(func) == self.Hooks[func].func then |
||
195 | setglobal(func, self.Hooks[func].orig) |
||
196 | self.Hooks[func] = nil |
||
197 | -- Magically all-done |
||
198 | else |
||
199 | self.Hooks[func].active = nil |
||
200 | end |
||
201 | end |
||
202 | end |
||
203 | |||
204 | --[[---------------------------------------------------------------------- |
||
205 | AceHook:_getFunctionHook- internal method |
||
206 | -------------------------------------------------------------------------]] |
||
207 | |||
208 | function AceHook:_getFunctionHook(func, handler, methodHandler) |
||
209 | if methodHandler then |
||
210 | -- The handler is a method, need to self it |
||
211 | return |
||
212 | function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) |
||
213 | if self.Hooks[func].active then |
||
214 | return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) |
||
215 | else |
||
216 | 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) |
||
217 | end |
||
218 | end |
||
219 | else |
||
220 | -- The handler is a function, just call it |
||
221 | return |
||
222 | function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) |
||
223 | if self.Hooks[func].active then |
||
224 | return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) |
||
225 | else |
||
226 | 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) |
||
227 | end |
||
228 | end |
||
229 | end |
||
230 | end |
||
231 | |||
232 | --[[---------------------------------------------------------------------- |
||
233 | AceHook:HookMeth - Takes an optional fourth argument |
||
234 | o script - Signifies whether this is a script hook or not |
||
235 | -------------------------------------------------------------------------]] |
||
236 | |||
237 | function AceHook:HookMeth(obj, method, handler, script) |
||
238 | if not handler then handler = method end |
||
239 | if self.Hooks[obj] and self.Hooks[obj][method] then |
||
240 | -- We have an active hook from this source. Don't multi-hook |
||
241 | if self.Hooks[obj][method].active then |
||
242 | if self.debug then self:debug( method .. " already has an active hook from this source.") end |
||
243 | return |
||
244 | end |
||
245 | -- The hook is inactive, so reactivate it. |
||
246 | if self.Hooks[obj][method].handler == handler then |
||
247 | self.Hooks[obj][method].active = true |
||
248 | return |
||
249 | else |
||
250 | error( "There is a stale hook for " .. method .. " can't hook or reactivate.",3) |
||
251 | end |
||
252 | end |
||
253 | -- We're clear to try the hook, let's make some checks first |
||
254 | local methodHandler |
||
255 | if type(handler) == "string" then |
||
256 | if self[handler] then |
||
257 | methodHandler = true |
||
258 | else |
||
259 | error( "Could not find the handler ("..handler..") you supplied when hooking method " .. method,3) |
||
260 | end |
||
261 | elseif type(handler) ~= "function" then |
||
262 | error( "Could not find the handler you supplied when hooking method " .. method,3) |
||
263 | end |
||
264 | -- Handler has been found, so now try to find the method we're trying to hook |
||
265 | local orig, setscript |
||
266 | -- Script |
||
267 | if script then |
||
268 | if (not obj) then |
||
269 | error("The object you supplied could not be found.", 3) |
||
270 | end |
||
271 | if not obj.GetScript then |
||
272 | error("The object you supplied does not have a GetScript method.", 3) |
||
273 | end |
||
274 | if not obj:HasScript(method) then |
||
275 | error("The object you supplied doesn't allows the " .. method .. " method.", 3) |
||
276 | end |
||
277 | -- Sometimes there is not a original function for a script. |
||
278 | orig = obj:GetScript(method) or function () end |
||
279 | -- Method |
||
280 | else |
||
281 | orig = obj[method] |
||
282 | end |
||
283 | if not orig then |
||
284 | error("Could not find the method or script ("..method..") you are trying to hook.",3) |
||
285 | end |
||
286 | if not self.Hooks[obj] then self.Hooks[obj] = {} end |
||
287 | self.Hooks[obj][method] = {} |
||
288 | self.Hooks[obj][method].orig = orig |
||
289 | self.Hooks[obj][method].active = true |
||
290 | self.Hooks[obj][method].handler = handler |
||
291 | self.Hooks[obj][method].script = script |
||
292 | self.Hooks[obj][method].func = self:_getMethodHook(obj, method, handler, methodHandler) |
||
293 | if script then |
||
294 | obj:SetScript(method, self.Hooks[obj][method].func) |
||
295 | else |
||
296 | obj[method] = self.Hooks[obj][method].func |
||
297 | end |
||
298 | end |
||
299 | |||
300 | --[[---------------------------------------------------------------------- |
||
301 | AceHook:UnhookMeth - Internal method |
||
302 | o If you attempt to unhook a method that has never been hooked, or to unhook in a |
||
303 | system that has never had a hook before, the system will error with a stack trace |
||
304 | o If we own the global method, then put the original back in its place and remove |
||
305 | all references to the Hooks[obj][method] structure. |
||
306 | o If we don't own the global method (we've been hooked) we deactivate the hook, |
||
307 | forcing the handler to passthrough. |
||
308 | -------------------------------------------------------------------------]] |
||
309 | function AceHook:UnhookMeth(obj, method) |
||
310 | if not self.Hooks or not self.Hooks[obj] or not self.Hooks[obj][method] then |
||
311 | if self.debug then self:debug( "Attempt to unhook a method ("..method..") that is not currently hooked.") end |
||
312 | return |
||
313 | end |
||
314 | if self.Hooks[obj][method].active then |
||
315 | -- If this is a script |
||
316 | if self.Hooks[obj][method].script then |
||
317 | if obj:GetScript(method) == self.Hooks[obj][method].func then |
||
318 | -- We own the global function. Kill it. |
||
319 | obj:SetScript(method, self.Hooks[obj][method].orig) |
||
320 | self.Hooks[obj][method] = nil |
||
321 | return |
||
322 | else |
||
323 | self.Hooks[obj][method].active = nil |
||
324 | end |
||
325 | else |
||
326 | if obj[method] == self.Hooks[obj][method].func then |
||
327 | -- We own the global function. Kill it. |
||
328 | obj[method] = self.Hooks[obj][method].orig |
||
329 | self.Hooks[obj][method] = nil |
||
330 | return |
||
331 | else |
||
332 | self.Hooks[obj][method].active = nil |
||
333 | end |
||
334 | end |
||
335 | end |
||
336 | if not next(self.Hooks[obj]) then |
||
337 | -- Spank the table |
||
338 | self.Hooks[obj] = nil |
||
339 | end |
||
340 | end |
||
341 | |||
342 | --[[---------------------------------------------------------------------- |
||
343 | AceHook:_getMethodHook - Internal Method |
||
344 | -------------------------------------------------------------------------]] |
||
345 | function AceHook:_getMethodHook(obj, method, handler, methodHook) |
||
346 | if methodHook then |
||
347 | -- The handler is a method, need to self it |
||
348 | return |
||
349 | function(o,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) |
||
350 | if self.Hooks[obj][method].active then |
||
351 | return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) |
||
352 | elseif self.Hooks[obj][method].orig then |
||
353 | 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) |
||
354 | end |
||
355 | end |
||
356 | else |
||
357 | -- The handler is a function, just call it |
||
358 | return |
||
359 | function(o,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) |
||
360 | if self.Hooks[obj][method].active then |
||
361 | return handler(o,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) |
||
362 | elseif self.Hooks[obj][method].orig then |
||
363 | 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) |
||
364 | end |
||
365 | end |
||
366 | end |
||
367 | end |
||
368 | |||
369 | --[[---------------------------------------------------------------------- |
||
370 | AceHook:Embed - Called to embed the vital methods into another object |
||
371 | -------------------------------------------------------------------------]] |
||
372 | function AceHook:Embed(object) |
||
373 | object.Hook = self.Hook |
||
374 | object.Unhook = self.Unhook |
||
375 | object.UnhookAll = self.UnhookAll |
||
376 | object.CallHook = self.CallHook |
||
377 | object.HookFunc = self.HookFunc |
||
378 | object.HookMeth = self.HookMeth |
||
379 | object.UnhookFunc = self.UnhookFunc |
||
380 | object.UnhookMeth = self.UnhookMeth |
||
381 | object._getFunctionHook = self._getFunctionHook |
||
382 | object._getMethodHook = self._getMethodHook |
||
383 | object.HookReport = self.HookReport |
||
384 | |||
385 | object.HookScript = self.HookScript |
||
386 | object.UnhookScript = self.Unhook |
||
387 | object.UnhookAllScripts = self.UnhookAllScripts |
||
388 | object.CallScript = self.CallHook |
||
389 | end |
||
390 | |||
391 | if AceCore then ace.hook = AceHook end |
||
392 | |||
393 | end |