]> git.0d.be Git - empathy.git/blob - tools/glib-ginterface-gen.py
account-settings: allow to change the service
[empathy.git] / tools / glib-ginterface-gen.py
1 #!/usr/bin/python
2
3 # glib-ginterface-gen.py: service-side interface generator
4 #
5 # Generate dbus-glib 0.x service GInterfaces 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, 2007 Collabora Limited
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
29 from libglibcodegen import Signature, type_to_gtype, cmp_by_name, \
30         NS_TP, dbus_gutils_wincaps_to_uscore, \
31         signal_to_marshal_name, method_to_glue_marshal_name
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, signal_marshal_prefix,
39                  headers, end_headers, not_implemented_func,
40                  allow_havoc):
41         self.dom = dom
42         self.__header = []
43         self.__body = []
44         self.__docs = []
45
46         assert prefix.endswith('_')
47         assert not signal_marshal_prefix.endswith('_')
48
49         # The main_prefix, sub_prefix thing is to get:
50         # FOO_ -> (FOO_, _)
51         # FOO_SVC_ -> (FOO_, _SVC_)
52         # but
53         # FOO_BAR/ -> (FOO_BAR_, _)
54         # FOO_BAR/SVC_ -> (FOO_BAR_, _SVC_)
55
56         if '/' in prefix:
57             main_prefix, sub_prefix = prefix.upper().split('/', 1)
58             prefix = prefix.replace('/', '_')
59         else:
60             main_prefix, sub_prefix = prefix.upper().split('_', 1)
61
62         self.MAIN_PREFIX_ = main_prefix + '_'
63         self._SUB_PREFIX_ = '_' + sub_prefix
64
65         self.Prefix_ = prefix
66         self.Prefix = prefix.replace('_', '')
67         self.prefix_ = prefix.lower()
68         self.PREFIX_ = prefix.upper()
69
70         self.basename = basename
71         self.signal_marshal_prefix = signal_marshal_prefix
72         self.headers = headers
73         self.end_headers = end_headers
74         self.not_implemented_func = not_implemented_func
75         self.allow_havoc = allow_havoc
76
77     def h(self, s):
78         if isinstance(s, unicode):
79             s = s.encode('utf-8')
80         self.__header.append(s)
81
82     def b(self, s):
83         if isinstance(s, unicode):
84             s = s.encode('utf-8')
85         self.__body.append(s)
86
87     def d(self, s):
88         if isinstance(s, unicode):
89             s = s.encode('utf-8')
90         self.__docs.append(s)
91
92     def do_node(self, node):
93         node_name = node.getAttribute('name').replace('/', '')
94         node_name_mixed = self.node_name_mixed = node_name.replace('_', '')
95         node_name_lc = self.node_name_lc = node_name.lower()
96         node_name_uc = self.node_name_uc = node_name.upper()
97
98         interfaces = node.getElementsByTagName('interface')
99         assert len(interfaces) == 1, interfaces
100         interface = interfaces[0]
101         self.iface_name = interface.getAttribute('name')
102
103         tmp = interface.getAttribute('tp:implement-service')
104         if tmp == "no":
105             return
106
107         tmp = interface.getAttribute('tp:causes-havoc')
108         if tmp and not self.allow_havoc:
109             raise AssertionError('%s is %s' % (self.iface_name, tmp))
110
111         self.b('static const DBusGObjectInfo _%s%s_object_info;'
112                % (self.prefix_, node_name_lc))
113         self.b('')
114
115         methods = interface.getElementsByTagName('method')
116         signals = interface.getElementsByTagName('signal')
117         properties = interface.getElementsByTagName('property')
118         # Don't put properties in dbus-glib glue
119         glue_properties = []
120
121         self.b('struct _%s%sClass {' % (self.Prefix, node_name_mixed))
122         self.b('    GTypeInterface parent_class;')
123         for method in methods:
124             self.b('    %s %s;' % self.get_method_impl_names(method))
125         self.b('};')
126         self.b('')
127
128         if signals:
129             self.b('enum {')
130             for signal in signals:
131                 self.b('    %s,' % self.get_signal_const_entry(signal))
132             self.b('    N_%s_SIGNALS' % node_name_uc)
133             self.b('};')
134             self.b('static guint %s_signals[N_%s_SIGNALS] = {0};'
135                    % (node_name_lc, node_name_uc))
136             self.b('')
137
138         self.b('static void %s%s_base_init (gpointer klass);'
139                % (self.prefix_, node_name_lc))
140         self.b('')
141
142         self.b('GType')
143         self.b('%s%s_get_type (void)'
144                % (self.prefix_, node_name_lc))
145         self.b('{')
146         self.b('  static GType type = 0;')
147         self.b('')
148         self.b('  if (G_UNLIKELY (type == 0))')
149         self.b('    {')
150         self.b('      static const GTypeInfo info = {')
151         self.b('        sizeof (%s%sClass),' % (self.Prefix, node_name_mixed))
152         self.b('        %s%s_base_init, /* base_init */'
153                % (self.prefix_, node_name_lc))
154         self.b('        NULL, /* base_finalize */')
155         self.b('        NULL, /* class_init */')
156         self.b('        NULL, /* class_finalize */')
157         self.b('        NULL, /* class_data */')
158         self.b('        0,')
159         self.b('        0, /* n_preallocs */')
160         self.b('        NULL /* instance_init */')
161         self.b('      };')
162         self.b('')
163         self.b('      type = g_type_register_static (G_TYPE_INTERFACE,')
164         self.b('          "%s%s", &info, 0);' % (self.Prefix, node_name_mixed))
165         self.b('    }')
166         self.b('')
167         self.b('  return type;')
168         self.b('}')
169         self.b('')
170
171         self.d('/**')
172         self.d(' * %s%s:' % (self.Prefix, node_name_mixed))
173         self.d(' *')
174         self.d(' * Dummy typedef representing any implementation of this '
175                'interface.')
176         self.d(' */')
177
178         self.h('typedef struct _%s%s %s%s;'
179                % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed))
180         self.h('')
181
182         self.d('/**')
183         self.d(' * %s%sClass:' % (self.Prefix, node_name_mixed))
184         self.d(' *')
185         self.d(' * The class of %s%s.' % (self.Prefix, node_name_mixed))
186
187         if methods:
188             self.d(' *')
189             self.d(' * In a full implementation of this interface (i.e. all')
190             self.d(' * methods implemented), the interface initialization')
191             self.d(' * function used in G_IMPLEMENT_INTERFACE() would')
192             self.d(' * typically look like this:')
193             self.d(' *')
194             self.d(' * <programlisting>')
195             self.d(' * static void')
196             self.d(' * implement_%s (gpointer klass,' % self.node_name_lc)
197             self.d(' *     gpointer unused G_GNUC_UNUSED)')
198             self.d(' * {')
199             self.d(' * #define IMPLEMENT(x) %s%s_implement_&num;&num;x (\\'
200                    % (self.prefix_, self.node_name_lc))
201             self.d(' *   klass, my_object_&num;&num;x)')
202
203             for method in methods:
204                 class_member_name = method.getAttribute('tp:name-for-bindings')
205                 class_member_name = class_member_name.lower()
206                 self.d(' *   IMPLEMENT (%s);' % class_member_name)
207
208             self.d(' * #undef IMPLEMENT')
209             self.d(' * }')
210             self.d(' * </programlisting>')
211         else:
212             self.d(' * This interface has no D-Bus methods, so an')
213             self.d(' * implementation can typically pass %NULL to')
214             self.d(' * G_IMPLEMENT_INTERFACE() as the interface')
215             self.d(' * initialization function.')
216
217         self.d(' */')
218         self.d('')
219
220         self.h('typedef struct _%s%sClass %s%sClass;'
221                % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed))
222         self.h('')
223         self.h('GType %s%s_get_type (void);'
224                % (self.prefix_, node_name_lc))
225
226         gtype = self.current_gtype = \
227                 self.MAIN_PREFIX_ + 'TYPE' + self._SUB_PREFIX_ + node_name_uc
228         classname = self.Prefix + node_name_mixed
229
230         self.h('#define %s \\\n  (%s%s_get_type ())'
231                % (gtype, self.prefix_, node_name_lc))
232         self.h('#define %s%s(obj) \\\n'
233                '  (G_TYPE_CHECK_INSTANCE_CAST((obj), %s, %s))'
234                % (self.PREFIX_, node_name_uc, gtype, classname))
235         self.h('#define %sIS%s%s(obj) \\\n'
236                '  (G_TYPE_CHECK_INSTANCE_TYPE((obj), %s))'
237                % (self.MAIN_PREFIX_, self._SUB_PREFIX_, node_name_uc, gtype))
238         self.h('#define %s%s_GET_CLASS(obj) \\\n'
239                '  (G_TYPE_INSTANCE_GET_INTERFACE((obj), %s, %sClass))'
240                % (self.PREFIX_, node_name_uc, gtype, classname))
241         self.h('')
242         self.h('')
243
244         base_init_code = []
245
246         for method in methods:
247             self.do_method(method)
248
249         for signal in signals:
250             base_init_code.extend(self.do_signal(signal))
251
252         self.b('static inline void')
253         self.b('%s%s_base_init_once (gpointer klass G_GNUC_UNUSED)'
254                % (self.prefix_, node_name_lc))
255         self.b('{')
256
257         if properties:
258             self.b('  static TpDBusPropertiesMixinPropInfo properties[%d] = {'
259                    % (len(properties) + 1))
260
261             for m in properties:
262                 access = m.getAttribute('access')
263                 assert access in ('read', 'write', 'readwrite')
264
265                 if access == 'read':
266                     flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_READ'
267                 elif access == 'write':
268                     flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE'
269                 else:
270                     flags = ('TP_DBUS_PROPERTIES_MIXIN_FLAG_READ | '
271                              'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE')
272
273                 self.b('      { 0, %s, "%s", 0, NULL, NULL }, /* %s */'
274                        % (flags, m.getAttribute('type'), m.getAttribute('name')))
275
276             self.b('      { 0, 0, NULL, 0, NULL, NULL }')
277             self.b('  };')
278             self.b('  static TpDBusPropertiesMixinIfaceInfo interface =')
279             self.b('      { 0, properties, NULL, NULL };')
280             self.b('')
281
282
283         self.b('  dbus_g_object_type_install_info (%s%s_get_type (),'
284                % (self.prefix_, node_name_lc))
285         self.b('      &_%s%s_object_info);'
286                % (self.prefix_, node_name_lc))
287         self.b('')
288
289         if properties:
290             self.b('  interface.dbus_interface = g_quark_from_static_string '
291                    '("%s");' % self.iface_name)
292
293             for i, m in enumerate(properties):
294                 self.b('  properties[%d].name = g_quark_from_static_string ("%s");'
295                        % (i, m.getAttribute('name')))
296                 self.b('  properties[%d].type = %s;'
297                            % (i, type_to_gtype(m.getAttribute('type'))[1]))
298
299             self.b('  tp_svc_interface_set_dbus_properties_info (%s, &interface);'
300                    % self.current_gtype)
301
302             self.b('')
303
304         for s in base_init_code:
305             self.b(s)
306         self.b('}')
307
308         self.b('static void')
309         self.b('%s%s_base_init (gpointer klass)'
310                % (self.prefix_, node_name_lc))
311         self.b('{')
312         self.b('  static gboolean initialized = FALSE;')
313         self.b('')
314         self.b('  if (!initialized)')
315         self.b('    {')
316         self.b('      initialized = TRUE;')
317         self.b('      %s%s_base_init_once (klass);'
318                % (self.prefix_, node_name_lc))
319         self.b('    }')
320         # insert anything we need to do per implementation here
321         self.b('}')
322
323         self.h('')
324
325         self.b('static const DBusGMethodInfo _%s%s_methods[] = {'
326                % (self.prefix_, node_name_lc))
327
328         method_blob, offsets = self.get_method_glue(methods)
329
330         for method, offset in zip(methods, offsets):
331             self.do_method_glue(method, offset)
332
333         if len(methods) == 0:
334             # empty arrays are a gcc extension, so put in a dummy member
335             self.b("  { NULL, NULL, 0 }")
336
337         self.b('};')
338         self.b('')
339
340         self.b('static const DBusGObjectInfo _%s%s_object_info = {'
341                % (self.prefix_, node_name_lc))
342         self.b('  0,')  # version
343         self.b('  _%s%s_methods,' % (self.prefix_, node_name_lc))
344         self.b('  %d,' % len(methods))
345         self.b('"' + method_blob.replace('\0', '\\0') + '",')
346         self.b('"' + self.get_signal_glue(signals).replace('\0', '\\0') + '",')
347         self.b('"' +
348                self.get_property_glue(glue_properties).replace('\0', '\\0') +
349                '",')
350         self.b('};')
351         self.b('')
352
353         self.node_name_mixed = None
354         self.node_name_lc = None
355         self.node_name_uc = None
356
357     def get_method_glue(self, methods):
358         info = []
359         offsets = []
360
361         for method in methods:
362             offsets.append(len(''.join(info)))
363
364             info.append(self.iface_name + '\0')
365             info.append(method.getAttribute('name') + '\0')
366
367             info.append('A\0')    # async
368
369             counter = 0
370             for arg in method.getElementsByTagName('arg'):
371                 out = arg.getAttribute('direction') == 'out'
372
373                 name = arg.getAttribute('name')
374                 if not name:
375                     assert out
376                     name = 'arg%u' % counter
377                 counter += 1
378
379                 info.append(name + '\0')
380
381                 if out:
382                     info.append('O\0')
383                 else:
384                     info.append('I\0')
385
386                 if out:
387                     info.append('F\0')    # not const
388                     info.append('N\0')    # not error or return
389                 info.append(arg.getAttribute('type') + '\0')
390
391             info.append('\0')
392
393         return ''.join(info) + '\0', offsets
394
395     def do_method_glue(self, method, offset):
396         lc_name = method.getAttribute('tp:name-for-bindings')
397         if method.getAttribute('name') != lc_name.replace('_', ''):
398             raise AssertionError('Method %s tp:name-for-bindings (%s) does '
399                     'not match' % (method.getAttribute('name'), lc_name))
400         lc_name = lc_name.lower()
401
402         marshaller = method_to_glue_marshal_name(method,
403                 self.signal_marshal_prefix)
404         wrapper = self.prefix_ + self.node_name_lc + '_' + lc_name
405
406         self.b("  { (GCallback) %s, %s, %d }," % (wrapper, marshaller, offset))
407
408     def get_signal_glue(self, signals):
409         info = []
410
411         for signal in signals:
412             info.append(self.iface_name)
413             info.append(signal.getAttribute('name'))
414
415         return '\0'.join(info) + '\0\0'
416
417     # the implementation can be the same
418     get_property_glue = get_signal_glue
419
420     def get_method_impl_names(self, method):
421         dbus_method_name = method.getAttribute('name')
422
423         class_member_name = method.getAttribute('tp:name-for-bindings')
424         if dbus_method_name != class_member_name.replace('_', ''):
425             raise AssertionError('Method %s tp:name-for-bindings (%s) does '
426                     'not match' % (dbus_method_name, class_member_name))
427         class_member_name = class_member_name.lower()
428
429         stub_name = (self.prefix_ + self.node_name_lc + '_' +
430                      class_member_name)
431         return (stub_name + '_impl', class_member_name + '_cb')
432
433     def do_method(self, method):
434         assert self.node_name_mixed is not None
435
436         in_class = []
437
438         # Examples refer to Thing.DoStuff (su) -> ii
439
440         # DoStuff
441         dbus_method_name = method.getAttribute('name')
442         # do_stuff
443         class_member_name = method.getAttribute('tp:name-for-bindings')
444         if dbus_method_name != class_member_name.replace('_', ''):
445             raise AssertionError('Method %s tp:name-for-bindings (%s) does '
446                     'not match' % (dbus_method_name, class_member_name))
447         class_member_name = class_member_name.lower()
448
449         # void tp_svc_thing_do_stuff (TpSvcThing *, const char *, guint,
450         #   DBusGMethodInvocation *);
451         stub_name = (self.prefix_ + self.node_name_lc + '_' +
452                      class_member_name)
453         # typedef void (*tp_svc_thing_do_stuff_impl) (TpSvcThing *,
454         #   const char *, guint, DBusGMethodInvocation);
455         impl_name = stub_name + '_impl'
456         # void tp_svc_thing_return_from_do_stuff (DBusGMethodInvocation *,
457         #   gint, gint);
458         ret_name = (self.prefix_ + self.node_name_lc + '_return_from_' +
459                     class_member_name)
460
461         # Gather arguments
462         in_args = []
463         out_args = []
464         for i in method.getElementsByTagName('arg'):
465             name = i.getAttribute('name')
466             direction = i.getAttribute('direction') or 'in'
467             dtype = i.getAttribute('type')
468
469             assert direction in ('in', 'out')
470
471             if name:
472                 name = direction + '_' + name
473             elif direction == 'in':
474                 name = direction + str(len(in_args))
475             else:
476                 name = direction + str(len(out_args))
477
478             ctype, gtype, marshaller, pointer = type_to_gtype(dtype)
479
480             if pointer:
481                 ctype = 'const ' + ctype
482
483             struct = (ctype, name)
484
485             if direction == 'in':
486                 in_args.append(struct)
487             else:
488                 out_args.append(struct)
489
490         # Implementation type declaration (in header, docs separated)
491         self.d('/**')
492         self.d(' * %s:' % impl_name)
493         self.d(' * @self: The object implementing this interface')
494         for (ctype, name) in in_args:
495             self.d(' * @%s: %s (FIXME, generate documentation)'
496                    % (name, ctype))
497         self.d(' * @context: Used to return values or throw an error')
498         self.d(' *')
499         self.d(' * The signature of an implementation of the D-Bus method')
500         self.d(' * %s on interface %s.' % (dbus_method_name, self.iface_name))
501         self.d(' */')
502
503         self.h('typedef void (*%s) (%s%s *self,'
504           % (impl_name, self.Prefix, self.node_name_mixed))
505         for (ctype, name) in in_args:
506             self.h('    %s%s,' % (ctype, name))
507         self.h('    DBusGMethodInvocation *context);')
508
509         # Class member (in class definition)
510         in_class.append('    %s %s;' % (impl_name, class_member_name))
511
512         # Stub definition (in body only - it's static)
513         self.b('static void')
514         self.b('%s (%s%s *self,'
515            % (stub_name, self.Prefix, self.node_name_mixed))
516         for (ctype, name) in in_args:
517             self.b('    %s%s,' % (ctype, name))
518         self.b('    DBusGMethodInvocation *context)')
519         self.b('{')
520         self.b('  %s impl = (%s%s_GET_CLASS (self)->%s_cb);'
521           % (impl_name, self.PREFIX_, self.node_name_uc, class_member_name))
522         self.b('')
523         self.b('  if (impl != NULL)')
524         tmp = ['self'] + [name for (ctype, name) in in_args] + ['context']
525         self.b('    {')
526         self.b('      (impl) (%s);' % ',\n        '.join(tmp))
527         self.b('    }')
528         self.b('  else')
529         self.b('    {')
530         if self.not_implemented_func:
531             self.b('      %s (context);' % self.not_implemented_func)
532         else:
533             self.b('      GError e = { DBUS_GERROR, ')
534             self.b('           DBUS_GERROR_UNKNOWN_METHOD,')
535             self.b('           "Method not implemented" };')
536             self.b('')
537             self.b('      dbus_g_method_return_error (context, &e);')
538         self.b('    }')
539         self.b('}')
540         self.b('')
541
542         # Implementation registration (in both header and body)
543         self.h('void %s%s_implement_%s (%s%sClass *klass, %s impl);'
544                % (self.prefix_, self.node_name_lc, class_member_name,
545                   self.Prefix, self.node_name_mixed, impl_name))
546
547         self.d('/**')
548         self.d(' * %s%s_implement_%s:'
549                % (self.prefix_, self.node_name_lc, class_member_name))
550         self.d(' * @klass: A class whose instances implement this interface')
551         self.d(' * @impl: A callback used to implement the %s D-Bus method'
552                % dbus_method_name)
553         self.d(' *')
554         self.d(' * Register an implementation for the %s method in the vtable'
555                % dbus_method_name)
556         self.d(' * of an implementation of this interface. To be called from')
557         self.d(' * the interface init function.')
558         self.d(' */')
559
560         self.b('void')
561         self.b('%s%s_implement_%s (%s%sClass *klass, %s impl)'
562                % (self.prefix_, self.node_name_lc, class_member_name,
563                   self.Prefix, self.node_name_mixed, impl_name))
564         self.b('{')
565         self.b('  klass->%s_cb = impl;' % class_member_name)
566         self.b('}')
567         self.b('')
568
569         # Return convenience function (static inline, in header)
570         self.d('/**')
571         self.d(' * %s:' % ret_name)
572         self.d(' * @context: The D-Bus method invocation context')
573         for (ctype, name) in out_args:
574             self.d(' * @%s: %s (FIXME, generate documentation)'
575                    % (name, ctype))
576         self.d(' *')
577         self.d(' * Return successfully by calling dbus_g_method_return().')
578         self.d(' * This inline function exists only to provide type-safety.')
579         self.d(' */')
580         self.d('')
581
582         tmp = (['DBusGMethodInvocation *context'] +
583                [ctype + name for (ctype, name) in out_args])
584         self.h('static inline')
585         self.h('/* this comment is to stop gtkdoc realising this is static */')
586         self.h(('void %s (' % ret_name) + (',\n    '.join(tmp)) + ');')
587         self.h('static inline void')
588         self.h(('%s (' % ret_name) + (',\n    '.join(tmp)) + ')')
589         self.h('{')
590         tmp = ['context'] + [name for (ctype, name) in out_args]
591         self.h('  dbus_g_method_return (' + ',\n      '.join(tmp) + ');')
592         self.h('}')
593         self.h('')
594
595         return in_class
596
597     def get_signal_const_entry(self, signal):
598         assert self.node_name_uc is not None
599         return ('SIGNAL_%s_%s'
600                 % (self.node_name_uc, signal.getAttribute('name')))
601
602     def do_signal(self, signal):
603         assert self.node_name_mixed is not None
604
605         in_base_init = []
606
607         # for signal: Thing::StuffHappened (s, u)
608         # we want to emit:
609         # void tp_svc_thing_emit_stuff_happened (gpointer instance,
610         #    const char *arg0, guint arg1);
611
612         dbus_name = signal.getAttribute('name')
613
614         ugly_name = signal.getAttribute('tp:name-for-bindings')
615         if dbus_name != ugly_name.replace('_', ''):
616             raise AssertionError('Signal %s tp:name-for-bindings (%s) does '
617                     'not match' % (dbus_name, ugly_name))
618
619         stub_name = (self.prefix_ + self.node_name_lc + '_emit_' +
620                      ugly_name.lower())
621
622         const_name = self.get_signal_const_entry(signal)
623
624         # Gather arguments
625         args = []
626         for i in signal.getElementsByTagName('arg'):
627             name = i.getAttribute('name')
628             dtype = i.getAttribute('type')
629             tp_type = i.getAttribute('tp:type')
630
631             if name:
632                 name = 'arg_' + name
633             else:
634                 name = 'arg' + str(len(args))
635
636             ctype, gtype, marshaller, pointer = type_to_gtype(dtype)
637
638             if pointer:
639                 ctype = 'const ' + ctype
640
641             struct = (ctype, name, gtype)
642             args.append(struct)
643
644         tmp = (['gpointer instance'] +
645                [ctype + name for (ctype, name, gtype) in args])
646
647         self.h(('void %s (' % stub_name) + (',\n    '.join(tmp)) + ');')
648
649         # FIXME: emit docs
650
651         self.d('/**')
652         self.d(' * %s:' % stub_name)
653         self.d(' * @instance: The object implementing this interface')
654         for (ctype, name, gtype) in args:
655             self.d(' * @%s: %s (FIXME, generate documentation)'
656                    % (name, ctype))
657         self.d(' *')
658         self.d(' * Type-safe wrapper around g_signal_emit to emit the')
659         self.d(' * %s signal on interface %s.'
660                % (dbus_name, self.iface_name))
661         self.d(' */')
662
663         self.b('void')
664         self.b(('%s (' % stub_name) + (',\n    '.join(tmp)) + ')')
665         self.b('{')
666         self.b('  g_assert (instance != NULL);')
667         self.b('  g_assert (G_TYPE_CHECK_INSTANCE_TYPE (instance, %s));'
668                % (self.current_gtype))
669         tmp = (['instance', '%s_signals[%s]' % (self.node_name_lc, const_name),
670                 '0'] + [name for (ctype, name, gtype) in args])
671         self.b('  g_signal_emit (' + ',\n      '.join(tmp) + ');')
672         self.b('}')
673         self.b('')
674
675         signal_name = dbus_gutils_wincaps_to_uscore(dbus_name).replace('_',
676                 '-')
677
678         self.d('/**')
679         self.d(' * %s%s::%s:'
680                 % (self.Prefix, self.node_name_mixed, signal_name))
681         self.d(' * @self: an object')
682         for (ctype, name, gtype) in args:
683             self.d(' * @%s: %s (FIXME, generate documentation)'
684                    % (name, ctype))
685         self.d(' *')
686         self.d(' * The %s D-Bus signal is emitted whenever '
687                 'this GObject signal is.' % dbus_name)
688         self.d(' */')
689         self.d('')
690
691         in_base_init.append('  %s_signals[%s] ='
692                             % (self.node_name_lc, const_name))
693         in_base_init.append('  g_signal_new ("%s",' % signal_name)
694         in_base_init.append('      G_OBJECT_CLASS_TYPE (klass),')
695         in_base_init.append('      G_SIGNAL_RUN_LAST|G_SIGNAL_DETAILED,')
696         in_base_init.append('      0,')
697         in_base_init.append('      NULL, NULL,')
698         in_base_init.append('      %s,'
699                 % signal_to_marshal_name(signal, self.signal_marshal_prefix))
700         in_base_init.append('      G_TYPE_NONE,')
701         tmp = ['%d' % len(args)] + [gtype for (ctype, name, gtype) in args]
702         in_base_init.append('      %s);' % ',\n      '.join(tmp))
703         in_base_init.append('')
704
705         return in_base_init
706
707     def have_properties(self, nodes):
708         for node in nodes:
709             interface =  node.getElementsByTagName('interface')[0]
710             if interface.getElementsByTagName('property'):
711                 return True
712         return False
713
714     def __call__(self):
715         nodes = self.dom.getElementsByTagName('node')
716         nodes.sort(cmp_by_name)
717
718         self.h('#include <glib-object.h>')
719         self.h('#include <dbus/dbus-glib.h>')
720
721         if self.have_properties(nodes):
722             self.h('#include <telepathy-glib/dbus-properties-mixin.h>')
723
724         self.h('')
725         self.h('G_BEGIN_DECLS')
726         self.h('')
727
728         self.b('#include "%s.h"' % self.basename)
729         self.b('')
730         for header in self.headers:
731             self.b('#include %s' % header)
732         self.b('')
733
734         for node in nodes:
735             self.do_node(node)
736
737         self.h('')
738         self.h('G_END_DECLS')
739
740         self.b('')
741         for header in self.end_headers:
742             self.b('#include %s' % header)
743
744         self.h('')
745         self.b('')
746         open(self.basename + '.h', 'w').write('\n'.join(self.__header))
747         open(self.basename + '.c', 'w').write('\n'.join(self.__body))
748         open(self.basename + '-gtk-doc.h', 'w').write('\n'.join(self.__docs))
749
750
751 def cmdline_error():
752     print """\
753 usage:
754     gen-ginterface [OPTIONS] xmlfile Prefix_
755 options:
756     --include='<header.h>' (may be repeated)
757     --include='"header.h"' (ditto)
758     --include-end='"header.h"' (ditto)
759         Include extra headers in the generated .c file
760     --signal-marshal-prefix='prefix'
761         Use the given prefix on generated signal marshallers (default is
762         prefix.lower()).
763     --filename='BASENAME'
764         Set the basename for the output files (default is prefix.lower()
765         + 'ginterfaces')
766     --not-implemented-func='symbol'
767         Set action when methods not implemented in the interface vtable are
768         called. symbol must have signature
769             void symbol (DBusGMethodInvocation *context)
770         and return some sort of "not implemented" error via
771             dbus_g_method_return_error (context, ...)
772 """
773     sys.exit(1)
774
775
776 if __name__ == '__main__':
777     from getopt import gnu_getopt
778
779     options, argv = gnu_getopt(sys.argv[1:], '',
780                                ['filename=', 'signal-marshal-prefix=',
781                                 'include=', 'include-end=',
782                                 'allow-unstable',
783                                 'not-implemented-func='])
784
785     try:
786         prefix = argv[1]
787     except IndexError:
788         cmdline_error()
789
790     basename = prefix.lower() + 'ginterfaces'
791     signal_marshal_prefix = prefix.lower().rstrip('_')
792     headers = []
793     end_headers = []
794     not_implemented_func = ''
795     allow_havoc = False
796
797     for option, value in options:
798         if option == '--filename':
799             basename = value
800         elif option == '--signal-marshal-prefix':
801             signal_marshal_prefix = value
802         elif option == '--include':
803             if value[0] not in '<"':
804                 value = '"%s"' % value
805             headers.append(value)
806         elif option == '--include-end':
807             if value[0] not in '<"':
808                 value = '"%s"' % value
809             end_headers.append(value)
810         elif option == '--not-implemented-func':
811             not_implemented_func = value
812         elif option == '--allow-unstable':
813             allow_havoc = True
814
815     try:
816         dom = xml.dom.minidom.parse(argv[0])
817     except IndexError:
818         cmdline_error()
819
820     Generator(dom, prefix, basename, signal_marshal_prefix, headers,
821               end_headers, not_implemented_func, allow_havoc)()