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