vapigen: Remove misleading --metadata, each .gir must have its own .metadata.
[vala-lang.git] / codegen / valagsignalmodule.vala
blob2634d00c8be90d646f277337335fda84454550a5
1 /* valagsignalmodule.vala
3 * Copyright (C) 2006-2010 Jürg Billeter
4 * Copyright (C) 2006-2008 Raffaele Sandrini
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 * Author:
21 * Jürg Billeter <j@bitron.ch>
22 * Raffaele Sandrini <raffaele@sandrini.ch>
26 public class Vala.GSignalModule : GObjectModule {
27 private string get_marshaller_type_name (DataType t, bool dbus = false) {
28 if (t is PointerType || t.type_parameter != null) {
29 return ("POINTER");
30 } else if (t is ErrorType) {
31 return ("POINTER");
32 } else if (t is ArrayType) {
33 if (dbus) {
34 return ("BOXED");
35 } else {
36 if (((ArrayType) t).element_type.data_type == string_type.data_type) {
37 return ("BOXED,INT");
38 } else {
39 return ("POINTER,INT");
42 } else if (t is VoidType) {
43 return ("VOID");
44 } else if (dbus && DBusModule.get_type_signature (t).has_prefix ("(")) {
45 return ("BOXED");
46 } else if (t.data_type is Enum) {
47 var en = (Enum) t.data_type;
48 if (dbus) {
49 if (en.is_flags) {
50 return ("UINT");
51 } else {
52 return ("INT");
54 } else {
55 return en.get_marshaller_type_name ();
57 } else {
58 return t.data_type.get_marshaller_type_name ();
62 private string get_marshaller_type_name_for_parameter (Parameter param, bool dbus = false) {
63 if (param.direction != ParameterDirection.IN) {
64 return ("POINTER");
65 } else {
66 return get_marshaller_type_name (param.variable_type, dbus);
70 public override string get_marshaller_function (List<Parameter> params, DataType return_type, string? prefix = null, bool dbus = false) {
71 var signature = get_marshaller_signature (params, return_type, dbus);
72 string ret;
74 if (prefix == null) {
75 if (predefined_marshal_set.contains (signature)) {
76 prefix = "g_cclosure_marshal";
77 } else {
78 prefix = "g_cclosure_user_marshal";
82 ret = "%s_%s_".printf (prefix, get_marshaller_type_name (return_type, dbus));
84 if (params == null || params.size == 0) {
85 ret = ret + "_VOID";
86 } else {
87 foreach (Parameter p in params) {
88 ret = "%s_%s".printf (ret, get_marshaller_type_name_for_parameter (p, dbus).replace (",", "_"));
92 return ret;
95 private string? get_value_type_name_from_type_reference (DataType t) {
96 if (t is PointerType || t.type_parameter != null) {
97 return "gpointer";
98 } else if (t is VoidType) {
99 return "void";
100 } else if (t.data_type == string_type.data_type) {
101 return "const char*";
102 } else if (t.data_type is Class || t.data_type is Interface) {
103 return "gpointer";
104 } else if (t.data_type is Struct) {
105 var st = (Struct) t.data_type;
106 if (st.is_simple_type ()) {
107 return t.data_type.get_cname ();
108 } else {
109 return "gpointer";
111 } else if (t.data_type is Enum) {
112 return "gint";
113 } else if (t is ArrayType) {
114 return "gpointer";
115 } else if (t is ErrorType) {
116 return "gpointer";
119 return null;
122 private string? get_value_type_name_from_parameter (Parameter p) {
123 if (p.direction != ParameterDirection.IN) {
124 return "gpointer";
125 } else {
126 return get_value_type_name_from_type_reference (p.variable_type);
130 private string get_marshaller_signature (List<Parameter> params, DataType return_type, bool dbus = false) {
131 string signature;
133 signature = "%s:".printf (get_marshaller_type_name (return_type, dbus));
134 if (params == null || params.size == 0) {
135 signature = signature + "VOID";
136 } else {
137 bool first = true;
138 foreach (Parameter p in params) {
139 if (first) {
140 signature = signature + get_marshaller_type_name_for_parameter (p, dbus);
141 first = false;
142 } else {
143 signature = "%s,%s".printf (signature, get_marshaller_type_name_for_parameter (p, dbus));
148 return signature;
151 private CCodeExpression? get_signal_name_cexpression (Signal sig, Expression? detail_expr, CodeNode node) {
152 if (detail_expr == null) {
153 return sig.get_canonical_cconstant ();
156 if (detail_expr.value_type is NullType || !detail_expr.value_type.compatible (string_type)) {
157 node.error = true;
158 Report.error (detail_expr.source_reference, "only string details are supported");
159 return null;
162 if (detail_expr is StringLiteral) {
163 return sig.get_canonical_cconstant (((StringLiteral) detail_expr).eval ());
166 var detail_decl = get_temp_variable (detail_expr.value_type, true, node);
167 emit_temp_var (detail_decl);
168 temp_ref_vars.insert (0, detail_decl);
170 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_strconcat"));
171 ccall.add_argument (sig.get_canonical_cconstant (""));
172 ccall.add_argument (get_cvalue (detail_expr));
173 ccall.add_argument (new CCodeConstant ("NULL"));
175 var ccomma = new CCodeCommaExpression ();
176 ccomma.append_expression (new CCodeAssignment (get_variable_cexpression (detail_decl.name), ccall));
177 ccomma.append_expression (get_variable_cexpression (detail_decl.name));
178 return ccomma;
181 public override void visit_signal (Signal sig) {
182 // parent_symbol may be null for dynamic signals
184 var cl = sig.parent_symbol as Class;
185 if (cl != null && cl.is_compact) {
186 sig.error = true;
187 Report.error (sig.source_reference, "Signals are not supported in compact classes");
188 return;
191 if (cl != null) {
192 foreach (DataType base_type in cl.get_base_types ()) {
193 if (SemanticAnalyzer.symbol_lookup_inherited (base_type.data_type, sig.name) is Signal) {
194 sig.error = true;
195 Report.error (sig.source_reference, "Signals with the same name as a signal in a base type are not supported");
196 return;
201 sig.accept_children (this);
203 // declare parameter type
204 foreach (Parameter p in sig.get_parameters ()) {
205 generate_parameter (p, cfile, new HashMap<int,CCodeParameter> (), null);
208 generate_marshaller (sig.get_parameters (), sig.return_type);
211 public override void generate_marshaller (List<Parameter> params, DataType return_type, bool dbus = false) {
212 string signature;
213 int n_params, i;
215 /* check whether a signal with the same signature already exists for this source file (or predefined) */
216 signature = get_marshaller_signature (params, return_type, dbus);
217 if (predefined_marshal_set.contains (signature) || user_marshal_set.contains (signature)) {
218 return;
221 var signal_marshaller = new CCodeFunction (get_marshaller_function (params, return_type, null, dbus), "void");
222 signal_marshaller.modifiers = CCodeModifiers.STATIC;
224 signal_marshaller.add_parameter (new CCodeParameter ("closure", "GClosure *"));
225 signal_marshaller.add_parameter (new CCodeParameter ("return_value", "GValue *"));
226 signal_marshaller.add_parameter (new CCodeParameter ("n_param_values", "guint"));
227 signal_marshaller.add_parameter (new CCodeParameter ("param_values", "const GValue *"));
228 signal_marshaller.add_parameter (new CCodeParameter ("invocation_hint", "gpointer"));
229 signal_marshaller.add_parameter (new CCodeParameter ("marshal_data", "gpointer"));
231 cfile.add_function_declaration (signal_marshaller);
233 var marshaller_body = new CCodeBlock ();
235 var callback_decl = new CCodeFunctionDeclarator (get_marshaller_function (params, return_type, "GMarshalFunc", dbus));
236 callback_decl.add_parameter (new CCodeParameter ("data1", "gpointer"));
237 n_params = 1;
238 foreach (Parameter p in params) {
239 callback_decl.add_parameter (new CCodeParameter ("arg_%d".printf (n_params), get_value_type_name_from_parameter (p)));
240 n_params++;
241 if (p.variable_type.is_array () && !dbus) {
242 callback_decl.add_parameter (new CCodeParameter ("arg_%d".printf (n_params), "gint"));
243 n_params++;
246 callback_decl.add_parameter (new CCodeParameter ("data2", "gpointer"));
247 marshaller_body.add_statement (new CCodeTypeDefinition (get_value_type_name_from_type_reference (return_type), callback_decl));
249 var var_decl = new CCodeDeclaration (get_marshaller_function (params, return_type, "GMarshalFunc", dbus));
250 var_decl.modifiers = CCodeModifiers.REGISTER;
251 var_decl.add_declarator (new CCodeVariableDeclarator ("callback"));
252 marshaller_body.add_statement (var_decl);
254 var_decl = new CCodeDeclaration ("GCClosure *");
255 var_decl.modifiers = CCodeModifiers.REGISTER;
256 var_decl.add_declarator (new CCodeVariableDeclarator ("cc", new CCodeCastExpression (new CCodeIdentifier ("closure"), "GCClosure *")));
257 marshaller_body.add_statement (var_decl);
259 var_decl = new CCodeDeclaration ("gpointer");
260 var_decl.modifiers = CCodeModifiers.REGISTER;
261 var_decl.add_declarator (new CCodeVariableDeclarator ("data1"));
262 var_decl.add_declarator (new CCodeVariableDeclarator ("data2"));
263 marshaller_body.add_statement (var_decl);
265 CCodeFunctionCall fc;
267 if (return_type.data_type != null || return_type.is_array ()) {
268 var_decl = new CCodeDeclaration (get_value_type_name_from_type_reference (return_type));
269 var_decl.add_declarator (new CCodeVariableDeclarator ("v_return"));
270 marshaller_body.add_statement (var_decl);
272 fc = new CCodeFunctionCall (new CCodeIdentifier ("g_return_if_fail"));
273 fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("return_value"), new CCodeConstant ("NULL")));
274 marshaller_body.add_statement (new CCodeExpressionStatement (fc));
277 fc = new CCodeFunctionCall (new CCodeIdentifier ("g_return_if_fail"));
278 fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("n_param_values"), new CCodeConstant (n_params.to_string())));
279 marshaller_body.add_statement (new CCodeExpressionStatement (fc));
281 var data = new CCodeMemberAccess (new CCodeIdentifier ("closure"), "data", true);
282 var param = new CCodeMemberAccess (new CCodeMemberAccess (new CCodeIdentifier ("param_values"), "data[0]", true), "v_pointer");
283 var cond = new CCodeFunctionCall (new CCodeConstant ("G_CCLOSURE_SWAP_DATA"));
284 cond.add_argument (new CCodeIdentifier ("closure"));
285 var true_block = new CCodeBlock ();
286 true_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("data1"), data)));
287 true_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("data2"), param)));
288 var false_block = new CCodeBlock ();
289 false_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("data1"), param)));
290 false_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("data2"), data)));
291 marshaller_body.add_statement (new CCodeIfStatement (cond, true_block, false_block));
293 var c_assign = new CCodeAssignment (new CCodeIdentifier ("callback"), new CCodeCastExpression (new CCodeConditionalExpression (new CCodeIdentifier ("marshal_data"), new CCodeIdentifier ("marshal_data"), new CCodeMemberAccess (new CCodeIdentifier ("cc"), "callback", true)), get_marshaller_function (params, return_type, "GMarshalFunc", dbus)));
294 marshaller_body.add_statement (new CCodeExpressionStatement (c_assign));
296 fc = new CCodeFunctionCall (new CCodeIdentifier ("callback"));
297 fc.add_argument (new CCodeIdentifier ("data1"));
298 i = 1;
299 foreach (Parameter p in params) {
300 string get_value_function;
301 bool is_array = p.variable_type.is_array ();
302 if (p.direction != ParameterDirection.IN) {
303 get_value_function = "g_value_get_pointer";
304 } else if (is_array) {
305 if (dbus) {
306 get_value_function = "g_value_get_boxed";
307 } else {
308 if (((ArrayType) p.variable_type).element_type.data_type == string_type.data_type) {
309 get_value_function = "g_value_get_boxed";
310 } else {
311 get_value_function = "g_value_get_pointer";
314 } else if (p.variable_type is PointerType || p.variable_type.type_parameter != null) {
315 get_value_function = "g_value_get_pointer";
316 } else if (p.variable_type is ErrorType) {
317 get_value_function = "g_value_get_pointer";
318 } else if (dbus && DBusModule.get_type_signature (p.variable_type).has_prefix ("(")) {
319 get_value_function = "g_value_get_boxed";
320 } else if (dbus && p.variable_type.data_type is Enum) {
321 var en = (Enum) p.variable_type.data_type;
322 if (en.is_flags) {
323 get_value_function = "g_value_get_uint";
324 } else {
325 get_value_function = "g_value_get_int";
327 } else {
328 get_value_function = p.variable_type.data_type.get_get_value_function ();
330 var inner_fc = new CCodeFunctionCall (new CCodeIdentifier (get_value_function));
331 inner_fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("param_values"), new CCodeIdentifier (i.to_string ())));
332 fc.add_argument (inner_fc);
333 i++;
334 if (is_array && !dbus) {
335 inner_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_get_int"));
336 inner_fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("param_values"), new CCodeIdentifier (i.to_string ())));
337 fc.add_argument (inner_fc);
338 i++;
341 fc.add_argument (new CCodeIdentifier ("data2"));
343 if (return_type.data_type != null || return_type.is_array ()) {
344 marshaller_body.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("v_return"), fc)));
346 CCodeFunctionCall set_fc;
347 if (return_type.is_array ()) {
348 if (dbus) {
349 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_take_boxed"));
350 } else {
351 if (((ArrayType) return_type).element_type.data_type == string_type.data_type) {
352 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_take_boxed"));
353 } else {
354 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_pointer"));
357 } else if (return_type.type_parameter != null) {
358 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_pointer"));
359 } else if (return_type is ErrorType) {
360 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_pointer"));
361 } else if (return_type.data_type == string_type.data_type) {
362 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_take_string"));
363 } else if (return_type.data_type is Class || return_type.data_type is Interface) {
364 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_take_object"));
365 } else if (dbus && DBusModule.get_type_signature (return_type).has_prefix ("(")) {
366 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_take_boxed"));
367 } else if (dbus && return_type.data_type is Enum) {
368 var en = (Enum) return_type.data_type;
369 if (en.is_flags) {
370 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_uint"));
371 } else {
372 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_int"));
374 } else {
375 set_fc = new CCodeFunctionCall (new CCodeIdentifier (return_type.data_type.get_set_value_function ()));
377 set_fc.add_argument (new CCodeIdentifier ("return_value"));
378 set_fc.add_argument (new CCodeIdentifier ("v_return"));
380 marshaller_body.add_statement (new CCodeExpressionStatement (set_fc));
381 } else {
382 marshaller_body.add_statement (new CCodeExpressionStatement (fc));
385 signal_marshaller.block = marshaller_body;
387 cfile.add_function (signal_marshaller);
388 user_marshal_set.add (signature);
391 public override CCodeFunctionCall get_signal_creation (Signal sig, TypeSymbol type) {
392 var csignew = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_new"));
393 var cl = sig.parent_symbol as Class;
394 csignew.add_argument (new CCodeConstant ("\"%s\"".printf (sig.get_cname ())));
395 csignew.add_argument (new CCodeIdentifier (type.get_type_id ()));
396 string[] flags = new string[0];
397 if (sig.run_type == "first") {
398 flags += "G_SIGNAL_RUN_FIRST";
399 } else if (sig.run_type == "cleanup") {
400 flags += "G_SIGNAL_RUN_CLEANUP";
401 } else {
402 flags += "G_SIGNAL_RUN_LAST";
404 if (sig.is_detailed) {
405 flags += "G_SIGNAL_DETAILED";
408 if (sig.no_recurse) {
409 flags += "G_SIGNAL_NO_RECURSE";
412 if (sig.is_action) {
413 flags += "G_SIGNAL_ACTION";
416 if (sig.no_hooks) {
417 flags += "G_SIGNAL_NO_HOOKS";
420 csignew.add_argument (new CCodeConstant (string.joinv (" | ", flags)));
422 if (sig.default_handler == null) {
423 csignew.add_argument (new CCodeConstant ("0"));
424 } else {
425 var struct_offset = new CCodeFunctionCall (new CCodeIdentifier ("G_STRUCT_OFFSET"));
426 struct_offset.add_argument (new CCodeIdentifier ("%sClass".printf (cl.get_cname ())));
427 struct_offset.add_argument (new CCodeIdentifier (sig.default_handler.vfunc_name));
428 csignew.add_argument (struct_offset);
430 csignew.add_argument (new CCodeConstant ("NULL"));
431 csignew.add_argument (new CCodeConstant ("NULL"));
433 string marshaller = get_marshaller_function (sig.get_parameters (), sig.return_type);
435 var marshal_arg = new CCodeIdentifier (marshaller);
436 csignew.add_argument (marshal_arg);
438 var params = sig.get_parameters ();
439 if (sig.return_type is PointerType || sig.return_type.type_parameter != null) {
440 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
441 } else if (sig.return_type is ErrorType) {
442 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
443 } else if (sig.return_type.data_type == null) {
444 csignew.add_argument (new CCodeConstant ("G_TYPE_NONE"));
445 } else {
446 csignew.add_argument (new CCodeConstant (sig.return_type.data_type.get_type_id ()));
449 int params_len = 0;
450 foreach (Parameter param in params) {
451 params_len++;
452 if (param.variable_type.is_array ()) {
453 params_len++;
457 csignew.add_argument (new CCodeConstant ("%d".printf (params_len)));
458 foreach (Parameter param in params) {
459 if (param.variable_type.is_array ()) {
460 if (((ArrayType) param.variable_type).element_type.data_type == string_type.data_type) {
461 csignew.add_argument (new CCodeConstant ("G_TYPE_STRV"));
462 } else {
463 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
465 csignew.add_argument (new CCodeConstant ("G_TYPE_INT"));
466 } else if (param.variable_type is PointerType || param.variable_type.type_parameter != null || param.direction != ParameterDirection.IN) {
467 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
468 } else if (param.variable_type is ErrorType) {
469 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
470 } else {
471 csignew.add_argument (new CCodeConstant (param.variable_type.data_type.get_type_id ()));
475 marshal_arg.name = marshaller;
477 return csignew;
480 public virtual CCodeExpression get_dbus_g_type (DataType data_type) {
481 return new CCodeConstant (data_type.data_type.get_type_id ());
484 public override void visit_element_access (ElementAccess expr) {
485 if (expr.container is MemberAccess && expr.container.symbol_reference is Signal) {
486 // detailed signal emission
487 var sig = (Signal) expr.symbol_reference;
488 var ma = (MemberAccess) expr.container;
490 var detail_expr = expr.get_indices ().get (0);
491 var signal_name_cexpr = get_signal_name_cexpression (sig, detail_expr, expr);
493 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_emit_by_name"));
494 ccall.add_argument (get_cvalue (ma.inner));
495 if (signal_name_cexpr != null) {
496 ccall.add_argument (signal_name_cexpr);
498 set_cvalue (expr, ccall);
499 } else {
500 base.visit_element_access (expr);
504 bool in_gobject_instance (Method m) {
505 bool result = false;
506 if (m.binding == MemberBinding.INSTANCE) {
507 result = m.this_parameter.variable_type.data_type.is_subtype_of (gobject_type);
509 return result;
512 CCodeExpression? emit_signal_assignment (Assignment assignment) {
513 var sig = (Signal) assignment.left.symbol_reference;
515 bool disconnect = false;
517 if (assignment.operator == AssignmentOperator.ADD) {
518 // connect
519 } else if (assignment.operator == AssignmentOperator.SUB) {
520 // disconnect
521 disconnect = true;
522 } else {
523 assignment.error = true;
524 Report.error (assignment.source_reference, "Specified compound assignment type for signals not supported.");
525 return null;
528 return connect_signal (sig, assignment.left, assignment.right, disconnect, false, assignment);
531 public override void visit_assignment (Assignment assignment) {
532 if (assignment.left.symbol_reference is Signal) {
533 if (assignment.left.error || assignment.right.error) {
534 assignment.error = true;
535 return;
538 set_cvalue (assignment, emit_signal_assignment (assignment));
539 } else {
540 base.visit_assignment (assignment);
544 public override void visit_member_access (MemberAccess expr) {
545 if (expr.symbol_reference is Signal) {
546 CCodeExpression pub_inst = null;
548 if (expr.inner != null) {
549 pub_inst = get_cvalue (expr.inner);
552 var sig = (Signal) expr.symbol_reference;
553 var cl = (TypeSymbol) sig.parent_symbol;
555 if (expr.inner is BaseAccess && sig.is_virtual) {
556 var m = sig.default_handler;
557 var base_class = (Class) m.parent_symbol;
558 var vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_CLASS".printf (base_class.get_upper_case_cname (null))));
559 vcast.add_argument (new CCodeIdentifier ("%s_parent_class".printf (current_class.get_lower_case_cname (null))));
561 set_cvalue (expr, new CCodeMemberAccess.pointer (vcast, m.name));
562 return;
565 if (sig.has_emitter) {
566 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("%s_%s".printf (cl.get_lower_case_cname (null), sig.name)));
568 ccall.add_argument (pub_inst);
569 set_cvalue (expr, ccall);
570 } else {
571 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_emit_by_name"));
572 ccall.add_argument (pub_inst);
574 ccall.add_argument (sig.get_canonical_cconstant ());
576 set_cvalue (expr, ccall);
578 } else {
579 base.visit_member_access (expr);
583 public override void visit_method_call (MethodCall expr) {
584 var method_type = expr.call.value_type as MethodType;
586 if (method_type == null || !(method_type.method_symbol.parent_symbol is Signal)) {
587 // no signal connect/disconnect call
588 base.visit_method_call (expr);
589 return;
592 var sig = (Signal) method_type.method_symbol.parent_symbol;
593 var signal_access = ((MemberAccess) expr.call).inner;
594 var handler = expr.get_argument_list ().get (0);
596 bool disconnect = (method_type.method_symbol.name == "disconnect");
597 bool after = (method_type.method_symbol.name == "connect_after");
599 set_cvalue (expr, connect_signal (sig, signal_access, handler, disconnect, after, expr));
602 CCodeExpression? connect_signal (Signal sig, Expression signal_access, Expression handler, bool disconnect, bool after, CodeNode expr) {
603 string connect_func;
605 var m = (Method) handler.symbol_reference;
607 if (!disconnect) {
608 // connect
609 if (sig is DynamicSignal) {
610 if (!after)
611 connect_func = get_dynamic_signal_connect_wrapper_name ((DynamicSignal) sig);
612 else
613 connect_func = get_dynamic_signal_connect_after_wrapper_name ((DynamicSignal) sig);
614 } else {
615 if (m.closure) {
616 connect_func = "g_signal_connect_data";
617 } else if (in_gobject_instance (m)) {
618 connect_func = "g_signal_connect_object";
619 } else if (!after) {
620 connect_func = "g_signal_connect";
621 } else
622 connect_func = "g_signal_connect_after";
624 } else {
625 // disconnect
626 if (handler is LambdaExpression) {
627 Report.error (handler.source_reference, "Cannot disconnect lambda expression from signal. Use Object.disconnect.");
629 if (sig is DynamicSignal) {
630 connect_func = get_dynamic_signal_disconnect_wrapper_name ((DynamicSignal) sig);
631 } else {
632 connect_func = "g_signal_handlers_disconnect_matched";
636 var ccall = new CCodeFunctionCall (new CCodeIdentifier (connect_func));
638 CCodeExpression signal_name_cexpr = null;
640 // first argument: instance of sender
641 MemberAccess ma;
642 if (signal_access is ElementAccess) {
643 var ea = (ElementAccess) signal_access;
644 ma = (MemberAccess) ea.container;
645 var detail_expr = ea.get_indices ().get (0);
646 signal_name_cexpr = get_signal_name_cexpression (sig, detail_expr, expr);
647 if (signal_name_cexpr == null) {
648 return null;
650 } else {
651 ma = (MemberAccess) signal_access;
652 signal_name_cexpr = get_signal_name_cexpression (sig, null, expr);
654 if (ma.inner != null) {
655 ccall.add_argument ((CCodeExpression) get_ccodenode (ma.inner));
656 } else {
657 ccall.add_argument (get_result_cexpression ("self"));
660 CCodeCommaExpression? ccomma = null;
662 if (sig is DynamicSignal) {
663 // dynamic_signal_connect or dynamic_signal_disconnect
665 // second argument: signal name
666 ccall.add_argument (new CCodeConstant ("\"%s\"".printf (sig.name)));
667 } else if (!disconnect) {
668 // g_signal_connect_object or g_signal_connect
670 // second argument: signal name
671 ccall.add_argument (signal_name_cexpr);
672 } else {
673 // g_signal_handlers_disconnect_matched
675 // second argument: mask
676 if (!(signal_access is ElementAccess)) {
677 ccall.add_argument (new CCodeConstant ("G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA"));
678 } else {
679 ccall.add_argument (new CCodeConstant ("G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA"));
682 // get signal id
683 ccomma = new CCodeCommaExpression ();
684 var temp_decl = get_temp_variable (uint_type);
685 emit_temp_var (temp_decl);
686 var parse_call = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_parse_name"));
687 parse_call.add_argument (signal_name_cexpr);
688 var decl_type = (TypeSymbol) sig.parent_symbol;
689 parse_call.add_argument (new CCodeIdentifier (decl_type.get_type_id ()));
690 parse_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (temp_decl.name)));
691 LocalVariable? detail_temp_decl = null;
692 if (!(signal_access is ElementAccess)) {
693 parse_call.add_argument (new CCodeConstant ("NULL"));
694 parse_call.add_argument (new CCodeConstant ("FALSE"));
695 } else {
696 detail_temp_decl = get_temp_variable (gquark_type);
697 emit_temp_var (detail_temp_decl);
698 parse_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (detail_temp_decl.name)));
699 parse_call.add_argument (new CCodeConstant ("TRUE"));
701 ccomma.append_expression (parse_call);
703 // third argument: signal_id
704 ccall.add_argument (get_variable_cexpression (temp_decl.name));
706 // fourth argument: detail
707 if (detail_temp_decl == null) {
708 ccall.add_argument (new CCodeConstant ("0"));
709 } else {
710 ccall.add_argument (get_variable_cexpression (detail_temp_decl.name));
712 // fifth argument: closure
713 ccall.add_argument (new CCodeConstant ("NULL"));
716 // third resp. sixth argument: handler
717 ccall.add_argument (new CCodeCastExpression (get_cvalue (handler), "GCallback"));
719 if (m.closure) {
720 // g_signal_connect_data
722 // fourth argument: user_data
723 CCodeExpression handler_destroy_notify;
724 ccall.add_argument (get_delegate_target_cexpression (handler, out handler_destroy_notify));
726 // fifth argument: destroy_notify
727 ccall.add_argument (new CCodeCastExpression (handler_destroy_notify, "GClosureNotify"));
729 // sixth argument: connect_flags
730 if (!after)
731 ccall.add_argument (new CCodeConstant ("0"));
732 else
733 ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
734 } else if (m.binding == MemberBinding.INSTANCE) {
735 // g_signal_connect_object or g_signal_handlers_disconnect_matched
736 // or dynamic_signal_connect or dynamic_signal_disconnect
738 // fourth resp. seventh argument: object/user_data
739 if (handler is MemberAccess) {
740 var right_ma = (MemberAccess) handler;
741 if (right_ma.inner != null) {
742 ccall.add_argument (get_cvalue (right_ma.inner));
743 } else {
744 ccall.add_argument (get_result_cexpression ("self"));
746 } else if (handler is LambdaExpression) {
747 ccall.add_argument (get_result_cexpression ("self"));
749 if (!disconnect && !(sig is DynamicSignal)
750 && in_gobject_instance (m)) {
751 // g_signal_connect_object
753 // fifth argument: connect_flags
754 if (!after)
755 ccall.add_argument (new CCodeConstant ("0"));
756 else
757 ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
759 } else {
760 // g_signal_connect or g_signal_connect_after or g_signal_handlers_disconnect_matched
761 // or dynamic_signal_connect or dynamic_signal_disconnect
763 // fourth resp. seventh argument: user_data
764 ccall.add_argument (new CCodeConstant ("NULL"));
767 if (ccomma != null) {
768 ccomma.append_expression (ccall);
769 return ccomma;
770 } else {
771 return ccall;