plugins/tcl/tcl_signals.c

changeset 10597
41d2db9fefd5
parent 10519
80801a34a246
child 10625
1ea4af762084
equal deleted inserted replaced
10596:5fe1670fec47 10597:41d2db9fefd5
33 #include "core.h" 33 #include "core.h"
34 34
35 static GList *tcl_callbacks; 35 static GList *tcl_callbacks;
36 36
37 static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handler); 37 static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handler);
38 static Tcl_Obj *new_cb_namespace ();
38 39
39 void tcl_signal_init() 40 void tcl_signal_init()
40 { 41 {
41 tcl_callbacks = NULL; 42 tcl_callbacks = NULL;
42 } 43 }
43 44
44 void tcl_signal_handler_free(struct tcl_signal_handler *handler) 45 void tcl_signal_handler_free(struct tcl_signal_handler *handler)
45 { 46 {
46 int i;
47
48 if (handler == NULL) 47 if (handler == NULL)
49 return; 48 return;
50 49
51 g_free(handler->signal); 50 Tcl_DecrRefCount(handler->signal);
52 if (handler->argnames != NULL) { 51 Tcl_DecrRefCount(handler->namespace);
53 for (i = 0; i < handler->nnames; i++)
54 g_free(handler->argnames[i]);
55 g_free(handler->argnames);
56 }
57 Tcl_DecrRefCount(handler->proc);
58 g_free(handler); 52 g_free(handler);
59 } 53 }
60 54
61 void tcl_signal_cleanup(Tcl_Interp *interp) 55 void tcl_signal_cleanup(Tcl_Interp *interp)
62 { 56 {
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 }

mercurial