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);
|
|
38
|
|
39 void tcl_signal_init()
|
|
40 {
|
|
41 tcl_callbacks = NULL;
|
|
42 }
|
|
43
|
|
44 void tcl_signal_handler_free(struct tcl_signal_handler *handler)
|
|
45 {
|
|
46 if (handler == NULL)
|
|
47 return;
|
|
48
|
|
49 g_free(handler->signal);
|
|
50 if (handler->argnames != NULL)
|
|
51 g_free(handler->argnames);
|
|
52 Tcl_DecrRefCount(handler->proc);
|
|
53 g_free(handler);
|
|
54 }
|
|
55
|
|
56 void tcl_signal_cleanup(Tcl_Interp *interp)
|
|
57 {
|
|
58 GList *cur;
|
|
59 struct tcl_signal_handler *handler;
|
|
60
|
|
61 for (cur = tcl_callbacks; cur != NULL; cur = g_list_next(cur)) {
|
|
62 handler = cur->data;
|
|
63 if (handler->interp == interp) {
|
|
64 tcl_signal_handler_free(handler);
|
|
65 cur->data = NULL;
|
|
66 }
|
|
67 }
|
|
68 tcl_callbacks = g_list_remove_all(tcl_callbacks, NULL);
|
|
69 }
|
|
70
|
|
71 gboolean tcl_signal_connect(struct tcl_signal_handler *handler)
|
|
72 {
|
|
73 gaim_signal_get_values(handler->instance, handler->signal, &handler->returntype,
|
|
74 &handler->nargs, &handler->argtypes);
|
|
75
|
|
76 if (handler->nargs != handler->nnames)
|
|
77 return FALSE;
|
|
78
|
|
79 tcl_signal_disconnect(handler->interp, handler->signal, handler->interp);
|
|
80
|
|
81 if (!gaim_signal_connect_vargs(handler->instance, handler->signal, (void *)handler->interp,
|
|
82 GAIM_CALLBACK(tcl_signal_callback), (void *)handler))
|
|
83 return FALSE;
|
|
84
|
|
85 tcl_callbacks = g_list_append(tcl_callbacks, (gpointer)handler);
|
|
86
|
|
87 return TRUE;
|
|
88 }
|
|
89
|
|
90 void tcl_signal_disconnect(void *instance, const char *signal, Tcl_Interp *interp)
|
|
91 {
|
|
92 GList *cur;
|
|
93 struct tcl_signal_handler *handler;
|
|
94 gboolean found = FALSE;
|
|
95
|
|
96 for (cur = tcl_callbacks; cur != NULL; cur = g_list_next(cur)) {
|
|
97 handler = cur->data;
|
|
98 if (handler->interp == interp && handler->instance == instance
|
|
99 && !strcmp(signal, handler->signal)) {
|
|
100 gaim_signal_disconnect(instance, signal, handler->interp,
|
|
101 GAIM_CALLBACK(tcl_signal_callback));
|
|
102 tcl_signal_handler_free(handler);
|
|
103 cur->data = NULL;
|
|
104 found = TRUE;
|
|
105 break;
|
|
106 }
|
|
107 }
|
|
108 if (found)
|
|
109 tcl_callbacks = g_list_remove_all(tcl_callbacks, NULL);
|
|
110 }
|
|
111
|
|
112 static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handler)
|
|
113 {
|
|
114 struct var {
|
|
115 void *val;
|
|
116 char *str;
|
|
117 } *vars;
|
|
118 GString *val, *name;
|
|
119 GaimBlistNode *node;
|
|
120 int error, i;
|
|
121 void *retval = NULL;
|
|
122 Tcl_Obj *result;
|
|
123
|
|
124 vars = g_new0(struct var, handler->nargs);
|
|
125 val = g_string_sized_new(32);
|
|
126 name = g_string_sized_new(32);
|
|
127
|
|
128 for (i = 0; i < handler->nargs; i++) {
|
|
129 g_string_printf(name, "::gaim::_callback::%s", handler->argnames[i]);
|
|
130
|
|
131 switch(gaim_value_get_type(handler->argtypes[i])) {
|
|
132 default: /* Yes, at the top */
|
|
133 case GAIM_TYPE_UNKNOWN: /* What? I guess just pass the word ... */
|
|
134 /* treat this as a pointer, but complain first */
|
|
135 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "unknown GaimValue type %d\n",
|
|
136 gaim_value_get_type(handler->argtypes[i]));
|
|
137 case GAIM_TYPE_POINTER:
|
|
138 case GAIM_TYPE_OBJECT:
|
|
139 case GAIM_TYPE_BOXED:
|
|
140 /* These are all "pointer" types to us */
|
|
141 if (gaim_value_is_outgoing(handler->argtypes[i])) {
|
|
142 vars[i].val = va_arg(args, void **);
|
|
143 Tcl_LinkVar(handler->interp, name->str, vars[i].val, TCL_LINK_INT);
|
|
144 } else {
|
|
145 vars[i].val = va_arg(args, void *);
|
|
146 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val,
|
|
147 TCL_LINK_INT|TCL_LINK_READ_ONLY);
|
|
148 }
|
|
149 break;
|
|
150 case GAIM_TYPE_BOOLEAN:
|
|
151 if (gaim_value_is_outgoing(handler->argtypes[i])) {
|
|
152 vars[i].val = va_arg(args, gboolean *);
|
|
153 Tcl_LinkVar(handler->interp, name->str, vars[i].val, TCL_LINK_BOOLEAN);
|
|
154 } else {
|
|
155 vars[i].val = (void *)va_arg(args, gboolean);
|
|
156 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val,
|
|
157 TCL_LINK_BOOLEAN|TCL_LINK_READ_ONLY);
|
|
158 }
|
|
159 break;
|
|
160 case GAIM_TYPE_CHAR:
|
|
161 case GAIM_TYPE_UCHAR:
|
|
162 case GAIM_TYPE_SHORT:
|
|
163 case GAIM_TYPE_USHORT:
|
|
164 case GAIM_TYPE_INT:
|
|
165 case GAIM_TYPE_UINT:
|
|
166 case GAIM_TYPE_LONG:
|
|
167 case GAIM_TYPE_ULONG:
|
|
168 case GAIM_TYPE_ENUM:
|
|
169 /* These next two are totally bogus */
|
|
170 case GAIM_TYPE_INT64:
|
|
171 case GAIM_TYPE_UINT64:
|
|
172 /* I should really cast these individually to
|
|
173 * preserve as much information as possible ...
|
|
174 * but heh */
|
|
175 if (gaim_value_is_outgoing(handler->argtypes[i])) {
|
|
176 vars[i].val = (void *)va_arg(args, int *);
|
|
177 Tcl_LinkVar(handler->interp, name->str, vars[i].val, TCL_LINK_INT);
|
|
178 } else {
|
|
179 vars[i].val = (void *)va_arg(args, int);
|
|
180 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val,
|
|
181 TCL_LINK_INT|TCL_LINK_READ_ONLY);
|
|
182 }
|
|
183 break;
|
|
184 case GAIM_TYPE_STRING:
|
|
185 if (gaim_value_is_outgoing(handler->argtypes[i])) {
|
|
186 vars[i].val = (void *)va_arg(args, char **);
|
|
187 if (vars[i].val != NULL && *(char **)vars[i].val != NULL) {
|
|
188 vars[i].str = (char *)ckalloc(strlen(*(char **)vars[i].val) + 1);
|
|
189 strcpy(vars[i].str, *(char **)vars[i].val);
|
|
190 } else {
|
|
191 vars[i].str = (char *)ckalloc(1);
|
|
192 *vars[i].str = '\0';
|
|
193 }
|
|
194 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].str, TCL_LINK_STRING);
|
|
195 } else {
|
|
196 vars[i].val = (void *)va_arg(args, char *);
|
|
197 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val,
|
|
198 TCL_LINK_STRING|TCL_LINK_READ_ONLY);
|
|
199 }
|
|
200 break;
|
|
201 case GAIM_TYPE_SUBTYPE:
|
|
202 switch (gaim_value_get_subtype(handler->argtypes[i])) {
|
|
203 default:
|
|
204 case GAIM_SUBTYPE_UNKNOWN:
|
|
205 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "subtype unknown\n");
|
|
206 case GAIM_SUBTYPE_ACCOUNT:
|
|
207 case GAIM_SUBTYPE_CONNECTION:
|
|
208 case GAIM_SUBTYPE_CONVERSATION:
|
|
209 case GAIM_SUBTYPE_CONV_WINDOW:
|
|
210 case GAIM_SUBTYPE_PLUGIN:
|
|
211 /* pointers again */
|
|
212 if (gaim_value_is_outgoing(handler->argtypes[i])) {
|
|
213 vars[i].val = va_arg(args, void **);
|
|
214 Tcl_LinkVar(handler->interp, name->str, vars[i].val, TCL_LINK_INT);
|
|
215 } else {
|
|
216 vars[i].val = va_arg(args, void *);
|
|
217 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val,
|
|
218 TCL_LINK_INT|TCL_LINK_READ_ONLY);
|
|
219 }
|
|
220 break;
|
|
221 case GAIM_SUBTYPE_BLIST:
|
|
222 case GAIM_SUBTYPE_BLIST_BUDDY:
|
|
223 case GAIM_SUBTYPE_BLIST_GROUP:
|
|
224 case GAIM_SUBTYPE_BLIST_CHAT:
|
|
225 /* We're going to switch again for code-deduping */
|
|
226 if (gaim_value_is_outgoing(handler->argtypes[i]))
|
|
227 node = *va_arg(args, GaimBlistNode **);
|
|
228 else
|
|
229 node = va_arg(args, GaimBlistNode *);
|
|
230 switch (node->type) {
|
|
231 case GAIM_BLIST_GROUP_NODE:
|
6700
|
232 g_string_printf(val, "group {%s}", ((GaimGroup *)node)->name);
|
6694
|
233 break;
|
|
234 case GAIM_BLIST_BUDDY_NODE:
|
6700
|
235 g_string_printf(val, "buddy {%s} %lu", ((GaimBuddy *)node)->name,
|
|
236 (unsigned long)((GaimBuddy *)node)->account);
|
6694
|
237 break;
|
|
238 case GAIM_BLIST_CHAT_NODE:
|
6700
|
239 g_string_printf(val, "chat {%s} %lu", ((GaimBlistChat *)node)->alias,
|
|
240 (unsigned long)((GaimBlistChat *)node)->account);
|
6694
|
241 break;
|
|
242 case GAIM_BLIST_OTHER_NODE:
|
|
243 g_string_printf(val, "other");
|
|
244 break;
|
|
245 }
|
|
246 vars[i].str = g_strdup(val->str);
|
|
247 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].str,
|
|
248 TCL_LINK_STRING|TCL_LINK_READ_ONLY);
|
|
249 break;
|
|
250 }
|
|
251 }
|
|
252 }
|
|
253
|
|
254 /* Call the friggin' procedure already */
|
|
255 if ((error = Tcl_EvalObjEx(handler->interp, handler->proc, TCL_EVAL_GLOBAL)) != TCL_OK) {
|
|
256 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "error evaluating callback: %s\n",
|
|
257 Tcl_GetString(Tcl_GetObjResult(handler->interp)));
|
|
258 } else {
|
|
259 result = Tcl_GetObjResult(handler->interp);
|
|
260 /* handle return values -- strings and words only */
|
|
261 if (handler->returntype) {
|
|
262 if (gaim_value_get_type(handler->returntype) == GAIM_TYPE_STRING) {
|
|
263 retval = (void *)g_strdup(Tcl_GetString(result));
|
|
264 } else {
|
|
265 if ((error = Tcl_GetIntFromObj(handler->interp, result, (int *)&retval)) != TCL_OK) {
|
|
266 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "Error retrieving procedure result: %s\n",
|
|
267 Tcl_GetString(Tcl_GetObjResult(handler->interp)));
|
|
268 retval = NULL;
|
|
269 }
|
|
270 }
|
|
271 }
|
|
272 }
|
|
273
|
|
274 /* And finally clean up */
|
|
275 for (i = 0; i < handler->nargs; i++) {
|
|
276 g_string_printf(name, "::gaim::_callback::%s", handler->argnames[i]);
|
|
277 Tcl_UnlinkVar(handler->interp, name->str);
|
|
278 /* We basically only have to deal with strings and buddies
|
|
279 * on the way out */
|
|
280 switch (gaim_value_get_type(handler->argtypes[i])) {
|
|
281 case GAIM_TYPE_STRING:
|
|
282 if (gaim_value_is_outgoing(handler->argtypes[i])) {
|
|
283 if (vars[i].val != NULL && *(char **)vars[i].val != NULL) {
|
|
284 g_free(*(char **)vars[i].val);
|
|
285 *(char **)vars[i].val = g_strdup(vars[i].str);
|
|
286 }
|
|
287 ckfree(vars[i].str);
|
|
288 }
|
|
289 break;
|
|
290 case GAIM_TYPE_SUBTYPE:
|
|
291 switch(gaim_value_get_subtype(handler->argtypes[i])) {
|
|
292 case GAIM_SUBTYPE_BLIST:
|
|
293 case GAIM_SUBTYPE_BLIST_BUDDY:
|
|
294 case GAIM_SUBTYPE_BLIST_GROUP:
|
|
295 case GAIM_SUBTYPE_BLIST_CHAT:
|
|
296 g_free(vars[i].str);
|
|
297 break;
|
|
298 default:
|
|
299 /* nothing */
|
|
300 ;
|
|
301 }
|
|
302 break;
|
|
303 default:
|
|
304 /* nothing */
|
|
305 ;
|
|
306 }
|
|
307 }
|
|
308
|
|
309 g_string_free(name, TRUE);
|
|
310 g_string_free(val, FALSE);
|
|
311 g_free(vars);
|
|
312
|
|
313 return retval;
|
|
314 }
|