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