Mercurial > pidgin
annotate plugins/tcl/tcl_signals.c @ 12919:248b8b39c671
[gaim-migrate @ 15272]
Replace GaimBlistNodeAction with the more generic GaimMenuAction, this is in
preparation for letting the chat room user list have extensible menus like the
blist entries do. (I know it's not exactly the prettiest, and the callback
isn't exactly type-safe, when we eventually gobjectify everything we can get
some safety back by using (GObject, gpointer) but that's for later.)
I'm planning to look into merging GaimPluginActions into GaimMenuActions as
well.
committer: Tailor Script <tailor@pidgin.im>
author | Etan Reisner <pidgin@unreliablesource.net> |
---|---|
date | Tue, 17 Jan 2006 23:22:19 +0000 |
parents | 1096bdfef887 |
children | d0f7f698b6de |
rev | line source |
---|---|
6694 | 1 /** |
2 * @file tcl_signals.c Gaim Tcl signal API | |
3 * | |
4 * gaim | |
5 * | |
6 * Copyright (C) 2003 Ethan Blanton <eblanton@cs.purdue.edu> | |
7 * | |
8 * This program is free software; you can redistribute it and/or modify | |
9 * it under the terms of the GNU General Public License as published by | |
10 * the Free Software Foundation; either version 2 of the License, or | |
11 * (at your option) any later version. | |
12 * | |
13 * This program is distributed in the hope that it will be useful, | |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 * GNU General Public License for more details. | |
17 * | |
18 * You should have received a copy of the GNU General Public License | |
19 * along with this program; if not, write to the Free Software | |
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 */ | |
22 #include <tcl.h> | |
23 #include <stdarg.h> | |
24 | |
25 #include "tcl_gaim.h" | |
26 | |
27 #include "internal.h" | |
28 #include "connection.h" | |
29 #include "conversation.h" | |
30 #include "signals.h" | |
31 #include "debug.h" | |
32 #include "value.h" | |
33 #include "core.h" | |
34 | |
35 static GList *tcl_callbacks; | |
36 | |
37 static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handler); | |
12397
8d1cf3f847b1
[gaim-migrate @ 14704]
Richard Laager <rlaager@wiktel.com>
parents:
10625
diff
changeset
|
38 static Tcl_Obj *new_cb_namespace (void); |
6694 | 39 |
40 void tcl_signal_init() | |
41 { | |
42 tcl_callbacks = NULL; | |
43 } | |
44 | |
45 void tcl_signal_handler_free(struct tcl_signal_handler *handler) | |
46 { | |
47 if (handler == NULL) | |
48 return; | |
49 | |
10597 | 50 Tcl_DecrRefCount(handler->signal); |
51 Tcl_DecrRefCount(handler->namespace); | |
6694 | 52 g_free(handler); |
53 } | |
54 | |
55 void tcl_signal_cleanup(Tcl_Interp *interp) | |
56 { | |
57 GList *cur; | |
58 struct tcl_signal_handler *handler; | |
59 | |
60 for (cur = tcl_callbacks; cur != NULL; cur = g_list_next(cur)) { | |
61 handler = cur->data; | |
62 if (handler->interp == interp) { | |
63 tcl_signal_handler_free(handler); | |
64 cur->data = NULL; | |
65 } | |
66 } | |
67 tcl_callbacks = g_list_remove_all(tcl_callbacks, NULL); | |
68 } | |
69 | |
70 gboolean tcl_signal_connect(struct tcl_signal_handler *handler) | |
71 { | |
10597 | 72 GString *proc; |
73 | |
74 gaim_signal_get_values(handler->instance, | |
75 Tcl_GetString(handler->signal), | |
76 &handler->returntype, &handler->nargs, | |
77 &handler->argtypes); | |
6694 | 78 |
10597 | 79 tcl_signal_disconnect(handler->interp, Tcl_GetString(handler->signal), |
80 handler->interp); | |
81 | |
82 if (!gaim_signal_connect_vargs(handler->instance, | |
83 Tcl_GetString(handler->signal), | |
84 (void *)handler->interp, | |
85 GAIM_CALLBACK(tcl_signal_callback), | |
86 (void *)handler)) | |
6694 | 87 return FALSE; |
88 | |
10597 | 89 Tcl_IncrRefCount(handler->signal); |
90 handler->namespace = new_cb_namespace (); | |
91 Tcl_IncrRefCount(handler->namespace); | |
92 proc = g_string_new(""); | |
93 g_string_append_printf(proc, "namespace eval %s { proc cb { %s } { %s } }", | |
94 Tcl_GetString(handler->namespace), | |
95 Tcl_GetString(handler->args), | |
96 Tcl_GetString(handler->proc)); | |
97 if (Tcl_Eval(handler->interp, proc->str) != TCL_OK) { | |
98 Tcl_DecrRefCount(handler->namespace); | |
99 g_string_free(proc, TRUE); | |
6694 | 100 return FALSE; |
10597 | 101 } |
102 g_string_free(proc, TRUE); | |
6694 | 103 |
104 tcl_callbacks = g_list_append(tcl_callbacks, (gpointer)handler); | |
105 | |
106 return TRUE; | |
107 } | |
108 | |
109 void tcl_signal_disconnect(void *instance, const char *signal, Tcl_Interp *interp) | |
110 { | |
111 GList *cur; | |
112 struct tcl_signal_handler *handler; | |
113 gboolean found = FALSE; | |
10597 | 114 GString *cmd; |
6694 | 115 |
116 for (cur = tcl_callbacks; cur != NULL; cur = g_list_next(cur)) { | |
117 handler = cur->data; | |
118 if (handler->interp == interp && handler->instance == instance | |
10597 | 119 && !strcmp(signal, Tcl_GetString(handler->signal))) { |
6694 | 120 gaim_signal_disconnect(instance, signal, handler->interp, |
121 GAIM_CALLBACK(tcl_signal_callback)); | |
10597 | 122 cmd = g_string_sized_new(64); |
123 g_string_printf(cmd, "namespace delete %s", | |
124 Tcl_GetString(handler->namespace)); | |
125 Tcl_EvalEx(interp, cmd->str, -1, TCL_EVAL_GLOBAL); | |
6694 | 126 tcl_signal_handler_free(handler); |
10597 | 127 g_string_free(cmd, TRUE); |
6694 | 128 cur->data = NULL; |
129 found = TRUE; | |
130 break; | |
131 } | |
132 } | |
133 if (found) | |
134 tcl_callbacks = g_list_remove_all(tcl_callbacks, NULL); | |
135 } | |
136 | |
137 static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handler) | |
138 { | |
10597 | 139 GString *name, *val; |
6694 | 140 GaimBlistNode *node; |
141 int error, i; | |
142 void *retval = NULL; | |
10597 | 143 Tcl_Obj *cmd, *arg, *result; |
144 void **vals; /* Used for inout parameters */ | |
145 char ***strs; | |
6694 | 146 |
10597 | 147 vals = g_new0(void *, handler->nargs); |
148 strs = g_new0(char **, handler->nargs); | |
149 name = g_string_sized_new(32); | |
6694 | 150 val = g_string_sized_new(32); |
10597 | 151 |
152 cmd = Tcl_NewListObj(0, NULL); | |
153 Tcl_IncrRefCount(cmd); | |
154 | |
155 arg = Tcl_DuplicateObj(handler->namespace); | |
156 Tcl_AppendStringsToObj(arg, "::cb", NULL); | |
157 Tcl_ListObjAppendElement(handler->interp, cmd, arg); | |
6694 | 158 |
159 for (i = 0; i < handler->nargs; i++) { | |
10597 | 160 if (gaim_value_is_outgoing(handler->argtypes[i])) |
161 g_string_printf(name, "%s::arg%d", | |
162 Tcl_GetString(handler->namespace), i); | |
6694 | 163 |
164 switch(gaim_value_get_type(handler->argtypes[i])) { | |
165 case GAIM_TYPE_UNKNOWN: /* What? I guess just pass the word ... */ | |
166 /* treat this as a pointer, but complain first */ | |
167 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "unknown GaimValue type %d\n", | |
168 gaim_value_get_type(handler->argtypes[i])); | |
169 case GAIM_TYPE_POINTER: | |
170 case GAIM_TYPE_OBJECT: | |
171 case GAIM_TYPE_BOXED: | |
172 /* These are all "pointer" types to us */ | |
173 if (gaim_value_is_outgoing(handler->argtypes[i])) { | |
10597 | 174 vals[i] = va_arg(args, void **); |
175 Tcl_LinkVar(handler->interp, name->str, | |
176 vals[i], TCL_LINK_INT); | |
177 arg = Tcl_NewStringObj(name->str, -1); | |
6694 | 178 } else { |
10597 | 179 arg = Tcl_NewIntObj((int)va_arg(args, void *)); |
6694 | 180 } |
181 break; | |
182 case GAIM_TYPE_BOOLEAN: | |
183 if (gaim_value_is_outgoing(handler->argtypes[i])) { | |
10597 | 184 vals[i] = va_arg(args, gboolean *); |
185 Tcl_LinkVar(handler->interp, name->str, | |
186 (char *)&vals[i], TCL_LINK_BOOLEAN); | |
187 arg = Tcl_NewStringObj(name->str, -1); | |
6694 | 188 } else { |
10597 | 189 arg = Tcl_NewBooleanObj(va_arg(args, gboolean)); |
6694 | 190 } |
191 break; | |
192 case GAIM_TYPE_CHAR: | |
193 case GAIM_TYPE_UCHAR: | |
194 case GAIM_TYPE_SHORT: | |
195 case GAIM_TYPE_USHORT: | |
196 case GAIM_TYPE_INT: | |
197 case GAIM_TYPE_UINT: | |
198 case GAIM_TYPE_LONG: | |
199 case GAIM_TYPE_ULONG: | |
200 case GAIM_TYPE_ENUM: | |
201 /* I should really cast these individually to | |
202 * preserve as much information as possible ... | |
203 * but heh */ | |
204 if (gaim_value_is_outgoing(handler->argtypes[i])) { | |
10597 | 205 vals[i] = va_arg(args, int *); |
206 Tcl_LinkVar(handler->interp, name->str, | |
207 vals[i], TCL_LINK_INT); | |
208 arg = Tcl_NewStringObj(name->str, -1); | |
6694 | 209 } else { |
10597 | 210 arg = Tcl_NewIntObj(va_arg(args, int)); |
211 } | |
212 case GAIM_TYPE_INT64: | |
213 case GAIM_TYPE_UINT64: | |
10625 | 214 /* Tcl < 8.4 doesn't have wide ints, so we have ugly |
215 * ifdefs in here */ | |
10597 | 216 if (gaim_value_is_outgoing(handler->argtypes[i])) { |
10625 | 217 vals[i] = (void *)va_arg(args, gint64 *); |
218 #if (TCL_MAJOR_VERSION >= 8 && TCL_MINOR_VERSION >= 4) | |
10597 | 219 Tcl_LinkVar(handler->interp, name->str, |
220 vals[i], TCL_LINK_WIDE_INT); | |
10625 | 221 #else |
222 /* This is going to cause weirdness at best, | |
223 * but what do you want ... we're losing | |
224 * precision */ | |
225 Tcl_LinkVar(handler->interp, name->str, | |
226 vals[i], TCL_LINK_INT); | |
227 #endif /* Tcl >= 8.4 */ | |
10597 | 228 arg = Tcl_NewStringObj(name->str, -1); |
229 } else { | |
10625 | 230 #if (TCL_MAJOR_VERSION >= 8 && TCL_MINOR_VERSION >= 4) |
231 arg = Tcl_NewWideIntObj(va_arg(args, gint64)); | |
232 #else | |
233 arg = Tcl_NewIntObj((int)va_arg(args, int)); | |
234 #endif /* Tcl >= 8.4 */ | |
6694 | 235 } |
236 break; | |
237 case GAIM_TYPE_STRING: | |
238 if (gaim_value_is_outgoing(handler->argtypes[i])) { | |
10597 | 239 strs[i] = va_arg(args, char **); |
240 if (strs[i] == NULL || *strs[i] == NULL) { | |
241 vals[i] = ckalloc(1); | |
242 *(char *)vals[i] = '\0'; | |
6694 | 243 } else { |
10597 | 244 vals[i] = ckalloc(strlen(*strs[i]) + 1); |
245 strcpy(vals[i], *strs[i]); | |
6694 | 246 } |
10597 | 247 Tcl_LinkVar(handler->interp, name->str, |
248 (char *)&vals[i], TCL_LINK_STRING); | |
249 arg = Tcl_NewStringObj(name->str, -1); | |
6694 | 250 } else { |
10597 | 251 arg = Tcl_NewStringObj(va_arg(args, char *), -1); |
6694 | 252 } |
253 break; | |
254 case GAIM_TYPE_SUBTYPE: | |
255 switch (gaim_value_get_subtype(handler->argtypes[i])) { | |
256 case GAIM_SUBTYPE_UNKNOWN: | |
257 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "subtype unknown\n"); | |
258 case GAIM_SUBTYPE_ACCOUNT: | |
259 case GAIM_SUBTYPE_CONNECTION: | |
260 case GAIM_SUBTYPE_CONVERSATION: | |
261 case GAIM_SUBTYPE_PLUGIN: | |
262 /* pointers again */ | |
263 if (gaim_value_is_outgoing(handler->argtypes[i])) { | |
10597 | 264 vals[i] = va_arg(args, void **); |
265 Tcl_LinkVar(handler->interp, name->str, | |
266 vals[i], TCL_LINK_INT); | |
267 arg = Tcl_NewStringObj(name->str, -1); | |
6694 | 268 } else { |
10597 | 269 arg = Tcl_NewIntObj((int)va_arg(args, void *)); |
6694 | 270 } |
271 break; | |
272 case GAIM_SUBTYPE_BLIST: | |
273 case GAIM_SUBTYPE_BLIST_BUDDY: | |
274 case GAIM_SUBTYPE_BLIST_GROUP: | |
275 case GAIM_SUBTYPE_BLIST_CHAT: | |
276 /* We're going to switch again for code-deduping */ | |
277 if (gaim_value_is_outgoing(handler->argtypes[i])) | |
278 node = *va_arg(args, GaimBlistNode **); | |
279 else | |
280 node = va_arg(args, GaimBlistNode *); | |
281 switch (node->type) { | |
282 case GAIM_BLIST_GROUP_NODE: | |
6700 | 283 g_string_printf(val, "group {%s}", ((GaimGroup *)node)->name); |
6694 | 284 break; |
6735 | 285 case GAIM_BLIST_CONTACT_NODE: |
286 /* g_string_printf(val, "contact {%s}", Contact Name? ); */ | |
287 break; | |
6694 | 288 case GAIM_BLIST_BUDDY_NODE: |
6700 | 289 g_string_printf(val, "buddy {%s} %lu", ((GaimBuddy *)node)->name, |
290 (unsigned long)((GaimBuddy *)node)->account); | |
6694 | 291 break; |
292 case GAIM_BLIST_CHAT_NODE: | |
7118
bf630f7dfdcd
[gaim-migrate @ 7685]
Christian Hammond <chipx86@chipx86.com>
parents:
6735
diff
changeset
|
293 g_string_printf(val, "chat {%s} %lu", ((GaimChat *)node)->alias, |
bf630f7dfdcd
[gaim-migrate @ 7685]
Christian Hammond <chipx86@chipx86.com>
parents:
6735
diff
changeset
|
294 (unsigned long)((GaimChat *)node)->account); |
6694 | 295 break; |
296 case GAIM_BLIST_OTHER_NODE: | |
297 g_string_printf(val, "other"); | |
298 break; | |
299 } | |
10597 | 300 arg = Tcl_NewStringObj(val->str, -1); |
6694 | 301 break; |
302 } | |
303 } | |
10597 | 304 Tcl_ListObjAppendElement(handler->interp, cmd, arg); |
6694 | 305 } |
306 | |
307 /* Call the friggin' procedure already */ | |
10597 | 308 if ((error = Tcl_EvalObjEx(handler->interp, cmd, TCL_EVAL_GLOBAL)) != TCL_OK) { |
6694 | 309 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "error evaluating callback: %s\n", |
310 Tcl_GetString(Tcl_GetObjResult(handler->interp))); | |
311 } else { | |
312 result = Tcl_GetObjResult(handler->interp); | |
313 /* handle return values -- strings and words only */ | |
314 if (handler->returntype) { | |
315 if (gaim_value_get_type(handler->returntype) == GAIM_TYPE_STRING) { | |
316 retval = (void *)g_strdup(Tcl_GetString(result)); | |
317 } else { | |
318 if ((error = Tcl_GetIntFromObj(handler->interp, result, (int *)&retval)) != TCL_OK) { | |
319 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "Error retrieving procedure result: %s\n", | |
320 Tcl_GetString(Tcl_GetObjResult(handler->interp))); | |
321 retval = NULL; | |
322 } | |
323 } | |
324 } | |
325 } | |
326 | |
327 /* And finally clean up */ | |
328 for (i = 0; i < handler->nargs; i++) { | |
10597 | 329 g_string_printf(name, "%s::arg%d", |
330 Tcl_GetString(handler->namespace), i); | |
331 if (gaim_value_is_outgoing(handler->argtypes[i])) | |
332 Tcl_UnlinkVar(handler->interp, name->str); | |
333 /* We basically only have to deal with strings on the | |
334 * way out */ | |
6694 | 335 switch (gaim_value_get_type(handler->argtypes[i])) { |
336 case GAIM_TYPE_STRING: | |
337 if (gaim_value_is_outgoing(handler->argtypes[i])) { | |
10597 | 338 if (vals[i] != NULL && *(char **)vals[i] != NULL) { |
339 g_free(*strs[i]); | |
340 *strs[i] = g_strdup(vals[i]); | |
6694 | 341 } |
10597 | 342 ckfree(vals[i]); |
6694 | 343 } |
344 break; | |
345 default: | |
346 /* nothing */ | |
347 ; | |
348 } | |
349 } | |
350 | |
351 g_string_free(name, TRUE); | |
10504 | 352 g_string_free(val, TRUE); |
10597 | 353 g_free(vals); |
354 g_free(strs); | |
355 | |
6694 | 356 |
357 return retval; | |
358 } | |
10597 | 359 |
360 static Tcl_Obj *new_cb_namespace () | |
361 { | |
362 static int cbnum; | |
363 char name[32]; | |
364 | |
365 g_snprintf (name, sizeof(name), "::gaim::_callback::cb_%d", cbnum++); | |
366 return Tcl_NewStringObj (name, -1); | |
367 } |