| 73 tcl_callbacks = g_list_remove_all(tcl_callbacks, NULL); |
67 tcl_callbacks = g_list_remove_all(tcl_callbacks, NULL); |
| 74 } |
68 } |
| 75 |
69 |
| 76 gboolean tcl_signal_connect(struct tcl_signal_handler *handler) |
70 gboolean tcl_signal_connect(struct tcl_signal_handler *handler) |
| 77 { |
71 { |
| 78 gaim_signal_get_values(handler->instance, handler->signal, &handler->returntype, |
72 GString *proc; |
| 79 &handler->nargs, &handler->argtypes); |
73 |
| 80 |
74 gaim_signal_get_values(handler->instance, |
| 81 if (handler->nargs != handler->nnames) |
75 Tcl_GetString(handler->signal), |
| |
76 &handler->returntype, &handler->nargs, |
| |
77 &handler->argtypes); |
| |
78 |
| |
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)) |
| 82 return FALSE; |
87 return FALSE; |
| 83 |
88 |
| 84 tcl_signal_disconnect(handler->interp, handler->signal, handler->interp); |
89 Tcl_IncrRefCount(handler->signal); |
| 85 |
90 handler->namespace = new_cb_namespace (); |
| 86 if (!gaim_signal_connect_vargs(handler->instance, handler->signal, (void *)handler->interp, |
91 Tcl_IncrRefCount(handler->namespace); |
| 87 GAIM_CALLBACK(tcl_signal_callback), (void *)handler)) |
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); |
| 88 return FALSE; |
100 return FALSE; |
| |
101 } |
| |
102 g_string_free(proc, TRUE); |
| 89 |
103 |
| 90 tcl_callbacks = g_list_append(tcl_callbacks, (gpointer)handler); |
104 tcl_callbacks = g_list_append(tcl_callbacks, (gpointer)handler); |
| 91 |
105 |
| 92 return TRUE; |
106 return TRUE; |
| 93 } |
107 } |
| 95 void tcl_signal_disconnect(void *instance, const char *signal, Tcl_Interp *interp) |
109 void tcl_signal_disconnect(void *instance, const char *signal, Tcl_Interp *interp) |
| 96 { |
110 { |
| 97 GList *cur; |
111 GList *cur; |
| 98 struct tcl_signal_handler *handler; |
112 struct tcl_signal_handler *handler; |
| 99 gboolean found = FALSE; |
113 gboolean found = FALSE; |
| |
114 GString *cmd; |
| 100 |
115 |
| 101 for (cur = tcl_callbacks; cur != NULL; cur = g_list_next(cur)) { |
116 for (cur = tcl_callbacks; cur != NULL; cur = g_list_next(cur)) { |
| 102 handler = cur->data; |
117 handler = cur->data; |
| 103 if (handler->interp == interp && handler->instance == instance |
118 if (handler->interp == interp && handler->instance == instance |
| 104 && !strcmp(signal, handler->signal)) { |
119 && !strcmp(signal, Tcl_GetString(handler->signal))) { |
| 105 gaim_signal_disconnect(instance, signal, handler->interp, |
120 gaim_signal_disconnect(instance, signal, handler->interp, |
| 106 GAIM_CALLBACK(tcl_signal_callback)); |
121 GAIM_CALLBACK(tcl_signal_callback)); |
| |
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); |
| 107 tcl_signal_handler_free(handler); |
126 tcl_signal_handler_free(handler); |
| |
127 g_string_free(cmd, TRUE); |
| 108 cur->data = NULL; |
128 cur->data = NULL; |
| 109 found = TRUE; |
129 found = TRUE; |
| 110 break; |
130 break; |
| 111 } |
131 } |
| 112 } |
132 } |
| 114 tcl_callbacks = g_list_remove_all(tcl_callbacks, NULL); |
134 tcl_callbacks = g_list_remove_all(tcl_callbacks, NULL); |
| 115 } |
135 } |
| 116 |
136 |
| 117 static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handler) |
137 static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handler) |
| 118 { |
138 { |
| 119 struct var { |
139 GString *name, *val; |
| 120 void *val; |
|
| 121 char *str; |
|
| 122 } *vars; |
|
| 123 GString *val, *name; |
|
| 124 GaimBlistNode *node; |
140 GaimBlistNode *node; |
| 125 int error, i; |
141 int error, i; |
| 126 void *retval = NULL; |
142 void *retval = NULL; |
| 127 Tcl_Obj *result; |
143 Tcl_Obj *cmd, *arg, *result; |
| 128 |
144 void **vals; /* Used for inout parameters */ |
| 129 vars = g_new0(struct var, handler->nargs); |
145 char ***strs; |
| |
146 |
| |
147 vals = g_new0(void *, handler->nargs); |
| |
148 strs = g_new0(char **, handler->nargs); |
| |
149 name = g_string_sized_new(32); |
| 130 val = g_string_sized_new(32); |
150 val = g_string_sized_new(32); |
| 131 name = g_string_sized_new(32); |
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); |
| 132 |
158 |
| 133 for (i = 0; i < handler->nargs; i++) { |
159 for (i = 0; i < handler->nargs; i++) { |
| 134 g_string_printf(name, "::gaim::_callback::%s", handler->argnames[i]); |
160 if (gaim_value_is_outgoing(handler->argtypes[i])) |
| |
161 g_string_printf(name, "%s::arg%d", |
| |
162 Tcl_GetString(handler->namespace), i); |
| 135 |
163 |
| 136 switch(gaim_value_get_type(handler->argtypes[i])) { |
164 switch(gaim_value_get_type(handler->argtypes[i])) { |
| 137 default: /* Yes, at the top */ |
|
| 138 case GAIM_TYPE_UNKNOWN: /* What? I guess just pass the word ... */ |
165 case GAIM_TYPE_UNKNOWN: /* What? I guess just pass the word ... */ |
| 139 /* treat this as a pointer, but complain first */ |
166 /* treat this as a pointer, but complain first */ |
| 140 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "unknown GaimValue type %d\n", |
167 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "unknown GaimValue type %d\n", |
| 141 gaim_value_get_type(handler->argtypes[i])); |
168 gaim_value_get_type(handler->argtypes[i])); |
| 142 case GAIM_TYPE_POINTER: |
169 case GAIM_TYPE_POINTER: |
| 143 case GAIM_TYPE_OBJECT: |
170 case GAIM_TYPE_OBJECT: |
| 144 case GAIM_TYPE_BOXED: |
171 case GAIM_TYPE_BOXED: |
| 145 /* These are all "pointer" types to us */ |
172 /* These are all "pointer" types to us */ |
| 146 if (gaim_value_is_outgoing(handler->argtypes[i])) { |
173 if (gaim_value_is_outgoing(handler->argtypes[i])) { |
| 147 vars[i].val = va_arg(args, void **); |
174 vals[i] = va_arg(args, void **); |
| 148 Tcl_LinkVar(handler->interp, name->str, vars[i].val, TCL_LINK_INT); |
175 Tcl_LinkVar(handler->interp, name->str, |
| 149 } else { |
176 vals[i], TCL_LINK_INT); |
| 150 vars[i].val = va_arg(args, void *); |
177 arg = Tcl_NewStringObj(name->str, -1); |
| 151 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val, |
178 } else { |
| 152 TCL_LINK_INT|TCL_LINK_READ_ONLY); |
179 arg = Tcl_NewIntObj((int)va_arg(args, void *)); |
| 153 } |
180 } |
| 154 break; |
181 break; |
| 155 case GAIM_TYPE_BOOLEAN: |
182 case GAIM_TYPE_BOOLEAN: |
| 156 if (gaim_value_is_outgoing(handler->argtypes[i])) { |
183 if (gaim_value_is_outgoing(handler->argtypes[i])) { |
| 157 vars[i].val = va_arg(args, gboolean *); |
184 vals[i] = va_arg(args, gboolean *); |
| 158 Tcl_LinkVar(handler->interp, name->str, vars[i].val, TCL_LINK_BOOLEAN); |
185 Tcl_LinkVar(handler->interp, name->str, |
| 159 } else { |
186 (char *)&vals[i], TCL_LINK_BOOLEAN); |
| 160 vars[i].val = (void *)va_arg(args, gboolean); |
187 arg = Tcl_NewStringObj(name->str, -1); |
| 161 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val, |
188 } else { |
| 162 TCL_LINK_BOOLEAN|TCL_LINK_READ_ONLY); |
189 arg = Tcl_NewBooleanObj(va_arg(args, gboolean)); |
| 163 } |
190 } |
| 164 break; |
191 break; |
| 165 case GAIM_TYPE_CHAR: |
192 case GAIM_TYPE_CHAR: |
| 166 case GAIM_TYPE_UCHAR: |
193 case GAIM_TYPE_UCHAR: |
| 167 case GAIM_TYPE_SHORT: |
194 case GAIM_TYPE_SHORT: |
| 169 case GAIM_TYPE_INT: |
196 case GAIM_TYPE_INT: |
| 170 case GAIM_TYPE_UINT: |
197 case GAIM_TYPE_UINT: |
| 171 case GAIM_TYPE_LONG: |
198 case GAIM_TYPE_LONG: |
| 172 case GAIM_TYPE_ULONG: |
199 case GAIM_TYPE_ULONG: |
| 173 case GAIM_TYPE_ENUM: |
200 case GAIM_TYPE_ENUM: |
| 174 /* These next two are totally bogus */ |
|
| 175 case GAIM_TYPE_INT64: |
|
| 176 case GAIM_TYPE_UINT64: |
|
| 177 /* I should really cast these individually to |
201 /* I should really cast these individually to |
| 178 * preserve as much information as possible ... |
202 * preserve as much information as possible ... |
| 179 * but heh */ |
203 * but heh */ |
| 180 if (gaim_value_is_outgoing(handler->argtypes[i])) { |
204 if (gaim_value_is_outgoing(handler->argtypes[i])) { |
| 181 vars[i].val = (void *)va_arg(args, int *); |
205 vals[i] = va_arg(args, int *); |
| 182 Tcl_LinkVar(handler->interp, name->str, vars[i].val, TCL_LINK_INT); |
206 Tcl_LinkVar(handler->interp, name->str, |
| 183 } else { |
207 vals[i], TCL_LINK_INT); |
| 184 vars[i].val = (void *)va_arg(args, int); |
208 arg = Tcl_NewStringObj(name->str, -1); |
| 185 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val, |
209 } else { |
| 186 TCL_LINK_INT|TCL_LINK_READ_ONLY); |
210 arg = Tcl_NewIntObj(va_arg(args, int)); |
| |
211 } |
| |
212 case GAIM_TYPE_INT64: |
| |
213 case GAIM_TYPE_UINT64: |
| |
214 if (gaim_value_is_outgoing(handler->argtypes[i])) { |
| |
215 vals[i] = (void *)va_arg(args, guint64 *); |
| |
216 Tcl_LinkVar(handler->interp, name->str, |
| |
217 vals[i], TCL_LINK_WIDE_INT); |
| |
218 arg = Tcl_NewStringObj(name->str, -1); |
| |
219 } else { |
| |
220 arg = Tcl_NewWideIntObj(va_arg(args, guint64)); |
| 187 } |
221 } |
| 188 break; |
222 break; |
| 189 case GAIM_TYPE_STRING: |
223 case GAIM_TYPE_STRING: |
| 190 if (gaim_value_is_outgoing(handler->argtypes[i])) { |
224 if (gaim_value_is_outgoing(handler->argtypes[i])) { |
| 191 vars[i].val = (void *)va_arg(args, char **); |
225 strs[i] = va_arg(args, char **); |
| 192 if (vars[i].val != NULL && *(char **)vars[i].val != NULL) { |
226 if (strs[i] == NULL || *strs[i] == NULL) { |
| 193 vars[i].str = (char *)ckalloc(strlen(*(char **)vars[i].val) + 1); |
227 vals[i] = ckalloc(1); |
| 194 strcpy(vars[i].str, *(char **)vars[i].val); |
228 *(char *)vals[i] = '\0'; |
| 195 } else { |
229 } else { |
| 196 vars[i].str = (char *)ckalloc(1); |
230 vals[i] = ckalloc(strlen(*strs[i]) + 1); |
| 197 *vars[i].str = '\0'; |
231 strcpy(vals[i], *strs[i]); |
| 198 } |
232 } |
| 199 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].str, TCL_LINK_STRING); |
233 Tcl_LinkVar(handler->interp, name->str, |
| 200 } else { |
234 (char *)&vals[i], TCL_LINK_STRING); |
| 201 vars[i].val = (void *)va_arg(args, char *); |
235 arg = Tcl_NewStringObj(name->str, -1); |
| 202 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val, |
236 } else { |
| 203 TCL_LINK_STRING|TCL_LINK_READ_ONLY); |
237 arg = Tcl_NewStringObj(va_arg(args, char *), -1); |
| 204 } |
238 } |
| 205 break; |
239 break; |
| 206 case GAIM_TYPE_SUBTYPE: |
240 case GAIM_TYPE_SUBTYPE: |
| 207 switch (gaim_value_get_subtype(handler->argtypes[i])) { |
241 switch (gaim_value_get_subtype(handler->argtypes[i])) { |
| 208 default: |
|
| 209 case GAIM_SUBTYPE_UNKNOWN: |
242 case GAIM_SUBTYPE_UNKNOWN: |
| 210 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "subtype unknown\n"); |
243 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "subtype unknown\n"); |
| 211 case GAIM_SUBTYPE_ACCOUNT: |
244 case GAIM_SUBTYPE_ACCOUNT: |
| 212 case GAIM_SUBTYPE_CONNECTION: |
245 case GAIM_SUBTYPE_CONNECTION: |
| 213 case GAIM_SUBTYPE_CONVERSATION: |
246 case GAIM_SUBTYPE_CONVERSATION: |
| 214 case GAIM_SUBTYPE_CONV_WINDOW: |
247 case GAIM_SUBTYPE_CONV_WINDOW: |
| 215 case GAIM_SUBTYPE_PLUGIN: |
248 case GAIM_SUBTYPE_PLUGIN: |
| 216 /* pointers again */ |
249 /* pointers again */ |
| 217 if (gaim_value_is_outgoing(handler->argtypes[i])) { |
250 if (gaim_value_is_outgoing(handler->argtypes[i])) { |
| 218 vars[i].val = va_arg(args, void **); |
251 vals[i] = va_arg(args, void **); |
| 219 Tcl_LinkVar(handler->interp, name->str, vars[i].val, TCL_LINK_INT); |
252 Tcl_LinkVar(handler->interp, name->str, |
| |
253 vals[i], TCL_LINK_INT); |
| |
254 arg = Tcl_NewStringObj(name->str, -1); |
| 220 } else { |
255 } else { |
| 221 vars[i].val = va_arg(args, void *); |
256 arg = Tcl_NewIntObj((int)va_arg(args, void *)); |
| 222 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val, |
|
| 223 TCL_LINK_INT|TCL_LINK_READ_ONLY); |
|
| 224 } |
257 } |
| 225 break; |
258 break; |
| 226 case GAIM_SUBTYPE_BLIST: |
259 case GAIM_SUBTYPE_BLIST: |
| 227 case GAIM_SUBTYPE_BLIST_BUDDY: |
260 case GAIM_SUBTYPE_BLIST_BUDDY: |
| 228 case GAIM_SUBTYPE_BLIST_GROUP: |
261 case GAIM_SUBTYPE_BLIST_GROUP: |
| 249 break; |
282 break; |
| 250 case GAIM_BLIST_OTHER_NODE: |
283 case GAIM_BLIST_OTHER_NODE: |
| 251 g_string_printf(val, "other"); |
284 g_string_printf(val, "other"); |
| 252 break; |
285 break; |
| 253 } |
286 } |
| 254 vars[i].str = g_strdup(val->str); |
287 arg = Tcl_NewStringObj(val->str, -1); |
| 255 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].str, |
|
| 256 TCL_LINK_STRING|TCL_LINK_READ_ONLY); |
|
| 257 break; |
288 break; |
| 258 } |
289 } |
| 259 } |
290 } |
| |
291 Tcl_ListObjAppendElement(handler->interp, cmd, arg); |
| 260 } |
292 } |
| 261 |
293 |
| 262 /* Call the friggin' procedure already */ |
294 /* Call the friggin' procedure already */ |
| 263 if ((error = Tcl_EvalObjEx(handler->interp, handler->proc, TCL_EVAL_GLOBAL)) != TCL_OK) { |
295 if ((error = Tcl_EvalObjEx(handler->interp, cmd, TCL_EVAL_GLOBAL)) != TCL_OK) { |
| 264 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "error evaluating callback: %s\n", |
296 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "error evaluating callback: %s\n", |
| 265 Tcl_GetString(Tcl_GetObjResult(handler->interp))); |
297 Tcl_GetString(Tcl_GetObjResult(handler->interp))); |
| 266 } else { |
298 } else { |
| 267 result = Tcl_GetObjResult(handler->interp); |
299 result = Tcl_GetObjResult(handler->interp); |
| 268 /* handle return values -- strings and words only */ |
300 /* handle return values -- strings and words only */ |
| 279 } |
311 } |
| 280 } |
312 } |
| 281 |
313 |
| 282 /* And finally clean up */ |
314 /* And finally clean up */ |
| 283 for (i = 0; i < handler->nargs; i++) { |
315 for (i = 0; i < handler->nargs; i++) { |
| 284 g_string_printf(name, "::gaim::_callback::%s", handler->argnames[i]); |
316 g_string_printf(name, "%s::arg%d", |
| 285 Tcl_UnlinkVar(handler->interp, name->str); |
317 Tcl_GetString(handler->namespace), i); |
| 286 /* We basically only have to deal with strings and buddies |
318 if (gaim_value_is_outgoing(handler->argtypes[i])) |
| 287 * on the way out */ |
319 Tcl_UnlinkVar(handler->interp, name->str); |
| |
320 /* We basically only have to deal with strings on the |
| |
321 * way out */ |
| 288 switch (gaim_value_get_type(handler->argtypes[i])) { |
322 switch (gaim_value_get_type(handler->argtypes[i])) { |
| 289 case GAIM_TYPE_STRING: |
323 case GAIM_TYPE_STRING: |
| 290 if (gaim_value_is_outgoing(handler->argtypes[i])) { |
324 if (gaim_value_is_outgoing(handler->argtypes[i])) { |
| 291 if (vars[i].val != NULL && *(char **)vars[i].val != NULL) { |
325 if (vals[i] != NULL && *(char **)vals[i] != NULL) { |
| 292 g_free(*(char **)vars[i].val); |
326 g_free(*strs[i]); |
| 293 *(char **)vars[i].val = g_strdup(vars[i].str); |
327 *strs[i] = g_strdup(vals[i]); |
| 294 } |
328 } |
| 295 ckfree(vars[i].str); |
329 ckfree(vals[i]); |
| 296 } |
|
| 297 break; |
|
| 298 case GAIM_TYPE_SUBTYPE: |
|
| 299 switch(gaim_value_get_subtype(handler->argtypes[i])) { |
|
| 300 case GAIM_SUBTYPE_BLIST: |
|
| 301 case GAIM_SUBTYPE_BLIST_BUDDY: |
|
| 302 case GAIM_SUBTYPE_BLIST_GROUP: |
|
| 303 case GAIM_SUBTYPE_BLIST_CHAT: |
|
| 304 g_free(vars[i].str); |
|
| 305 break; |
|
| 306 default: |
|
| 307 /* nothing */ |
|
| 308 ; |
|
| 309 } |
330 } |
| 310 break; |
331 break; |
| 311 default: |
332 default: |
| 312 /* nothing */ |
333 /* nothing */ |
| 313 ; |
334 ; |
| 314 } |
335 } |
| 315 } |
336 } |
| 316 |
337 |
| 317 g_string_free(name, TRUE); |
338 g_string_free(name, TRUE); |
| 318 g_string_free(val, TRUE); |
339 g_string_free(val, TRUE); |
| 319 g_free(vars); |
340 g_free(vals); |
| |
341 g_free(strs); |
| |
342 |
| 320 |
343 |
| 321 return retval; |
344 return retval; |
| 322 } |
345 } |
| |
346 |
| |
347 static Tcl_Obj *new_cb_namespace () |
| |
348 { |
| |
349 static int cbnum; |
| |
350 char name[32]; |
| |
351 |
| |
352 g_snprintf (name, sizeof(name), "::gaim::_callback::cb_%d", cbnum++); |
| |
353 return Tcl_NewStringObj (name, -1); |
| |
354 } |