]> git.0d.be Git - empathy.git/blob - tools/glib-client-gen.py
fede212dca495e268f76342eb192dffbae2bc859
[empathy.git] / tools / glib-client-gen.py
1 #!/usr/bin/python
2
3 # glib-client-gen.py: "I Can't Believe It's Not dbus-binding-tool"
4 #
5 # Generate GLib client wrappers from the Telepathy specification.
6 # The master copy of this program is in the telepathy-glib repository -
7 # please make any changes there.
8 #
9 # Copyright (C) 2006-2008 Collabora Ltd. <http://www.collabora.co.uk/>
10 #
11 # This library is free software; you can redistribute it and/or
12 # modify it under the terms of the GNU Lesser General Public
13 # License as published by the Free Software Foundation; either
14 # version 2.1 of the License, or (at your option) any later version.
15 #
16 # This library is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 # Lesser General Public License for more details.
20 #
21 # You should have received a copy of the GNU Lesser General Public
22 # License along with this library; if not, write to the Free Software
23 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
24
25 import sys
26 import os.path
27 import xml.dom.minidom
28 from getopt import gnu_getopt
29
30 from libglibcodegen import Signature, type_to_gtype, cmp_by_name, \
31         camelcase_to_lower, get_docstring, xml_escape
32
33
34 NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
35
36 class Generator(object):
37
38     def __init__(self, dom, prefix, basename, opts):
39         self.dom = dom
40         self.__header = []
41         self.__body = []
42
43         self.prefix_lc = prefix.lower()
44         self.prefix_uc = prefix.upper()
45         self.prefix_mc = prefix.replace('_', '')
46         self.basename = basename
47         self.group = opts.get('--group', None)
48         self.iface_quark_prefix = opts.get('--iface-quark-prefix', None)
49         self.tp_proxy_api = tuple(map(int,
50                 opts.get('--tp-proxy-api', '0').split('.')))
51         self.proxy_cls = opts.get('--subclass', 'TpProxy') + ' *'
52         self.proxy_arg = opts.get('--subclass', 'void') + ' *'
53         self.proxy_assert = opts.get('--subclass-assert', 'TP_IS_PROXY')
54         self.proxy_doc = ('A #%s or subclass'
55             % opts.get('--subclass', 'TpProxy'))
56         if self.proxy_arg == 'void *':
57             self.proxy_arg = 'gpointer '
58
59     def h(self, s):
60         self.__header.append(s)
61
62     def b(self, s):
63         self.__body.append(s)
64
65     def get_iface_quark(self):
66         assert self.iface_dbus is not None
67         assert self.iface_uc is not None
68         if self.iface_quark_prefix is None:
69             return 'g_quark_from_static_string (\"%s\")' % self.iface_dbus
70         else:
71             return '%s_%s' % (self.iface_quark_prefix, self.iface_uc)
72
73     def do_signal(self, iface, signal):
74         iface_lc = iface.lower()
75
76         member = signal.getAttribute('name')
77         member_lc = camelcase_to_lower(member)
78         member_uc = member_lc.upper()
79
80         arg_count = 0
81         args = []
82         out_args = []
83
84         for arg in signal.getElementsByTagName('arg'):
85             name = arg.getAttribute('name')
86             type = arg.getAttribute('type')
87             tp_type = arg.getAttribute('tp:type')
88
89             if not name:
90                 name = 'arg%u' % arg_count
91                 arg_count += 1
92             else:
93                 name = 'arg_%s' % name
94
95             info = type_to_gtype(type)
96             args.append((name, info, tp_type, arg))
97
98         callback_name = ('%s_%s_signal_callback_%s'
99                          % (self.prefix_lc, iface_lc, member_lc))
100         collect_name = ('_%s_%s_collect_args_of_%s'
101                         % (self.prefix_lc, iface_lc, member_lc))
102         invoke_name = ('_%s_%s_invoke_callback_for_%s'
103                        % (self.prefix_lc, iface_lc, member_lc))
104
105         # Example:
106         #
107         # typedef void (*tp_cli_connection_signal_callback_new_channel)
108         #   (TpConnection *proxy, const gchar *arg_object_path,
109         #   const gchar *arg_channel_type, guint arg_handle_type,
110         #   guint arg_handle, gboolean arg_suppress_handler,
111         #   gpointer user_data, GObject *weak_object);
112
113         self.b('/**')
114         self.b(' * %s:' % callback_name)
115         self.b(' * @proxy: The proxy on which %s_%s_connect_to_%s ()'
116                % (self.prefix_lc, iface_lc, member_lc))
117         self.b(' *  was called')
118
119         for arg in args:
120             name, info, tp_type, elt = arg
121             ctype, gtype, marshaller, pointer = info
122
123             self.b(' * @%s: %s' % (name,
124                 xml_escape(get_docstring(elt) or '(Undocumented)')))
125
126         self.b(' * @user_data: User-supplied data')
127         self.b(' * @weak_object: User-supplied weakly referenced object')
128         self.b(' *')
129         self.b(' * Represents the signature of a callback for the signal %s.'
130                % member)
131         self.b(' */')
132         self.h('typedef void (*%s) (%sproxy,'
133                % (callback_name, self.proxy_cls))
134
135         for arg in args:
136             name, info, tp_type, elt = arg
137             ctype, gtype, marshaller, pointer = info
138
139             const = pointer and 'const ' or ''
140
141             self.h('    %s%s%s,' % (const, ctype, name))
142
143         self.h('    gpointer user_data, GObject *weak_object);')
144
145         if args:
146             self.b('static void')
147             self.b('%s (DBusGProxy *proxy G_GNUC_UNUSED,' % collect_name)
148
149             for arg in args:
150                 name, info, tp_type, elt = arg
151                 ctype, gtype, marshaller, pointer = info
152
153                 const = pointer and 'const ' or ''
154
155                 self.b('    %s%s%s,' % (const, ctype, name))
156
157             self.b('    TpProxySignalConnection *sc)')
158             self.b('{')
159             self.b('  GValueArray *args = g_value_array_new (%d);' % len(args))
160             self.b('  GValue blank = { 0 };')
161             self.b('  guint i;')
162             self.b('')
163             self.b('  g_value_init (&blank, G_TYPE_INT);')
164             self.b('')
165             self.b('  for (i = 0; i < %d; i++)' % len(args))
166             self.b('    g_value_array_append (args, &blank);')
167             self.b('')
168
169             for i, arg in enumerate(args):
170                 name, info, tp_type, elt = arg
171                 ctype, gtype, marshaller, pointer = info
172
173                 self.b('  g_value_unset (args->values + %d);' % i)
174                 self.b('  g_value_init (args->values + %d, %s);' % (i, gtype))
175
176                 if gtype == 'G_TYPE_STRING':
177                     self.b('  g_value_set_string (args->values + %d, %s);'
178                            % (i, name))
179                 elif marshaller == 'BOXED':
180                     self.b('  g_value_set_boxed (args->values + %d, %s);'
181                            % (i, name))
182                 elif gtype == 'G_TYPE_UCHAR':
183                     self.b('  g_value_set_uchar (args->values + %d, %s);'
184                            % (i, name))
185                 elif gtype == 'G_TYPE_BOOLEAN':
186                     self.b('  g_value_set_boolean (args->values + %d, %s);'
187                            % (i, name))
188                 elif gtype == 'G_TYPE_INT':
189                     self.b('  g_value_set_int (args->values + %d, %s);'
190                            % (i, name))
191                 elif gtype == 'G_TYPE_UINT':
192                     self.b('  g_value_set_uint (args->values + %d, %s);'
193                            % (i, name))
194                 elif gtype == 'G_TYPE_INT64':
195                     self.b('  g_value_set_int (args->values + %d, %s);'
196                            % (i, name))
197                 elif gtype == 'G_TYPE_UINT64':
198                     self.b('  g_value_set_uint (args->values + %d, %s);'
199                            % (i, name))
200                 elif gtype == 'G_TYPE_DOUBLE':
201                     self.b('  g_value_set_double (args->values + %d, %s);'
202                            % (i, name))
203                 else:
204                     assert False, ("Don't know how to put %s in a GValue"
205                                    % gtype)
206                 self.b('')
207
208             self.b('  tp_proxy_signal_connection_v0_take_results (sc, args);')
209             self.b('}')
210
211         self.b('static void')
212         self.b('%s (TpProxy *tpproxy,' % invoke_name)
213         self.b('    GError *error G_GNUC_UNUSED,')
214         self.b('    GValueArray *args,')
215         self.b('    GCallback generic_callback,')
216         self.b('    gpointer user_data,')
217         self.b('    GObject *weak_object)')
218         self.b('{')
219         self.b('  %s callback =' % callback_name)
220         self.b('      (%s) generic_callback;' % callback_name)
221         self.b('')
222         self.b('  if (callback != NULL)')
223         self.b('    callback (g_object_ref (tpproxy),')
224
225         # FIXME: factor out into a function
226         for i, arg in enumerate(args):
227             name, info, tp_type, elt = arg
228             ctype, gtype, marshaller, pointer = info
229
230             if marshaller == 'BOXED':
231                 self.b('      g_value_get_boxed (args->values + %d),' % i)
232             elif gtype == 'G_TYPE_STRING':
233                 self.b('      g_value_get_string (args->values + %d),' % i)
234             elif gtype == 'G_TYPE_UCHAR':
235                 self.b('      g_value_get_uchar (args->values + %d),' % i)
236             elif gtype == 'G_TYPE_BOOLEAN':
237                 self.b('      g_value_get_boolean (args->values + %d),' % i)
238             elif gtype == 'G_TYPE_UINT':
239                 self.b('      g_value_get_uint (args->values + %d),' % i)
240             elif gtype == 'G_TYPE_INT':
241                 self.b('      g_value_get_int (args->values + %d),' % i)
242             elif gtype == 'G_TYPE_UINT64':
243                 self.b('      g_value_get_uint64 (args->values + %d),' % i)
244             elif gtype == 'G_TYPE_INT64':
245                 self.b('      g_value_get_int64 (args->values + %d),' % i)
246             elif gtype == 'G_TYPE_DOUBLE':
247                 self.b('      g_value_get_double (args->values + %d),' % i)
248             else:
249                 assert False, "Don't know how to get %s from a GValue" % gtype
250
251         self.b('      user_data,')
252         self.b('      weak_object);')
253         self.b('')
254
255         if len(args) > 0:
256             self.b('  g_value_array_free (args);')
257         else:
258             self.b('  if (args != NULL)')
259             self.b('    g_value_array_free (args);')
260             self.b('')
261
262         self.b('  g_object_unref (tpproxy);')
263         self.b('}')
264
265         # Example:
266         #
267         # TpProxySignalConnection *
268         #   tp_cli_connection_connect_to_new_channel
269         #   (TpConnection *proxy,
270         #   tp_cli_connection_signal_callback_new_channel callback,
271         #   gpointer user_data,
272         #   GDestroyNotify destroy);
273         #
274         # destroy is invoked when the signal becomes disconnected. This
275         # is either because the signal has been disconnected explicitly
276         # by the user, because the TpProxy has become invalid and
277         # emitted the 'invalidated' signal, or because the weakly referenced
278         # object has gone away.
279
280         self.b('/**')
281         self.b(' * %s_%s_connect_to_%s:'
282                % (self.prefix_lc, iface_lc, member_lc))
283         self.b(' * @proxy: %s' % self.proxy_doc)
284         self.b(' * @callback: Callback to be called when the signal is')
285         self.b(' *   received')
286         self.b(' * @user_data: User-supplied data for the callback')
287         self.b(' * @destroy: Destructor for the user-supplied data, which')
288         self.b(' *   will be called when this signal is disconnected, or')
289         self.b(' *   before this function returns %NULL')
290         self.b(' * @weak_object: A #GObject which will be weakly referenced; ')
291         self.b(' *   if it is destroyed, this callback will automatically be')
292         self.b(' *   disconnected')
293         self.b(' * @error: If not %NULL, used to raise an error if %NULL is')
294         self.b(' *   returned')
295         self.b(' *')
296         self.b(' * Connect a handler to the signal %s.' % member)
297         self.b(' *')
298         self.b(' * %s' % xml_escape(get_docstring(signal) or '(Undocumented)'))
299         self.b(' *')
300         self.b(' * Returns: a #TpProxySignalConnection containing all of the')
301         self.b(' * above, which can be used to disconnect the signal; or')
302         self.b(' * %NULL if the proxy does not have the desired interface')
303         self.b(' * or has become invalid.')
304         self.b(' */')
305         self.h('TpProxySignalConnection *%s_%s_connect_to_%s (%sproxy,'
306                % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg))
307         self.h('    %s callback,' % callback_name)
308         self.h('    gpointer user_data,')
309         self.h('    GDestroyNotify destroy,')
310         self.h('    GObject *weak_object,')
311         self.h('    GError **error);')
312
313         self.b('TpProxySignalConnection *')
314         self.b('%s_%s_connect_to_%s (%sproxy,'
315                % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg))
316         self.b('    %s callback,' % callback_name)
317         self.b('    gpointer user_data,')
318         self.b('    GDestroyNotify destroy,')
319         self.b('    GObject *weak_object,')
320         self.b('    GError **error)')
321         self.b('{')
322         self.b('  GType expected_types[%d] = {' % (len(args) + 1))
323
324         for arg in args:
325             name, info, tp_type, elt = arg
326             ctype, gtype, marshaller, pointer = info
327
328             self.b('      %s,' % gtype)
329
330         self.b('      G_TYPE_INVALID };')
331         self.b('')
332         self.b('  g_return_val_if_fail (%s (proxy), NULL);'
333                % self.proxy_assert)
334         self.b('  g_return_val_if_fail (callback != NULL, NULL);')
335         self.b('')
336         self.b('  return tp_proxy_signal_connection_v0_new ((TpProxy *) proxy,')
337         self.b('      %s, \"%s\",' % (self.get_iface_quark(), member))
338         self.b('      expected_types,')
339
340         if args:
341             self.b('      G_CALLBACK (%s),' % collect_name)
342         else:
343             self.b('      NULL, /* no args => no collector function */')
344
345         self.b('      %s,' % invoke_name)
346         self.b('      G_CALLBACK (callback), user_data, destroy,')
347         self.b('      weak_object, error);')
348         self.b('}')
349         self.b('')
350
351         self.h('')
352
353     def do_method(self, iface, method):
354         iface_lc = iface.lower()
355
356         member = method.getAttribute('name')
357         member_lc = camelcase_to_lower(member)
358         member_uc = member_lc.upper()
359
360         in_count = 0
361         ret_count = 0
362         in_args = []
363         out_args = []
364
365         for arg in method.getElementsByTagName('arg'):
366             name = arg.getAttribute('name')
367             direction = arg.getAttribute('direction')
368             type = arg.getAttribute('type')
369             tp_type = arg.getAttribute('tp:type')
370
371             if direction != 'out':
372                 if not name:
373                     name = 'in%u' % in_count
374                     in_count += 1
375                 else:
376                     name = 'in_%s' % name
377             else:
378                 if not name:
379                     name = 'out%u' % ret_count
380                     ret_count += 1
381                 else:
382                     name = 'out_%s' % name
383
384             info = type_to_gtype(type)
385             if direction != 'out':
386                 in_args.append((name, info, tp_type, arg))
387             else:
388                 out_args.append((name, info, tp_type, arg))
389
390         # Async reply callback type
391
392         # Example:
393         # void (*tp_cli_properties_interface_callback_for_get_properties)
394         #   (TpProxy *proxy,
395         #       const GPtrArray *out0,
396         #       const GError *error,
397         #       gpointer user_data,
398         #       GObject *weak_object);
399
400         self.b('/**')
401         self.b(' * %s_%s_callback_for_%s:'
402                % (self.prefix_lc, iface_lc, member_lc))
403         self.b(' * @proxy: the proxy on which the call was made')
404
405         for arg in out_args:
406             name, info, tp_type, elt = arg
407             ctype, gtype, marshaller, pointer = info
408
409             self.b(' * @%s: Used to return an \'out\' argument if @error is '
410                    '%%NULL: %s'
411                    % (name, xml_escape(get_docstring(elt) or '(Undocumented)')))
412
413         self.b(' * @error: %NULL on success, or an error on failure')
414         self.b(' * @user_data: user-supplied data')
415         self.b(' * @weak_object: user-supplied object')
416         self.b(' *')
417         self.b(' * Signature of the callback called when a %s method call'
418                % member)
419         self.b(' * succeeds or fails.')
420         self.b(' */')
421
422         callback_name = '%s_%s_callback_for_%s' % (self.prefix_lc, iface_lc,
423                                                    member_lc)
424
425         self.h('typedef void (*%s) (%sproxy,'
426                % (callback_name, self.proxy_cls))
427
428         for arg in out_args:
429             name, info, tp_type, elt = arg
430             ctype, gtype, marshaller, pointer = info
431             const = pointer and 'const ' or ''
432
433             self.h('    %s%s%s,' % (const, ctype, name))
434
435         self.h('    const GError *error, gpointer user_data,')
436         self.h('    GObject *weak_object);')
437         self.h('')
438
439         # Async callback implementation
440
441         invoke_callback = '_%s_%s_invoke_callback_%s' % (self.prefix_lc,
442                                                          iface_lc,
443                                                          member_lc)
444
445         collect_callback = '_%s_%s_collect_callback_%s' % (self.prefix_lc,
446                                                            iface_lc,
447                                                            member_lc)
448
449         # The callback called by dbus-glib; this ends the call and collects
450         # the results into a GValueArray.
451         self.b('static void')
452         self.b('%s (DBusGProxy *proxy,' % collect_callback)
453         self.b('    DBusGProxyCall *call,')
454         self.b('    gpointer user_data)')
455         self.b('{')
456         self.b('  GError *error = NULL;')
457
458         if len(out_args) > 0:
459             self.b('  GValueArray *args;')
460             self.b('  GValue blank = { 0 };')
461             self.b('  guint i;')
462
463             for arg in out_args:
464                 name, info, tp_type, elt = arg
465                 ctype, gtype, marshaller, pointer = info
466
467                 # "We handle variants specially; the caller is expected to
468                 # have already allocated storage for them". Thanks,
469                 # dbus-glib...
470                 if gtype == 'G_TYPE_VALUE':
471                     self.b('  GValue *%s = g_new0 (GValue, 1);' % name)
472                 else:
473                     self.b('  %s%s;' % (ctype, name))
474
475         self.b('')
476         self.b('  dbus_g_proxy_end_call (proxy, call, &error,')
477
478         for arg in out_args:
479             name, info, tp_type, elt = arg
480             ctype, gtype, marshaller, pointer = info
481
482             if gtype == 'G_TYPE_VALUE':
483                 self.b('      %s, %s,' % (gtype, name))
484             else:
485                 self.b('      %s, &%s,' % (gtype, name))
486
487         self.b('      G_TYPE_INVALID);')
488
489         if len(out_args) == 0:
490             self.b('  tp_proxy_pending_call_v0_take_results (user_data, error,'
491                    'NULL);')
492         else:
493             self.b('')
494             self.b('  if (error != NULL)')
495             self.b('    {')
496             self.b('      tp_proxy_pending_call_v0_take_results (user_data, error,')
497             self.b('          NULL);')
498
499             for arg in out_args:
500                 name, info, tp_type, elt = arg
501                 ctype, gtype, marshaller, pointer = info
502                 if gtype == 'G_TYPE_VALUE':
503                     self.b('      g_free (%s);' % name)
504
505             self.b('      return;')
506             self.b('    }')
507             self.b('')
508             self.b('  args = g_value_array_new (%d);' % len(out_args))
509             self.b('  g_value_init (&blank, G_TYPE_INT);')
510             self.b('')
511             self.b('  for (i = 0; i < %d; i++)' % len(out_args))
512             self.b('    g_value_array_append (args, &blank);')
513
514             for i, arg in enumerate(out_args):
515                 name, info, tp_type, elt = arg
516                 ctype, gtype, marshaller, pointer = info
517
518                 self.b('')
519                 self.b('  g_value_unset (args->values + %d);' % i)
520                 self.b('  g_value_init (args->values + %d, %s);' % (i, gtype))
521
522                 if gtype == 'G_TYPE_STRING':
523                     self.b('  g_value_take_string (args->values + %d, %s);'
524                            % (i, name))
525                 elif marshaller == 'BOXED':
526                     self.b('  g_value_take_boxed (args->values + %d, %s);'
527                             % (i, name))
528                 elif gtype == 'G_TYPE_UCHAR':
529                     self.b('  g_value_set_uchar (args->values + %d, %s);'
530                             % (i, name))
531                 elif gtype == 'G_TYPE_BOOLEAN':
532                     self.b('  g_value_set_boolean (args->values + %d, %s);'
533                             % (i, name))
534                 elif gtype == 'G_TYPE_INT':
535                     self.b('  g_value_set_int (args->values + %d, %s);'
536                             % (i, name))
537                 elif gtype == 'G_TYPE_UINT':
538                     self.b('  g_value_set_uint (args->values + %d, %s);'
539                             % (i, name))
540                 elif gtype == 'G_TYPE_INT64':
541                     self.b('  g_value_set_int (args->values + %d, %s);'
542                             % (i, name))
543                 elif gtype == 'G_TYPE_UINT64':
544                     self.b('  g_value_set_uint (args->values + %d, %s);'
545                             % (i, name))
546                 elif gtype == 'G_TYPE_DOUBLE':
547                     self.b('  g_value_set_double (args->values + %d, %s);'
548                             % (i, name))
549                 else:
550                     assert False, ("Don't know how to put %s in a GValue"
551                                    % gtype)
552
553             self.b('  tp_proxy_pending_call_v0_take_results (user_data, '
554                    'NULL, args);')
555
556         self.b('}')
557
558         self.b('static void')
559         self.b('%s (TpProxy *self,' % invoke_callback)
560         self.b('    GError *error,')
561         self.b('    GValueArray *args,')
562         self.b('    GCallback generic_callback,')
563         self.b('    gpointer user_data,')
564         self.b('    GObject *weak_object)')
565         self.b('{')
566         self.b('  %s callback = (%s) generic_callback;'
567                % (callback_name, callback_name))
568         self.b('')
569         self.b('  if (error != NULL)')
570         self.b('    {')
571         self.b('      callback ((%s) self,' % self.proxy_cls)
572
573         for arg in out_args:
574             name, info, tp_type, elt = arg
575             ctype, gtype, marshaller, pointer = info
576
577             if marshaller == 'BOXED' or pointer:
578                 self.b('          NULL,')
579             elif gtype == 'G_TYPE_DOUBLE':
580                 self.b('          0.0,')
581             else:
582                 self.b('          0,')
583
584         self.b('          error, user_data, weak_object);')
585         self.b('      g_error_free (error);')
586         self.b('      return;')
587         self.b('    }')
588
589         self.b('  callback ((%s) self,' % self.proxy_cls)
590
591         # FIXME: factor out into a function
592         for i, arg in enumerate(out_args):
593             name, info, tp_type, elt = arg
594             ctype, gtype, marshaller, pointer = info
595
596             if marshaller == 'BOXED':
597                 self.b('      g_value_get_boxed (args->values + %d),' % i)
598             elif gtype == 'G_TYPE_STRING':
599                 self.b('      g_value_get_string (args->values + %d),' % i)
600             elif gtype == 'G_TYPE_UCHAR':
601                 self.b('      g_value_get_uchar (args->values + %d),' % i)
602             elif gtype == 'G_TYPE_BOOLEAN':
603                 self.b('      g_value_get_boolean (args->values + %d),' % i)
604             elif gtype == 'G_TYPE_UINT':
605                 self.b('      g_value_get_uint (args->values + %d),' % i)
606             elif gtype == 'G_TYPE_INT':
607                 self.b('      g_value_get_int (args->values + %d),' % i)
608             elif gtype == 'G_TYPE_UINT64':
609                 self.b('      g_value_get_uint64 (args->values + %d),' % i)
610             elif gtype == 'G_TYPE_INT64':
611                 self.b('      g_value_get_int64 (args->values + %d),' % i)
612             elif gtype == 'G_TYPE_DOUBLE':
613                 self.b('      g_value_get_double (args->values + %d),' % i)
614             else:
615                 assert False, "Don't know how to get %s from a GValue" % gtype
616
617         self.b('      error, user_data, weak_object);')
618         self.b('')
619
620         if len(out_args) > 0:
621             self.b('  g_value_array_free (args);')
622         else:
623             self.b('  if (args != NULL)')
624             self.b('    g_value_array_free (args);')
625
626         self.b('}')
627         self.b('')
628
629         # Async stub
630
631         # Example:
632         # TpProxyPendingCall *
633         #   tp_cli_properties_interface_call_get_properties
634         #   (gpointer proxy,
635         #   gint timeout_ms,
636         #   const GArray *in_properties,
637         #   tp_cli_properties_interface_callback_for_get_properties callback,
638         #   gpointer user_data,
639         #   GDestroyNotify *destructor);
640
641         self.h('TpProxyPendingCall *%s_%s_call_%s (%sproxy,'
642                % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg))
643         self.h('    gint timeout_ms,')
644
645         self.b('/**')
646         self.b(' * %s_%s_call_%s:'
647                % (self.prefix_lc, iface_lc, member_lc))
648         self.b(' * @proxy: the #TpProxy')
649         self.b(' * @timeout_ms: the timeout in milliseconds, or -1 to use the')
650         self.b(' *   default')
651
652         for arg in in_args:
653             name, info, tp_type, elt = arg
654             ctype, gtype, marshaller, pointer = info
655
656             self.b(' * @%s: Used to pass an \'in\' argument: %s'
657                    % (name, xml_escape(get_docstring(elt) or '(Undocumented)')))
658
659         self.b(' * @callback: called when the method call succeeds or fails')
660         self.b(' * @user_data: user-supplied data passed to the callback')
661         self.b(' * @destroy: called with the user_data as argument, after the')
662         self.b(' *   call has succeeded, failed or been cancelled')
663         self.b(' * @weak_object: A #GObject which will be weakly referenced; ')
664         self.b(' *   if it is destroyed, this callback will automatically be')
665         self.b(' *   disconnected')
666         self.b(' *')
667         self.b(' * Start a %s method call.' % member)
668         self.b(' *')
669         self.b(' * %s' % xml_escape(get_docstring(method) or '(Undocumented)'))
670         self.b(' *')
671         self.b(' * Returns: a #TpProxyPendingCall representing the call in')
672         self.b(' *  progress. It is borrowed from the object, and will become')
673         self.b(' *  invalid when the callback is called, the call is')
674         self.b(' *  cancelled or the #TpProxy becomes invalid.')
675         self.b(' */')
676         self.b('TpProxyPendingCall *\n%s_%s_call_%s (%sproxy,'
677                % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg))
678         self.b('    gint timeout_ms,')
679
680         for arg in in_args:
681             name, info, tp_type, elt = arg
682             ctype, gtype, marshaller, pointer = info
683
684             const = pointer and 'const ' or ''
685
686             self.h('    %s%s%s,' % (const, ctype, name))
687             self.b('    %s%s%s,' % (const, ctype, name))
688
689         self.h('    %s callback,' % callback_name)
690         self.h('    gpointer user_data,')
691         self.h('    GDestroyNotify destroy,')
692         self.h('    GObject *weak_object);')
693         self.h('')
694
695         self.b('    %s callback,' % callback_name)
696         self.b('    gpointer user_data,')
697         self.b('    GDestroyNotify destroy,')
698         self.b('    GObject *weak_object)')
699         self.b('{')
700         self.b('  GError *error = NULL;')
701         self.b('  GQuark interface = %s;' % self.get_iface_quark())
702         self.b('  DBusGProxy *iface;')
703         self.b('')
704         self.b('  g_return_val_if_fail (%s (proxy), NULL);'
705                % self.proxy_assert)
706         self.b('')
707         self.b('  iface = tp_proxy_borrow_interface_by_id (')
708         self.b('      (TpProxy *) proxy,')
709         self.b('      interface, &error);')
710         self.b('')
711         self.b('  if (iface == NULL)')
712         self.b('    {')
713         self.b('      if (callback != NULL)')
714         self.b('        callback (proxy,')
715
716         for arg in out_args:
717             name, info, tp_type, elt = arg
718             ctype, gtype, marshaller, pointer = info
719
720             if pointer:
721                 self.b('            NULL,')
722             else:
723                 self.b('            0,')
724
725         self.b('            error, user_data, weak_object);')
726         self.b('      g_error_free (error);')
727         self.b('      return NULL;')
728         self.b('    }')
729         self.b('')
730         self.b('  if (callback == NULL)')
731         self.b('    {')
732         self.b('      dbus_g_proxy_call_no_reply (iface, "%s",' % member)
733
734         for arg in in_args:
735             name, info, tp_type, elt = arg
736             ctype, gtype, marshaller, pointer = info
737
738             const = pointer and 'const ' or ''
739
740             self.b('          %s, %s,' % (gtype, name))
741
742         self.b('          G_TYPE_INVALID);')
743         self.b('      return NULL;')
744         self.b('    }')
745         self.b('  else')
746         self.b('    {')
747         self.b('      TpProxyPendingCall *data;')
748         self.b('')
749         self.b('      data = tp_proxy_pending_call_v0_new ((TpProxy *) proxy,')
750         self.b('          interface, "%s", iface,' % member)
751         self.b('          %s,' % invoke_callback)
752         self.b('          G_CALLBACK (callback), user_data, destroy,')
753         self.b('          weak_object, FALSE);')
754         self.b('      tp_proxy_pending_call_v0_take_pending_call (data,')
755         self.b('          dbus_g_proxy_begin_call_with_timeout (iface,')
756         self.b('              "%s",' % member)
757         self.b('              %s,' % collect_callback)
758         self.b('              data,')
759         self.b('              tp_proxy_pending_call_v0_completed,')
760         self.b('              timeout_ms,')
761
762         for arg in in_args:
763             name, info, tp_type, elt = arg
764             ctype, gtype, marshaller, pointer = info
765
766             const = pointer and 'const ' or ''
767
768             self.b('              %s, %s,' % (gtype, name))
769
770         self.b('              G_TYPE_INVALID));')
771         self.b('')
772         self.b('      return data;')
773         self.b('    }')
774         self.b('}')
775         self.b('')
776
777         # Reentrant blocking calls
778         # Example:
779         # gboolean tp_cli_properties_interface_run_get_properties
780         #   (gpointer proxy,
781         #       gint timeout_ms,
782         #       const GArray *in_properties,
783         #       GPtrArray **out0,
784         #       GError **error,
785         #       GMainLoop **loop);
786
787         self.b('typedef struct {')
788         self.b('    GMainLoop *loop;')
789         self.b('    GError **error;')
790
791         for arg in out_args:
792             name, info, tp_type, elt = arg
793             ctype, gtype, marshaller, pointer = info
794
795             self.b('    %s*%s;' % (ctype, name))
796
797         self.b('    gboolean success:1;')
798         self.b('    gboolean completed:1;')
799         self.b('} _%s_%s_run_state_%s;'
800                % (self.prefix_lc, iface_lc, member_lc))
801
802         reentrant_invoke = '_%s_%s_finish_running_%s' % (self.prefix_lc,
803                                                          iface_lc,
804                                                          member_lc)
805
806         self.b('static void')
807         self.b('%s (TpProxy *self G_GNUC_UNUSED,' % reentrant_invoke)
808         self.b('    GError *error,')
809         self.b('    GValueArray *args,')
810         self.b('    GCallback unused G_GNUC_UNUSED,')
811         self.b('    gpointer user_data G_GNUC_UNUSED,')
812         self.b('    GObject *unused2 G_GNUC_UNUSED)')
813         self.b('{')
814         self.b('  _%s_%s_run_state_%s *state = user_data;'
815                % (self.prefix_lc, iface_lc, member_lc))
816         self.b('')
817         self.b('  state->success = (error == NULL);')
818         self.b('  state->completed = TRUE;')
819         self.b('  g_main_loop_quit (state->loop);')
820         self.b('')
821         self.b('  if (error != NULL)')
822         self.b('    {')
823         self.b('      if (state->error != NULL)')
824         self.b('        *state->error = error;')
825         self.b('      else')
826         self.b('        g_error_free (error);')
827         self.b('')
828         self.b('      return;')
829         self.b('    }')
830         self.b('')
831
832         for i, arg in enumerate(out_args):
833             name, info, tp_type, elt = arg
834             ctype, gtype, marshaller, pointer = info
835
836             self.b('  if (state->%s != NULL)' % name)
837             if marshaller == 'BOXED':
838                 self.b('    *state->%s = g_value_dup_boxed ('
839                        'args->values + %d);' % (name, i))
840             elif marshaller == 'STRING':
841                 self.b('    *state->%s = g_value_dup_string '
842                        '(args->values + %d);' % (name, i))
843             elif marshaller in ('UCHAR', 'BOOLEAN', 'INT', 'UINT',
844                     'INT64', 'UINT64', 'DOUBLE'):
845                 self.b('    *state->%s = g_value_get_%s (args->values + %d);'
846                        % (name, marshaller.lower(), i))
847             else:
848                 assert False, "Don't know how to copy %s" % gtype
849
850             self.b('')
851
852         if len(out_args) > 0:
853             self.b('  g_value_array_free (args);')
854         else:
855             self.b('  if (args != NULL)')
856             self.b('    g_value_array_free (args);')
857
858         self.b('}')
859         self.b('')
860
861         self.h('gboolean %s_%s_run_%s (%sproxy,'
862                % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg))
863         self.h('    gint timeout_ms,')
864
865         self.b('/**')
866         self.b(' * %s_%s_run_%s:' % (self.prefix_lc, iface_lc, member_lc))
867         self.b(' * @proxy: %s' % self.proxy_doc)
868         self.b(' * @timeout_ms: Timeout in milliseconds, or -1 for default')
869
870         for arg in in_args:
871             name, info, tp_type, elt = arg
872             ctype, gtype, marshaller, pointer = info
873
874             self.b(' * @%s: Used to pass an \'in\' argument: %s'
875                    % (name, xml_escape(get_docstring(elt) or '(Undocumented)')))
876
877         for arg in out_args:
878             name, info, tp_type, elt = arg
879             ctype, gtype, marshaller, pointer = info
880
881             self.b(' * @%s: Used to return an \'out\' argument if %%TRUE is '
882                    'returned: %s'
883                    % (name, xml_escape(get_docstring(elt) or '(Undocumented)')))
884
885         self.b(' * @error: If not %NULL, used to return errors if %FALSE ')
886         self.b(' *  is returned')
887         self.b(' * @loop: If not %NULL, set before re-entering ')
888         self.b(' *  the main loop, to point to a #GMainLoop ')
889         self.b(' *  which can be used to cancel this call with ')
890         self.b(' *  g_main_loop_quit(), causing a return of ')
891         self.b(' *  %FALSE with @error set to %TP_DBUS_ERROR_CANCELLED')
892         self.b(' *')
893         self.b(' * Call the method %s and run the main loop' % member)
894         self.b(' * until it returns. Before calling this method, you must')
895         self.b(' * add a reference to any borrowed objects you need to keep,')
896         self.b(' * and generally ensure that everything is in a consistent')
897         self.b(' * state.')
898         self.b(' *')
899         self.b(' * %s' % xml_escape(get_docstring(method) or '(Undocumented)'))
900         self.b(' *')
901         self.b(' * Returns: TRUE on success, FALSE and sets @error on error')
902         self.b(' */')
903         self.b('gboolean\n%s_%s_run_%s (%sproxy,'
904                % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg))
905         self.b('    gint timeout_ms,')
906
907         for arg in in_args:
908             name, info, tp_type, elt = arg
909             ctype, gtype, marshaller, pointer = info
910
911             const = pointer and 'const ' or ''
912
913             self.h('    %s%s%s,' % (const, ctype, name))
914             self.b('    %s%s%s,' % (const, ctype, name))
915
916         for arg in out_args:
917             name, info, tp_type, elt = arg
918             ctype, gtype, marshaller, pointer = info
919
920             self.h('    %s*%s,' % (ctype, name))
921             self.b('    %s*%s,' % (ctype, name))
922
923         self.h('    GError **error,')
924         self.h('    GMainLoop **loop);')
925         self.h('')
926
927         self.b('    GError **error,')
928         self.b('    GMainLoop **loop)')
929         self.b('{')
930         self.b('  DBusGProxy *iface;')
931         self.b('  GQuark interface = %s;' % self.get_iface_quark())
932         self.b('  TpProxyPendingCall *pc;')
933         self.b('  _%s_%s_run_state_%s state = {'
934                % (self.prefix_lc, iface_lc, member_lc))
935         self.b('      NULL /* loop */, error,')
936
937         for arg in out_args:
938             name, info, tp_type, elt = arg
939
940             self.b('    %s,' % name)
941
942         self.b('      FALSE /* completed */, FALSE /* success */ };')
943         self.b('')
944         self.b('  g_return_val_if_fail (%s (proxy), FALSE);'
945                % self.proxy_assert)
946         self.b('')
947         self.b('  iface = tp_proxy_borrow_interface_by_id')
948         self.b('       ((TpProxy *) proxy, interface, error);')
949         self.b('')
950         self.b('  if (iface == NULL)')
951         self.b('    return FALSE;')
952         self.b('')
953         self.b('  state.loop = g_main_loop_new (NULL, FALSE);')
954         self.b('')
955         self.b('  pc = tp_proxy_pending_call_v0_new ((TpProxy *) proxy,')
956         self.b('      interface, "%s", iface,' % member)
957         self.b('      %s,' % reentrant_invoke)
958         self.b('      NULL, &state, NULL, NULL, TRUE);')
959         self.b('')
960         self.b('  if (loop != NULL)')
961         self.b('    *loop = state.loop;')
962         self.b('')
963         self.b('  tp_proxy_pending_call_v0_take_pending_call (pc,')
964         self.b('      dbus_g_proxy_begin_call_with_timeout (iface,')
965         self.b('          "%s",' % member)
966         self.b('          %s,' % collect_callback)
967         self.b('          pc,')
968         self.b('          tp_proxy_pending_call_v0_completed,')
969         self.b('          timeout_ms,')
970
971         for arg in in_args:
972             name, info, tp_type, elt = arg
973             ctype, gtype, marshaller, pointer = info
974
975             const = pointer and 'const ' or ''
976
977             self.b('              %s, %s,' % (gtype, name))
978
979         self.b('          G_TYPE_INVALID));')
980         self.b('')
981         self.b('  if (!state.completed)')
982         self.b('    g_main_loop_run (state.loop);')
983         self.b('')
984         self.b('  if (!state.completed)')
985         self.b('    tp_proxy_pending_call_cancel (pc);')
986         self.b('')
987         self.b('  if (loop != NULL)')
988         self.b('    *loop = NULL;')
989         self.b('')
990         self.b('  g_main_loop_unref (state.loop);')
991         self.b('')
992         self.b('  return state.success;')
993         self.b('}')
994         self.b('')
995
996         # leave a gap for the end of the method
997         self.b('')
998         self.h('')
999
1000     def do_signal_add(self, signal):
1001         marshaller_items = []
1002         gtypes = []
1003
1004         for i in signal.getElementsByTagName('arg'):
1005             name = i.getAttribute('name')
1006             type = i.getAttribute('type')
1007             info = type_to_gtype(type)
1008             # type, GType, STRING, is a pointer
1009             gtypes.append(info[1])
1010
1011         self.b('  dbus_g_proxy_add_signal (proxy, "%s",'
1012                % signal.getAttribute('name'))
1013         for gtype in gtypes:
1014             self.b('      %s,' % gtype)
1015         self.b('      G_TYPE_INVALID);')
1016
1017     def do_interface(self, node):
1018         ifaces = node.getElementsByTagName('interface')
1019         assert len(ifaces) == 1
1020         iface = ifaces[0]
1021         name = node.getAttribute('name').replace('/', '')
1022
1023         self.iface = name
1024         self.iface_lc = name.lower()
1025         self.iface_uc = name.upper()
1026         self.iface_mc = name.replace('_', '')
1027         self.iface_dbus = iface.getAttribute('name')
1028
1029         signals = node.getElementsByTagName('signal')
1030         methods = node.getElementsByTagName('method')
1031
1032         if signals:
1033             self.b('static inline void')
1034             self.b('%s_add_signals_for_%s (DBusGProxy *proxy)'
1035                     % (self.prefix_lc, name.lower()))
1036             self.b('{')
1037
1038             if self.tp_proxy_api >= (0, 7, 6):
1039                 self.b('  if (!tp_proxy_dbus_g_proxy_claim_for_signal_adding '
1040                        '(proxy))')
1041                 self.b('    return;')
1042
1043             for signal in signals:
1044                 self.do_signal_add(signal)
1045
1046             self.b('}')
1047             self.b('')
1048             self.b('')
1049
1050         for signal in signals:
1051             self.do_signal(name, signal)
1052
1053         for method in methods:
1054             self.do_method(name, method)
1055
1056         self.iface_dbus = None
1057
1058     def __call__(self):
1059
1060         self.h('G_BEGIN_DECLS')
1061         self.h('')
1062
1063         self.b('/* We don\'t want gtkdoc scanning this file, it\'ll get')
1064         self.b(' * confused by seeing function definitions, so mark it as: */')
1065         self.b('/*<private_header>*/')
1066         self.b('')
1067
1068         nodes = self.dom.getElementsByTagName('node')
1069         nodes.sort(cmp_by_name)
1070
1071         for node in nodes:
1072             self.do_interface(node)
1073
1074         if self.group is not None:
1075
1076             self.b('/*')
1077             self.b(' * %s_%s_add_signals:' % (self.prefix_lc, self.group))
1078             self.b(' * @self: the #TpProxy')
1079             self.b(' * @quark: a quark whose string value is the interface')
1080             self.b(' *   name whose signals should be added')
1081             self.b(' * @proxy: the D-Bus proxy to which to add the signals')
1082             self.b(' * @unused: not used for anything')
1083             self.b(' *')
1084             self.b(' * Tell dbus-glib that @proxy has the signatures of all')
1085             self.b(' * signals on the given interface, if it\'s one we')
1086             self.b(' * support.')
1087             self.b(' *')
1088             self.b(' * This function should be used as a signal handler for')
1089             self.b(' * #TpProxy::interface-added.')
1090             self.b(' */')
1091             self.b('static void')
1092             self.b('%s_%s_add_signals (TpProxy *self G_GNUC_UNUSED,'
1093                     % (self.prefix_lc, self.group))
1094             self.b('    guint quark,')
1095             self.b('    DBusGProxy *proxy,')
1096             self.b('    gpointer unused G_GNUC_UNUSED)')
1097
1098             self.b('{')
1099
1100             for node in nodes:
1101                 iface = node.getElementsByTagName('interface')[0]
1102                 self.iface_dbus = iface.getAttribute('name')
1103                 signals = node.getElementsByTagName('signal')
1104                 if not signals:
1105                     continue
1106                 name = node.getAttribute('name').replace('/', '').lower()
1107                 self.iface_uc = name.upper()
1108                 self.b('  if (quark == %s)' % self.get_iface_quark())
1109                 self.b('    %s_add_signals_for_%s (proxy);'
1110                        % (self.prefix_lc, name))
1111
1112             self.b('}')
1113             self.b('')
1114
1115         self.h('G_END_DECLS')
1116         self.h('')
1117
1118         open(self.basename + '.h', 'w').write('\n'.join(self.__header))
1119         open(self.basename + '-body.h', 'w').write('\n'.join(self.__body))
1120
1121
1122 def types_to_gtypes(types):
1123     return [type_to_gtype(t)[1] for t in types]
1124
1125
1126 if __name__ == '__main__':
1127     options, argv = gnu_getopt(sys.argv[1:], '',
1128                                ['group=', 'subclass=', 'subclass-assert=',
1129                                 'iface-quark-prefix=', 'tp-proxy-api='])
1130
1131     opts = {}
1132
1133     for option, value in options:
1134         opts[option] = value
1135
1136     dom = xml.dom.minidom.parse(argv[0])
1137
1138     Generator(dom, argv[1], argv[2], opts)()