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