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